2024-03-05 09:13:53

by Isaev Ruslan

[permalink] [raw]
Subject: [PATCH v5] Add JSON output options to 'iw' for scan results

v5 changes:
- add json print to print_ap_channel_report()
- minor refactor open_json_object()

branch: v5.19

This commit introduces the ability to output scan results from 'iw' in
JSON format, similar to the 'ip' utility from the iproute2 package.
The addition aims to enhance the tool's usability and scriptability
by providing a structured data format option.

Two new command-line options are added:
- '-j': Outputs scan results in compact JSON format.
- '-jj': Outputs scan results in pretty-printed JSON format.

The implementation focuses on modifying the printing functions to format
the output as JSON without altering the existing netlink
response parsing logic. This patch demonstrates the concept with scan
results, showcasing the potential for expanding JSON support
to other 'iw' functionalities.

If these changes prove valuable, I am prepared to continue working on
extending JSON output support throughout the tool.
Feedback is welcome and appreciated.

Thank you.

Signed-off-by: Isaev Ruslan <[email protected]>
---
Makefile | 2 +-
iw.c | 27 +-
iw.h | 1 +
json/json_print.c | 206 +++++
json/json_print.h | 57 ++
json/json_writer.c | 296 +++++++
json/json_writer.h | 68 ++
json/main.c | 28 +
scan.c | 2117 ++++++++++++++++++++++++++++----------------
util.c | 492 +++++++---
10 files changed, 2407 insertions(+), 887 deletions(-)
create mode 100644 json/json_print.c
create mode 100644 json/json_print.h
create mode 100644 json/json_writer.c
create mode 100644 json/json_writer.h
create mode 100644 json/main.c

diff --git a/Makefile b/Makefile
index 2fb8db8..6cda648 100644
--- a/Makefile
+++ b/Makefile
@@ -19,7 +19,7 @@ CFLAGS += -Werror-implicit-function-declaration -Wsign-compare -Wno-unused-param
CFLAGS += -Wdeclaration-after-statement
CFLAGS += $(CFLAGS_EVAL)

-_OBJS := $(sort $(patsubst %.c,%.o,$(wildcard *.c)))
+_OBJS := $(sort $(patsubst %.c,%.o,$(wildcard *.c) json/json_writer.c json/json_print.c))
VERSION_OBJS := $(filter-out version.o, $(_OBJS))
OBJS := $(VERSION_OBJS) version.o

diff --git a/iw.c b/iw.c
index 35308ba..779a728 100644
--- a/iw.c
+++ b/iw.c
@@ -24,6 +24,8 @@
#include "nl80211.h"
#include "iw.h"

+#include "json/json_print.h"
+
/* libnl 1.x compatibility code */
#if !defined(CONFIG_LIBNL20) && !defined(CONFIG_LIBNL30)
static inline struct nl_handle *nl_socket_alloc(void)
@@ -44,6 +46,7 @@ static inline int nl_socket_set_buffer_size(struct nl_sock *sk,
#endif /* CONFIG_LIBNL20 && CONFIG_LIBNL30 */

int iw_debug = 0;
+int iw_json = 0;

static int nl80211_init(struct nl80211_state *state)
{
@@ -181,7 +184,11 @@ static void __usage_cmd(const struct cmd *cmd, char *indent, bool full)
static void usage_options(void)
{
printf("Options:\n");
- printf("\t--debug\t\tenable netlink debugging\n");
+ printf(""
+ "\t--debug\t\tenable netlink debugging\n"
+ "\t-j \t\tjson output\n"
+ "\t-jj \t\tjson pretty output\n"
+ "");
}

static const char *argv0;
@@ -592,6 +599,20 @@ int main(int argc, char **argv)
argv++;
}

+ if (argc > 0 && strcmp(*argv, "-j") == 0) {
+ iw_json = 1;
+ new_json_obj(1, 0); //pretty print disabled
+ argc--;
+ argv++;
+ }
+
+ if (argc > 0 && strcmp(*argv, "-jj") == 0) {
+ iw_json = 1;
+ new_json_obj(1, 1); //pretty print enabled
+ argc--;
+ argv++;
+ }
+
if (argc > 0 && strcmp(*argv, "--version") == 0) {
version();
return 0;
@@ -648,5 +669,9 @@ int main(int argc, char **argv)

nl80211_cleanup(&nlstate);

+ //close root json object and deinit jsonw
+ if(iw_json){
+ delete_json_obj();
+ }
return err;
}
diff --git a/iw.h b/iw.h
index e712c59..b97eb55 100644
--- a/iw.h
+++ b/iw.h
@@ -179,6 +179,7 @@ struct vendor_event {
extern const char iw_version[];

extern int iw_debug;
+extern int iw_json;

int handle_cmd(struct nl80211_state *state, enum id_input idby,
int argc, char **argv);
diff --git a/json/json_print.c b/json/json_print.c
new file mode 100644
index 0000000..e2772cf
--- /dev/null
+++ b/json/json_print.c
@@ -0,0 +1,206 @@
+/*
+ * json_print.c "print regular or json output, based on json_writer".
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: Julien Fortin, <[email protected]>
+ */
+#include "json_print.h"
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#define SPRINT_BSIZE 64
+#define SPRINT_BUF(x) char x[SPRINT_BSIZE]
+static json_writer_t *_jw;
+#define _IS_JSON_CONTEXT(type) ((type & PRINT_JSON || type & PRINT_ANY) && _jw)
+#define _IS_FP_CONTEXT(type) (!_jw && (type & PRINT_FP || type & PRINT_ANY))
+void new_json_obj(int json, bool is_pretty) {
+ if (json) {
+ _jw = jsonw_new(stdout);
+ if (!_jw) {
+ perror("json object");
+ exit(1);
+ }
+ jsonw_pretty(_jw, is_pretty);
+ jsonw_start_object(_jw);
+ }
+}
+void delete_json_obj(void) {
+ if (_jw) {
+ jsonw_end_object(_jw);
+ jsonw_destroy(&_jw);
+ }
+}
+bool is_json_context(void) {
+ return _jw != NULL;
+}
+json_writer_t *get_json_writer(void) {
+ return _jw;
+}
+void open_json_object(const char *fmt, ...) {
+ if (_IS_JSON_CONTEXT(PRINT_JSON)) {
+ char buffer[1024];
+ va_list args;
+ va_start(args, fmt);
+ vsnprintf(buffer, sizeof(buffer), fmt, args);
+ // if not empty
+ if (*buffer) {
+ jsonw_name(_jw, buffer);
+ }
+ jsonw_start_object(_jw);
+ va_end(args);
+ }
+}
+
+void close_json_object(void) {
+ if (_IS_JSON_CONTEXT(PRINT_JSON))
+ jsonw_end_object(_jw);
+}
+/*
+ * Start json array or string array using
+ * the provided string as json key (if not null)
+ * or array delimiter in non-json context.
+ */
+void open_json_array(const char *key, const char *str) {
+ if (is_json_context()) {
+ if (key)
+ jsonw_name(_jw, key);
+ jsonw_start_array(_jw);
+ } else {
+ printf("%s", str);
+ }
+}
+/*
+ * End json array or string array
+ */
+void close_json_array(const char *delim) {
+ if (is_json_context())
+ jsonw_end_array(_jw);
+ else
+ printf("%s", delim);
+}
+/*
+ * pre-processor directive to generate similar
+ * functions handling different types
+ */
+#define _PRINT_FUNC(type_name, type) \
+__attribute__((format(printf, 3, 0))) \
+void print_##type_name( \
+ enum output_type t, \
+ const char *key, \
+ const char *fmt, \
+ type value) { \
+ if (_IS_JSON_CONTEXT(t)) { \
+ if (!key) \
+ jsonw_##type_name(_jw, value); \
+ else \
+ jsonw_##type_name##_field(_jw, key, value); \
+ } else if (_IS_FP_CONTEXT(t)) { \
+ fprintf(stdout, fmt, value); \
+ } \
+}
+
+_PRINT_FUNC(u8, uint8_t);
+_PRINT_FUNC(u16, uint16_t);
+_PRINT_FUNC(u32, uint32_t);
+_PRINT_FUNC(u64, uint64_t);
+_PRINT_FUNC(s8, int8_t);
+_PRINT_FUNC(s16, int16_t);
+_PRINT_FUNC(s32, int32_t);
+_PRINT_FUNC(s64, int64_t);
+_PRINT_FUNC(int, int);
+_PRINT_FUNC(uint, unsigned int);
+_PRINT_FUNC(float, double);
+#undef _PRINT_FUNC
+void print_string(enum output_type type, const char *key, const char *fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+
+ if (_IS_JSON_CONTEXT(type)) {
+ char buffer[1024];
+ vsnprintf(buffer, sizeof(buffer), fmt, args);
+ if (key) {
+ jsonw_string_field(_jw, key, buffer);
+ } else {
+ jsonw_string(_jw, buffer);
+ }
+ } else if (_IS_FP_CONTEXT(type)) {
+ vfprintf(stdout, fmt, args);
+ }
+
+ va_end(args);
+}
+/*
+ * value's type is bool. When using this function in FP context you can't pass
+ * a value to it, you will need to use "is_json_context()" to have different
+ * branch for json and regular output. grep -r "print_bool" for example
+ */
+void print_bool(enum output_type type,
+ const char *key,
+ const char *fmt,
+ bool value) {
+ if (_IS_JSON_CONTEXT(type)) {
+ if (key)
+ jsonw_bool_field(_jw, key, value);
+ else
+ jsonw_bool(_jw, value);
+ } else if (_IS_FP_CONTEXT(type)) {
+ fprintf(stdout, fmt, value ? "true" : "false");
+ }
+}
+/*
+ * In JSON context uses hardcode %#x format: 42 -> 0x2a
+ */
+void print_0xhex(enum output_type type,
+ const char *key,
+ const char *fmt,
+ unsigned long long hex) {
+ if (_IS_JSON_CONTEXT(type)) {
+ SPRINT_BUF(b1);
+ snprintf(b1, sizeof(b1), "%#llx", hex);
+ print_string(PRINT_JSON, key, NULL, b1);
+ } else if (_IS_FP_CONTEXT(type)) {
+ fprintf(stdout, fmt, hex);
+ }
+}
+void print_hex(enum output_type type,
+ const char *key,
+ const char *fmt,
+ unsigned int hex) {
+ if (_IS_JSON_CONTEXT(type)) {
+ SPRINT_BUF(b1);
+ snprintf(b1, sizeof(b1), "%x", hex);
+ if (key)
+ jsonw_string_field(_jw, key, b1);
+ else
+ jsonw_string(_jw, b1);
+ } else if (_IS_FP_CONTEXT(type)) {
+ fprintf(stdout, fmt, hex);
+ }
+}
+/*
+ * In JSON context we don't use the argument "value" we simply call jsonw_null
+ * whereas FP context can use "value" to output anything
+ */
+void print_null(enum output_type type,
+ const char *key,
+ const char *fmt,
+ const char *value) {
+ if (_IS_JSON_CONTEXT(type)) {
+ if (key)
+ jsonw_null_field(_jw, key);
+ else
+ jsonw_null(_jw);
+ } else if (_IS_FP_CONTEXT(type)) {
+ fprintf(stdout, fmt, value);
+ }
+}
+/* Print line separator (if not in JSON mode) */
+void print_nl(void) {
+ if (!_jw)
+ printf("%s", "\n");
+}
diff --git a/json/json_print.h b/json/json_print.h
new file mode 100644
index 0000000..4925803
--- /dev/null
+++ b/json/json_print.h
@@ -0,0 +1,57 @@
+/*
+ * json_print.h "print regular or json output, based on json_writer".
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: Julien Fortin, <[email protected]>
+ */
+#ifndef _JSON_PRINT_H_
+#define _JSON_PRINT_H_
+#include "json_writer.h"
+json_writer_t *get_json_writer(void);
+/*
+ * use:
+ * - PRINT_ANY for context based output
+ * - PRINT_FP for non json specific output
+ * - PRINT_JSON for json specific output
+ */
+enum output_type {
+ PRINT_FP = 1,
+ PRINT_JSON = 2,
+ PRINT_ANY = 4,
+};
+void new_json_obj(int json, bool is_pretty);
+void delete_json_obj(void);
+bool is_json_context(void);
+void fflush_fp(void);
+void open_json_object(const char *fmt, ...);
+void close_json_object(void);
+void open_json_array(const char *key, const char *str);
+void close_json_array(const char *delim);
+void print_nl(void);
+#define _PRINT_FUNC(type_name, type) \
+ void print_##type_name(enum output_type t, \
+ const char *key, \
+ const char *fmt, \
+ type value)
+_PRINT_FUNC(u8, uint8_t);
+_PRINT_FUNC(u16, uint16_t);
+_PRINT_FUNC(u32, uint32_t);
+_PRINT_FUNC(u64, uint64_t);
+_PRINT_FUNC(s8, int8_t);
+_PRINT_FUNC(s16, int16_t);
+_PRINT_FUNC(s32, int32_t);
+_PRINT_FUNC(s64, int64_t);
+_PRINT_FUNC(int, int);
+_PRINT_FUNC(float, double);
+_PRINT_FUNC(bool, bool);
+_PRINT_FUNC(null, const char *);
+_PRINT_FUNC(hex, unsigned int);
+_PRINT_FUNC(0xhex, unsigned long long);
+#undef _PRINT_FUNC
+
+void print_string(enum output_type type, const char *key, const char *fmt, ...);
+#endif /* _JSON_PRINT_H_ */
diff --git a/json/json_writer.c b/json/json_writer.c
new file mode 100644
index 0000000..ae59389
--- /dev/null
+++ b/json/json_writer.c
@@ -0,0 +1,296 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) //
+/*
+ * Simple streaming JSON writer
+ *
+ * This takes care of the annoying bits of JSON syntax like the commas
+ * after elements
+ *
+ * Authors: Stephen Hemminger <[email protected]>
+ */
+#include "json_writer.h"
+#include <assert.h>
+#include <inttypes.h>
+#include <malloc.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+struct json_writer {
+ FILE *out; /* output file */
+ unsigned int depth; /* nesting */
+ bool pretty; /* optional whitepace */
+ char sep; /* either nul or comma */
+};
+/* indentation for pretty print */
+static void jsonw_indent(json_writer_t *self) {
+ unsigned int i;
+ for (i = 0; i < self->depth; ++i)
+ fputs(" ", self->out);
+}
+/* end current line and indent if pretty printing */
+static void jsonw_eol(json_writer_t *self) {
+ if (!self->pretty)
+ return;
+ putc('\n', self->out);
+ jsonw_indent(self);
+}
+/* If current object is not empty print a comma */
+static void jsonw_eor(json_writer_t *self) {
+ if (self->sep != '\0')
+ putc(self->sep, self->out);
+ self->sep = ',';
+}
+/* Output JSON encoded string */
+/* Handles C escapes, does not do Unicode */
+static void jsonw_puts(json_writer_t *self, const char *str) {
+ putc('"', self->out);
+ for (; *str; ++str)
+ switch (*str) {
+ case '\t':
+ fputs("\\t", self->out);
+ break;
+ case '\n':
+ fputs("\\n", self->out);
+ break;
+ case '\r':
+ fputs("\\r", self->out);
+ break;
+ case '\f':
+ fputs("\\f", self->out);
+ break;
+ case '\b':
+ fputs("\\b", self->out);
+ break;
+ case '\\':
+ fputs("\\\\", self->out);
+ break;
+ case '"':
+ fputs("\\\"", self->out);
+ break;
+ case '\'':
+ fputs("\\\'", self->out);
+ break;
+ default:
+ putc(*str, self->out);
+ }
+ putc('"', self->out);
+}
+/* Create a new JSON stream */
+json_writer_t *jsonw_new(FILE *f) {
+ json_writer_t *self = malloc(sizeof(*self));
+ if (self) {
+ self->out = f;
+ self->depth = 0;
+ self->pretty = false;
+ self->sep = '\0';
+ }
+ return self;
+}
+/* End output to JSON stream */
+void jsonw_destroy(json_writer_t **self_p) {
+ json_writer_t *self = *self_p;
+ assert(self->depth == 0);
+ fputs("\n", self->out);
+ fflush(self->out);
+ free(self);
+ *self_p = NULL;
+}
+void jsonw_pretty(json_writer_t *self, bool on) {
+ self->pretty = on;
+}
+/* Basic blocks */
+static void jsonw_begin(json_writer_t *self, int c) {
+ jsonw_eor(self);
+ putc(c, self->out);
+ ++self->depth;
+ self->sep = '\0';
+}
+static void jsonw_end(json_writer_t *self, int c) {
+ assert(self->depth > 0);
+ --self->depth;
+ if (self->sep != '\0')
+ jsonw_eol(self);
+ putc(c, self->out);
+ self->sep = ',';
+}
+/* Add a JSON property name */
+void jsonw_name(json_writer_t *self, const char *name) {
+ jsonw_eor(self);
+ jsonw_eol(self);
+ self->sep = '\0';
+ jsonw_puts(self, name);
+ putc(':', self->out);
+ if (self->pretty)
+ putc(' ', self->out);
+}
+__attribute__((format(printf, 2, 3))) void jsonw_printf(json_writer_t *self, const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ jsonw_eor(self);
+ vfprintf(self->out, fmt, ap);
+ va_end(ap);
+}
+/* Collections */
+void jsonw_start_object(json_writer_t *self) {
+ jsonw_begin(self, '{');
+}
+void jsonw_end_object(json_writer_t *self) {
+ jsonw_end(self, '}');
+}
+void jsonw_start_array(json_writer_t *self) {
+ jsonw_begin(self, '[');
+ if (self->pretty)
+ putc(' ', self->out);
+}
+void jsonw_end_array(json_writer_t *self) {
+ if (self->pretty && self->sep)
+ putc(' ', self->out);
+ self->sep = '\0';
+ jsonw_end(self, ']');
+}
+/* JSON value types */
+void jsonw_string(json_writer_t *self, const char *value) {
+ jsonw_eor(self);
+ jsonw_puts(self, value);
+}
+void jsonw_bool(json_writer_t *self, bool val) {
+ jsonw_printf(self, "%s", val ? "true" : "false");
+}
+void jsonw_null(json_writer_t *self) {
+ jsonw_printf(self, "null");
+}
+void jsonw_float(json_writer_t *self, double num) {
+ jsonw_printf(self, "%g", num);
+}
+void jsonw_int(json_writer_t *self, int num) {
+ jsonw_printf(self, "%d", num);
+}
+void jsonw_uint(json_writer_t *self, unsigned int num) {
+ jsonw_printf(self, "%u", num);
+}
+void jsonw_xint(json_writer_t *self, uint64_t num) {
+ jsonw_printf(self, "%" PRIx64, num);
+}
+void jsonw_u8(json_writer_t *self, uint8_t num) {
+ jsonw_printf(self, "%hhu", num);
+}
+void jsonw_u16(json_writer_t *self, uint16_t num) {
+ jsonw_printf(self, "%hu", num);
+}
+void jsonw_u32(json_writer_t *self, uint32_t num) {
+ jsonw_printf(self, "%u", num);
+}
+void jsonw_u64(json_writer_t *self, uint64_t num) {
+ jsonw_printf(self, "%" PRIu64, num);
+}
+void jsonw_s8(json_writer_t *self, int8_t num) {
+ jsonw_printf(self, "%hhu", num);
+}
+void jsonw_s16(json_writer_t *self, int16_t num) {
+ jsonw_printf(self, "%hu", num);
+}
+void jsonw_s32(json_writer_t *self, int32_t num) {
+ jsonw_printf(self, "%u", num);
+}
+void jsonw_s64(json_writer_t *self, int64_t num) {
+ jsonw_printf(self, "%" PRId64, num);
+}
+/* Basic name/value objects */
+void jsonw_string_field(json_writer_t *self, const char *prop, const char *val) {
+ jsonw_name(self, prop);
+ jsonw_string(self, val);
+}
+void jsonw_bool_field(json_writer_t *self, const char *prop, bool val) {
+ jsonw_name(self, prop);
+ jsonw_bool(self, val);
+}
+void jsonw_null_field(json_writer_t *self, const char *prop) {
+ jsonw_name(self, prop);
+ jsonw_null(self);
+}
+void jsonw_float_field(json_writer_t *self, const char *prop, double val) {
+ jsonw_name(self, prop);
+ jsonw_float(self, val);
+}
+void jsonw_int_field(json_writer_t *self, const char *prop, int num) {
+ jsonw_name(self, prop);
+ jsonw_int(self, num);
+}
+void jsonw_uint_field(json_writer_t *self, const char *prop, unsigned int num) {
+ jsonw_name(self, prop);
+ jsonw_uint(self, num);
+}
+void jsonw_xint_field(json_writer_t *self, const char *prop, uint64_t num) {
+ jsonw_name(self, prop);
+ jsonw_xint(self, num);
+}
+void jsonw_u8_field(json_writer_t *self, const char *prop, uint8_t num) {
+ jsonw_name(self, prop);
+ jsonw_u8(self, num);
+}
+void jsonw_u16_field(json_writer_t *self, const char *prop, uint16_t num) {
+ jsonw_name(self, prop);
+ jsonw_u16(self, num);
+}
+void jsonw_u32_field(json_writer_t *self, const char *prop, uint32_t num) {
+ jsonw_name(self, prop);
+ jsonw_u32(self, num);
+}
+void jsonw_u64_field(json_writer_t *self, const char *prop, uint64_t num) {
+ jsonw_name(self, prop);
+ jsonw_u64(self, num);
+}
+void jsonw_s8_field(json_writer_t *self, const char *prop, int8_t num) {
+ jsonw_name(self, prop);
+ jsonw_s8(self, num);
+}
+void jsonw_s16_field(json_writer_t *self, const char *prop, int16_t num) {
+ jsonw_name(self, prop);
+ jsonw_s16(self, num);
+}
+void jsonw_s32_field(json_writer_t *self, const char *prop, int32_t num) {
+ jsonw_name(self, prop);
+ jsonw_s32(self, num);
+}
+void jsonw_s64_field(json_writer_t *self, const char *prop, int64_t num) {
+ jsonw_name(self, prop);
+ jsonw_s64(self, num);
+}
+#ifdef TEST
+int main(int argc, char **argv) {
+ json_writer_t *wr = jsonw_new(stdout);
+ jsonw_start_object(wr);
+ jsonw_pretty(wr, true);
+ jsonw_name(wr, "Vyatta");
+ jsonw_start_object(wr);
+ jsonw_string_field(wr, "url", "http://vyatta.com");
+ jsonw_uint_field(wr, "downloads", 2000000ul);
+ jsonw_float_field(wr, "stock", 8.16);
+ jsonw_name(wr, "ARGV");
+ jsonw_start_array(wr);
+ while (--argc)
+ jsonw_string(wr, *++argv);
+ jsonw_end_array(wr);
+ jsonw_name(wr, "empty");
+ jsonw_start_array(wr);
+ jsonw_end_array(wr);
+ jsonw_name(wr, "NIL");
+ jsonw_start_object(wr);
+ jsonw_end_object(wr);
+ jsonw_null_field(wr, "my_null");
+ jsonw_name(wr, "special chars");
+ jsonw_start_array(wr);
+ jsonw_string_field(wr, "slash", "/");
+ jsonw_string_field(wr, "newline", "\n");
+ jsonw_string_field(wr, "tab", "\t");
+ jsonw_string_field(wr, "ff", "\f");
+ jsonw_string_field(wr, "quote", "\"");
+ jsonw_string_field(wr, "tick", "\'");
+ jsonw_string_field(wr, "backslash", "\\");
+ jsonw_end_array(wr);
+ jsonw_end_object(wr);
+ jsonw_end_object(wr);
+ jsonw_destroy(&wr);
+ return 0;
+}
+#endif
diff --git a/json/json_writer.h b/json/json_writer.h
new file mode 100644
index 0000000..a092e56
--- /dev/null
+++ b/json/json_writer.h
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) */
+/*
+ * Simple streaming JSON writer
+ *
+ * This takes care of the annoying bits of JSON syntax like the commas
+ * after elements
+ *
+ * Authors: Stephen Hemminger <[email protected]>
+ */
+#ifndef _JSON_WRITER_H_
+#define _JSON_WRITER_H_
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+/* Opaque class structure */
+typedef struct json_writer json_writer_t;
+/* Create a new JSON stream */
+json_writer_t *jsonw_new(FILE *f);
+/* End output to JSON stream */
+void jsonw_destroy(json_writer_t **self_p);
+/* Cause output to have pretty whitespace */
+void jsonw_pretty(json_writer_t *self, bool on);
+/* Add property name */
+void jsonw_name(json_writer_t *self, const char *name);
+/* Add value */
+__attribute__((format(printf, 2, 3))) void jsonw_printf(json_writer_t *self, const char *fmt, ...);
+void jsonw_float_fmt(json_writer_t *self, const char *fmt, double num);
+
+void jsonw_string(json_writer_t *self, const char *value);
+void jsonw_bool(json_writer_t *self, bool val);
+void jsonw_null(json_writer_t *self);
+void jsonw_float(json_writer_t *self, double num);
+void jsonw_int(json_writer_t *self, int num);
+void jsonw_uint(json_writer_t *self, unsigned int);
+void jsonw_xint(json_writer_t *self, uint64_t num);
+void jsonw_u8(json_writer_t *self, uint8_t num);
+void jsonw_u16(json_writer_t *self, uint16_t num);
+void jsonw_u32(json_writer_t *self, uint32_t num);
+void jsonw_u64(json_writer_t *self, uint64_t num);
+void jsonw_s8(json_writer_t *self, int8_t num);
+void jsonw_s16(json_writer_t *self, int16_t num);
+void jsonw_s32(json_writer_t *self, int32_t num);
+void jsonw_s64(json_writer_t *self, int64_t num);
+
+/* Useful Combinations of name and value */
+void jsonw_string_field(json_writer_t *self, const char *prop, const char *val);
+void jsonw_bool_field(json_writer_t *self, const char *prop, bool value);
+void jsonw_null_field(json_writer_t *self, const char *prop);
+void jsonw_float_field(json_writer_t *self, const char *prop, double num);
+void jsonw_int_field(json_writer_t *self, const char *prop, int num);
+void jsonw_uint_field(json_writer_t *self, const char *prop, unsigned int num);
+void jsonw_xint_field(json_writer_t *self, const char *prop, uint64_t num);
+void jsonw_u8_field(json_writer_t *self, const char *prop, unsigned char num);
+void jsonw_u16_field(json_writer_t *self, const char *prop, uint16_t num);
+void jsonw_u32_field(json_writer_t *self, const char *prop, uint32_t num);
+void jsonw_u64_field(json_writer_t *self, const char *prop, uint64_t num);
+void jsonw_s8_field(json_writer_t *self, const char *prop, int8_t num);
+void jsonw_s16_field(json_writer_t *self, const char *prop, int16_t num);
+void jsonw_s32_field(json_writer_t *self, const char *prop, int32_t num);
+void jsonw_s64_field(json_writer_t *self, const char *prop, int64_t num);
+/* Collections */
+void jsonw_start_object(json_writer_t *self);
+void jsonw_end_object(json_writer_t *self);
+void jsonw_start_array(json_writer_t *self);
+void jsonw_end_array(json_writer_t *self);
+/* Override default exception handling */
+typedef void(jsonw_err_handler_fn)(const char *);
+#endif /* _JSON_WRITER_H_ */
diff --git a/json/main.c b/json/main.c
new file mode 100644
index 0000000..21d7cf4
--- /dev/null
+++ b/json/main.c
@@ -0,0 +1,28 @@
+#include <stdio.h>
+#include <stdint.h>
+
+#include "json_print.h"
+
+
+int main() {
+ // init json print without pretty print
+ new_json_obj(1, 0);
+ open_json_object("tx");
+ print_u64(PRINT_JSON, "bytes", NULL, 1);
+ print_u64(PRINT_JSON, "packets", NULL, 1);
+ print_u64(PRINT_JSON, "errors", NULL, 1);
+ print_u64(PRINT_JSON, "dropped", NULL, 1);
+ open_json_array("hobbies", NULL); // create object entry hobbies with array
+ print_string(PRINT_JSON, NULL, "%s", "Reading");
+ print_string(PRINT_JSON, NULL, "%s", "Hiking");
+ print_string(PRINT_JSON, NULL, "%s", "Coding");
+ close_json_array(NULL);
+ close_json_object();
+
+
+ // finish
+ delete_json_obj();
+
+ return 0;
+}
+
diff --git a/scan.c b/scan.c
index dfc136a..f04efb1 100644
--- a/scan.c
+++ b/scan.c
@@ -12,6 +12,8 @@
#include "nl80211.h"
#include "iw.h"

+#include "json/json_print.h"
+
#define WLAN_CAPABILITY_ESS (1<<0)
#define WLAN_CAPABILITY_IBSS (1<<1)
#define WLAN_CAPABILITY_CF_POLLABLE (1<<2)
@@ -546,10 +548,13 @@ static int handle_scan(struct nl80211_state *state,

static void tab_on_first(bool *first)
{
- if (!*first)
- printf("\t");
- else
+ if (!*first){
+ if(!iw_json){
+ printf("\t");
+ }
+ }else{
*first = false;
+ }
}

struct print_ies_data {
@@ -560,9 +565,13 @@ struct print_ies_data {
static void print_ssid(const uint8_t type, uint8_t len, const uint8_t *data,
const struct print_ies_data *ie_buffer)
{
- printf(" ");
- print_ssid_escaped(len, data);
- printf("\n");
+ if(!iw_json){
+ printf(" ");
+ print_ssid_escaped(len, data);
+ printf("\n");
+ } else {
+ print_ssid_escaped(len, data);
+ }
}

#define BSS_MEMBERSHIP_SELECTOR_VHT_PHY 126
@@ -574,21 +583,35 @@ static void print_supprates(const uint8_t type, uint8_t len,
{
int i;

- printf(" ");
-
- for (i = 0; i < len; i++) {
- int r = data[i] & 0x7f;
+ if(iw_json){
+ open_json_array("array", NULL);
+ for (i = 0; i < len; i++) {
+ int r = data[i] & 0x7f;
+ if (r == BSS_MEMBERSHIP_SELECTOR_VHT_PHY && data[i] & 0x80){
+ print_string(PRINT_JSON, NULL, "VHT%s", data[i] & 0x80 ? "*" : "");
+ } else if (r == BSS_MEMBERSHIP_SELECTOR_HT_PHY && data[i] & 0x80){
+ print_string(PRINT_JSON, NULL, "HT%s", data[i] & 0x80 ? "*" : "");
+ } else {
+ print_string(PRINT_JSON, NULL, "%d.%d%s", r/2, 5*(r&1), data[i] & 0x80 ? "*" : "");
+ }
+ }
+ close_json_array(NULL);
+ }else{
+ printf(" ");
+ for (i = 0; i < len; i++) {
+ int r = data[i] & 0x7f;

- if (r == BSS_MEMBERSHIP_SELECTOR_VHT_PHY && data[i] & 0x80)
- printf("VHT");
- else if (r == BSS_MEMBERSHIP_SELECTOR_HT_PHY && data[i] & 0x80)
- printf("HT");
- else
- printf("%d.%d", r/2, 5*(r&1));
+ if (r == BSS_MEMBERSHIP_SELECTOR_VHT_PHY && data[i] & 0x80)
+ printf("VHT");
+ else if (r == BSS_MEMBERSHIP_SELECTOR_HT_PHY && data[i] & 0x80)
+ printf("HT");
+ else
+ printf("%d.%d", r/2, 5*(r&1));

- printf("%s ", data[i] & 0x80 ? "*" : "");
+ printf("%s ", data[i] & 0x80 ? "*" : "");
+ }
+ printf("\n");
}
- printf("\n");
}

static void print_rm_enabled_capabilities(const uint8_t type, uint8_t len,
@@ -601,16 +624,18 @@ static void print_rm_enabled_capabilities(const uint8_t type, uint8_t len,
((__u64) data[3]) << 24 |
((__u64) data[4]) << 32;

- printf("\n");
- printf("\t\tCapabilities: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
- data[0], data[1],
- data[2], data[3],
- data[4]);
+ if(iw_json){
+ print_string(PRINT_JSON, "capabilities_raw", "0x%02x 0x%02x 0x%02x 0x%02x 0x%02x", data[0], data[1], data[2], data[3], data[4]);
+ open_json_array("capabilities", NULL);
+ }else{
+ printf("\n");
+ printf("\t\tCapabilities: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",data[0], data[1], data[2], data[3], data[4]);
+ }

#define PRINT_RM_CAPA(_bit, _str) \
do { \
if (capa & BIT(_bit)) \
- printf("\t\t\t" _str "\n"); \
+ iw_json ? print_string(PRINT_JSON, NULL, _str) : printf("\t\t\t" _str "\n"); \
} while (0)

PRINT_RM_CAPA(0, "Link Measurement");
@@ -642,14 +667,24 @@ static void print_rm_enabled_capabilities(const uint8_t type, uint8_t len,
PRINT_RM_CAPA(34, "FTM Range Report");
PRINT_RM_CAPA(35, "Civic Location Measurement");

- printf("\t\tNonoperating Channel Max Measurement Duration: %i\n", data[3] >> 5);
- printf("\t\tMeasurement Pilot Capability: %i\n", data[4] & 7);
+ if(iw_json){
+ close_json_array(NULL);
+ print_int(PRINT_JSON, "nonoperating_channel_max_measurement_duration", "%d", data[3] >> 5);
+ print_int(PRINT_JSON, "measurement_pilot_capability", "%d", data[4] & 7);
+ }else{
+ printf("\t\tNonoperating Channel Max Measurement Duration: %i\n", data[3] >> 5);
+ printf("\t\tMeasurement Pilot Capability: %i\n", data[4] & 7);
+ }
}

static void print_ds(const uint8_t type, uint8_t len, const uint8_t *data,
const struct print_ies_data *ie_buffer)
{
- printf(" channel %d\n", data[0]);
+ if(iw_json){
+ print_int(PRINT_JSON, "channel", "%d", data[0]);
+ }else{
+ printf(" channel %d\n", data[0]);
+ }
}

static const char *country_env_str(char environment)
@@ -669,46 +704,90 @@ static const char *country_env_str(char environment)
static void print_country(const uint8_t type, uint8_t len, const uint8_t *data,
const struct print_ies_data *ie_buffer)
{
- printf(" %.*s", 2, data);
+ if(iw_json){
+ print_string(PRINT_JSON, "country", "%.*s", 2, data);
+ print_string(PRINT_JSON, "environment", "%s", country_env_str(data[2]));

- printf("\tEnvironment: %s\n", country_env_str(data[2]));
+ data += 3;
+ len -= 3;

- data += 3;
- len -= 3;
+ if (len < 3) {
+ print_bool(PRINT_JSON, "no_country_ie_triplets_present", "%s", true);
+ return;
+ }

- if (len < 3) {
- printf("\t\tNo country IE triplets present\n");
- return;
- }
+ open_json_object("channels");
+ while (len >= 3) {
+ int end_channel;
+ union ieee80211_country_ie_triplet *triplet = (void *) data;

- while (len >= 3) {
- int end_channel;
- union ieee80211_country_ie_triplet *triplet = (void *) data;
+ if (triplet->ext.reg_extension_id >= IEEE80211_COUNTRY_EXTENSION_ID) {
+ print_int(PRINT_JSON, "extension id", "%d", triplet->ext.reg_extension_id);
+ print_int(PRINT_JSON, "regulatory class", "%d", triplet->ext.reg_class);
+ print_int(PRINT_JSON, "coverage class", "%d", triplet->ext.coverage_class);
+ print_int(PRINT_JSON, "coverage class up to", "%d", triplet->ext.coverage_class * 450);
+
+ data += 3;
+ len -= 3;
+ continue;
+ }

- if (triplet->ext.reg_extension_id >= IEEE80211_COUNTRY_EXTENSION_ID) {
- printf("\t\tExtension ID: %d Regulatory Class: %d Coverage class: %d (up to %dm)\n",
- triplet->ext.reg_extension_id,
- triplet->ext.reg_class,
- triplet->ext.coverage_class,
- triplet->ext.coverage_class * 450);
+ /* 2 GHz */
+ if (triplet->chans.first_channel <= 14)
+ end_channel = triplet->chans.first_channel + (triplet->chans.num_channels - 1);
+ else
+ end_channel = triplet->chans.first_channel + (4 * (triplet->chans.num_channels - 1));

+ open_json_object("%d", triplet->chans.first_channel);
+ print_int(PRINT_JSON, "first", "%d", triplet->chans.first_channel);
+ print_int(PRINT_JSON, "end", "%d", end_channel);
+ print_int(PRINT_JSON, "max power dbm", "%d", triplet->chans.max_power);
+ close_json_object();
data += 3;
len -= 3;
- continue;
}
+ close_json_object();
+ }else{
+ printf(" %.*s", 2, data);

- /* 2 GHz */
- if (triplet->chans.first_channel <= 14)
- end_channel = triplet->chans.first_channel + (triplet->chans.num_channels - 1);
- else
- end_channel = triplet->chans.first_channel + (4 * (triplet->chans.num_channels - 1));
-
- printf("\t\tChannels [%d - %d] @ %d dBm\n", triplet->chans.first_channel, end_channel, triplet->chans.max_power);
+ printf("\tEnvironment: %s\n", country_env_str(data[2]));

data += 3;
len -= 3;
- }

+ if (len < 3) {
+ printf("\t\tNo country IE triplets present\n");
+ return;
+ }
+
+ while (len >= 3) {
+ int end_channel;
+ union ieee80211_country_ie_triplet *triplet = (void *) data;
+
+ if (triplet->ext.reg_extension_id >= IEEE80211_COUNTRY_EXTENSION_ID) {
+ printf("\t\tExtension ID: %d Regulatory Class: %d Coverage class: %d (up to %dm)\n",
+ triplet->ext.reg_extension_id,
+ triplet->ext.reg_class,
+ triplet->ext.coverage_class,
+ triplet->ext.coverage_class * 450);
+
+ data += 3;
+ len -= 3;
+ continue;
+ }
+
+ /* 2 GHz */
+ if (triplet->chans.first_channel <= 14)
+ end_channel = triplet->chans.first_channel + (triplet->chans.num_channels - 1);
+ else
+ end_channel = triplet->chans.first_channel + (4 * (triplet->chans.num_channels - 1));
+
+ printf("\t\tChannels [%d - %d] @ %d dBm\n", triplet->chans.first_channel, end_channel, triplet->chans.max_power);
+
+ data += 3;
+ len -= 3;
+ }
+ }
return;
}

@@ -716,29 +795,37 @@ static void print_powerconstraint(const uint8_t type, uint8_t len,
const uint8_t *data,
const struct print_ies_data *ie_buffer)
{
- printf(" %d dB\n", data[0]);
+ if(iw_json){
+ print_int(PRINT_JSON, "db", "%d", data[0]);
+ }else{
+ printf(" %d dB\n", data[0]);
+ }
}

static void print_tpcreport(const uint8_t type, uint8_t len,
const uint8_t *data,
const struct print_ies_data *ie_buffer)
{
- printf(" TX power: %d dBm\n", data[0]);
- /* printf(" Link Margin (%d dB) is reserved in Beacons\n", data[1]); */
+ if(iw_json){
+ print_int(PRINT_JSON, "tx_power_dbm", "%d", data[0]);
+ }else{
+ printf(" TX power: %d dBm\n", data[0]);
+ /* printf(" Link Margin (%d dB) is reserved in Beacons\n", data[1]); */
+ }
}

static void print_erp(const uint8_t type, uint8_t len, const uint8_t *data,
const struct print_ies_data *ie_buffer)
{
if (data[0] == 0x00)
- printf(" <no flags>");
+ iw_json ? print_bool(PRINT_JSON, "no_flags", "%s", true) : printf(" <no flags>");
if (data[0] & 0x01)
- printf(" NonERP_Present");
+ iw_json ? print_bool(PRINT_JSON, "nonerp_present", "%s", true) : printf(" NonERP_Present");
if (data[0] & 0x02)
- printf(" Use_Protection");
+ iw_json ? print_bool(PRINT_JSON, "use_protection", "%s", true) : printf(" Use_Protection");
if (data[0] & 0x04)
- printf(" Barker_Preamble_Mode");
- printf("\n");
+ iw_json ? print_bool(PRINT_JSON, "barker_preamble_mode", "%s", true) : printf(" Barker_Preamble_Mode");
+ if(!iw_json) printf("\n");
}

static void print_ap_channel_report(const uint8_t type, uint8_t len, const uint8_t *data,
@@ -747,13 +834,22 @@ static void print_ap_channel_report(const uint8_t type, uint8_t len, const uint8
uint8_t oper_class = data[0];
int i;

- printf("\n");
- printf("\t\t * operating class: %d\n", oper_class);
- printf("\t\t * channel(s):");
- for (i = 1; i < len; ++i) {
- printf(" %d", data[i]);
+ if(iw_json){
+ print_int(PRINT_JSON, "operating_class", "%d", oper_class);
+ open_json_array("channels", NULL);
+ for (i = 1; i < len; ++i) {
+ print_int(PRINT_JSON, NULL, "%d", data[i]);
+ }
+ close_json_array(NULL);
+ }else{
+ printf("\n");
+ printf("\t\t * operating class: %d\n", oper_class);
+ printf("\t\t * channel(s):");
+ for (i = 1; i < len; ++i) {
+ printf(" %d", data[i]);
+ }
+ printf("\n");
}
- printf("\n");
}

static void print_cipher(const uint8_t *data)
@@ -761,59 +857,56 @@ static void print_cipher(const uint8_t *data)
if (memcmp(data, ms_oui, 3) == 0) {
switch (data[3]) {
case 0:
- printf("Use group cipher suite");
+ iw_json ? print_string(PRINT_JSON, NULL, "Use group cipher suite") : printf("Use group cipher suite");
break;
case 1:
- printf("WEP-40");
+ iw_json ? print_string(PRINT_JSON, NULL, "WEP-40") : printf("WEP-40");
break;
case 2:
- printf("TKIP");
+ iw_json ? print_string(PRINT_JSON, NULL, "TKIP") : printf("TKIP");
break;
case 4:
- printf("CCMP");
+ iw_json ? print_string(PRINT_JSON, NULL, "CCMP") : printf("CCMP");
break;
case 5:
- printf("WEP-104");
+ iw_json ? print_string(PRINT_JSON, NULL, "WEP-104") : printf("WEP-104");
break;
default:
- printf("%.02x-%.02x-%.02x:%d",
- data[0], data[1] ,data[2], data[3]);
+ iw_json ? print_string(PRINT_JSON, NULL, "%.02x-%.02x-%.02x:%d", data[0], data[1] ,data[2], data[3]) : printf("%.02x-%.02x-%.02x:%d", data[0], data[1] ,data[2], data[3]);
break;
}
} else if (memcmp(data, ieee80211_oui, 3) == 0) {
switch (data[3]) {
case 0:
- printf("Use group cipher suite");
+ iw_json ? print_string(PRINT_JSON, NULL, "Use group cipher suite") : printf("Use group cipher suite");
break;
case 1:
- printf("WEP-40");
+ iw_json ? print_string(PRINT_JSON, NULL, "WEP-40") : printf("WEP-40");
break;
case 2:
- printf("TKIP");
+ iw_json ? print_string(PRINT_JSON, NULL, "TKIP") : printf("TKIP");
break;
case 4:
- printf("CCMP");
+ iw_json ? print_string(PRINT_JSON, NULL, "CCMP") : printf("CCMP");
break;
case 5:
- printf("WEP-104");
+ iw_json ? print_string(PRINT_JSON, NULL, "WEP-104") : printf("WEP-104");
break;
case 6:
- printf("AES-128-CMAC");
+ iw_json ? print_string(PRINT_JSON, NULL, "AES-128-CMAC") : printf("AES-128-CMAC");
break;
case 7:
- printf("NO-GROUP");
+ iw_json ? print_string(PRINT_JSON, NULL, "NO-GROUP") : printf("NO-GROUP");
break;
case 8:
- printf("GCMP");
+ iw_json ? print_string(PRINT_JSON, NULL, "GCMP") : printf("GCMP");
break;
default:
- printf("%.02x-%.02x-%.02x:%d",
- data[0], data[1] ,data[2], data[3]);
+ iw_json ? print_string(PRINT_JSON, NULL, "%.02x-%.02x-%.02x:%d", data[0], data[1] ,data[2], data[3]) : printf("%.02x-%.02x-%.02x:%d", data[0], data[1] ,data[2], data[3]);
break;
}
} else
- printf("%.02x-%.02x-%.02x:%d",
- data[0], data[1] ,data[2], data[3]);
+ iw_json ? print_string(PRINT_JSON, NULL, "%.02x-%.02x-%.02x:%d", data[0], data[1] ,data[2], data[3]) : printf("%.02x-%.02x-%.02x:%d", data[0], data[1] ,data[2], data[3]);
}

static void print_auth(const uint8_t *data)
@@ -821,90 +914,86 @@ static void print_auth(const uint8_t *data)
if (memcmp(data, ms_oui, 3) == 0) {
switch (data[3]) {
case 1:
- printf("IEEE 802.1X");
+ iw_json ? print_string(PRINT_JSON, NULL, "IEEE 802.1X") : printf("IEEE 802.1X");
break;
case 2:
- printf("PSK");
+ iw_json ? print_string(PRINT_JSON, NULL, "PSK") : printf("PSK");
break;
default:
- printf("%.02x-%.02x-%.02x:%d",
- data[0], data[1] ,data[2], data[3]);
+ iw_json ? print_string(PRINT_JSON, NULL, "%.02x-%.02x-%.02x:%d", data[0], data[1] ,data[2], data[3]) : printf("%.02x-%.02x-%.02x:%d", data[0], data[1] ,data[2], data[3]);
break;
}
} else if (memcmp(data, ieee80211_oui, 3) == 0) {
switch (data[3]) {
case 1:
- printf("IEEE 802.1X");
+ iw_json ? print_string(PRINT_JSON, NULL, "IEEE 802.1X") : printf("IEEE 802.1X");
break;
case 2:
- printf("PSK");
+ iw_json ? print_string(PRINT_JSON, NULL, "PSK") : printf("PSK");
break;
case 3:
- printf("FT/IEEE 802.1X");
+ iw_json ? print_string(PRINT_JSON, NULL, "FT/IEEE 802.1X") : printf("FT/IEEE 802.1X");
break;
case 4:
- printf("FT/PSK");
+ iw_json ? print_string(PRINT_JSON, NULL, "FT/PSK") : printf("FT/PSK");
break;
case 5:
- printf("IEEE 802.1X/SHA-256");
+ iw_json ? print_string(PRINT_JSON, NULL, "IEEE 802.1X/SHA-256") : printf("IEEE 802.1X/SHA-256");
break;
case 6:
- printf("PSK/SHA-256");
+ iw_json ? print_string(PRINT_JSON, NULL, "PSK/SHA-256") : printf("PSK/SHA-256");
break;
case 7:
- printf("TDLS/TPK");
+ iw_json ? print_string(PRINT_JSON, NULL, "TDLS/TPK") : printf("TDLS/TPK");
break;
case 8:
- printf("SAE");
+ iw_json ? print_string(PRINT_JSON, NULL, "SAE") : printf("SAE");
break;
case 9:
- printf("FT/SAE");
+ iw_json ? print_string(PRINT_JSON, NULL, "FT/SAE") : printf("FT/SAE");
break;
case 11:
- printf("IEEE 802.1X/SUITE-B");
+ iw_json ? print_string(PRINT_JSON, NULL, "IEEE 802.1X/SUITE-B") : printf("IEEE 802.1X/SUITE-B");
break;
case 12:
- printf("IEEE 802.1X/SUITE-B-192");
+ iw_json ? print_string(PRINT_JSON, NULL, "IEEE 802.1X/SUITE-B-192") : printf("IEEE 802.1X/SUITE-B-192");
break;
case 13:
- printf("FT/IEEE 802.1X/SHA-384");
+ iw_json ? print_string(PRINT_JSON, NULL, "FT/IEEE 802.1X/SHA-384") : printf("FT/IEEE 802.1X/SHA-384");
break;
case 14:
- printf("FILS/SHA-256");
+ iw_json ? print_string(PRINT_JSON, NULL, "FILS/SHA-256") : printf("FILS/SHA-256");
break;
case 15:
- printf("FILS/SHA-384");
+ iw_json ? print_string(PRINT_JSON, NULL, "FILS/SHA-384") : printf("FILS/SHA-384");
break;
case 16:
- printf("FT/FILS/SHA-256");
+ iw_json ? print_string(PRINT_JSON, NULL, "FT/FILS/SHA-256") : printf("FT/FILS/SHA-256");
break;
case 17:
- printf("FT/FILS/SHA-384");
+ iw_json ? print_string(PRINT_JSON, NULL, "FT/FILS/SHA-384") : printf("FT/FILS/SHA-384");
break;
case 18:
- printf("OWE");
+ iw_json ? print_string(PRINT_JSON, NULL, "OWE") : printf("OWE");
break;
default:
- printf("%.02x-%.02x-%.02x:%d",
- data[0], data[1] ,data[2], data[3]);
+ iw_json ? print_string(PRINT_JSON, NULL, "%.02x-%.02x-%.02x:%d", data[0], data[1] ,data[2], data[3]) : printf("%.02x-%.02x-%.02x:%d", data[0], data[1] ,data[2], data[3]);
break;
}
} else if (memcmp(data, wfa_oui, 3) == 0) {
switch (data[3]) {
case 1:
- printf("OSEN");
+ iw_json ? print_string(PRINT_JSON, NULL, "OSEN") : printf("OSEN");
break;
case 2:
- printf("DPP");
+ iw_json ? print_string(PRINT_JSON, NULL, "DPP") : printf("DPP");
break;
default:
- printf("%.02x-%.02x-%.02x:%d",
- data[0], data[1] ,data[2], data[3]);
+ iw_json ? print_string(PRINT_JSON, NULL, "%.02x-%.02x-%.02x:%d", data[0], data[1] ,data[2], data[3]) : printf("%.02x-%.02x-%.02x:%d", data[0], data[1] ,data[2], data[3]);
break;
}
} else
- printf("%.02x-%.02x-%.02x:%d",
- data[0], data[1] ,data[2], data[3]);
+ iw_json ? print_string(PRINT_JSON, NULL, "%.02x-%.02x-%.02x:%d", data[0], data[1] ,data[2], data[3]) : printf("%.02x-%.02x-%.02x:%d", data[0], data[1] ,data[2], data[3]);
}

static void _print_rsn_ie(const char *defcipher, const char *defauth,
@@ -913,158 +1002,305 @@ static void _print_rsn_ie(const char *defcipher, const char *defauth,
bool first = true;
__u16 count, capa;
int i;
+ if(iw_json){
+ if (!is_osen) {
+ __u16 version;
+ version = data[0] + (data[1] << 8);
+ print_int(PRINT_JSON, "version", "%d", version);
+ data += 2;
+ len -= 2;
+ }
+
+ if (len < 4) {
+ print_string(PRINT_JSON, "group_cipher", "%s", defcipher);
+ print_string(PRINT_JSON, "Pairwise ciphers", "%s", defcipher);
+ return;
+ }

- if (!is_osen) {
- __u16 version;
- version = data[0] + (data[1] << 8);
tab_on_first(&first);
- printf("\t * Version: %d\n", version);
+ open_json_array("group_cipher_data", NULL);
+ print_cipher(data);
+ close_json_array(NULL);

- data += 2;
- len -= 2;
- }
+ data += 4;
+ len -= 4;
+
+ if (len < 2) {
+ print_string(PRINT_JSON, "pairwise_ciphers", "%s", defcipher);
+ return;
+ }
+
+ count = data[0] | (data[1] << 8);
+ if (2 + (count * 4) > len)
+ goto invalid;

- if (len < 4) {
tab_on_first(&first);
- printf("\t * Group cipher: %s\n", defcipher);
- printf("\t * Pairwise ciphers: %s\n", defcipher);
- return;
- }
+ open_json_array("pairwise_ciphers_data", NULL);
+ for (i = 0; i < count; i++) {
+ print_cipher(data + 2 + (i * 4));
+ }
+ close_json_array(NULL);

- tab_on_first(&first);
- printf("\t * Group cipher: ");
- print_cipher(data);
- printf("\n");
+ data += 2 + (count * 4);
+ len -= 2 + (count * 4);

- data += 4;
- len -= 4;
+ if (len < 2) {
+ print_string(PRINT_JSON, "authentication_suites", "%s", defauth);
+ return;
+ }

- if (len < 2) {
- tab_on_first(&first);
- printf("\t * Pairwise ciphers: %s\n", defcipher);
- return;
- }
+ count = data[0] | (data[1] << 8);
+ if (2 + (count * 4) > len)
+ goto invalid;

- count = data[0] | (data[1] << 8);
- if (2 + (count * 4) > len)
- goto invalid;
+ open_json_array("authentication_suites_data", NULL);
+ for (i = 0; i < count; i++) {
+ print_auth(data + 2 + (i * 4));
+ }
+ close_json_array(NULL);
+
+ data += 2 + (count * 4);
+ len -= 2 + (count * 4);
+
+ if (len >= 2) {
+ capa = data[0] | (data[1] << 8);
+ print_string(PRINT_JSON, "capabilities_raw", "0x%.4x", capa);
+
+ open_json_array("capabilities", NULL);
+ if (capa & 0x0001)
+ print_string(PRINT_JSON, NULL, "PreAuth");
+ if (capa & 0x0002)
+ print_string(PRINT_JSON, NULL, "NoPairwise");
+ switch ((capa & 0x000c) >> 2) {
+ case 0:
+ print_string(PRINT_JSON, NULL, "1-PTKSA-RC");
+ break;
+ case 1:
+ print_string(PRINT_JSON, NULL, "2-PTKSA-RC");
+ break;
+ case 2:
+ print_string(PRINT_JSON, NULL, "4-PTKSA-RC");
+ break;
+ case 3:
+ print_string(PRINT_JSON, NULL, "16-PTKSA-RC");
+ break;
+ }
+ switch ((capa & 0x0030) >> 4) {
+ case 0:
+ print_string(PRINT_JSON, NULL, "1-GTKSA-RC");
+ break;
+ case 1:
+ print_string(PRINT_JSON, NULL, "2-GTKSA-RC");
+ break;
+ case 2:
+ print_string(PRINT_JSON, NULL, "4-GTKSA-RC");
+ break;
+ case 3:
+ print_string(PRINT_JSON, NULL, "16-GTKSA-RC");
+ break;
+ }
+ if (capa & 0x0040)
+ print_string(PRINT_JSON, NULL, "MFP-required");
+ if (capa & 0x0080)
+ print_string(PRINT_JSON, NULL, "MFP-capable");
+ if (capa & 0x0200)
+ print_string(PRINT_JSON, NULL, "Peerkey-enabled");
+ if (capa & 0x0400)
+ print_string(PRINT_JSON, NULL, "SPP-AMSDU-capable");
+ if (capa & 0x0800)
+ print_string(PRINT_JSON, NULL, "SPP-AMSDU-required");
+ if (capa & 0x2000)
+ print_string(PRINT_JSON, NULL, "Extended-Key-ID");
+ close_json_array(NULL);
+
+ data += 2;
+ len -= 2;
+ }

- tab_on_first(&first);
- printf("\t * Pairwise ciphers:");
- for (i = 0; i < count; i++) {
- printf(" ");
- print_cipher(data + 2 + (i * 4));
- }
- printf("\n");
+ if (len >= 2) {
+ int pmkid_count = data[0] | (data[1] << 8);

- data += 2 + (count * 4);
- len -= 2 + (count * 4);
+ if (len >= 2 + 16 * pmkid_count) {
+ print_int(PRINT_JSON, "pmkids_count", "%d", pmkid_count);
+ /* not printing PMKID values */
+ data += 2 + 16 * pmkid_count;
+ len -= 2 + 16 * pmkid_count;
+ } else
+ goto invalid;
+ }

- if (len < 2) {
- tab_on_first(&first);
- printf("\t * Authentication suites: %s\n", defauth);
- return;
- }
+ if (len >= 4) {
+ tab_on_first(&first);
+ open_json_array("group_mgmt_cipher_suite_data", NULL);
+ print_cipher(data);
+ close_json_array(NULL);
+ data += 4;
+ len -= 4;
+ }

- count = data[0] | (data[1] << 8);
- if (2 + (count * 4) > len)
- goto invalid;
+ }else{
+ if (!is_osen) {
+ __u16 version;
+ version = data[0] + (data[1] << 8);
+ tab_on_first(&first);
+ printf("\t * Version: %d\n", version);

- tab_on_first(&first);
- printf("\t * Authentication suites:");
- for (i = 0; i < count; i++) {
- printf(" ");
- print_auth(data + 2 + (i * 4));
- }
- printf("\n");
+ data += 2;
+ len -= 2;
+ }

- data += 2 + (count * 4);
- len -= 2 + (count * 4);
+ if (len < 4) {
+ tab_on_first(&first);
+ printf("\t * Group cipher: %s\n", defcipher);
+ printf("\t * Pairwise ciphers: %s\n", defcipher);
+ return;
+ }

- if (len >= 2) {
- capa = data[0] | (data[1] << 8);
tab_on_first(&first);
- printf("\t * Capabilities:");
- if (capa & 0x0001)
- printf(" PreAuth");
- if (capa & 0x0002)
- printf(" NoPairwise");
- switch ((capa & 0x000c) >> 2) {
- case 0:
- printf(" 1-PTKSA-RC");
- break;
- case 1:
- printf(" 2-PTKSA-RC");
- break;
- case 2:
- printf(" 4-PTKSA-RC");
- break;
- case 3:
- printf(" 16-PTKSA-RC");
- break;
+ printf("\t * Group cipher: ");
+ print_cipher(data);
+ printf("\n");
+
+ data += 4;
+ len -= 4;
+
+ if (len < 2) {
+ tab_on_first(&first);
+ printf("\t * Pairwise ciphers: %s\n", defcipher);
+ return;
}
- switch ((capa & 0x0030) >> 4) {
- case 0:
- printf(" 1-GTKSA-RC");
- break;
- case 1:
- printf(" 2-GTKSA-RC");
- break;
- case 2:
- printf(" 4-GTKSA-RC");
- break;
- case 3:
- printf(" 16-GTKSA-RC");
- break;
+
+ count = data[0] | (data[1] << 8);
+ if (2 + (count * 4) > len)
+ goto invalid;
+
+ tab_on_first(&first);
+ printf("\t * Pairwise ciphers:");
+ for (i = 0; i < count; i++) {
+ printf(" ");
+ print_cipher(data + 2 + (i * 4));
}
- if (capa & 0x0040)
- printf(" MFP-required");
- if (capa & 0x0080)
- printf(" MFP-capable");
- if (capa & 0x0200)
- printf(" Peerkey-enabled");
- if (capa & 0x0400)
- printf(" SPP-AMSDU-capable");
- if (capa & 0x0800)
- printf(" SPP-AMSDU-required");
- if (capa & 0x2000)
- printf(" Extended-Key-ID");
- printf(" (0x%.4x)\n", capa);
- data += 2;
- len -= 2;
- }
+ printf("\n");

- if (len >= 2) {
- int pmkid_count = data[0] | (data[1] << 8);
+ data += 2 + (count * 4);
+ len -= 2 + (count * 4);

- if (len >= 2 + 16 * pmkid_count) {
+ if (len < 2) {
tab_on_first(&first);
- printf("\t * %d PMKIDs\n", pmkid_count);
- /* not printing PMKID values */
- data += 2 + 16 * pmkid_count;
- len -= 2 + 16 * pmkid_count;
- } else
+ printf("\t * Authentication suites: %s\n", defauth);
+ return;
+ }
+
+ count = data[0] | (data[1] << 8);
+ if (2 + (count * 4) > len)
goto invalid;
- }

- if (len >= 4) {
tab_on_first(&first);
- printf("\t * Group mgmt cipher suite: ");
- print_cipher(data);
+ printf("\t * Authentication suites:");
+ for (i = 0; i < count; i++) {
+ printf(" ");
+ print_auth(data + 2 + (i * 4));
+ }
printf("\n");
- data += 4;
- len -= 4;
- }

+ data += 2 + (count * 4);
+ len -= 2 + (count * 4);
+
+ if (len >= 2) {
+ capa = data[0] | (data[1] << 8);
+ tab_on_first(&first);
+ printf("\t * Capabilities:");
+ if (capa & 0x0001)
+ printf(" PreAuth");
+ if (capa & 0x0002)
+ printf(" NoPairwise");
+ switch ((capa & 0x000c) >> 2) {
+ case 0:
+ printf(" 1-PTKSA-RC");
+ break;
+ case 1:
+ printf(" 2-PTKSA-RC");
+ break;
+ case 2:
+ printf(" 4-PTKSA-RC");
+ break;
+ case 3:
+ printf(" 16-PTKSA-RC");
+ break;
+ }
+ switch ((capa & 0x0030) >> 4) {
+ case 0:
+ printf(" 1-GTKSA-RC");
+ break;
+ case 1:
+ printf(" 2-GTKSA-RC");
+ break;
+ case 2:
+ printf(" 4-GTKSA-RC");
+ break;
+ case 3:
+ printf(" 16-GTKSA-RC");
+ break;
+ }
+ if (capa & 0x0040)
+ printf(" MFP-required");
+ if (capa & 0x0080)
+ printf(" MFP-capable");
+ if (capa & 0x0200)
+ printf(" Peerkey-enabled");
+ if (capa & 0x0400)
+ printf(" SPP-AMSDU-capable");
+ if (capa & 0x0800)
+ printf(" SPP-AMSDU-required");
+ if (capa & 0x2000)
+ printf(" Extended-Key-ID");
+ printf(" (0x%.4x)\n", capa);
+ data += 2;
+ len -= 2;
+ }
+
+ if (len >= 2) {
+ int pmkid_count = data[0] | (data[1] << 8);
+
+ if (len >= 2 + 16 * pmkid_count) {
+ tab_on_first(&first);
+ printf("\t * %d PMKIDs\n", pmkid_count);
+ /* not printing PMKID values */
+ data += 2 + 16 * pmkid_count;
+ len -= 2 + 16 * pmkid_count;
+ } else
+ goto invalid;
+ }
+
+ if (len >= 4) {
+ tab_on_first(&first);
+ printf("\t * Group mgmt cipher suite: ");
+ print_cipher(data);
+ printf("\n");
+ data += 4;
+ len -= 4;
+ }
+ }
invalid:
if (len != 0) {
- printf("\t\t * bogus tail data (%d):", len);
- while (len) {
- printf(" %.2x", *data);
- data++;
- len--;
+ if(iw_json){
+ print_int(PRINT_JSON,"bogus_tail_data_len", "%d", len);
+ open_json_array("bogus_tail_data", NULL);
+ while (len) {
+ print_string(PRINT_JSON, NULL, "%.2x", *data);
+ data++;
+ len--;
+ }
+ close_json_array(NULL);
+ }else{
+ printf("\t\t * bogus tail data (%d):", len);
+ while (len) {
+ printf(" %.2x", *data);
+ data++;
+ len--;
+ }
+ printf("\n");
}
- printf("\n");
}
}

@@ -1090,7 +1326,11 @@ static void print_rsn(const uint8_t type, uint8_t len, const uint8_t *data,
static void print_ht_capa(const uint8_t type, uint8_t len, const uint8_t *data,
const struct print_ies_data *ie_buffer)
{
- printf("\n");
+ //text output
+ if(!iw_json){
+ printf("\n");
+ }
+
print_ht_capability(data[0] | (data[1] << 8));
print_ampdu_length(data[2] & 3);
print_ampdu_spacing((data[2] >> 2) & 7);
@@ -1264,7 +1504,7 @@ static void print_tx_power_envelope(const uint8_t type, uint8_t len,
"Local Maximum Transmit Power For 160/80+80 MHz",
};

- printf("\n");
+ if(!iw_json) printf("\n");

if (local_max_tx_power_count + 2 != len)
return;
@@ -1274,9 +1514,9 @@ static void print_tx_power_envelope(const uint8_t type, uint8_t len,
int8_t power_val = ((int8_t)data[1 + i]) >> 1;
int8_t point5 = data[1 + i] & 1;
if (point5)
- printf("\t\t * %s: %i.5 dBm\n", power_names[i], power_val);
+ iw_json ? print_int(PRINT_JSON, power_names[i], "%i.5", power_val) : printf("\t\t * %s: %i.5 dBm\n", power_names[i], power_val);
else
- printf("\t\t * %s: %i dBm\n", power_names[i], power_val);
+ iw_json ? print_int(PRINT_JSON, power_names[i], "%i", power_val) : printf("\t\t * %s: %i dBm\n", power_names[i], power_val);
}
}

@@ -1300,22 +1540,36 @@ static void print_ht_op(const uint8_t type, uint8_t len, const uint8_t *data,
"20 MHz",
"any",
};
-
- printf("\n");
- printf("\t\t * primary channel: %d\n", data[0]);
- printf("\t\t * secondary channel offset: %s\n",
- ht_secondary_offset[data[1] & 0x3]);
- printf("\t\t * STA channel width: %s\n", sta_chan_width[(data[1] & 0x4)>>2]);
- printf("\t\t * RIFS: %d\n", (data[1] & 0x8)>>3);
- printf("\t\t * HT protection: %s\n", protection[data[2] & 0x3]);
- printf("\t\t * non-GF present: %d\n", (data[2] & 0x4) >> 2);
- printf("\t\t * OBSS non-GF present: %d\n", (data[2] & 0x10) >> 4);
- printf("\t\t * dual beacon: %d\n", (data[4] & 0x40) >> 6);
- printf("\t\t * dual CTS protection: %d\n", (data[4] & 0x80) >> 7);
- printf("\t\t * STBC beacon: %d\n", data[5] & 0x1);
- printf("\t\t * L-SIG TXOP Prot: %d\n", (data[5] & 0x2) >> 1);
- printf("\t\t * PCO active: %d\n", (data[5] & 0x4) >> 2);
- printf("\t\t * PCO phase: %d\n", (data[5] & 0x8) >> 3);
+ if(iw_json){
+ print_int(PRINT_JSON, "primary_channel", "%d", data[0]);
+ print_string(PRINT_JSON, "secondary_channel_offset", "%s", ht_secondary_offset[data[1] & 0x3]);
+ print_string(PRINT_JSON, "sta_channel_width", "%s", sta_chan_width[(data[1] & 0x4)>>2]);
+ print_int(PRINT_JSON, "rifs", "%d", (data[1] & 0x8)>>3);
+ print_string(PRINT_JSON, "ht_protection", "%s", protection[data[2] & 0x3]);
+ print_int(PRINT_JSON, "non-gf_present", "%d", (data[2] & 0x4) >> 2);
+ print_int(PRINT_JSON, "obss_non-gf_present", "%d", (data[2] & 0x10) >> 4);
+ print_int(PRINT_JSON, "dual_beacon", "%d", (data[4] & 0x40) >> 6);
+ print_int(PRINT_JSON, "dual_cts_protection", "%d", (data[4] & 0x80) >> 7);
+ print_int(PRINT_JSON, "stbc_beacon", "%d", data[5] & 0x1);
+ print_int(PRINT_JSON, "l-sig_txop_prot", "%d", (data[5] & 0x2) >> 1);
+ print_int(PRINT_JSON, "pco_active", "%d", (data[5] & 0x4) >> 2);
+ print_int(PRINT_JSON, "pco_phase", "%d", (data[5] & 0x8) >> 3);
+ }else{
+ printf("\n");
+ printf("\t\t * primary channel: %d\n", data[0]);
+ printf("\t\t * secondary channel offset: %s\n", ht_secondary_offset[data[1] & 0x3]);
+ printf("\t\t * STA channel width: %s\n", sta_chan_width[(data[1] & 0x4)>>2]);
+ printf("\t\t * RIFS: %d\n", (data[1] & 0x8)>>3);
+ printf("\t\t * HT protection: %s\n", protection[data[2] & 0x3]);
+ printf("\t\t * non-GF present: %d\n", (data[2] & 0x4) >> 2);
+ printf("\t\t * OBSS non-GF present: %d\n", (data[2] & 0x10) >> 4);
+ printf("\t\t * dual beacon: %d\n", (data[4] & 0x40) >> 6);
+ printf("\t\t * dual CTS protection: %d\n", (data[4] & 0x80) >> 7);
+ printf("\t\t * STBC beacon: %d\n", data[5] & 0x1);
+ printf("\t\t * L-SIG TXOP Prot: %d\n", (data[5] & 0x2) >> 1);
+ printf("\t\t * PCO active: %d\n", (data[5] & 0x4) >> 2);
+ printf("\t\t * PCO phase: %d\n", (data[5] & 0x8) >> 3);
+ }
}

static void print_capabilities(const uint8_t type, uint8_t len,
@@ -1336,6 +1590,7 @@ static void print_capabilities(const uint8_t type, uint8_t len,
ie += ie[1] + 2;
}

+ if(iw_json) open_json_array("array", NULL);
for (i = 0; i < len; i++) {
base = i * 8;

@@ -1343,15 +1598,15 @@ static void print_capabilities(const uint8_t type, uint8_t len,
if (!(data[i] & (1 << bit)))
continue;

- printf("\n\t\t *");
+ if(!iw_json) printf("\n\t\t *");

-#define CAPA(bit, name) case bit: printf(" " name); break
+#define CAPA(bit, name) case bit: iw_json ? print_string(PRINT_JSON, NULL, name) : printf(" " name); break

/* if the capability 'cap' exists add 'val' to 'sum'
* otherwise print 'Reserved' */
#define ADD_BIT_VAL(bit, cap, sum, val) case (bit): do { \
if (!(cap)) { \
- printf(" Reserved"); \
+ if(!iw_json) printf(" Reserved"); \
break; \
} \
sum += val; \
@@ -1368,7 +1623,7 @@ static void print_capabilities(const uint8_t type, uint8_t len,

case 6:
s_psmp_support = true;
- printf(" S-PSMP Capability");
+ iw_json ? print_string(PRINT_JSON, NULL, "S-PSMP Capability") : printf(" S-PSMP Capability");
break;

CAPA(7, "Event");
@@ -1424,7 +1679,7 @@ static void print_capabilities(const uint8_t type, uint8_t len,
CAPA(55, "QLoad Report");
CAPA(56, "Alternate EDCA");
CAPA(57, "Unprotected TXOP Negotiation");
- CAPA(58, "Protected TXOP egotiation");
+ CAPA(58, "Protected TXOP Negotiation");
CAPA(59, "Reserved");
CAPA(60, "Protected QLoad Report");
CAPA(61, "TDLS Wider Bandwidth");
@@ -1444,39 +1699,53 @@ static void print_capabilities(const uint8_t type, uint8_t len,
CAPA(73, "Extended Spectrum Management Capable");
CAPA(74, "Reserved");
default:
- printf(" %d", bit);
+ iw_json ? print_int(PRINT_JSON, NULL, "%d", bit) : printf(" %d", bit);
break;
}
+
#undef ADD_BIT_VAL
#undef CAPA
- }
- }
+ } //inner for loop
+ } //outer for loop
+ if(iw_json) close_json_array(NULL);
+

if (s_psmp_support)
- printf("\n\t\t * Service Interval Granularity is %d ms",
- (si_duration + 1) * 5);
+ iw_json ? print_int(PRINT_JSON, "service_interval_granularity_ms", "%d", (si_duration + 1) * 5) : printf("\n\t\t * Service Interval Granularity is %d ms", (si_duration + 1) * 5);

if (is_vht_cap) {
- printf("\n\t\t * Max Number Of MSDUs In A-MSDU is ");
- switch (max_amsdu) {
- case 0:
- printf("unlimited");
- break;
- case 1:
- printf("32");
- break;
- case 2:
- printf("16");
- break;
- case 3:
- printf("8");
- break;
- default:
- break;
+ if(iw_json){
+ int max_amsdu_ = 0;
+ switch (max_amsdu) {
+ case 0: max_amsdu_ = -1; break;
+ case 1: max_amsdu_ = 32; break;
+ case 2: max_amsdu_ = 16; break;
+ case 3: max_amsdu_ = 8; break;
+ default: break;
+ }
+ print_int(PRINT_JSON, "max_number_of_mssdu_in_a-msdu", "%d", max_amsdu_);
+ }else{
+ printf("\n\t\t * Max Number Of MSDUs In A-MSDU is ");
+ switch (max_amsdu) {
+ case 0:
+ printf("unlimited");
+ break;
+ case 1:
+ printf("32");
+ break;
+ case 2:
+ printf("16");
+ break;
+ case 3:
+ printf("8");
+ break;
+ default:
+ break;
+ }
}
}

- printf("\n");
+ if(!iw_json) printf("\n");
}

static void print_tim(const uint8_t type, uint8_t len, const uint8_t *data,
@@ -1499,7 +1768,6 @@ static void print_ibssatim(const uint8_t type, uint8_t len, const uint8_t *data,
static void print_vht_capa(const uint8_t type, uint8_t len, const uint8_t *data,
const struct print_ies_data *ie_buffer)
{
- printf("\n");
print_vht_info((__u32) data[0] | ((__u32)data[1] << 8) |
((__u32)data[2] << 16) | ((__u32)data[3] << 24),
data + 4);
@@ -1515,12 +1783,20 @@ static void print_vht_oper(const uint8_t type, uint8_t len, const uint8_t *data,
[2] = "160 MHz",
};

- printf("\n");
- printf("\t\t * channel width: %d (%s)\n", data[0],
- data[0] < ARRAY_SIZE(chandwidths) ? chandwidths[data[0]] : "unknown");
- printf("\t\t * center freq segment 1: %d\n", data[1]);
- printf("\t\t * center freq segment 2: %d\n", data[2]);
- printf("\t\t * VHT basic MCS set: 0x%.2x%.2x\n", data[4], data[3]);
+ if(iw_json){
+ print_string(PRINT_JSON, "channel_width", "%d (%s)", data[0],
+ data[0] < ARRAY_SIZE(chandwidths) ? chandwidths[data[0]] : "unknown");
+ print_int(PRINT_JSON, "center freq segment 1", "%d", data[1]);
+ print_int(PRINT_JSON, "center freq segment 2", "%d", data[2]);
+ print_string(PRINT_JSON, "vht_basic_mcs_set", "0x%.2x%.2x", data[4], data[3]);
+ }else{
+ printf("\n");
+ printf("\t\t * channel width: %d (%s)\n", data[0],
+ data[0] < ARRAY_SIZE(chandwidths) ? chandwidths[data[0]] : "unknown");
+ printf("\t\t * center freq segment 1: %d\n", data[1]);
+ printf("\t\t * center freq segment 2: %d\n", data[2]);
+ printf("\t\t * VHT basic MCS set: 0x%.2x%.2x\n", data[4], data[3]);
+ }
}

static void print_supp_op_classes(const uint8_t type, uint8_t len,
@@ -1532,29 +1808,70 @@ static void print_supp_op_classes(const uint8_t type, uint8_t len,
int zero_delimiter = 0;
int one_hundred_thirty_delimiter = 0;

- printf("\n");
- printf("\t\t * current operating class: %d\n", *p);
- while (++p < next_data) {
- if (*p == 130) {
- one_hundred_thirty_delimiter = 1;
- break;
+
+
+ if(iw_json){
+ print_string(PRINT_JSON, "current_operating_class", "%d", *p);
+ open_json_array("operating_class", NULL);
+ while (++p < next_data) {
+ if (*p == 130) {
+ one_hundred_thirty_delimiter = 1;
+ break;
+ }
+ if (*p == 0) {
+ zero_delimiter = 0;
+ break;
+ }
+ print_int(PRINT_JSON, NULL, "%d", *p);
}
- if (*p == 0) {
- zero_delimiter = 0;
- break;
+ close_json_array(NULL);
+
+ open_json_array("current operating class extension", NULL);
+ if (one_hundred_thirty_delimiter){
+ while (++p < next_data) {
+ print_int(PRINT_JSON, NULL, "%d", *p);
+ }
}
- printf("\t\t * operating class: %d\n", *p);
- }
- if (one_hundred_thirty_delimiter)
- while (++p < next_data) {
- printf("\t\t * current operating class extension: %d\n", *p);
+ close_json_array(NULL);
+
+ open_json_array("operating class tuple", NULL);
+ if (zero_delimiter){
+ while (++p < next_data - 1) {
+ print_string(PRINT_JSON, NULL, "%d %d", p[0], p[1]);
+ if (*p == 0)
+ break;
+ }
}
- if (zero_delimiter)
- while (++p < next_data - 1) {
- printf("\t\t * operating class tuple: %d %d\n", p[0], p[1]);
- if (*p == 0)
+ close_json_array(NULL);
+ }else{
+ printf("\n");
+ printf("\t\t * current operating class: %d\n", *p);
+ while (++p < next_data) {
+ if (*p == 130) {
+ one_hundred_thirty_delimiter = 1;
+ break;
+ }
+ if (*p == 0) {
+ zero_delimiter = 0;
break;
+ }
+ printf("\t\t * operating class: %d\n", *p);
+ }
+
+ if (one_hundred_thirty_delimiter){
+ while (++p < next_data) {
+ printf("\t\t * current operating class extension: %d\n", *p);
+ }
+ }
+
+ if (zero_delimiter){
+ while (++p < next_data - 1) {
+ printf("\t\t * operating class tuple: %d %d\n", p[0], p[1]);
+ if (*p == 0)
+ break;
+ }
}
+ }
}

static void print_measurement_pilot_tx(const uint8_t type, uint8_t len,
@@ -1563,8 +1880,12 @@ static void print_measurement_pilot_tx(const uint8_t type, uint8_t len,
{
uint8_t *p, len_remaining;

- printf("\n");
- printf("\t\t * interval: %d TUs\n", data[0]);
+ if(iw_json){
+ print_int(PRINT_JSON, "interval_tus", "%d", data[0]);
+ }else{
+ printf("\n");
+ printf("\t\t * interval: %d TUs\n", data[0]);
+ }

if (len <= 1)
return;
@@ -1584,24 +1905,32 @@ static void print_measurement_pilot_tx(const uint8_t type, uint8_t len,

/* 802.11-2016 only allows vendor specific elements */
if (subelement_id != 221) {
- printf("\t\t * <Invalid subelement ID %d>\n", subelement_id);
+ iw_json ? print_int(PRINT_JSON, "invalid_subelement_id", "%d", subelement_id) : printf("\t\t * <Invalid subelement ID %d>\n", subelement_id);
return;
}

if (len < 3 || len > len_remaining) {
- printf(" <Parse error, element too short>\n");
+ iw_json ? print_int(PRINT_JSON, "invalid_subelement_id", "%d", subelement_id) : printf(" <Parse error, element too short>\n");
return;
}

- printf("\t\t * vendor specific: OUI %.2x:%.2x:%.2x, data:",
- p[0], p[1], p[2]);
- /* add only two here and use ++p in while loop */
- p += 2;
-
- while (++p < end)
- printf(" %.2x", *p);
- printf("\n");
+ if(iw_json){
+ open_json_array("vendor_specific_oui", NULL);

+ while (++p < end){
+ print_string(PRINT_JSON, NULL, "0x%.2x", *p);
+ }
+ close_json_array(NULL);
+ }else{
+ printf("\t\t * vendor specific: OUI %.2x:%.2x:%.2x, data:",
+ p[0], p[1], p[2]);
+ /* add only two here and use ++p in while loop */
+ p += 2;
+
+ while (++p < end)
+ printf(" %.2x", *p);
+ printf("\n");
+ }
len_remaining -= len;
}
}
@@ -1610,67 +1939,123 @@ static void print_obss_scan_params(const uint8_t type, uint8_t len,
const uint8_t *data,
const struct print_ies_data *ie_buffer)
{
- printf("\n");
- printf("\t\t * passive dwell: %d TUs\n", (data[1] << 8) | data[0]);
- printf("\t\t * active dwell: %d TUs\n", (data[3] << 8) | data[2]);
- printf("\t\t * channel width trigger scan interval: %d s\n", (data[5] << 8) | data[4]);
- printf("\t\t * scan passive total per channel: %d TUs\n", (data[7] << 8) | data[6]);
- printf("\t\t * scan active total per channel: %d TUs\n", (data[9] << 8) | data[8]);
- printf("\t\t * BSS width channel transition delay factor: %d\n", (data[11] << 8) | data[10]);
- printf("\t\t * OBSS Scan Activity Threshold: %d.%02d %%\n",
- ((data[13] << 8) | data[12]) / 100, ((data[13] << 8) | data[12]) % 100);
+ if(iw_json){
+ print_int(PRINT_JSON, "passive_dwell_tus", "%d", (data[1] << 8) | data[0]);
+ print_int(PRINT_JSON, "active_dwell_tus", "%d", (data[3] << 8) | data[2]);
+ print_int(PRINT_JSON, "channel_width_trigger_scan_interval_s", "%d", (data[5] << 8) | data[4]);
+ print_int(PRINT_JSON, "scan_passive_total_per_channel_tus", "%d", (data[7] << 8) | data[6]);
+ print_int(PRINT_JSON, "scan_active_total_per_channel_tus", "%d", (data[9] << 8) | data[8]);
+ print_int(PRINT_JSON, "bss_width_channel_transition_delay_factor", "%d", (data[11] << 8) | data[10]);
+ print_string(PRINT_JSON, "obss_scan_ctivity_threshold", "%d.%02d %%\n",
+ ((data[13] << 8) | data[12]) / 100, ((data[13] << 8) | data[12]) % 100);
+ } else {
+ printf("\n");
+ printf("\t\t * passive dwell: %d TUs\n", (data[1] << 8) | data[0]);
+ printf("\t\t * active dwell: %d TUs\n", (data[3] << 8) | data[2]);
+ printf("\t\t * channel width trigger scan interval: %d s\n", (data[5] << 8) | data[4]);
+ printf("\t\t * scan passive total per channel: %d TUs\n", (data[7] << 8) | data[6]);
+ printf("\t\t * scan active total per channel: %d TUs\n", (data[9] << 8) | data[8]);
+ printf("\t\t * BSS width channel transition delay factor: %d\n", (data[11] << 8) | data[10]);
+ printf("\t\t * OBSS Scan Activity Threshold: %d.%02d %%\n",
+ ((data[13] << 8) | data[12]) / 100, ((data[13] << 8) | data[12]) % 100);
+ }
}

static void print_secchan_offs(const uint8_t type, uint8_t len,
const uint8_t *data,
const struct print_ies_data *ie_buffer)
{
- if (data[0] < ARRAY_SIZE(ht_secondary_offset))
- printf(" %s (%d)\n", ht_secondary_offset[data[0]], data[0]);
- else
- printf(" %d\n", data[0]);
+ if(iw_json){
+ if (data[0] < ARRAY_SIZE(ht_secondary_offset))
+ print_string(PRINT_JSON, "value", "%s (%d)", ht_secondary_offset[data[0]], data[0]);
+ else
+ print_string(PRINT_JSON, "value", "%d", data[0]);
+ }else{
+ if (data[0] < ARRAY_SIZE(ht_secondary_offset))
+ printf(" %s (%d)\n", ht_secondary_offset[data[0]], data[0]);
+ else
+ printf(" %d\n", data[0]);
+ }
}

-static void print_bss_load(const uint8_t type, uint8_t len, const uint8_t *data,
+static void print_bss_load(const uint8_t type, uint8_t len, const uint8_t *_data,
const struct print_ies_data *ie_buffer)
{
- printf("\n");
- printf("\t\t * station count: %d\n", (data[1] << 8) | data[0]);
- printf("\t\t * channel utilisation: %d/255\n", data[2]);
- printf("\t\t * available admission capacity: %d [*32us]\n", (data[4] << 8) | data[3]);
+ const int8_t *data = (int8_t *)_data;
+ if(iw_json){
+ print_int(PRINT_JSON, "station count", "%d", (data[1] << 8) | data[0]);
+ print_int(PRINT_JSON, "channel utilisation", "%d", data[2]);
+ print_int(PRINT_JSON, "available admission capacity", "%d", (data[4] << 8) | data[3]);
+ }else{
+ printf("\n");
+ printf("\t\t * station count: %d\n", (data[1] << 8) | data[0]);
+ printf("\t\t * channel utilisation: %d/255\n", data[2]);
+ printf("\t\t * available admission capacity: %d [*32us]\n", (data[4] << 8) | data[3]);
+ }
}

static void print_mesh_conf(const uint8_t type, uint8_t len,
const uint8_t *data,
const struct print_ies_data *ie_buffer)
{
- printf("\n");
- printf("\t\t * Active Path Selection Protocol ID: %d\n", data[0]);
- printf("\t\t * Active Path Selection Metric ID: %d\n", data[1]);
- printf("\t\t * Congestion Control Mode ID: %d\n", data[2]);
- printf("\t\t * Synchronization Method ID: %d\n", data[3]);
- printf("\t\t * Authentication Protocol ID: %d\n", data[4]);
- printf("\t\t * Mesh Formation Info:\n");
- printf("\t\t\t Number of Peerings: %d\n", (data[5] & 0x7E) >> 1);
- if (data[5] & 0x01)
- printf("\t\t\t Connected to Mesh Gate\n");
- if (data[5] & 0x80)
- printf("\t\t\t Connected to AS\n");
- printf("\t\t * Mesh Capability\n");
- if (data[6] & 0x01)
- printf("\t\t\t Accepting Additional Mesh Peerings\n");
- if (data[6] & 0x02)
- printf("\t\t\t MCCA Supported\n");
- if (data[6] & 0x04)
- printf("\t\t\t MCCA Enabled\n");
- if (data[6] & 0x08)
- printf("\t\t\t Forwarding\n");
- if (data[6] & 0x10)
- printf("\t\t\t MBCA Supported\n");
- if (data[6] & 0x20)
- printf("\t\t\t TBTT Adjusting\n");
- if (data[6] & 0x40)
- printf("\t\t\t Mesh Power Save Level\n");
+ if(iw_json){
+ print_string(PRINT_JSON, "active_path_selection_protocol_id", "%d", data[0]);
+ print_string(PRINT_JSON, "active_path_selection_metric_id", "%d", data[1]);
+ print_string(PRINT_JSON, "congestion_control_mode_id", "%d", data[2]);
+ print_string(PRINT_JSON, "synchronization_method_id", "%d", data[3]);
+ print_string(PRINT_JSON, "authentication_protocol_id", "%d", data[4]);
+ open_json_array("mesh_formation_info", NULL);
+ print_string(PRINT_JSON, "number_of_peerings", "%d", (data[5] & 0x7E) >> 1);
+ if (data[5] & 0x01)
+ print_string(PRINT_JSON, NULL, "Connected to Mesh Gate");
+ if (data[5] & 0x80)
+ print_string(PRINT_JSON, NULL, "Connected to AS");
+ close_json_array(NULL);
+ open_json_array("mesh_capability", NULL);
+ if (data[6] & 0x01)
+ print_string(PRINT_JSON, NULL, "Accepting Additional Mesh Peerings");
+ if (data[6] & 0x02)
+ print_string(PRINT_JSON, NULL, "MCCA Supported");
+ if (data[6] & 0x04)
+ print_string(PRINT_JSON, NULL, "MCCA Enabled");
+ if (data[6] & 0x08)
+ print_string(PRINT_JSON, NULL, "Forwarding");
+ if (data[6] & 0x10)
+ print_string(PRINT_JSON, NULL, "MBCA Supported");
+ if (data[6] & 0x20)
+ print_string(PRINT_JSON, NULL, "TBTT Adjusting");
+ if (data[6] & 0x40)
+ print_string(PRINT_JSON, NULL, "Mesh Power Save Level");
+ close_json_array(NULL);
+ }else{
+ printf("\n");
+ printf("\t\t * Active Path Selection Protocol ID: %d\n", data[0]);
+ printf("\t\t * Active Path Selection Metric ID: %d\n", data[1]);
+ printf("\t\t * Congestion Control Mode ID: %d\n", data[2]);
+ printf("\t\t * Synchronization Method ID: %d\n", data[3]);
+ printf("\t\t * Authentication Protocol ID: %d\n", data[4]);
+ printf("\t\t * Mesh Formation Info:\n");
+ printf("\t\t\t Number of Peerings: %d\n", (data[5] & 0x7E) >> 1);
+ if (data[5] & 0x01)
+ printf("\t\t\t Connected to Mesh Gate\n");
+ if (data[5] & 0x80)
+ printf("\t\t\t Connected to AS\n");
+ printf("\t\t * Mesh Capability\n");
+ if (data[6] & 0x01)
+ printf("\t\t\t Accepting Additional Mesh Peerings\n");
+ if (data[6] & 0x02)
+ printf("\t\t\t MCCA Supported\n");
+ if (data[6] & 0x04)
+ printf("\t\t\t MCCA Enabled\n");
+ if (data[6] & 0x08)
+ printf("\t\t\t Forwarding\n");
+ if (data[6] & 0x10)
+ printf("\t\t\t MBCA Supported\n");
+ if (data[6] & 0x20)
+ printf("\t\t\t TBTT Adjusting\n");
+ if (data[6] & 0x40)
+ printf("\t\t\t Mesh Power Save Level\n");
+ }
}

struct ie_print {
@@ -1682,31 +2067,54 @@ struct ie_print {
};

static void print_ie(const struct ie_print *p, const uint8_t type, uint8_t len,
- const uint8_t *data,
- const struct print_ies_data *ie_buffer)
+ const uint8_t *data,
+ const struct print_ies_data *ie_buffer)
{
- int i;
-
- if (!p->print)
- return;
-
- printf("\t%s:", p->name);
- if (len < p->minlen || len > p->maxlen) {
- if (len > 1) {
- printf(" <invalid: %d bytes:", len);
- for (i = 0; i < len; i++)
- printf(" %.02x", data[i]);
- printf(">\n");
- } else if (len)
- printf(" <invalid: 1 byte: %.02x>\n", data[0]);
- else
- printf(" <invalid: no data>\n");
- return;
- }
-
- p->print(type, len, data, ie_buffer);
+ int i;
+
+ if (!p->print)
+ return;
+
+ if (iw_json) {
+ open_json_object(p->name);
+ if (len < p->minlen || len > p->maxlen) {
+ print_int(PRINT_JSON, "invalid_bytes", "%d", len);
+ if (len > 1) {
+ open_json_array("data", NULL);
+ for (i = 0; i < len; i++)
+ print_string(PRINT_JSON, NULL, "%.02x", data[i]);
+ close_json_array(NULL);
+ } else if (len) {
+ print_string(PRINT_JSON, "invalid_1_byte", "%d", data[0]);
+ } else {
+ print_bool(PRINT_JSON, "no_data", "%s", true);
+ }
+ } else {
+ // print callback
+ p->print(type, len, data, ie_buffer);
+ }
+ close_json_object();
+ } else {
+ // text output
+ printf("\t%s:", p->name);
+ if (len < p->minlen || len > p->maxlen) {
+ if (len > 1) {
+ printf(" <invalid: %d bytes:", len);
+ for (i = 0; i < len; i++)
+ printf(" %.02x", data[i]);
+ printf(">\n");
+ } else if (len)
+ printf(" <invalid: 1 byte: %.02x>\n", data[0]);
+ else
+ printf(" <invalid: no data>\n");
+ } else {
+ // print callback
+ p->print(type, len, data, ie_buffer);
+ }
+ }
}

+
#define PRINT_IGN { \
.name = "IGNORE", \
.print = NULL, \
@@ -1765,41 +2173,66 @@ static bool print_wifi_wmm_param(const uint8_t *data, uint8_t len)
int i;
static const char *aci_tbl[] = { "BE", "BK", "VI", "VO" };

- if (len < 19)
- goto invalid;
-
- if (data[0] != 1) {
- printf("Parameter: not version 1: ");
+ if (len < 19){
+ if(!iw_json) printf("invalid: ");
return false;
}

- printf("\t * Parameter version 1");

- data++;
+ if(iw_json) {
+ print_int(PRINT_JSON, "version", "%d", data[0]);

- if (data[0] & 0x80)
- printf("\n\t\t * u-APSD");
+ if (data[0] != 1) {
+ return false;
+ }

- data += 2;
+ data++;

- for (i = 0; i < 4; i++) {
- printf("\n\t\t * %s:", aci_tbl[(data[0] >> 5) & 3]);
- if (data[0] & 0x10)
- printf(" acm");
- printf(" CW %d-%d", (1 << (data[1] & 0xf)) - 1,
- (1 << (data[1] >> 4)) - 1);
- printf(", AIFSN %d", data[0] & 0xf);
- if (data[2] | data[3])
- printf(", TXOP %d usec", (data[2] + (data[3] << 8)) * 32);
- data += 4;
- }
+ if (data[0] & 0x80){
+ print_bool(PRINT_JSON, "u-APSD", "%s", true);
+ }
+ data += 2;

- printf("\n");
- return true;
+ for (i = 0; i < 4; i++) {
+ open_json_object("%s", aci_tbl[(data[0] >> 5) & 3]);
+ if (data[0] & 0x10) print_bool(PRINT_JSON, "acm", "%s", true);
+ print_string(PRINT_JSON, "CW", "%d-%d", (1 << (data[1] & 0xf)) - 1, (1 << (data[1] >> 4)) - 1);
+ print_string(PRINT_JSON, "AIFSN", "%d", data[0] & 0xf);

- invalid:
- printf("invalid: ");
- return false;
+ if (data[2] | data[3]) print_int(PRINT_JSON, "TXOP_usec", "%d", (data[2] + (data[3] << 8)) * 32);
+ data += 4;
+ close_json_object();
+ }
+ }else{
+ if (data[0] != 1) {
+ printf("Parameter: not version 1: ");
+ return false;
+ }
+
+ printf("\t * Parameter version 1");
+
+ data++;
+
+ if (data[0] & 0x80)
+ printf("\n\t\t * u-APSD");
+
+ data += 2;
+
+ for (i = 0; i < 4; i++) {
+ printf("\n\t\t * %s:", aci_tbl[(data[0] >> 5) & 3]);
+ if (data[0] & 0x10)
+ printf(" acm");
+ printf(" CW %d-%d", (1 << (data[1] & 0xf)) - 1,
+ (1 << (data[1] >> 4)) - 1);
+ printf(", AIFSN %d", data[0] & 0xf);
+ if (data[2] | data[3])
+ printf(", TXOP %d usec", (data[2] + (data[3] << 8)) * 32);
+ data += 4;
+ }
+
+ printf("\n");
+ }
+ return true;
}

static void print_wifi_wmm(const uint8_t type, uint8_t len, const uint8_t *data,
@@ -1858,209 +2291,217 @@ static void print_wifi_wps(const uint8_t type, uint8_t len, const uint8_t *data,
break;

switch (subtype) {
- case 0x104a:
- tab_on_first(&first);
- if (sublen < 1) {
- printf("\t * Version: (invalid "
- "length %d)\n", sublen);
+ case 0x104a:
+ tab_on_first(&first);
+ if (sublen < 1) {
+ iw_json ? print_string(PRINT_JSON, "version", "%d", sublen) : printf("\t * Version: (invalid length %d)\n", sublen);
+ break;
+ }
+ iw_json ? print_string(PRINT_JSON, "version", "%d.%d", data[4] >> 4, data[4] & 0xF) : printf("\t * Version: %d.%d\n", data[4] >> 4, data[4] & 0xF);
break;
- }
- printf("\t * Version: %d.%d\n", data[4] >> 4, data[4] & 0xF);
- break;
- case 0x1011:
- tab_on_first(&first);
- printf("\t * Device name: %.*s\n", sublen, data + 4);
- break;
- case 0x1012: {
- uint16_t id;
- tab_on_first(&first);
- if (sublen != 2) {
- printf("\t * Device Password ID: (invalid length %d)\n",
- sublen);
+ case 0x1011:
+ tab_on_first(&first);
+ iw_json ? print_string(PRINT_JSON, "device name", "%.*s", sublen, data + 4) : printf("\t * Device name: %.*s\n", sublen, data + 4);
+ break;
+ case 0x1012: {
+ uint16_t id;
+ tab_on_first(&first);
+ if (sublen != 2) {
+ iw_json ? print_string(PRINT_JSON, "device_password_id", "%d", sublen) : printf("\t * Device Password ID: (invalid length %d)\n", sublen);
+ break;
+ }
+ id = data[4] << 8 | data[5];
+ iw_json ? print_string(PRINT_JSON, "device_password_id", "%u (%s)", id, wifi_wps_dev_passwd_id(id)) : printf("\t * Device Password ID: %u (%s)\n", id, wifi_wps_dev_passwd_id(id));
break;
}
- id = data[4] << 8 | data[5];
- printf("\t * Device Password ID: %u (%s)\n",
- id, wifi_wps_dev_passwd_id(id));
- break;
- }
- case 0x1021:
- tab_on_first(&first);
- printf("\t * Manufacturer: %.*s\n", sublen, data + 4);
- break;
- case 0x1023:
- tab_on_first(&first);
- printf("\t * Model: %.*s\n", sublen, data + 4);
- break;
- case 0x1024:
- tab_on_first(&first);
- printf("\t * Model Number: %.*s\n", sublen, data + 4);
- break;
- case 0x103b: {
- __u8 val;
+ case 0x1021:
+ tab_on_first(&first);
+ iw_json ? print_string(PRINT_JSON, "manufacturer", "%.*s", sublen, data + 4) : printf("\t * Manufacturer: %.*s\n", sublen, data + 4);
+ break;
+ case 0x1023:
+ tab_on_first(&first);
+ iw_json ? print_string(PRINT_JSON, "model", "%.*s", sublen, data + 4) : printf("\t * Model: %.*s\n", sublen, data + 4);
+ break;
+ case 0x1024:
+ tab_on_first(&first);
+ iw_json ? print_string(PRINT_JSON, "model_number", "%.*s\n", sublen, data + 4) : printf("\t * Model Number: %.*s\n", sublen, data + 4);
+ break;
+ case 0x103b: {
+ __u8 val;

- if (sublen < 1) {
- printf("\t * Response Type: (invalid length %d)\n",
- sublen);
+ if (sublen < 1) {
+ iw_json ? print_int(PRINT_JSON, "response_type_invalid_length", "%d", sublen) : printf("\t * Response Type: (invalid length %d)\n", sublen);
+ break;
+ }
+ val = data[4];
+ tab_on_first(&first);
+ iw_json ? print_string(PRINT_JSON, "response_type", "%d%s", val, val == 3 ? " (AP)" : "") : printf("\t * Response Type: %d%s\n", val, val == 3 ? " (AP)" : "");
break;
}
- val = data[4];
- tab_on_first(&first);
- printf("\t * Response Type: %d%s\n",
- val, val == 3 ? " (AP)" : "");
- break;
- }
- case 0x103c: {
- __u8 val;
+ case 0x103c: {
+ __u8 val;

- if (sublen < 1) {
- printf("\t * RF Bands: (invalid length %d)\n",
- sublen);
+ if (sublen < 1) {
+ iw_json ? print_int(PRINT_JSON, "rf_bands_invalid_length", "%d", sublen) : printf("\t * RF Bands: (invalid length %d)\n", sublen);
+ break;
+ }
+ val = data[4];
+ tab_on_first(&first);
+ iw_json ? print_string(PRINT_JSON, "rf_bands", "0x%x", val) : printf("\t * RF Bands: 0x%x\n", val);
break;
}
- val = data[4];
- tab_on_first(&first);
- printf("\t * RF Bands: 0x%x\n", val);
- break;
- }
- case 0x1041: {
- __u8 val;
+ case 0x1041: {
+ __u8 val;

- if (sublen < 1) {
- printf("\t * Selected Registrar: (invalid length %d)\n",
- sublen);
+ if (sublen < 1) {
+ iw_json ? print_string(PRINT_JSON, "selected_registrar_invalid_length", "%d", sublen) : printf("\t * Selected Registrar: (invalid length %d)\n", sublen);
+ break;
+ }
+ val = data[4];
+ tab_on_first(&first);
+ iw_json ? print_string(PRINT_JSON, "selected_registrar", "0x%x", val) : printf("\t * Selected Registrar: 0x%x\n", val);
break;
}
- val = data[4];
- tab_on_first(&first);
- printf("\t * Selected Registrar: 0x%x\n", val);
- break;
- }
- case 0x1042:
- tab_on_first(&first);
- printf("\t * Serial Number: %.*s\n", sublen, data + 4);
- break;
- case 0x1044: {
- __u8 val;
+ case 0x1042:
+ tab_on_first(&first);
+ iw_json ? print_string(PRINT_JSON, "serial_number", "%.*s", sublen, data + 4) : printf("\t * Serial Number: %.*s\n", sublen, data + 4);
+ break;
+ case 0x1044: {
+ __u8 val;

- if (sublen < 1) {
- printf("\t * Wi-Fi Protected Setup State: (invalid length %d)\n",
- sublen);
+ if (sublen < 1) {
+ iw_json ? print_string(PRINT_JSON, "wi-fi_protected_setup_state_invalid_length", "%d", sublen) : printf("\t * Wi-Fi Protected Setup State: (invalid length %d)\n", sublen);
+ break;
+ }
+ val = data[4];
+ tab_on_first(&first);
+ iw_json ? print_string(PRINT_JSON, "wi-fi_protected_setup_state", "%d%s%s", val, val == 1 ? " (Unconfigured)" : "", val == 2 ? " (Configured)" : "") : printf("\t * Wi-Fi Protected Setup State: %d%s%s\n", val, val == 1 ? " (Unconfigured)" : "", val == 2 ? " (Configured)" : "");
break;
}
- val = data[4];
- tab_on_first(&first);
- printf("\t * Wi-Fi Protected Setup State: %d%s%s\n",
- val,
- val == 1 ? " (Unconfigured)" : "",
- val == 2 ? " (Configured)" : "");
- break;
- }
- case 0x1047:
- tab_on_first(&first);
- printf("\t * UUID: ");
- if (sublen != 16) {
- printf("(invalid, length=%d)\n", sublen);
+ case 0x1047:
+ tab_on_first(&first);
+ if(!iw_json) printf("\t * UUID: ");
+ if (sublen != 16) {
+ iw_json ? print_int(PRINT_JSON, "uuid_invalid_length", "%d", sublen) : printf("(invalid, length=%d)\n", sublen);
+ break;
+ }
+ iw_json ? print_string(PRINT_JSON, "uuid", "%02x%02x-%02x%02x%02x%02x%02x%02x",
+ "%02x%02x-%02x%02x%02x%02x%02x%02x",
+ data[4], data[5], data[6], data[7],
+ data[8], data[9], data[10], data[11],
+ data[12], data[13], data[14], data[15],
+ data[16], data[17], data[18], data[19]) : printf("%02x%02x%02x%02x-%02x%02x-%02x%02x-"
+ "%02x%02x-%02x%02x%02x%02x%02x%02x\n",
+ data[4], data[5], data[6], data[7],
+ data[8], data[9], data[10], data[11],
+ data[12], data[13], data[14], data[15],
+ data[16], data[17], data[18], data[19]);
break;
- }
- printf("%02x%02x%02x%02x-%02x%02x-%02x%02x-"
- "%02x%02x-%02x%02x%02x%02x%02x%02x\n",
- data[4], data[5], data[6], data[7],
- data[8], data[9], data[10], data[11],
- data[12], data[13], data[14], data[15],
- data[16], data[17], data[18], data[19]);
- break;
- case 0x1049:
- tab_on_first(&first);
- if (sublen == 6 &&
- data[4] == 0x00 &&
- data[5] == 0x37 &&
- data[6] == 0x2a &&
- data[7] == 0x00 &&
- data[8] == 0x01) {
- uint8_t v2 = data[9];
- printf("\t * Version2: %d.%d\n", v2 >> 4, v2 & 0xf);
- } else {
- printf("\t * Unknown vendor extension. len=%u\n",
- sublen);
- }
- break;
- case 0x1054: {
- tab_on_first(&first);
- if (sublen != 8) {
- printf("\t * Primary Device Type: (invalid length %d)\n",
- sublen);
+ case 0x1049:
+ tab_on_first(&first);
+ if (sublen == 6 &&
+ data[4] == 0x00 &&
+ data[5] == 0x37 &&
+ data[6] == 0x2a &&
+ data[7] == 0x00 &&
+ data[8] == 0x01) {
+ uint8_t v2 = data[9];
+ iw_json ? print_string(PRINT_JSON, "version2", "%d.%d", v2 >> 4, v2 & 0xf) : printf("\t * Version2: %d.%d\n", v2 >> 4, v2 & 0xf);
+ } else {
+ iw_json ? print_string(PRINT_JSON, "unknown_vendor_extension_length", "%u", sublen) : printf("\t * Unknown vendor extension. len=%u\n", sublen);
+ }
break;
- }
- printf("\t * Primary Device Type: "
- "%u-%02x%02x%02x%02x-%u\n",
- data[4] << 8 | data[5],
- data[6], data[7], data[8], data[9],
- data[10] << 8 | data[11]);
- break;
- }
- case 0x1057: {
- __u8 val;
- tab_on_first(&first);
- if (sublen < 1) {
- printf("\t * AP setup locked: (invalid length %d)\n",
- sublen);
+ case 0x1054: {
+ tab_on_first(&first);
+ if (sublen != 8) {
+ iw_json ? print_string(PRINT_JSON, "primary_device_type_invalid_length", "%d", sublen) : printf("\t * Primary Device Type: (invalid length %d)\n", sublen);
+ break;
+ }
+ iw_json ? print_string(PRINT_JSON, "primary_device_type",
+ "%u-%02x%02x%02x%02x-%u",
+ data[4] << 8 | data[5],
+ data[6], data[7], data[8], data[9],
+ data[10] << 8 | data[11]) : printf("\t * Primary Device Type: "
+ "%u-%02x%02x%02x%02x-%u\n",
+ data[4] << 8 | data[5],
+ data[6], data[7], data[8], data[9],
+ data[10] << 8 | data[11]);
break;
}
- val = data[4];
- printf("\t * AP setup locked: 0x%.2x\n", val);
- break;
- }
- case 0x1008:
- case 0x1053: {
- __u16 meth;
- bool comma;
-
- if (sublen < 2) {
- printf("\t * Config methods: (invalid length %d)\n",
- sublen);
+ case 0x1057: {
+ __u8 val;
+ tab_on_first(&first);
+ if (sublen < 1) {
+ iw_json ? print_string(PRINT_JSON, "ap_setup_locked_invalid_length", "%d", sublen) : printf("\t * AP setup locked: (invalid length %d)\n", sublen);
+ break;
+ }
+ val = data[4];
+ iw_json ? print_string(PRINT_JSON, "ap_setup_locked", "0x%.2x", val) : printf("\t * AP setup locked: 0x%.2x\n", val);
break;
}
- meth = (data[4] << 8) + data[5];
- comma = false;
- tab_on_first(&first);
- printf("\t * %sConfig methods:",
- subtype == 0x1053 ? "Selected Registrar ": "");
+ case 0x1008:
+ case 0x1053: {
+ __u16 meth;
+ bool comma;
+
+ if (sublen < 2) {
+ iw_json ? print_string(PRINT_JSON, "config_methods_invalid_length", "%d", sublen) : printf("\t * Config methods: (invalid length %d)\n", sublen);
+ break;
+ }
+ meth = (data[4] << 8) + data[5];
+ comma = false;
+ tab_on_first(&first);
+ if(iw_json){
+ print_bool(PRINT_JSON, "config_methods_selected_registrar", "%s", true);
+ open_json_array("config_methods", NULL);
+ }else{
+ printf("\t * %sConfig methods:", subtype == 0x1053 ? "Selected Registrar ": "");
+ }
#define T(bit, name) do { \
if (meth & (1<<bit)) { \
- if (comma) \
- printf(","); \
- comma = true; \
- printf(" " name); \
- } } while (0)
- T(0, "USB");
- T(1, "Ethernet");
- T(2, "Label");
- T(3, "Display");
- T(4, "Ext. NFC");
- T(5, "Int. NFC");
- T(6, "NFC Intf.");
- T(7, "PBC");
- T(8, "Keypad");
- printf("\n");
- break;
-#undef T
- }
- default: {
- const __u8 *subdata = data + 4;
- __u16 tmplen = sublen;
+ if(iw_json){ \
+ print_string(PRINT_JSON, NULL, name); \
+ }else{ \
+ if (comma) \
+ printf(","); \
+ comma = true; \
+ printf(" " name); \
+ } \
+ } \
+} while (0)

- tab_on_first(&first);
- printf("\t * Unknown TLV (%#.4x, %d bytes):",
- subtype, tmplen);
- while (tmplen) {
- printf(" %.2x", *subdata);
- subdata++;
- tmplen--;
+ T(0, "USB");
+ T(1, "Ethernet");
+ T(2, "Label");
+ T(3, "Display");
+ T(4, "Ext. NFC");
+ T(5, "Int. NFC");
+ T(6, "NFC Intf.");
+ T(7, "PBC");
+ T(8, "Keypad");
+ iw_json ? close_json_array(NULL) : printf("\n");
+ break;
+#undef T
+ } //case 0x1053
+ default: {
+ const __u8 *subdata = data + 4;
+ __u16 tmplen = sublen;
+
+ tab_on_first(&first);
+ if(iw_json){
+ print_string(PRINT_JSON, "unknown_tlv", "%#.4x, %d bytes", subtype, tmplen);
+ open_json_array("unknown_tlv_data", NULL);
+ }else{
+ printf("\t * Unknown TLV (%#.4x, %d bytes):", subtype, tmplen);
+ }
+ while (tmplen) {
+ iw_json ? print_string(PRINT_JSON, NULL, "%.2x", *subdata) : printf(" %.2x", *subdata);
+ subdata++;
+ tmplen--;
+ }
+ iw_json ? close_json_array(NULL) : printf("\n");
+ break;
}
- printf("\n");
- break;
- }
}

data += sublen + 4;
@@ -2068,13 +2509,19 @@ static void print_wifi_wps(const uint8_t type, uint8_t len, const uint8_t *data,
}

if (len != 0) {
- printf("\t\t * bogus tail data (%d):", len);
+ if(iw_json){
+ print_string(PRINT_JSON, " bogus_tail_data_length", "%d", len);
+ open_json_array("bogus_tail_data", NULL);
+ } else{
+ printf("\t\t * bogus tail data (%d):", len);
+ }
+
while (len) {
- printf(" %.2x", *data);
+ iw_json ? print_string(PRINT_JSON, NULL, "%.2x", *data) : printf(" %.2x", *data);
data++;
len--;
}
- printf("\n");
+ iw_json ? close_json_array(NULL) : printf("\n");
}
}

@@ -2217,31 +2664,60 @@ static void print_vendor(unsigned char len, unsigned char *data,
bool unknown, enum print_ie_type ptype)
{
int i;
-
if (len < 3) {
- printf("\tVendor specific: <too short> data:");
- for(i = 0; i < len; i++)
- printf(" %.02x", data[i]);
- printf("\n");
- return;
+ if(iw_json){
+ open_json_array("vendor specific too short", NULL);
+ for(i = 0; i < len; i++){
+ print_int(PRINT_JSON, NULL, "%.02x", data[i]);
+ }
+ close_json_array(NULL);
+ return;
+ }else{
+ printf("\tVendor specific: <too short> data:");
+ for(i = 0; i < len; i++)
+ printf(" %.02x", data[i]);
+ printf("\n");
+ return;
+ }
}
+ if(iw_json){
+ if (len >= 4 && memcmp(data, ms_oui, 3) == 0) {
+ if (data[3] < ARRAY_SIZE(wifiprinters) &&
+ wifiprinters[data[3]].name &&
+ wifiprinters[data[3]].flags & BIT(ptype)) {
+ print_ie(&wifiprinters[data[3]],
+ data[3], len - 4, data + 4,
+ NULL);
+ return;
+ }
+ if (!unknown) return;

- if (len >= 4 && memcmp(data, ms_oui, 3) == 0) {
- if (data[3] < ARRAY_SIZE(wifiprinters) &&
- wifiprinters[data[3]].name &&
- wifiprinters[data[3]].flags & BIT(ptype)) {
- print_ie(&wifiprinters[data[3]],
- data[3], len - 4, data + 4,
- NULL);
+ print_string(PRINT_JSON,"MS/WiFi", "%#.2x", data[3]);
+ open_json_array("data", NULL);
+ for(i = 0; i < len - 4; i++){
+ print_float(PRINT_JSON, NULL, "%.02x", data[i + 4]);
+ }
+ close_json_array(NULL);
return;
}
- if (!unknown)
+ }else{
+ if (len >= 4 && memcmp(data, ms_oui, 3) == 0) {
+ if (data[3] < ARRAY_SIZE(wifiprinters) &&
+ wifiprinters[data[3]].name &&
+ wifiprinters[data[3]].flags & BIT(ptype)) {
+ print_ie(&wifiprinters[data[3]],
+ data[3], len - 4, data + 4,
+ NULL);
+ return;
+ }
+ if (!unknown)
+ return;
+ printf("\tMS/WiFi %#.2x, data:", data[3]);
+ for(i = 0; i < len - 4; i++)
+ printf(" %.02x", data[i + 4]);
+ printf("\n");
return;
- printf("\tMS/WiFi %#.2x, data:", data[3]);
- for(i = 0; i < len - 4; i++)
- printf(" %.02x", data[i + 4]);
- printf("\n");
- return;
+ }
}

if (len >= 4 && memcmp(data, wfa_oui, 3) == 0) {
@@ -2275,7 +2751,6 @@ static void print_vendor(unsigned char len, unsigned char *data,
static void print_he_capa(const uint8_t type, uint8_t len, const uint8_t *data,
const struct print_ies_data *ie_buffer)
{
- printf("\n");
print_he_capability(data, len);
}

@@ -2345,66 +2820,93 @@ void print_ies(unsigned char *ie, int ielen, bool unknown,

static void print_capa_dmg(__u16 capa)
{
- switch (capa & WLAN_CAPABILITY_DMG_TYPE_MASK) {
- case WLAN_CAPABILITY_DMG_TYPE_AP:
- printf(" DMG_ESS");
- break;
- case WLAN_CAPABILITY_DMG_TYPE_PBSS:
- printf(" DMG_PCP");
- break;
- case WLAN_CAPABILITY_DMG_TYPE_IBSS:
- printf(" DMG_IBSS");
- break;
- }
+ if(iw_json){
+ switch (capa & WLAN_CAPABILITY_DMG_TYPE_MASK) {
+ case WLAN_CAPABILITY_DMG_TYPE_AP:
+ print_string(PRINT_JSON, NULL, "DMG_ESS");
+ break;
+ case WLAN_CAPABILITY_DMG_TYPE_PBSS:
+ print_string(PRINT_JSON, NULL, "DMG_PCP");
+ break;
+ case WLAN_CAPABILITY_DMG_TYPE_IBSS:
+ print_string(PRINT_JSON, NULL, "DMG_IBSS");
+ break;
+ }
+
+ if (capa & WLAN_CAPABILITY_DMG_CBAP_ONLY)
+ print_string(PRINT_JSON, NULL, "CBAP_Only");
+ if (capa & WLAN_CAPABILITY_DMG_CBAP_SOURCE)
+ print_string(PRINT_JSON, NULL, "CBAP_Src");
+ if (capa & WLAN_CAPABILITY_DMG_PRIVACY)
+ print_string(PRINT_JSON, NULL, "Privacy");
+ if (capa & WLAN_CAPABILITY_DMG_ECPAC)
+ print_string(PRINT_JSON, NULL, "ECPAC");
+ if (capa & WLAN_CAPABILITY_DMG_SPECTRUM_MGMT)
+ print_string(PRINT_JSON, NULL, "SpectrumMgmt");
+ if (capa & WLAN_CAPABILITY_DMG_RADIO_MEASURE)
+ print_string(PRINT_JSON, NULL, "RadioMeasure");
+ }else{
+ switch (capa & WLAN_CAPABILITY_DMG_TYPE_MASK) {
+ case WLAN_CAPABILITY_DMG_TYPE_AP:
+ printf(" DMG_ESS");
+ break;
+ case WLAN_CAPABILITY_DMG_TYPE_PBSS:
+ printf(" DMG_PCP");
+ break;
+ case WLAN_CAPABILITY_DMG_TYPE_IBSS:
+ printf(" DMG_IBSS");
+ break;
+ }

- if (capa & WLAN_CAPABILITY_DMG_CBAP_ONLY)
- printf(" CBAP_Only");
- if (capa & WLAN_CAPABILITY_DMG_CBAP_SOURCE)
- printf(" CBAP_Src");
- if (capa & WLAN_CAPABILITY_DMG_PRIVACY)
- printf(" Privacy");
- if (capa & WLAN_CAPABILITY_DMG_ECPAC)
- printf(" ECPAC");
- if (capa & WLAN_CAPABILITY_DMG_SPECTRUM_MGMT)
- printf(" SpectrumMgmt");
- if (capa & WLAN_CAPABILITY_DMG_RADIO_MEASURE)
- printf(" RadioMeasure");
+ if (capa & WLAN_CAPABILITY_DMG_CBAP_ONLY)
+ printf(" CBAP_Only");
+ if (capa & WLAN_CAPABILITY_DMG_CBAP_SOURCE)
+ printf(" CBAP_Src");
+ if (capa & WLAN_CAPABILITY_DMG_PRIVACY)
+ printf(" Privacy");
+ if (capa & WLAN_CAPABILITY_DMG_ECPAC)
+ printf(" ECPAC");
+ if (capa & WLAN_CAPABILITY_DMG_SPECTRUM_MGMT)
+ printf(" SpectrumMgmt");
+ if (capa & WLAN_CAPABILITY_DMG_RADIO_MEASURE)
+ printf(" RadioMeasure");
+ }
}

static void print_capa_non_dmg(__u16 capa)
{
if (capa & WLAN_CAPABILITY_ESS)
- printf(" ESS");
+ iw_json ? print_string(PRINT_JSON, NULL, "ESS") : printf(" ESS");
if (capa & WLAN_CAPABILITY_IBSS)
- printf(" IBSS");
+ iw_json ? print_string(PRINT_JSON, NULL, "IBSS") : printf(" IBSS");
if (capa & WLAN_CAPABILITY_CF_POLLABLE)
- printf(" CfPollable");
+ iw_json ? print_string(PRINT_JSON, NULL, "CfPollable") : printf(" CfPollable");
if (capa & WLAN_CAPABILITY_CF_POLL_REQUEST)
- printf(" CfPollReq");
+ iw_json ? print_string(PRINT_JSON, NULL, "CfPollReq") : printf(" CfPollReq");
if (capa & WLAN_CAPABILITY_PRIVACY)
- printf(" Privacy");
+ iw_json ? print_string(PRINT_JSON, NULL, "Privacy") : printf(" Privacy");
if (capa & WLAN_CAPABILITY_SHORT_PREAMBLE)
- printf(" ShortPreamble");
+ iw_json ? print_string(PRINT_JSON, NULL, "ShortPreamble") : printf(" ShortPreamble");
if (capa & WLAN_CAPABILITY_PBCC)
- printf(" PBCC");
+ iw_json ? print_string(PRINT_JSON, NULL, "PBCC") : printf(" PBCC");
if (capa & WLAN_CAPABILITY_CHANNEL_AGILITY)
- printf(" ChannelAgility");
+ iw_json ? print_string(PRINT_JSON, NULL, "ChannelAgility") : printf(" ChannelAgility");
if (capa & WLAN_CAPABILITY_SPECTRUM_MGMT)
- printf(" SpectrumMgmt");
+ iw_json ? print_string(PRINT_JSON, NULL, "SpectrumMgmt") : printf(" SpectrumMgmt");
if (capa & WLAN_CAPABILITY_QOS)
- printf(" QoS");
+ iw_json ? print_string(PRINT_JSON, NULL, "QoS") : printf(" QoS");
if (capa & WLAN_CAPABILITY_SHORT_SLOT_TIME)
- printf(" ShortSlotTime");
+ iw_json ? print_string(PRINT_JSON, NULL, "ShortSlotTime") : printf(" ShortSlotTime");
if (capa & WLAN_CAPABILITY_APSD)
- printf(" APSD");
+ iw_json ? print_string(PRINT_JSON, NULL, "APSD") : printf(" APSD");
if (capa & WLAN_CAPABILITY_RADIO_MEASURE)
- printf(" RadioMeasure");
+ iw_json ? print_string(PRINT_JSON, NULL, "RadioMeasure") : printf(" RadioMeasure");
if (capa & WLAN_CAPABILITY_DSSS_OFDM)
- printf(" DSSS-OFDM");
+ iw_json ? print_string(PRINT_JSON, NULL, "DSSS-OFDM") : printf(" DSSS-OFDM");
if (capa & WLAN_CAPABILITY_DEL_BACK)
- printf(" DelayedBACK");
+ iw_json ? print_string(PRINT_JSON, NULL, "DelayedBACK") : printf(" DelayedBACK");
if (capa & WLAN_CAPABILITY_IMM_BACK)
- printf(" ImmediateBACK");
+ iw_json ? print_string(PRINT_JSON, NULL, "ImmediateBACK") : printf(" ImmediateBACK");
}

static int print_bss_handler(struct nl_msg *msg, void *arg)
@@ -2433,108 +2935,225 @@ static int print_bss_handler(struct nl_msg *msg, void *arg)
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);

- if (!tb[NL80211_ATTR_BSS]) {
- fprintf(stderr, "bss info missing!\n");
- return NL_SKIP;
- }
- if (nla_parse_nested(bss, NL80211_BSS_MAX,
- tb[NL80211_ATTR_BSS],
- bss_policy)) {
- fprintf(stderr, "failed to parse nested attributes!\n");
- return NL_SKIP;
- }
+ if(iw_json){
+ if (!tb[NL80211_ATTR_BSS]) {
+ fprintf(stderr, "bss info missing!\n");
+ return NL_SKIP;
+ }
+ if (nla_parse_nested(bss, NL80211_BSS_MAX,
+ tb[NL80211_ATTR_BSS],
+ bss_policy)) {
+ fprintf(stderr, "failed to parse nested attributes!\n");
+ return NL_SKIP;
+ }

- if (!bss[NL80211_BSS_BSSID])
- return NL_SKIP;
+ if (!bss[NL80211_BSS_BSSID])
+ return NL_SKIP;

- mac_addr_n2a(mac_addr, nla_data(bss[NL80211_BSS_BSSID]));
- printf("BSS %s", mac_addr);
- if (tb[NL80211_ATTR_IFINDEX]) {
- if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), dev);
- printf("(on %s)", dev);
- }
+ mac_addr_n2a(mac_addr, nla_data(bss[NL80211_BSS_BSSID]));
+ open_json_object(mac_addr);

- if (bss[NL80211_BSS_STATUS]) {
- switch (nla_get_u32(bss[NL80211_BSS_STATUS])) {
- case NL80211_BSS_STATUS_AUTHENTICATED:
- printf(" -- authenticated");
- break;
- case NL80211_BSS_STATUS_ASSOCIATED:
- printf(" -- associated");
- break;
- case NL80211_BSS_STATUS_IBSS_JOINED:
- printf(" -- joined");
- break;
- default:
- printf(" -- unknown status: %d",
- nla_get_u32(bss[NL80211_BSS_STATUS]));
- break;
+ if (tb[NL80211_ATTR_IFINDEX]) {
+ if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), dev);
+ print_string(PRINT_JSON, "ifname", "%s", dev);
}
- }
- printf("\n");

- if (bss[NL80211_BSS_LAST_SEEN_BOOTTIME]) {
- unsigned long long bt;
- bt = (unsigned long long)nla_get_u64(bss[NL80211_BSS_LAST_SEEN_BOOTTIME]);
- printf("\tlast seen: %llu.%.3llus [boottime]\n", bt/1000000000, (bt%1000000000)/1000000);
- }
+ char buf[32] = {0};
+ if (bss[NL80211_BSS_STATUS]) {
+ switch (nla_get_u32(bss[NL80211_BSS_STATUS])) {
+ case NL80211_BSS_STATUS_AUTHENTICATED:
+ snprintf(buf, sizeof(buf), "authenticated");
+ break;
+ case NL80211_BSS_STATUS_ASSOCIATED:
+ snprintf(buf, sizeof(buf), "associated");
+ break;
+ case NL80211_BSS_STATUS_IBSS_JOINED:
+ snprintf(buf, sizeof(buf), "joined");
+ break;
+ default:
+ snprintf(buf, sizeof(buf), "unknown status: %d", nla_get_u32(bss[NL80211_BSS_STATUS]));
+ break;
+ }
+ }
+ print_string(PRINT_JSON, "status", "%s", buf);

- if (bss[NL80211_BSS_TSF]) {
- unsigned long long tsf;
- tsf = (unsigned long long)nla_get_u64(bss[NL80211_BSS_TSF]);
- printf("\tTSF: %llu usec (%llud, %.2lld:%.2llu:%.2llu)\n",
- tsf, tsf/1000/1000/60/60/24, (tsf/1000/1000/60/60) % 24,
- (tsf/1000/1000/60) % 60, (tsf/1000/1000) % 60);
- }
- if (bss[NL80211_BSS_FREQUENCY]) {
- int freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
- printf("\tfreq: %d\n", freq);
- if (freq > 45000)
- is_dmg = true;
- }
- if (bss[NL80211_BSS_BEACON_INTERVAL])
- printf("\tbeacon interval: %d TUs\n",
- nla_get_u16(bss[NL80211_BSS_BEACON_INTERVAL]));
- if (bss[NL80211_BSS_CAPABILITY]) {
- __u16 capa = nla_get_u16(bss[NL80211_BSS_CAPABILITY]);
- printf("\tcapability:");
- if (is_dmg)
- print_capa_dmg(capa);
- else
- print_capa_non_dmg(capa);
- printf(" (0x%.4x)\n", capa);
- }
- if (bss[NL80211_BSS_SIGNAL_MBM]) {
- int s = nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]);
- printf("\tsignal: %d.%.2d dBm\n", s/100, s%100);
- }
- if (bss[NL80211_BSS_SIGNAL_UNSPEC]) {
- unsigned char s = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]);
- printf("\tsignal: %d/100\n", s);
- }
- if (bss[NL80211_BSS_SEEN_MS_AGO]) {
- int age = nla_get_u32(bss[NL80211_BSS_SEEN_MS_AGO]);
- printf("\tlast seen: %d ms ago\n", age);
- }
+ if (bss[NL80211_BSS_LAST_SEEN_BOOTTIME]) {
+ unsigned long long bt;
+ bt = (unsigned long long)nla_get_u64(bss[NL80211_BSS_LAST_SEEN_BOOTTIME]);
+ print_string(PRINT_JSON, "last seen", "%llu.%.3llus [boottime]", bt/1000000000, (bt%1000000000)/1000000);
+ }

- if (bss[NL80211_BSS_INFORMATION_ELEMENTS] && show--) {
- struct nlattr *ies = bss[NL80211_BSS_INFORMATION_ELEMENTS];
- struct nlattr *bcnies = bss[NL80211_BSS_BEACON_IES];
-
- if (bss[NL80211_BSS_PRESP_DATA] ||
- (bcnies && (nla_len(ies) != nla_len(bcnies) ||
- memcmp(nla_data(ies), nla_data(bcnies),
- nla_len(ies)))))
- printf("\tInformation elements from Probe Response "
- "frame:\n");
- print_ies(nla_data(ies), nla_len(ies),
- params->unknown, params->type);
- }
- if (bss[NL80211_BSS_BEACON_IES] && show--) {
- printf("\tInformation elements from Beacon frame:\n");
- print_ies(nla_data(bss[NL80211_BSS_BEACON_IES]),
- nla_len(bss[NL80211_BSS_BEACON_IES]),
- params->unknown, params->type);
+ if (bss[NL80211_BSS_TSF]) {
+ unsigned long long tsf;
+ tsf = (unsigned long long)nla_get_u64(bss[NL80211_BSS_TSF]);
+
+ open_json_object("TSF");
+ print_u64(PRINT_JSON, "usec", "%llu", tsf);
+ print_u64(PRINT_JSON, "dd", "%llu", tsf/1000/1000/60/60/24);
+ print_u64(PRINT_JSON, "hh", "%llu", (tsf/1000/1000/60/60) % 24);
+ print_u64(PRINT_JSON, "mm", "%llu", (tsf/1000/1000/60) % 60);
+ print_u64(PRINT_JSON, "ss", "%llu", (tsf/1000/1000) % 60);
+ close_json_object();
+ }
+ if (bss[NL80211_BSS_FREQUENCY]) {
+ int freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
+ print_int(PRINT_JSON, "freq", "%d", freq);
+ if (freq > 45000) is_dmg = true;
+ }
+ if (bss[NL80211_BSS_BEACON_INTERVAL])
+ print_int(PRINT_JSON, "beacon_interval_tus", "%d", nla_get_u16(bss[NL80211_BSS_BEACON_INTERVAL]));
+ if (bss[NL80211_BSS_CAPABILITY]) {
+ __u16 capa = nla_get_u16(bss[NL80211_BSS_CAPABILITY]);
+ open_json_array("capability", NULL);
+ if (is_dmg){
+ print_capa_dmg(capa);
+ }else{
+ print_capa_non_dmg(capa);
+ }
+ close_json_array(NULL);
+
+ print_string(PRINT_JSON, "capability_raw", "0x%.4x", capa);
+ }
+ if (bss[NL80211_BSS_SIGNAL_MBM]) {
+ int s = nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]);
+ print_string(PRINT_JSON, "signal", "%d.%.2d dBm", s/100, s%100);
+ }
+ if (bss[NL80211_BSS_SIGNAL_UNSPEC]) {
+ unsigned char s = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]);
+ print_u8(PRINT_JSON, "signal_dbm", "%d/100", s);
+ }
+ if (bss[NL80211_BSS_SEEN_MS_AGO]) {
+ int age = nla_get_u32(bss[NL80211_BSS_SEEN_MS_AGO]);
+ print_int(PRINT_JSON, "last_seen_ms_ago", "%d", age);
+ }
+
+ if (bss[NL80211_BSS_INFORMATION_ELEMENTS] && show--) {
+ struct nlattr *ies = bss[NL80211_BSS_INFORMATION_ELEMENTS];
+ struct nlattr *bcnies = bss[NL80211_BSS_BEACON_IES];
+
+ if (bss[NL80211_BSS_PRESP_DATA] ||
+ (bcnies && (nla_len(ies) != nla_len(bcnies) ||
+ memcmp(nla_data(ies), nla_data(bcnies),
+ nla_len(ies))))){
+ open_json_object("bss_probe_resp_data_frame");
+ print_ies(nla_data(ies), nla_len(ies), params->unknown, params->type);
+ close_json_object();
+ }
+ }
+ if (bss[NL80211_BSS_BEACON_IES] && show--) {
+ open_json_object("bss_beacon_frame");
+ print_ies(nla_data(bss[NL80211_BSS_BEACON_IES]),
+ nla_len(bss[NL80211_BSS_BEACON_IES]),
+ params->unknown, params->type);
+ close_json_object();
+ }
+
+ close_json_object();
+ } else { //text output
+ if (!tb[NL80211_ATTR_BSS]) {
+ fprintf(stderr, "bss info missing!\n");
+ return NL_SKIP;
+ }
+ if (nla_parse_nested(bss, NL80211_BSS_MAX,
+ tb[NL80211_ATTR_BSS],
+ bss_policy)) {
+ fprintf(stderr, "failed to parse nested attributes!\n");
+ return NL_SKIP;
+ }
+
+ if (!bss[NL80211_BSS_BSSID])
+ return NL_SKIP;
+
+ mac_addr_n2a(mac_addr, nla_data(bss[NL80211_BSS_BSSID]));
+ printf("BSS %s", mac_addr);
+
+ if (tb[NL80211_ATTR_IFINDEX]) {
+ if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), dev);
+ printf("(on %s)", dev);
+ }
+
+ if (bss[NL80211_BSS_STATUS]) {
+ switch (nla_get_u32(bss[NL80211_BSS_STATUS])) {
+ case NL80211_BSS_STATUS_AUTHENTICATED:
+ printf(" -- authenticated");
+ break;
+ case NL80211_BSS_STATUS_ASSOCIATED:
+ printf(" -- associated");
+ break;
+ case NL80211_BSS_STATUS_IBSS_JOINED:
+ printf(" -- joined");
+ break;
+ default:
+ printf(" -- unknown status: %d",
+ nla_get_u32(bss[NL80211_BSS_STATUS]));
+ break;
+ }
+ }
+ printf("\n");
+
+ if (bss[NL80211_BSS_LAST_SEEN_BOOTTIME]) {
+ unsigned long long bt;
+ bt = (unsigned long long)nla_get_u64(bss[NL80211_BSS_LAST_SEEN_BOOTTIME]);
+ printf("\tlast seen: %llu.%.3llus [boottime]\n", bt/1000000000, (bt%1000000000)/1000000);
+ }
+
+ if (bss[NL80211_BSS_TSF]) {
+ unsigned long long tsf;
+ tsf = (unsigned long long)nla_get_u64(bss[NL80211_BSS_TSF]);
+ printf("\tTSF: %llu usec (%llud, %.2lld:%.2llu:%.2llu)\n",
+ tsf, tsf/1000/1000/60/60/24, (tsf/1000/1000/60/60) % 24,
+ (tsf/1000/1000/60) % 60, (tsf/1000/1000) % 60);
+ }
+ if (bss[NL80211_BSS_FREQUENCY]) {
+ int freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
+ printf("\tfreq: %d\n", freq);
+ if (freq > 45000)
+ is_dmg = true;
+ }
+ if (bss[NL80211_BSS_BEACON_INTERVAL])
+ printf("\tbeacon interval: %d TUs\n",
+ nla_get_u16(bss[NL80211_BSS_BEACON_INTERVAL]));
+ if (bss[NL80211_BSS_CAPABILITY]) {
+ __u16 capa = nla_get_u16(bss[NL80211_BSS_CAPABILITY]);
+ printf("\tcapability:");
+ if (is_dmg)
+ print_capa_dmg(capa);
+ else
+ print_capa_non_dmg(capa);
+ printf(" (0x%.4x)\n", capa);
+ }
+ if (bss[NL80211_BSS_SIGNAL_MBM]) {
+ int s = nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]);
+ printf("\tsignal: %d.%.2d dBm\n", s/100, s%100);
+ }
+ if (bss[NL80211_BSS_SIGNAL_UNSPEC]) {
+ unsigned char s = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]);
+ printf("\tsignal: %d/100\n", s);
+ }
+ if (bss[NL80211_BSS_SEEN_MS_AGO]) {
+ int age = nla_get_u32(bss[NL80211_BSS_SEEN_MS_AGO]);
+ printf("\tlast seen: %d ms ago\n", age);
+ }
+
+ if (bss[NL80211_BSS_INFORMATION_ELEMENTS] && show--) {
+ struct nlattr *ies = bss[NL80211_BSS_INFORMATION_ELEMENTS];
+ struct nlattr *bcnies = bss[NL80211_BSS_BEACON_IES];
+
+ if (bss[NL80211_BSS_PRESP_DATA] ||
+ (bcnies && (nla_len(ies) != nla_len(bcnies) ||
+ memcmp(nla_data(ies), nla_data(bcnies),
+ nla_len(ies)))))
+ printf("\tInformation elements from Probe Response "
+ "frame:\n");
+ print_ies(nla_data(ies), nla_len(ies),
+ params->unknown, params->type);
+ }
+ if (bss[NL80211_BSS_BEACON_IES] && show--) {
+ printf("\tInformation elements from Beacon frame:\n");
+ print_ies(nla_data(bss[NL80211_BSS_BEACON_IES]),
+ nla_len(bss[NL80211_BSS_BEACON_IES]),
+ params->unknown, params->type);
+ }
}

return NL_SKIP;
diff --git a/util.c b/util.c
index 8a2ba10..0692633 100644
--- a/util.c
+++ b/util.c
@@ -5,6 +5,8 @@
#include "iw.h"
#include "nl80211.h"

+#include "json/json_print.h"
+
void mac_addr_n2a(char *mac_addr, const unsigned char *arg)
{
int i, l;
@@ -220,19 +222,30 @@ int ieee80211_frequency_to_channel(int freq)
return 0;
}

-void print_ssid_escaped(const uint8_t len, const uint8_t *data)
-{
- int i;
+static char *ssid_escape(const uint8_t len, const uint8_t *data) {
+ static char ssid_escaped[512]; // static buffer
+ int pos = 0;
+
+ for (int i = 0; i < len && pos < sizeof(ssid_escaped) - 5; i++) { // leave space for "\xXX" and terminator byte
+ if (isprint(data[i]) && data[i] != ' ' && data[i] != '\\') {
+ ssid_escaped[pos++] = data[i];
+ } else if (data[i] == ' ' && (i != 0 && i != len - 1)) {
+ ssid_escaped[pos++] = ' ';
+ } else {
+ pos += snprintf(ssid_escaped + pos, sizeof(ssid_escaped) - pos, "\\x%.2x", data[i]);
+ }
+ }
+ ssid_escaped[pos] = '\0'; // set terminator byte
+ return ssid_escaped;
+}

- for (i = 0; i < len; i++) {
- if (isprint(data[i]) && data[i] != ' ' && data[i] != '\\')
- printf("%c", data[i]);
- else if (data[i] == ' ' &&
- (i != 0 && i != len -1))
- printf(" ");
- else
- printf("\\x%.2x", data[i]);
- }
+void print_ssid_escaped(const uint8_t len, const uint8_t *data) {
+ char *escaped_ssid = ssid_escape(len, data);
+ if (!iw_json) {
+ printf("%s", escaped_ssid);
+ } else {
+ print_string(PRINT_JSON, "SSID", "%s", escaped_ssid);
+ }
}

static int hex2num(char digit)
@@ -743,38 +756,103 @@ int put_chandef(struct nl_msg *msg, struct chandef *chandef)
return -ENOBUFS;
}

+static void print_mcs_index3(const __u8 *mcs)
+{
+ int mcs_bit, prev_bit = -2, prev_cont = 0;
+
+ for (mcs_bit = 0; mcs_bit <= 76; mcs_bit++) {
+ unsigned int mcs_octet = mcs_bit/8;
+ unsigned int MCS_RATE_BIT = 1 << mcs_bit % 8;
+ bool mcs_rate_idx_set;
+
+ mcs_rate_idx_set = !!(mcs[mcs_octet] & MCS_RATE_BIT);
+
+ if (!mcs_rate_idx_set)
+ continue;
+
+ if (prev_bit != mcs_bit - 1) {
+ if (prev_bit != -2)
+ printf("%d, ", prev_bit);
+ else
+ printf(" ");
+ printf("%d", mcs_bit);
+ prev_cont = 0;
+ } else if (!prev_cont) {
+ printf("-");
+ prev_cont = 1;
+ }
+
+ prev_bit = mcs_bit;
+ }
+
+ if (prev_cont)
+ printf("%d", prev_bit);
+ printf("\n");
+}
+
static void print_mcs_index(const __u8 *mcs)
{
int mcs_bit, prev_bit = -2, prev_cont = 0;

+ char buf[8];
+ char *p = (char *)buf;
+ int bufleft = (int)sizeof(buf);
+ int written = 0;
+
for (mcs_bit = 0; mcs_bit <= 76; mcs_bit++) {
- unsigned int mcs_octet = mcs_bit/8;
- unsigned int MCS_RATE_BIT = 1 << mcs_bit % 8;
- bool mcs_rate_idx_set;
+ unsigned int mcs_octet = mcs_bit / 8;
+ unsigned int MCS_RATE_BIT = 1 << (mcs_bit % 8);
+ bool mcs_rate_idx_set = (mcs[mcs_octet] & MCS_RATE_BIT) != 0;

- mcs_rate_idx_set = !!(mcs[mcs_octet] & MCS_RATE_BIT);
+ if (!mcs_rate_idx_set) continue;

- if (!mcs_rate_idx_set)
- continue;

if (prev_bit != mcs_bit - 1) {
- if (prev_bit != -2)
- printf("%d, ", prev_bit);
- else
- printf(" ");
- printf("%d", mcs_bit);
+ if (prev_bit != -2) {
+
+ //print single or range value
+ if(iw_json){
+ snprintf(p, bufleft, "%d", prev_bit);
+ print_string(PRINT_JSON, NULL, "%s", buf);
+ }else{
+ snprintf(p, bufleft, "%d, ", prev_bit);
+ printf("%s", buf);
+ }
+
+ //reset buffer
+ p = buf;
+ bufleft = sizeof(buf);
+
+ } else if (!iw_json) {
+ written = snprintf(p, bufleft, " ");
+ p += written;
+ bufleft -= written;
+ }
+
+ written = snprintf(p, bufleft, "%d", mcs_bit);
+ p += written;
+ bufleft -= written;
prev_cont = 0;
} else if (!prev_cont) {
- printf("-");
+ written = snprintf(p, bufleft, "-");
+ p += written;
+ bufleft -= written;
prev_cont = 1;
}

prev_bit = mcs_bit;
}

- if (prev_cont)
- printf("%d", prev_bit);
- printf("\n");
+ //if is range write last chunk
+ if (prev_cont) {
+ snprintf(p, bufleft, "%d", prev_bit);
+ }
+
+ //print last value
+ iw_json ? print_string(PRINT_JSON, NULL, "%s", buf) : printf("%s", buf);
+
+ //text output only
+ if(!iw_json) printf("\n");
}

/*
@@ -816,19 +894,32 @@ void print_ampdu_length(__u8 exponent)

max_ampdu_length = compute_ampdu_length(exponent);

- if (max_ampdu_length) {
- printf("\t\tMaximum RX AMPDU length %d bytes (exponent: 0x0%02x)\n",
- max_ampdu_length, exponent);
- } else {
- printf("\t\tMaximum RX AMPDU length: unrecognized bytes "
- "(exponent: %d)\n", exponent);
+ if(iw_json){
+ if (max_ampdu_length) {
+ print_int(PRINT_JSON, "max_ampdu_length", "%d", max_ampdu_length);
+ } else {
+ print_int(PRINT_JSON, "max_ampdu_length", "%d", -1);
+ }
+ print_string(PRINT_JSON, "max_ampdu_exponent", "0x0%02x", exponent);
+ }else{
+ if (max_ampdu_length) {
+ printf("\t\tMaximum RX AMPDU length %d bytes (exponent: 0x0%02x)\n",
+ max_ampdu_length, exponent);
+ } else {
+ printf("\t\tMaximum RX AMPDU length: unrecognized bytes "
+ "(exponent: %d)\n", exponent);
+ }
}
}

void print_ampdu_spacing(__u8 spacing)
{
- printf("\t\tMinimum RX AMPDU time spacing: %s (0x%02x)\n",
- print_ampdu_space(spacing), spacing);
+ if(iw_json){
+ print_string(PRINT_JSON, "min_rx_ampdu_time_spacing", "%s", print_ampdu_space(spacing));
+ print_string(PRINT_JSON, "min_rx_ampdu_time_spacing_value", "0x0%02x", spacing);
+ }else{
+ printf("\t\tMinimum RX AMPDU time spacing: %s (0x%02x)\n", print_ampdu_space(spacing), spacing);
+ }
}

void print_ht_capability(__u16 cap)
@@ -836,10 +927,19 @@ void print_ht_capability(__u16 cap)
#define PRINT_HT_CAP(_cond, _str) \
do { \
if (_cond) \
- printf("\t\t\t" _str "\n"); \
+ if(iw_json){ \
+ print_string(PRINT_JSON, NULL, _str); \
+ } else { \
+ printf("\t\t\t" _str "\n"); \
+ } \
} while (0)

- printf("\t\tCapabilities: 0x%02x\n", cap);
+ if(iw_json){
+ print_string(PRINT_JSON, "capabilities_value", "0x%02x", cap);
+ open_json_array("capabilities", NULL);
+ }else{
+ printf("\t\tCapabilities: 0x%02x\n", cap);
+ }

PRINT_HT_CAP((cap & BIT(0)), "RX LDPC");
PRINT_HT_CAP((cap & BIT(1)), "HT20/HT40");
@@ -878,6 +978,10 @@ void print_ht_capability(__u16 cap)
PRINT_HT_CAP((cap & BIT(14)), "40 MHz Intolerant");

PRINT_HT_CAP((cap & BIT(15)), "L-SIG TXOP protection");
+
+ if(iw_json){
+ close_json_array(NULL);
+ }
#undef PRINT_HT_CAP
}

@@ -893,32 +997,48 @@ void print_ht_mcs(const __u8 *mcs)
tx_max_num_spatial_streams = ((mcs[12] >> 2) & 3) + 1;
tx_unequal_modulation = !!(mcs[12] & (1 << 4));

- if (max_rx_supp_data_rate)
- printf("\t\tHT Max RX data rate: %d Mbps\n", max_rx_supp_data_rate);
+ if (max_rx_supp_data_rate){
+ if(iw_json){
+ print_int(PRINT_JSON, "ht_max_rx_data_rate_mbps", "%d", max_rx_supp_data_rate);
+ }else{
+ printf("\t\tHT Max RX data rate: %d Mbps\n", max_rx_supp_data_rate);
+ }
+ }
/* XXX: else see 9.6.0e.5.3 how to get this I think */

if (tx_mcs_set_defined) {
if (tx_mcs_set_equal) {
- printf("\t\tHT TX/RX MCS rate indexes supported:");
+ iw_json ? open_json_array("ht_tx_rx_mcs_idx_supported", NULL) : printf("\t\tHT TX/RX MCS rate indexes supported:");
print_mcs_index(mcs);
+ if(iw_json) close_json_array(NULL);
} else {
- printf("\t\tHT RX MCS rate indexes supported:");
+ iw_json ? open_json_array("ht_rx_mcs_idx_supported", NULL) : printf("\t\tHT RX MCS rate indexes supported:");
print_mcs_index(mcs);
+ if(iw_json) close_json_array(NULL);

- if (tx_unequal_modulation)
- printf("\t\tTX unequal modulation supported\n");
- else
- printf("\t\tTX unequal modulation not supported\n");
+ if(iw_json){
+ print_bool(PRINT_JSON, "tx_unequal_modulation", "%s", tx_unequal_modulation);
+ print_int(PRINT_JSON, "ht_tx_max_spatial_streams", "%d", tx_max_num_spatial_streams);
+ }else{

- printf("\t\tHT TX Max spatial streams: %d\n",
- tx_max_num_spatial_streams);
+ if (tx_unequal_modulation)
+ printf("\t\tTX unequal modulation supported\n");
+ else
+ printf("\t\tTX unequal modulation not supported\n");

- printf("\t\tHT TX MCS rate indexes supported may differ\n");
+ printf("\t\tHT TX Max spatial streams: %d\n", tx_max_num_spatial_streams);
+ printf("\t\tHT TX MCS rate indexes supported may differ\n");
+ }
}
} else {
- printf("\t\tHT RX MCS rate indexes supported:");
+ iw_json ? open_json_array("ht_rx_mcs_idx_supported", NULL) : printf("\t\tHT RX MCS rate indexes supported:");
print_mcs_index(mcs);
- printf("\t\tHT TX MCS rate indexes are undefined\n");
+ if(iw_json){
+ close_json_array(NULL);
+ }else{
+ //text output only
+ printf("\t\tHT TX MCS rate indexes are undefined\n");
+ }
}
}

@@ -1033,13 +1153,13 @@ static const struct vht_nss_ratio nss_ratio_tbl[3][4] = {
},
};

-static void print_nss_ratio_value(int ratio)
+static const char *nss_ratio_value(int ratio)
{
const char *rstr;

switch (ratio) {
case 4:
- return;
+ return NULL;
case 3:
rstr = "3/4";
break;
@@ -1054,7 +1174,7 @@ static void print_nss_ratio_value(int ratio)
break;
}

- printf("(%s NSS) ", rstr);
+ return rstr;
}

static void print_nss_ratio(const char *str, bool force_show, int ratio)
@@ -1063,53 +1183,83 @@ static void print_nss_ratio(const char *str, bool force_show, int ratio)
return;
if (ratio == 4) {
if (force_show)
- printf("%s ", str);
+ iw_json ? print_string(PRINT_JSON, str, "") : printf("%s ", str);
} else {
- printf("%s ", str);
- print_nss_ratio_value(ratio);
+ if(iw_json){
+ print_string(PRINT_JSON, str, "%s NSS", nss_ratio_value(ratio));
+ }else{
+ printf("%s ", str);
+ printf("%s NSS", nss_ratio_value(ratio));
+ }
}
}

void print_vht_info(__u32 capa, const __u8 *mcs)
{
+#define PRINT_VHT_CAPA(_bit, _str) \
+ do { \
+ if (capa & BIT(_bit)){ \
+ iw_json ? print_bool(PRINT_JSON, _str, "%s", true) : printf("\t\t\t" _str "\n"); \
+ } \
+ } while (0)
+
__u16 tmp;
__u32 supp_chan_width, ext_nss_bw;
const struct vht_nss_ratio *nss_tbl;
int i;

- printf("\t\tVHT Capabilities (0x%.8x):\n", capa);
-
-#define PRINT_VHT_CAPA(_bit, _str) \
- do { \
- if (capa & BIT(_bit)) \
- printf("\t\t\t" _str "\n"); \
- } while (0)
-
- printf("\t\t\tMax MPDU length: ");
- switch (capa & 3) {
- case 0: printf("3895\n"); break;
- case 1: printf("7991\n"); break;
- case 2: printf("11454\n"); break;
- case 3: printf("(reserved)\n");
+ if(iw_json){
+ print_string(PRINT_JSON, "vht_capabilities_raw", "0x%.8x", capa);
+ switch (capa & 3) {
+ case 0: print_string(PRINT_JSON, "vht_capabilities", "3895"); break;
+ case 1: print_string(PRINT_JSON, "vht_capabilities_data", "7991"); break;
+ case 2: print_string(PRINT_JSON, "vht_capabilities_data", "11454"); break;
+ case 3: print_string(PRINT_JSON, "vht_capabilities_data", "reserved");
+ }
+ }else{
+ printf("\n");
+ printf("\t\tVHT Capabilities (0x%.8x):\n", capa);
+ printf("\t\t\tMax MPDU length: ");
+ switch (capa & 3) {
+ case 0: printf("3895\n"); break;
+ case 1: printf("7991\n"); break;
+ case 2: printf("11454\n"); break;
+ case 3: printf("(reserved)\n");
+ }
}

- printf("\t\t\tSupported Channel Width: ");
+ iw_json ? open_json_object("supported_channel_width") : printf("\t\t\tSupported Channel Width: ");
+
supp_chan_width = (capa >> 2) & 3;
ext_nss_bw = (capa >> 30) & 3;
nss_tbl = &nss_ratio_tbl[supp_chan_width][ext_nss_bw];

if (!nss_tbl->valid)
- printf("(reserved)\n");
+ iw_json ? print_bool(PRINT_JSON,"reserved", "%s", true) : printf("(reserved)\n");
else if (nss_tbl->bw_20 == 4 &&
nss_tbl->bw_40 == 4 &&
nss_tbl->bw_80 == 4 &&
(!nss_tbl->bw_160 || nss_tbl->bw_160 == 4) &&
(!nss_tbl->bw_80_80 || nss_tbl->bw_80_80 == 4)) {
/* old style print format */
- switch (supp_chan_width) {
- case 0: printf("neither 160 nor 80+80\n"); break;
- case 1: printf("160 MHz\n"); break;
- case 2: printf("160 MHz, 80+80 MHz\n"); break;
+ if(iw_json){
+ switch (supp_chan_width) {
+ case 0:
+ break;
+ case 1:
+ print_bool(PRINT_JSON, "160", "%s", true);
+ break;
+ case 2:
+ print_bool(PRINT_JSON, "160", "%s", true);
+ print_bool(PRINT_JSON, "80+80", "%s", true);
+ break;
+ }
+ }else{
+ switch (supp_chan_width) {
+ case 0: printf("neither 160 nor 80+80\n"); break;
+ case 1: printf("160 MHz\n"); break;
+ case 2: printf("160 MHz, 80+80 MHz\n"); break;
+ }
}
} else {
print_nss_ratio("20Mhz", false, nss_tbl->bw_20);
@@ -1119,6 +1269,9 @@ void print_vht_info(__u32 capa, const __u8 *mcs)
print_nss_ratio("80+80Mhz", false, nss_tbl->bw_80_80);
printf("\n");
}
+ if(iw_json){
+ close_json_object();
+ }

PRINT_VHT_CAPA(4, "RX LDPC");
PRINT_VHT_CAPA(5, "short GI (80 MHz)");
@@ -1138,36 +1291,39 @@ void print_vht_info(__u32 capa, const __u8 *mcs)
PRINT_VHT_CAPA(28, "RX antenna pattern consistency");
PRINT_VHT_CAPA(29, "TX antenna pattern consistency");

- printf("\t\tVHT RX MCS set:\n");
- tmp = mcs[0] | (mcs[1] << 8);
- for (i = 1; i <= 8; i++) {
- printf("\t\t\t%d streams: ", i);
- switch ((tmp >> ((i-1)*2) ) & 3) {
- case 0: printf("MCS 0-7\n"); break;
- case 1: printf("MCS 0-8\n"); break;
- case 2: printf("MCS 0-9\n"); break;
- case 3: printf("not supported\n"); break;
+ if(iw_json){
+
+ }else{
+ printf("\t\tVHT RX MCS set:\n");
+ tmp = mcs[0] | (mcs[1] << 8);
+ for (i = 1; i <= 8; i++) {
+ printf("\t\t\t%d streams: ", i);
+ switch ((tmp >> ((i-1)*2) ) & 3) {
+ case 0: printf("MCS 0-7\n"); break;
+ case 1: printf("MCS 0-8\n"); break;
+ case 2: printf("MCS 0-9\n"); break;
+ case 3: printf("not supported\n"); break;
+ }
}
- }
- tmp = mcs[2] | (mcs[3] << 8);
- printf("\t\tVHT RX highest supported: %d Mbps\n", tmp & 0x1fff);
-
- printf("\t\tVHT TX MCS set:\n");
- tmp = mcs[4] | (mcs[5] << 8);
- for (i = 1; i <= 8; i++) {
- printf("\t\t\t%d streams: ", i);
- switch ((tmp >> ((i-1)*2) ) & 3) {
- case 0: printf("MCS 0-7\n"); break;
- case 1: printf("MCS 0-8\n"); break;
- case 2: printf("MCS 0-9\n"); break;
- case 3: printf("not supported\n"); break;
+ tmp = mcs[2] | (mcs[3] << 8);
+ printf("\t\tVHT RX highest supported: %d Mbps\n", tmp & 0x1fff);
+
+ printf("\t\tVHT TX MCS set:\n");
+ tmp = mcs[4] | (mcs[5] << 8);
+ for (i = 1; i <= 8; i++) {
+ printf("\t\t\t%d streams: ", i);
+ switch ((tmp >> ((i-1)*2) ) & 3) {
+ case 0: printf("MCS 0-7\n"); break;
+ case 1: printf("MCS 0-8\n"); break;
+ case 2: printf("MCS 0-9\n"); break;
+ case 3: printf("not supported\n"); break;
+ }
}
- }
- tmp = mcs[6] | (mcs[7] << 8);
- printf("\t\tVHT TX highest supported: %d Mbps\n", tmp & 0x1fff);
+ tmp = mcs[6] | (mcs[7] << 8);
+ printf("\t\tVHT TX highest supported: %d Mbps\n", tmp & 0x1fff);

- printf("\t\tVHT extended NSS: %ssupported\n",
- (tmp & (1 << 13)) ? "" : "not ");
+ printf("\t\tVHT extended NSS: %ssupported\n", (tmp & (1 << 13)) ? "" : "not ");
+ }
}

static void __print_he_capa(const __u16 *mac_cap,
@@ -1183,13 +1339,13 @@ static void __print_he_capa(const __u16 *mac_cap,
#define PRINT_HE_CAP(_var, _idx, _bit, _str) \
do { \
if (_var[_idx] & BIT(_bit)) \
- printf("%s\t\t\t" _str "\n", pre); \
+ iw_json ? print_string(PRINT_JSON, NULL, _str) : printf("%s\t\t\t" _str "\n", pre); \
} while (0)

#define PRINT_HE_CAP_MASK(_var, _idx, _shift, _mask, _str) \
do { \
if ((_var[_idx] >> _shift) & _mask) \
- printf("%s\t\t\t" _str ": %d\n", pre, (_var[_idx] >> _shift) & _mask); \
+ iw_json ? print_string(PRINT_JSON, NULL, "%s %d", _str, (_var[_idx] >> _shift) & _mask) : printf("%s\t\t\t" _str ": %d\n", pre, (_var[_idx] >> _shift) & _mask); \
} while (0)

#define PRINT_HE_MAC_CAP(...) PRINT_HE_CAP(mac_cap, __VA_ARGS__)
@@ -1198,10 +1354,16 @@ static void __print_he_capa(const __u16 *mac_cap,
#define PRINT_HE_PHY_CAP0(_idx, _bit, ...) PRINT_HE_CAP(phy_cap, _idx, _bit + 8, __VA_ARGS__)
#define PRINT_HE_PHY_CAP_MASK(...) PRINT_HE_CAP_MASK(phy_cap, __VA_ARGS__)

- printf("%s\t\tHE MAC Capabilities (0x", pre);
- for (i = 0; i < 3; i++)
- printf("%04x", mac_cap[i]);
- printf("):\n");
+ if(iw_json){
+ print_string(PRINT_JSON, "he_mac_capabilities_raw", "%s 0x%04x%04x%04x", pre, mac_cap[0], mac_cap[1], mac_cap[2]);
+ open_json_array("he_mac_capabilities", NULL);
+ }else{
+ printf("\n");
+ printf("%s\t\tHE MAC Capabilities (0x", pre);
+ for (i = 0; i < 3; i++)
+ printf("%04x", mac_cap[i]);
+ printf("):\n");
+ }

PRINT_HE_MAC_CAP(0, 0, "+HTC HE Supported");
PRINT_HE_MAC_CAP(0, 1, "TWT Requester");
@@ -1238,10 +1400,25 @@ static void __print_he_capa(const __u16 *mac_cap,
PRINT_HE_MAC_CAP(2, 11, "UL 2x996-Tone RU");
PRINT_HE_MAC_CAP(2, 12, "OM Control UL MU Data Disable RX");

- printf("%s\t\tHE PHY Capabilities: (0x", pre);
- for (i = 0; i < 11; i++)
- printf("%02x", ((__u8 *)phy_cap)[i + 1]);
- printf("):\n");
+ {
+ char buf[64] = {0};
+ int bufleft = (int)sizeof(buf);
+ char *p = buf;
+ int written = 0;
+ for (i = 0; i < 11; i++){
+ written = snprintf(p, bufleft, "%02x", ((__u8 *)phy_cap)[i + 1]);
+ bufleft -= written;
+ p += written;
+ }
+
+ if(iw_json){
+ close_json_array(NULL);
+ print_string(PRINT_JSON, "he_phy_capabilities_raw", "%s 0x%s", pre, buf);
+ open_json_array("he_phy_capabilities", NULL);
+ }else{
+ printf("%s\t\tHE PHY Capabilities: (0x%s)", pre, buf);
+ }
+ }

PRINT_HE_PHY_CAP0(0, 1, "HE40/2.4GHz");
PRINT_HE_PHY_CAP0(0, 2, "HE40/HE80/5GHz");
@@ -1308,6 +1485,8 @@ static void __print_he_capa(const __u16 *mac_cap,
PRINT_HE_PHY_CAP(5, 4, "RX Full BW SU Using HE MU PPDU with Compression SIGB");
PRINT_HE_PHY_CAP(5, 5, "RX Full BW SU Using HE MU PPDU with Non-Compression SIGB");

+ if(iw_json) close_json_array(NULL);
+
mcs_used = 0;
for (i = 0; i < 3; i++) {
__u8 phy_cap_support[] = { BIT(1) | BIT(2), BIT(3), BIT(4) };
@@ -1321,20 +1500,43 @@ static void __print_he_capa(const __u16 *mac_cap,
if ((i * 2 + 2) * sizeof(mcs_set[0]) >= mcs_len)
return;

- for (j = 0; j < 2; j++) {
- int k;
- printf("%s\t\tHE %s MCS and NSS set %s MHz\n", pre, j ? "TX" : "RX", bw[i]);
- for (k = 0; k < 8; k++) {
- __u16 mcs = mcs_set[(i * 2) + j];
- mcs >>= k * 2;
- mcs &= 0x3;
- printf("%s\t\t\t%d streams: ", pre, k + 1);
- if (mcs == 3)
- printf("not supported\n");
- else
- printf("MCS 0-%d\n", 7 + (mcs * 2));
+ if(iw_json){
+ for (j = 0; j < 2; j++) {
+ int k;
+ char buf[512] = {0};
+ snprintf(buf, sizeof(buf), "%s_he_%s_mcs_and_nss_set", pre, j ? "tx" : "rx");
+ open_json_object(buf);
+ print_string(PRINT_JSON, "width_mhz", "%s", bw[i]);
+ open_json_object("streams");
+ for (k = 0; k < 8; k++) {
+ __u16 mcs = mcs_set[(i * 2) + j];
+ mcs >>= k * 2;
+ mcs &= 0x3;
+ char keybuf[16];
+ snprintf(keybuf, sizeof(keybuf), "%s_%d_streams", pre, k + 1);
+ if (mcs == 3)
+ print_string(PRINT_JSON, keybuf, "not supported");
+ else
+ print_string(PRINT_JSON, keybuf, "MCS 0-%d", 7 + (mcs * 2));
+ }
+ close_json_object();
+ close_json_object();
+ }
+ }else{
+ for (j = 0; j < 2; j++) {
+ int k;
+ printf("%s\t\tHE %s MCS and NSS set %s MHz\n", pre, j ? "TX" : "RX", bw[i]);
+ for (k = 0; k < 8; k++) {
+ __u16 mcs = mcs_set[(i * 2) + j];
+ mcs >>= k * 2;
+ mcs &= 0x3;
+ printf("%s\t\t\t%d streams: ", pre, k + 1);
+ if (mcs == 3)
+ printf("not supported\n");
+ else
+ printf("MCS 0-%d\n", 7 + (mcs * 2));
+ }
}
-
}
mcs_used += 2 * sizeof(mcs_set[0]);
}
@@ -1348,12 +1550,23 @@ static void __print_he_capa(const __u16 *mac_cap,
ppet_len = 0;
}

- if (ppet_len && (phy_cap[3] & BIT(15))) {
- printf("%s\t\tPPE Threshold ", pre);
- for (i = 0; i < ppet_len; i++)
- if (ppet[i])
- printf("0x%02x ", ppet[i]);
- printf("\n");
+ {
+ char buf[64];
+ char *p = buf;
+ int bufleft = (int)sizeof(buf);
+ int written = 0;
+
+ for (i = 0; i < ppet_len; i++){
+ if (ppet[i]){
+ written = snprintf(p, bufleft, "0x%02x ", ppet[i]);
+ bufleft -= written;
+ p += written;
+ }
+ }
+
+ if (ppet_len && (phy_cap[3] & BIT(15))) {
+ iw_json ? print_string(PRINT_JSON, "ppe_threshold", "%s", buf) : printf("%s\t\tPPE Threshold %s\n", pre, buf);
+ }
}
}

@@ -1362,9 +1575,16 @@ void print_iftype_list(const char *name, const char *pfx, struct nlattr *attr)
struct nlattr *ift;
int rem;

- printf("%s:\n", name);
- nla_for_each_nested(ift, attr, rem)
- printf("%s * %s\n", pfx, iftype_name(nla_type(ift)));
+ if(iw_json){
+ nla_for_each_nested(ift, attr, rem){
+ print_string(PRINT_JSON, name, "%s * %s", pfx, iftype_name(nla_type(ift)));
+ }
+ } else {
+ printf("%s:\n", name);
+ nla_for_each_nested(ift, attr, rem){
+ printf("%s * %s\n", pfx, iftype_name(nla_type(ift)));
+ }
+ }
}

void print_iftype_line(struct nlattr *attr)
@@ -1694,7 +1914,7 @@ int get_cf1(const struct chanmode *chanmode, unsigned long freq)
6435, 6515, 6595, 6675, 6755, 6835,
6195, 6995 };
unsigned int bw160[] = { 5180, 5500, 5955, 6115, 6275, 6435,
- 6595, 6755, 6915 };
+ 6595, 6755, 6915 };

switch (chanmode->width) {
case NL80211_CHAN_WIDTH_80:
--
2.42.0



2024-03-05 09:42:17

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH v5] Add JSON output options to 'iw' for scan results

On Mon, 2024-03-04 at 23:01 +0300, Isaev Ruslan wrote:
> v5 changes:
> - add json print to print_ap_channel_report()
> - minor refactor open_json_object()

Alright, yay, so mechanical submission issues out of the way, this looks
readable :)

> branch: v5.19

That's almost two years old, so you'll obviously have to rebase it on
the latest main branch. But we can have more fundamental discussions
first.

> This commit introduces the ability to output scan results from 'iw' in
> JSON format, similar to the 'ip' utility from the iproute2 package.
> The addition aims to enhance the tool's usability and scriptability
> by providing a structured data format option.

Generally, I'm not opposed to that, but I think we've had this
discussion before, and I don't want to see a lot of extra complexity in
the code for it.

> Two new command-line options are added:
> - '-j': Outputs scan results in compact JSON format.
> - '-jj': Outputs scan results in pretty-printed JSON format.


With tools like 'jq' being near ubiquitous, is there much value in
having two output formats? I'd almost say only support -j (and I don't
like '-jj' anyway, probably would prefer -j/--json and --json=pretty or
so), and then 'jq' can do pretty-printing and coloring etc.?

> + //close root json object and deinit jsonw
> + if(iw_json){
> + delete_json_obj();
> + }

Please generally try to follow the (existing/Linux) coding style more
closely. I don't mind C99 comments but a space would be nice, space
after if, space before the opening brace, no braces needed here though,
perhaps more (just not on these four lines :-) )

> +++ b/json/json_print.c

I'm generally not going to look into these files for now, but including
them internally means we have to maintain them. I'd almost prefer a
library that can be used.

However, with that said,

> +/*
> + * json_print.c "print regular or json output, based on json_writer".
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version
> + * 2 of the License, or (at your option) any later version.


This doesn't work well with the ISC license of iw. Which is another
reason to prefer an existing library, I suppose.

> +++ b/json/json_writer.c
> @@ -0,0 +1,296 @@
> +// SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) //

We don't use SPDX license tags here yet, so not sure this is meaningful,
we also don't have a dir with the actual license texts like the kernel
has for instance.

If going this way it'd probably be useful to add SPDX tags to all
existing files too.

Here, off the top of my head, I suspect the BSD-2-Clause should actually
be compatible to releasing the whole thing under ISC, but need to check
that too.

> --- a/scan.c
> +++ b/scan.c
> @@ -12,6 +12,8 @@
> #include "nl80211.h"
> #include "iw.h"
>
> +#include "json/json_print.h"
> +
> #define WLAN_CAPABILITY_ESS (1<<0)
> #define WLAN_CAPABILITY_IBSS (1<<1)
> #define WLAN_CAPABILITY_CF_POLLABLE (1<<2)
> @@ -546,10 +548,13 @@ static int handle_scan(struct nl80211_state *state,
>
> static void tab_on_first(bool *first)
> {
> - if (!*first)
> - printf("\t");
> - else
> + if (!*first){
> + if(!iw_json){
> + printf("\t");
> + }
> + }else{
> *first = false;
> + }

Coding style. Not sure why you don't just skip the whole thing if json
is turned on?

> static void print_ssid(const uint8_t type, uint8_t len, const uint8_t *data,
> const struct print_ies_data *ie_buffer)
> {
> - printf(" ");
> - print_ssid_escaped(len, data);
> - printf("\n");
> + if(!iw_json){
> + printf(" ");
> + print_ssid_escaped(len, data);
> + printf("\n");
> + } else {
> + print_ssid_escaped(len, data);
> + }
> }

Might be more useful to provide a JSON-output for SSIDs? Surely you'd
also want to escape them in a JSON-compatible way, since you'd want to
use '\uXXXX' encoding to actually get proper bytes out on the other side
without a special decoder?

> @@ -574,21 +583,35 @@ static void print_supprates(const uint8_t type, uint8_t len,
> {
> int i;
>
> - printf(" ");
> -
> - for (i = 0; i < len; i++) {
> - int r = data[i] & 0x7f;
> + if(iw_json){
> + open_json_array("array", NULL);
> + for (i = 0; i < len; i++) {
> + int r = data[i] & 0x7f;
> + if (r == BSS_MEMBERSHIP_SELECTOR_VHT_PHY && data[i] & 0x80){
> + print_string(PRINT_JSON, NULL, "VHT%s", data[i] & 0x80 ? "*" : "");
> + } else if (r == BSS_MEMBERSHIP_SELECTOR_HT_PHY && data[i] & 0x80){
> + print_string(PRINT_JSON, NULL, "HT%s", data[i] & 0x80 ? "*" : "");
> + } else {
> + print_string(PRINT_JSON, NULL, "%d.%d%s", r/2, 5*(r&1), data[i] & 0x80 ? "*" : "");
> + }
> + }
> + close_json_array(NULL);
> + }else{
> + printf(" ");
> + for (i = 0; i < len; i++) {
> + int r = data[i] & 0x7f;

So generally, here, no - I don't like this style of working at all. We'd
really want to have as little "if (iw_json)" as possible in the code. In
code like this, we really shouldn't have such an if at all, but only
e.g.

open_json_array();
for (...) {
if (...)
print_string(...);
}
close_json_array();

Surely we can come up with an abstraction where something like
print_string() does the right thing either way? And open/close JSON
array does the '\n' needed after it, or something?
Maybe we'd pass an argument to open_json_array() that says how we want
it printed in the non-JSON case (and rename it to open_array?), e.g.
should it all be on one line like here, or should it be multiple lines
with " - " prefix on each line, etc. JSON also tracks nesting, I guess,
so we could even do that for nested stuff in the normal output where we
use tabs etc.?

Generally, I'd also say that we never promised stable output, so if the
output changes slightly due to such, I'm perfectly fine with that,
whoever relied on it (and that includes me in some internal code) gets
to keep the pieces when it breaks ...


Also, looking at the function calls, all those NULL and PRINT_JSON are
pointless, as far as I can tell? So I think it'd make more sense to come
up with a JSON-supporting but otherwise agnostic API abstraction:

open_array(SINGLE_LINE); // could also be MULTI_LINE
for ...
print_string("VHT%s", data[i] & 0x80 ? "*" : "");
close_array();


Maybe open_array returns some kind of token that you pass to the inner
calls down to close_array(), or maybe it just keeps a stack of these
internally, I could see either way working (though an internal stack
would probably be simpler to use, and with a lot of code using it, that
might make sense for to trade off against the additional internal
complexity)


Somewhat unrelated to that discussion, you might also want to not
actually use print_string() for rates here though, and possibly use a
different JSON encoding for the rates (and membership selectors), e.g.
as an object with a 'basic' property? But I don't care much about that
part, though generally I'd argue strings shouldn't be used where there's
a better representation with native JSON types.


> + if(iw_json){
> + print_string(PRINT_JSON, "capabilities_raw", "0x%02x 0x%02x 0x%02x 0x%02x 0x%02x", data[0], data[1], data[2], data[3], data[4]);

That's a really bad JSON representation, a string with bytes, rather
than an array of bytes?

> static void print_erp(const uint8_t type, uint8_t len, const uint8_t *data,
> const struct print_ies_data *ie_buffer)
> {
> if (data[0] == 0x00)
> - printf(" <no flags>");
> + iw_json ? print_bool(PRINT_JSON, "no_flags", "%s", true) : printf(" <no flags>");

It seems like you'd generally want to have a container for each of
these, so you have

// probably these two lines outside of the print_erp() call even
open_json_object("erp");
print_u8("tag", type);

print_flags(...);

or something, rather than including "no_flags" in the object?

> + iw_json ? print_string(PRINT_JSON, NULL, "WEP-40") : printf("WEP-40");

This goes just along with what I wrote above, but that iw_json thing
here seems really pointless, and just makes things difficult.

(I also don't like the use of the ternary operator in this way but
that's maybe not quite the point.)

[snip lots of similar stuff]

> unsigned int bw160[] = { 5180, 5500, 5955, 6115, 6275, 6435,
> - 6595, 6755, 6915 };
> + 6595, 6755, 6915 };

nit: unrelated whitespace change



So ... Like I wrote above, generally I'm not against doing this. But
like I also tried to explain above, I think it needs to be less
"duplicative". I'm happy to change the code in major ways to make JSON
output easier, I'm also happy to let that change the output in some ways
(maybe the default should be YAML-compatible ;-) ).

But I'd really want to see this done in a way that doesn't end up with
having to duplicate everything all the time.

We'll also need to figure out the licensing situation, and perhaps
ideally find a way to not add ~1.5k LOC to support it, but link against
something that exists already.

johannes

2024-03-05 10:32:43

by Nicolas Escande

[permalink] [raw]
Subject: Re: [PATCH v5] Add JSON output options to 'iw' for scan results

On Tue Mar 5, 2024 at 10:41 AM CET, Johannes Berg wrote:
[...]
>
> I'm generally not going to look into these files for now, but including
> them internally means we have to maintain them. I'd almost prefer a
> library that can be used.

For what it's worth jansson has been very good to me. It has printf() like
object creation which usually integrates well with c programs.

>
> However, with that said,
>
> > +/*
> > + * json_print.c "print regular or json output, based on json_writer".
> > + *
> > + * This program is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU General Public License
> > + * as published by the Free Software Foundation; either version
> > + * 2 of the License, or (at your option) any later version.
>
>
> This doesn't work well with the ISC license of iw. Which is another
> reason to prefer an existing library, I suppose.

With MIT license https://github.com/akheron/jansson/blob/master/LICENSE

[...]

>
> So ... Like I wrote above, generally I'm not against doing this. But
> like I also tried to explain above, I think it needs to be less
> "duplicative". I'm happy to change the code in major ways to make JSON
> output easier, I'm also happy to let that change the output in some ways
> (maybe the default should be YAML-compatible ;-) ).

>
> But I'd really want to see this done in a way that doesn't end up with
> having to duplicate everything all the time.
>

For me, having a "good" json representation means, as you pointed out, using the
right json underlying type and most of the time I'm afraid that means a complete
different code path because of the underlying type/container. It's always a
blurry line obviously but duplicating in a complete seperate function that only
does the right thing for json output may end up being cleaner codewise.

> We'll also need to figure out the licensing situation, and perhaps
> ideally find a way to not add ~1.5k LOC to support it, but link against
> something that exists already.

+1 for not reimplementing the wheel.

>
> johannes


2024-03-05 10:46:02

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH v5] Add JSON output options to 'iw' for scan results

On Tue, 2024-03-05 at 11:17 +0100, Nicolas Escande wrote:

> For what it's worth jansson has been very good to me. It has printf() like
> object creation which usually integrates well with c programs.

Nice :)

> With MIT license https://github.com/akheron/jansson/blob/master/LICENSE

If we link it as a library rather than copying it, that wouldn't even
matter (well unless it was GPL too, but even then you could still make a
build without it if you don't want the result to become GPL.)

> For me, having a "good" json representation means, as you pointed out, using the
> right json underlying type and most of the time I'm afraid that means a complete
> different code path because of the underlying type/container. It's always a
> blurry line obviously but duplicating in a complete seperate function that only
> does the right thing for json output may end up being cleaner codewise.

Yeah, depends, I guess. I'd rather see less duplicating, and in the
cases like in this patch it seems like it wouldn't even matter so much.
But agree it depends on the objects.

Just things like

iw_json ? print_string(PRINT_JSON, "manufacturer", "%.*s", sublen, data + 4) : printf("\t * Manufacturer: %.*s\n", sublen, data + 4);

seem like the worst of both worlds... More obviously perhaps where it's
not even different:

iw_json ? print_string(PRINT_JSON, NULL, "FT/IEEE 802.1X") : printf("FT/IEEE 802.1X");

or

iw_json ? print_string(PRINT_JSON, NULL, "%.02x-%.02x-%.02x:%d", data[0], data[1] ,data[2], data[3]) : printf("%.02x-%.02x-%.02x:%d", data[0], data[1] ,data[2], data[3]);


Whereas with something like

iw_json ? print_string(PRINT_JSON, "response_type", "%d%s", val, val == 3 ? " (AP)" : "") : printf("\t * Response Type: %d%s\n", val, val == 3 ? " (AP)" : "");

(ugh, I don't like those long lines)

you probably don't even want to have the " (AP)" thing in the JSON, but
make it a real integer type rather than a string with print_string()!

But we could make that

output_int("response_type", "Response Type", val);
if (val == 3)
hint("(AP)");

and the hint() just doesn't do anything in JSON?

And maybe even "response_type" is translated to "Response Type" for the
human output so you don't need to add the other "Response Type" there.
Dunno. I really wouldn't mind if in some places that meant we'd now
output "Response Type" instead of "response type" or "Response type" or
something.

Or perhaps do it the other way around, so "Response Type" is lower-cased
and illegal chars like " " are replaced by "_", although that makes the
JSON representation less obvious in the code.

Picking one arbitrarily, we'd have something

output_int("Response Type", val);
if (val == 3)
hint("(AP)");

which seems far nicer than

iw_json ? print_string(PRINT_JSON, "response_type", "%d%s", val, val == 3 ? " (AP)" : "") : printf("\t * Response Type: %d%s\n", val, val == 3 ? " (AP)" : "");

in both code and JSON representation.

Yes we'll have to do something about the \n and indentation and I know
I'm handwaving about that, but it seems like with open_object() and
close_object() etc. that need to keep a state stack anyway, that should
be solvable?

I'd argue that we should try to get somewhere around 80-90% of the cases
unified in this matter, and then have special things for the rest that
are special kinds of objects, etc.

Right now it's pretty much 100% special cases.

johannes

2024-03-05 11:24:52

by Isaev Ruslan

[permalink] [raw]
Subject: Re: [PATCH v5] Add JSON output options to 'iw' for scan results

On Tue, 5 Mar 2024 at 13:17, Nicolas Escande <[email protected]> wrote:
>
> On Tue Mar 5, 2024 at 10:41 AM CET, Johannes Berg wrote:
> [...]
> >
> > I'm generally not going to look into these files for now, but including
> > them internally means we have to maintain them. I'd almost prefer a
> > library that can be used.
>
> For what it's worth jansson has been very good to me. It has printf() like
> object creation which usually integrates well with c programs.
>
> >
> > However, with that said,
> >
> > > +/*
> > > + * json_print.c "print regular or json output, based on json_writer".
> > > + *
> > > + * This program is free software; you can redistribute it and/or
> > > + * modify it under the terms of the GNU General Public License
> > > + * as published by the Free Software Foundation; either version
> > > + * 2 of the License, or (at your option) any later version.
> >
> >
> > This doesn't work well with the ISC license of iw. Which is another
> > reason to prefer an existing library, I suppose.
>
IMHO jansson library is more complicated than stream jsonw which is
simple as a hatchet.
I can rewrite json_print.c with any LiCENCE we need.

2024-03-05 11:34:36

by Isaev Ruslan

[permalink] [raw]
Subject: Re: [PATCH v5] Add JSON output options to 'iw' for scan results

On Tue, 5 Mar 2024 at 13:31, Johannes Berg <[email protected]> wrote:
>
> On Tue, 2024-03-05 at 11:17 +0100, Nicolas Escande wrote:
>
> > For what it's worth jansson has been very good to me. It has printf() like
> > object creation which usually integrates well with c programs.
>
> Nice :)
>
> > With MIT license https://github.com/akheron/jansson/blob/master/LICENSE
>
> If we link it as a library rather than copying it, that wouldn't even
> matter (well unless it was GPL too, but even then you could still make a
> build without it if you don't want the result to become GPL.)
>
> > For me, having a "good" json representation means, as you pointed out, using the
> > right json underlying type and most of the time I'm afraid that means a complete
> > different code path because of the underlying type/container. It's always a
> > blurry line obviously but duplicating in a complete seperate function that only
> > does the right thing for json output may end up being cleaner codewise.
>
> Yeah, depends, I guess. I'd rather see less duplicating, and in the
> cases like in this patch it seems like it wouldn't even matter so much.
> But agree it depends on the objects.

I think i could make a universal printf() like function with
additional args for json representation.

Something like this:
before:
printf("\t * Manufacturer: %.*s\n", sublen, data + 4);

after:
printf_json("\t * Manufacturer", "%.*s", sublen, data + 4);


void printf_json(const char* key, const char* fmt, ...) {
char buf[1024];
va_list args;
va_start(args, format);

if (iw_json) {
vsnprintf(buf, sizeof(buf), format, args);
print_string(PRINT_JSON, key, "%s", buf);
} else {
printf("\t * %s: ", key);
vprintf(fmt, args);
printf("\n");
}

va_end(args);
}



>
> Just things like
>
> iw_json ? print_string(PRINT_JSON, "manufacturer", "%.*s", sublen, data + 4) : printf("\t * Manufacturer: %.*s\n", sublen, data + 4);
>
> seem like the worst of both worlds... More obviously perhaps where it's
> not even different:
>
> iw_json ? print_string(PRINT_JSON, NULL, "FT/IEEE 802.1X") : printf("FT/IEEE 802.1X");
>
> or
>
> iw_json ? print_string(PRINT_JSON, NULL, "%.02x-%.02x-%.02x:%d", data[0], data[1] ,data[2], data[3]) : printf("%.02x-%.02x-%.02x:%d", data[0], data[1] ,data[2], data[3]);
>
>
> Whereas with something like
>
> iw_json ? print_string(PRINT_JSON, "response_type", "%d%s", val, val == 3 ? " (AP)" : "") : printf("\t * Response Type: %d%s\n", val, val == 3 ? " (AP)" : "");
>
> (ugh, I don't like those long lines)
>
> you probably don't even want to have the " (AP)" thing in the JSON, but
> make it a real integer type rather than a string with print_string()!
>
> But we could make that
>
> output_int("response_type", "Response Type", val);
> if (val == 3)
> hint("(AP)");
>
> and the hint() just doesn't do anything in JSON?
>
> And maybe even "response_type" is translated to "Response Type" for the
> human output so you don't need to add the other "Response Type" there.
> Dunno. I really wouldn't mind if in some places that meant we'd now
> output "Response Type" instead of "response type" or "Response type" or
> something.
>
> Or perhaps do it the other way around, so "Response Type" is lower-cased
> and illegal chars like " " are replaced by "_", although that makes the
> JSON representation less obvious in the code.
>
> Picking one arbitrarily, we'd have something
>
> output_int("Response Type", val);
> if (val == 3)
> hint("(AP)");
>
> which seems far nicer than
>
> iw_json ? print_string(PRINT_JSON, "response_type", "%d%s", val, val == 3 ? " (AP)" : "") : printf("\t * Response Type: %d%s\n", val, val == 3 ? " (AP)" : "");
>
> in both code and JSON representation.
>
> Yes we'll have to do something about the \n and indentation and I know
> I'm handwaving about that, but it seems like with open_object() and
> close_object() etc. that need to keep a state stack anyway, that should
> be solvable?
>
> I'd argue that we should try to get somewhere around 80-90% of the cases
> unified in this matter, and then have special things for the rest that
> are special kinds of objects, etc.
>
> Right now it's pretty much 100% special cases.
>
> johannes

2024-03-05 13:30:10

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH v5] Add JSON output options to 'iw' for scan results

On Tue, 2024-03-05 at 14:32 +0300, Isaev Ruslan wrote:
>
> I think i could make a universal printf() like function with
> additional args for json representation.
>
> Something like this:
> before:
> printf("\t * Manufacturer: %.*s\n", sublen, data + 4);
>
> after:
> printf_json("\t * Manufacturer", "%.*s", sublen, data + 4);

I think you meant

printf_json("Manufacturer", "%.*s", sublen, data + 4);

but yeah, that seems better? Not sure I'd call it print_json() over just
print() or output() or so at that point.

> void printf_json(const char* key, const char* fmt, ...) {
> char buf[1024];
> va_list args;
> va_start(args, format);
>
> if (iw_json) {
> vsnprintf(buf, sizeof(buf), format, args);
> print_string(PRINT_JSON, key, "%s", buf);
> } else {
> printf("\t * %s: ", key);

Guess we'd have to be careful about the indentation. Track the (JSON)
object nesting level and apply it that way?

johannes


2024-03-05 15:56:53

by Jeff Johnson

[permalink] [raw]
Subject: Re: [PATCH v5] Add JSON output options to 'iw' for scan results

On 3/5/2024 1:41 AM, Johannes Berg wrote:
> On Mon, 2024-03-04 at 23:01 +0300, Isaev Ruslan wrote:
>> v5 changes:
>> - add json print to print_ap_channel_report()
>> - minor refactor open_json_object()
>
> Alright, yay, so mechanical submission issues out of the way, this looks
> readable :)


Did the v5 version hit the list? I don't see it, and neither does lore:
<https://lore.kernel.org/all/[email protected]/>

I received v4 directly, not via the list, and that was addressed to
"Undisclosed Recipients". lore also doesn't have v4:
<https://lore.kernel.org/all/[email protected]/>

So I don't think the mechanical submission issues are out of the way
unless the list rejected the patch due to size, which seems unlikely.

/jeff

2024-03-05 16:36:41

by Aditya Kumar Singh

[permalink] [raw]
Subject: Re: [PATCH v5] Add JSON output options to 'iw' for scan results

On 3/5/24 21:25, Jeff Johnson wrote:
> On 3/5/2024 1:41 AM, Johannes Berg wrote:
>> On Mon, 2024-03-04 at 23:01 +0300, Isaev Ruslan wrote:
>>> v5 changes:
>>> - add json print to print_ap_channel_report()
>>> - minor refactor open_json_object()
>>
>> Alright, yay, so mechanical submission issues out of the way, this looks
>> readable :)
>
>
> Did the v5 version hit the list? I don't see it, and neither does lore:
> <https://lore.kernel.org/all/[email protected]/>
>
> I received v4 directly, not via the list, and that was addressed to
> "Undisclosed Recipients". lore also doesn't have v4:
> <https://lore.kernel.org/all/[email protected]/>
>
> So I don't think the mechanical submission issues are out of the way
> unless the list rejected the patch due to size, which seems unlikely.
>

Yes it did -
https://patchwork.kernel.org/project/linux-wireless/patch/[email protected]/

Looks like the first reply started a separate thread. Probably
--in-reply-to was missed.


2024-03-05 17:04:13

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH v5] Add JSON output options to 'iw' for scan results

On Tue, 2024-03-05 at 22:05 +0530, Aditya Kumar Singh wrote:
>
> Yes it did -
> https://patchwork.kernel.org/project/linux-wireless/patch/[email protected]/
>
> Looks like the first reply started a separate thread. Probably
> --in-reply-to was missed.
>

No, somehow I got two copies in my inbox, the first of which hadn't made
it to the list for some reason, but I didn't notice in time that it
hadn't ... Sorry.

johannes

2024-03-05 17:47:51

by Jeff Johnson

[permalink] [raw]
Subject: Re: [PATCH v5] Add JSON output options to 'iw' for scan results

On 3/5/2024 8:35 AM, Aditya Kumar Singh wrote:
> On 3/5/24 21:25, Jeff Johnson wrote:
>> On 3/5/2024 1:41 AM, Johannes Berg wrote:
>>> On Mon, 2024-03-04 at 23:01 +0300, Isaev Ruslan wrote:
>>>> v5 changes:
>>>> - add json print to print_ap_channel_report()
>>>> - minor refactor open_json_object()
>>>
>>> Alright, yay, so mechanical submission issues out of the way, this looks
>>> readable :)
>>
>>
>> Did the v5 version hit the list? I don't see it, and neither does lore:
>> <https://lore.kernel.org/all/[email protected]/>
>>
>> I received v4 directly, not via the list, and that was addressed to
>> "Undisclosed Recipients". lore also doesn't have v4:
>> <https://lore.kernel.org/all/[email protected]/>
>>
>> So I don't think the mechanical submission issues are out of the way
>> unless the list rejected the patch due to size, which seems unlikely.
>>
>
> Yes it did -
> https://patchwork.kernel.org/project/linux-wireless/patch/[email protected]/
>
> Looks like the first reply started a separate thread. Probably
> --in-reply-to was missed.
>

Very strange. There is obviously still some Message-Id: strangeness
since the patchwork message id (and lore message id)
"[email protected]" doesn't match
the In-Reply-To message id in Johannes' reply
"[email protected]"

Perhaps the patch was sent to Johannes as a separate unicast message
that was separate from the message sent to the list?

/jeff