netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH iproute2 json v2 00/27] ip: add -json support to 'ip link show'
@ 2017-08-17 17:35 Julien Fortin
  2017-08-17 17:35 ` [PATCH iproute2 json v2 01/27] color: add new COLOR_NONE and disable_color function Julien Fortin
                   ` (28 more replies)
  0 siblings, 29 replies; 33+ messages in thread
From: Julien Fortin @ 2017-08-17 17:35 UTC (permalink / raw)
  To: netdev; +Cc: roopa, nikolay, dsa, Julien Fortin

From: Julien Fortin <julien@cumulusnetworks.com>

This patch series adds json support to 'ip [-details] link show [dev DEV]'
Each patch describes the json schema it adds and provides some examples.

Julien Fortin (27):
  color: add new COLOR_NONE and disable_color function
  ip: add new command line argument -json (mutually exclusive with
    -color)
  json_writer: add new json handlers (null, float with format, lluint,
    hu)
  ip: ip_print: add new API to print JSON or regular format output
  ip: ipaddress.c: add support for json output
  ip: iplink.c: open/close json obj for ip -brief -json link show dev
    DEV
  ip: iplink_bond.c: add json output support
  ip: iplink_bond_slave.c: add json output support (info_slave_data)
  ip: iplink_hsr.c: add json output support
  ip: iplink_bridge.c: add json output support
  ip: iplink_bridge_slave.c: add json output support
  ip: iplink_can.c: add json output support
  ip: iplink_geneve.c: add json output support
  ip: iplink_ipoib.c: add json output support
  ip: iplink_ipvlan.c: add json output support
  ip: iplink_vrf.c: add json output support
  ip: iplink_vxlan.c: add json output support
  ip: iplink_xdp.c: add json output support
  ip: ipmacsec.c: add json output support
  ip: link_gre.c: add json output support
  ip: link_gre6.c: add json output support
  ip: link_ip6tnl.c: add json output support
  ip: link_iptnl.c: add json output support
  ip: link_vti.c: add json output support
  ip: link_vti6.c: add json output support
  ip: link_macvlan.c: add json output support
  ip: iplink_vlan.c: add json output support

 include/color.h          |    2 +
 include/json_writer.h    |    9 +
 include/utils.h          |    1 +
 ip/Makefile              |    2 +-
 ip/ip.c                  |    6 +
 ip/ip_common.h           |   56 +++
 ip/ip_print.c            |  233 ++++++++++
 ip/ipaddress.c           | 1064 ++++++++++++++++++++++++++++++++--------------
 ip/iplink.c              |    2 +
 ip/iplink_bond.c         |  231 ++++++----
 ip/iplink_bond_slave.c   |   57 ++-
 ip/iplink_bridge.c       |  293 ++++++++-----
 ip/iplink_bridge_slave.c |  185 ++++----
 ip/iplink_can.c          |  282 ++++++++----
 ip/iplink_geneve.c       |   86 +++-
 ip/iplink_hsr.c          |   36 +-
 ip/iplink_ipoib.c        |   30 +-
 ip/iplink_ipvlan.c       |    8 +-
 ip/iplink_macvlan.c      |   37 +-
 ip/iplink_vlan.c         |   62 ++-
 ip/iplink_vrf.c          |   13 +-
 ip/iplink_vxlan.c        |  161 ++++---
 ip/iplink_xdp.c          |   31 +-
 ip/ipmacsec.c            |   84 +++-
 ip/link_gre.c            |  147 ++++---
 ip/link_gre6.c           |  142 +++++--
 ip/link_ip6tnl.c         |  172 +++++---
 ip/link_iptnl.c          |  155 ++++---
 ip/link_vti.c            |   24 +-
 ip/link_vti6.c           |   22 +-
 lib/color.c              |   12 +-
 lib/json_writer.c        |   44 +-
 32 files changed, 2663 insertions(+), 1026 deletions(-)
 create mode 100644 ip/ip_print.c

-- 
2.14.1

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH iproute2 json v2 01/27] color: add new COLOR_NONE and disable_color function
  2017-08-17 17:35 [PATCH iproute2 json v2 00/27] ip: add -json support to 'ip link show' Julien Fortin
@ 2017-08-17 17:35 ` Julien Fortin
  2017-08-17 17:35 ` [PATCH iproute2 json v2 02/27] ip: add new command line argument -json (mutually exclusive with -color) Julien Fortin
                   ` (27 subsequent siblings)
  28 siblings, 0 replies; 33+ messages in thread
From: Julien Fortin @ 2017-08-17 17:35 UTC (permalink / raw)
  To: netdev; +Cc: roopa, nikolay, dsa, Julien Fortin

From: Julien Fortin <julien@cumulusnetworks.com>

Signed-off-by: Julien Fortin <julien@cumulusnetworks.com>
---
 include/color.h |  2 ++
 lib/color.c     | 12 ++++++++++--
 2 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/include/color.h b/include/color.h
index ba0b237e..1cd6f7d2 100644
--- a/include/color.h
+++ b/include/color.h
@@ -2,6 +2,7 @@
 #define __COLOR_H__ 1
 
 enum color_attr {
+	COLOR_NONE,
 	COLOR_IFNAME,
 	COLOR_MAC,
 	COLOR_INET,
@@ -12,6 +13,7 @@ enum color_attr {
 };
 
 void enable_color(void);
+void check_if_color_enabled(void);
 void set_color_palette(void);
 int color_fprintf(FILE *fp, enum color_attr attr, const char *fmt, ...);
 enum color_attr ifa_family_color(__u8 ifa_family);
diff --git a/lib/color.c b/lib/color.c
index 4e947500..79d5e289 100644
--- a/lib/color.c
+++ b/lib/color.c
@@ -89,6 +89,14 @@ void set_color_palette(void)
 		is_dark_bg = 1;
 }
 
+void check_if_color_enabled(void)
+{
+	if (color_is_enabled) {
+		fprintf(stderr, "Option \"-json\" conflicts with \"-color\".\n");
+		exit(1);
+	}
+}
+
 int color_fprintf(FILE *fp, enum color_attr attr, const char *fmt, ...)
 {
 	int ret = 0;
@@ -96,13 +104,13 @@ int color_fprintf(FILE *fp, enum color_attr attr, const char *fmt, ...)
 
 	va_start(args, fmt);
 
-	if (!color_is_enabled) {
+	if (!color_is_enabled || attr == COLOR_NONE) {
 		ret = vfprintf(fp, fmt, args);
 		goto end;
 	}
 
 	ret += fprintf(fp, "%s",
-		       color_codes[attr_colors[is_dark_bg ? attr + 7 : attr]]);
+		       color_codes[attr_colors[is_dark_bg ? attr + 8 : attr]]);
 	ret += vfprintf(fp, fmt, args);
 	ret += fprintf(fp, "%s", color_codes[C_CLEAR]);
 
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH iproute2 json v2 02/27] ip: add new command line argument -json (mutually exclusive with -color)
  2017-08-17 17:35 [PATCH iproute2 json v2 00/27] ip: add -json support to 'ip link show' Julien Fortin
  2017-08-17 17:35 ` [PATCH iproute2 json v2 01/27] color: add new COLOR_NONE and disable_color function Julien Fortin
@ 2017-08-17 17:35 ` Julien Fortin
  2017-08-17 17:35 ` [PATCH iproute2 json v2 03/27] json_writer: add new json handlers (null, float with format, lluint, hu) Julien Fortin
                   ` (26 subsequent siblings)
  28 siblings, 0 replies; 33+ messages in thread
From: Julien Fortin @ 2017-08-17 17:35 UTC (permalink / raw)
  To: netdev; +Cc: roopa, nikolay, dsa, Julien Fortin

From: Julien Fortin <julien@cumulusnetworks.com>

Signed-off-by: Julien Fortin <julien@cumulusnetworks.com>
---
 include/utils.h | 1 +
 ip/ip.c         | 6 ++++++
 2 files changed, 7 insertions(+)

diff --git a/include/utils.h b/include/utils.h
index 6080b962..565bda60 100644
--- a/include/utils.h
+++ b/include/utils.h
@@ -20,6 +20,7 @@ extern int show_raw;
 extern int resolve_hosts;
 extern int oneline;
 extern int brief;
+extern int json;
 extern int timestamp;
 extern int timestamp_short;
 extern const char * _SL_;
diff --git a/ip/ip.c b/ip/ip.c
index 7c14a8ec..e66f6970 100644
--- a/ip/ip.c
+++ b/ip/ip.c
@@ -33,6 +33,7 @@ int show_details;
 int resolve_hosts;
 int oneline;
 int brief;
+int json;
 int timestamp;
 const char *_SL_;
 int force;
@@ -258,6 +259,8 @@ int main(int argc, char **argv)
 			batch_file = argv[1];
 		} else if (matches(opt, "-brief") == 0) {
 			++brief;
+		} else if (matches(opt, "-json") == 0) {
+			++json;
 		} else if (matches(opt, "-rcvbuf") == 0) {
 			unsigned int size;
 
@@ -292,6 +295,9 @@ int main(int argc, char **argv)
 
 	_SL_ = oneline ? "\\" : "\n";
 
+	if (json)
+		check_if_color_enabled();
+
 	if (batch_file)
 		return batch(batch_file);
 
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH iproute2 json v2 03/27] json_writer: add new json handlers (null, float with format, lluint, hu)
  2017-08-17 17:35 [PATCH iproute2 json v2 00/27] ip: add -json support to 'ip link show' Julien Fortin
  2017-08-17 17:35 ` [PATCH iproute2 json v2 01/27] color: add new COLOR_NONE and disable_color function Julien Fortin
  2017-08-17 17:35 ` [PATCH iproute2 json v2 02/27] ip: add new command line argument -json (mutually exclusive with -color) Julien Fortin
@ 2017-08-17 17:35 ` Julien Fortin
  2017-08-17 17:35 ` [PATCH iproute2 json v2 04/27] ip: ip_print: add new API to print JSON or regular format output Julien Fortin
                   ` (25 subsequent siblings)
  28 siblings, 0 replies; 33+ messages in thread
From: Julien Fortin @ 2017-08-17 17:35 UTC (permalink / raw)
  To: netdev; +Cc: roopa, nikolay, dsa, Julien Fortin

From: Julien Fortin <julien@cumulusnetworks.com>

Signed-off-by: Julien Fortin <julien@cumulusnetworks.com>
---
 include/json_writer.h |  9 +++++++++
 lib/json_writer.c     | 44 ++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 49 insertions(+), 4 deletions(-)

diff --git a/include/json_writer.h b/include/json_writer.h
index ab9a008a..1516aafb 100644
--- a/include/json_writer.h
+++ b/include/json_writer.h
@@ -33,20 +33,29 @@ void jsonw_pretty(json_writer_t *self, bool on);
 void jsonw_name(json_writer_t *self, const char *name);
 
 /* Add value  */
+void jsonw_printf(json_writer_t *self, const char *fmt, ...);
 void jsonw_string(json_writer_t *self, const char *value);
 void jsonw_bool(json_writer_t *self, bool value);
 void jsonw_float(json_writer_t *self, double number);
+void jsonw_float_fmt(json_writer_t *self, const char *fmt, double num);
 void jsonw_uint(json_writer_t *self, uint64_t number);
+void jsonw_hu(json_writer_t *self, unsigned short number);
 void jsonw_int(json_writer_t *self, int64_t number);
 void jsonw_null(json_writer_t *self);
+void jsonw_lluint(json_writer_t *self, unsigned long long int 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_float_field(json_writer_t *self, const char *prop, double num);
 void jsonw_uint_field(json_writer_t *self, const char *prop, uint64_t num);
+void jsonw_hu_field(json_writer_t *self, const char *prop, unsigned short num);
 void jsonw_int_field(json_writer_t *self, const char *prop, int64_t num);
 void jsonw_null_field(json_writer_t *self, const char *prop);
+void jsonw_lluint_field(json_writer_t *self, const char *prop,
+			unsigned long long int num);
+void jsonw_float_field_fmt(json_writer_t *self, const char *prop,
+			   const char *fmt, double val);
 
 /* Collections */
 void jsonw_start_object(json_writer_t *self);
diff --git a/lib/json_writer.c b/lib/json_writer.c
index 9fc05e96..6b77d288 100644
--- a/lib/json_writer.c
+++ b/lib/json_writer.c
@@ -156,7 +156,7 @@ void jsonw_name(json_writer_t *self, const char *name)
 		putc(' ', self->out);
 }
 
-static void jsonw_printf(json_writer_t *self, const char *fmt, ...)
+void jsonw_printf(json_writer_t *self, const char *fmt, ...)
 {
 	va_list ap;
 
@@ -199,23 +199,38 @@ void jsonw_bool(json_writer_t *self, bool val)
 	jsonw_printf(self, "%s", val ? "true" : "false");
 }
 
-#ifdef notused
 void jsonw_null(json_writer_t *self)
 {
 	jsonw_printf(self, "null");
 }
 
+void jsonw_float_fmt(json_writer_t *self, const char *fmt, double num)
+{
+	jsonw_printf(self, fmt, num);
+}
+
+#ifdef notused
 void jsonw_float(json_writer_t *self, double num)
 {
 	jsonw_printf(self, "%g", num);
 }
 #endif
 
+void jsonw_hu(json_writer_t *self, unsigned short num)
+{
+	jsonw_printf(self, "%hu", num);
+}
+
 void jsonw_uint(json_writer_t *self, uint64_t num)
 {
 	jsonw_printf(self, "%"PRIu64, num);
 }
 
+void jsonw_lluint(json_writer_t *self, unsigned long long int num)
+{
+	jsonw_printf(self, "%llu", num);
+}
+
 void jsonw_int(json_writer_t *self, int64_t num)
 {
 	jsonw_printf(self, "%"PRId64, num);
@@ -242,25 +257,46 @@ void jsonw_float_field(json_writer_t *self, const char *prop, double val)
 }
 #endif
 
+void jsonw_float_field_fmt(json_writer_t *self,
+			   const char *prop,
+			   const char *fmt,
+			   double val)
+{
+	jsonw_name(self, prop);
+	jsonw_float_fmt(self, fmt, val);
+}
+
 void jsonw_uint_field(json_writer_t *self, const char *prop, uint64_t num)
 {
 	jsonw_name(self, prop);
 	jsonw_uint(self, num);
 }
 
+void jsonw_hu_field(json_writer_t *self, const char *prop, unsigned short num)
+{
+	jsonw_name(self, prop);
+	jsonw_hu(self, num);
+}
+
+void jsonw_lluint_field(json_writer_t *self,
+			const char *prop,
+			unsigned long long int num)
+{
+	jsonw_name(self, prop);
+	jsonw_lluint(self, num);
+}
+
 void jsonw_int_field(json_writer_t *self, const char *prop, int64_t num)
 {
 	jsonw_name(self, prop);
 	jsonw_int(self, num);
 }
 
-#ifdef notused
 void jsonw_null_field(json_writer_t *self, const char *prop)
 {
 	jsonw_name(self, prop);
 	jsonw_null(self);
 }
-#endif
 
 #ifdef TEST
 int main(int argc, char **argv)
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH iproute2 json v2 04/27] ip: ip_print: add new API to print JSON or regular format output
  2017-08-17 17:35 [PATCH iproute2 json v2 00/27] ip: add -json support to 'ip link show' Julien Fortin
                   ` (2 preceding siblings ...)
  2017-08-17 17:35 ` [PATCH iproute2 json v2 03/27] json_writer: add new json handlers (null, float with format, lluint, hu) Julien Fortin
@ 2017-08-17 17:35 ` Julien Fortin
  2017-08-17 17:35 ` [PATCH iproute2 json v2 05/27] ip: ipaddress.c: add support for json output Julien Fortin
                   ` (24 subsequent siblings)
  28 siblings, 0 replies; 33+ messages in thread
From: Julien Fortin @ 2017-08-17 17:35 UTC (permalink / raw)
  To: netdev; +Cc: roopa, nikolay, dsa, Julien Fortin

From: Julien Fortin <julien@cumulusnetworks.com>

To avoid code duplication and have a ligther impact on most of the files,
these functions were made to handle both stdout (FP context) or JSON
output. Using this api, the changes are easier to read and the code
stays as compact as possible.

includes json_writer.h in ip_common.h to make the lib/json_writer.c
functions available to the new "ip_print" api.

Signed-off-by: Julien Fortin <julien@cumulusnetworks.com>
---
 ip/Makefile    |   2 +-
 ip/ip_common.h |  56 ++++++++++++++
 ip/ip_print.c  | 233 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 290 insertions(+), 1 deletion(-)
 create mode 100644 ip/ip_print.c

diff --git a/ip/Makefile b/ip/Makefile
index a754c04d..8ed2686c 100644
--- a/ip/Makefile
+++ b/ip/Makefile
@@ -9,7 +9,7 @@ IPOBJ=ip.o ipaddress.o ipaddrlabel.o iproute.o iprule.o ipnetns.o \
     link_iptnl.o link_gre6.o iplink_bond.o iplink_bond_slave.o iplink_hsr.o \
     iplink_bridge.o iplink_bridge_slave.o ipfou.o iplink_ipvlan.o \
     iplink_geneve.o iplink_vrf.o iproute_lwtunnel.o ipmacsec.o ipila.o \
-    ipvrf.o iplink_xstats.o ipseg6.o
+    ipvrf.o iplink_xstats.o ipseg6.o ip_print.o
 
 RTMONOBJ=rtmon.o
 
diff --git a/ip/ip_common.h b/ip/ip_common.h
index 77e9dd06..efc789cb 100644
--- a/ip/ip_common.h
+++ b/ip/ip_common.h
@@ -140,3 +140,59 @@ int name_is_vrf(const char *name);
 #endif
 
 void print_num(FILE *fp, unsigned int width, uint64_t count);
+
+#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, FILE *fp);
+void delete_json_obj(void);
+
+bool is_json_context(void);
+
+void set_current_fp(FILE *fp);
+
+void fflush_fp(void);
+
+void open_json_object(const char *str);
+void close_json_object(void);
+void open_json_array(enum output_type type, const char *delim);
+void close_json_array(enum output_type type, const char *delim);
+
+#include "color.h"
+
+#define _PRINT_FUNC(type_name, type)					\
+	void print_color_##type_name(enum output_type t,		\
+				     enum color_attr color,		\
+				     const char *key,			\
+				     const char *fmt,			\
+				     type value);			\
+									\
+	static inline void print_##type_name(enum output_type t,	\
+					     const char *key,		\
+					     const char *fmt,		\
+					     type value)		\
+	{								\
+		print_color_##type_name(t, -1, key, fmt, value);	\
+	}
+_PRINT_FUNC(int, int);
+_PRINT_FUNC(bool, bool);
+_PRINT_FUNC(null, const char*);
+_PRINT_FUNC(string, const char*);
+_PRINT_FUNC(uint, uint64_t);
+_PRINT_FUNC(hu, unsigned short);
+_PRINT_FUNC(hex, unsigned int);
+_PRINT_FUNC(0xhex, unsigned int);
+_PRINT_FUNC(lluint, unsigned long long int);
+#undef _PRINT_FUNC
diff --git a/ip/ip_print.c b/ip/ip_print.c
new file mode 100644
index 00000000..4cd6a0bc
--- /dev/null
+++ b/ip/ip_print.c
@@ -0,0 +1,233 @@
+/*
+ * ip_print.c          "ip print regular or json output".
+ *
+ *             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, <julien@cumulusnetworks.com>
+ *
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "utils.h"
+#include "ip_common.h"
+#include "json_writer.h"
+
+static json_writer_t *_jw;
+static FILE *_fp;
+
+#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, FILE *fp)
+{
+	if (json) {
+		_jw = jsonw_new(fp);
+		if (!_jw) {
+			perror("json object");
+			exit(1);
+		}
+		jsonw_pretty(_jw, true);
+		jsonw_start_array(_jw);
+	}
+	set_current_fp(fp);
+}
+
+void delete_json_obj(void)
+{
+	if (_jw) {
+		jsonw_end_array(_jw);
+		jsonw_destroy(&_jw);
+	}
+}
+
+bool is_json_context(void)
+{
+	return _jw != NULL;
+}
+
+void set_current_fp(FILE *fp)
+{
+	if (!fp) {
+		fprintf(stderr, "Error: invalid file pointer.\n");
+		exit(1);
+	}
+	_fp = fp;
+}
+
+json_writer_t *get_json_writer(void)
+{
+	return _jw;
+}
+
+void open_json_object(const char *str)
+{
+	if (_IS_JSON_CONTEXT(PRINT_JSON)) {
+		if (str)
+			jsonw_name(_jw, str);
+		jsonw_start_object(_jw);
+	}
+}
+
+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 as array delimiter in non-json context.
+ */
+void open_json_array(enum output_type type, const char *str)
+{
+	if (_IS_JSON_CONTEXT(type)) {
+		if (str)
+			jsonw_name(_jw, str);
+		jsonw_start_array(_jw);
+	} else if (_IS_FP_CONTEXT(type)) {
+		fprintf(_fp, "%s", str);
+	}
+}
+
+/*
+ * End json array or string array
+ */
+void close_json_array(enum output_type type, const char *str)
+{
+	if (_IS_JSON_CONTEXT(type)) {
+		jsonw_pretty(_jw, false);
+		jsonw_end_array(_jw);
+		jsonw_pretty(_jw, true);
+	} else if (_IS_FP_CONTEXT(type)) {
+		fprintf(_fp, "%s", str);
+	}
+}
+
+/*
+ * pre-processor directive to generate similar
+ * functions handling different types
+ */
+#define _PRINT_FUNC(type_name, type)					\
+	void print_color_##type_name(enum output_type t,		\
+				     enum color_attr color,		\
+				     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)) {				\
+			color_fprintf(_fp, color, fmt, value);          \
+		}							\
+	}
+_PRINT_FUNC(int, int);
+_PRINT_FUNC(hu, unsigned short);
+_PRINT_FUNC(uint, uint64_t);
+_PRINT_FUNC(lluint, unsigned long long int);
+#undef _PRINT_FUNC
+
+void print_color_string(enum output_type type,
+			enum color_attr color,
+			const char *key,
+			const char *fmt,
+			const char *value)
+{
+	if (_IS_JSON_CONTEXT(type)) {
+		if (key && !value)
+			jsonw_name(_jw, key);
+		else if (!key && value)
+			jsonw_string(_jw, value);
+		else
+			jsonw_string_field(_jw, key, value);
+	} else if (_IS_FP_CONTEXT(type)) {
+		color_fprintf(_fp, color, fmt, value);
+	}
+}
+
+/*
+ * 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_color_bool(enum output_type type,
+		      enum color_attr color,
+		      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)) {
+		color_fprintf(_fp, color, fmt, value ? "true" : "false");
+	}
+}
+
+/*
+ * In JSON context uses hardcode %#x format: 42 -> 0x2a
+ */
+void print_color_0xhex(enum output_type type,
+		       enum color_attr color,
+		       const char *key,
+		       const char *fmt,
+		       unsigned int hex)
+{
+	if (_IS_JSON_CONTEXT(type)) {
+		SPRINT_BUF(b1);
+
+		snprintf(b1, sizeof(b1), "%#x", hex);
+		print_string(PRINT_JSON, key, NULL, b1);
+	} else if (_IS_FP_CONTEXT(type)) {
+		color_fprintf(_fp, color, fmt, hex);
+	}
+}
+
+void print_color_hex(enum output_type type,
+		     enum color_attr color,
+		     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)) {
+		color_fprintf(_fp, color, 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_color_null(enum output_type type,
+		      enum color_attr color,
+		      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)) {
+		color_fprintf(_fp, color, fmt, value);
+	}
+}
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH iproute2 json v2 05/27] ip: ipaddress.c: add support for json output
  2017-08-17 17:35 [PATCH iproute2 json v2 00/27] ip: add -json support to 'ip link show' Julien Fortin
                   ` (3 preceding siblings ...)
  2017-08-17 17:35 ` [PATCH iproute2 json v2 04/27] ip: ip_print: add new API to print JSON or regular format output Julien Fortin
@ 2017-08-17 17:35 ` Julien Fortin
  2017-09-20 13:33   ` Sabrina Dubroca
  2017-08-17 17:35 ` [PATCH iproute2 json v2 06/27] ip: iplink.c: open/close json obj for ip -brief -json link show dev DEV Julien Fortin
                   ` (23 subsequent siblings)
  28 siblings, 1 reply; 33+ messages in thread
From: Julien Fortin @ 2017-08-17 17:35 UTC (permalink / raw)
  To: netdev; +Cc: roopa, nikolay, dsa, Julien Fortin

From: Julien Fortin <julien@cumulusnetworks.com>

This patch converts all output (mostly fprintfs) to the new ip_print api
which handle both regular and json output.
Initialize a json_writer and open an array object if -json was specified.
Note that the JSON attribute naming follows the NETLINK_ATTRIBUTE naming.

In many places throughout the code, IP, matches integer values with
hardcoded strings tables, such as link mode, link operstate or link
family.
In JSON context, this will result in a named string field. In the
very unlikely event that the requested index is out of bound, IP
displays the raw integer value. For JSON context this result in
having a different integer field example bellow:

if (mode >= ARRAY_SIZE(link_modes))
	print_int(PRINT_ANY, "linkmode_index", "mode %d ", mode);
else
	print_string(PRINT_ANY, "linkmode", "mode %s ",
		     link_modes[mode]);

The "_index" suffix is open to discussion and it is something that I came
up with. The bottom line is that you can't have a string field that may
become an int field in specific cases. Programs written in strongly type
languages (like C) might break if they are expecting a string value and
got an integer instead. We don't want to confuse anybody or make the code
even more complicated handling these specifics cases.
Hence the extra "_index" field that is easy to check for and deal with.

JSON schema, followed by live example:

Live config used:
$ ip link add dev vxlan42 type vxlan id 42
$ ip link add dev bond0 type bond
$ ip link add name swp1.50 link swp1 type vlan id 50
$ ip link add dev br0 type bridge
$ ip link set dev vxlan42 master br0
$ ip link set dev bond0 master br0
$ ip link set dev swp1.50 master br0
$ ip link set dev br0 up

$ ip -d link show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode
DEFAULT group default
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 promiscuity 0
addrgenmode eui64
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast
state UP mode DEFAULT group default qlen 1000
    link/ether 08:00:27:db:31:88 brd ff:ff:ff:ff:ff:ff promiscuity 0
addrgenmode eui64
3: swp1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode
DEFAULT group default qlen 1000
    link/ether 08:00:27:5b:b1:75 brd ff:ff:ff:ff:ff:ff promiscuity 0
addrgenmode eui64
10: vxlan42: <BROADCAST,MULTICAST> mtu 1500 qdisc noop master br0 state
DOWN mode DEFAULT group default
    link/ether 4a:d9:91:42:a2:d2 brd ff:ff:ff:ff:ff:ff promiscuity 1
    vxlan id 42 srcport 0 0 dstport 8472 ageing 300
    bridge_slave state disabled priority 8 cost 100 hairpin off guard off
root_block off fastleave off learning on flood on port_id 0x8001 port_no
0x1 designated_port 32769 designated_cost 0 designated_bridge
8000.8:0:27:5b:b1:75 designated_root 8000.8:0:27:5b:b1:75 hold_timer
0.00 message_age_timer    0.00 forward_delay_timer    0.00
topology_change_ack 0 config_pending 0 proxy_arp off proxy_arp_wifi off
mcast_router 1 mcast_fast_leave off mcast_flood on neigh_suppress off
addrgenmode eui64
11: bond0: <BROADCAST,MULTICAST,MASTER> mtu 1500 qdisc noop master br0
state DOWN mode DEFAULT group default
    link/ether e2:aa:7b:17:c5:14 brd ff:ff:ff:ff:ff:ff promiscuity 1
    bond mode 802.3ad miimon 100 updelay 0 downdelay 0 use_carrier 1
arp_interval 0 arp_validate none arp_all_targets any primary_reselect
always fail_over_mac none xmit_hash_policy layer3+4 resend_igmp 1
num_grat_arp 1 all_slaves_active 0 min_links 1 lp_interval 1
packets_per_slave 1 lacp_rate fast ad_select stable ad_actor_sys_prio
65535 ad_user_port_key 0 ad_actor_system 00:00:00:00:00:00
    bridge_slave state disabled priority 8 cost 100 hairpin off guard off
root_block off fastleave off learning on flood on port_id 0x8002 port_no
0x2 designated_port 32770 designated_cost 0 designated_bridge
8000.8:0:27:5b:b1:75 designated_root 8000.8:0:27:5b:b1:75 hold_timer
0.00 message_age_timer    0.00 forward_delay_timer    0.00
topology_change_ack 0 config_pending 0 proxy_arp off proxy_arp_wifi off
mcast_router 1 mcast_fast_leave off mcast_flood on neigh_suppress off
addrgenmode eui64
12: swp1.50@swp1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop master
br0 state DOWN mode DEFAULT group default
    link/ether 08:00:27:5b:b1:75 brd ff:ff:ff:ff:ff:ff promiscuity 1
    vlan protocol 802.1Q id 50 <REORDER_HDR>
    bridge_slave state disabled priority 8 cost 100 hairpin off guard off
root_block off fastleave off learning on flood on port_id 0x8003 port_no
0x3 designated_port 32771 designated_cost 0 designated_bridge
8000.8:0:27:5b:b1:75 designated_root 8000.8:0:27:5b:b1:75 hold_timer
0.00 message_age_timer    0.00 forward_delay_timer    0.00
topology_change_ack 0 config_pending 0 proxy_arp off proxy_arp_wifi off
mcast_router 1 mcast_fast_leave off mcast_flood on neigh_suppress off
addrgenmode eui64
13: br0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state
DOWN mode DEFAULT group default
    link/ether 08:00:27:5b:b1:75 brd ff:ff:ff:ff:ff:ff promiscuity 0
    bridge forward_delay 1500 hello_time 200 max_age 2000 ageing_time
30000 stp_state 0 priority 32768 vlan_filtering 0 vlan_protocol 802.1Q
bridge_id 8000.8:0:27:5b:b1:75 designated_root 8000.8:0:27:5b:b1:75
root_port 0 root_path_cost 0 topology_change 0 topology_change_detected 0
hello_timer    0.00 tcn_timer    0.00 topology_change_timer    0.00
gc_timer  244.44 vlan_default_pvid 1 vlan_stats_enabled 0 group_fwd_mask 0
group_address 01:80:c2:00:00:00 mcast_snooping 1 mcast_router 1
mcast_query_use_ifaddr 0 mcast_querier 0 mcast_hash_elasticity 4096
mcast_hash_max 4096 mcast_last_member_count 2 mcast_startup_query_count 2
mcast_last_member_interval 100 mcast_membership_interval 26000
mcast_querier_interval 25500 mcast_query_interval 12500
mcast_query_response_interval 1000 mcast_startup_query_interval 3125
mcast_stats_enabled 0 mcast_igmp_version 2 mcast_mld_version 1
nf_call_iptables 0 nf_call_ip6tables 0 nf_call_arptables 0 addrgenmode
eui64

// Schema for: ip -brief link show
[
    {
        "deleted": {
            "type": "bool",
            "attr": "RTM_DELLINK"
        },
        "link": {
            "type": "string",
            "attr": "IFLA_LINK"
        },
        "ifname": {
            "type": "string",
            "attr": "IFNAME"
        },
        "operstate": {
            "type": "string",
            "attr": "IFLA_OPERSTATE",
            "mutually_exclusive": {
                "operstate_index": {
                    "type": "uint",
                    "comment": "if state >= ARRAY_SIZE(oper_states)"
                }
            }
        },
        "address": {
            "type": "string",
            "attr": "IFLA_ADDRESS"
        },
        "flags": {
            "type": "array",
            "attr": "IFF_LOOPBACK, IFF_BROADCAST...IFF_*"
        },
        "addr_info": {
            "type": "array",
            "array": [
                {
                    "deleted": {
                        "type": "bool",
                        "attr": "RTM_DELADDR"
                    },
                    "family": {
                        "type": "string",
                        "attr": "ifa->ifa_family",
                        "mutually_exclusive": {
                            "family_index": {
                                "type": "uint",
                                "comment": "if family is not known"
                            }
                        }
                    },
                    "local": {
                        "type": "string",
                        "attr": "IFA_LOCAL"
                    },
                    "address": {
                        "type": "string",
                        "attr": "IFLA_LOCAL && IFA_ADDRESS"
                    },
                    "prefixlen": {
                        "type": "int",
                        "attr": "IFLA_LOCAL"
                    }
                }
            ]
        }
    }
]

$ ip -json -brief link show
[{
        "ifname": "lo",
        "operstate": "UNKNOWN",
        "address": "00:00:00:00:00:00",
        "flags": ["LOOPBACK","UP","LOWER_UP"]
    },{
        "ifname": "eth0",
        "operstate": "UP",
        "address": "08:00:27:db:31:88",
        "flags": ["BROADCAST","MULTICAST","UP","LOWER_UP"]
    },{
        "ifname": "swp1",
        "operstate": "DOWN",
        "address": "08:00:27:5b:b1:75",
        "flags": ["BROADCAST","MULTICAST"]
    },{
        "ifname": "vxlan42",
        "operstate": "DOWN",
        "address": "4a:d9:91:42:a2:d2",
        "flags": ["BROADCAST","MULTICAST"]
    },{
        "ifname": "bond0",
        "operstate": "DOWN",
        "address": "e2:aa:7b:17:c5:14",
        "flags": ["BROADCAST","MULTICAST","MASTER"]
    },{
        "link": "swp1",
        "ifname": "swp1.50",
        "operstate": "DOWN",
        "address": "08:00:27:5b:b1:75",
        "flags": ["BROADCAST","MULTICAST","M-DOWN"]
    },{
        "ifname": "br0",
        "operstate": "DOWN",
        "address": "08:00:27:5b:b1:75",
        "flags": ["NO-CARRIER","BROADCAST","MULTICAST","UP"]
    }
]

Schema for normal plus -details: ip -json -details link show

[
    {
        "deleted": {
            "type": "bool",
            "attr": "RTM_DELLINK"
        },
        "ifindex": {
            "type": "int"
        },
        "ifname": {
            "type": "string",
            "attr": "IFLA_IFNAME"
        },
        "link": {
            "type": "string",
            "attr": "IFLA_LINK",
            "mutually_exclusive": {
                "link_index": {
                    "type": "int",
                    "comment": "if IFLA_LINK_NETNSID exists"
                }
            }
        },
        "flags": {
            "type": "array",
            "attr": "IFF_LOOPBACK, IFF_BROADCAST...IFF_*"
        },
        "mtu": {
            "type": "int",
            "attr": "IFLA_MTU"
        },
        "xdp": {
            "type": "object",
            "attr": "IFLA_XDP",
            "object": {
                "mode": {
                    "type": "utin",
                    "attr": "IFLA_XDP_ATTACHED"
                },
                "prog_id": {
                    "type": "uint",
                    "attr": "IFLA_XDP_PROG_ID"
                }
            }
        },
        "qdisc": {
            "type": "string",
            "attr": "IFLA_QDISC"
        },
        "master": {
            "type": "string",
            "attr": "IFLA_MASTER"
        },
        "operstate": {
            "type": "string",
            "attr": "IFLA_OPERSTATE",
            "mutually_exclusive": {
                "operstate_index": {
                    "type": "uint",
                    "comment": "if state >= ARRAY_SIZE(oper_states)"
                }
            }
        },
        "linkmode": {
            "type": "string",
            "attr": "IFLA_LINKMODE",
            "mutually_exclusive": {
                "linkmode_index": {
                    "type": "uint",
                    "comment": "if mode >= ARRAY_SIZE(link_modes)"
                }
            }
        },
        "group": {
            "type": "string",
            "attr": "IFLA_GROUP"
        },
        "txqlen": {
            "type": "int",
            "attr": "IFLA_TXQLEN"
        },
        "event": {
            "type": "string",
            "attr": "IFLA_EVENT",
            "mutually_exclusive": {
                "event_index": {
                    "type": "uint",
                    "attr": "IFLA_OPERSTATE",
                    "comment": "if event >= ARRAY_SIZE(link_events)"
                }
            }
        },
        "link_type": {
            "type": "string",
            "attr": "ifi_type"
        },
        "address": {
            "type": "string",
            "attr": "IFLA_ADDRESS"
        },
        "link_pointtopoint": {
            "type": "bool",
            "attr": "IFF_POINTOPOINT"
        },
        "broadcast": {
            "type": "string",
            "attr": "IFLA_BROADCAST"
        },
        "link_netnsid": {
            "type": "int",
            "attr": "IFLA_LINK_NETNSID"
        },
        "proto_down": {
            "type": "bool",
            "attr": "IFLA_PROTO_DOWN"
        },

        //
        // if -details
        //

        "promiscuity": {
            "type": "uint",
            "attr": "IFLA_PROMISCUITY"
        },
        "linkinfo": {
            "type": "dict",
            "attr": "IFLA_LINKINFO",
            "dict": {
                "info_kind": {
                    "type": "string",
                    "attr": "IFLA_INFO_KIND"
                },
                "info_data": {
                    "type": "dict",
                    "attr": "IFLA_INFO_DATA",
                    "dict": {}
                },
                "info_xstats": {
                    "type": "dict",
                    "attr": "IFLA_INFO_XSTATS",
                    "dict": {}
                },
                "info_slave_data": {
                    "type": "dict",
                    "attr": "IFLA_INFO_SLAVE_DATA",
                    "dict": {}
                }
            }
        },
        "inet6_addr_gen_mode": {
            "type": "string",
            "attr": "IFLA_INET6_ADDR_GEN_MODE"
        },
        "num_tx_queues": {
            "type": "uint",
            "attr": "IFLA_NUM_TX_QUEUES"
        },
        "num_rx_queues": {
            "type": "uint",
            "attr": "IFLA_NUM_RX_QUEUES"
        },
        "gso_max_size": {
            "type": "uint",
            "attr": "IFLA_GSO_MAX_SIZE"
        },
        "gso_max_segs": {
            "type": "uint",
            "attr": "IFLA_GSO_MAX_SEGS"
        },
        "phys_port_name": {
            "type": "string",
            "attr": "IFLA_PHYS_PORT_NAME"
        },
        "phys_port_id": {
            "type": "string",
            "attr": "IFLA_PHYS_PORT_ID"
        },
        "phys_switch_id": {
            "type": "string",
            "attr": "IFLA_PHYS_SWITCH_ID"
        },
        "ifalias": {
            "type": "string",
            "attr": "IFLA_IFALIAS"
        },
        "stats": {
            "type": "dict",
            "attr": "IFLA_STATS",
            "dict": {
                "rx": {
                    "type": "dict",
                    "dict": {
                        "bytes": {
                            "type": "uint"
                        },
                        "packets": {
                            "type": "uint"
                        },
                        "errors": {
                            "type": "uint"
                        },
                        "dropped": {
                            "type": "uint"
                        },
                        "over_errors": {
                            "type": "uint"
                        },
                        "multicast": {
                            "type": "uint"
                        },
                        "compressed": {
                            "type": "uint"
                        },
                        "length_errors": {
                            "type": "uint"
                        },
                        "crc_errors": {
                            "type": "uint"
                        },
                        "frame_errors": {
                            "type": "uint"
                        },
                        "fifo_errors": {
                            "type": "uint"
                        },
                        "missed_errors": {
                            "type": "uint"
                        },
                        "nohandler": {
                            "type": "uint"
                        }
                    }
                },
                "tx": {
                    "type": "dict",
                    "dict": {
                        "bytes": {
                            "type": "uint"
                        },
                        "packets": {
                            "type": "uint"
                        },
                        "errors": {
                            "type": "uint"
                        },
                        "dropped": {
                            "type": "uint"
                        },
                        "carrier_errors": {
                            "type": "uint"
                        },
                        "collisions": {
                            "type": "uint"
                        },
                        "compressed": {
                            "type": "uint"
                        },
                        "aborted_errors": {
                            "type": "uint"
                        },
                        "fifo_errors": {
                            "type": "uint"
                        },
                        "window_errors": {
                            "type": "uint"
                        },
                        "heartbeat_errors": {
                            "type": "uint"
                        },
                        "carrier_changes": {
                            "type": "uint"
                        }
                    }
                }
            }
        },
        "stats64": {
            "type": "dict",
            "attr": "IFLA_STATS64",
            "dict": {
                "rx": {
                    "type": "dict",
                    "dict": {
                        "bytes": {
                            "type": "uint"
                        },
                        "packets": {
                            "type": "uint"
                        },
                        "errors": {
                            "type": "uint"
                        },
                        "dropped": {
                            "type": "uint"
                        },
                        "over_errors": {
                            "type": "uint"
                        },
                        "multicast": {
                            "type": "uint"
                        },
                        "compressed": {
                            "type": "uint"
                        },
                        "length_errors": {
                            "type": "uint"
                        },
                        "crc_errors": {
                            "type": "uint"
                        },
                        "frame_errors": {
                            "type": "uint"
                        },
                        "fifo_errors": {
                            "type": "uint"
                        },
                        "missed_errors": {
                            "type": "uint"
                        },
                        "nohandler": {
                            "type": "uint"
                        }
                    }
                },
                "tx": {
                    "type": "dict",
                    "dict": {
                        "bytes": {
                            "type": "uint"
                        },
                        "packets": {
                            "type": "uint"
                        },
                        "errors": {
                            "type": "uint"
                        },
                        "dropped": {
                            "type": "uint"
                        },
                        "carrier_errors": {
                            "type": "uint"
                        },
                        "collisions": {
                            "type": "uint"
                        },
                        "compressed": {
                            "type": "uint"
                        },
                        "aborted_errors": {
                            "type": "uint"
                        },
                        "fifo_errors": {
                            "type": "uint"
                        },
                        "window_errors": {
                            "type": "uint"
                        },
                        "heartbeat_errors": {
                            "type": "uint"
                        },
                        "carrier_changes": {
                            "type": "uint"
                        }
                    }
                }
            }
        },
        "vfinfo_list": {
            "type": "array",
            "attr": "IFLA_VFINFO_LIST",
            "array": [
                {
                    "vf": {
                        "type": "int"
                    },
                    "mac": {
                        "type": "string"
                    },
                    "vlan_list": {
                        "type": "array",
                        "attr": "IFLA_VF_VLAN_LIST",
                        "array": [
                            {
                                "vlan": {
                                    "type": "int"
                                },
                                "qos": {
                                    "type": "int"
                                },
                                "protocol": {
                                    "type": "string"
                                }
                            }
                        ]
                    },
                    "vlan": {
                        "type": "int",
                        "attr": "!IFLA_VF_VLAN_LIST && IFLA_VF_VLAN"
                    },
                    "qos": {
                        "type": "int",
                        "attr": "!IFLA_VF_VLAN_LIST && IFLA_VF_VLAN"
                    },
                    "tx_rate": {
                        "type": "int"
                    },
                    "rate": {
                        "type": "dict",
                        "attr": "IFLA_VF_RATE",
                        "dict": {
                            "max_tx": {
                                "type": "int"
                            },
                            "min_tx": {
                                "type": "int"
                            }
                        }
                    },
                    "spoofchk": {
                        "type": "bool",
                        "attr": "IFLA_VF_SPOOFCHK"
                    },
                    "link_state": {
                        "type": "string",
                        "attr": "IFLA_VF_LINK_STATE"
                    },
                    "trust": {
                        "type": "bool",
                        "attr": "IFLA_VF_TRUST"
                    },
                    "query_rss_en": {
                        "type": "bool",
                        "attr": "IFLA_VF_RSS_QUERY_EN"
                    },
                    "stats": {
                        "type": "dict",
                        "attr": "IFLA_VF_STATS",
                        "dict": {
                            "rx": {
                                "type": "dict",
                                "dict": {
                                    "bytes": {
                                        "type": "uint",
                                        "attr": "IFLA_VF_STATS_RX_BYTES"
                                    },
                                    "packets": {
                                        "type": "uint",
                                        "attr": "IFLA_VF_STATS_RX_PACKETS"
                                    },
                                    "multicast": {
                                        "type": "uint",
                                        "attr": "IFLA_VF_STATS_MULTICAST"
                                    },
                                    "broadcast": {
                                        "type": "uint",
                                        "attr": "IFLA_VF_STATS_BROADCAST"
                                    }
                                }
                            },
                            "tx": {
                                "type": "dict",
                                "dict": {
                                    "bytes": {
                                        "type": "uint",
                                        "attr": "IFLA_VF_STATS_TX_BYTES"
                                    },
                                    "packets": {
                                        "type": "uint",
                                        "attr": "IFLA_VF_STATS_TX_PACKETS"
                                    }
                                }
                            }
                        }
                    }
                }
            ]
        }
    }
]

Example with the config previously given:
Note that here, linkinfo attributes are not populated.
The schemas are provided in each link type patches.

$ ip -details -json link show
[{
        "ifindex": 1,
        "ifname": "lo",
        "flags": ["LOOPBACK","UP","LOWER_UP"],
        "mtu": 65536,
        "qdisc": "noqueue",
        "operstate": "UNKNOWN",
        "linkmode": "DEFAULT",
        "group": "default",
        "link_type": "loopback",
        "address": "00:00:00:00:00:00",
        "broadcast": "00:00:00:00:00:00",
        "promiscuity": 0,
        "inet6_addr_gen_mode": "eui64",
        "num_tx_queues": 1,
        "num_rx_queues": 1,
        "gso_max_size": 65536,
        "gso_max_segs": 65535
    },{
        "ifindex": 2,
        "ifname": "eth0",
        "flags": ["BROADCAST","MULTICAST","UP","LOWER_UP"],
        "mtu": 1500,
        "qdisc": "pfifo_fast",
        "operstate": "UP",
        "linkmode": "DEFAULT",
        "group": "default",
        "txqlen": 1000,
        "link_type": "ether",
        "address": "08:00:27:db:31:88",
        "broadcast": "ff:ff:ff:ff:ff:ff",
        "promiscuity": 0,
        "inet6_addr_gen_mode": "eui64",
        "num_tx_queues": 1,
        "num_rx_queues": 1,
        "gso_max_size": 65536,
        "gso_max_segs": 65535
    },{
        "ifindex": 3,
        "ifname": "swp1",
        "flags": ["BROADCAST","MULTICAST"],
        "mtu": 1500,
        "qdisc": "noop",
        "operstate": "DOWN",
        "linkmode": "DEFAULT",
        "group": "default",
        "txqlen": 1000,
        "link_type": "ether",
        "address": "08:00:27:5b:b1:75",
        "broadcast": "ff:ff:ff:ff:ff:ff",
        "promiscuity": 0,
        "inet6_addr_gen_mode": "eui64",
        "num_tx_queues": 1,
        "num_rx_queues": 1,
        "gso_max_size": 65536,
        "gso_max_segs": 65535
    },{
        "ifindex": 10,
        "ifname": "vxlan42",
        "flags": ["BROADCAST","MULTICAST"],
        "mtu": 1500,
        "qdisc": "noop",
        "master": "br0",
        "operstate": "DOWN",
        "linkmode": "DEFAULT",
        "group": "default",
        "link_type": "ether",
        "address": "4a:d9:91:42:a2:d2",
        "broadcast": "ff:ff:ff:ff:ff:ff",
        "promiscuity": 1,
        "linkinfo": {
            "info_kind": "vxlan",
            "info_data": {},
            "info_slave_kind": "bridge",
            "info_slave_data": {}
        },
        "inet6_addr_gen_mode": "eui64",
        "num_tx_queues": 1,
        "num_rx_queues": 1,
        "gso_max_size": 65536,
        "gso_max_segs": 65535
    },{
        "ifindex": 11,
        "ifname": "bond0",
        "flags": ["BROADCAST","MULTICAST","MASTER"],
        "mtu": 1500,
        "qdisc": "noop",
        "master": "br0",
        "operstate": "DOWN",
        "linkmode": "DEFAULT",
        "group": "default",
        "link_type": "ether",
        "address": "e2:aa:7b:17:c5:14",
        "broadcast": "ff:ff:ff:ff:ff:ff",
        "promiscuity": 1,
        "linkinfo": {
            "info_kind": "bond",
            "info_data": {},
            "info_slave_kind": "bridge",
            "info_slave_data": {},
        "inet6_addr_gen_mode": "eui64",
        "num_tx_queues": 16,
        "num_rx_queues": 16,
        "gso_max_size": 65536,
        "gso_max_segs": 65535
    },{
        "ifindex": 12,
        "ifname": "swp1.50",
        "link": "swp1",
        "flags": ["BROADCAST","MULTICAST","M-DOWN"],
        "mtu": 1500,
        "qdisc": "noop",
        "master": "br0",
        "operstate": "DOWN",
        "linkmode": "DEFAULT",
        "group": "default",
        "link_type": "ether",
        "address": "08:00:27:5b:b1:75",
        "broadcast": "ff:ff:ff:ff:ff:ff",
        "promiscuity": 1,
        "linkinfo": {
            "info_kind": "vlan",
            "info_data": {},
            "info_slave_kind": "bridge",
            "info_slave_data": {},
        "inet6_addr_gen_mode": "eui64",
        "num_tx_queues": 1,
        "num_rx_queues": 1,
        "gso_max_size": 65536,
        "gso_max_segs": 65535
    },{
        "ifindex": 13,
        "ifname": "br0",
        "flags": ["NO-CARRIER","BROADCAST","MULTICAST","UP"],
        "mtu": 1500,
        "qdisc": "noqueue",
        "operstate": "DOWN",
        "linkmode": "DEFAULT",
        "group": "default",
        "link_type": "ether",
        "address": "08:00:27:5b:b1:75",
        "broadcast": "ff:ff:ff:ff:ff:ff",
        "promiscuity": 0,
        "linkinfo": {
            "info_kind": "bridge",
            "info_data": {},
        "inet6_addr_gen_mode": "eui64",
        "num_tx_queues": 1,
        "num_rx_queues": 1,
        "gso_max_size": 65536,
        "gso_max_segs": 65535
    }
]

Signed-off-by: Julien Fortin <julien@cumulusnetworks.com>
---
 ip/ipaddress.c | 1064 +++++++++++++++++++++++++++++++++++++++-----------------
 1 file changed, 753 insertions(+), 311 deletions(-)

diff --git a/ip/ipaddress.c b/ip/ipaddress.c
index 4d37c5e0..81a5888f 100644
--- a/ip/ipaddress.c
+++ b/ip/ipaddress.c
@@ -30,6 +30,7 @@
 #include <linux/sockios.h>
 #include <linux/net_namespace.h>
 
+#include "utils.h"
 #include "rt_names.h"
 #include "utils.h"
 #include "ll_map.h"
@@ -84,13 +85,14 @@ static void usage(void)
 
 static void print_link_flags(FILE *fp, unsigned int flags, unsigned int mdown)
 {
-	fprintf(fp, "<");
+	open_json_array(PRINT_ANY, is_json_context() ? "flags" : "<");
 	if (flags & IFF_UP && !(flags & IFF_RUNNING))
-		fprintf(fp, "NO-CARRIER%s", flags ? "," : "");
+		print_string(PRINT_ANY, NULL,
+			     flags ? "%s," : "%s", "NO-CARRIER");
 	flags &= ~IFF_RUNNING;
-#define _PF(f) if (flags&IFF_##f) { \
-		  flags &= ~IFF_##f ; \
-		  fprintf(fp, #f "%s", flags ? "," : ""); }
+#define _PF(f) if (flags&IFF_##f) {					\
+		flags &= ~IFF_##f ;					\
+		print_string(PRINT_ANY, NULL, flags ? "%s," : "%s", #f); }
 	_PF(LOOPBACK);
 	_PF(BROADCAST);
 	_PF(POINTOPOINT);
@@ -111,10 +113,10 @@ static void print_link_flags(FILE *fp, unsigned int flags, unsigned int mdown)
 	_PF(ECHO);
 #undef _PF
 	if (flags)
-		fprintf(fp, "%x", flags);
+		print_hex(PRINT_ANY, NULL, "%x", flags);
 	if (mdown)
-		fprintf(fp, ",M-DOWN");
-	fprintf(fp, "> ");
+		print_string(PRINT_ANY, NULL, ",%s", "M-DOWN");
+	close_json_array(PRINT_ANY, "> ");
 }
 
 static const char *oper_states[] = {
@@ -125,14 +127,26 @@ static const char *oper_states[] = {
 static void print_operstate(FILE *f, __u8 state)
 {
 	if (state >= ARRAY_SIZE(oper_states)) {
-		fprintf(f, "state %#x ", state);
+		if (is_json_context())
+			print_uint(PRINT_JSON, "operstate_index", NULL, state);
+		else
+			print_0xhex(PRINT_FP, NULL, "state %#x", state);
 	} else if (brief) {
-		color_fprintf(f, oper_state_color(state),
-			      "%-14s ", oper_states[state]);
+		print_color_string(PRINT_ANY,
+				   oper_state_color(state),
+				   "operstate",
+				   "%-14s ",
+				   oper_states[state]);
 	} else {
-		fprintf(f, "state ");
-		color_fprintf(f, oper_state_color(state),
-			      "%s ", oper_states[state]);
+		if (is_json_context())
+			print_string(PRINT_JSON,
+				     "operstate",
+				     NULL, oper_states[state]);
+		else {
+			fprintf(f, "state ");
+			color_fprintf(f, oper_state_color(state),
+				      "%s ", oper_states[state]);
+		}
 	}
 }
 
@@ -169,7 +183,7 @@ static void print_queuelen(FILE *f, struct rtattr *tb[IFLA_MAX + 1])
 		qlen = ifr.ifr_qlen;
 	}
 	if (qlen)
-		fprintf(f, "qlen %d", qlen);
+		print_int(PRINT_ANY, "txqlen", "qlen %d", qlen);
 }
 
 static const char *link_modes[] = {
@@ -181,9 +195,15 @@ static void print_linkmode(FILE *f, struct rtattr *tb)
 	unsigned int mode = rta_getattr_u8(tb);
 
 	if (mode >= ARRAY_SIZE(link_modes))
-		fprintf(f, "mode %d ", mode);
+		print_int(PRINT_ANY,
+			  "linkmode_index",
+			  "mode %d ",
+			  mode);
 	else
-		fprintf(f, "mode %s ", link_modes[mode]);
+		print_string(PRINT_ANY,
+			     "linkmode",
+			     "mode %s "
+			     , link_modes[mode]);
 }
 
 static char *parse_link_kind(struct rtattr *tb, bool slave)
@@ -215,13 +235,14 @@ static void print_linktype(FILE *fp, struct rtattr *tb)
 	char slave[32];
 
 	parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb);
+	open_json_object("linkinfo");
 
 	if (linkinfo[IFLA_INFO_KIND]) {
 		const char *kind
 			= rta_getattr_str(linkinfo[IFLA_INFO_KIND]);
 
-		fprintf(fp, "%s", _SL_);
-		fprintf(fp, "    %s ", kind);
+		print_string(PRINT_FP, NULL, "%s", _SL_);
+		print_string(PRINT_ANY, "info_kind", "    %s ", kind);
 
 		lu = get_link_kind(kind);
 		if (lu && lu->print_opt) {
@@ -232,11 +253,16 @@ static void print_linktype(FILE *fp, struct rtattr *tb)
 						    linkinfo[IFLA_INFO_DATA]);
 				data = attr;
 			}
+			open_json_object("info_data");
 			lu->print_opt(lu, fp, data);
+			close_json_object();
 
 			if (linkinfo[IFLA_INFO_XSTATS] && show_stats &&
-			    lu->print_xstats)
+			    lu->print_xstats) {
+				open_json_object("info_xstats");
 				lu->print_xstats(lu, fp, linkinfo[IFLA_INFO_XSTATS]);
+				close_json_object();
+			}
 		}
 	}
 
@@ -244,8 +270,12 @@ static void print_linktype(FILE *fp, struct rtattr *tb)
 		const char *slave_kind
 			= rta_getattr_str(linkinfo[IFLA_INFO_SLAVE_KIND]);
 
-		fprintf(fp, "%s", _SL_);
-		fprintf(fp, "    %s_slave ", slave_kind);
+		print_string(PRINT_FP, NULL, "%s", _SL_);
+		print_string(PRINT_ANY,
+			     "info_slave_kind",
+			     "    %s_slave ",
+			     slave_kind);
+
 		snprintf(slave, sizeof(slave), "%s_slave", slave_kind);
 
 		slave_lu = get_link_kind(slave);
@@ -257,9 +287,12 @@ static void print_linktype(FILE *fp, struct rtattr *tb)
 						    linkinfo[IFLA_INFO_SLAVE_DATA]);
 				data = attr;
 			}
+			open_json_object("info_slave_data");
 			slave_lu->print_opt(slave_lu, fp, data);
+			close_json_object();
 		}
 	}
+	close_json_object();
 }
 
 static void print_af_spec(FILE *fp, struct rtattr *af_spec_attr)
@@ -275,22 +308,39 @@ static void print_af_spec(FILE *fp, struct rtattr *af_spec_attr)
 
 	if (tb[IFLA_INET6_ADDR_GEN_MODE]) {
 		__u8 mode = rta_getattr_u8(tb[IFLA_INET6_ADDR_GEN_MODE]);
+		SPRINT_BUF(b1);
 
 		switch (mode) {
 		case IN6_ADDR_GEN_MODE_EUI64:
-			fprintf(fp, "addrgenmode eui64 ");
+			print_string(PRINT_ANY,
+				     "inet6_addr_gen_mode",
+				     "addrgenmode %s ",
+				     "eui64");
 			break;
 		case IN6_ADDR_GEN_MODE_NONE:
-			fprintf(fp, "addrgenmode none ");
+			print_string(PRINT_ANY,
+				     "inet6_addr_gen_mode",
+				     "addrgenmode %s ",
+				     "none");
 			break;
 		case IN6_ADDR_GEN_MODE_STABLE_PRIVACY:
-			fprintf(fp, "addrgenmode stable_secret ");
+			print_string(PRINT_ANY,
+				     "inet6_addr_gen_mode",
+				     "addrgenmode %s ",
+				     "stable_secret");
 			break;
 		case IN6_ADDR_GEN_MODE_RANDOM:
-			fprintf(fp, "addrgenmode random ");
+			print_string(PRINT_ANY,
+				     "inet6_addr_gen_mode",
+				     "addrgenmode %s ",
+				     "random");
 			break;
 		default:
-			fprintf(fp, "addrgenmode %#.2hhx ", mode);
+			snprintf(b1, sizeof(b1), "%#.2hhx", mode);
+			print_string(PRINT_ANY,
+				     "inet6_addr_gen_mode",
+				     "addrgenmode %s ",
+				     b1);
 			break;
 		}
 	}
@@ -316,83 +366,135 @@ static void print_vfinfo(FILE *fp, struct rtattr *vfinfo)
 	vf_mac = RTA_DATA(vf[IFLA_VF_MAC]);
 	vf_tx_rate = RTA_DATA(vf[IFLA_VF_TX_RATE]);
 
-	fprintf(fp, "%s    vf %d MAC %s", _SL_, vf_mac->vf,
-		ll_addr_n2a((unsigned char *)&vf_mac->mac,
-			    ETH_ALEN, 0, b1, sizeof(b1)));
+	print_string(PRINT_FP, NULL, "%s    ", _SL_);
+	print_int(PRINT_ANY, "vf", "vf %d ", vf_mac->vf);
+	print_string(PRINT_ANY, "mac", "MAC %s",
+		     ll_addr_n2a((unsigned char *) &vf_mac->mac,
+				 ETH_ALEN, 0, b1, sizeof(b1)));
+
 	if (vf[IFLA_VF_VLAN_LIST]) {
 		struct rtattr *i, *vfvlanlist = vf[IFLA_VF_VLAN_LIST];
 		int rem = RTA_PAYLOAD(vfvlanlist);
 
+		open_json_array(PRINT_JSON, "vlan_list");
 		for (i = RTA_DATA(vfvlanlist);
-		      RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
-			struct ifla_vf_vlan_info *vf_vlan_info =
-					RTA_DATA(i);
+		     RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
+			struct ifla_vf_vlan_info *vf_vlan_info = RTA_DATA(i);
 			SPRINT_BUF(b2);
 
+			open_json_object(NULL);
 			if (vf_vlan_info->vlan)
-				fprintf(fp, ", vlan %d", vf_vlan_info->vlan);
+				print_int(PRINT_ANY,
+					  "vlan",
+					  ", vlan %d",
+					  vf_vlan_info->vlan);
 			if (vf_vlan_info->qos)
-				fprintf(fp, ", qos %d", vf_vlan_info->qos);
+				print_int(PRINT_ANY,
+					  "qos",
+					  ", qos %d",
+					  vf_vlan_info->qos);
 			if (vf_vlan_info->vlan_proto &&
 			    vf_vlan_info->vlan_proto != htons(ETH_P_8021Q))
-				fprintf(fp, ", vlan protocol %s",
-					ll_proto_n2a(vf_vlan_info->vlan_proto,
+				print_string(PRINT_ANY,
+					     "protocol",
+					     ", vlan protocol %s",
+					     ll_proto_n2a(
+						     vf_vlan_info->vlan_proto,
 						     b2, sizeof(b2)));
-
+			close_json_object();
 		}
+		close_json_array(PRINT_JSON, NULL);
 	} else {
 		struct ifla_vf_vlan *vf_vlan = RTA_DATA(vf[IFLA_VF_VLAN]);
 
 		if (vf_vlan->vlan)
-			fprintf(fp, ", vlan %d", vf_vlan->vlan);
+			print_int(PRINT_ANY,
+				  "vlan",
+				  ", vlan %d",
+				  vf_vlan->vlan);
 		if (vf_vlan->qos)
-			fprintf(fp, ", qos %d", vf_vlan->qos);
+			print_int(PRINT_ANY, "qos", ", qos %d", vf_vlan->qos);
 	}
+
 	if (vf_tx_rate->rate)
-		fprintf(fp, ", tx rate %d (Mbps)", vf_tx_rate->rate);
+		print_int(PRINT_ANY,
+			  "tx_rate",
+			  ", tx rate %d (Mbps)",
+			  vf_tx_rate->rate);
 
 	if (vf[IFLA_VF_RATE]) {
 		struct ifla_vf_rate *vf_rate = RTA_DATA(vf[IFLA_VF_RATE]);
-
-		if (vf_rate->max_tx_rate)
-			fprintf(fp, ", max_tx_rate %dMbps", vf_rate->max_tx_rate);
-		if (vf_rate->min_tx_rate)
-			fprintf(fp, ", min_tx_rate %dMbps", vf_rate->min_tx_rate);
+		int max_tx = vf_rate->max_tx_rate;
+		int min_tx = vf_rate->min_tx_rate;
+
+		if (is_json_context()) {
+			open_json_object("rate");
+			print_int(PRINT_JSON, "max_tx", NULL, max_tx);
+			print_int(PRINT_ANY, "min_tx", NULL, min_tx);
+			close_json_object();
+		} else {
+			if (max_tx)
+				fprintf(fp, ", max_tx_rate %dMbps", max_tx);
+			if (min_tx)
+				fprintf(fp, ", min_tx_rate %dMbps", min_tx);
+		}
 	}
+
 	if (vf[IFLA_VF_SPOOFCHK]) {
 		struct ifla_vf_spoofchk *vf_spoofchk =
 			RTA_DATA(vf[IFLA_VF_SPOOFCHK]);
 
 		if (vf_spoofchk->setting != -1)
-			fprintf(fp, ", spoof checking %s",
-				vf_spoofchk->setting ? "on" : "off");
+			print_bool(PRINT_ANY,
+				   "spoofchk",
+				   vf_spoofchk->setting ?
+				   ", spoof checking on" : ", spoof checking off",
+				   vf_spoofchk->setting);
 	}
+
 	if (vf[IFLA_VF_LINK_STATE]) {
 		struct ifla_vf_link_state *vf_linkstate =
 			RTA_DATA(vf[IFLA_VF_LINK_STATE]);
 
 		if (vf_linkstate->link_state == IFLA_VF_LINK_STATE_AUTO)
-			fprintf(fp, ", link-state auto");
+			print_string(PRINT_ANY,
+				     "link_state",
+				     ", link-state %s",
+				     "auto");
 		else if (vf_linkstate->link_state == IFLA_VF_LINK_STATE_ENABLE)
-			fprintf(fp, ", link-state enable");
+			print_string(PRINT_ANY,
+				     "link_state",
+				     ", link-state %s",
+				     "enable");
 		else
-			fprintf(fp, ", link-state disable");
+			print_string(PRINT_ANY,
+				     "link_state",
+				     ", link-state %s",
+				     "disable");
 	}
+
 	if (vf[IFLA_VF_TRUST]) {
 		struct ifla_vf_trust *vf_trust = RTA_DATA(vf[IFLA_VF_TRUST]);
 
 		if (vf_trust->setting != -1)
-			fprintf(fp, ", trust %s",
-				vf_trust->setting ? "on" : "off");
+			print_bool(PRINT_ANY,
+				   "trust",
+				   vf_trust->setting ? ", trust on" : ", trust off",
+				   vf_trust->setting);
 	}
+
 	if (vf[IFLA_VF_RSS_QUERY_EN]) {
 		struct ifla_vf_rss_query_en *rss_query =
 			RTA_DATA(vf[IFLA_VF_RSS_QUERY_EN]);
 
 		if (rss_query->setting != -1)
-			fprintf(fp, ", query_rss %s",
-			        rss_query->setting ? "on" : "off");
+			print_bool(PRINT_ANY,
+				   "query_rss_en",
+				   rss_query->setting ? ", query_rss on"
+				   : ", query_rss off",
+				   rss_query->setting);
 	}
+
 	if (vf[IFLA_VF_STATS] && show_stats)
 		print_vf_stats64(fp, vf[IFLA_VF_STATS]);
 }
@@ -432,7 +534,7 @@ void print_num(FILE *fp, unsigned int width, uint64_t count)
 	}
 
 	snprintf(buf, sizeof(buf), "%.*f%c%s", precision,
-		(double) count / powi, *prefix, use_iec ? "i" : "");
+		 (double) count / powi, *prefix, use_iec ? "i" : "");
 
 	fprintf(fp, "%-*s ", width, buf);
 }
@@ -448,155 +550,339 @@ static void print_vf_stats64(FILE *fp, struct rtattr *vfstats)
 
 	parse_rtattr_nested(vf, IFLA_VF_MAX, vfstats);
 
-	/* RX stats */
-	fprintf(fp, "%s", _SL_);
-	fprintf(fp, "    RX: bytes  packets  mcast   bcast %s", _SL_);
-	fprintf(fp, "    ");
+	if (is_json_context()) {
+		open_json_object("stats");
+
+		/* RX stats */
+		open_json_object("rx");
+		print_uint(PRINT_JSON, "bytes", NULL,
+			   rta_getattr_u64(vf[IFLA_VF_STATS_RX_BYTES]));
+		print_uint(PRINT_JSON, "packets", NULL,
+			   rta_getattr_u64(vf[IFLA_VF_STATS_RX_PACKETS]));
+		print_uint(PRINT_JSON, "multicast", NULL,
+			   rta_getattr_u64(vf[IFLA_VF_STATS_MULTICAST]));
+		print_uint(PRINT_JSON, "broadcast", NULL,
+			   rta_getattr_u64(vf[IFLA_VF_STATS_BROADCAST]));
+		close_json_object();
+
+		/* TX stats */
+		open_json_object("tx");
+		print_uint(PRINT_JSON, "tx_bytes", NULL,
+			   rta_getattr_u64(vf[IFLA_VF_STATS_TX_BYTES]));
+		print_uint(PRINT_JSON, "tx_packets", NULL,
+			   rta_getattr_u64(vf[IFLA_VF_STATS_TX_PACKETS]));
+		close_json_object();
+		close_json_object();
+	} else {
+		/* RX stats */
+		fprintf(fp, "%s", _SL_);
+		fprintf(fp, "    RX: bytes  packets  mcast   bcast %s", _SL_);
+		fprintf(fp, "    ");
 
-	print_num(fp, 10,  rta_getattr_u64(vf[IFLA_VF_STATS_RX_BYTES]));
-	print_num(fp, 8, rta_getattr_u64(vf[IFLA_VF_STATS_RX_PACKETS]));
-	print_num(fp, 7, rta_getattr_u64(vf[IFLA_VF_STATS_MULTICAST]));
-	print_num(fp, 7, rta_getattr_u64(vf[IFLA_VF_STATS_BROADCAST]));
+		print_num(fp, 10, rta_getattr_u64(vf[IFLA_VF_STATS_RX_BYTES]));
+		print_num(fp, 8, rta_getattr_u64(vf[IFLA_VF_STATS_RX_PACKETS]));
+		print_num(fp, 7, rta_getattr_u64(vf[IFLA_VF_STATS_MULTICAST]));
+		print_num(fp, 7, rta_getattr_u64(vf[IFLA_VF_STATS_BROADCAST]));
 
-	/* TX stats */
-	fprintf(fp, "%s", _SL_);
-	fprintf(fp, "    TX: bytes  packets %s", _SL_);
-	fprintf(fp, "    ");
+		/* TX stats */
+		fprintf(fp, "%s", _SL_);
+		fprintf(fp, "    TX: bytes  packets %s", _SL_);
+		fprintf(fp, "    ");
 
-	print_num(fp, 10, rta_getattr_u64(vf[IFLA_VF_STATS_TX_BYTES]));
-	print_num(fp, 8, rta_getattr_u64(vf[IFLA_VF_STATS_TX_PACKETS]));
+		print_num(fp, 10, rta_getattr_u64(vf[IFLA_VF_STATS_TX_BYTES]));
+		print_num(fp, 8, rta_getattr_u64(vf[IFLA_VF_STATS_TX_PACKETS]));
+	}
 }
 
 static void print_link_stats64(FILE *fp, const struct rtnl_link_stats64 *s,
 			       const struct rtattr *carrier_changes)
 {
-	/* RX stats */
-	fprintf(fp, "    RX: bytes  packets  errors  dropped overrun mcast   %s%s",
-		s->rx_compressed ? "compressed" : "", _SL_);
-
-	fprintf(fp, "    ");
-	print_num(fp, 10, s->rx_bytes);
-	print_num(fp, 8, s->rx_packets);
-	print_num(fp, 7, s->rx_errors);
-	print_num(fp, 7, s->rx_dropped);
-	print_num(fp, 7, s->rx_over_errors);
-	print_num(fp, 7, s->multicast);
-	if (s->rx_compressed)
-		print_num(fp, 7, s->rx_compressed);
-
-	/* RX error stats */
-	if (show_stats > 1) {
-		fprintf(fp, "%s", _SL_);
-		fprintf(fp, "    RX errors: length   crc     frame   fifo    missed%s%s",
-			s->rx_nohandler ? "   nohandler" : "",  _SL_);
-
-		fprintf(fp, "               ");
-		print_num(fp, 8, s->rx_length_errors);
-		print_num(fp, 7, s->rx_crc_errors);
-		print_num(fp, 7, s->rx_frame_errors);
-		print_num(fp, 7, s->rx_fifo_errors);
-		print_num(fp, 7, s->rx_missed_errors);
-		if (s->rx_nohandler)
-			print_num(fp, 7, s->rx_nohandler);
+	if (is_json_context()) {
+		open_json_object("stats644");
+
+		/* RX stats */
+		open_json_object("rx");
+		print_uint(PRINT_JSON, "bytes", NULL, s->rx_bytes);
+		print_uint(PRINT_JSON, "packets", NULL, s->rx_packets);
+		print_uint(PRINT_JSON, "errors", NULL, s->rx_errors);
+		print_uint(PRINT_JSON, "dropped", NULL, s->rx_dropped);
+		print_uint(PRINT_JSON, "over_errors", NULL, s->rx_over_errors);
+		print_uint(PRINT_JSON, "multicast", NULL, s->multicast);
+		if (s->rx_compressed)
+			print_uint(PRINT_JSON,
+				   "compressed",
+				   NULL, s->rx_compressed);
+
+		/* RX error stats */
+		if (show_stats > 1) {
+			print_uint(PRINT_JSON,
+				   "length_errors",
+				   NULL, s->rx_length_errors);
+			print_uint(PRINT_JSON,
+				   "crc_errors",
+				   NULL, s->rx_crc_errors);
+			print_uint(PRINT_JSON,
+				   "frame_errors",
+				   NULL, s->rx_frame_errors);
+			print_uint(PRINT_JSON,
+				   "fifo_errors",
+				   NULL, s->rx_fifo_errors);
+			print_uint(PRINT_JSON,
+				   "missed_errors",
+				   NULL, s->rx_missed_errors);
+			if (s->rx_nohandler)
+				print_uint(PRINT_JSON,
+					   "nohandler", NULL, s->rx_nohandler);
+		}
+		close_json_object();
+
+		/* TX stats */
+		open_json_object("tx");
+		print_uint(PRINT_JSON, "bytes", NULL, s->tx_bytes);
+		print_uint(PRINT_JSON, "packets", NULL, s->tx_packets);
+		print_uint(PRINT_JSON, "errors", NULL, s->tx_errors);
+		print_uint(PRINT_JSON, "dropped", NULL, s->tx_dropped);
+		print_uint(PRINT_JSON,
+			   "carrier_errors",
+			   NULL, s->tx_carrier_errors);
+		print_uint(PRINT_JSON, "collisions", NULL, s->collisions);
+		if (s->tx_compressed)
+			print_uint(PRINT_JSON,
+				   "compressed",
+				   NULL, s->tx_compressed);
+
+		/* TX error stats */
+		if (show_stats > 1) {
+			print_uint(PRINT_JSON,
+				   "aborted_errors",
+				   NULL, s->tx_aborted_errors);
+			print_uint(PRINT_JSON,
+				   "fifo_errors",
+				   NULL, s->tx_fifo_errors);
+			print_uint(PRINT_JSON,
+				   "window_errors",
+				   NULL, s->tx_window_errors);
+			print_uint(PRINT_JSON,
+				   "heartbeat_errors",
+				   NULL, s->tx_heartbeat_errors);
+			if (carrier_changes)
+				print_uint(PRINT_JSON, "carrier_changes", NULL,
+					   rta_getattr_u32(carrier_changes));
+		}
+		close_json_object();
+		close_json_object();
 
-	}
-	fprintf(fp, "%s", _SL_);
+	} else {
+		/* RX stats */
+		fprintf(fp, "    RX: bytes  packets  errors  dropped overrun mcast   %s%s",
+			s->rx_compressed ? "compressed" : "", _SL_);
+
+		fprintf(fp, "    ");
+		print_num(fp, 10, s->rx_bytes);
+		print_num(fp, 8, s->rx_packets);
+		print_num(fp, 7, s->rx_errors);
+		print_num(fp, 7, s->rx_dropped);
+		print_num(fp, 7, s->rx_over_errors);
+		print_num(fp, 7, s->multicast);
+		if (s->rx_compressed)
+			print_num(fp, 7, s->rx_compressed);
+
+		/* RX error stats */
+		if (show_stats > 1) {
+			fprintf(fp, "%s", _SL_);
+			fprintf(fp, "    RX errors: length   crc     frame   fifo    missed%s%s",
+				s->rx_nohandler ? "   nohandler" : "", _SL_);
+
+			fprintf(fp, "               ");
+			print_num(fp, 8, s->rx_length_errors);
+			print_num(fp, 7, s->rx_crc_errors);
+			print_num(fp, 7, s->rx_frame_errors);
+			print_num(fp, 7, s->rx_fifo_errors);
+			print_num(fp, 7, s->rx_missed_errors);
+			if (s->rx_nohandler)
+				print_num(fp, 7, s->rx_nohandler);
 
-	/* TX stats */
-	fprintf(fp, "    TX: bytes  packets  errors  dropped carrier collsns %s%s",
-		s->tx_compressed ? "compressed" : "", _SL_);
-
-	fprintf(fp, "    ");
-	print_num(fp, 10, s->tx_bytes);
-	print_num(fp, 8, s->tx_packets);
-	print_num(fp, 7, s->tx_errors);
-	print_num(fp, 7, s->tx_dropped);
-	print_num(fp, 7, s->tx_carrier_errors);
-	print_num(fp, 7, s->collisions);
-	if (s->tx_compressed)
-		print_num(fp, 7, s->tx_compressed);
-
-	/* TX error stats */
-	if (show_stats > 1) {
-		fprintf(fp, "%s", _SL_);
-		fprintf(fp, "    TX errors: aborted  fifo   window heartbeat");
-		if (carrier_changes)
-			fprintf(fp, " transns");
+		}
 		fprintf(fp, "%s", _SL_);
 
-		fprintf(fp, "               ");
-		print_num(fp, 8, s->tx_aborted_errors);
-		print_num(fp, 7, s->tx_fifo_errors);
-		print_num(fp, 7, s->tx_window_errors);
-		print_num(fp, 7, s->tx_heartbeat_errors);
-		if (carrier_changes)
-			print_num(fp, 7, rta_getattr_u32(carrier_changes));
+		/* TX stats */
+		fprintf(fp, "    TX: bytes  packets  errors  dropped carrier collsns %s%s",
+			s->tx_compressed ? "compressed" : "", _SL_);
+
+		fprintf(fp, "    ");
+		print_num(fp, 10, s->tx_bytes);
+		print_num(fp, 8, s->tx_packets);
+		print_num(fp, 7, s->tx_errors);
+		print_num(fp, 7, s->tx_dropped);
+		print_num(fp, 7, s->tx_carrier_errors);
+		print_num(fp, 7, s->collisions);
+		if (s->tx_compressed)
+			print_num(fp, 7, s->tx_compressed);
+
+		/* TX error stats */
+		if (show_stats > 1) {
+			fprintf(fp, "%s", _SL_);
+			fprintf(fp, "    TX errors: aborted  fifo   window heartbeat");
+			if (carrier_changes)
+				fprintf(fp, " transns");
+			fprintf(fp, "%s", _SL_);
+
+			fprintf(fp, "               ");
+			print_num(fp, 8, s->tx_aborted_errors);
+			print_num(fp, 7, s->tx_fifo_errors);
+			print_num(fp, 7, s->tx_window_errors);
+			print_num(fp, 7, s->tx_heartbeat_errors);
+			if (carrier_changes)
+				print_num(fp, 7,
+					  rta_getattr_u32(carrier_changes));
+		}
 	}
 }
 
 static void print_link_stats32(FILE *fp, const struct rtnl_link_stats *s,
 			       const struct rtattr *carrier_changes)
 {
-	/* RX stats */
-	fprintf(fp, "    RX: bytes  packets  errors  dropped overrun mcast   %s%s",
-		s->rx_compressed ? "compressed" : "", _SL_);
-
-
-	fprintf(fp, "    ");
-	print_num(fp, 10, s->rx_bytes);
-	print_num(fp, 8, s->rx_packets);
-	print_num(fp, 7, s->rx_errors);
-	print_num(fp, 7, s->rx_dropped);
-	print_num(fp, 7, s->rx_over_errors);
-	print_num(fp, 7, s->multicast);
-	if (s->rx_compressed)
-		print_num(fp, 7, s->rx_compressed);
-
-	/* RX error stats */
-	if (show_stats > 1) {
-		fprintf(fp, "%s", _SL_);
-		fprintf(fp, "    RX errors: length   crc     frame   fifo    missed%s%s",
-			s->rx_nohandler ? "   nohandler" : "",  _SL_);
-		fprintf(fp, "               ");
-		print_num(fp, 8, s->rx_length_errors);
-		print_num(fp, 7, s->rx_crc_errors);
-		print_num(fp, 7, s->rx_frame_errors);
-		print_num(fp, 7, s->rx_fifo_errors);
-		print_num(fp, 7, s->rx_missed_errors);
-		if (s->rx_nohandler)
-			print_num(fp, 7, s->rx_nohandler);
-	}
-	fprintf(fp, "%s", _SL_);
+	if (is_json_context()) {
+		open_json_object("stats");
+
+		/* RX stats */
+		open_json_object("rx");
+		print_uint(PRINT_JSON, "bytes", NULL, s->rx_bytes);
+		print_uint(PRINT_JSON, "packets", NULL, s->rx_packets);
+		print_uint(PRINT_JSON, "errors", NULL, s->rx_errors);
+		print_uint(PRINT_JSON, "dropped", NULL, s->rx_dropped);
+		print_uint(PRINT_JSON, "over_errors", NULL, s->rx_over_errors);
+		print_uint(PRINT_JSON, "multicast", NULL, s->multicast);
+		if (s->rx_compressed)
+			print_int(PRINT_JSON,
+				  "compressed",
+				  NULL, s->rx_compressed);
+
+		/* RX error stats */
+		if (show_stats > 1) {
+			print_uint(PRINT_JSON,
+				   "length_errors",
+				   NULL, s->rx_length_errors);
+			print_uint(PRINT_JSON,
+				   "crc_errors",
+				   NULL, s->rx_crc_errors);
+			print_uint(PRINT_JSON,
+				   "frame_errors",
+				   NULL, s->rx_frame_errors);
+			print_uint(PRINT_JSON,
+				   "fifo_errors",
+				   NULL, s->rx_fifo_errors);
+			print_uint(PRINT_JSON,
+				   "missed_errors",
+				   NULL, s->rx_missed_errors);
+			if (s->rx_nohandler)
+				print_int(PRINT_JSON,
+					  "nohandler",
+					  NULL, s->rx_nohandler);
+		}
+		close_json_object();
+
+		/* TX stats */
+		open_json_object("tx");
+		print_uint(PRINT_JSON, "bytes", NULL, s->tx_bytes);
+		print_uint(PRINT_JSON, "packets", NULL, s->tx_packets);
+		print_uint(PRINT_JSON, "errors", NULL, s->tx_errors);
+		print_uint(PRINT_JSON, "dropped", NULL, s->tx_dropped);
+		print_uint(PRINT_JSON,
+			   "carrier_errors",
+			   NULL, s->tx_carrier_errors);
+		print_uint(PRINT_JSON, "collisions", NULL, s->collisions);
+		if (s->tx_compressed)
+			print_int(PRINT_JSON,
+				  "compressed",
+				  NULL, s->tx_compressed);
+
+		/* TX error stats */
+		if (show_stats > 1) {
+			print_uint(PRINT_JSON,
+				   "aborted_errors",
+				   NULL, s->tx_aborted_errors);
+			print_uint(PRINT_JSON,
+				   "fifo_errors",
+				   NULL, s->tx_fifo_errors);
+			print_uint(PRINT_JSON,
+				   "window_errors",
+				   NULL, s->tx_window_errors);
+			print_uint(PRINT_JSON,
+				   "heartbeat_errors",
+				   NULL, s->tx_heartbeat_errors);
+			if (carrier_changes)
+				print_uint(PRINT_JSON,
+					   "carrier_changes",
+					   NULL,
+					   rta_getattr_u32(carrier_changes));
+		}
 
-	/* TX stats */
-	fprintf(fp, "    TX: bytes  packets  errors  dropped carrier collsns %s%s",
-		s->tx_compressed ? "compressed" : "", _SL_);
-
-	fprintf(fp, "    ");
-	print_num(fp, 10, s->tx_bytes);
-	print_num(fp, 8, s->tx_packets);
-	print_num(fp, 7, s->tx_errors);
-	print_num(fp, 7, s->tx_dropped);
-	print_num(fp, 7, s->tx_carrier_errors);
-	print_num(fp, 7, s->collisions);
-	if (s->tx_compressed)
-		print_num(fp, 7, s->tx_compressed);
-
-	/* TX error stats */
-	if (show_stats > 1) {
-		fprintf(fp, "%s", _SL_);
-		fprintf(fp, "    TX errors: aborted  fifo   window heartbeat");
-		if (carrier_changes)
-			fprintf(fp, " transns");
+		close_json_object();
+		close_json_object();
+	} else {
+		/* RX stats */
+		fprintf(fp, "    RX: bytes  packets  errors  dropped overrun mcast   %s%s",
+			s->rx_compressed ? "compressed" : "", _SL_);
+
+
+		fprintf(fp, "    ");
+		print_num(fp, 10, s->rx_bytes);
+		print_num(fp, 8, s->rx_packets);
+		print_num(fp, 7, s->rx_errors);
+		print_num(fp, 7, s->rx_dropped);
+		print_num(fp, 7, s->rx_over_errors);
+		print_num(fp, 7, s->multicast);
+		if (s->rx_compressed)
+			print_num(fp, 7, s->rx_compressed);
+
+		/* RX error stats */
+		if (show_stats > 1) {
+			fprintf(fp, "%s", _SL_);
+			fprintf(fp, "    RX errors: length   crc     frame   fifo    missed%s%s",
+				s->rx_nohandler ? "   nohandler" : "", _SL_);
+			fprintf(fp, "               ");
+			print_num(fp, 8, s->rx_length_errors);
+			print_num(fp, 7, s->rx_crc_errors);
+			print_num(fp, 7, s->rx_frame_errors);
+			print_num(fp, 7, s->rx_fifo_errors);
+			print_num(fp, 7, s->rx_missed_errors);
+			if (s->rx_nohandler)
+				print_num(fp, 7, s->rx_nohandler);
+		}
 		fprintf(fp, "%s", _SL_);
 
-		fprintf(fp, "               ");
-		print_num(fp, 8, s->tx_aborted_errors);
-		print_num(fp, 7, s->tx_fifo_errors);
-		print_num(fp, 7, s->tx_window_errors);
-		print_num(fp, 7, s->tx_heartbeat_errors);
-		if (carrier_changes)
-			print_num(fp, 7, rta_getattr_u32(carrier_changes));
+		/* TX stats */
+		fprintf(fp, "    TX: bytes  packets  errors  dropped carrier collsns %s%s",
+			s->tx_compressed ? "compressed" : "", _SL_);
+
+		fprintf(fp, "    ");
+		print_num(fp, 10, s->tx_bytes);
+		print_num(fp, 8, s->tx_packets);
+		print_num(fp, 7, s->tx_errors);
+		print_num(fp, 7, s->tx_dropped);
+		print_num(fp, 7, s->tx_carrier_errors);
+		print_num(fp, 7, s->collisions);
+		if (s->tx_compressed)
+			print_num(fp, 7, s->tx_compressed);
+
+		/* TX error stats */
+		if (show_stats > 1) {
+			fprintf(fp, "%s", _SL_);
+			fprintf(fp, "    TX errors: aborted  fifo   window heartbeat");
+			if (carrier_changes)
+				fprintf(fp, " transns");
+			fprintf(fp, "%s", _SL_);
+
+			fprintf(fp, "               ");
+			print_num(fp, 8, s->tx_aborted_errors);
+			print_num(fp, 7, s->tx_fifo_errors);
+			print_num(fp, 7, s->tx_window_errors);
+			print_num(fp, 7, s->tx_heartbeat_errors);
+			if (carrier_changes)
+				print_num(fp, 7,
+					  rta_getattr_u32(carrier_changes));
+		}
 	}
 }
 
@@ -694,44 +980,50 @@ int print_linkinfo_brief(const struct sockaddr_nl *who,
 		return -1;
 
 	if (n->nlmsg_type == RTM_DELLINK)
-		fprintf(fp, "Deleted ");
+		print_bool(PRINT_ANY, "deleted", "Deleted ", true);
 
 	if (tb[IFLA_LINK]) {
 		SPRINT_BUF(b1);
 		int iflink = rta_getattr_u32(tb[IFLA_LINK]);
 
-		if (iflink == 0)
+		if (iflink == 0) {
 			snprintf(buf, sizeof(buf), "%s@NONE", name);
-		else {
-			snprintf(buf, sizeof(buf),
-				 "%s@%s", name, ll_idx_n2a(iflink, b1));
+			print_null(PRINT_JSON, "link", NULL, NULL);
+		} else {
+			const char *link = ll_idx_n2a(iflink, b1);
+
+			print_string(PRINT_JSON, "link", NULL, link);
+			snprintf(buf, sizeof(buf), "%s@%s", name, link);
 			m_flag = ll_index_to_flags(iflink);
 			m_flag = !(m_flag & IFF_UP);
 		}
 	} else
 		snprintf(buf, sizeof(buf), "%s", name);
 
-	fprintf(fp, "%-16s ", buf);
+	print_string(PRINT_FP, NULL, "%-16s ", buf);
+	print_string(PRINT_JSON, "ifname", NULL, name);
 
 	if (tb[IFLA_OPERSTATE])
 		print_operstate(fp, rta_getattr_u8(tb[IFLA_OPERSTATE]));
 
 	if (pfilter->family == AF_PACKET) {
 		SPRINT_BUF(b1);
+
 		if (tb[IFLA_ADDRESS]) {
-			color_fprintf(fp, COLOR_MAC, "%s ",
-					ll_addr_n2a(RTA_DATA(tb[IFLA_ADDRESS]),
-						RTA_PAYLOAD(tb[IFLA_ADDRESS]),
-						ifi->ifi_type,
-						b1, sizeof(b1)));
+			print_color_string(PRINT_ANY, COLOR_MAC,
+					   "address", "%s ",
+					   ll_addr_n2a(
+						   RTA_DATA(tb[IFLA_ADDRESS]),
+						   RTA_PAYLOAD(tb[IFLA_ADDRESS]),
+						   ifi->ifi_type,
+						   b1, sizeof(b1)));
 		}
 	}
 
-	if (pfilter->family == AF_PACKET)
+	if (pfilter->family == AF_PACKET) {
 		print_link_flags(fp, ifi->ifi_flags, m_flag);
-
-	if (pfilter->family == AF_PACKET)
-		fprintf(fp, "\n");
+		print_string(PRINT_FP, NULL, "%s", "\n");
+	}
 	fflush(fp);
 	return 0;
 }
@@ -749,10 +1041,12 @@ static const char *link_events[] = {
 static void print_link_event(FILE *f, __u32 event)
 {
 	if (event >= ARRAY_SIZE(link_events))
-		fprintf(f, "event %d ", event);
+		print_int(PRINT_ANY, "event", "event %d ", event);
 	else {
 		if (event)
-			fprintf(f, "event %s ", link_events[event]);
+			print_string(PRINT_ANY,
+				     "event", "event %s ",
+				     link_events[event]);
 	}
 }
 
@@ -808,41 +1102,63 @@ int print_linkinfo(const struct sockaddr_nl *who,
 		return -1;
 
 	if (n->nlmsg_type == RTM_DELLINK)
-		fprintf(fp, "Deleted ");
-
-	fprintf(fp, "%d: ", ifi->ifi_index);
-	color_fprintf(fp, COLOR_IFNAME, "%s",
-		      tb[IFLA_IFNAME] ? rta_getattr_str(tb[IFLA_IFNAME]) : "<nil>");
+		print_bool(PRINT_ANY, "deleted", "Deleted ", true);
+
+	print_int(PRINT_ANY, "ifindex", "%d: ", ifi->ifi_index);
+	if (tb[IFLA_IFNAME]) {
+		print_color_string(PRINT_ANY,
+				   COLOR_IFNAME,
+				   "ifname", "%s",
+				   rta_getattr_str(tb[IFLA_IFNAME]));
+	} else {
+		print_null(PRINT_JSON, "ifname", NULL, NULL);
+		print_color_null(PRINT_FP, COLOR_IFNAME,
+				 "ifname", "%s", "<nil>");
+	}
 
 	if (tb[IFLA_LINK]) {
-		SPRINT_BUF(b1);
 		int iflink = rta_getattr_u32(tb[IFLA_LINK]);
 
 		if (iflink == 0)
-			fprintf(fp, "@NONE: ");
+			print_null(PRINT_ANY, "link", "@%s: ", "NONE");
 		else {
 			if (tb[IFLA_LINK_NETNSID])
-				fprintf(fp, "@if%d: ", iflink);
+				print_int(PRINT_ANY,
+					  "link_index", "@if%d: ", iflink);
 			else {
-				fprintf(fp, "@%s: ", ll_idx_n2a(iflink, b1));
+				SPRINT_BUF(b1);
+
+				print_string(PRINT_ANY,
+					     "link",
+					     "@%s: ",
+					     ll_idx_n2a(iflink, b1));
 				m_flag = ll_index_to_flags(iflink);
 				m_flag = !(m_flag & IFF_UP);
 			}
 		}
 	} else {
-		fprintf(fp, ": ");
+		print_string(PRINT_FP, NULL, ": ", NULL);
 	}
 	print_link_flags(fp, ifi->ifi_flags, m_flag);
 
 	if (tb[IFLA_MTU])
-		fprintf(fp, "mtu %u ", rta_getattr_u32(tb[IFLA_MTU]));
+		print_int(PRINT_ANY,
+			  "mtu", "mtu %u ",
+			  rta_getattr_u32(tb[IFLA_MTU]));
 	if (tb[IFLA_XDP])
 		xdp_dump(fp, tb[IFLA_XDP]);
 	if (tb[IFLA_QDISC])
-		fprintf(fp, "qdisc %s ", rta_getattr_str(tb[IFLA_QDISC]));
+		print_string(PRINT_ANY,
+			     "qdisc",
+			     "qdisc %s ",
+			     rta_getattr_str(tb[IFLA_QDISC]));
 	if (tb[IFLA_MASTER]) {
 		SPRINT_BUF(b1);
-		fprintf(fp, "master %s ", ll_idx_n2a(rta_getattr_u32(tb[IFLA_MASTER]), b1));
+
+		print_string(PRINT_ANY,
+			     "master",
+			     "master %s ",
+			     ll_idx_n2a(rta_getattr_u32(tb[IFLA_MASTER]), b1));
 	}
 
 	if (tb[IFLA_OPERSTATE])
@@ -855,7 +1171,10 @@ int print_linkinfo(const struct sockaddr_nl *who,
 		SPRINT_BUF(b1);
 		int group = rta_getattr_u32(tb[IFLA_GROUP]);
 
-		fprintf(fp, "group %s ", rtnl_group_n2a(group, b1, sizeof(b1)));
+		print_string(PRINT_ANY,
+			     "group",
+			     "group %s ",
+			     rtnl_group_n2a(group, b1, sizeof(b1)));
 	}
 
 	if (filter.showqueue)
@@ -866,47 +1185,68 @@ int print_linkinfo(const struct sockaddr_nl *who,
 
 	if (!filter.family || filter.family == AF_PACKET || show_details) {
 		SPRINT_BUF(b1);
-		fprintf(fp, "%s", _SL_);
-		fprintf(fp, "    link/%s ", ll_type_n2a(ifi->ifi_type, b1, sizeof(b1)));
 
+		print_string(PRINT_FP, NULL, "%s", _SL_);
+		print_string(PRINT_ANY,
+			     "link_type",
+			     "    link/%s ",
+			     ll_type_n2a(ifi->ifi_type, b1, sizeof(b1)));
 		if (tb[IFLA_ADDRESS]) {
-			color_fprintf(fp, COLOR_MAC, "%s",
-				      ll_addr_n2a(RTA_DATA(tb[IFLA_ADDRESS]),
-						  RTA_PAYLOAD(tb[IFLA_ADDRESS]),
-						  ifi->ifi_type,
-						  b1, sizeof(b1)));
+			print_color_string(PRINT_ANY,
+					   COLOR_MAC,
+					   "address",
+					   "%s",
+					   ll_addr_n2a(RTA_DATA(tb[IFLA_ADDRESS]),
+						       RTA_PAYLOAD(tb[IFLA_ADDRESS]),
+						       ifi->ifi_type,
+						       b1, sizeof(b1)));
 		}
 		if (tb[IFLA_BROADCAST]) {
-			if (ifi->ifi_flags&IFF_POINTOPOINT)
-				fprintf(fp, " peer ");
-			else
-				fprintf(fp, " brd ");
-			color_fprintf(fp, COLOR_MAC, "%s",
-				      ll_addr_n2a(RTA_DATA(tb[IFLA_BROADCAST]),
-						  RTA_PAYLOAD(tb[IFLA_BROADCAST]),
-						  ifi->ifi_type,
-						  b1, sizeof(b1)));
+			if (ifi->ifi_flags&IFF_POINTOPOINT) {
+				print_string(PRINT_FP, NULL, " peer ", NULL);
+				print_bool(PRINT_JSON,
+					   "link_pointtopoint", NULL, true);
+			} else {
+				print_string(PRINT_FP, NULL, " brd ", NULL);
+			}
+			print_color_string(PRINT_ANY,
+					   COLOR_MAC,
+					   "broadcast",
+					   "%s",
+					   ll_addr_n2a(RTA_DATA(tb[IFLA_BROADCAST]),
+						       RTA_PAYLOAD(tb[IFLA_BROADCAST]),
+						       ifi->ifi_type,
+						       b1, sizeof(b1)));
 		}
 	}
 
 	if (tb[IFLA_LINK_NETNSID]) {
 		int id = rta_getattr_u32(tb[IFLA_LINK_NETNSID]);
 
-		if (id >= 0)
-			fprintf(fp, " link-netnsid %d", id);
-		else
-			fprintf(fp, " link-netnsid unknown");
+		if (is_json_context()) {
+			print_int(PRINT_JSON, "link_netnsid", NULL, id);
+		} else {
+			if (id >= 0)
+				print_int(PRINT_FP, NULL,
+					  " link-netnsid %d", id);
+			else
+				print_string(PRINT_FP, NULL,
+					     " link-netnsid %s", "unknown");
+		}
 	}
 
 	if (tb[IFLA_PROTO_DOWN]) {
 		if (rta_getattr_u8(tb[IFLA_PROTO_DOWN]))
-			fprintf(fp, " protodown on ");
+			print_bool(PRINT_ANY,
+				   "proto_down", " protodown on ", true);
 	}
 
 	if (show_details) {
 		if (tb[IFLA_PROMISCUITY])
-			fprintf(fp, " promiscuity %u ",
-				rta_getattr_u32(tb[IFLA_PROMISCUITY]));
+			print_uint(PRINT_ANY,
+				   "promiscuity",
+				   " promiscuity %u ",
+				   rta_getattr_u32(tb[IFLA_PROMISCUITY]));
 
 		if (tb[IFLA_LINKINFO])
 			print_linktype(fp, tb[IFLA_LINKINFO]);
@@ -915,50 +1255,68 @@ int print_linkinfo(const struct sockaddr_nl *who,
 			print_af_spec(fp, tb[IFLA_AF_SPEC]);
 
 		if (tb[IFLA_NUM_TX_QUEUES])
-			fprintf(fp, "numtxqueues %u ",
-				rta_getattr_u32(tb[IFLA_NUM_TX_QUEUES]));
+			print_uint(PRINT_ANY,
+				   "num_tx_queues",
+				   "numtxqueues %u ",
+				   rta_getattr_u32(tb[IFLA_NUM_TX_QUEUES]));
 
 		if (tb[IFLA_NUM_RX_QUEUES])
-			fprintf(fp, "numrxqueues %u ",
-				rta_getattr_u32(tb[IFLA_NUM_RX_QUEUES]));
+			print_uint(PRINT_ANY,
+				   "num_rx_queues",
+				   "numrxqueues %u ",
+				   rta_getattr_u32(tb[IFLA_NUM_RX_QUEUES]));
 
 		if (tb[IFLA_GSO_MAX_SIZE])
-			fprintf(fp, "gso_max_size %u ",
-				rta_getattr_u32(tb[IFLA_GSO_MAX_SIZE]));
+			print_uint(PRINT_ANY,
+				   "gso_max_size",
+				   "gso_max_size %u ",
+				   rta_getattr_u32(tb[IFLA_GSO_MAX_SIZE]));
 
 		if (tb[IFLA_GSO_MAX_SEGS])
-			fprintf(fp, "gso_max_segs %u ",
-				rta_getattr_u32(tb[IFLA_GSO_MAX_SEGS]));
+			print_uint(PRINT_ANY,
+				   "gso_max_segs",
+				   "gso_max_segs %u ",
+				   rta_getattr_u32(tb[IFLA_GSO_MAX_SEGS]));
 
 		if (tb[IFLA_PHYS_PORT_NAME])
-			fprintf(fp, "portname %s ",
-				rta_getattr_str(tb[IFLA_PHYS_PORT_NAME]));
+			print_string(PRINT_ANY,
+				     "phys_port_name",
+				     "portname %s ",
+				     rta_getattr_str(tb[IFLA_PHYS_PORT_NAME]));
 
 		if (tb[IFLA_PHYS_PORT_ID]) {
 			SPRINT_BUF(b1);
-			fprintf(fp, "portid %s ",
-				hexstring_n2a(RTA_DATA(tb[IFLA_PHYS_PORT_ID]),
-					      RTA_PAYLOAD(tb[IFLA_PHYS_PORT_ID]),
-					      b1, sizeof(b1)));
+			print_string(PRINT_ANY,
+				     "phys_port_id",
+				     "portid %s ",
+				     hexstring_n2a(
+					     RTA_DATA(tb[IFLA_PHYS_PORT_ID]),
+					     RTA_PAYLOAD(tb[IFLA_PHYS_PORT_ID]),
+					     b1, sizeof(b1)));
 		}
 
 		if (tb[IFLA_PHYS_SWITCH_ID]) {
 			SPRINT_BUF(b1);
-			fprintf(fp, "switchid %s ",
-				hexstring_n2a(RTA_DATA(tb[IFLA_PHYS_SWITCH_ID]),
-					      RTA_PAYLOAD(tb[IFLA_PHYS_SWITCH_ID]),
-					      b1, sizeof(b1)));
+			print_string(PRINT_ANY,
+				     "phys_switch_id",
+				     "switchid %s ",
+				     hexstring_n2a(RTA_DATA(tb[IFLA_PHYS_SWITCH_ID]),
+						   RTA_PAYLOAD(tb[IFLA_PHYS_SWITCH_ID]),
+						   b1, sizeof(b1)));
 		}
 	}
 
 
 	if ((do_link || show_details) && tb[IFLA_IFALIAS]) {
-		fprintf(fp, "%s    alias %s", _SL_,
-			rta_getattr_str(tb[IFLA_IFALIAS]));
+		print_string(PRINT_FP, NULL, "%s    ", _SL_);
+		print_string(PRINT_ANY,
+			     "ifalias",
+			     "alias %s",
+			     rta_getattr_str(tb[IFLA_IFALIAS]));
 	}
 
 	if (do_link && show_stats) {
-		fprintf(fp, "%s", _SL_);
+		print_string(PRINT_FP, NULL, "%s", _SL_);
 		__print_link_stats(fp, tb);
 	}
 
@@ -966,11 +1324,16 @@ int print_linkinfo(const struct sockaddr_nl *who,
 		struct rtattr *i, *vflist = tb[IFLA_VFINFO_LIST];
 		int rem = RTA_PAYLOAD(vflist);
 
-		for (i = RTA_DATA(vflist); RTA_OK(i, rem); i = RTA_NEXT(i, rem))
+		open_json_array(PRINT_JSON, "vfinfo_list");
+		for (i = RTA_DATA(vflist); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
+			open_json_object(NULL);
 			print_vfinfo(fp, i);
+			close_json_object();
+		}
+		close_json_array(PRINT_JSON, NULL);
 	}
 
-	fprintf(fp, "\n");
+	print_string(PRINT_FP, NULL, "\n", NULL);
 	fflush(fp);
 	return 1;
 }
@@ -1009,7 +1372,7 @@ static unsigned int get_ifa_flags(struct ifaddrmsg *ifa,
 				  struct rtattr *ifa_flags_attr)
 {
 	return ifa_flags_attr ? rta_getattr_u32(ifa_flags_attr) :
-				ifa->ifa_flags;
+		ifa->ifa_flags;
 }
 
 /* Mapping from argument to address flag mask */
@@ -1042,20 +1405,34 @@ static void print_ifa_flags(FILE *fp, const struct ifaddrmsg *ifa,
 
 		if (mask == IFA_F_PERMANENT) {
 			if (!(flags & mask))
-				fprintf(fp, "dynamic ");
+				print_bool(PRINT_ANY,
+					   "dynamic", "dynamic ", true);
 		} else if (flags & mask) {
 			if (mask == IFA_F_SECONDARY &&
-			    ifa->ifa_family == AF_INET6)
-				fprintf(fp, "temporary ");
-			else
-				fprintf(fp, "%s ", ifa_flag_names[i].name);
+			    ifa->ifa_family == AF_INET6) {
+				print_bool(PRINT_ANY,
+					   "temporary", "temporary ", true);
+			} else {
+				print_string(PRINT_FP, NULL,
+					     "%s ", ifa_flag_names[i].name);
+				print_bool(PRINT_JSON,
+					   ifa_flag_names[i].name, NULL, true);
+			}
 		}
 
 		flags &= ~mask;
 	}
 
-	if (flags)
-		fprintf(fp, "flags %02x ", flags);
+	if (flags) {
+		if (is_json_context()) {
+			SPRINT_BUF(b1);
+
+			snprintf(b1, sizeof(b1), "%02x", flags);
+			print_string(PRINT_JSON, "ifa_flags", NULL, b1);
+		} else {
+			fprintf(fp, "flags %02x ", flags);
+		}
+	}
 
 }
 
@@ -1176,80 +1553,130 @@ int print_addrinfo(const struct sockaddr_nl *who, struct nlmsghdr *n,
 	}
 
 	if (n->nlmsg_type == RTM_DELADDR)
-		fprintf(fp, "Deleted ");
+		print_bool(PRINT_ANY, "deleted", "Deleted ", true);
 
 	if (!brief) {
-		if (filter.oneline || filter.flushb)
-			fprintf(fp, "%u: %s", ifa->ifa_index, ll_index_to_name(ifa->ifa_index));
+		if (filter.oneline || filter.flushb) {
+			const char *dev = ll_index_to_name(ifa->ifa_index);
+
+			if (is_json_context()) {
+				print_int(PRINT_JSON,
+					  "index", NULL, ifa->ifa_index);
+				print_string(PRINT_JSON, "dev", NULL, dev);
+			} else {
+				fprintf(fp, "%u: %s", ifa->ifa_index, dev);
+			}
+		}
+
+		int family = ifa->ifa_family;
+
 		if (ifa->ifa_family == AF_INET)
-			fprintf(fp, "    inet ");
+			print_string(PRINT_ANY, "family", "    %s ", "inet");
 		else if (ifa->ifa_family == AF_INET6)
-			fprintf(fp, "    inet6 ");
+			print_string(PRINT_ANY, "family", "    %s ", "inet6");
 		else if (ifa->ifa_family == AF_DECnet)
-			fprintf(fp, "    dnet ");
+			print_string(PRINT_ANY, "family", "    %s ", "dnet");
 		else if (ifa->ifa_family == AF_IPX)
-			fprintf(fp, "     ipx ");
+			print_string(PRINT_ANY, "family", "     %s ", "ipx");
 		else
-			fprintf(fp, "    family %d ", ifa->ifa_family);
+			print_int(PRINT_ANY,
+				  "family_index",
+				  "    family %d ", family);
 	}
 
 	if (rta_tb[IFA_LOCAL]) {
-		color_fprintf(fp, ifa_family_color(ifa->ifa_family), "%s",
-			      format_host_rta(ifa->ifa_family,
-					      rta_tb[IFA_LOCAL]));
+		print_color_string(PRINT_ANY,
+				   ifa_family_color(ifa->ifa_family),
+				   "local", "%s",
+				   format_host_rta(ifa->ifa_family,
+						   rta_tb[IFA_LOCAL]));
 		if (rta_tb[IFA_ADDRESS] &&
 		    memcmp(RTA_DATA(rta_tb[IFA_ADDRESS]),
 			   RTA_DATA(rta_tb[IFA_LOCAL]),
 			   ifa->ifa_family == AF_INET ? 4 : 16)) {
-			fprintf(fp, " peer ");
-			color_fprintf(fp, ifa_family_color(ifa->ifa_family),
-				      "%s", format_host_rta(ifa->ifa_family,
-				      rta_tb[IFA_ADDRESS]));
+			print_string(PRINT_FP, NULL, " %s ", "peer");
+			print_color_string(PRINT_ANY,
+					   ifa_family_color(ifa->ifa_family),
+					   "address",
+					   "%s",
+					   format_host_rta(ifa->ifa_family,
+							   rta_tb[IFA_ADDRESS]));
 		}
-		fprintf(fp, "/%d ", ifa->ifa_prefixlen);
+		print_int(PRINT_ANY, "prefixlen", "/%d", ifa->ifa_prefixlen);
 	}
 
 	if (brief)
 		goto brief_exit;
 
 	if (rta_tb[IFA_BROADCAST]) {
-		fprintf(fp, "brd ");
-		color_fprintf(fp, ifa_family_color(ifa->ifa_family), "%s ",
-			      format_host_rta(ifa->ifa_family,
-					      rta_tb[IFA_BROADCAST]));
+		print_string(PRINT_FP, NULL, "%s ", "brd");
+		print_color_string(PRINT_ANY,
+				   ifa_family_color(ifa->ifa_family),
+				   "broadcast",
+				   "%s ",
+				   format_host_rta(ifa->ifa_family,
+						   rta_tb[IFA_BROADCAST]));
 	}
+
 	if (rta_tb[IFA_ANYCAST]) {
-		fprintf(fp, "any ");
-		color_fprintf(fp, ifa_family_color(ifa->ifa_family), "%s ",
-			      format_host_rta(ifa->ifa_family,
-					      rta_tb[IFA_ANYCAST]));
+		print_string(PRINT_FP, NULL, "%s ", "any");
+		print_color_string(PRINT_ANY,
+				   ifa_family_color(ifa->ifa_family),
+				   "anycast",
+				   "%s ",
+				   format_host_rta(ifa->ifa_family,
+						   rta_tb[IFA_ANYCAST]));
 	}
-	fprintf(fp, "scope %s ", rtnl_rtscope_n2a(ifa->ifa_scope, b1, sizeof(b1)));
+
+	print_string(PRINT_ANY,
+		     "scope",
+		     "scope %s ",
+		     rtnl_rtscope_n2a(ifa->ifa_scope, b1, sizeof(b1)));
 
 	print_ifa_flags(fp, ifa, ifa_flags);
 
 	if (rta_tb[IFA_LABEL])
-		fprintf(fp, "%s", rta_getattr_str(rta_tb[IFA_LABEL]));
+		print_string(PRINT_ANY,
+			     "label",
+			     "%s",
+			     rta_getattr_str(rta_tb[IFA_LABEL]));
+
 	if (rta_tb[IFA_CACHEINFO]) {
 		struct ifa_cacheinfo *ci = RTA_DATA(rta_tb[IFA_CACHEINFO]);
 
-		fprintf(fp, "%s", _SL_);
-		fprintf(fp, "       valid_lft ");
-		if (ci->ifa_valid == INFINITY_LIFE_TIME)
-			fprintf(fp, "forever");
-		else
-			fprintf(fp, "%usec", ci->ifa_valid);
-		fprintf(fp, " preferred_lft ");
-		if (ci->ifa_prefered == INFINITY_LIFE_TIME)
-			fprintf(fp, "forever");
-		else {
+		print_string(PRINT_FP, NULL, "%s", _SL_);
+		print_string(PRINT_FP, NULL, "       valid_lft ", NULL);
+
+		if (ci->ifa_valid == INFINITY_LIFE_TIME) {
+			print_uint(PRINT_JSON,
+				   "valid_life_time",
+				   NULL, INFINITY_LIFE_TIME);
+			print_string(PRINT_FP, NULL, "%s", "forever");
+		} else {
+			print_uint(PRINT_ANY,
+				   "valid_life_time", "%usec", ci->ifa_valid);
+		}
+
+		print_string(PRINT_FP, NULL, " preferred_lft ", NULL);
+		if (ci->ifa_prefered == INFINITY_LIFE_TIME) {
+			print_uint(PRINT_JSON,
+				   "preferred_life_time",
+				   NULL, INFINITY_LIFE_TIME);
+			print_string(PRINT_FP, NULL, "%s", "forever");
+		} else {
 			if (ifa_flags & IFA_F_DEPRECATED)
-				fprintf(fp, "%dsec", ci->ifa_prefered);
+				print_int(PRINT_ANY,
+					  "preferred_life_time",
+					  "%dsec",
+					  ci->ifa_prefered);
 			else
-				fprintf(fp, "%usec", ci->ifa_prefered);
+				print_uint(PRINT_ANY,
+					   "preferred_life_time",
+					   "%usec",
+					   ci->ifa_prefered);
 		}
 	}
-	fprintf(fp, "\n");
+	print_string(PRINT_FP, NULL, "%s", "\n");
 brief_exit:
 	fflush(fp);
 	return 0;
@@ -1258,6 +1685,7 @@ brief_exit:
 static int print_selected_addrinfo(struct ifinfomsg *ifi,
 				   struct nlmsg_list *ainfo, FILE *fp)
 {
+	open_json_array(PRINT_JSON, "addr_info");
 	for ( ; ainfo ;  ainfo = ainfo->next) {
 		struct nlmsghdr *n = &ainfo->h;
 		struct ifaddrmsg *ifa = NLMSG_DATA(n);
@@ -1275,10 +1703,14 @@ static int print_selected_addrinfo(struct ifinfomsg *ifi,
 		if (filter.up && !(ifi->ifi_flags&IFF_UP))
 			continue;
 
+		open_json_object(NULL);
 		print_addrinfo(NULL, n, fp);
+		close_json_object();
 	}
+	close_json_array(PRINT_JSON, NULL);
+
 	if (brief) {
-		fprintf(fp, "\n");
+		print_string(PRINT_FP, NULL, "%s", "\n");
 		fflush(fp);
 	}
 	return 0;
@@ -1724,6 +2156,12 @@ static int ipaddr_list_flush_or_save(int argc, char **argv, int action)
 		exit(0);
 	}
 
+	/*
+	 * Initialize a json_writer and open an array object
+	 * if -json was specified.
+	 */
+	new_json_obj(json, stdout);
+
 	/*
 	 * If only filter_dev present and none of the other
 	 * link filters are present, use RTM_GETLINK to get
@@ -1732,8 +2170,10 @@ static int ipaddr_list_flush_or_save(int argc, char **argv, int action)
 	if (filter_dev && filter.group == -1 && do_link == 1) {
 		if (iplink_get(0, filter_dev, RTEXT_FILTER_VF) < 0) {
 			perror("Cannot send link get request");
+			delete_json_obj();
 			exit(1);
 		}
+		delete_json_obj();
 		exit(0);
 	}
 
@@ -1755,6 +2195,7 @@ static int ipaddr_list_flush_or_save(int argc, char **argv, int action)
 		int res = 0;
 		struct ifinfomsg *ifi = NLMSG_DATA(&l->h);
 
+		open_json_object(NULL);
 		if (brief) {
 			if (print_linkinfo_brief(NULL, &l->h,
 						 stdout, NULL) == 0)
@@ -1763,13 +2204,14 @@ static int ipaddr_list_flush_or_save(int argc, char **argv, int action)
 								ainfo->head,
 								stdout);
 		} else if (no_link ||
-			  (res = print_linkinfo(NULL, &l->h, stdout)) >= 0) {
+			   (res = print_linkinfo(NULL, &l->h, stdout)) >= 0) {
 			if (filter.family != AF_PACKET)
 				print_selected_addrinfo(ifi,
 							ainfo->head, stdout);
 			if (res > 0 && !do_link && show_stats)
 				print_link_stats(stdout, &l->h);
 		}
+		close_json_object();
 	}
 	fflush(stdout);
 
@@ -1777,7 +2219,7 @@ out:
 	if (ainfo)
 		free_nlmsg_chain(ainfo);
 	free_nlmsg_chain(&linfo);
-
+	delete_json_obj();
 	return 0;
 }
 
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH iproute2 json v2 06/27] ip: iplink.c: open/close json obj for ip -brief -json link show dev DEV
  2017-08-17 17:35 [PATCH iproute2 json v2 00/27] ip: add -json support to 'ip link show' Julien Fortin
                   ` (4 preceding siblings ...)
  2017-08-17 17:35 ` [PATCH iproute2 json v2 05/27] ip: ipaddress.c: add support for json output Julien Fortin
@ 2017-08-17 17:35 ` Julien Fortin
  2017-08-17 17:35 ` [PATCH iproute2 json v2 07/27] ip: iplink_bond.c: add json output support Julien Fortin
                   ` (22 subsequent siblings)
  28 siblings, 0 replies; 33+ messages in thread
From: Julien Fortin @ 2017-08-17 17:35 UTC (permalink / raw)
  To: netdev; +Cc: roopa, nikolay, dsa, Julien Fortin

From: Julien Fortin <julien@cumulusnetworks.com>

Signed-off-by: Julien Fortin <julien@cumulusnetworks.com>
---
 ip/iplink.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/ip/iplink.c b/ip/iplink.c
index 5aff2fde..19bda1b9 100644
--- a/ip/iplink.c
+++ b/ip/iplink.c
@@ -1041,10 +1041,12 @@ int iplink_get(unsigned int flags, char *name, __u32 filt_mask)
 	if (rtnl_talk(&rth, &req.n, &answer.n, sizeof(answer)) < 0)
 		return -2;
 
+	open_json_object(NULL);
 	if (brief)
 		print_linkinfo_brief(NULL, &answer.n, stdout, NULL);
 	else
 		print_linkinfo(NULL, &answer.n, stdout);
+	close_json_object();
 
 	return 0;
 }
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH iproute2 json v2 07/27] ip: iplink_bond.c: add json output support
  2017-08-17 17:35 [PATCH iproute2 json v2 00/27] ip: add -json support to 'ip link show' Julien Fortin
                   ` (5 preceding siblings ...)
  2017-08-17 17:35 ` [PATCH iproute2 json v2 06/27] ip: iplink.c: open/close json obj for ip -brief -json link show dev DEV Julien Fortin
@ 2017-08-17 17:35 ` Julien Fortin
  2017-08-17 17:35 ` [PATCH iproute2 json v2 08/27] ip: iplink_bond_slave.c: add json output support (info_slave_data) Julien Fortin
                   ` (21 subsequent siblings)
  28 siblings, 0 replies; 33+ messages in thread
From: Julien Fortin @ 2017-08-17 17:35 UTC (permalink / raw)
  To: netdev; +Cc: roopa, nikolay, dsa, Julien Fortin

From: Julien Fortin <julien@cumulusnetworks.com>

Schema and live example:
bond: IFLA_INFO_DATA
{
    "mode": {
        "type": "string",
        "attr": "IFLA_BOND_MODE"
    },
    "active_slave": {
        "type": "string",
        "attr": "IFLA_BOND_ACTIVE_SLAVE",
        "mutually_exclusive": {
            "active_slave_index": {
                "type": "int",
                "comment": "if active slave doesn't have a valid ifname"
            }
        }
    },
    "miimon": {
        "type": "uint",
        "attr": "IFLA_BOND_MIIMON"
    },
    "updelay": {
        "type": "uint",
        "attr": "IFLA_BOND_UPDELAY"
    },
    "downdelay": {
        "type": "uint",
        "attr": "IFLA_BOND_DOWNDELAY"
    },
    "use_carrier": {
        "type": "uint",
        "attr": "IFLA_BOND_USE_CARRIER"
    },
    "arp_interval": {
        "type": "uint",
        "attr": "IFLA_BOND_ARP_INTERVAL"
    },
    "arp_ip_target": {
        "type": "array",
        "attr": "IFLA_BOND_ARP_IP_TARGET",
        "array": [
            {
                "type": "string"
            }
        ]
    },
    "arp_validate": {
        "type": "string",
        "attr": "IFLA_BOND_ARP_VALIDATE"
    },
    "arp_all_targets": {
        "type": "string",
        "attr": "IFLA_BOND_ARP_ALL_TARGETS"
    },
    "primary": {
        "type": "string",
        "attr": "IFLA_BOND_PRIMARY",
        "mutually_exclusive": {
            "primary_index": {
                "type": "int",
                "comment": "if primary doesn't have a valid ifname"
            }
        }
    },
    "primary_reselect": {
        "type": "string",
        "attr": "IFLA_BOND_PRIMARY_RESELECT"
    },
    "fail_over_mac": {
        "type": "string",
        "attr": "IFLA_BOND_FAIL_OVER_MAC"
    },
    "xmit_hash_policy": {
        "type": "string",
        "attr": "IFLA_BOND_XMIT_HASH_POLICY"
    },
    "resend_igmp": {
        "type": "uint",
        "attr": "IFLA_BOND_RESEND_IGMP"
    },
    "num_peer_notif": {
        "type": "uint",
        "attr": "IFLA_BOND_NUM_PEER_NOTIF"
    },
    "all_slaves_active": {
        "type": "uint",
        "attr": "IFLA_BOND_ALL_SLAVES_ACTIVE"
    },
    "min_links": {
        "type": "uint",
        "attr": "IFLA_BOND_MIN_LINKS"
    },
    "lp_interval": {
        "type": "uint",
        "attr": "IFLA_BOND_LP_INTERVAL"
    },
    "packets_per_slave": {
        "type": "uint",
        "attr": "IFLA_BOND_PACKETS_PER_SLAVE"
    },
    "ad_lacp_rate": {
        "type": "string",
        "attr": "IFLA_BOND_AD_LACP_RATE"
    },
    "ad_select": {
        "type": "string",
        "attr": "IFLA_BOND_AD_SELECT"
    },
    "ad_info": {
        "type": "dict",
        "attr": "IFLA_BOND_AD_INFO",
        "dict": {
            "aggregator": {
                "type": "int",
                "attr": "IFLA_BOND_AD_INFO_AGGREGATOR"
            },
            "num_ports": {
                "type": "int",
                "attr": "IFLA_BOND_AD_INFO_NUM_PORTS"
            },
            "actor_key": {
                "type": "int",
                "attr": "IFLA_BOND_AD_INFO_ACTOR_KEY"
            },
            "partner_key": {
                "type": "int",
                "attr": "IFLA_BOND_AD_INFO_PARTNER_KEY"
            },
            "partner_mac": {
                "type": "string",
                "attr": "IFLA_BOND_AD_INFO_PARTNER_MAC"
            }
        }
    },
    "ad_actor_sys_prio": {
        "type": "uint",
        "attr": "IFLA_BOND_AD_ACTOR_SYS_PRIO"
    },
    "ad_user_port_key": {
        "type": "uint",
        "attr": "IFLA_BOND_AD_USER_PORT_KEY"
    },
    "ad_actor_system": {
        "type": "string",
        "attr": "IFLA_BOND_AD_ACTOR_SYSTEM"
    },
    "tlb_dynamic_lb": {
        "type": "uint",
        "attr": "IFLA_BOND_TLB_DYNAMIC_LB"
    }
}

$ ip link add dev bond42 type bond
$ ip link set dev swp5 master bond42
$ ip link set dev bond42 up
$ ip link set dev swp5 up
$ ip -details -json link show
[{
        "ifindex": 7,
        "ifname": "swp5",
        "flags": ["BROADCAST","MULTICAST","SLAVE","UP","LOWER_UP"],
        "mtu": 1500,
        "qdisc": "pfifo_fast",
        "master": "bond42",
        "operstate": "UP",
        "linkmode": "DEFAULT",
        "group": "default",
        "txqlen": 1000,
        "link_type": "ether",
        "address": "08:00:27:5c:03:c6",
        "broadcast": "ff:ff:ff:ff:ff:ff",
        "promiscuity": 0,
        "linkinfo": {
            "info_slave_kind": "bond",
            "info_slave_data": {
                "state": "BACKUP",
                "mii_status": "UP",
                "link_failure_count": 0,
                "perm_hwaddr": "08:00:27:5c:03:c6",
                "queue_id": 0,
                "ad_aggregator_id": 1,
                "ad_actor_oper_port_state": 79,
                "ad_partner_oper_port_state": 1
            }
        },
        "inet6_addr_gen_mode": "eui64",
        "num_tx_queues": 1,
        "num_rx_queues": 1,
        "gso_max_size": 65536,
        "gso_max_segs": 65535
    },{
        "ifindex": 14,
        "ifname": "bond42",
        "flags": ["NO-CARRIER","BROADCAST","MULTICAST","MASTER","UP"],
        "mtu": 1500,
        "qdisc": "noqueue",
        "operstate": "DOWN",
        "linkmode": "DEFAULT",
        "group": "default",
        "link_type": "ether",
        "address": "08:00:27:5c:03:c6",
        "broadcast": "ff:ff:ff:ff:ff:ff",
        "promiscuity": 0,
        "linkinfo": {
            "info_kind": "bond",
            "info_data": {
                "mode": "802.3ad",
                "miimon": 100,
                "updelay": 0,
                "downdelay": 0,
                "use_carrier": 1,
                "arp_interval": 0,
                "arp_validate": null,
                "arp_all_targets": "any",
                "primary_reselect": "always",
                "fail_over_mac": "none",
                "xmit_hash_policy": "layer3+4",
                "resend_igmp": 1,
                "num_peer_notif": 1,
                "all_slaves_active": 0,
                "min_links": 1,
                "lp_interval": 1,
                "packets_per_slave": 1,
                "ad_lacp_rate": "fast",
                "ad_select": "stable",
                "ad_info": {
                    "aggregator": 1,
                    "num_ports": 1,
                    "actor_key": 0,
                    "partner_key": 1,
                    "partner_mac": "00:00:00:00:00:00"
                },
                "ad_actor_sys_prio": 65535,
                "ad_user_port_key": 0,
                "ad_actor_system": "00:00:00:00:00:00"
            }
        },
        "inet6_addr_gen_mode": "eui64",
        "num_tx_queues": 16,
        "num_rx_queues": 16,
        "gso_max_size": 65536,
        "gso_max_segs": 65535
    }
]

Signed-off-by: Julien Fortin <julien@cumulusnetworks.com>
---
 ip/iplink_bond.c | 231 ++++++++++++++++++++++++++++++++++++++-----------------
 1 file changed, 160 insertions(+), 71 deletions(-)

diff --git a/ip/iplink_bond.c b/ip/iplink_bond.c
index 772b05fd..2b5cf4f6 100644
--- a/ip/iplink_bond.c
+++ b/ip/iplink_bond.c
@@ -376,8 +376,8 @@ static void bond_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 
 	if (tb[IFLA_BOND_MODE]) {
 		const char *mode = get_name(mode_tbl,
-			rta_getattr_u8(tb[IFLA_BOND_MODE]));
-		fprintf(f, "mode %s ", mode);
+					    rta_getattr_u8(tb[IFLA_BOND_MODE]));
+		print_string(PRINT_ANY, "mode", "mode %s ", mode);
 	}
 
 	if (tb[IFLA_BOND_ACTIVE_SLAVE] &&
@@ -386,61 +386,97 @@ static void bond_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 		const char *n = if_indextoname(ifindex, buf);
 
 		if (n)
-			fprintf(f, "active_slave %s ", n);
+			print_string(PRINT_ANY,
+				     "active_slave",
+				     "active_slave %s ",
+				     n);
 		else
-			fprintf(f, "active_slave %u ", ifindex);
+			print_uint(PRINT_ANY,
+				   "active_slave_index",
+				   "active_slave %u ",
+				   ifindex);
 	}
 
 	if (tb[IFLA_BOND_MIIMON])
-		fprintf(f, "miimon %u ", rta_getattr_u32(tb[IFLA_BOND_MIIMON]));
+		print_uint(PRINT_ANY,
+			   "miimon",
+			   "miimon %u ",
+			   rta_getattr_u32(tb[IFLA_BOND_MIIMON]));
 
 	if (tb[IFLA_BOND_UPDELAY])
-		fprintf(f, "updelay %u ", rta_getattr_u32(tb[IFLA_BOND_UPDELAY]));
+		print_uint(PRINT_ANY,
+			   "updelay",
+			   "updelay %u ",
+			   rta_getattr_u32(tb[IFLA_BOND_UPDELAY]));
 
 	if (tb[IFLA_BOND_DOWNDELAY])
-		fprintf(f, "downdelay %u ",
-			rta_getattr_u32(tb[IFLA_BOND_DOWNDELAY]));
+		print_uint(PRINT_ANY,
+			   "downdelay",
+			   "downdelay %u ",
+			   rta_getattr_u32(tb[IFLA_BOND_DOWNDELAY]));
 
 	if (tb[IFLA_BOND_USE_CARRIER])
-		fprintf(f, "use_carrier %u ",
-			rta_getattr_u8(tb[IFLA_BOND_USE_CARRIER]));
+		print_uint(PRINT_ANY,
+			   "use_carrier",
+			   "use_carrier %u ",
+			   rta_getattr_u8(tb[IFLA_BOND_USE_CARRIER]));
 
 	if (tb[IFLA_BOND_ARP_INTERVAL])
-		fprintf(f, "arp_interval %u ",
-			rta_getattr_u32(tb[IFLA_BOND_ARP_INTERVAL]));
+		print_uint(PRINT_ANY,
+			   "arp_interval",
+			   "arp_interval %u ",
+			   rta_getattr_u32(tb[IFLA_BOND_ARP_INTERVAL]));
 
 	if (tb[IFLA_BOND_ARP_IP_TARGET]) {
 		struct rtattr *iptb[BOND_MAX_ARP_TARGETS + 1];
 		int i;
 
 		parse_rtattr_nested(iptb, BOND_MAX_ARP_TARGETS,
-			tb[IFLA_BOND_ARP_IP_TARGET]);
+				    tb[IFLA_BOND_ARP_IP_TARGET]);
 
-		if (iptb[0])
-			fprintf(f, "arp_ip_target ");
+		if (iptb[0]) {
+			open_json_array(PRINT_JSON, "arp_ip_target");
+			print_string(PRINT_FP, NULL, "arp_ip_target ", NULL);
+		}
 
 		for (i = 0; i < BOND_MAX_ARP_TARGETS; i++) {
 			if (iptb[i])
-				fprintf(f, "%s",
-					rt_addr_n2a_rta(AF_INET, iptb[i]));
-			if (i < BOND_MAX_ARP_TARGETS-1 && iptb[i+1])
+				print_string(PRINT_ANY,
+					     NULL,
+					     "%s",
+					     rt_addr_n2a_rta(AF_INET, iptb[i]));
+			if (!is_json_context()
+			    && i < BOND_MAX_ARP_TARGETS-1
+			    && iptb[i+1])
 				fprintf(f, ",");
 		}
 
-		if (iptb[0])
-			fprintf(f, " ");
+		if (iptb[0]) {
+			print_string(PRINT_FP, NULL, " ", NULL);
+			close_json_array(PRINT_JSON, NULL);
+		}
 	}
 
 	if (tb[IFLA_BOND_ARP_VALIDATE]) {
-		const char *arp_validate = get_name(arp_validate_tbl,
-			rta_getattr_u32(tb[IFLA_BOND_ARP_VALIDATE]));
-		fprintf(f, "arp_validate %s ", arp_validate);
+		__u32 arp_v = rta_getattr_u32(tb[IFLA_BOND_ARP_VALIDATE]);
+		const char *arp_validate = get_name(arp_validate_tbl, arp_v);
+
+		if (!arp_v && is_json_context())
+			print_null(PRINT_JSON, "arp_validate", NULL, NULL);
+		else
+			print_string(PRINT_ANY,
+				     "arp_validate",
+				     "arp_validate %s ",
+				     arp_validate);
 	}
 
 	if (tb[IFLA_BOND_ARP_ALL_TARGETS]) {
 		const char *arp_all_targets = get_name(arp_all_targets_tbl,
-			rta_getattr_u32(tb[IFLA_BOND_ARP_ALL_TARGETS]));
-		fprintf(f, "arp_all_targets %s ", arp_all_targets);
+						       rta_getattr_u32(tb[IFLA_BOND_ARP_ALL_TARGETS]));
+		print_string(PRINT_ANY,
+			     "arp_all_targets",
+			     "arp_all_targets %s ",
+			     arp_all_targets);
 	}
 
 	if (tb[IFLA_BOND_PRIMARY] &&
@@ -449,123 +485,176 @@ static void bond_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 		const char *n = if_indextoname(ifindex, buf);
 
 		if (n)
-			fprintf(f, "primary %s ", n);
+			print_string(PRINT_ANY, "primary", "primary %s ", n);
 		else
-			fprintf(f, "primary %u ", ifindex);
+			print_uint(PRINT_ANY,
+				   "primary_index",
+				   "primary %u ",
+				   ifindex);
 	}
 
 	if (tb[IFLA_BOND_PRIMARY_RESELECT]) {
 		const char *primary_reselect = get_name(primary_reselect_tbl,
-			rta_getattr_u8(tb[IFLA_BOND_PRIMARY_RESELECT]));
-		fprintf(f, "primary_reselect %s ", primary_reselect);
+							rta_getattr_u8(tb[IFLA_BOND_PRIMARY_RESELECT]));
+		print_string(PRINT_ANY,
+			     "primary_reselect",
+			     "primary_reselect %s ",
+			     primary_reselect);
 	}
 
 	if (tb[IFLA_BOND_FAIL_OVER_MAC]) {
 		const char *fail_over_mac = get_name(fail_over_mac_tbl,
-			rta_getattr_u8(tb[IFLA_BOND_FAIL_OVER_MAC]));
-		fprintf(f, "fail_over_mac %s ", fail_over_mac);
+						     rta_getattr_u8(tb[IFLA_BOND_FAIL_OVER_MAC]));
+		print_string(PRINT_ANY,
+			     "fail_over_mac",
+			     "fail_over_mac %s ",
+			     fail_over_mac);
 	}
 
 	if (tb[IFLA_BOND_XMIT_HASH_POLICY]) {
 		const char *xmit_hash_policy = get_name(xmit_hash_policy_tbl,
-			rta_getattr_u8(tb[IFLA_BOND_XMIT_HASH_POLICY]));
-		fprintf(f, "xmit_hash_policy %s ", xmit_hash_policy);
+							rta_getattr_u8(tb[IFLA_BOND_XMIT_HASH_POLICY]));
+		print_string(PRINT_ANY,
+			     "xmit_hash_policy",
+			     "xmit_hash_policy %s ",
+			     xmit_hash_policy);
 	}
 
 	if (tb[IFLA_BOND_RESEND_IGMP])
-		fprintf(f, "resend_igmp %u ",
-			rta_getattr_u32(tb[IFLA_BOND_RESEND_IGMP]));
+		print_uint(PRINT_ANY,
+			   "resend_igmp",
+			   "resend_igmp %u ",
+			   rta_getattr_u32(tb[IFLA_BOND_RESEND_IGMP]));
 
 	if (tb[IFLA_BOND_NUM_PEER_NOTIF])
-		fprintf(f, "num_grat_arp %u ",
-			rta_getattr_u8(tb[IFLA_BOND_NUM_PEER_NOTIF]));
+		print_uint(PRINT_ANY,
+			   "num_peer_notif",
+			   "num_grat_arp %u ",
+			   rta_getattr_u8(tb[IFLA_BOND_NUM_PEER_NOTIF]));
 
 	if (tb[IFLA_BOND_ALL_SLAVES_ACTIVE])
-		fprintf(f, "all_slaves_active %u ",
-			rta_getattr_u8(tb[IFLA_BOND_ALL_SLAVES_ACTIVE]));
+		print_uint(PRINT_ANY,
+			   "all_slaves_active",
+			   "all_slaves_active %u ",
+			   rta_getattr_u8(tb[IFLA_BOND_ALL_SLAVES_ACTIVE]));
 
 	if (tb[IFLA_BOND_MIN_LINKS])
-		fprintf(f, "min_links %u ",
-			rta_getattr_u32(tb[IFLA_BOND_MIN_LINKS]));
+		print_uint(PRINT_ANY,
+			   "min_links",
+			   "min_links %u ",
+			   rta_getattr_u32(tb[IFLA_BOND_MIN_LINKS]));
 
 	if (tb[IFLA_BOND_LP_INTERVAL])
-		fprintf(f, "lp_interval %u ",
-			rta_getattr_u32(tb[IFLA_BOND_LP_INTERVAL]));
+		print_uint(PRINT_ANY,
+			   "lp_interval",
+			   "lp_interval %u ",
+			   rta_getattr_u32(tb[IFLA_BOND_LP_INTERVAL]));
 
 	if (tb[IFLA_BOND_PACKETS_PER_SLAVE])
-		fprintf(f, "packets_per_slave %u ",
-			rta_getattr_u32(tb[IFLA_BOND_PACKETS_PER_SLAVE]));
+		print_uint(PRINT_ANY,
+			   "packets_per_slave",
+			   "packets_per_slave %u ",
+			   rta_getattr_u32(tb[IFLA_BOND_PACKETS_PER_SLAVE]));
 
 	if (tb[IFLA_BOND_AD_LACP_RATE]) {
 		const char *lacp_rate = get_name(lacp_rate_tbl,
-			rta_getattr_u8(tb[IFLA_BOND_AD_LACP_RATE]));
-		fprintf(f, "lacp_rate %s ", lacp_rate);
+						 rta_getattr_u8(tb[IFLA_BOND_AD_LACP_RATE]));
+		print_string(PRINT_ANY,
+			     "ad_lacp_rate",
+			     "lacp_rate %s ",
+			     lacp_rate);
 	}
 
 	if (tb[IFLA_BOND_AD_SELECT]) {
 		const char *ad_select = get_name(ad_select_tbl,
-			rta_getattr_u8(tb[IFLA_BOND_AD_SELECT]));
-		fprintf(f, "ad_select %s ", ad_select);
+						 rta_getattr_u8(tb[IFLA_BOND_AD_SELECT]));
+		print_string(PRINT_ANY,
+			     "ad_select",
+			     "ad_select %s ",
+			     ad_select);
 	}
 
 	if (tb[IFLA_BOND_AD_INFO]) {
 		struct rtattr *adtb[IFLA_BOND_AD_INFO_MAX + 1];
 
 		parse_rtattr_nested(adtb, IFLA_BOND_AD_INFO_MAX,
-			tb[IFLA_BOND_AD_INFO]);
+				    tb[IFLA_BOND_AD_INFO]);
+
+		open_json_object("ad_info");
 
 		if (adtb[IFLA_BOND_AD_INFO_AGGREGATOR])
-			fprintf(f, "ad_aggregator %d ",
-			  rta_getattr_u16(adtb[IFLA_BOND_AD_INFO_AGGREGATOR]));
+			print_int(PRINT_ANY,
+				  "aggregator",
+				  "ad_aggregator %d ",
+				  rta_getattr_u16(adtb[IFLA_BOND_AD_INFO_AGGREGATOR]));
 
 		if (adtb[IFLA_BOND_AD_INFO_NUM_PORTS])
-			fprintf(f, "ad_num_ports %d ",
-			  rta_getattr_u16(adtb[IFLA_BOND_AD_INFO_NUM_PORTS]));
+			print_int(PRINT_ANY,
+				  "num_ports",
+				  "ad_num_ports %d ",
+				  rta_getattr_u16(adtb[IFLA_BOND_AD_INFO_NUM_PORTS]));
 
 		if (adtb[IFLA_BOND_AD_INFO_ACTOR_KEY])
-			fprintf(f, "ad_actor_key %d ",
-			  rta_getattr_u16(adtb[IFLA_BOND_AD_INFO_ACTOR_KEY]));
+			print_int(PRINT_ANY,
+				  "actor_key",
+				  "ad_actor_key %d ",
+				  rta_getattr_u16(adtb[IFLA_BOND_AD_INFO_ACTOR_KEY]));
 
 		if (adtb[IFLA_BOND_AD_INFO_PARTNER_KEY])
-			fprintf(f, "ad_partner_key %d ",
-			  rta_getattr_u16(adtb[IFLA_BOND_AD_INFO_PARTNER_KEY]));
+			print_int(PRINT_ANY,
+				  "partner_key",
+				  "ad_partner_key %d ",
+				  rta_getattr_u16(adtb[IFLA_BOND_AD_INFO_PARTNER_KEY]));
 
 		if (adtb[IFLA_BOND_AD_INFO_PARTNER_MAC]) {
 			unsigned char *p =
 				RTA_DATA(adtb[IFLA_BOND_AD_INFO_PARTNER_MAC]);
 			SPRINT_BUF(b);
-			fprintf(f, "ad_partner_mac %s ",
-				ll_addr_n2a(p, ETH_ALEN, 0, b, sizeof(b)));
+			print_string(PRINT_ANY,
+				     "partner_mac",
+				     "ad_partner_mac %s ",
+				     ll_addr_n2a(p, ETH_ALEN, 0, b, sizeof(b)));
 		}
+
+		close_json_object();
 	}
 
 	if (tb[IFLA_BOND_AD_ACTOR_SYS_PRIO]) {
-		fprintf(f, "ad_actor_sys_prio %u ",
-			rta_getattr_u16(tb[IFLA_BOND_AD_ACTOR_SYS_PRIO]));
+		print_uint(PRINT_ANY,
+			   "ad_actor_sys_prio",
+			   "ad_actor_sys_prio %u ",
+			   rta_getattr_u16(tb[IFLA_BOND_AD_ACTOR_SYS_PRIO]));
 	}
 
 	if (tb[IFLA_BOND_AD_USER_PORT_KEY]) {
-		fprintf(f, "ad_user_port_key %u ",
-			rta_getattr_u16(tb[IFLA_BOND_AD_USER_PORT_KEY]));
+		print_uint(PRINT_ANY,
+			   "ad_user_port_key",
+			   "ad_user_port_key %u ",
+			   rta_getattr_u16(tb[IFLA_BOND_AD_USER_PORT_KEY]));
 	}
 
 	if (tb[IFLA_BOND_AD_ACTOR_SYSTEM]) {
 		/* We assume the l2 address is an Ethernet MAC address */
 		SPRINT_BUF(b1);
-		fprintf(f, "ad_actor_system %s ",
-			ll_addr_n2a(RTA_DATA(tb[IFLA_BOND_AD_ACTOR_SYSTEM]),
-				    RTA_PAYLOAD(tb[IFLA_BOND_AD_ACTOR_SYSTEM]),
-				    1 /*ARPHDR_ETHER*/, b1, sizeof(b1)));
+
+		print_string(PRINT_ANY,
+			     "ad_actor_system",
+			     "ad_actor_system %s ",
+			     ll_addr_n2a(RTA_DATA(tb[IFLA_BOND_AD_ACTOR_SYSTEM]),
+					 RTA_PAYLOAD(tb[IFLA_BOND_AD_ACTOR_SYSTEM]),
+					 1 /*ARPHDR_ETHER*/, b1, sizeof(b1)));
 	}
 
 	if (tb[IFLA_BOND_TLB_DYNAMIC_LB]) {
-		fprintf(f, "tlb_dynamic_lb %u ",
-			rta_getattr_u8(tb[IFLA_BOND_TLB_DYNAMIC_LB]));
+		print_uint(PRINT_ANY,
+			   "tlb_dynamic_lb",
+			   "tlb_dynamic_lb %u ",
+			   rta_getattr_u8(tb[IFLA_BOND_TLB_DYNAMIC_LB]));
 	}
 }
 
 static void bond_print_help(struct link_util *lu, int argc, char **argv,
-	FILE *f)
+			    FILE *f)
 {
 	print_explain(f);
 }
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH iproute2 json v2 08/27] ip: iplink_bond_slave.c: add json output support (info_slave_data)
  2017-08-17 17:35 [PATCH iproute2 json v2 00/27] ip: add -json support to 'ip link show' Julien Fortin
                   ` (6 preceding siblings ...)
  2017-08-17 17:35 ` [PATCH iproute2 json v2 07/27] ip: iplink_bond.c: add json output support Julien Fortin
@ 2017-08-17 17:35 ` Julien Fortin
  2017-08-17 17:35 ` [PATCH iproute2 json v2 09/27] ip: iplink_hsr.c: add json output support Julien Fortin
                   ` (20 subsequent siblings)
  28 siblings, 0 replies; 33+ messages in thread
From: Julien Fortin @ 2017-08-17 17:35 UTC (permalink / raw)
  To: netdev; +Cc: roopa, nikolay, dsa, Julien Fortin

From: Julien Fortin <julien@cumulusnetworks.com>

Schema and live example:
bond_slave: IFLA_INFO_SLAVE_DATA
{
    "state": {
        "type": "string",
        "attr": "IFLA_BOND_SLAVE_STATE",
        "mutually_exclusive": {
            "state_index": {
                "type": "int",
                "comment": "if (state >= ARRAY_SIZE(slave_states))"
            }
        }
    },
    "mii_status": {
        "type": "string",
        "attr": "IFLA_BOND_SLAVE_MII_STATUS",
        "mutually_exclusive": {
            "mii_status_index": {
                "type": "int",
                "comment": "if (status >= ARRAY_SIZE(slave_mii_status))"
            }
        }
    },
    "link_failure_count": {
        "type": "int",
        "attr": "IFLA_BOND_SLAVE_LINK_FAILURE_COUNT"
    },
    "perm_hwaddr": {
        "type": "string",
        "attr": "IFLA_BOND_SLAVE_PERM_HWADDR"
    },
    "queue_id": {
        "type": "int",
        "attr": "IFLA_BOND_SLAVE_QUEUE_ID"
    },
    "ad_aggregator_id": {
        "type": "int",
        "attr": "IFLA_BOND_SLAVE_AD_AGGREGATOR_ID"
    },
    "ad_actor_oper_port_state": {
        "type": "int",
        "attr": "IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE"
    },
    "ad_partner_oper_port_state": {
        "type": "int",
        "attr": "IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE"
    }
}

$ ip link add dev bond42 type bond
$ ip link set dev swp5 master bond42
$ ip link set dev bond42 up
$ ip link set dev swp5 up
$ ip -details -json link show
[{
        "ifindex": 7,
        "ifname": "swp5",
        "flags": ["BROADCAST","MULTICAST","SLAVE","UP","LOWER_UP"],
        "mtu": 1500,
        "qdisc": "pfifo_fast",
        "master": "bond42",
        "operstate": "UP",
        "linkmode": "DEFAULT",
        "group": "default",
        "txqlen": 1000,
        "link_type": "ether",
        "address": "08:00:27:5c:03:c6",
        "broadcast": "ff:ff:ff:ff:ff:ff",
        "promiscuity": 0,
        "linkinfo": {
            "info_slave_kind": "bond",
            "info_slave_data": {
                "state": "BACKUP",
                "mii_status": "UP",
                "link_failure_count": 0,
                "perm_hwaddr": "08:00:27:5c:03:c6",
                "queue_id": 0,
                "ad_aggregator_id": 1,
                "ad_actor_oper_port_state": 79,
                "ad_partner_oper_port_state": 1
            }
        },
        "inet6_addr_gen_mode": "eui64",
        "num_tx_queues": 1,
        "num_rx_queues": 1,
        "gso_max_size": 65536,
        "gso_max_segs": 65535
    },{
        "ifindex": 14,
        "ifname": "bond42",
        "flags": ["NO-CARRIER","BROADCAST","MULTICAST","MASTER","UP"],
        "mtu": 1500,
        "qdisc": "noqueue",
        "operstate": "DOWN",
        "linkmode": "DEFAULT",
        "group": "default",
        "link_type": "ether",
        "address": "08:00:27:5c:03:c6",
        "broadcast": "ff:ff:ff:ff:ff:ff",
        "promiscuity": 0,
        "linkinfo": {
            "info_kind": "bond",
            "info_data": {
                "mode": "802.3ad",
                "miimon": 100,
                "updelay": 0,
                "downdelay": 0,
                "use_carrier": 1,
                "arp_interval": 0,
                "arp_validate": null,
                "arp_all_targets": "any",
                "primary_reselect": "always",
                "fail_over_mac": "none",
                "xmit_hash_policy": "layer3+4",
                "resend_igmp": 1,
                "num_peer_notif": 1,
                "all_slaves_active": 0,
                "min_links": 1,
                "lp_interval": 1,
                "packets_per_slave": 1,
                "ad_lacp_rate": "fast",
                "ad_select": "stable",
                "ad_info": {
                    "aggregator": 1,
                    "num_ports": 1,
                    "actor_key": 0,
                    "partner_key": 1,
                    "partner_mac": "00:00:00:00:00:00"
                },
                "ad_actor_sys_prio": 65535,
                "ad_user_port_key": 0,
                "ad_actor_system": "00:00:00:00:00:00"
            }
        },
        "inet6_addr_gen_mode": "eui64",
        "num_tx_queues": 16,
        "num_rx_queues": 16,
        "gso_max_size": 65536,
        "gso_max_segs": 65535
    }
]

Signed-off-by: Julien Fortin <julien@cumulusnetworks.com>
---
 ip/iplink_bond_slave.c | 57 ++++++++++++++++++++++++++++++++++----------------
 1 file changed, 39 insertions(+), 18 deletions(-)

diff --git a/ip/iplink_bond_slave.c b/ip/iplink_bond_slave.c
index 877e2d9e..67219c67 100644
--- a/ip/iplink_bond_slave.c
+++ b/ip/iplink_bond_slave.c
@@ -37,9 +37,12 @@ static void print_slave_state(FILE *f, struct rtattr *tb)
 	unsigned int state = rta_getattr_u8(tb);
 
 	if (state >= ARRAY_SIZE(slave_states))
-		fprintf(f, "state %d ", state);
+		print_int(PRINT_ANY, "state_index", "state %d ", state);
 	else
-		fprintf(f, "state %s ", slave_states[state]);
+		print_string(PRINT_ANY,
+			     "state",
+			     "state %s ",
+			     slave_states[state]);
 }
 
 static const char *slave_mii_status[] = {
@@ -54,9 +57,15 @@ static void print_slave_mii_status(FILE *f, struct rtattr *tb)
 	unsigned int status = rta_getattr_u8(tb);
 
 	if (status >= ARRAY_SIZE(slave_mii_status))
-		fprintf(f, "mii_status %d ", status);
+		print_int(PRINT_ANY,
+			  "mii_status_index",
+			  "mii_status %d ",
+			  status);
 	else
-		fprintf(f, "mii_status %s ", slave_mii_status[status]);
+		print_string(PRINT_ANY,
+			     "mii_status",
+			     "mii_status %s ",
+			     slave_mii_status[status]);
 }
 
 static void bond_slave_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
@@ -72,30 +81,42 @@ static void bond_slave_print_opt(struct link_util *lu, FILE *f, struct rtattr *t
 		print_slave_mii_status(f, tb[IFLA_BOND_SLAVE_MII_STATUS]);
 
 	if (tb[IFLA_BOND_SLAVE_LINK_FAILURE_COUNT])
-		fprintf(f, "link_failure_count %d ",
-			rta_getattr_u32(tb[IFLA_BOND_SLAVE_LINK_FAILURE_COUNT]));
+		print_int(PRINT_ANY,
+			  "link_failure_count",
+			  "link_failure_count %d ",
+			  rta_getattr_u32(tb[IFLA_BOND_SLAVE_LINK_FAILURE_COUNT]));
 
 	if (tb[IFLA_BOND_SLAVE_PERM_HWADDR])
-		fprintf(f, "perm_hwaddr %s ",
-			ll_addr_n2a(RTA_DATA(tb[IFLA_BOND_SLAVE_PERM_HWADDR]),
-				    RTA_PAYLOAD(tb[IFLA_BOND_SLAVE_PERM_HWADDR]),
-				    0, b1, sizeof(b1)));
+		print_string(PRINT_ANY,
+			     "perm_hwaddr",
+			     "perm_hwaddr %s ",
+			     ll_addr_n2a(RTA_DATA(tb[IFLA_BOND_SLAVE_PERM_HWADDR]),
+					 RTA_PAYLOAD(tb[IFLA_BOND_SLAVE_PERM_HWADDR]),
+					 0, b1, sizeof(b1)));
 
 	if (tb[IFLA_BOND_SLAVE_QUEUE_ID])
-		fprintf(f, "queue_id %d ",
-			rta_getattr_u16(tb[IFLA_BOND_SLAVE_QUEUE_ID]));
+		print_int(PRINT_ANY,
+			  "queue_id",
+			  "queue_id %d ",
+			  rta_getattr_u16(tb[IFLA_BOND_SLAVE_QUEUE_ID]));
 
 	if (tb[IFLA_BOND_SLAVE_AD_AGGREGATOR_ID])
-		fprintf(f, "ad_aggregator_id %d ",
-			rta_getattr_u16(tb[IFLA_BOND_SLAVE_AD_AGGREGATOR_ID]));
+		print_int(PRINT_ANY,
+			  "ad_aggregator_id",
+			  "ad_aggregator_id %d ",
+			  rta_getattr_u16(tb[IFLA_BOND_SLAVE_AD_AGGREGATOR_ID]));
 
 	if (tb[IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE])
-		fprintf(f, "ad_actor_oper_port_state %d ",
-			rta_getattr_u8(tb[IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE]));
+		print_int(PRINT_ANY,
+			  "ad_actor_oper_port_state",
+			  "ad_actor_oper_port_state %d ",
+			  rta_getattr_u8(tb[IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE]));
 
 	if (tb[IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE])
-		fprintf(f, "ad_partner_oper_port_state %d ",
-			rta_getattr_u16(tb[IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE]));
+		print_int(PRINT_ANY,
+			  "ad_partner_oper_port_state",
+			  "ad_partner_oper_port_state %d ",
+			  rta_getattr_u16(tb[IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE]));
 }
 
 static int bond_slave_parse_opt(struct link_util *lu, int argc, char **argv,
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH iproute2 json v2 09/27] ip: iplink_hsr.c: add json output support
  2017-08-17 17:35 [PATCH iproute2 json v2 00/27] ip: add -json support to 'ip link show' Julien Fortin
                   ` (7 preceding siblings ...)
  2017-08-17 17:35 ` [PATCH iproute2 json v2 08/27] ip: iplink_bond_slave.c: add json output support (info_slave_data) Julien Fortin
@ 2017-08-17 17:35 ` Julien Fortin
  2017-08-17 17:35 ` [PATCH iproute2 json v2 10/27] ip: iplink_bridge.c: " Julien Fortin
                   ` (19 subsequent siblings)
  28 siblings, 0 replies; 33+ messages in thread
From: Julien Fortin @ 2017-08-17 17:35 UTC (permalink / raw)
  To: netdev; +Cc: roopa, nikolay, dsa, Julien Fortin

From: Julien Fortin <julien@cumulusnetworks.com>

Schema:
hsr: IFLA_INFO_DATA
{
    "slave1": {
        "type": "string",
        "attr": "IFLA_HSR_SLAVE1"
    },
    "slave2": {
        "type": "string",
        "attr": "IFLA_HSR_SLAVE2"
    },
    "seq_nr": {
        "type": "int",
        "attr": "IFLA_HSR_SEQ_NR"
    },
    "supervision_addr": {
        "type": "int",
        "attr": "IFLA_HSR_SUPERVISION_ADDR"
    }
}

Signed-off-by: Julien Fortin <julien@cumulusnetworks.com>
---
 ip/iplink_hsr.c | 36 +++++++++++++++++++++---------------
 1 file changed, 21 insertions(+), 15 deletions(-)

diff --git a/ip/iplink_hsr.c b/ip/iplink_hsr.c
index 696b2c91..c673ccf7 100644
--- a/ip/iplink_hsr.c
+++ b/ip/iplink_hsr.c
@@ -110,30 +110,36 @@ static void hsr_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 	    RTA_PAYLOAD(tb[IFLA_HSR_SUPERVISION_ADDR]) < ETH_ALEN)
 		return;
 
-	fprintf(f, "slave1 ");
 	if (tb[IFLA_HSR_SLAVE1])
-		fprintf(f, "%s ",
-			ll_index_to_name(rta_getattr_u32(tb[IFLA_HSR_SLAVE1])));
+		print_string(PRINT_ANY,
+			     "slave1",
+			     "slave1 %s ",
+			     ll_index_to_name(rta_getattr_u32(tb[IFLA_HSR_SLAVE1])));
 	else
-		fprintf(f, "<none> ");
+		print_null(PRINT_ANY, "slave1", "slave1 %s ", "<none>");
 
-	fprintf(f, "slave2 ");
 	if (tb[IFLA_HSR_SLAVE2])
-		fprintf(f, "%s ",
-			ll_index_to_name(rta_getattr_u32(tb[IFLA_HSR_SLAVE2])));
+		print_string(PRINT_ANY,
+			     "slave2",
+			     "slave2 %s ",
+			     ll_index_to_name(rta_getattr_u32(tb[IFLA_HSR_SLAVE2])));
 	else
-		fprintf(f, "<none> ");
+		print_null(PRINT_ANY, "slave2", "slave2 %s ", "<none>");
 
 	if (tb[IFLA_HSR_SEQ_NR])
-		fprintf(f, "sequence %d ",
-			rta_getattr_u16(tb[IFLA_HSR_SEQ_NR]));
+		print_int(PRINT_ANY,
+			  "seq_nr",
+			  "sequence %d ",
+			  rta_getattr_u16(tb[IFLA_HSR_SEQ_NR]));
 
 	if (tb[IFLA_HSR_SUPERVISION_ADDR])
-		fprintf(f, "supervision %s ",
-			ll_addr_n2a(RTA_DATA(tb[IFLA_HSR_SUPERVISION_ADDR]),
-				    RTA_PAYLOAD(tb[IFLA_HSR_SUPERVISION_ADDR]),
-				    ARPHRD_VOID,
-				    b1, sizeof(b1)));
+		print_string(PRINT_ANY,
+			     "supervision_addr",
+			     "supervision %s ",
+			     ll_addr_n2a(RTA_DATA(tb[IFLA_HSR_SUPERVISION_ADDR]),
+					 RTA_PAYLOAD(tb[IFLA_HSR_SUPERVISION_ADDR]),
+					 ARPHRD_VOID,
+					 b1, sizeof(b1)));
 }
 
 static void hsr_print_help(struct link_util *lu, int argc, char **argv,
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH iproute2 json v2 10/27] ip: iplink_bridge.c: add json output support
  2017-08-17 17:35 [PATCH iproute2 json v2 00/27] ip: add -json support to 'ip link show' Julien Fortin
                   ` (8 preceding siblings ...)
  2017-08-17 17:35 ` [PATCH iproute2 json v2 09/27] ip: iplink_hsr.c: add json output support Julien Fortin
@ 2017-08-17 17:35 ` Julien Fortin
  2017-08-17 17:35 ` [PATCH iproute2 json v2 11/27] ip: iplink_bridge_slave.c: " Julien Fortin
                   ` (18 subsequent siblings)
  28 siblings, 0 replies; 33+ messages in thread
From: Julien Fortin @ 2017-08-17 17:35 UTC (permalink / raw)
  To: netdev; +Cc: roopa, nikolay, dsa, Julien Fortin

From: Julien Fortin <julien@cumulusnetworks.com>

Schema and live example:
bridge: IFLA_INFO_DATA
{
    "forward_delay": {
        "type": "uint",
        "attr": "IFLA_BR_FORWARD_DELAY"
    },
    "hello_time": {
        "type": "uint",
        "attr": "IFLA_BR_HELLO_TIME"
    },
    "max_age": {
        "type": "uint",
        "attr": "IFLA_BR_MAX_AGE"
    },
    "ageing_time": {
        "type": "uint",
        "attr": "IFLA_BR_AGEING_TIME"
    },
    "stp_state": {
        "type": "uint",
        "attr": "IFLA_BR_STP_STATE"
    },
    "priority": {
        "type": "uint",
        "attr": "IFLA_BR_PRIORITY"
    },
    "vlan_filtering": {
        "type": "uint",
        "attr": "IFLA_BR_VLAN_FILTERING"
    },
    "vlan_protocol": {
        "type": "string",
        "attr": "IFLA_BR_VLAN_PROTOCOL"
    },
    "bridge_id": {
        "type": "string",
        "attr": "IFLA_BR_BRIDGE_ID"
    },
    "root_id": {
        "type": "string",
        "attr": "IFLA_BR_ROOT_ID"
    },
    "root_port": {
        "type": "uint",
        "attr": "IFLA_BR_ROOT_PORT"
    },
    "root_path_cost": {
        "type": "uint",
        "attr": "IFLA_BR_ROOT_PATH_COST"
    },
    "topology_change": {
        "type": "uint",
        "attr": "IFLA_BR_TOPOLOGY_CHANGE"
    },
    "topology_change_detected": {
        "type": "uint",
        "attr": "IFLA_BR_TOPOLOGY_CHANGE_DETECTED"
    },
    "hello_timer": {
        "type": "float",
        "attr": "IFLA_BR_HELLO_TIMER"
    },
    "tcn_timer": {
        "type": "float",
        "attr": "IFLA_BR_TCN_TIMER"
    },
    "topology_change_timer": {
        "type": "float",
        "attr": "IFLA_BR_TOPOLOGY_CHANGE_TIMER"
    },
    "gc_timer": {
        "type": "float",
        "attr": "IFLA_BR_GC_TIMER"
    },
    "vlan_default_pvid": {
        "type": "uint",
        "attr": "IFLA_BR_VLAN_DEFAULT_PVID"
    },
    "vlan_stats_enabled": {
        "type": "uint",
        "attr": "IFLA_BR_VLAN_STATS_ENABLED"
    },
    "group_fwd_mask": {
        "type": "string",
        "attr": "IFLA_BR_GROUP_FWD_MASK"
    },
    "group_addr": {
        "type": "string",
        "attr": "IFLA_BR_GROUP_ADDR"
    },
    "mcast_snooping": {
        "type": "uint",
        "attr": "IFLA_BR_MCAST_SNOOPING"
    },
    "mcast_router": {
        "type": "uint",
        "attr": "IFLA_BR_MCAST_ROUTER"
    },
    "mcast_query_use_ifaddr": {
        "type": "uint",
        "attr": "IFLA_BR_MCAST_QUERY_USE_IFADDR"
    },
    "mcast_querier": {
        "type": "uint",
        "attr": "IFLA_BR_MCAST_QUERIER"
    },
    "mcast_hash_elasticity": {
        "type": "uint",
        "attr": "IFLA_BR_MCAST_HASH_ELASTICITY"
    },
    "mcast_hash_max": {
        "type": "uint",
        "attr": "IFLA_BR_MCAST_HASH_MAX"
    },
    "mcast_last_member_cnt": {
        "type": "uint",
        "attr": "IFLA_BR_MCAST_LAST_MEMBER_CNT"
    },
    "mcast_startup_query_cnt": {
        "type": "uint",
        "attr": "IFLA_BR_MCAST_STARTUP_QUERY_CNT"
    },
    "mcast_last_member_intvl": {
        "type": "lluint",
        "attr": "IFLA_BR_MCAST_LAST_MEMBER_INTVL"
    },
    "mcast_membership_intvl": {
        "type": "lluint",
        "attr": "IFLA_BR_MCAST_MEMBERSHIP_INTVL"
    },
    "mcast_querier_intvl": {
        "type": "lluint",
        "attr": "IFLA_BR_MCAST_QUERIER_INTVL"
    },
    "mcast_query_intvl": {
        "type": "lluint",
        "attr": "IFLA_BR_MCAST_QUERY_INTVL"
    },
    "mcast_query_response_intvl": {
        "type": "lluint",
        "attr": "IFLA_BR_MCAST_QUERY_RESPONSE_INTVL"
    },
    "mcast_startup_query_intvl": {
        "type": "lluint",
        "attr": "IFLA_BR_MCAST_STARTUP_QUERY_INTVL"
    },
    "mcast_stats_enabled": {
        "type": "uint",
        "attr": "IFLA_BR_MCAST_STATS_ENABLED"
    },
    "mcast_igmp_version": {
        "type": "uint",
        "attr": "IFLA_BR_MCAST_IGMP_VERSION"
    },
    "mcast_mld_version": {
        "type": "uint",
        "attr": "IFLA_BR_MCAST_MLD_VERSION"
    },
    "nf_call_iptables": {
        "type": "uint",
        "attr": "IFLA_BR_NF_CALL_IPTABLES"
    },
    "nf_call_ip6tables": {
        "type": "uint",
        "attr": "IFLA_BR_NF_CALL_IP6TABLES"
    },
    "nf_call_arptables": {
        "type": "uint",
        "attr": "IFLA_BR_NF_CALL_ARPTABLES"
    }
}

$ ip link add dev br42 type bridge
$ ip link add dev bond42 type bond
$ ip link set dev bond42 master br42
$ ip link set dev bond42 up
$ ip link set dev br42 up

$ ip -details link show
15: br42: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state
UP mode DEFAULT group default
    link/ether 22:8f:91:bb:9f:09 brd ff:ff:ff:ff:ff:ff promiscuity 0
    bridge forward_delay 1500 hello_time 200 max_age 2000 ageing_time
30000 stp_state 0 priority 32768 vlan_filtering 0 vlan_protocol 802.1Q
bridge_id 8000.22:8f:91:bb:9f:9 designated_root 8000.22:8f:91:bb:9f:9
root_port 0 root_path_cost 0 topology_change 0 topology_change_detected 0
hello_timer    0.00 tcn_timer    0.00 topology_change_timer    0.00
gc_timer  199.11 vlan_default_pvid 1 vlan_stats_enabled 0 group_fwd_mask 0
group_address 01:80:c2:00:00:00 mcast_snooping 1 mcast_router 1
mcast_query_use_ifaddr 0 mcast_querier 0 mcast_hash_elasticity 4096
mcast_hash_max 4096 mcast_last_member_count 2 mcast_startup_query_count 2
mcast_last_member_interval 100 mcast_membership_interval 26000
mcast_querier_interval 25500 mcast_query_interval 12500
mcast_query_response_interval 1000 mcast_startup_query_interval 3125
mcast_stats_enabled 0 mcast_igmp_version 2 mcast_mld_version 1
nf_call_iptables 0 nf_call_ip6tables 0 nf_call_arptables 0 addrgenmode
eui64
16: bond42: <BROADCAST,MULTICAST,MASTER,UP,LOWER_UP> mtu 1500 qdisc
noqueue master br42 state UNKNOWN mode DEFAULT group default
    link/ether 22:8f:91:bb:9f:09 brd ff:ff:ff:ff:ff:ff promiscuity 1
    bond mode 802.3ad miimon 100 updelay 0 downdelay 0 use_carrier 1
arp_interval 0 arp_validate none arp_all_targets any primary_reselect
always fail_over_mac none xmit_hash_policy layer3+4 resend_igmp 1
num_grat_arp 1 all_slaves_active 0 min_links 1 lp_interval 1
packets_per_slave 1 lacp_rate fast ad_select stable ad_actor_sys_prio
65535 ad_user_port_key 0 ad_actor_system 00:00:00:00:00:00
    bridge_slave state forwarding priority 8 cost 100 hairpin off guard
off root_block off fastleave off learning on flood on port_id 0x8001
port_no 0x1 designated_port 32769 designated_cost 0 designated_bridge
8000.22:8f:91:bb:9f:9 designated_root 8000.22:8f:91:bb:9f:9 hold_timer
0.00 message_age_timer    0.00 forward_delay_timer    0.00
topology_change_ack 0 config_pending 0 proxy_arp off proxy_arp_wifi off
mcast_router 1 mcast_fast_leave off mcast_flood on neigh_suppress off
addrgenmode eui64

$ ip -details -json link show
[{
        "ifindex": 15,
        "ifname": "br42",
        "flags": ["BROADCAST","MULTICAST","UP","LOWER_UP"],
        "mtu": 1500,
        "qdisc": "noqueue",
        "operstate": "UP",
        "linkmode": "DEFAULT",
        "group": "default",
        "link_type": "ether",
        "address": "22:8f:91:bb:9f:09",
        "broadcast": "ff:ff:ff:ff:ff:ff",
        "promiscuity": 0,
        "linkinfo": {
            "info_kind": "bridge",
            "info_data": {
                "forward_delay": 1500,
                "hello_time": 200,
                "max_age": 2000,
                "ageing_time": 30000,
                "stp_state": 0,
                "priority": 32768,
                "vlan_filtering": 0,
                "vlan_protocol": "802.1Q",
                "bridge_id": "8000.22:8f:91:bb:9f:9",
                "root_id": "8000.22:8f:91:bb:9f:9",
                "root_port": 0,
                "root_path_cost": 0,
                "topology_change": 0,
                "topology_change_detected": 0,
                "hello_timer": 0.00,
                "tcn_timer": 0.00,
                "topology_change_timer": 0.00,
                "gc_timer": 298.27,
                "vlan_default_pvid": 1,
                "vlan_stats_enabled": 0,
                "group_fwd_mask": "0",
                "group_addr": "01:80:c2:00:00:00",
                "mcast_snooping": 1,
                "mcast_router": 1,
                "mcast_query_use_ifaddr": 0,
                "mcast_querier": 0,
                "mcast_hash_elasticity": 4096,
                "mcast_hash_max": 4096,
                "mcast_last_member_cnt": 2,
                "mcast_startup_query_cnt": 2,
                "mcast_last_member_intvl": 100,
                "mcast_membership_intvl": 26000,
                "mcast_querier_intvl": 25500,
                "mcast_query_intvl": 12500,
                "mcast_query_response_intvl": 1000,
                "mcast_startup_query_intvl": 3125,
                "mcast_stats_enabled": 0,
                "mcast_igmp_version": 2,
                "mcast_mld_version": 1,
                "nf_call_iptables": 0,
                "nf_call_ip6tables": 0,
                "nf_call_arptables": 0
            }
        },
        "inet6_addr_gen_mode": "eui64",
        "num_tx_queues": 1,
        "num_rx_queues": 1,
        "gso_max_size": 65536,
        "gso_max_segs": 65535
    },{
        "ifindex": 16,
        "ifname": "bond42",
        "flags": ["BROADCAST","MULTICAST","MASTER","UP","LOWER_UP"],
        "mtu": 1500,
        "qdisc": "noqueue",
        "master": "br42",
        "operstate": "UNKNOWN",
        "linkmode": "DEFAULT",
        "group": "default",
        "link_type": "ether",
        "address": "22:8f:91:bb:9f:09",
        "broadcast": "ff:ff:ff:ff:ff:ff",
        "promiscuity": 1,
        "linkinfo": {
            "info_kind": "bond",
            "info_data": {
                "mode": "802.3ad",
                "miimon": 100,
                "updelay": 0,
                "downdelay": 0,
                "use_carrier": 1,
                "arp_interval": 0,
                "arp_validate": null,
                "arp_all_targets": "any",
                "primary_reselect": "always",
                "fail_over_mac": "none",
                "xmit_hash_policy": "layer3+4",
                "resend_igmp": 1,
                "num_peer_notif": 1,
                "all_slaves_active": 0,
                "min_links": 1,
                "lp_interval": 1,
                "packets_per_slave": 1,
                "ad_lacp_rate": "fast",
                "ad_select": "stable",
                "ad_actor_sys_prio": 65535,
                "ad_user_port_key": 0,
                "ad_actor_system": "00:00:00:00:00:00"
            },
            "info_slave_kind": "bridge",
            "info_slave_data": {
                "state": "forwarding",
                "priority": 8,
                "cost": 100,
                "hairpin": false,
                "guard": false,
                "root_block": false,
                "fastleave": false,
                "learning": true,
                "flood": true,
                "id": "0x8001",
                "no": "0x1",
                "designated_port": 32769,
                "designated_cost": 0,
                "bridge_id": "8000.22:8f:91:bb:9f:9",
                "root_id": "8000.22:8f:91:bb:9f:9",
                "hold_timer": 0.00,
                "message_age_timer": 0.00,
                "forward_delay_timer": 11.97,
                "topology_change_ack": 0,
                "config_pending": 0,
                "proxy_arp": false,
                "proxy_arp_wifi": false,
                "multicast_router": 1,
                "mcast_flood": true
            }
        },
        "inet6_addr_gen_mode": "eui64",
        "num_tx_queues": 16,
        "num_rx_queues": 16,
        "gso_max_size": 65536,
        "gso_max_segs": 65535
    }
]

Signed-off-by: Julien Fortin <julien@cumulusnetworks.com>
---
 ip/iplink_bridge.c | 293 +++++++++++++++++++++++++++++++++--------------------
 1 file changed, 185 insertions(+), 108 deletions(-)

diff --git a/ip/iplink_bridge.c b/ip/iplink_bridge.c
index cccdec1c..d3250980 100644
--- a/ip/iplink_bridge.c
+++ b/ip/iplink_bridge.c
@@ -373,45 +373,81 @@ static int bridge_parse_opt(struct link_util *lu, int argc, char **argv,
 	return 0;
 }
 
+static void _bridge_print_timer(FILE *f,
+				const char *attr,
+				struct rtattr *timer)
+{
+	struct timeval tv;
+
+	__jiffies_to_tv(&tv, rta_getattr_u64(timer));
+	if (is_json_context()) {
+		json_writer_t *jw = get_json_writer();
+
+		jsonw_name(jw, attr);
+		jsonw_printf(jw, "%i.%.2i",
+			     (int)tv.tv_sec,
+			     (int)tv.tv_usec / 10000);
+	} else {
+		fprintf(f, "%s %4i.%.2i ", attr, (int)tv.tv_sec,
+			(int)tv.tv_usec / 10000);
+	}
+}
+
 static void bridge_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 {
 	if (!tb)
 		return;
 
 	if (tb[IFLA_BR_FORWARD_DELAY])
-		fprintf(f, "forward_delay %u ",
-			rta_getattr_u32(tb[IFLA_BR_FORWARD_DELAY]));
+		print_uint(PRINT_ANY,
+			   "forward_delay",
+			   "forward_delay %u ",
+			   rta_getattr_u32(tb[IFLA_BR_FORWARD_DELAY]));
 
 	if (tb[IFLA_BR_HELLO_TIME])
-		fprintf(f, "hello_time %u ",
-			rta_getattr_u32(tb[IFLA_BR_HELLO_TIME]));
+		print_uint(PRINT_ANY,
+			   "hello_time",
+			   "hello_time %u ",
+			   rta_getattr_u32(tb[IFLA_BR_HELLO_TIME]));
 
 	if (tb[IFLA_BR_MAX_AGE])
-		fprintf(f, "max_age %u ",
-			rta_getattr_u32(tb[IFLA_BR_MAX_AGE]));
+		print_uint(PRINT_ANY,
+			   "max_age",
+			   "max_age %u ",
+			   rta_getattr_u32(tb[IFLA_BR_MAX_AGE]));
 
 	if (tb[IFLA_BR_AGEING_TIME])
-		fprintf(f, "ageing_time %u ",
-			rta_getattr_u32(tb[IFLA_BR_AGEING_TIME]));
+		print_uint(PRINT_ANY,
+			   "ageing_time",
+			   "ageing_time %u ",
+			   rta_getattr_u32(tb[IFLA_BR_AGEING_TIME]));
 
 	if (tb[IFLA_BR_STP_STATE])
-		fprintf(f, "stp_state %u ",
-			rta_getattr_u32(tb[IFLA_BR_STP_STATE]));
+		print_uint(PRINT_ANY,
+			   "stp_state",
+			   "stp_state %u ",
+			   rta_getattr_u32(tb[IFLA_BR_STP_STATE]));
 
 	if (tb[IFLA_BR_PRIORITY])
-		fprintf(f, "priority %u ",
-			rta_getattr_u16(tb[IFLA_BR_PRIORITY]));
+		print_uint(PRINT_ANY,
+			   "priority",
+			   "priority %u ",
+			   rta_getattr_u16(tb[IFLA_BR_PRIORITY]));
 
 	if (tb[IFLA_BR_VLAN_FILTERING])
-		fprintf(f, "vlan_filtering %u ",
-			rta_getattr_u8(tb[IFLA_BR_VLAN_FILTERING]));
+		print_uint(PRINT_ANY,
+			   "vlan_filtering",
+			   "vlan_filtering %u ",
+			   rta_getattr_u8(tb[IFLA_BR_VLAN_FILTERING]));
 
 	if (tb[IFLA_BR_VLAN_PROTOCOL]) {
 		SPRINT_BUF(b1);
 
-		fprintf(f, "vlan_protocol %s ",
-			ll_proto_n2a(rta_getattr_u16(tb[IFLA_BR_VLAN_PROTOCOL]),
-				     b1, sizeof(b1)));
+		print_string(PRINT_ANY,
+			     "vlan_protocol",
+			     "vlan_protocol %s ",
+			     ll_proto_n2a(rta_getattr_u16(tb[IFLA_BR_VLAN_PROTOCOL]),
+					  b1, sizeof(b1)));
 	}
 
 	if (tb[IFLA_BR_BRIDGE_ID]) {
@@ -419,7 +455,10 @@ static void bridge_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 
 		br_dump_bridge_id(RTA_DATA(tb[IFLA_BR_BRIDGE_ID]), bridge_id,
 				  sizeof(bridge_id));
-		fprintf(f, "bridge_id %s ", bridge_id);
+		print_string(PRINT_ANY,
+			     "bridge_id",
+			     "bridge_id %s ",
+			     bridge_id);
 	}
 
 	if (tb[IFLA_BR_ROOT_ID]) {
@@ -427,163 +466,201 @@ static void bridge_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 
 		br_dump_bridge_id(RTA_DATA(tb[IFLA_BR_BRIDGE_ID]), root_id,
 				  sizeof(root_id));
-		fprintf(f, "designated_root %s ", root_id);
+		print_string(PRINT_ANY,
+			     "root_id",
+			     "designated_root %s ",
+			     root_id);
 	}
 
 	if (tb[IFLA_BR_ROOT_PORT])
-		fprintf(f, "root_port %u ",
-			rta_getattr_u16(tb[IFLA_BR_ROOT_PORT]));
+		print_uint(PRINT_ANY,
+			   "root_port",
+			   "root_port %u ",
+			   rta_getattr_u16(tb[IFLA_BR_ROOT_PORT]));
 
 	if (tb[IFLA_BR_ROOT_PATH_COST])
-		fprintf(f, "root_path_cost %u ",
-			rta_getattr_u32(tb[IFLA_BR_ROOT_PATH_COST]));
+		print_uint(PRINT_ANY,
+			   "root_path_cost",
+			   "root_path_cost %u ",
+			   rta_getattr_u32(tb[IFLA_BR_ROOT_PATH_COST]));
 
 	if (tb[IFLA_BR_TOPOLOGY_CHANGE])
-		fprintf(f, "topology_change %u ",
-			rta_getattr_u8(tb[IFLA_BR_TOPOLOGY_CHANGE]));
+		print_uint(PRINT_ANY,
+			   "topology_change",
+			   "topology_change %u ",
+			   rta_getattr_u8(tb[IFLA_BR_TOPOLOGY_CHANGE]));
 
 	if (tb[IFLA_BR_TOPOLOGY_CHANGE_DETECTED])
-		fprintf(f, "topology_change_detected %u ",
-			rta_getattr_u8(tb[IFLA_BR_TOPOLOGY_CHANGE_DETECTED]));
-
-	if (tb[IFLA_BR_HELLO_TIMER]) {
-		struct timeval tv;
-
-		__jiffies_to_tv(&tv, rta_getattr_u64(tb[IFLA_BR_HELLO_TIMER]));
-		fprintf(f, "hello_timer %4i.%.2i ", (int)tv.tv_sec,
-			(int)tv.tv_usec/10000);
-	}
-
-	if (tb[IFLA_BR_TCN_TIMER]) {
-		struct timeval tv;
-
-		__jiffies_to_tv(&tv, rta_getattr_u64(tb[IFLA_BR_TCN_TIMER]));
-		fprintf(f, "tcn_timer %4i.%.2i ", (int)tv.tv_sec,
-			(int)tv.tv_usec/10000);
-	}
+		print_uint(PRINT_ANY,
+			   "topology_change_detected",
+			   "topology_change_detected %u ",
+			   rta_getattr_u8(tb[IFLA_BR_TOPOLOGY_CHANGE_DETECTED]));
 
-	if (tb[IFLA_BR_TOPOLOGY_CHANGE_TIMER]) {
-		unsigned long jiffies;
-		struct timeval tv;
+	if (tb[IFLA_BR_HELLO_TIMER])
+		_bridge_print_timer(f, "hello_timer", tb[IFLA_BR_HELLO_TIMER]);
 
-		jiffies = rta_getattr_u64(tb[IFLA_BR_TOPOLOGY_CHANGE_TIMER]);
-		__jiffies_to_tv(&tv, jiffies);
-		fprintf(f, "topology_change_timer %4i.%.2i ", (int)tv.tv_sec,
-			(int)tv.tv_usec/10000);
-	}
+	if (tb[IFLA_BR_TCN_TIMER])
+		_bridge_print_timer(f, "tcn_timer", tb[IFLA_BR_TCN_TIMER]);
 
-	if (tb[IFLA_BR_GC_TIMER]) {
-		struct timeval tv;
+	if (tb[IFLA_BR_TOPOLOGY_CHANGE_TIMER])
+		_bridge_print_timer(f, "topology_change_timer",
+				    tb[IFLA_BR_TOPOLOGY_CHANGE_TIMER]);
 
-		__jiffies_to_tv(&tv, rta_getattr_u64(tb[IFLA_BR_GC_TIMER]));
-		fprintf(f, "gc_timer %4i.%.2i ", (int)tv.tv_sec,
-			(int)tv.tv_usec/10000);
-	}
+	if (tb[IFLA_BR_GC_TIMER])
+		_bridge_print_timer(f, "gc_timer", tb[IFLA_BR_GC_TIMER]);
 
 	if (tb[IFLA_BR_VLAN_DEFAULT_PVID])
-		fprintf(f, "vlan_default_pvid %u ",
-			rta_getattr_u16(tb[IFLA_BR_VLAN_DEFAULT_PVID]));
+		print_uint(PRINT_ANY,
+			   "vlan_default_pvid",
+			   "vlan_default_pvid %u ",
+			   rta_getattr_u16(tb[IFLA_BR_VLAN_DEFAULT_PVID]));
 
 	if (tb[IFLA_BR_VLAN_STATS_ENABLED])
-		fprintf(f, "vlan_stats_enabled %u ",
-			rta_getattr_u8(tb[IFLA_BR_VLAN_STATS_ENABLED]));
+		print_uint(PRINT_ANY,
+			   "vlan_stats_enabled",
+			   "vlan_stats_enabled %u ",
+			   rta_getattr_u8(tb[IFLA_BR_VLAN_STATS_ENABLED]));
 
 	if (tb[IFLA_BR_GROUP_FWD_MASK])
-		fprintf(f, "group_fwd_mask %#x ",
-			rta_getattr_u16(tb[IFLA_BR_GROUP_FWD_MASK]));
+		print_0xhex(PRINT_ANY,
+			    "group_fwd_mask",
+			    "group_fwd_mask %#x ",
+			    rta_getattr_u16(tb[IFLA_BR_GROUP_FWD_MASK]));
 
 	if (tb[IFLA_BR_GROUP_ADDR]) {
 		SPRINT_BUF(mac);
 
-		fprintf(f, "group_address %s ",
-			ll_addr_n2a(RTA_DATA(tb[IFLA_BR_GROUP_ADDR]),
-				    RTA_PAYLOAD(tb[IFLA_BR_GROUP_ADDR]),
-				    1 /*ARPHDR_ETHER*/, mac, sizeof(mac)));
+		print_string(PRINT_ANY,
+			     "group_addr",
+			     "group_address %s ",
+			     ll_addr_n2a(RTA_DATA(tb[IFLA_BR_GROUP_ADDR]),
+					 RTA_PAYLOAD(tb[IFLA_BR_GROUP_ADDR]),
+					 1 /*ARPHDR_ETHER*/, mac, sizeof(mac)));
 	}
 
 	if (tb[IFLA_BR_MCAST_SNOOPING])
-		fprintf(f, "mcast_snooping %u ",
-			rta_getattr_u8(tb[IFLA_BR_MCAST_SNOOPING]));
+		print_uint(PRINT_ANY,
+			   "mcast_snooping",
+			   "mcast_snooping %u ",
+			   rta_getattr_u8(tb[IFLA_BR_MCAST_SNOOPING]));
 
 	if (tb[IFLA_BR_MCAST_ROUTER])
-		fprintf(f, "mcast_router %u ",
-			rta_getattr_u8(tb[IFLA_BR_MCAST_ROUTER]));
+		print_uint(PRINT_ANY,
+			   "mcast_router",
+			   "mcast_router %u ",
+			   rta_getattr_u8(tb[IFLA_BR_MCAST_ROUTER]));
 
 	if (tb[IFLA_BR_MCAST_QUERY_USE_IFADDR])
-		fprintf(f, "mcast_query_use_ifaddr %u ",
-			rta_getattr_u8(tb[IFLA_BR_MCAST_QUERY_USE_IFADDR]));
+		print_uint(PRINT_ANY,
+			   "mcast_query_use_ifaddr",
+			   "mcast_query_use_ifaddr %u ",
+			   rta_getattr_u8(tb[IFLA_BR_MCAST_QUERY_USE_IFADDR]));
 
 	if (tb[IFLA_BR_MCAST_QUERIER])
-		fprintf(f, "mcast_querier %u ",
-			rta_getattr_u8(tb[IFLA_BR_MCAST_QUERIER]));
+		print_uint(PRINT_ANY,
+			   "mcast_querier",
+			   "mcast_querier %u ",
+			   rta_getattr_u8(tb[IFLA_BR_MCAST_QUERIER]));
 
 	if (tb[IFLA_BR_MCAST_HASH_ELASTICITY])
-		fprintf(f, "mcast_hash_elasticity %u ",
-			rta_getattr_u32(tb[IFLA_BR_MCAST_HASH_ELASTICITY]));
+		print_uint(PRINT_ANY,
+			   "mcast_hash_elasticity",
+			   "mcast_hash_elasticity %u ",
+			   rta_getattr_u32(tb[IFLA_BR_MCAST_HASH_ELASTICITY]));
 
 	if (tb[IFLA_BR_MCAST_HASH_MAX])
-		fprintf(f, "mcast_hash_max %u ",
-			rta_getattr_u32(tb[IFLA_BR_MCAST_HASH_MAX]));
+		print_uint(PRINT_ANY,
+			   "mcast_hash_max",
+			   "mcast_hash_max %u ",
+			   rta_getattr_u32(tb[IFLA_BR_MCAST_HASH_MAX]));
 
 	if (tb[IFLA_BR_MCAST_LAST_MEMBER_CNT])
-		fprintf(f, "mcast_last_member_count %u ",
-			rta_getattr_u32(tb[IFLA_BR_MCAST_LAST_MEMBER_CNT]));
+		print_uint(PRINT_ANY,
+			   "mcast_last_member_cnt",
+			   "mcast_last_member_count %u ",
+			   rta_getattr_u32(tb[IFLA_BR_MCAST_LAST_MEMBER_CNT]));
 
 	if (tb[IFLA_BR_MCAST_STARTUP_QUERY_CNT])
-		fprintf(f, "mcast_startup_query_count %u ",
-			rta_getattr_u32(tb[IFLA_BR_MCAST_STARTUP_QUERY_CNT]));
+		print_uint(PRINT_ANY,
+			   "mcast_startup_query_cnt",
+			   "mcast_startup_query_count %u ",
+			   rta_getattr_u32(tb[IFLA_BR_MCAST_STARTUP_QUERY_CNT]));
 
 	if (tb[IFLA_BR_MCAST_LAST_MEMBER_INTVL])
-		fprintf(f, "mcast_last_member_interval %llu ",
-			rta_getattr_u64(tb[IFLA_BR_MCAST_LAST_MEMBER_INTVL]));
+		print_lluint(PRINT_ANY,
+			     "mcast_last_member_intvl",
+			     "mcast_last_member_interval %llu ",
+			     rta_getattr_u64(tb[IFLA_BR_MCAST_LAST_MEMBER_INTVL]));
 
 	if (tb[IFLA_BR_MCAST_MEMBERSHIP_INTVL])
-		fprintf(f, "mcast_membership_interval %llu ",
-			rta_getattr_u64(tb[IFLA_BR_MCAST_MEMBERSHIP_INTVL]));
+		print_lluint(PRINT_ANY,
+			     "mcast_membership_intvl",
+			     "mcast_membership_interval %llu ",
+			     rta_getattr_u64(tb[IFLA_BR_MCAST_MEMBERSHIP_INTVL]));
 
 	if (tb[IFLA_BR_MCAST_QUERIER_INTVL])
-		fprintf(f, "mcast_querier_interval %llu ",
-			rta_getattr_u64(tb[IFLA_BR_MCAST_QUERIER_INTVL]));
+		print_lluint(PRINT_ANY,
+			     "mcast_querier_intvl",
+			     "mcast_querier_interval %llu ",
+			     rta_getattr_u64(tb[IFLA_BR_MCAST_QUERIER_INTVL]));
 
 	if (tb[IFLA_BR_MCAST_QUERY_INTVL])
-		fprintf(f, "mcast_query_interval %llu ",
-			rta_getattr_u64(tb[IFLA_BR_MCAST_QUERY_INTVL]));
+		print_lluint(PRINT_ANY,
+			     "mcast_query_intvl",
+			     "mcast_query_interval %llu ",
+			     rta_getattr_u64(tb[IFLA_BR_MCAST_QUERY_INTVL]));
 
 	if (tb[IFLA_BR_MCAST_QUERY_RESPONSE_INTVL])
-		fprintf(f, "mcast_query_response_interval %llu ",
-			rta_getattr_u64(tb[IFLA_BR_MCAST_QUERY_RESPONSE_INTVL]));
+		print_lluint(PRINT_ANY,
+			     "mcast_query_response_intvl",
+			     "mcast_query_response_interval %llu ",
+			     rta_getattr_u64(tb[IFLA_BR_MCAST_QUERY_RESPONSE_INTVL]));
 
 	if (tb[IFLA_BR_MCAST_STARTUP_QUERY_INTVL])
-		fprintf(f, "mcast_startup_query_interval %llu ",
-			rta_getattr_u64(tb[IFLA_BR_MCAST_STARTUP_QUERY_INTVL]));
+		print_lluint(PRINT_ANY,
+			     "mcast_startup_query_intvl",
+			     "mcast_startup_query_interval %llu ",
+			     rta_getattr_u64(tb[IFLA_BR_MCAST_STARTUP_QUERY_INTVL]));
 
 	if (tb[IFLA_BR_MCAST_STATS_ENABLED])
-		fprintf(f, "mcast_stats_enabled %u ",
-			rta_getattr_u8(tb[IFLA_BR_MCAST_STATS_ENABLED]));
+		print_uint(PRINT_ANY,
+			   "mcast_stats_enabled",
+			   "mcast_stats_enabled %u ",
+			   rta_getattr_u8(tb[IFLA_BR_MCAST_STATS_ENABLED]));
 
 	if (tb[IFLA_BR_MCAST_IGMP_VERSION])
-		fprintf(f, "mcast_igmp_version %u ",
-			rta_getattr_u8(tb[IFLA_BR_MCAST_IGMP_VERSION]));
+		print_uint(PRINT_ANY,
+			   "mcast_igmp_version",
+			   "mcast_igmp_version %u ",
+			   rta_getattr_u8(tb[IFLA_BR_MCAST_IGMP_VERSION]));
 
 	if (tb[IFLA_BR_MCAST_MLD_VERSION])
-		fprintf(f, "mcast_mld_version %u ",
-			rta_getattr_u8(tb[IFLA_BR_MCAST_MLD_VERSION]));
+		print_uint(PRINT_ANY,
+			   "mcast_mld_version",
+			   "mcast_mld_version %u ",
+			   rta_getattr_u8(tb[IFLA_BR_MCAST_MLD_VERSION]));
 
 	if (tb[IFLA_BR_NF_CALL_IPTABLES])
-		fprintf(f, "nf_call_iptables %u ",
-			rta_getattr_u8(tb[IFLA_BR_NF_CALL_IPTABLES]));
+		print_uint(PRINT_ANY,
+			   "nf_call_iptables",
+			   "nf_call_iptables %u ",
+			   rta_getattr_u8(tb[IFLA_BR_NF_CALL_IPTABLES]));
 
 	if (tb[IFLA_BR_NF_CALL_IP6TABLES])
-		fprintf(f, "nf_call_ip6tables %u ",
-			rta_getattr_u8(tb[IFLA_BR_NF_CALL_IP6TABLES]));
+		print_uint(PRINT_ANY,
+			   "nf_call_ip6tables",
+			   "nf_call_ip6tables %u ",
+			   rta_getattr_u8(tb[IFLA_BR_NF_CALL_IP6TABLES]));
 
 	if (tb[IFLA_BR_NF_CALL_ARPTABLES])
-		fprintf(f, "nf_call_arptables %u ",
-			rta_getattr_u8(tb[IFLA_BR_NF_CALL_ARPTABLES]));
+		print_uint(PRINT_ANY,
+			   "nf_call_arptables",
+			   "nf_call_arptables %u ",
+			   rta_getattr_u8(tb[IFLA_BR_NF_CALL_ARPTABLES]));
 }
 
 static void bridge_print_help(struct link_util *lu, int argc, char **argv,
-		FILE *f)
+			      FILE *f)
 {
 	print_explain(f);
 }
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH iproute2 json v2 11/27] ip: iplink_bridge_slave.c: add json output support
  2017-08-17 17:35 [PATCH iproute2 json v2 00/27] ip: add -json support to 'ip link show' Julien Fortin
                   ` (9 preceding siblings ...)
  2017-08-17 17:35 ` [PATCH iproute2 json v2 10/27] ip: iplink_bridge.c: " Julien Fortin
@ 2017-08-17 17:35 ` Julien Fortin
  2017-08-17 17:35 ` [PATCH iproute2 json v2 12/27] ip: iplink_can.c: " Julien Fortin
                   ` (17 subsequent siblings)
  28 siblings, 0 replies; 33+ messages in thread
From: Julien Fortin @ 2017-08-17 17:35 UTC (permalink / raw)
  To: netdev; +Cc: roopa, nikolay, dsa, Julien Fortin

From: Julien Fortin <julien@cumulusnetworks.com>

Schema:
bridge_slave: IFLA_INFO_SLAVE_DATA
{
    "state": {
        "type": "string",
        "attr": "IFLA_BRPORT_STATE",
        "mutually_exclusive": {
            "state_index": {
                "type": "uint",
                "comment": "if (state > BR_STATE_BLOCKING)"
            }
        }
    },
    "priority": {
        "type": "int",
        "attr": "IFLA_BRPORT_PRIORITY"
    },
    "cost": {
        "type": "int",
        "attr": "IFLA_BRPORT_COST"
    },
    "mode": {
        "type": "bool",
        "attr": "IFLA_BRPORT_MODE"
    },
    "guard": {
        "type": "bool",
        "attr": "IFLA_BRPORT_GUARD"
    },
    "protect": {
        "type": "bool",
        "attr": "IFLA_BRPORT_PROTECT"
    },
    "fast_leave": {
        "type": "bool",
        "attr": "IFLA_BRPORT_FAST_LEAVE"
    },
    "learning": {
        "type": "bool",
        "attr": "IFLA_BRPORT_LEARNING"
    },
    "unicast_flood": {
        "type": "bool",
        "attr": "IFLA_BRPORT_UNICAST_FLOOD"
    },
    "id": {
        "type": "string",
        "attr": "IFLA_BRPORT_ID"
    },
    "no": {
        "type": "string",
        "attr": "IFLA_BRPORT_NO"
    },
    "designated_port": {
        "type": "uint",
        "attr": "IFLA_BRPORT_DESIGNATED_PORT"
    },
    "designated_cost": {
        "type": "uint",
        "attr": "IFLA_BRPORT_DESIGNATED_COST"
    },
    "bridge_id": {
        "type": "string",
        "attr": "IFLA_BRPORT_BRIDGE_ID"
    },
    "root_id": {
        "type": "string",
        "attr": "IFLA_BRPORT_ROOT_ID"
    },
    "hold_timer": {
        "type": "float",
        "attr": "IFLA_BRPORT_HOLD_TIMER"
    },
    "message_age_timer": {
        "type": "float",
        "attr": "IFLA_BRPORT_MESSAGE_AGE_TIMER"
    },
    "forward_delay_timer": {
        "type": "float",
        "attr": "IFLA_BRPORT_FORWARD_DELAY_TIMER"
    },
    "topology_change_ack": {
        "type": "uint",
        "attr": "IFLA_BRPORT_TOPOLOGY_CHANGE_ACK"
    },
    "config_pending": {
        "type": "uint",
        "attr": "IFLA_BRPORT_CONFIG_PENDING"
    },
    "proxyarp": {
        "type": "bool",
        "attr": "IFLA_BRPORT_PROXYARP"
    },
    "proxyarp_wifi": {
        "type": "bool",
        "attr": "IFLA_BRPORT_PROXYARP_WIFI"
    },
    "multicast_router": {
        "type": "uint",
        "attr": "IFLA_BRPORT_MULTICAST_ROUTER"
    },
    "mcast_flood": {
        "type": "bool",
        "attr": "IFLA_BRPORT_MCAST_FLOOD"
    }
}

$ ip link add dev br42 type bridge
$ ip link add dev bond42 type bond
$ ip link set dev bond42 master br42
$ ip link set dev bond42 up
$ ip link set dev br42 up
$ ip -details link show
$ ip -details link show
15: br42: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state
UP mode DEFAULT group default
    link/ether 22:8f:91:bb:9f:09 brd ff:ff:ff:ff:ff:ff promiscuity 0
    bridge forward_delay 1500 hello_time 200 max_age 2000 ageing_time
30000 stp_state 0 priority 32768 vlan_filtering 0 vlan_protocol 802.1Q
bridge_id 8000.22:8f:91:bb:9f:9 designated_root 8000.22:8f:91:bb:9f:9
root_port 0 root_path_cost 0 topology_change 0 topology_change_detected 0
hello_timer    0.00 tcn_timer    0.00 topology_change_timer    0.00
gc_timer  199.11 vlan_default_pvid 1 vlan_stats_enabled 0 group_fwd_mask 0
group_address 01:80:c2:00:00:00 mcast_snooping 1 mcast_router 1
mcast_query_use_ifaddr 0 mcast_querier 0 mcast_hash_elasticity 4096
mcast_hash_max 4096 mcast_last_member_count 2 mcast_startup_query_count 2
mcast_last_member_interval 100 mcast_membership_interval 26000
mcast_querier_interval 25500 mcast_query_interval 12500
mcast_query_response_interval 1000 mcast_startup_query_interval 3125
mcast_stats_enabled 0 mcast_igmp_version 2 mcast_mld_version 1
nf_call_iptables 0 nf_call_ip6tables 0 nf_call_arptables 0 addrgenmode
eui64
16: bond42: <BROADCAST,MULTICAST,MASTER,UP,LOWER_UP> mtu 1500 qdisc
noqueue master br42 state UNKNOWN mode DEFAULT group default
    link/ether 22:8f:91:bb:9f:09 brd ff:ff:ff:ff:ff:ff promiscuity 1
    bond mode 802.3ad miimon 100 updelay 0 downdelay 0 use_carrier 1
arp_interval 0 arp_validate none arp_all_targets any primary_reselect
always fail_over_mac none xmit_hash_policy layer3+4 resend_igmp 1
num_grat_arp 1 all_slaves_active 0 min_links 1 lp_interval 1
packets_per_slave 1 lacp_rate fast ad_select stable ad_actor_sys_prio
65535 ad_user_port_key 0 ad_actor_system 00:00:00:00:00:00
    bridge_slave state forwarding priority 8 cost 100 hairpin off guard
off root_block off fastleave off learning on flood on port_id 0x8001
port_no 0x1 designated_port 32769 designated_cost 0 designated_bridge
8000.22:8f:91:bb:9f:9 designated_root 8000.22:8f:91:bb:9f:9 hold_timer
0.00 message_age_timer    0.00 forward_delay_timer    0.00
topology_change_ack 0 config_pending 0 proxy_arp off proxy_arp_wifi off
mcast_router 1 mcast_fast_leave off mcast_flood on neigh_suppress off
addrgenmode eui64

$ ip -details -json link show
[{
        "ifindex": 15,
        "ifname": "br42",
        "flags": ["BROADCAST","MULTICAST","UP","LOWER_UP"],
        "mtu": 1500,
        "qdisc": "noqueue",
        "operstate": "UP",
        "linkmode": "DEFAULT",
        "group": "default",
        "link_type": "ether",
        "address": "22:8f:91:bb:9f:09",
        "broadcast": "ff:ff:ff:ff:ff:ff",
        "promiscuity": 0,
        "linkinfo": {
            "info_kind": "bridge",
            "info_data": {
                "forward_delay": 1500,
                "hello_time": 200,
                "max_age": 2000,
                "ageing_time": 30000,
                "stp_state": 0,
                "priority": 32768,
                "vlan_filtering": 0,
                "vlan_protocol": "802.1Q",
                "bridge_id": "8000.22:8f:91:bb:9f:9",
                "root_id": "8000.22:8f:91:bb:9f:9",
                "root_port": 0,
                "root_path_cost": 0,
                "topology_change": 0,
                "topology_change_detected": 0,
                "hello_timer": 0.00,
                "tcn_timer": 0.00,
                "topology_change_timer": 0.00,
                "gc_timer": 298.27,
                "vlan_default_pvid": 1,
                "vlan_stats_enabled": 0,
                "group_fwd_mask": "0",
                "group_addr": "01:80:c2:00:00:00",
                "mcast_snooping": 1,
                "mcast_router": 1,
                "mcast_query_use_ifaddr": 0,
                "mcast_querier": 0,
                "mcast_hash_elasticity": 4096,
                "mcast_hash_max": 4096,
                "mcast_last_member_cnt": 2,
                "mcast_startup_query_cnt": 2,
                "mcast_last_member_intvl": 100,
                "mcast_membership_intvl": 26000,
                "mcast_querier_intvl": 25500,
                "mcast_query_intvl": 12500,
                "mcast_query_response_intvl": 1000,
                "mcast_startup_query_intvl": 3125,
                "mcast_stats_enabled": 0,
                "mcast_igmp_version": 2,
                "mcast_mld_version": 1,
                "nf_call_iptables": 0,
                "nf_call_ip6tables": 0,
                "nf_call_arptables": 0
            }
        },
        "inet6_addr_gen_mode": "eui64",
        "num_tx_queues": 1,
        "num_rx_queues": 1,
        "gso_max_size": 65536,
        "gso_max_segs": 65535
    },{
        "ifindex": 16,
        "ifname": "bond42",
        "flags": ["BROADCAST","MULTICAST","MASTER","UP","LOWER_UP"],
        "mtu": 1500,
        "qdisc": "noqueue",
        "master": "br42",
        "operstate": "UNKNOWN",
        "linkmode": "DEFAULT",
        "group": "default",
        "link_type": "ether",
        "address": "22:8f:91:bb:9f:09",
        "broadcast": "ff:ff:ff:ff:ff:ff",
        "promiscuity": 1,
        "linkinfo": {
            "info_kind": "bond",
            "info_data": {
                "mode": "802.3ad",
                "miimon": 100,
                "updelay": 0,
                "downdelay": 0,
                "use_carrier": 1,
                "arp_interval": 0,
                "arp_validate": null,
                "arp_all_targets": "any",
                "primary_reselect": "always",
                "fail_over_mac": "none",
                "xmit_hash_policy": "layer3+4",
                "resend_igmp": 1,
                "num_peer_notif": 1,
                "all_slaves_active": 0,
                "min_links": 1,
                "lp_interval": 1,
                "packets_per_slave": 1,
                "ad_lacp_rate": "fast",
                "ad_select": "stable",
                "ad_actor_sys_prio": 65535,
                "ad_user_port_key": 0,
                "ad_actor_system": "00:00:00:00:00:00"
            },
            "info_slave_kind": "bridge",
            "info_slave_data": {
                "state": "forwarding",
                "priority": 8,
                "cost": 100,
                "hairpin": false,
                "guard": false,
                "root_block": false,
                "fastleave": false,
                "learning": true,
                "flood": true,
                "id": "0x8001",
                "no": "0x1",
                "designated_port": 32769,
                "designated_cost": 0,
                "bridge_id": "8000.22:8f:91:bb:9f:9",
                "root_id": "8000.22:8f:91:bb:9f:9",
                "hold_timer": 0.00,
                "message_age_timer": 0.00,
                "forward_delay_timer": 11.97,
                "topology_change_ack": 0,
                "config_pending": 0,
                "proxy_arp": false,
                "proxy_arp_wifi": false,
                "multicast_router": 1,
                "mcast_flood": true
            }
        },
        "inet6_addr_gen_mode": "eui64",
        "num_tx_queues": 16,
        "num_rx_queues": 16,
        "gso_max_size": 65536,
        "gso_max_segs": 65535
    }
]

Signed-off-by: Julien Fortin <julien@cumulusnetworks.com>
---
 ip/iplink_bridge_slave.c | 185 +++++++++++++++++++++++++++++------------------
 1 file changed, 114 insertions(+), 71 deletions(-)

diff --git a/ip/iplink_bridge_slave.c b/ip/iplink_bridge_slave.c
index 3e883328..80272b09 100644
--- a/ip/iplink_bridge_slave.c
+++ b/ip/iplink_bridge_slave.c
@@ -56,14 +56,52 @@ static const char *port_states[] = {
 static void print_portstate(FILE *f, __u8 state)
 {
 	if (state <= BR_STATE_BLOCKING)
-		fprintf(f, "state %s ", port_states[state]);
+		print_string(PRINT_ANY,
+			     "state",
+			     "state %s ",
+			     port_states[state]);
 	else
-		fprintf(f, "state (%d) ", state);
+		print_int(PRINT_ANY, "state_index", "state (%d) ", state);
 }
 
-static void print_onoff(FILE *f, char *flag, __u8 val)
+static void _print_onoff(FILE *f, char *json_flag, char *flag, __u8 val)
 {
-	fprintf(f, "%s %s ", flag, val ? "on" : "off");
+	if (is_json_context())
+		print_bool(PRINT_JSON, flag, NULL, val);
+	else
+		fprintf(f, "%s %s ", flag, val ? "on" : "off");
+}
+
+static void _print_hex(FILE *f,
+		       const char *json_attr,
+		       const char *attr,
+		       __u16 val)
+{
+	if (is_json_context()) {
+		SPRINT_BUF(b1);
+
+		snprintf(b1, sizeof(b1), "0x%x", val);
+		print_string(PRINT_JSON, json_attr, NULL, b1);
+	} else {
+		fprintf(f, "%s 0x%x ", attr, val);
+	}
+}
+
+static void _print_timer(FILE *f, const char *attr, struct rtattr *timer)
+{
+	struct timeval tv;
+
+	__jiffies_to_tv(&tv, rta_getattr_u64(timer));
+	if (is_json_context()) {
+		json_writer_t *jw = get_json_writer();
+
+		jsonw_name(jw, attr);
+		jsonw_printf(jw, "%i.%.2i",
+			     (int)tv.tv_sec, (int)tv.tv_usec / 10000);
+	} else {
+		fprintf(f, "%s %4i.%.2i ", attr, (int)tv.tv_sec,
+			(int)tv.tv_usec / 10000);
+	}
 }
 
 static void bridge_slave_print_opt(struct link_util *lu, FILE *f,
@@ -76,59 +114,70 @@ static void bridge_slave_print_opt(struct link_util *lu, FILE *f,
 		print_portstate(f, rta_getattr_u8(tb[IFLA_BRPORT_STATE]));
 
 	if (tb[IFLA_BRPORT_PRIORITY])
-		fprintf(f, "priority %d ",
-			rta_getattr_u16(tb[IFLA_BRPORT_PRIORITY]));
+		print_int(PRINT_ANY,
+			  "priority",
+			  "priority %d ",
+			  rta_getattr_u16(tb[IFLA_BRPORT_PRIORITY]));
 
 	if (tb[IFLA_BRPORT_COST])
-		fprintf(f, "cost %d ",
-			rta_getattr_u32(tb[IFLA_BRPORT_COST]));
+		print_int(PRINT_ANY,
+			  "cost",
+			  "cost %d ",
+			  rta_getattr_u32(tb[IFLA_BRPORT_COST]));
 
 	if (tb[IFLA_BRPORT_MODE])
-		print_onoff(f, "hairpin",
-			    rta_getattr_u8(tb[IFLA_BRPORT_MODE]));
+		_print_onoff(f, "mode", "hairpin",
+			     rta_getattr_u8(tb[IFLA_BRPORT_MODE]));
 
 	if (tb[IFLA_BRPORT_GUARD])
-		print_onoff(f, "guard",
-			    rta_getattr_u8(tb[IFLA_BRPORT_GUARD]));
+		_print_onoff(f, "guard", "guard",
+			     rta_getattr_u8(tb[IFLA_BRPORT_GUARD]));
 
 	if (tb[IFLA_BRPORT_PROTECT])
-		print_onoff(f, "root_block",
-			    rta_getattr_u8(tb[IFLA_BRPORT_PROTECT]));
+		_print_onoff(f, "protect", "root_block",
+			     rta_getattr_u8(tb[IFLA_BRPORT_PROTECT]));
 
 	if (tb[IFLA_BRPORT_FAST_LEAVE])
-		print_onoff(f, "fastleave",
-			    rta_getattr_u8(tb[IFLA_BRPORT_FAST_LEAVE]));
+		_print_onoff(f, "fast_leave", "fastleave",
+			     rta_getattr_u8(tb[IFLA_BRPORT_FAST_LEAVE]));
 
 	if (tb[IFLA_BRPORT_LEARNING])
-		print_onoff(f, "learning",
-			rta_getattr_u8(tb[IFLA_BRPORT_LEARNING]));
+		_print_onoff(f, "learning", "learning",
+			     rta_getattr_u8(tb[IFLA_BRPORT_LEARNING]));
 
 	if (tb[IFLA_BRPORT_UNICAST_FLOOD])
-		print_onoff(f, "flood",
-			rta_getattr_u8(tb[IFLA_BRPORT_UNICAST_FLOOD]));
+		_print_onoff(f, "unicast_flood", "flood",
+			     rta_getattr_u8(tb[IFLA_BRPORT_UNICAST_FLOOD]));
 
 	if (tb[IFLA_BRPORT_ID])
-		fprintf(f, "port_id 0x%x ",
-			rta_getattr_u16(tb[IFLA_BRPORT_ID]));
+		_print_hex(f, "id", "port_id",
+			   rta_getattr_u16(tb[IFLA_BRPORT_ID]));
 
 	if (tb[IFLA_BRPORT_NO])
-		fprintf(f, "port_no 0x%x ",
-			rta_getattr_u16(tb[IFLA_BRPORT_NO]));
+		_print_hex(f, "no", "port_no",
+			   rta_getattr_u16(tb[IFLA_BRPORT_NO]));
 
 	if (tb[IFLA_BRPORT_DESIGNATED_PORT])
-		fprintf(f, "designated_port %u ",
-			rta_getattr_u16(tb[IFLA_BRPORT_DESIGNATED_PORT]));
+		print_uint(PRINT_ANY,
+			   "designated_port",
+			   "designated_port %u ",
+			   rta_getattr_u16(tb[IFLA_BRPORT_DESIGNATED_PORT]));
 
 	if (tb[IFLA_BRPORT_DESIGNATED_COST])
-		fprintf(f, "designated_cost %u ",
-			rta_getattr_u16(tb[IFLA_BRPORT_DESIGNATED_COST]));
+		print_uint(PRINT_ANY,
+			   "designated_cost",
+			   "designated_cost %u ",
+			   rta_getattr_u16(tb[IFLA_BRPORT_DESIGNATED_COST]));
 
 	if (tb[IFLA_BRPORT_BRIDGE_ID]) {
 		char bridge_id[32];
 
 		br_dump_bridge_id(RTA_DATA(tb[IFLA_BRPORT_BRIDGE_ID]),
 				  bridge_id, sizeof(bridge_id));
-		fprintf(f, "designated_bridge %s ", bridge_id);
+		print_string(PRINT_ANY,
+			     "bridge_id",
+			     "designated_bridge %s ",
+			     bridge_id);
 	}
 
 	if (tb[IFLA_BRPORT_ROOT_ID]) {
@@ -136,65 +185,59 @@ static void bridge_slave_print_opt(struct link_util *lu, FILE *f,
 
 		br_dump_bridge_id(RTA_DATA(tb[IFLA_BRPORT_ROOT_ID]),
 				  root_id, sizeof(root_id));
-		fprintf(f, "designated_root %s ", root_id);
-	}
-
-	if (tb[IFLA_BRPORT_HOLD_TIMER]) {
-		struct timeval tv;
-		__u64 htimer;
-
-		htimer = rta_getattr_u64(tb[IFLA_BRPORT_HOLD_TIMER]);
-		__jiffies_to_tv(&tv, htimer);
-		fprintf(f, "hold_timer %4i.%.2i ", (int)tv.tv_sec,
-			(int)tv.tv_usec/10000);
+		print_string(PRINT_ANY,
+			     "root_id",
+			     "designated_root %s ", root_id);
 	}
 
-	if (tb[IFLA_BRPORT_MESSAGE_AGE_TIMER]) {
-		struct timeval tv;
-		__u64 agetimer;
+	if (tb[IFLA_BRPORT_HOLD_TIMER])
+		_print_timer(f, "hold_timer", tb[IFLA_BRPORT_HOLD_TIMER]);
 
-		agetimer = rta_getattr_u64(tb[IFLA_BRPORT_MESSAGE_AGE_TIMER]);
-		__jiffies_to_tv(&tv, agetimer);
-		fprintf(f, "message_age_timer %4i.%.2i ", (int)tv.tv_sec,
-			(int)tv.tv_usec/10000);
-	}
-
-	if (tb[IFLA_BRPORT_FORWARD_DELAY_TIMER]) {
-		struct timeval tv;
-		__u64 fwdtimer;
+	if (tb[IFLA_BRPORT_MESSAGE_AGE_TIMER])
+		_print_timer(f, "message_age_timer",
+			     tb[IFLA_BRPORT_MESSAGE_AGE_TIMER]);
 
-		fwdtimer = rta_getattr_u64(tb[IFLA_BRPORT_FORWARD_DELAY_TIMER]);
-		__jiffies_to_tv(&tv, fwdtimer);
-		fprintf(f, "forward_delay_timer %4i.%.2i ", (int)tv.tv_sec,
-			(int)tv.tv_usec/10000);
-	}
+	if (tb[IFLA_BRPORT_FORWARD_DELAY_TIMER])
+		_print_timer(f, "forward_delay_timer",
+			     tb[IFLA_BRPORT_FORWARD_DELAY_TIMER]);
 
 	if (tb[IFLA_BRPORT_TOPOLOGY_CHANGE_ACK])
-		fprintf(f, "topology_change_ack %u ",
-			rta_getattr_u8(tb[IFLA_BRPORT_TOPOLOGY_CHANGE_ACK]));
+		print_uint(PRINT_ANY,
+			   "topology_change_ack",
+			   "topology_change_ack %u ",
+			   rta_getattr_u8(tb[IFLA_BRPORT_TOPOLOGY_CHANGE_ACK]));
 
 	if (tb[IFLA_BRPORT_CONFIG_PENDING])
-		fprintf(f, "config_pending %u ",
-			rta_getattr_u8(tb[IFLA_BRPORT_CONFIG_PENDING]));
+		print_uint(PRINT_ANY,
+			   "config_pending",
+			   "config_pending %u ",
+			   rta_getattr_u8(tb[IFLA_BRPORT_CONFIG_PENDING]));
+
 	if (tb[IFLA_BRPORT_PROXYARP])
-		print_onoff(f, "proxy_arp",
-			    rta_getattr_u8(tb[IFLA_BRPORT_PROXYARP]));
+		_print_onoff(f, "proxyarp", "proxy_arp",
+			     rta_getattr_u8(tb[IFLA_BRPORT_PROXYARP]));
 
 	if (tb[IFLA_BRPORT_PROXYARP_WIFI])
-		print_onoff(f, "proxy_arp_wifi",
-			    rta_getattr_u8(tb[IFLA_BRPORT_PROXYARP_WIFI]));
+		_print_onoff(f, "proxyarp_wifi", "proxy_arp_wifi",
+			     rta_getattr_u8(tb[IFLA_BRPORT_PROXYARP_WIFI]));
 
 	if (tb[IFLA_BRPORT_MULTICAST_ROUTER])
-		fprintf(f, "mcast_router %u ",
-			rta_getattr_u8(tb[IFLA_BRPORT_MULTICAST_ROUTER]));
+		print_uint(PRINT_ANY,
+			   "multicast_router",
+			   "mcast_router %u ",
+			   rta_getattr_u8(tb[IFLA_BRPORT_MULTICAST_ROUTER]));
 
 	if (tb[IFLA_BRPORT_FAST_LEAVE])
-		print_onoff(f, "mcast_fast_leave",
-			    rta_getattr_u8(tb[IFLA_BRPORT_FAST_LEAVE]));
+		// not printing any json here because
+		// we already printed fast_leave before
+		print_string(PRINT_FP,
+			     NULL,
+			     "mcast_fast_leave %s ",
+			     rta_getattr_u8(tb[IFLA_BRPORT_FAST_LEAVE]) ? "on" : "off");
 
 	if (tb[IFLA_BRPORT_MCAST_FLOOD])
-		print_onoff(f, "mcast_flood",
-			rta_getattr_u8(tb[IFLA_BRPORT_MCAST_FLOOD]));
+		_print_onoff(f, "mcast_flood", "mcast_flood",
+			     rta_getattr_u8(tb[IFLA_BRPORT_MCAST_FLOOD]));
 }
 
 static void bridge_slave_parse_on_off(char *arg_name, char *arg_val,
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH iproute2 json v2 12/27] ip: iplink_can.c: add json output support
  2017-08-17 17:35 [PATCH iproute2 json v2 00/27] ip: add -json support to 'ip link show' Julien Fortin
                   ` (10 preceding siblings ...)
  2017-08-17 17:35 ` [PATCH iproute2 json v2 11/27] ip: iplink_bridge_slave.c: " Julien Fortin
@ 2017-08-17 17:35 ` Julien Fortin
  2017-08-17 17:36 ` [PATCH iproute2 json v2 13/27] ip: iplink_geneve.c: " Julien Fortin
                   ` (16 subsequent siblings)
  28 siblings, 0 replies; 33+ messages in thread
From: Julien Fortin @ 2017-08-17 17:35 UTC (permalink / raw)
  To: netdev; +Cc: roopa, nikolay, dsa, Julien Fortin

From: Julien Fortin <julien@cumulusnetworks.com>

Schema: IFLA_INFO_DATA
{
    "ctrlmode": {
        "type": "array",
        "attr": "IFLA_CAN_CTRLMODE",
        "array": [
            {
                "type": "string"
            }
        ]
    },
    "state": {
        "type": "string",
        "attr": "IFLA_CAN_STATE"
    },
    "berr_counter": {
        "type": "dict",
        "attr": "IFLA_CAN_BERR_COUNTER",
        "dict": {
            "tx": {
                "type": "int"
            },
            "rx": {
                "type": "int"
            }
        }
    },
    "restart_ms": {
        "type": "int",
        "attr": "IFLA_CAN_RESTART_MS"
    },
    "bittiming": {
        "type": "dict",
        "attr": "IFLA_CAN_BITTIMING",
        "dict": {
            "bitrate": {
                "type": "int"
            },
            "sample_point": {
                "type": "float"
            },
            "tq": {
                "type": "int"
            },
            "prop_seg": {
                "type": "int"
            },
            "phase_seg1": {
                "type": "int"
            },
            "phase_seg2": {
                "type": "int"
            },
            "sjw": {
                "type": "int"
            }
        }
    },
    "bittiming_const": {
        "type": "dict",
        "attr": "IFLA_CAN_BITTIMING_CONST",
        "dict": {
            "name": {
                "type": "string"
            },
            "tseg1": {
                "type": "dict",
                "dict": {
                    "min": {
                        "type": "int"
                    },
                    "max": {
                        "type": "int"
                    }
                }
            },
            "tseg2": {
                "type": "dict",
                "dict": {
                    "min": {
                        "type": "int"
                    },
                    "max": {
                        "type": "int"
                    }
                }
            },
            "sjw": {
                "type": "dict",
                "dict": {
                    "min": {
                        "type": "int"
                    },
                    "max": {
                        "type": "int"
                    }
                }
            },
            "brp": {
                "type": "dict",
                "dict": {
                    "min": {
                        "type": "int"
                    },
                    "max": {
                        "type": "int"
                    }
                }
            },
            "brp_inc": {
                "type": "int"
            }
        }
    },
    "bittiming_bitrate": {
        "type": "uint",
        "attr": "IFLA_CAN_BITTIMING"
    },
    "bitrate_const": {
        "type": "array",
        "attr": "IFLA_CAN_BITRATE_CONST",
        "array": [
            {
                "type": "uint"
            }
        ]
    },
    "data_bittiming": {
        "type": "dict",
        "attr": "IFLA_CAN_DATA_BITTIMING",
        "dict": {
            "bitrate": {
                "type": "int"
            },
            "sample_point": {
                "type": "float"
            },
            "tq": {
                "type": "int"
            },
            "prop_seg": {
                "type": "int"
            },
            "phase_seg1": {
                "type": "int"
            },
            "phase_seg2": {
                "type": "int"
            },
            "sjw": {
                "type": "int"
            }
        }
    },
    "data_bittiming_const": {
        "type": "dict",
        "attr": "IFLA_CAN_DATA_BITTIMING_CONST",
        "dict": {
            "name": {
                "type": "string"
            },
            "tseg1": {
                "type": "dict",
                "dict": {
                    "min": {
                        "type": "int"
                    },
                    "max": {
                        "type": "int"
                    }
                }
            },
            "tseg2": {
                "type": "dict",
                "dict": {
                    "min": {
                        "type": "int"
                    },
                    "max": {
                        "type": "int"
                    }
                }
            },
            "sjw": {
                "type": "dict",
                "dict": {
                    "min": {
                        "type": "int"
                    },
                    "max": {
                        "type": "int"
                    }
                }
            },
            "brp": {
                "type": "dict",
                "dict": {
                    "min": {
                        "type": "int"
                    },
                    "max": {
                        "type": "int"
                    }
                }
            },
            "brp_inc": {
                "type": "int"
            }
        }
    },
    "data_bittiming_bitrate": {
        "type": "uint",
        "attr": "IFLA_CAN_DATA_BITTIMING"
    },
    "data_bitrate_const": {
        "type": "array",
        "attr": "IFLA_CAN_DATA_BITRATE_CONST",
        "array": [
            {
                "type": "uint"
            }
        ]
    },
    "termination": {
        "type": "unsigned short",
        "attr": "IFLA_CAN_TERMINATION"
    },
    "termination_const": {
        "type": "array",
        "attr": "IFLA_CAN_TERMINATION_CONST",
        "array": [
            {
                "type": "unsigned short"
            }
        ]
    },
    "clock": {
        "type": "int",
        "attr": "IFLA_CAN_CLOCK"
    }
}

IFLA_INFO_XSTATS
{
    "restarts": {
        "type": "int"
    },
    "bus_error": {
        "type": "int"
    },
    "arbitration_lost": {
        "type": "int"
    },
    "error_warning": {
        "type": "int"
    },
    "error_passive": {
        "type": "int"
    },
    "bus_off": {
        "type": "int"
    }
}

Signed-off-by: Julien Fortin <julien@cumulusnetworks.com>
---
 ip/iplink_can.c | 282 ++++++++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 211 insertions(+), 71 deletions(-)

diff --git a/ip/iplink_can.c b/ip/iplink_can.c
index 5df56b2b..f415558a 100644
--- a/ip/iplink_can.c
+++ b/ip/iplink_can.c
@@ -89,11 +89,11 @@ static void set_ctrlmode(char *name, char *arg,
 
 static void print_ctrlmode(FILE *f, __u32 cm)
 {
-	fprintf(f, "<");
-#define _PF(cmflag, cmname)					\
-	if (cm & cmflag) {					\
-		cm &= ~cmflag;					\
-		fprintf(f, "%s%s", cmname, cm ? "," : "");	\
+	open_json_array(PRINT_ANY, is_json_context() ? "ctrlmode" : "<");
+#define _PF(cmflag, cmname)						\
+	if (cm & cmflag) {						\
+		cm &= ~cmflag;						\
+		print_string(PRINT_ANY, NULL, cm ? "%s," : "%s", cmname); \
 	}
 	_PF(CAN_CTRLMODE_LOOPBACK, "LOOPBACK");
 	_PF(CAN_CTRLMODE_LISTENONLY, "LISTEN-ONLY");
@@ -105,8 +105,8 @@ static void print_ctrlmode(FILE *f, __u32 cm)
 	_PF(CAN_CTRLMODE_PRESUME_ACK, "PRESUME-ACK");
 #undef _PF
 	if (cm)
-		fprintf(f, "%x", cm);
-	fprintf(f, "> ");
+		print_hex(PRINT_ANY, NULL, "%x", cm);
+	close_json_array(PRINT_ANY, "> ");
 }
 
 static int can_parse_opt(struct link_util *lu, int argc, char **argv,
@@ -260,6 +260,14 @@ static const char *can_state_names[] = {
 	[CAN_STATE_SLEEPING] = "SLEEPING"
 };
 
+static void can_print_json_timing_min_max(const char *attr, int min, int max)
+{
+	open_json_object(attr);
+	print_int(PRINT_JSON, "min", NULL, min);
+	print_int(PRINT_JSON, "max", NULL, max);
+	close_json_object();
+}
+
 static void can_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 {
 	if (!tb)
@@ -275,32 +283,64 @@ static void can_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 	if (tb[IFLA_CAN_STATE]) {
 		uint32_t state = rta_getattr_u32(tb[IFLA_CAN_STATE]);
 
-		fprintf(f, "state %s ", state <= CAN_STATE_MAX ?
-			can_state_names[state] : "UNKNOWN");
+		if (state <= CAN_STATE_MAX)
+			print_string(PRINT_ANY, "state", "state %s ",
+				     can_state_names[state]);
+		else
+			print_null(PRINT_ANY, "state", "state UNKNOWN", NULL);
 	}
 
 	if (tb[IFLA_CAN_BERR_COUNTER]) {
 		struct can_berr_counter *bc =
 			RTA_DATA(tb[IFLA_CAN_BERR_COUNTER]);
 
-		fprintf(f, "(berr-counter tx %d rx %d) ", bc->txerr, bc->rxerr);
+		if (is_json_context()) {
+			open_json_object("berr_counter");
+			print_int(PRINT_JSON, "tx", NULL, bc->txerr);
+			print_int(PRINT_JSON, "rx", NULL, bc->rxerr);
+			close_json_object();
+		} else {
+			fprintf(f, "(berr-counter tx %d rx %d) ",
+				bc->txerr, bc->rxerr);
+		}
 	}
 
 	if (tb[IFLA_CAN_RESTART_MS]) {
 		__u32 *restart_ms = RTA_DATA(tb[IFLA_CAN_RESTART_MS]);
 
-		fprintf(f, "restart-ms %d ", *restart_ms);
+		print_int(PRINT_ANY,
+			  "restart_ms",
+			  "restart-ms %d ",
+			  *restart_ms);
 	}
 
 	/* bittiming is irrelevant if fixed bitrate is defined */
 	if (tb[IFLA_CAN_BITTIMING] && !tb[IFLA_CAN_BITRATE_CONST]) {
 		struct can_bittiming *bt = RTA_DATA(tb[IFLA_CAN_BITTIMING]);
 
-		fprintf(f, "\n	  bitrate %d sample-point %.3f ",
-			bt->bitrate, (float)bt->sample_point / 1000.);
-		fprintf(f, "\n	  tq %d prop-seg %d phase-seg1 %d phase-seg2 %d sjw %d",
-			bt->tq, bt->prop_seg, bt->phase_seg1, bt->phase_seg2,
-			bt->sjw);
+		if (is_json_context()) {
+			open_json_object("bittiming");
+			print_int(PRINT_ANY, "bitrate", NULL, bt->bitrate);
+			jsonw_float_field_fmt(get_json_writer(),
+					      "sample_point", "%.3f",
+					      (float) bt->sample_point / 1000.);
+			print_int(PRINT_ANY, "tq", NULL, bt->tq);
+			print_int(PRINT_ANY, "prop_seg", NULL, bt->prop_seg);
+			print_int(PRINT_ANY, "phase_seg1",
+				  NULL, bt->phase_seg1);
+			print_int(PRINT_ANY, "phase_seg2",
+				  NULL, bt->phase_seg2);
+			print_int(PRINT_ANY, "sjw", NULL, bt->sjw);
+			close_json_object();
+		} else {
+			fprintf(f, "\n	  bitrate %d sample-point %.3f ",
+				bt->bitrate, (float) bt->sample_point / 1000.);
+			fprintf(f,
+				"\n	  tq %d prop-seg %d phase-seg1 %d phase-seg2 %d sjw %d",
+				bt->tq, bt->prop_seg,
+				bt->phase_seg1, bt->phase_seg2,
+				bt->sjw);
+		}
 	}
 
 	/* bittiming const is irrelevant if fixed bitrate is defined */
@@ -308,40 +348,68 @@ static void can_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 		struct can_bittiming_const *btc =
 			RTA_DATA(tb[IFLA_CAN_BITTIMING_CONST]);
 
-		fprintf(f, "\n	  %s: tseg1 %d..%d tseg2 %d..%d "
-			"sjw 1..%d brp %d..%d brp-inc %d",
-			btc->name, btc->tseg1_min, btc->tseg1_max,
-			btc->tseg2_min, btc->tseg2_max, btc->sjw_max,
-			btc->brp_min, btc->brp_max, btc->brp_inc);
+		if (is_json_context()) {
+			open_json_object("bittiming_const");
+			print_string(PRINT_JSON, "name", NULL, btc->name);
+			can_print_json_timing_min_max("tseg1",
+						      btc->tseg1_min,
+						      btc->tseg1_max);
+			can_print_json_timing_min_max("tseg2",
+						      btc->tseg2_min,
+						      btc->tseg2_max);
+			can_print_json_timing_min_max("sjw", 1, btc->sjw_max);
+			can_print_json_timing_min_max("brp",
+						      btc->brp_min,
+						      btc->brp_max);
+			print_int(PRINT_JSON, "brp_inc", NULL, btc->brp_inc);
+			close_json_object();
+		} else {
+			fprintf(f, "\n	  %s: tseg1 %d..%d tseg2 %d..%d "
+				"sjw 1..%d brp %d..%d brp-inc %d",
+				btc->name, btc->tseg1_min, btc->tseg1_max,
+				btc->tseg2_min, btc->tseg2_max, btc->sjw_max,
+				btc->brp_min, btc->brp_max, btc->brp_inc);
+		}
 	}
 
 	if (tb[IFLA_CAN_BITRATE_CONST]) {
 		__u32 *bitrate_const = RTA_DATA(tb[IFLA_CAN_BITRATE_CONST]);
 		int bitrate_cnt = RTA_PAYLOAD(tb[IFLA_CAN_BITRATE_CONST]) /
-				  sizeof(*bitrate_const);
+			sizeof(*bitrate_const);
 		int i;
 		__u32 bitrate = 0;
 
 		if (tb[IFLA_CAN_BITTIMING]) {
 			struct can_bittiming *bt =
-			    RTA_DATA(tb[IFLA_CAN_BITTIMING]);
+				RTA_DATA(tb[IFLA_CAN_BITTIMING]);
 			bitrate = bt->bitrate;
 		}
 
-		fprintf(f, "\n	  bitrate %u", bitrate);
-		fprintf(f, "\n	     [");
+		if (is_json_context()) {
+			print_uint(PRINT_JSON,
+				   "bittiming_bitrate",
+				   NULL, bitrate);
+			open_json_array(PRINT_JSON, "bitrate_const");
+			for (i = 0; i < bitrate_cnt; ++i)
+				print_uint(PRINT_JSON, NULL, NULL,
+					   bitrate_const[i]);
+			close_json_array(PRINT_JSON, NULL);
+		} else {
+			fprintf(f, "\n	  bitrate %u", bitrate);
+			fprintf(f, "\n	     [");
+
+			for (i = 0; i < bitrate_cnt - 1; ++i) {
+				/* This will keep lines below 80 signs */
+				if (!(i % 6) && i)
+					fprintf(f, "\n	      ");
+
+				fprintf(f, "%8u, ", bitrate_const[i]);
+			}
 
-		for (i = 0; i < bitrate_cnt - 1; ++i) {
-			/* This will keep lines below 80 signs */
 			if (!(i % 6) && i)
 				fprintf(f, "\n	      ");
-
-			fprintf(f, "%8u, ", bitrate_const[i]);
+			fprintf(f, "%8u ]", bitrate_const[i]);
 		}
-
-		if (!(i % 6) && i)
-			fprintf(f, "\n	      ");
-		fprintf(f, "%8u ]", bitrate_const[i]);
 	}
 
 	/* data bittiming is irrelevant if fixed bitrate is defined */
@@ -349,12 +417,30 @@ static void can_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 		struct can_bittiming *dbt =
 			RTA_DATA(tb[IFLA_CAN_DATA_BITTIMING]);
 
-		fprintf(f, "\n	  dbitrate %d dsample-point %.3f ",
-			dbt->bitrate, (float)dbt->sample_point / 1000.);
-		fprintf(f, "\n	  dtq %d dprop-seg %d dphase-seg1 %d "
-			"dphase-seg2 %d dsjw %d",
-			dbt->tq, dbt->prop_seg, dbt->phase_seg1,
-			dbt->phase_seg2, dbt->sjw);
+		if (is_json_context()) {
+			open_json_object("data_bittiming");
+			print_int(PRINT_JSON, "bitrate", NULL, dbt->bitrate);
+			jsonw_float_field_fmt(get_json_writer(),
+					      "sample_point",
+					      "%.3f",
+					      (float) dbt->sample_point / 1000.);
+			print_int(PRINT_JSON, "tq", NULL, dbt->tq);
+			print_int(PRINT_JSON, "prop_seg", NULL, dbt->prop_seg);
+			print_int(PRINT_JSON, "phase_seg1",
+				  NULL, dbt->phase_seg1);
+			print_int(PRINT_JSON, "phase_seg2",
+				  NULL, dbt->phase_seg2);
+			print_int(PRINT_JSON, "sjw", NULL, dbt->sjw);
+			close_json_object();
+		} else {
+			fprintf(f, "\n	  dbitrate %d dsample-point %.3f ",
+				dbt->bitrate,
+				(float) dbt->sample_point / 1000.);
+			fprintf(f, "\n	  dtq %d dprop-seg %d dphase-seg1 %d "
+				"dphase-seg2 %d dsjw %d",
+				dbt->tq, dbt->prop_seg, dbt->phase_seg1,
+				dbt->phase_seg2, dbt->sjw);
+		}
 	}
 
 	/* data bittiming const is irrelevant if fixed bitrate is defined */
@@ -363,63 +449,102 @@ static void can_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 		struct can_bittiming_const *dbtc =
 			RTA_DATA(tb[IFLA_CAN_DATA_BITTIMING_CONST]);
 
-		fprintf(f, "\n	  %s: dtseg1 %d..%d dtseg2 %d..%d "
-			"dsjw 1..%d dbrp %d..%d dbrp-inc %d",
-			dbtc->name, dbtc->tseg1_min, dbtc->tseg1_max,
-			dbtc->tseg2_min, dbtc->tseg2_max, dbtc->sjw_max,
-			dbtc->brp_min, dbtc->brp_max, dbtc->brp_inc);
+		if (is_json_context()) {
+			open_json_object("data_bittiming_const");
+			print_string(PRINT_JSON, "name", NULL, dbtc->name);
+			can_print_json_timing_min_max("tseg1",
+						      dbtc->tseg1_min,
+						      dbtc->tseg1_max);
+			can_print_json_timing_min_max("tseg2",
+						      dbtc->tseg2_min,
+						      dbtc->tseg2_max);
+			can_print_json_timing_min_max("sjw", 1, dbtc->sjw_max);
+			can_print_json_timing_min_max("brp",
+						      dbtc->brp_min,
+						      dbtc->brp_max);
+
+			print_int(PRINT_JSON, "brp_inc", NULL, dbtc->brp_inc);
+			close_json_object();
+		} else {
+			fprintf(f, "\n	  %s: dtseg1 %d..%d dtseg2 %d..%d "
+				"dsjw 1..%d dbrp %d..%d dbrp-inc %d",
+				dbtc->name, dbtc->tseg1_min, dbtc->tseg1_max,
+				dbtc->tseg2_min, dbtc->tseg2_max, dbtc->sjw_max,
+				dbtc->brp_min, dbtc->brp_max, dbtc->brp_inc);
+		}
 	}
 
 	if (tb[IFLA_CAN_DATA_BITRATE_CONST]) {
 		__u32 *dbitrate_const =
-		    RTA_DATA(tb[IFLA_CAN_DATA_BITRATE_CONST]);
+			RTA_DATA(tb[IFLA_CAN_DATA_BITRATE_CONST]);
 		int dbitrate_cnt =
-		    RTA_PAYLOAD(tb[IFLA_CAN_DATA_BITRATE_CONST]) /
-		    sizeof(*dbitrate_const);
+			RTA_PAYLOAD(tb[IFLA_CAN_DATA_BITRATE_CONST]) /
+			sizeof(*dbitrate_const);
 		int i;
 		__u32 dbitrate = 0;
 
 		if (tb[IFLA_CAN_DATA_BITTIMING]) {
 			struct can_bittiming *dbt =
-			    RTA_DATA(tb[IFLA_CAN_DATA_BITTIMING]);
+				RTA_DATA(tb[IFLA_CAN_DATA_BITTIMING]);
 			dbitrate = dbt->bitrate;
 		}
 
-		fprintf(f, "\n	  dbitrate %u", dbitrate);
-		fprintf(f, "\n	     [");
+		if (is_json_context()) {
+			print_uint(PRINT_JSON, "data_bittiming_bitrate",
+				   NULL, dbitrate);
+			open_json_array(PRINT_JSON, "data_bitrate_const");
+			for (i = 0; i < dbitrate_cnt; ++i)
+				print_uint(PRINT_JSON, NULL, NULL,
+					   dbitrate_const[i]);
+			close_json_array(PRINT_JSON, NULL);
+		} else {
+			fprintf(f, "\n	  dbitrate %u", dbitrate);
+			fprintf(f, "\n	     [");
+
+			for (i = 0; i < dbitrate_cnt - 1; ++i) {
+				/* This will keep lines below 80 signs */
+				if (!(i % 6) && i)
+					fprintf(f, "\n	      ");
+
+				fprintf(f, "%8u, ", dbitrate_const[i]);
+			}
 
-		for (i = 0; i < dbitrate_cnt - 1; ++i) {
-			/* This will keep lines below 80 signs */
 			if (!(i % 6) && i)
 				fprintf(f, "\n	      ");
-
-			fprintf(f, "%8u, ", dbitrate_const[i]);
+			fprintf(f, "%8u ]", dbitrate_const[i]);
 		}
-
-		if (!(i % 6) && i)
-			fprintf(f, "\n	      ");
-		fprintf(f, "%8u ]", dbitrate_const[i]);
 	}
 
 	if (tb[IFLA_CAN_TERMINATION_CONST] && tb[IFLA_CAN_TERMINATION]) {
 		__u16 *trm = RTA_DATA(tb[IFLA_CAN_TERMINATION]);
 		__u16 *trm_const = RTA_DATA(tb[IFLA_CAN_TERMINATION_CONST]);
 		int trm_cnt = RTA_PAYLOAD(tb[IFLA_CAN_TERMINATION_CONST]) /
-			      sizeof(*trm_const);
+			sizeof(*trm_const);
 		int i;
 
-		fprintf(f, "\n	  termination %hu [ ", *trm);
+		if (is_json_context()) {
+			print_hu(PRINT_JSON, "termination", NULL, *trm);
+			open_json_array(PRINT_JSON, "termination_const");
+			for (i = 0; i < trm_cnt; ++i)
+				print_hu(PRINT_JSON, NULL, NULL, trm_const[i]);
+			close_json_array(PRINT_JSON, NULL);
+		} else {
+			fprintf(f, "\n	  termination %hu [ ", *trm);
 
-		for (i = 0; i < trm_cnt - 1; ++i)
-			fprintf(f, "%hu, ", trm_const[i]);
+			for (i = 0; i < trm_cnt - 1; ++i)
+				fprintf(f, "%hu, ", trm_const[i]);
 
-		fprintf(f, "%hu ]", trm_const[i]);
+			fprintf(f, "%hu ]", trm_const[i]);
+		}
 	}
 
 	if (tb[IFLA_CAN_CLOCK]) {
 		struct can_clock *clock = RTA_DATA(tb[IFLA_CAN_CLOCK]);
 
-		fprintf(f, "\n	  clock %d", clock->freq);
+		print_int(PRINT_ANY,
+			  "clock",
+			  "\n	  clock %d",
+			  clock->freq);
 	}
 
 }
@@ -431,17 +556,32 @@ static void can_print_xstats(struct link_util *lu,
 
 	if (xstats && RTA_PAYLOAD(xstats) == sizeof(*stats)) {
 		stats = RTA_DATA(xstats);
-		fprintf(f, "\n	  re-started bus-errors arbit-lost "
-			"error-warn error-pass bus-off");
-		fprintf(f, "\n	  %-10d %-10d %-10d %-10d %-10d %-10d",
-			stats->restarts, stats->bus_error,
-			stats->arbitration_lost, stats->error_warning,
-			stats->error_passive, stats->bus_off);
+
+		if (is_json_context()) {
+			print_int(PRINT_JSON, "restarts",
+				  NULL, stats->restarts);
+			print_int(PRINT_JSON, "bus_error",
+				  NULL, stats->bus_error);
+			print_int(PRINT_JSON, "arbitration_lost",
+				  NULL, stats->arbitration_lost);
+			print_int(PRINT_JSON, "error_warning",
+				  NULL, stats->error_warning);
+			print_int(PRINT_JSON, "error_passive",
+				  NULL, stats->error_passive);
+			print_int(PRINT_JSON, "bus_off", NULL, stats->bus_off);
+		} else {
+			fprintf(f, "\n	  re-started bus-errors arbit-lost "
+				"error-warn error-pass bus-off");
+			fprintf(f, "\n	  %-10d %-10d %-10d %-10d %-10d %-10d",
+				stats->restarts, stats->bus_error,
+				stats->arbitration_lost, stats->error_warning,
+				stats->error_passive, stats->bus_off);
+		}
 	}
 }
 
 static void can_print_help(struct link_util *lu, int argc, char **argv,
-	FILE *f)
+			   FILE *f)
 {
 	print_usage(f);
 }
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH iproute2 json v2 13/27] ip: iplink_geneve.c: add json output support
  2017-08-17 17:35 [PATCH iproute2 json v2 00/27] ip: add -json support to 'ip link show' Julien Fortin
                   ` (11 preceding siblings ...)
  2017-08-17 17:35 ` [PATCH iproute2 json v2 12/27] ip: iplink_can.c: " Julien Fortin
@ 2017-08-17 17:36 ` Julien Fortin
  2017-08-17 17:36 ` [PATCH iproute2 json v2 14/27] ip: iplink_ipoib.c: " Julien Fortin
                   ` (15 subsequent siblings)
  28 siblings, 0 replies; 33+ messages in thread
From: Julien Fortin @ 2017-08-17 17:36 UTC (permalink / raw)
  To: netdev; +Cc: roopa, nikolay, dsa, Julien Fortin

From: Julien Fortin <julien@cumulusnetworks.com>

Schema:
{
    "id": {
        "type": "uint",
        "attr": "IFLA_GENEVE_ID"
    },
    "remote": {
        "type": "string",
        "attr": "IFLA_GENEVE_REMOTE"
    },
    "remote6": {
        "type": "string",
        "attr": "IFLA_GENEVE_REMOTE6"
    },
    "ttl": {
        "type": "int",
        "attr": "IFLA_GENEVE_TTL"
    },
    "tos": {
        "type": "string",
        "attr": "IFLA_GENEVE_TOS"
    },
    "label": {
        "type": "string",
        "attr": "IFLA_GENEVE_LABEL"
    },
    "port": {
        "type": "uint",
        "attr": "IFLA_GENEVE_PORT"
    },
    "collect_metadata": {
        "type": "bool",
        "attr": "IFLA_GENEVE_COLLECT_METADATA"
    },
    "udp_csum": {
        "type": "bool",
        "attr": "IFLA_GENEVE_UDP_CSUM"
    },
    "udp_zero_csum6_tx": {
        "type": "bool",
        "attr": "IFLA_GENEVE_UDP_ZERO_CSUM6_TX"
    },
    "udp_zero_csum6_rx": {
        "type": "bool",
        "attr": "IFLA_GENEVE_UDP_ZERO_CSUM6_RX"
    }
}

Signed-off-by: Julien Fortin <julien@cumulusnetworks.com>
---
 ip/iplink_geneve.c | 86 +++++++++++++++++++++++++++++++++++++++---------------
 1 file changed, 63 insertions(+), 23 deletions(-)

diff --git a/ip/iplink_geneve.c b/ip/iplink_geneve.c
index 594a3e59..f0f1d1c7 100644
--- a/ip/iplink_geneve.c
+++ b/ip/iplink_geneve.c
@@ -237,22 +237,28 @@ static void geneve_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 		return;
 
 	vni = rta_getattr_u32(tb[IFLA_GENEVE_ID]);
-	fprintf(f, "id %u ", vni);
+	print_uint(PRINT_ANY, "id", "id %u ", vni);
 
 	if (tb[IFLA_GENEVE_REMOTE]) {
 		__be32 addr = rta_getattr_u32(tb[IFLA_GENEVE_REMOTE]);
 
 		if (addr)
-			fprintf(f, "remote %s ",
-				format_host(AF_INET, 4, &addr));
+			print_string(PRINT_ANY,
+				     "remote",
+				     "remote %s ",
+				     format_host(AF_INET, 4, &addr));
 	} else if (tb[IFLA_GENEVE_REMOTE6]) {
 		struct in6_addr addr;
 
 		memcpy(&addr, RTA_DATA(tb[IFLA_GENEVE_REMOTE6]), sizeof(struct in6_addr));
 		if (!IN6_IS_ADDR_UNSPECIFIED(&addr)) {
 			if (!IN6_IS_ADDR_MULTICAST(&addr))
-				fprintf(f, "remote %s ",
-					format_host(AF_INET6, sizeof(struct in6_addr), &addr));
+				print_string(PRINT_ANY,
+					     "remote6",
+					     "remote %s ",
+					     format_host(AF_INET6,
+							 sizeof(struct in6_addr),
+							 &addr));
 		}
 	}
 
@@ -260,47 +266,81 @@ static void geneve_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 		__u8 ttl = rta_getattr_u8(tb[IFLA_GENEVE_TTL]);
 
 		if (ttl)
-			fprintf(f, "ttl %d ", ttl);
+			print_int(PRINT_ANY, "ttl", "ttl %d ", ttl);
 	}
 
 	if (tb[IFLA_GENEVE_TOS] &&
 	    (tos = rta_getattr_u8(tb[IFLA_GENEVE_TOS]))) {
-		if (tos == 1)
-			fprintf(f, "tos inherit ");
-		else
-			fprintf(f, "tos %#x ", tos);
+		if (is_json_context()) {
+			print_0xhex(PRINT_JSON, "tos", "%#x", tos);
+		} else {
+			if (tos == 1) {
+				print_string(PRINT_FP,
+					     "tos",
+					     "tos %s ",
+					     "inherit");
+			} else {
+				fprintf(f, "tos %#x ", tos);
+			}
+		}
 	}
 
 	if (tb[IFLA_GENEVE_LABEL]) {
 		__u32 label = rta_getattr_u32(tb[IFLA_GENEVE_LABEL]);
 
 		if (label)
-			fprintf(f, "flowlabel %#x ", ntohl(label));
+			print_0xhex(PRINT_ANY,
+				    "label",
+				    "flowlabel %#x ",
+				    ntohl(label));
 	}
 
 	if (tb[IFLA_GENEVE_PORT])
-		fprintf(f, "dstport %u ",
-			rta_getattr_be16(tb[IFLA_GENEVE_PORT]));
+		print_uint(PRINT_ANY,
+			   "port",
+			   "dstport %u ",
+			   rta_getattr_be16(tb[IFLA_GENEVE_PORT]));
 
 	if (tb[IFLA_GENEVE_COLLECT_METADATA])
-		fputs("external ", f);
+		print_bool(PRINT_ANY, "collect_metadata", "external ", true);
 
 	if (tb[IFLA_GENEVE_UDP_CSUM]) {
-		if (!rta_getattr_u8(tb[IFLA_GENEVE_UDP_CSUM]))
-			fputs("no", f);
-		fputs("udpcsum ", f);
+		if (is_json_context()) {
+			print_bool(PRINT_JSON,
+				   "udp_csum",
+				   NULL,
+				   rta_getattr_u8(tb[IFLA_GENEVE_UDP_CSUM]));
+		} else {
+			if (!rta_getattr_u8(tb[IFLA_GENEVE_UDP_CSUM]))
+				fputs("no", f);
+			fputs("udpcsum ", f);
+		}
 	}
 
 	if (tb[IFLA_GENEVE_UDP_ZERO_CSUM6_TX]) {
-		if (!rta_getattr_u8(tb[IFLA_GENEVE_UDP_ZERO_CSUM6_TX]))
-			fputs("no", f);
-		fputs("udp6zerocsumtx ", f);
+		if (is_json_context()) {
+			print_bool(PRINT_JSON,
+				   "udp_zero_csum6_tx",
+				   NULL,
+				   rta_getattr_u8(tb[IFLA_GENEVE_UDP_ZERO_CSUM6_TX]));
+		} else {
+			if (!rta_getattr_u8(tb[IFLA_GENEVE_UDP_ZERO_CSUM6_TX]))
+				fputs("no", f);
+			fputs("udp6zerocsumtx ", f);
+		}
 	}
 
 	if (tb[IFLA_GENEVE_UDP_ZERO_CSUM6_RX]) {
-		if (!rta_getattr_u8(tb[IFLA_GENEVE_UDP_ZERO_CSUM6_RX]))
-			fputs("no", f);
-		fputs("udp6zerocsumrx ", f);
+		if (is_json_context()) {
+			print_bool(PRINT_JSON,
+				   "udp_zero_csum6_rx",
+				   NULL,
+				   rta_getattr_u8(tb[IFLA_GENEVE_UDP_ZERO_CSUM6_RX]));
+		} else {
+			if (!rta_getattr_u8(tb[IFLA_GENEVE_UDP_ZERO_CSUM6_RX]))
+				fputs("no", f);
+			fputs("udp6zerocsumrx ", f);
+		}
 	}
 }
 
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH iproute2 json v2 14/27] ip: iplink_ipoib.c: add json output support
  2017-08-17 17:35 [PATCH iproute2 json v2 00/27] ip: add -json support to 'ip link show' Julien Fortin
                   ` (12 preceding siblings ...)
  2017-08-17 17:36 ` [PATCH iproute2 json v2 13/27] ip: iplink_geneve.c: " Julien Fortin
@ 2017-08-17 17:36 ` Julien Fortin
  2017-08-17 17:36 ` [PATCH iproute2 json v2 15/27] ip: iplink_ipvlan.c: " Julien Fortin
                   ` (14 subsequent siblings)
  28 siblings, 0 replies; 33+ messages in thread
From: Julien Fortin @ 2017-08-17 17:36 UTC (permalink / raw)
  To: netdev; +Cc: roopa, nikolay, dsa, Julien Fortin

From: Julien Fortin <julien@cumulusnetworks.com>

Schema:
{
    "key": {
        "type": "string",
        "attr": "IFLA_IPOIB_PKEY"
    },
    "mode": {
        "type": "string",
        "attr": "IFLA_IPOIB_PKEY"
    },
    "umcast": {
        "type": "string",
        "attr": "IFLA_IPOIB_UMCAST"
    }
}

Signed-off-by: Julien Fortin <julien@cumulusnetworks.com>
---
 ip/iplink_ipoib.c | 30 +++++++++++++++++++++++++-----
 1 file changed, 25 insertions(+), 5 deletions(-)

diff --git a/ip/iplink_ipoib.c b/ip/iplink_ipoib.c
index 86dc65ca..e69bda0e 100644
--- a/ip/iplink_ipoib.c
+++ b/ip/iplink_ipoib.c
@@ -91,23 +91,43 @@ static void ipoib_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 	    RTA_PAYLOAD(tb[IFLA_IPOIB_PKEY]) < sizeof(__u16))
 		return;
 
-	fprintf(f, "pkey  %#.4x ", rta_getattr_u16(tb[IFLA_IPOIB_PKEY]));
+	__u16 pkey = rta_getattr_u16(tb[IFLA_IPOIB_PKEY]);
+
+	if (is_json_context()) {
+		SPRINT_BUF(b1);
+
+		snprintf(b1, sizeof(b1), "%#.4x", pkey);
+		print_string(PRINT_JSON, "key", NULL, b1);
+	} else {
+		fprintf(f, "pkey  %#.4x ", pkey);
+	}
 
 	if (!tb[IFLA_IPOIB_MODE] ||
 	    RTA_PAYLOAD(tb[IFLA_IPOIB_MODE]) < sizeof(__u16))
 		return;
 
 	mode = rta_getattr_u16(tb[IFLA_IPOIB_MODE]);
-	fprintf(f, "mode  %s ",
+
+	const char *mode_str =
 		mode == IPOIB_MODE_DATAGRAM ? "datagram" :
-		mode == IPOIB_MODE_CONNECTED ? "connected" :
-		"unknown");
+		mode == IPOIB_MODE_CONNECTED ? "connected" : "unknown";
+
+	print_string(PRINT_ANY, "mode", "mode  %s ", mode_str);
 
 	if (!tb[IFLA_IPOIB_UMCAST] ||
 	    RTA_PAYLOAD(tb[IFLA_IPOIB_UMCAST]) < sizeof(__u16))
 		return;
 
-	fprintf(f, "umcast  %.4x ", rta_getattr_u16(tb[IFLA_IPOIB_UMCAST]));
+	__u16 umcast = rta_getattr_u16(tb[IFLA_IPOIB_UMCAST]);
+
+	if (is_json_context()) {
+		SPRINT_BUF(b1);
+
+		snprintf(b1, sizeof(b1), "%.4x", umcast);
+		print_string(PRINT_JSON, "umcast", NULL, b1);
+	} else {
+		fprintf(f, "umcast  %.4x ", umcast);
+	}
 }
 
 static void ipoib_print_help(struct link_util *lu, int argc, char **argv,
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH iproute2 json v2 15/27] ip: iplink_ipvlan.c: add json output support
  2017-08-17 17:35 [PATCH iproute2 json v2 00/27] ip: add -json support to 'ip link show' Julien Fortin
                   ` (13 preceding siblings ...)
  2017-08-17 17:36 ` [PATCH iproute2 json v2 14/27] ip: iplink_ipoib.c: " Julien Fortin
@ 2017-08-17 17:36 ` Julien Fortin
  2017-08-17 17:36 ` [PATCH iproute2 json v2 16/27] ip: iplink_vrf.c: " Julien Fortin
                   ` (13 subsequent siblings)
  28 siblings, 0 replies; 33+ messages in thread
From: Julien Fortin @ 2017-08-17 17:36 UTC (permalink / raw)
  To: netdev; +Cc: roopa, nikolay, dsa, Julien Fortin

From: Julien Fortin <julien@cumulusnetworks.com>

Schema:
{
    "mode": {
        "type": "string",
        "attr": "IFLA_IPVLAN_MODE"
    }
}

Signed-off-by: Julien Fortin <julien@cumulusnetworks.com>
---
 ip/iplink_ipvlan.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/ip/iplink_ipvlan.c b/ip/iplink_ipvlan.c
index f7735f3a..9f48309e 100644
--- a/ip/iplink_ipvlan.c
+++ b/ip/iplink_ipvlan.c
@@ -68,11 +68,11 @@ static void ipvlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 	if (tb[IFLA_IPVLAN_MODE]) {
 		if (RTA_PAYLOAD(tb[IFLA_IPVLAN_MODE]) == sizeof(__u16)) {
 			__u16 mode = rta_getattr_u16(tb[IFLA_IPVLAN_MODE]);
-
-			fprintf(f, " mode %s ",
-				mode == IPVLAN_MODE_L2 ? "l2" :
+			const char *mode_str = mode == IPVLAN_MODE_L2 ? "l2" :
 				mode == IPVLAN_MODE_L3 ? "l3" :
-				mode == IPVLAN_MODE_L3S ? "l3s" : "unknown");
+				mode == IPVLAN_MODE_L3S ? "l3s" : "unknown";
+
+			print_string(PRINT_ANY, "mode", " mode %s ", mode_str);
 		}
 	}
 }
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH iproute2 json v2 16/27] ip: iplink_vrf.c: add json output support
  2017-08-17 17:35 [PATCH iproute2 json v2 00/27] ip: add -json support to 'ip link show' Julien Fortin
                   ` (14 preceding siblings ...)
  2017-08-17 17:36 ` [PATCH iproute2 json v2 15/27] ip: iplink_ipvlan.c: " Julien Fortin
@ 2017-08-17 17:36 ` Julien Fortin
  2017-08-17 17:36 ` [PATCH iproute2 json v2 17/27] ip: iplink_vxlan.c: " Julien Fortin
                   ` (12 subsequent siblings)
  28 siblings, 0 replies; 33+ messages in thread
From: Julien Fortin @ 2017-08-17 17:36 UTC (permalink / raw)
  To: netdev; +Cc: roopa, nikolay, dsa, Julien Fortin

From: Julien Fortin <julien@cumulusnetworks.com>

Schema:
{
    "table": {
        "type": "uint",
        "attr": "IFLA_VRF_TABLE"
    }
}

Signed-off-by: Julien Fortin <julien@cumulusnetworks.com>
---
 ip/iplink_vrf.c | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/ip/iplink_vrf.c b/ip/iplink_vrf.c
index 917630e8..f13b1d24 100644
--- a/ip/iplink_vrf.c
+++ b/ip/iplink_vrf.c
@@ -62,7 +62,10 @@ static void vrf_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 		return;
 
 	if (tb[IFLA_VRF_TABLE])
-		fprintf(f, "table %u ", rta_getattr_u32(tb[IFLA_VRF_TABLE]));
+		print_uint(PRINT_ANY,
+			   "table",
+			   "table %u ",
+			   rta_getattr_u32(tb[IFLA_VRF_TABLE]));
 }
 
 static void vrf_slave_print_opt(struct link_util *lu, FILE *f,
@@ -72,13 +75,15 @@ static void vrf_slave_print_opt(struct link_util *lu, FILE *f,
 		return;
 
 	if (tb[IFLA_VRF_PORT_TABLE]) {
-		fprintf(f, "table %u ",
-			rta_getattr_u32(tb[IFLA_VRF_PORT_TABLE]));
+		print_uint(PRINT_ANY,
+			   "table",
+			   "table %u ",
+			   rta_getattr_u32(tb[IFLA_VRF_PORT_TABLE]));
 	}
 }
 
 static void vrf_print_help(struct link_util *lu, int argc, char **argv,
-			      FILE *f)
+			   FILE *f)
 {
 	vrf_explain(f);
 }
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH iproute2 json v2 17/27] ip: iplink_vxlan.c: add json output support
  2017-08-17 17:35 [PATCH iproute2 json v2 00/27] ip: add -json support to 'ip link show' Julien Fortin
                   ` (15 preceding siblings ...)
  2017-08-17 17:36 ` [PATCH iproute2 json v2 16/27] ip: iplink_vrf.c: " Julien Fortin
@ 2017-08-17 17:36 ` Julien Fortin
  2017-08-17 17:36 ` [PATCH iproute2 json v2 18/27] ip: iplink_xdp.c: " Julien Fortin
                   ` (11 subsequent siblings)
  28 siblings, 0 replies; 33+ messages in thread
From: Julien Fortin @ 2017-08-17 17:36 UTC (permalink / raw)
  To: netdev; +Cc: roopa, nikolay, dsa, Julien Fortin

From: Julien Fortin <julien@cumulusnetworks.com>

Schema:
{
    "id": {
        "type": "uint",
        "attr": "IFLA_VXLAN_ID"
    },
    "group": {
        "type": "string",
        "attr": "IFLA_VXLAN_GROUP"
    },
    "remote": {
        "type": "string",
        "attr": "IFLA_VXLAN_GROUP"
    },
    "group6": {
        "type": "string",
        "attr": "IFLA_VXLAN_GROUP6"
    },
    "remote6": {
        "type": "string",
        "attr": "IFLA_VXLAN_GROUP6"
    },
    "local": {
        "type": "string",
        "attr": "IFLA_VXLAN_LOCAL"
    },
    "local6": {
        "type": "string",
        "attr": "IFLA_VXLAN_LOCAL6"
    },
    "link": {
        "type": "string",
        "attr": "IFLA_VXLAN_LINK",
        "mutually_exclusive": {
            "link_index": {
                "type": "uint",
                "comment": "if not ifname for ifindex"
            }
        }
    },
    "port_range": {
        "type": "dict",
        "attr": "IFLA_VXLAN_PORT_RANGE",
        "dict": {
            "low": {
                "type": "uint"
            },
            "high": {
                "type": "uint"
            }
        }
    },
    "port": {
        "type": "uint",
        "attr": "IFLA_VXLAN_PORT"
    },
    "learning": {
        "type": "bool",
        "attr": "IFLA_VXLAN_LEARNING"
    },
    "proxy": {
        "type": "bool",
        "attr": "IFLA_VXLAN_PROXY"
    },
    "rsc": {
        "type": "bool",
        "attr": "IFLA_VXLAN_RSC"
    },
    "l2miss": {
        "type": "bool",
        "attr": "IFLA_VXLAN_L2MISS"
    },
    "l3miss": {
        "type": "bool",
        "attr": "IFLA_VXLAN_L3MISS"
    },
    "tos": {
        "type": "string",
        "attr": "IFLA_VXLAN_TOS"
    },
    "ttl": {
        "type": "int",
        "attr": "IFLA_VXLAN_TTL"
    },
    "label": {
        "type": "string",
        "attr": "IFLA_VXLAN_LABEL"
    },
    "ageing": {
        "type": "uint",
        "attr": "IFLA_VXLAN_AGEING"
    },
    "limit": {
        "type": "uint",
        "attr": "IFLA_VXLAN_LIMIT"
    },
    "udp_csum": {
        "type": "bool",
        "attr": "IFLA_VXLAN_UDP_CSUM"
    },
    "udp_zero_csum6_tx": {
        "type": "bool",
        "attr": "IFLA_VXLAN_UDP_ZERO_CSUM6_TX"
    },
    "udp_zero_csum6_rx": {
        "type": "bool",
        "attr": "IFLA_VXLAN_UDP_ZERO_CSUM6_RX"
    },
    "remcsum_tx": {
        "type": "bool",
        "attr": "IFLA_VXLAN_REMCSUM_TX"
    },
    "remcsum_rx": {
        "type": "bool",
        "attr": "IFLA_VXLAN_REMCSUM_RX"
    },
    "collect_metadata": {
        "type": "bool",
        "attr": "IFLA_VXLAN_COLLECT_METADATA"
    },
    "gbp": {
        "type": "bool",
        "attr": "IFLA_VXLAN_GBP"
    },
    "gpe": {
        "type": "bool",
        "attr": "IFLA_VXLAN_GPE"
    }
}

$ ip link add name vxlan42 type vxlan id 42 dev eth0 remote 203.0.113.6
local 192.0.2.1 dstport 4789
$ ip link add name vxlan43 type vxlan id 43 dev eth0 group 239.0.0.1
dstport 4789
$ ip -details -json link show
[{
        "ifindex": 17,
        "ifname": "vxlan42",
        "flags": ["BROADCAST","MULTICAST"],
        "mtu": 1450,
        "qdisc": "noop",
        "operstate": "DOWN",
        "linkmode": "DEFAULT",
        "group": "default",
        "link_type": "ether",
        "address": "b2:92:0e:1a:c6:42",
        "broadcast": "ff:ff:ff:ff:ff:ff",
        "promiscuity": 0,
        "linkinfo": {
            "info_kind": "vxlan",
            "info_data": {
                "id": 42,
                "remote": "203.0.113.6",
                "local": "192.0.2.1",
                "link": "eth0",
                "port_range": {
                    "low": 0,
                    "high": 0
                },
                "port": 4789,
                "learning": true,
                "ttl": 0,
                "ageing": 300,
                "udp_csum": false,
                "udp_zero_csum6_tx": false,
                "udp_zero_csum6_rx": false
            }
        },
        "inet6_addr_gen_mode": "eui64",
        "num_tx_queues": 1,
        "num_rx_queues": 1,
        "gso_max_size": 65536,
        "gso_max_segs": 65535
    },{
        "ifindex": 18,
        "ifname": "vxlan43",
        "flags": ["BROADCAST","MULTICAST"],
        "mtu": 1450,
        "qdisc": "noop",
        "operstate": "DOWN",
        "linkmode": "DEFAULT",
        "group": "default",
        "link_type": "ether",
        "address": "c6:51:4d:7f:f9:2f",
        "broadcast": "ff:ff:ff:ff:ff:ff",
        "promiscuity": 0,
        "linkinfo": {
            "info_kind": "vxlan",
            "info_data": {
                "id": 43,
                "group": "239.0.0.1",
                "link": "eth0",
                "port_range": {
                    "low": 0,
                    "high": 0
                },
                "port": 4789,
                "learning": true,
                "ttl": 0,
                "ageing": 300,
                "udp_csum": false,
                "udp_zero_csum6_tx": false,
                "udp_zero_csum6_rx": false
            }
        },
        "inet6_addr_gen_mode": "eui64",
        "num_tx_queues": 1,
        "num_rx_queues": 1,
        "gso_max_size": 65536,
        "gso_max_segs": 65535
    }
]

Signed-off-by: Julien Fortin <julien@cumulusnetworks.com>
---
 ip/iplink_vxlan.c | 161 +++++++++++++++++++++++++++++++++++++-----------------
 1 file changed, 112 insertions(+), 49 deletions(-)

diff --git a/ip/iplink_vxlan.c b/ip/iplink_vxlan.c
index 2bd619d4..a0530dda 100644
--- a/ip/iplink_vxlan.c
+++ b/ip/iplink_vxlan.c
@@ -406,18 +406,22 @@ static void vxlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 		return;
 
 	vni = rta_getattr_u32(tb[IFLA_VXLAN_ID]);
-	fprintf(f, "id %u ", vni);
+	print_uint(PRINT_ANY, "id", "id %u ", vni);
 
 	if (tb[IFLA_VXLAN_GROUP]) {
 		__be32 addr = rta_getattr_u32(tb[IFLA_VXLAN_GROUP]);
 
 		if (addr) {
 			if (IN_MULTICAST(ntohl(addr)))
-				fprintf(f, "group %s ",
-					format_host(AF_INET, 4, &addr));
+				print_string(PRINT_ANY,
+					     "group",
+					     "group %s ",
+					     format_host(AF_INET, 4, &addr));
 			else
-				fprintf(f, "remote %s ",
-					format_host(AF_INET, 4, &addr));
+				print_string(PRINT_ANY,
+					     "remote",
+					     "remote %s ",
+					     format_host(AF_INET, 4, &addr));
 		}
 	} else if (tb[IFLA_VXLAN_GROUP6]) {
 		struct in6_addr addr;
@@ -425,11 +429,19 @@ static void vxlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 		memcpy(&addr, RTA_DATA(tb[IFLA_VXLAN_GROUP6]), sizeof(struct in6_addr));
 		if (!IN6_IS_ADDR_UNSPECIFIED(&addr)) {
 			if (IN6_IS_ADDR_MULTICAST(&addr))
-				fprintf(f, "group %s ",
-					format_host(AF_INET6, sizeof(struct in6_addr), &addr));
+				print_string(PRINT_ANY,
+					     "group6",
+					     "group %s ",
+					     format_host(AF_INET6,
+							 sizeof(struct in6_addr),
+							 &addr));
 			else
-				fprintf(f, "remote %s ",
-					format_host(AF_INET6, sizeof(struct in6_addr), &addr));
+				print_string(PRINT_ANY,
+					     "remote6",
+					     "remote %s ",
+					     format_host(AF_INET6,
+							 sizeof(struct in6_addr),
+							 &addr));
 		}
 	}
 
@@ -437,15 +449,21 @@ static void vxlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 		__be32 addr = rta_getattr_u32(tb[IFLA_VXLAN_LOCAL]);
 
 		if (addr)
-			fprintf(f, "local %s ",
-				format_host(AF_INET, 4, &addr));
+			print_string(PRINT_ANY,
+				     "local",
+				     "local %s ",
+				     format_host(AF_INET, 4, &addr));
 	} else if (tb[IFLA_VXLAN_LOCAL6]) {
 		struct in6_addr addr;
 
 		memcpy(&addr, RTA_DATA(tb[IFLA_VXLAN_LOCAL6]), sizeof(struct in6_addr));
 		if (!IN6_IS_ADDR_UNSPECIFIED(&addr))
-			fprintf(f, "local %s ",
-				format_host(AF_INET6, sizeof(struct in6_addr), &addr));
+			print_string(PRINT_ANY,
+				     "local6",
+				     "local %s ",
+				     format_host(AF_INET6,
+						 sizeof(struct in6_addr),
+						 &addr));
 	}
 
 	if (tb[IFLA_VXLAN_LINK] &&
@@ -453,110 +471,155 @@ static void vxlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 		const char *n = if_indextoname(link, s2);
 
 		if (n)
-			fprintf(f, "dev %s ", n);
+			print_string(PRINT_ANY, "link", "dev %s ", n);
 		else
-			fprintf(f, "dev %u ", link);
+			print_uint(PRINT_ANY, "link_index", "dev %u ", link);
 	}
 
 	if (tb[IFLA_VXLAN_PORT_RANGE]) {
 		const struct ifla_vxlan_port_range *r
 			= RTA_DATA(tb[IFLA_VXLAN_PORT_RANGE]);
-		fprintf(f, "srcport %u %u ", ntohs(r->low), ntohs(r->high));
+		if (is_json_context()) {
+			open_json_object("port_range");
+			print_uint(PRINT_JSON, "low", NULL, ntohs(r->low));
+			print_uint(PRINT_JSON, "high", NULL, ntohs(r->high));
+			close_json_object();
+		} else {
+			fprintf(f, "srcport %u %u ",
+				ntohs(r->low), ntohs(r->high));
+		}
 	}
 
 	if (tb[IFLA_VXLAN_PORT])
-		fprintf(f, "dstport %u ",
-			rta_getattr_be16(tb[IFLA_VXLAN_PORT]));
+		print_uint(PRINT_ANY,
+			   "port",
+			   "dstport %u ",
+			   rta_getattr_be16(tb[IFLA_VXLAN_PORT]));
 
-	if (tb[IFLA_VXLAN_LEARNING] &&
-	    !rta_getattr_u8(tb[IFLA_VXLAN_LEARNING]))
-		fputs("nolearning ", f);
+	if (tb[IFLA_VXLAN_LEARNING]) {
+		__u8 learning = rta_getattr_u8(tb[IFLA_VXLAN_LEARNING]);
+
+		print_bool(PRINT_JSON, "learning", NULL, learning);
+		if (!learning)
+			print_bool(PRINT_FP, NULL, "nolearning ", true);
+	}
 
 	if (tb[IFLA_VXLAN_PROXY] && rta_getattr_u8(tb[IFLA_VXLAN_PROXY]))
-		fputs("proxy ", f);
+		print_bool(PRINT_ANY, "proxy", "proxy ", true);
 
 	if (tb[IFLA_VXLAN_RSC] && rta_getattr_u8(tb[IFLA_VXLAN_RSC]))
-		fputs("rsc ", f);
+		print_bool(PRINT_ANY, "rsc", "rsc ", true);
 
 	if (tb[IFLA_VXLAN_L2MISS] && rta_getattr_u8(tb[IFLA_VXLAN_L2MISS]))
-		fputs("l2miss ", f);
+		print_bool(PRINT_ANY, "l2miss", "l2miss ", true);
 
 	if (tb[IFLA_VXLAN_L3MISS] && rta_getattr_u8(tb[IFLA_VXLAN_L3MISS]))
-		fputs("l3miss ", f);
+		print_bool(PRINT_ANY, "l3miss", "l3miss ", true);
 
 	if (tb[IFLA_VXLAN_TOS] &&
 	    (tos = rta_getattr_u8(tb[IFLA_VXLAN_TOS]))) {
-		if (tos == 1)
-			fprintf(f, "tos inherit ");
-		else
-			fprintf(f, "tos %#x ", tos);
+		if (is_json_context()) {
+			print_0xhex(PRINT_JSON, "tos", "%#x", tos);
+		} else {
+			if (tos == 1)
+				fprintf(f, "tos %s ", "inherit");
+			else
+				fprintf(f, "tos %#x ", tos);
+		}
 	}
 
 	if (tb[IFLA_VXLAN_TTL]) {
 		__u8 ttl = rta_getattr_u8(tb[IFLA_VXLAN_TTL]);
 
 		if (ttl)
-			fprintf(f, "ttl %d ", ttl);
+			print_int(PRINT_ANY, "ttl", "ttl %d ", ttl);
+		else
+			print_int(PRINT_JSON, "ttl", NULL, ttl);
 	}
 
 	if (tb[IFLA_VXLAN_LABEL]) {
 		__u32 label = rta_getattr_u32(tb[IFLA_VXLAN_LABEL]);
 
 		if (label)
-			fprintf(f, "flowlabel %#x ", ntohl(label));
+			print_0xhex(PRINT_ANY,
+				    "label",
+				    "flowlabel %#x ",
+				    ntohl(label));
 	}
 
 	if (tb[IFLA_VXLAN_AGEING]) {
 		__u32 age = rta_getattr_u32(tb[IFLA_VXLAN_AGEING]);
 
 		if (age == 0)
-			fprintf(f, "ageing none ");
+			print_uint(PRINT_ANY, "ageing", "ageing none ", 0);
 		else
-			fprintf(f, "ageing %u ", age);
+			print_uint(PRINT_ANY, "ageing", "ageing %u ", age);
 	}
 
 	if (tb[IFLA_VXLAN_LIMIT] &&
 	    ((maxaddr = rta_getattr_u32(tb[IFLA_VXLAN_LIMIT])) != 0))
-		    fprintf(f, "maxaddr %u ", maxaddr);
+		print_uint(PRINT_ANY, "limit", "maxaddr %u ", maxaddr);
 
 	if (tb[IFLA_VXLAN_UDP_CSUM]) {
-		if (!rta_getattr_u8(tb[IFLA_VXLAN_UDP_CSUM]))
-			fputs("no", f);
-		fputs("udpcsum ", f);
+		__u8 udp_csum = rta_getattr_u8(tb[IFLA_VXLAN_UDP_CSUM]);
+
+		if (is_json_context()) {
+			print_bool(PRINT_ANY, "udp_csum", NULL, udp_csum);
+		} else {
+			if (!udp_csum)
+				fputs("no", f);
+			fputs("udpcsum ", f);
+		}
 	}
 
 	if (tb[IFLA_VXLAN_UDP_ZERO_CSUM6_TX]) {
-		if (!rta_getattr_u8(tb[IFLA_VXLAN_UDP_ZERO_CSUM6_TX]))
-			fputs("no", f);
-		fputs("udp6zerocsumtx ", f);
+		__u8 csum6 = rta_getattr_u8(tb[IFLA_VXLAN_UDP_ZERO_CSUM6_TX]);
+
+		if (is_json_context()) {
+			print_bool(PRINT_ANY,
+				   "udp_zero_csum6_tx", NULL, csum6);
+		} else {
+			if (!csum6)
+				fputs("no", f);
+			fputs("udp6zerocsumtx ", f);
+		}
 	}
 
 	if (tb[IFLA_VXLAN_UDP_ZERO_CSUM6_RX]) {
-		if (!rta_getattr_u8(tb[IFLA_VXLAN_UDP_ZERO_CSUM6_RX]))
-			fputs("no", f);
-		fputs("udp6zerocsumrx ", f);
+		__u8 csum6 = rta_getattr_u8(tb[IFLA_VXLAN_UDP_ZERO_CSUM6_RX]);
+
+		if (is_json_context()) {
+			print_bool(PRINT_ANY,
+				   "udp_zero_csum6_rx",
+				   NULL,
+				   csum6);
+		} else {
+			if (!csum6)
+				fputs("no", f);
+			fputs("udp6zerocsumrx ", f);
+		}
 	}
 
 	if (tb[IFLA_VXLAN_REMCSUM_TX] &&
 	    rta_getattr_u8(tb[IFLA_VXLAN_REMCSUM_TX]))
-		fputs("remcsumtx ", f);
+		print_bool(PRINT_ANY, "remcsum_tx", "remcsumtx ", true);
 
 	if (tb[IFLA_VXLAN_REMCSUM_RX] &&
 	    rta_getattr_u8(tb[IFLA_VXLAN_REMCSUM_RX]))
-		fputs("remcsumrx ", f);
+		print_bool(PRINT_ANY, "remcsum_rx", "remcsumrx ", true);
 
 	if (tb[IFLA_VXLAN_COLLECT_METADATA] &&
 	    rta_getattr_u8(tb[IFLA_VXLAN_COLLECT_METADATA]))
-		fputs("external ", f);
+		print_bool(PRINT_ANY, "collect_metadata", "external ", true);
 
 	if (tb[IFLA_VXLAN_GBP])
-		fputs("gbp ", f);
+		print_bool(PRINT_ANY, "gbp", "gbp ", true);
 	if (tb[IFLA_VXLAN_GPE])
-		fputs("gpe ", f);
+		print_bool(PRINT_ANY, "gpe", "gpe ", true);
 }
 
 static void vxlan_print_help(struct link_util *lu, int argc, char **argv,
-	FILE *f)
+			     FILE *f)
 {
 	print_explain(f);
 }
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH iproute2 json v2 18/27] ip: iplink_xdp.c: add json output support
  2017-08-17 17:35 [PATCH iproute2 json v2 00/27] ip: add -json support to 'ip link show' Julien Fortin
                   ` (16 preceding siblings ...)
  2017-08-17 17:36 ` [PATCH iproute2 json v2 17/27] ip: iplink_vxlan.c: " Julien Fortin
@ 2017-08-17 17:36 ` Julien Fortin
  2017-08-17 17:36 ` [PATCH iproute2 json v2 19/27] ip: ipmacsec.c: " Julien Fortin
                   ` (10 subsequent siblings)
  28 siblings, 0 replies; 33+ messages in thread
From: Julien Fortin @ 2017-08-17 17:36 UTC (permalink / raw)
  To: netdev; +Cc: roopa, nikolay, dsa, Julien Fortin

From: Julien Fortin <julien@cumulusnetworks.com>

Schema
{
    "attached": {
        "type": "uint",
        "attr": "IFLA_XDP_ATTACHED"
    },
    "prog_id": {
        "type": "uint",
        "attr": "IFLA_XDP_PROG_ID"
    }
}

Signed-off-by: Julien Fortin <julien@cumulusnetworks.com>
---
 ip/iplink_xdp.c | 31 ++++++++++++++++++-------------
 1 file changed, 18 insertions(+), 13 deletions(-)

diff --git a/ip/iplink_xdp.c b/ip/iplink_xdp.c
index 9ae9ee5d..3a61076e 100644
--- a/ip/iplink_xdp.c
+++ b/ip/iplink_xdp.c
@@ -16,6 +16,7 @@
 
 #include "xdp.h"
 #include "bpf_util.h"
+#include "ip_common.h"
 
 extern int force;
 
@@ -92,20 +93,24 @@ void xdp_dump(FILE *fp, struct rtattr *xdp)
 		return;
 
 	mode = rta_getattr_u8(tb[IFLA_XDP_ATTACHED]);
-	if (mode == XDP_ATTACHED_NONE)
-		return;
-	else if (mode == XDP_ATTACHED_DRV)
-		fprintf(fp, "xdp");
-	else if (mode == XDP_ATTACHED_SKB)
-		fprintf(fp, "xdpgeneric");
-	else if (mode == XDP_ATTACHED_HW)
-		fprintf(fp, "xdpoffload");
-	else
-		fprintf(fp, "xdp[%u]", mode);
+	if (is_json_context()) {
+		print_uint(PRINT_JSON, "attached", NULL, mode);
+	} else {
+		if (mode == XDP_ATTACHED_NONE)
+			return;
+		else if (mode == XDP_ATTACHED_DRV)
+			fprintf(fp, "xdp");
+		else if (mode == XDP_ATTACHED_SKB)
+			fprintf(fp, "xdpgeneric");
+		else if (mode == XDP_ATTACHED_HW)
+			fprintf(fp, "xdpoffload");
+		else
+			fprintf(fp, "xdp[%u]", mode);
+	}
 
 	if (tb[IFLA_XDP_PROG_ID])
-		fprintf(fp, "/id:%u",
-			rta_getattr_u32(tb[IFLA_XDP_PROG_ID]));
+		print_uint(PRINT_ANY, "prog_id", "/id:%u",
+				   rta_getattr_u32(tb[IFLA_XDP_PROG_ID]));
 
-	fprintf(fp, " ");
+	print_string(PRINT_FP, NULL, "%c", " ");
 }
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH iproute2 json v2 19/27] ip: ipmacsec.c: add json output support
  2017-08-17 17:35 [PATCH iproute2 json v2 00/27] ip: add -json support to 'ip link show' Julien Fortin
                   ` (17 preceding siblings ...)
  2017-08-17 17:36 ` [PATCH iproute2 json v2 18/27] ip: iplink_xdp.c: " Julien Fortin
@ 2017-08-17 17:36 ` Julien Fortin
  2017-08-17 17:36 ` [PATCH iproute2 json v2 20/27] ip: link_gre.c: " Julien Fortin
                   ` (9 subsequent siblings)
  28 siblings, 0 replies; 33+ messages in thread
From: Julien Fortin @ 2017-08-17 17:36 UTC (permalink / raw)
  To: netdev; +Cc: roopa, nikolay, dsa, Julien Fortin

From: Julien Fortin <julien@cumulusnetworks.com>

Schema
{
    "sci": {
        "type": "string",
        "attr": "IFLA_MACSEC_SCI"
    },
    "protect": {
        "type": "string",
        "attr": "IFLA_MACSEC_PROTECT"
    },
    "cipher_suite": {
        "type": "string",
        "attr": "IFLA_MACSEC_CIPHER_SUITE"
    },
    "icv_len": {
        "type": "uint",
        "attr": "IFLA_MACSEC_ICV_LEN"
    },
    "encoding_sa": {
        "type": "uint",
        "attr": "IFLA_MACSEC_ENCODING_SA"
    },
    "validation": {
        "type": "string",
        "attr": "IFLA_MACSEC_VALIDATION"
    },
    "encrypt": {
        "type": "string",
        "attr": "IFLA_MACSEC_ENCRYPT"
    },
    "inc_sci": {
        "type": "string",
        "attr": "IFLA_MACSEC_INC_SCI"
    },
    "es": {
        "type": "string",
        "attr": "IFLA_MACSEC_ES"
    },
    "scb": {
        "type": "string",
        "attr": "IFLA_MACSEC_SCB"
    },
    "replay_protect": {
        "type": "string",
        "attr": "IFLA_MACSEC_REPLAY_PROTECT"
    },
    "window": {
        "type": "int",
        "attr": ""
    }
}

Signed-off-by: Julien Fortin <julien@cumulusnetworks.com>
---
 ip/ipmacsec.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 66 insertions(+), 18 deletions(-)

diff --git a/ip/ipmacsec.c b/ip/ipmacsec.c
index aa89a00f..ecc371a5 100644
--- a/ip/ipmacsec.c
+++ b/ip/ipmacsec.c
@@ -561,9 +561,14 @@ static int validate_secy_dump(struct rtattr **attrs)
 static void print_flag(FILE *f, struct rtattr *attrs[], const char *desc,
 		       int field)
 {
-	if (attrs[field])
-		fprintf(f, "%s %s ", desc,
-			values_on_off[!!rta_getattr_u8(attrs[field])]);
+	if (attrs[field]) {
+		const char *v = values_on_off[!!rta_getattr_u8(attrs[field])];
+
+		if (is_json_context())
+			print_string(PRINT_JSON, desc, NULL, v);
+		else
+			fprintf(f, "%s %s ", desc, v);
+	}
 }
 
 #define DEFAULT_CIPHER_NAME "GCM-AES-128"
@@ -1017,8 +1022,16 @@ static void macsec_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 		return;
 
 	if (tb[IFLA_MACSEC_SCI]) {
-		fprintf(f, "sci %016llx ",
-			ntohll(rta_getattr_u64(tb[IFLA_MACSEC_SCI])));
+		if (is_json_context()) {
+			SPRINT_BUF(b1);
+
+			snprintf(b1, sizeof(b1), "%016llx",
+				 ntohll(rta_getattr_u64(tb[IFLA_MACSEC_SCI])));
+			print_string(PRINT_JSON, "sci", NULL, b1);
+		} else {
+			fprintf(f, "sci %016llx ",
+				ntohll(rta_getattr_u64(tb[IFLA_MACSEC_SCI])));
+		}
 	}
 
 	print_flag(f, tb, "protect", IFLA_MACSEC_PROTECT);
@@ -1026,35 +1039,70 @@ static void macsec_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 	if (tb[IFLA_MACSEC_CIPHER_SUITE]) {
 		__u64 csid = rta_getattr_u64(tb[IFLA_MACSEC_CIPHER_SUITE]);
 
-		fprintf(f, "cipher %s ", cs_id_to_name(csid));
+		print_string(PRINT_ANY,
+			     "cipher_suite",
+			     "cipher %s ",
+			     cs_id_to_name(csid));
 	}
 
 	if (tb[IFLA_MACSEC_ICV_LEN]) {
-		fprintf(f, "icvlen %hhu ",
-			rta_getattr_u8(tb[IFLA_MACSEC_ICV_LEN]));
+		if (is_json_context()) {
+			char b2[4];
+
+			snprintf(b2, sizeof(b2), "%hhu",
+				 rta_getattr_u8(tb[IFLA_MACSEC_ICV_LEN]));
+			print_uint(PRINT_JSON, "icv_len", NULL, atoi(b2));
+		} else {
+			fprintf(f, "icvlen %hhu ",
+				rta_getattr_u8(tb[IFLA_MACSEC_ICV_LEN]));
+		}
 	}
 
 	if (tb[IFLA_MACSEC_ENCODING_SA]) {
-		fprintf(f, "encodingsa %hhu ",
-			rta_getattr_u8(tb[IFLA_MACSEC_ENCODING_SA]));
+		if (is_json_context()) {
+			char b2[4];
+
+			snprintf(b2, sizeof(b2), "%hhu",
+				 rta_getattr_u8(tb[IFLA_MACSEC_ENCODING_SA]));
+			print_uint(PRINT_JSON, "encoding_sa", NULL, atoi(b2));
+		} else {
+			fprintf(f, "encodingsa %hhu ",
+				rta_getattr_u8(tb[IFLA_MACSEC_ENCODING_SA]));
+		}
 	}
 
 	if (tb[IFLA_MACSEC_VALIDATION]) {
 		__u8 val = rta_getattr_u8(tb[IFLA_MACSEC_VALIDATION]);
 
-		fprintf(f, "validate %s ", VALIDATE_STR[val]);
+		print_string(PRINT_ANY,
+			     "validation",
+			     "validate %s ",
+			     VALIDATE_STR[val]);
+	}
+
+	const char *inc_sci, *es, *replay;
+
+	if (is_json_context()) {
+		inc_sci = "inc_sci";
+		replay = "replay_protect";
+		es = "es";
+	} else {
+		inc_sci = "send_sci";
+		es = "end_station";
+		replay = "replay";
 	}
 
 	print_flag(f, tb, "encrypt", IFLA_MACSEC_ENCRYPT);
-	print_flag(f, tb, "send_sci", IFLA_MACSEC_INC_SCI);
-	print_flag(f, tb, "end_station", IFLA_MACSEC_ES);
+	print_flag(f, tb, inc_sci, IFLA_MACSEC_INC_SCI);
+	print_flag(f, tb, es, IFLA_MACSEC_ES);
 	print_flag(f, tb, "scb", IFLA_MACSEC_SCB);
+	print_flag(f, tb, replay, IFLA_MACSEC_REPLAY_PROTECT);
 
-	print_flag(f, tb, "replay", IFLA_MACSEC_REPLAY_PROTECT);
-	if (tb[IFLA_MACSEC_WINDOW]) {
-		fprintf(f, "window %d ",
-			rta_getattr_u32(tb[IFLA_MACSEC_WINDOW]));
-	}
+	if (tb[IFLA_MACSEC_WINDOW])
+		print_int(PRINT_ANY,
+			  "window",
+			  "window %d ",
+			  rta_getattr_u32(tb[IFLA_MACSEC_WINDOW]));
 }
 
 static bool check_txsc_flags(bool es, bool scb, bool sci)
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH iproute2 json v2 20/27] ip: link_gre.c: add json output support
  2017-08-17 17:35 [PATCH iproute2 json v2 00/27] ip: add -json support to 'ip link show' Julien Fortin
                   ` (18 preceding siblings ...)
  2017-08-17 17:36 ` [PATCH iproute2 json v2 19/27] ip: ipmacsec.c: " Julien Fortin
@ 2017-08-17 17:36 ` Julien Fortin
  2017-08-17 17:36 ` [PATCH iproute2 json v2 21/27] ip: link_gre6.c: " Julien Fortin
                   ` (8 subsequent siblings)
  28 siblings, 0 replies; 33+ messages in thread
From: Julien Fortin @ 2017-08-17 17:36 UTC (permalink / raw)
  To: netdev; +Cc: roopa, nikolay, dsa, Julien Fortin

From: Julien Fortin <julien@cumulusnetworks.com>

Schema
{
    "external": {
        "type": "bool",
        "comment": "!tb[IFLA_GRE_COLLECT_METADATA]"
    },
    "remote": {
        "type": "string",
        "attr": "IFLA_GRE_REMOTE"
    },
    "local": {
        "type": "string",
        "attr": "IFLA_GRE_LOCAL"
    },
    "link": {
        "type": "string",
        "attr": "IFLA_GRE_LINK",
        "mutually_exclusive": {
            "link_index": {
                "type": "uint",
            }
        }
    },
    "ttl": {
        "type": "int",
        "attr": "IFLA_GRE_TTL"
    },
    "tos": {
        "type": "string",
        "attr": "IFLA_GRE_TOS"
    },
    "pmtudisc": {
        "type": "bool",
        "attr": "IFLA_GRE_PMTUDISC"
    },
    "ikey": {
        "type": "string",
        "attr": "IFLA_GRE_IKEY"
    },
    "okey": {
        "type": "string",
        "attr": "IFLA_GRE_OKEY"
    },
    "iseq": {
        "type": "bool",
        "attr": "IFLA_GRE_IFLAGS & GRE_SEQ"
    },
    "oseq": {
        "type": "bool",
        "attr": "IFLA_GRE_OFLAGS & GRE_SEQ"
    },
    "icsum": {
        "type": "bool",
        "attr": "IFLA_GRE_IFLAGS & GRE_CSUM"
    },
    "ocsum": {
        "type": "bool",
        "attr": "IFLA_GRE_OFLAGS & GRE_CSUM"
    },
    "ignore_df": {
        "type": "bool",
        "attr": "IFLA_GRE_IGNORE_DF"
    },
    "encap": {
        "type": "dict",
        "attr": "IFLA_GRE_ENCAP_TYPE != TUNNEL_ENCAP_NONE",
        "dict": {
            "type": {
                "type": "string",
                "attr": "IFLA_GRE_ENCAP_TYPE"
            },
            "sport": {
                "type": "uint",
                "attr": "IFLA_GRE_ENCAP_SPORT"
            },
            "dport": {
                "type": "uint",
                "attr": "IFLA_GRE_ENCAP_DPORT"
            },
            "csum": {
                "type": "bool",
                "attr": "TUNNEL_ENCAP_FLAG_CSUM"
            },
            "csum6": {
                "type": "bool",
                "attr": "TUNNEL_ENCAP_FLAG_CSUM6"
            },
            "remcsum": {
                "type": "bool",
                "attr": "TUNNEL_ENCAP_FLAG_REMCSUM"
            }
        }
    }
}

$ ip link show
$ ip tunnel add tun42 mode gre local 192.0.2.42 remote 203.0.113.42 key 42
$ ip link show
10: gre0@NONE: <NOARP> mtu 1476 qdisc noop state DOWN mode DEFAULT group
default
    link/gre 0.0.0.0 brd 0.0.0.0
11: gretap0@NONE: <BROADCAST,MULTICAST> mtu 1462 qdisc noop state DOWN
mode DEFAULT group default qlen 1000
    link/ether 00:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff
12: tun42@NONE: <POINTOPOINT,NOARP> mtu 1472 qdisc noop state DOWN mode
DEFAULT group default
    link/gre 192.0.2.42 peer 203.0.113.42
$ ip -details -json link show
[{
        "ifindex": 10,
        "ifname": "gre0",
        "link": null,
        "flags": ["NOARP"],
        "mtu": 1476,
        "qdisc": "noop",
        "operstate": "DOWN",
        "linkmode": "DEFAULT",
        "group": "default",
        "link_type": "gre",
        "address": "0.0.0.0",
        "broadcast": "0.0.0.0",
        "promiscuity": 0,
        "linkinfo": {
            "info_kind": "gre",
            "info_data": {
                "remote": "any",
                "local": "any",
                "ttl": 0,
                "pmtudisc": false
            }
        },
        "inet6_addr_gen_mode": "eui64",
        "num_tx_queues": 1,
        "num_rx_queues": 1,
        "gso_max_size": 65536,
        "gso_max_segs": 65535
    },{
        "ifindex": 11,
        "ifname": "gretap0",
        "link": null,
        "flags": ["BROADCAST","MULTICAST"],
        "mtu": 1462,
        "qdisc": "noop",
        "operstate": "DOWN",
        "linkmode": "DEFAULT",
        "group": "default",
        "txqlen": 1000,
        "link_type": "ether",
        "address": "00:00:00:00:00:00",
        "broadcast": "ff:ff:ff:ff:ff:ff",
        "promiscuity": 0,
        "linkinfo": {
            "info_kind": "gretap",
            "info_data": {
                "remote": "any",
                "local": "any",
                "ttl": 0,
                "pmtudisc": false
            }
        },
        "inet6_addr_gen_mode": "eui64",
        "num_tx_queues": 1,
        "num_rx_queues": 1,
        "gso_max_size": 65536,
        "gso_max_segs": 65535
    },{
        "ifindex": 12,
        "ifname": "tun42",
        "link": null,
        "flags": ["POINTOPOINT","NOARP"],
        "mtu": 1472,
        "qdisc": "noop",
        "operstate": "DOWN",
        "linkmode": "DEFAULT",
        "group": "default",
        "link_type": "gre",
        "address": "192.0.2.42",
        "link_pointtopoint": true,
        "broadcast": "203.0.113.42",
        "promiscuity": 0,
        "linkinfo": {
            "info_kind": "gre",
            "info_data": {
                "remote": "203.0.113.42",
                "local": "192.0.2.42",
                "ttl": 0,
                "pmtudisc": true,
                "ikey": "0.0.0.42",
                "okey": "0.0.0.42"
            }
        },
        "inet6_addr_gen_mode": "eui64",
        "num_tx_queues": 1,
        "num_rx_queues": 1,
        "gso_max_size": 65536,
        "gso_max_segs": 65535
    }
]

Signed-off-by: Julien Fortin <julien@cumulusnetworks.com>
---
 ip/link_gre.c | 147 ++++++++++++++++++++++++++++++++++++++--------------------
 1 file changed, 98 insertions(+), 49 deletions(-)

diff --git a/ip/link_gre.c b/ip/link_gre.c
index c2ec5f26..3c9f8194 100644
--- a/ip/link_gre.c
+++ b/ip/link_gre.c
@@ -389,7 +389,7 @@ static void gre_print_direct_opt(FILE *f, struct rtattr *tb[])
 			remote = format_host(AF_INET, 4, &addr);
 	}
 
-	fprintf(f, "remote %s ", remote);
+	print_string(PRINT_ANY, "remote", "remote %s ", remote);
 
 	if (tb[IFLA_GRE_LOCAL]) {
 		unsigned int addr = rta_getattr_u32(tb[IFLA_GRE_LOCAL]);
@@ -398,36 +398,52 @@ static void gre_print_direct_opt(FILE *f, struct rtattr *tb[])
 			local = format_host(AF_INET, 4, &addr);
 	}
 
-	fprintf(f, "local %s ", local);
+	print_string(PRINT_ANY, "local", "local %s ", local);
 
 	if (tb[IFLA_GRE_LINK] && rta_getattr_u32(tb[IFLA_GRE_LINK])) {
 		unsigned int link = rta_getattr_u32(tb[IFLA_GRE_LINK]);
 		const char *n = if_indextoname(link, s2);
 
 		if (n)
-			fprintf(f, "dev %s ", n);
+			print_string(PRINT_ANY, "link", "dev %s ", n);
 		else
-			fprintf(f, "dev %u ", link);
+			print_uint(PRINT_ANY, "link_index", "dev %u ", link);
 	}
 
-	if (tb[IFLA_GRE_TTL] && rta_getattr_u8(tb[IFLA_GRE_TTL]))
-		fprintf(f, "ttl %d ", rta_getattr_u8(tb[IFLA_GRE_TTL]));
-	else
-		fprintf(f, "ttl inherit ");
+	if (tb[IFLA_GRE_TTL]) {
+		__u8 ttl = rta_getattr_u8(tb[IFLA_GRE_TTL]);
+
+		if (ttl)
+			print_int(PRINT_ANY, "ttl", "ttl %d ", ttl);
+		else
+			print_int(PRINT_JSON, "ttl", NULL, ttl);
+	} else {
+		print_string(PRINT_FP, NULL, "ttl %s ", "inherit");
+	}
 
 	if (tb[IFLA_GRE_TOS] && rta_getattr_u8(tb[IFLA_GRE_TOS])) {
 		int tos = rta_getattr_u8(tb[IFLA_GRE_TOS]);
 
-		fputs("tos ", f);
-		if (tos == 1)
-			fputs("inherit ", f);
-		else
-			fprintf(f, "0x%x ", tos);
+		if (is_json_context()) {
+			SPRINT_BUF(b1);
+
+			snprintf(b1, sizeof(b1), "0x%x", tos);
+			print_string(PRINT_JSON, "tos", NULL, b1);
+		} else {
+			fputs("tos ", f);
+			if (tos == 1)
+				fputs("inherit ", f);
+			else
+				fprintf(f, "0x%x ", tos);
+		}
 	}
 
-	if (tb[IFLA_GRE_PMTUDISC] &&
-	    !rta_getattr_u8(tb[IFLA_GRE_PMTUDISC]))
-		fputs("nopmtudisc ", f);
+	if (tb[IFLA_GRE_PMTUDISC]) {
+		if (!rta_getattr_u8(tb[IFLA_GRE_PMTUDISC]))
+			print_bool(PRINT_ANY, "pmtudisc", "nopmtudisc ", false);
+		else
+			print_bool(PRINT_JSON, "pmtudisc", NULL, true);
+	}
 
 	if (tb[IFLA_GRE_IFLAGS])
 		iflags = rta_getattr_u16(tb[IFLA_GRE_IFLAGS]);
@@ -437,26 +453,31 @@ static void gre_print_direct_opt(FILE *f, struct rtattr *tb[])
 
 	if ((iflags & GRE_KEY) && tb[IFLA_GRE_IKEY]) {
 		inet_ntop(AF_INET, RTA_DATA(tb[IFLA_GRE_IKEY]), s2, sizeof(s2));
-		fprintf(f, "ikey %s ", s2);
+		print_string(PRINT_ANY, "ikey", "ikey %s ", s2);
 	}
 
 	if ((oflags & GRE_KEY) && tb[IFLA_GRE_OKEY]) {
 		inet_ntop(AF_INET, RTA_DATA(tb[IFLA_GRE_OKEY]), s2, sizeof(s2));
-		fprintf(f, "okey %s ", s2);
+		print_string(PRINT_ANY, "okey", "okey %s ", s2);
 	}
 
 	if (iflags & GRE_SEQ)
-		fputs("iseq ", f);
+		print_bool(PRINT_ANY, "iseq", "iseq ", true);
 	if (oflags & GRE_SEQ)
-		fputs("oseq ", f);
+		print_bool(PRINT_ANY, "oseq", "oseq ", true);
 	if (iflags & GRE_CSUM)
-		fputs("icsum ", f);
+		print_bool(PRINT_ANY, "icsum", "icsum ", true);
 	if (oflags & GRE_CSUM)
-		fputs("ocsum ", f);
+		print_bool(PRINT_ANY, "ocsum", "ocsum ", true);
+
+	if (tb[IFLA_GRE_FWMARK]) {
+		__u32 fwmark = rta_getattr_u32(tb[IFLA_GRE_FWMARK]);
+
+		if (fwmark) {
+			snprintf(s2, sizeof(s2), "0x%x", fwmark);
 
-	if (tb[IFLA_GRE_FWMARK] && rta_getattr_u32(tb[IFLA_GRE_FWMARK])) {
-		fprintf(f, "fwmark 0x%x ",
-			rta_getattr_u32(tb[IFLA_GRE_FWMARK]));
+			print_string(PRINT_ANY, "fwmark", "fwmark %s ", s2);
+		}
 	}
 }
 
@@ -468,10 +489,10 @@ static void gre_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 	if (!tb[IFLA_GRE_COLLECT_METADATA])
 		gre_print_direct_opt(f, tb);
 	else
-		fputs("external ", f);
+		print_bool(PRINT_ANY, "external", "external ", true);
 
 	if (tb[IFLA_GRE_IGNORE_DF] && rta_getattr_u8(tb[IFLA_GRE_IGNORE_DF]))
-		fputs("ignore-df ", f);
+		print_bool(PRINT_ANY, "ignore_df", "ignore-df ", true);
 
 	if (tb[IFLA_GRE_ENCAP_TYPE] &&
 	    rta_getattr_u16(tb[IFLA_GRE_ENCAP_TYPE]) != TUNNEL_ENCAP_NONE) {
@@ -480,45 +501,73 @@ static void gre_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 		__u16 sport = rta_getattr_u16(tb[IFLA_GRE_ENCAP_SPORT]);
 		__u16 dport = rta_getattr_u16(tb[IFLA_GRE_ENCAP_DPORT]);
 
-		fputs("encap ", f);
+
+		open_json_object("encap");
+		print_string(PRINT_FP, NULL, "encap ", NULL);
+
 		switch (type) {
 		case TUNNEL_ENCAP_FOU:
-			fputs("fou ", f);
+			print_string(PRINT_ANY, "type", "%s ", "fou");
 			break;
 		case TUNNEL_ENCAP_GUE:
-			fputs("gue ", f);
+			print_string(PRINT_ANY, "type", "%s ", "gue");
 			break;
 		default:
-			fputs("unknown ", f);
+			print_null(PRINT_ANY, "type", "%s ", "unknown");
 			break;
 		}
 
-		if (sport == 0)
-			fputs("encap-sport auto ", f);
-		else
-			fprintf(f, "encap-sport %u", ntohs(sport));
+		if (is_json_context()) {
+			print_uint(PRINT_JSON,
+				   "sport",
+				   NULL,
+				   sport ? ntohs(sport) : 0);
+			print_uint(PRINT_JSON, "dport", NULL, ntohs(dport));
+
+			print_bool(PRINT_JSON,
+				   "csum",
+				   NULL,
+				   flags & TUNNEL_ENCAP_FLAG_CSUM);
+
+			print_bool(PRINT_JSON,
+				   "csum6",
+				   NULL,
+				   flags & TUNNEL_ENCAP_FLAG_CSUM6);
+
+			print_bool(PRINT_JSON,
+				   "remcsum",
+				   NULL,
+				   flags & TUNNEL_ENCAP_FLAG_REMCSUM);
+
+			close_json_object();
+		} else {
+			if (sport == 0)
+				fputs("encap-sport auto ", f);
+			else
+				fprintf(f, "encap-sport %u", ntohs(sport));
 
-		fprintf(f, "encap-dport %u ", ntohs(dport));
+			fprintf(f, "encap-dport %u ", ntohs(dport));
 
-		if (flags & TUNNEL_ENCAP_FLAG_CSUM)
-			fputs("encap-csum ", f);
-		else
-			fputs("noencap-csum ", f);
+			if (flags & TUNNEL_ENCAP_FLAG_CSUM)
+				fputs("encap-csum ", f);
+			else
+				fputs("noencap-csum ", f);
 
-		if (flags & TUNNEL_ENCAP_FLAG_CSUM6)
-			fputs("encap-csum6 ", f);
-		else
-			fputs("noencap-csum6 ", f);
+			if (flags & TUNNEL_ENCAP_FLAG_CSUM6)
+				fputs("encap-csum6 ", f);
+			else
+				fputs("noencap-csum6 ", f);
 
-		if (flags & TUNNEL_ENCAP_FLAG_REMCSUM)
-			fputs("encap-remcsum ", f);
-		else
-			fputs("noencap-remcsum ", f);
+			if (flags & TUNNEL_ENCAP_FLAG_REMCSUM)
+				fputs("encap-remcsum ", f);
+			else
+				fputs("noencap-remcsum ", f);
+		}
 	}
 }
 
 static void gre_print_help(struct link_util *lu, int argc, char **argv,
-	FILE *f)
+			   FILE *f)
 {
 	print_usage(f);
 }
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH iproute2 json v2 21/27] ip: link_gre6.c: add json output support
  2017-08-17 17:35 [PATCH iproute2 json v2 00/27] ip: add -json support to 'ip link show' Julien Fortin
                   ` (19 preceding siblings ...)
  2017-08-17 17:36 ` [PATCH iproute2 json v2 20/27] ip: link_gre.c: " Julien Fortin
@ 2017-08-17 17:36 ` Julien Fortin
  2017-08-17 17:36 ` [PATCH iproute2 json v2 22/27] ip: link_ip6tnl.c: " Julien Fortin
                   ` (7 subsequent siblings)
  28 siblings, 0 replies; 33+ messages in thread
From: Julien Fortin @ 2017-08-17 17:36 UTC (permalink / raw)
  To: netdev; +Cc: roopa, nikolay, dsa, Julien Fortin

From: Julien Fortin <julien@cumulusnetworks.com>

Schema
{
    "remote": {
        "type": "string",
        "attr": "IFLA_GRE_REMOTE"
    },
    "local": {
        "type": "string",
        "attr": "IFLA_GRE_LOCAL"
    },
    "link": {
        "type": "string",
        "attr": "IFLA_GRE_LINK",
        "mutually_exclusive": {
            "link_index": {
                "type": "uint",
            }
        }
    },
    "ttl": {
        "type": "int",
        "attr": "IFLA_GRE_TTL"
    },
    "ip6_tnl_f_ign_encap_limit": {
        "type": "bool",
        "attr": "IP6_TNL_F_IGN_ENCAP_LIMIT"
    },
    "encap_limit": {
        "type": "int",
        "attr": "IFLA_GRE_ENCAP_LIMIT"
    },
    "ip6_tnl_f_use_orig_flowlabel": {
        "type": "bool",
        "attr": "IP6_TNL_F_USE_ORIG_FLOWLABEL"
    },
    "flowlabel": {
        "type": "string",
        "attr": "IP6_FLOWINFO_FLOWLABEL"
    },
    "ip6_tnl_f_rcv_dscp_copy": {
        "type": "bool",
        "attr": "IP6_TNL_F_RCV_DSCP_COPY"
    },
    "ikey": {
        "type": "string",
        "attr": "IFLA_GRE_IKEY"
    },
    "okey": {
        "type": "string",
        "attr": "IFLA_GRE_OKEY"
    },
    "iseq": {
        "type": "bool",
        "attr": "IFLA_GRE_IFLAGS & GRE_SEQ"
    },
    "oseq": {
        "type": "bool",
        "attr": "IFLA_GRE_OFLAGS & GRE_SEQ"
    },
    "icsum": {
        "type": "bool",
        "attr": "IFLA_GRE_IFLAGS & GRE_CSUM"
    },
    "ocsum": {
        "type": "bool",
        "attr": "IFLA_GRE_OFLAGS & GRE_CSUM"
    },
    "encap": {
        "type": "dict",
        "attr": "IFLA_GRE_ENCAP_TYPE != TUNNEL_ENCAP_NONE",
        "dict": {
            "type": {
                "type": "string",
                "attr": "IFLA_GRE_ENCAP_TYPE"
            },
            "sport": {
                "type": "uint",
                "attr": "IFLA_GRE_ENCAP_SPORT"
            },
            "dport": {
                "type": "uint",
                "attr": "IFLA_GRE_ENCAP_DPORT"
            },
            "csum": {
                "type": "bool",
                "attr": "TUNNEL_ENCAP_FLAG_CSUM"
            },
            "csum6": {
                "type": "bool",
                "attr": "TUNNEL_ENCAP_FLAG_CSUM6"
            },
            "remcsum": {
                "type": "bool",
                "attr": "TUNNEL_ENCAP_FLAG_REMCSUM"
            }
        }
    }
}

$ ip link show
$ ip -6 tunnel add name tun6 mode ip6gre local 2001:db8:1::1/64 remote
2001:0db8:85a3:0000:0000:8a2e:0370:7334
$ ip link show
10: ip6tnl0@NONE: <NOARP> mtu 1452 qdisc noop state DOWN mode DEFAULT
group default
    link/tunnel6 :: brd ::
11: ip6gre0@NONE: <NOARP> mtu 1448 qdisc noop state DOWN mode DEFAULT
group default
    link/gre6 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 brd
00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00
12: tun6@NONE: <POINTOPOINT,NOARP> mtu 1448 qdisc noop state DOWN mode
DEFAULT group default
    link/gre6 20:01:0d:b8:00:01:00:00:00:00:00:00:00:00:00:01 peer
20:01:0d:b8:85:a3:00:00:00:00:8a:2e:03:70:73:34
➜  ~ ./ip -details -json link show
[{
        "ifindex": 10,
        "ifname": "ip6tnl0",
        "link": null,
        "flags": ["NOARP"],
        "mtu": 1452,
        "qdisc": "noop",
        "operstate": "DOWN",
        "linkmode": "DEFAULT",
        "group": "default",
        "link_type": "tunnel6",
        "address": "::",
        "broadcast": "::",
        "promiscuity": 0,
        "linkinfo": {
            "info_kind": "ip6tnl",
            "info_data": {
                "proto": "ip6ip6",
                "remote": "::",
                "local": "::",
                "encap_limit": 0,
                "ttl": 0,
                "flowinfo_tclass": "0x00",
                "flowlabel": "0x00000",
                "flowinfo": "0x00000000"
            }
        },
        "inet6_addr_gen_mode": "eui64",
        "num_tx_queues": 1,
        "num_rx_queues": 1,
        "gso_max_size": 65536,
        "gso_max_segs": 65535
    },{
        "ifindex": 11,
        "ifname": "ip6gre0",
        "link": null,
        "flags": ["NOARP"],
        "mtu": 1448,
        "qdisc": "noop",
        "operstate": "DOWN",
        "linkmode": "DEFAULT",
        "group": "default",
        "link_type": "gre6",
        "address": "00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00",
        "broadcast": "00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00",
        "promiscuity": 0,
        "linkinfo": {
            "info_kind": "ip6gre",
            "info_data": {
                "remote": "any",
                "local": "any",
                "ttl": 0,
                "encap_limit": 0,
                "flowlabel": "0x00000"
            }
        },
        "inet6_addr_gen_mode": "eui64",
        "num_tx_queues": 1,
        "num_rx_queues": 1,
        "gso_max_size": 65536,
        "gso_max_segs": 65535
    },{
        "ifindex": 12,
        "ifname": "tun6",
        "link": null,
        "flags": ["POINTOPOINT","NOARP"],
        "mtu": 1448,
        "qdisc": "noop",
        "operstate": "DOWN",
        "linkmode": "DEFAULT",
        "group": "default",
        "link_type": "gre6",
        "address": "20:01:0d:b8:00:01:00:00:00:00:00:00:00:00:00:01",
        "link_pointtopoint": true,
        "broadcast": "20:01:0d:b8:85:a3:00:00:00:00:8a:2e:03:70:73:34",
        "promiscuity": 0,
        "linkinfo": {
            "info_kind": "ip6gre",
            "info_data": {
                "remote": "2001:db8:85a3::8a2e:370:7334",
                "local": "2001:db8:1::1",
                "ttl": 64,
                "encap_limit": 4,
                "flowlabel": "0x00000"
            }
        },
        "inet6_addr_gen_mode": "eui64",
        "num_tx_queues": 1,
        "num_rx_queues": 1,
        "gso_max_size": 65536,
        "gso_max_segs": 65535
    }
]

Signed-off-by: Julien Fortin <julien@cumulusnetworks.com>
---
 ip/link_gre6.c | 142 ++++++++++++++++++++++++++++++++++++++++-----------------
 1 file changed, 99 insertions(+), 43 deletions(-)

diff --git a/ip/link_gre6.c b/ip/link_gre6.c
index 4d3d4b54..957853d6 100644
--- a/ip/link_gre6.c
+++ b/ip/link_gre6.c
@@ -423,7 +423,7 @@ static void gre_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 			remote = format_host(AF_INET6, sizeof(addr), &addr);
 	}
 
-	fprintf(f, "remote %s ", remote);
+	print_string(PRINT_ANY, "remote", "remote %s ", remote);
 
 	if (tb[IFLA_GRE_LOCAL]) {
 		struct in6_addr addr;
@@ -434,36 +434,65 @@ static void gre_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 			local = format_host(AF_INET6, sizeof(addr), &addr);
 	}
 
-	fprintf(f, "local %s ", local);
+	print_string(PRINT_ANY, "local", "local %s ", local);
 
 	if (tb[IFLA_GRE_LINK] && rta_getattr_u32(tb[IFLA_GRE_LINK])) {
 		unsigned int link = rta_getattr_u32(tb[IFLA_GRE_LINK]);
 		const char *n = if_indextoname(link, s2);
 
 		if (n)
-			fprintf(f, "dev %s ", n);
+			print_string(PRINT_ANY, "link", "dev %s ", n);
 		else
-			fprintf(f, "dev %u ", link);
+			print_uint(PRINT_ANY, "link_index", "dev %u ", link);
 	}
 
-	if (tb[IFLA_GRE_TTL] && rta_getattr_u8(tb[IFLA_GRE_TTL]))
-		fprintf(f, "hoplimit %d ", rta_getattr_u8(tb[IFLA_GRE_TTL]));
+	if (tb[IFLA_GRE_TTL]) {
+		__u8 ttl = rta_getattr_u8(tb[IFLA_GRE_TTL]);
+
+		if (ttl)
+			print_int(PRINT_ANY, "ttl", "hoplimit %d ", ttl);
+		else
+			print_int(PRINT_JSON, "ttl", NULL, ttl);
+	}
 
 	if (flags & IP6_TNL_F_IGN_ENCAP_LIMIT)
-		fprintf(f, "encaplimit none ");
+		print_bool(PRINT_ANY,
+			   "ip6_tnl_f_ign_encap_limit",
+			   "encaplimit none ",
+			   true);
 	else if (tb[IFLA_GRE_ENCAP_LIMIT]) {
 		int encap_limit = rta_getattr_u8(tb[IFLA_GRE_ENCAP_LIMIT]);
 
-		fprintf(f, "encaplimit %d ", encap_limit);
+		print_int(PRINT_ANY,
+			  "encap_limit",
+			  "encaplimit %d ",
+			  encap_limit);
 	}
 
-	if (flags & IP6_TNL_F_USE_ORIG_FLOWLABEL)
-		fprintf(f, "flowlabel inherit ");
-	else
-		fprintf(f, "flowlabel 0x%05x ", ntohl(flowinfo & IP6_FLOWINFO_FLOWLABEL));
+	if (flags & IP6_TNL_F_USE_ORIG_FLOWLABEL) {
+		print_bool(PRINT_ANY,
+			   "ip6_tnl_f_use_orig_flowlabel",
+			   "flowlabel inherit ",
+			   true);
+	} else {
+		if (is_json_context()) {
+			SPRINT_BUF(b1);
+
+			snprintf(b1, sizeof(b1), "0x%05x",
+				 ntohl(flowinfo & IP6_FLOWINFO_FLOWLABEL));
+			print_string(PRINT_JSON, "flowlabel", NULL, b1);
+
+		} else {
+			fprintf(f, "flowlabel 0x%05x ",
+				ntohl(flowinfo & IP6_FLOWINFO_FLOWLABEL));
+		}
+	}
 
 	if (flags & IP6_TNL_F_RCV_DSCP_COPY)
-		fprintf(f, "dscp inherit ");
+		print_bool(PRINT_ANY,
+			   "ip6_tnl_f_rcv_dscp_copy",
+			   "dscp inherit ",
+			   true);
 
 	if (tb[IFLA_GRE_IFLAGS])
 		iflags = rta_getattr_u16(tb[IFLA_GRE_IFLAGS]);
@@ -473,27 +502,37 @@ static void gre_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 
 	if ((iflags & GRE_KEY) && tb[IFLA_GRE_IKEY]) {
 		inet_ntop(AF_INET, RTA_DATA(tb[IFLA_GRE_IKEY]), s2, sizeof(s2));
-		fprintf(f, "ikey %s ", s2);
+		print_string(PRINT_ANY, "ikey", "ikey %s ", s2);
 	}
 
 	if ((oflags & GRE_KEY) && tb[IFLA_GRE_OKEY]) {
 		inet_ntop(AF_INET, RTA_DATA(tb[IFLA_GRE_OKEY]), s2, sizeof(s2));
-		fprintf(f, "okey %s ", s2);
+		print_string(PRINT_ANY, "okey", "okey %s ", s2);
 	}
 
 	if (iflags & GRE_SEQ)
-		fputs("iseq ", f);
+		print_bool(PRINT_ANY, "iseq", "iseq ", true);
 	if (oflags & GRE_SEQ)
-		fputs("oseq ", f);
+		print_bool(PRINT_ANY, "oseq", "oseq ", true);
 	if (iflags & GRE_CSUM)
-		fputs("icsum ", f);
+		print_bool(PRINT_ANY, "icsum", "icsum ", true);
 	if (oflags & GRE_CSUM)
-		fputs("ocsum ", f);
+		print_bool(PRINT_ANY, "ocsum", "ocsum ", true);
 
 	if (flags & IP6_TNL_F_USE_ORIG_FWMARK)
-		fprintf(f, "fwmark inherit ");
-	else if (tb[IFLA_GRE_FWMARK] && rta_getattr_u32(tb[IFLA_GRE_FWMARK]))
-		fprintf(f, "fwmark 0x%x ", rta_getattr_u32(tb[IFLA_GRE_FWMARK]));
+		print_bool(PRINT_ANY,
+			   "ip6_tnl_f_use_orig_fwmark",
+			   "fwmark inherit ",
+			   true);
+	else if (tb[IFLA_GRE_FWMARK]) {
+		__u32 fwmark = rta_getattr_u32(tb[IFLA_GRE_FWMARK]);
+
+		if (fwmark) {
+			snprintf(s2, sizeof(s2), "0x%x", fwmark);
+
+			print_string(PRINT_ANY, "fwmark", "fwmark %s ", s2);
+		}
+	}
 
 	if (tb[IFLA_GRE_ENCAP_TYPE] &&
 	    rta_getattr_u16(tb[IFLA_GRE_ENCAP_TYPE]) != TUNNEL_ENCAP_NONE) {
@@ -502,40 +541,57 @@ static void gre_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 		__u16 sport = rta_getattr_u16(tb[IFLA_GRE_ENCAP_SPORT]);
 		__u16 dport = rta_getattr_u16(tb[IFLA_GRE_ENCAP_DPORT]);
 
-		fputs("encap ", f);
+		open_json_object("encap");
+
+		print_string(PRINT_FP, NULL, "encap ", NULL);
 		switch (type) {
 		case TUNNEL_ENCAP_FOU:
-			fputs("fou ", f);
+			print_string(PRINT_ANY, "type", "%s ", "fou");
 			break;
 		case TUNNEL_ENCAP_GUE:
-			fputs("gue ", f);
+			print_string(PRINT_ANY, "type", "%s ", "gue");
 			break;
 		default:
-			fputs("unknown ", f);
+			print_null(PRINT_ANY, "type", "unknown ", NULL);
 			break;
 		}
 
-		if (sport == 0)
-			fputs("encap-sport auto ", f);
-		else
-			fprintf(f, "encap-sport %u", ntohs(sport));
+		if (is_json_context()) {
+			print_uint(PRINT_JSON,
+				   "sport",
+				   NULL,
+				   sport ? ntohs(sport) : 0);
+			print_uint(PRINT_JSON, "dport", NULL, ntohs(dport));
+			print_bool(PRINT_JSON, "csum", NULL,
+					   flags & TUNNEL_ENCAP_FLAG_CSUM);
+			print_bool(PRINT_JSON, "csum6", NULL,
+					   flags & TUNNEL_ENCAP_FLAG_CSUM6);
+			print_bool(PRINT_JSON, "remcsum", NULL,
+					   flags & TUNNEL_ENCAP_FLAG_REMCSUM);
+			close_json_object();
+		} else {
+			if (sport == 0)
+				fputs("encap-sport auto ", f);
+			else
+				fprintf(f, "encap-sport %u", ntohs(sport));
 
-		fprintf(f, "encap-dport %u ", ntohs(dport));
+			fprintf(f, "encap-dport %u ", ntohs(dport));
 
-		if (flags & TUNNEL_ENCAP_FLAG_CSUM)
-			fputs("encap-csum ", f);
-		else
-			fputs("noencap-csum ", f);
+			if (flags & TUNNEL_ENCAP_FLAG_CSUM)
+				fputs("encap-csum ", f);
+			else
+				fputs("noencap-csum ", f);
 
-		if (flags & TUNNEL_ENCAP_FLAG_CSUM6)
-			fputs("encap-csum6 ", f);
-		else
-			fputs("noencap-csum6 ", f);
+			if (flags & TUNNEL_ENCAP_FLAG_CSUM6)
+				fputs("encap-csum6 ", f);
+			else
+				fputs("noencap-csum6 ", f);
 
-		if (flags & TUNNEL_ENCAP_FLAG_REMCSUM)
-			fputs("encap-remcsum ", f);
-		else
-			fputs("noencap-remcsum ", f);
+			if (flags & TUNNEL_ENCAP_FLAG_REMCSUM)
+				fputs("encap-remcsum ", f);
+			else
+				fputs("noencap-remcsum ", f);
+		}
 	}
 }
 
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH iproute2 json v2 22/27] ip: link_ip6tnl.c: add json output support
  2017-08-17 17:35 [PATCH iproute2 json v2 00/27] ip: add -json support to 'ip link show' Julien Fortin
                   ` (20 preceding siblings ...)
  2017-08-17 17:36 ` [PATCH iproute2 json v2 21/27] ip: link_gre6.c: " Julien Fortin
@ 2017-08-17 17:36 ` Julien Fortin
  2017-08-17 17:36 ` [PATCH iproute2 json v2 23/27] ip: link_iptnl.c: " Julien Fortin
                   ` (6 subsequent siblings)
  28 siblings, 0 replies; 33+ messages in thread
From: Julien Fortin @ 2017-08-17 17:36 UTC (permalink / raw)
  To: netdev; +Cc: roopa, nikolay, dsa, Julien Fortin

From: Julien Fortin <julien@cumulusnetworks.com>

Schema
{
    "proto": {
        "type": "string",
        "attr": "IFLA_IPTUN_PROTO"
    },
    "remote": {
        "type": "string",
        "attr": "IFLA_IPTUN_REMOTE"
    },
    "local": {
        "type": "string",
        "attr": "IFLA_IPTUN_LOCAL"
    },
    "link": {
        "type": "string",
        "attr": "IFLA_IPTUN_LINK",
        "mutually_exclusive": {
            "link_index": {
                "type": "uint",
            }
        }
    },
    "ip6_tnl_f_ign_encap_limit": {
        "type": "bool",
        "attr": "IP6_TNL_F_IGN_ENCAP_LIMIT"
    },
    "encap_limit": {
        "type": "uint",
        "attr": "IFLA_IPTUN_ENCAP_LIMIT"
    },
    "ttl": {
        "type": "uint",
        "attr": "IFLA_IPTUN_TTL"
    },
    "ip6_tnl_f_use_orig_tclass": {
        "type": "",
        "attr": "IP6_TNL_F_USE_ORIG_TCLASS"
    },
    "flowinfo_tclass": {
        "type": "string",
        "attr": "IP6_FLOWINFO_TCLASS"
    },
    "ip6_tnl_f_use_orig_flowlabel": {
        "type": "bool",
        "attr": "IP6_TNL_F_USE_ORIG_FLOWLABEL"
    },
    "flowlabel": {
        "type": "string",
        "attr": "IP6_FLOWINFO_FLOWLABEL"
    },
    "flowinfo": {
        "type": "string"
    },
    "ip6_tnl_f_rcv_dscp_copy": {
        "type": "bool",
        "attr": "IP6_TNL_F_RCV_DSCP_COPY"
    },
    "ip6_tnl_f_mip6_dev": {
        "type": "bool",
        "attr": "IP6_TNL_F_MIP6_DEV"
    },
    "ip6_tnl_f_use_orig_fwmark": {
        "type": "bool",
        "attr": "IP6_TNL_F_USE_ORIG_FWMARK"
    },
    "encap": {
        "type": "dict",
        "attr": "IFLA_IPTUN_ENCAP_TYPE",
        "dict": {
            "type": {
                "type": "string",
                "attr": "IFLA_IPTUN_ENCAP_TYPE"
            },
            "sport": {
                "type": "uint",
                "attr": "IFLA_IPTUN_ENCAP_SPORT"
            },
            "dport": {
                "type": "uint",
                "attr": "IFLA_IPTUN_ENCAP_DPORT"
            },
            "csum": {
                "type": "bool",
                "attr": "TUNNEL_ENCAP_FLAG_CSUM"
            },
            "csum6": {
                "type": "bool",
                "attr": "TUNNEL_ENCAP_FLAG_CSUM6"
            },
            "remcsum": {
                "type": "bool",
                "attr": "TUNNEL_ENCAP_FLAG_REMCSUM"
            }
        }
    }
}

$ ip link show
$ ip -6 tunnel add name tun6 mode ip6gre local 2001:db8:1::1/64 remote
2001:0db8:85a3:0000:0000:8a2e:0370:7334
$ ip link show
10: ip6tnl0@NONE: <NOARP> mtu 1452 qdisc noop state DOWN mode DEFAULT group
default
    link/tunnel6 :: brd ::
11: ip6gre0@NONE: <NOARP> mtu 1448 qdisc noop state DOWN mode DEFAULT group
default
    link/gre6 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 brd
00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00
12: tun6@NONE: <POINTOPOINT,NOARP> mtu 1448 qdisc noop state DOWN mode
DEFAULT group default
    link/gre6 20:01:0d:b8:00:01:00:00:00:00:00:00:00:00:00:01 peer
20:01:0d:b8:85:a3:00:00:00:00:8a:2e:03:70:73:34
➜  ~ ./ip -details -json link show
[{
        "ifindex": 10,
        "ifname": "ip6tnl0",
        "link": null,
        "flags": ["NOARP"],
        "mtu": 1452,
        "qdisc": "noop",
        "operstate": "DOWN",
        "linkmode": "DEFAULT",
        "group": "default",
        "link_type": "tunnel6",
        "address": "::",
        "broadcast": "::",
        "promiscuity": 0,
        "linkinfo": {
            "info_kind": "ip6tnl",
            "info_data": {
                "proto": "ip6ip6",
                "remote": "::",
                "local": "::",
                "encap_limit": 0,
                "ttl": 0,
                "flowinfo_tclass": "0x00",
                "flowlabel": "0x00000",
                "flowinfo": "0x00000000"
            }
        },
        "inet6_addr_gen_mode": "eui64",
        "num_tx_queues": 1,
        "num_rx_queues": 1,
        "gso_max_size": 65536,
        "gso_max_segs": 65535
    },{
        "ifindex": 11,
        "ifname": "ip6gre0",
        "link": null,
        "flags": ["NOARP"],
        "mtu": 1448,
        "qdisc": "noop",
        "operstate": "DOWN",
        "linkmode": "DEFAULT",
        "group": "default",
        "link_type": "gre6",
        "address": "00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00",
        "broadcast": "00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00",
        "promiscuity": 0,
        "linkinfo": {
            "info_kind": "ip6gre",
            "info_data": {
                "remote": "any",
                "local": "any",
                "ttl": 0,
                "encap_limit": 0,
                "flowlabel": "0x00000"
            }
        },
        "inet6_addr_gen_mode": "eui64",
        "num_tx_queues": 1,
        "num_rx_queues": 1,
        "gso_max_size": 65536,
        "gso_max_segs": 65535
    },{
        "ifindex": 12,
        "ifname": "tun6",
        "link": null,
        "flags": ["POINTOPOINT","NOARP"],
        "mtu": 1448,
        "qdisc": "noop",
        "operstate": "DOWN",
        "linkmode": "DEFAULT",
        "group": "default",
        "link_type": "gre6",
        "address": "20:01:0d:b8:00:01:00:00:00:00:00:00:00:00:00:01",
        "link_pointtopoint": true,
        "broadcast": "20:01:0d:b8:85:a3:00:00:00:00:8a:2e:03:70:73:34",
        "promiscuity": 0,
        "linkinfo": {
            "info_kind": "ip6gre",
            "info_data": {
                "remote": "2001:db8:85a3::8a2e:370:7334",
                "local": "2001:db8:1::1",
                "ttl": 64,
                "encap_limit": 4,
                "flowlabel": "0x00000"
            }
        },
        "inet6_addr_gen_mode": "eui64",
        "num_tx_queues": 1,
        "num_rx_queues": 1,
        "gso_max_size": 65536,
        "gso_max_segs": 65535
    }
]

Signed-off-by: Julien Fortin <julien@cumulusnetworks.com>
---
 ip/link_ip6tnl.c | 172 +++++++++++++++++++++++++++++++++++++++----------------
 1 file changed, 122 insertions(+), 50 deletions(-)

diff --git a/ip/link_ip6tnl.c b/ip/link_ip6tnl.c
index 505fb476..a4199006 100644
--- a/ip/link_ip6tnl.c
+++ b/ip/link_ip6tnl.c
@@ -346,25 +346,29 @@ static void ip6tunnel_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb
 	if (tb[IFLA_IPTUN_PROTO]) {
 		switch (rta_getattr_u8(tb[IFLA_IPTUN_PROTO])) {
 		case IPPROTO_IPIP:
-			fprintf(f, "ipip6 ");
+			print_string(PRINT_ANY, "proto", "%s ", "ipip6");
 			break;
 		case IPPROTO_IPV6:
-			fprintf(f, "ip6ip6 ");
+			print_string(PRINT_ANY, "proto", "%s ", "ip6ip6");
 			break;
 		case 0:
-			fprintf(f, "any ");
+			print_string(PRINT_ANY, "proto", "%s ", "any");
 			break;
 		}
 	}
 
 	if (tb[IFLA_IPTUN_REMOTE]) {
-		fprintf(f, "remote %s ",
-			rt_addr_n2a_rta(AF_INET6, tb[IFLA_IPTUN_REMOTE]));
+		print_string(PRINT_ANY,
+			     "remote",
+			     "remote %s ",
+			     rt_addr_n2a_rta(AF_INET6, tb[IFLA_IPTUN_REMOTE]));
 	}
 
 	if (tb[IFLA_IPTUN_LOCAL]) {
-		fprintf(f, "local %s ",
-			rt_addr_n2a_rta(AF_INET6, tb[IFLA_IPTUN_LOCAL]));
+		print_string(PRINT_ANY,
+			     "local",
+			     "local %s ",
+			     rt_addr_n2a_rta(AF_INET6, tb[IFLA_IPTUN_LOCAL]));
 	}
 
 	if (tb[IFLA_IPTUN_LINK] && rta_getattr_u32(tb[IFLA_IPTUN_LINK])) {
@@ -372,93 +376,161 @@ static void ip6tunnel_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb
 		const char *n = if_indextoname(link, s2);
 
 		if (n)
-			fprintf(f, "dev %s ", n);
+			print_string(PRINT_ANY, "link", "dev %s ", n);
 		else
-			fprintf(f, "dev %u ", link);
+			print_uint(PRINT_ANY, "link_index", "dev %u ", link);
 	}
 
 	if (flags & IP6_TNL_F_IGN_ENCAP_LIMIT)
-		printf("encaplimit none ");
+		print_bool(PRINT_ANY,
+			   "ip6_tnl_f_ign_encap_limit",
+			   "encaplimit none ",
+			   true);
 	else if (tb[IFLA_IPTUN_ENCAP_LIMIT])
-		fprintf(f, "encaplimit %u ",
-			rta_getattr_u8(tb[IFLA_IPTUN_ENCAP_LIMIT]));
+		print_uint(PRINT_ANY,
+			   "encap_limit",
+			   "encaplimit %u ",
+			   rta_getattr_u8(tb[IFLA_IPTUN_ENCAP_LIMIT]));
 
 	if (tb[IFLA_IPTUN_TTL])
-		fprintf(f, "hoplimit %u ", rta_getattr_u8(tb[IFLA_IPTUN_TTL]));
+		print_uint(PRINT_ANY,
+			   "ttl",
+			   "hoplimit %u ",
+			   rta_getattr_u8(tb[IFLA_IPTUN_TTL]));
 
 	if (flags & IP6_TNL_F_USE_ORIG_TCLASS)
-		printf("tclass inherit ");
+		print_bool(PRINT_ANY,
+			   "ip6_tnl_f_use_orig_tclass",
+			   "tclass inherit ",
+			   true);
 	else if (tb[IFLA_IPTUN_FLOWINFO]) {
 		__u32 val = ntohl(flowinfo & IP6_FLOWINFO_TCLASS);
 
-		printf("tclass 0x%02x ", (__u8)(val >> 20));
+		if (is_json_context()) {
+			SPRINT_BUF(b1);
+
+			snprintf(b1, sizeof(b1), "0x%02x", (__u8)(val >> 20));
+			print_string(PRINT_JSON, "flowinfo_tclass", NULL, b1);
+		} else {
+			printf("tclass 0x%02x ", (__u8)(val >> 20));
+		}
+	}
+
+	if (flags & IP6_TNL_F_USE_ORIG_FLOWLABEL) {
+		print_bool(PRINT_ANY,
+			   "ip6_tnl_f_use_orig_flowlabel",
+			   "flowlabel inherit ",
+			   true);
+	} else {
+		if (is_json_context()) {
+			SPRINT_BUF(b1);
+
+			snprintf(b1, sizeof(b1), "0x%05x",
+				 ntohl(flowinfo & IP6_FLOWINFO_FLOWLABEL));
+			print_string(PRINT_JSON, "flowlabel", NULL, b1);
+		} else {
+			printf("flowlabel 0x%05x ",
+			       ntohl(flowinfo & IP6_FLOWINFO_FLOWLABEL));
+		}
 	}
 
-	if (flags & IP6_TNL_F_USE_ORIG_FLOWLABEL)
-		printf("flowlabel inherit ");
-	else
-		printf("flowlabel 0x%05x ", ntohl(flowinfo & IP6_FLOWINFO_FLOWLABEL));
+	if (is_json_context()) {
+		SPRINT_BUF(flwinfo);
 
-	printf("(flowinfo 0x%08x) ", ntohl(flowinfo));
+		snprintf(flwinfo, sizeof(flwinfo), "0x%08x", ntohl(flowinfo));
+		print_string(PRINT_JSON, "flowinfo", NULL, flwinfo);
+	} else {
+		printf("(flowinfo 0x%08x) ", ntohl(flowinfo));
+
+	}
 
 	if (flags & IP6_TNL_F_RCV_DSCP_COPY)
-		printf("dscp inherit ");
+		print_bool(PRINT_ANY,
+			   "ip6_tnl_f_rcv_dscp_copy",
+			   "dscp inherit ",
+			   true);
 
 	if (flags & IP6_TNL_F_MIP6_DEV)
-		fprintf(f, "mip6 ");
+		print_bool(PRINT_ANY, "ip6_tnl_f_mip6_dev", "mip6 ", true);
+
+	if (flags & IP6_TNL_F_USE_ORIG_FWMARK) {
+		print_bool(PRINT_ANY,
+			   "ip6_tnl_f_use_orig_fwmark",
+			   "fwmark inherit ",
+			   true);
+	} else if (tb[IFLA_IPTUN_FWMARK]) {
+		__u32 fwmark = rta_getattr_u32(tb[IFLA_IPTUN_FWMARK]);
 
-	if (flags & IP6_TNL_F_USE_ORIG_FWMARK)
-		fprintf(f, "fwmark inherit ");
-	else if (tb[IFLA_IPTUN_FWMARK] && rta_getattr_u32(tb[IFLA_IPTUN_FWMARK]))
-		fprintf(f, "fwmark 0x%x ", rta_getattr_u32(tb[IFLA_IPTUN_FWMARK]));
+		if (fwmark) {
+			SPRINT_BUF(b1);
+
+			snprintf(b1, sizeof(b1), "0x%x", fwmark);
+			print_string(PRINT_ANY, "fwmark", "fwmark %s ", b1);
+		}
+	}
 
 	if (tb[IFLA_IPTUN_ENCAP_TYPE] &&
-	    rta_getattr_u16(tb[IFLA_IPTUN_ENCAP_TYPE]) !=
-	    TUNNEL_ENCAP_NONE) {
+	    rta_getattr_u16(tb[IFLA_IPTUN_ENCAP_TYPE]) != TUNNEL_ENCAP_NONE) {
 		__u16 type = rta_getattr_u16(tb[IFLA_IPTUN_ENCAP_TYPE]);
 		__u16 flags = rta_getattr_u16(tb[IFLA_IPTUN_ENCAP_FLAGS]);
 		__u16 sport = rta_getattr_u16(tb[IFLA_IPTUN_ENCAP_SPORT]);
 		__u16 dport = rta_getattr_u16(tb[IFLA_IPTUN_ENCAP_DPORT]);
 
-		fputs("encap ", f);
+		open_json_object("encap");
+		print_string(PRINT_FP, NULL, "encap ", NULL);
 		switch (type) {
 		case TUNNEL_ENCAP_FOU:
-			fputs("fou ", f);
+			print_string(PRINT_ANY, "type", "%s ", "fou");
 			break;
 		case TUNNEL_ENCAP_GUE:
-			fputs("gue ", f);
+			print_string(PRINT_ANY, "type", "%s ", "gue");
 			break;
 		default:
-			fputs("unknown ", f);
+			print_null(PRINT_ANY, "type", "unknown ", NULL);
 			break;
 		}
 
-		if (sport == 0)
-			fputs("encap-sport auto ", f);
-		else
-			fprintf(f, "encap-sport %u", ntohs(sport));
+		if (is_json_context()) {
+			print_uint(PRINT_JSON,
+				   "sport",
+				   NULL,
+				   sport ? ntohs(sport) : 0);
+			print_uint(PRINT_JSON, "dport", NULL, ntohs(dport));
+			print_bool(PRINT_JSON, "csum", NULL,
+				   flags & TUNNEL_ENCAP_FLAG_CSUM);
+			print_bool(PRINT_JSON, "csum6", NULL,
+				   flags & TUNNEL_ENCAP_FLAG_CSUM6);
+			print_bool(PRINT_JSON, "remcsum", NULL,
+				   flags & TUNNEL_ENCAP_FLAG_REMCSUM);
+			close_json_object();
+		} else {
+			if (sport == 0)
+				fputs("encap-sport auto ", f);
+			else
+				fprintf(f, "encap-sport %u", ntohs(sport));
 
-		fprintf(f, "encap-dport %u ", ntohs(dport));
+			fprintf(f, "encap-dport %u ", ntohs(dport));
 
-		if (flags & TUNNEL_ENCAP_FLAG_CSUM)
-			fputs("encap-csum ", f);
-		else
-			fputs("noencap-csum ", f);
+			if (flags & TUNNEL_ENCAP_FLAG_CSUM)
+				fputs("encap-csum ", f);
+			else
+				fputs("noencap-csum ", f);
 
-		if (flags & TUNNEL_ENCAP_FLAG_CSUM6)
-			fputs("encap-csum6 ", f);
-		else
-			fputs("noencap-csum6 ", f);
+			if (flags & TUNNEL_ENCAP_FLAG_CSUM6)
+				fputs("encap-csum6 ", f);
+			else
+				fputs("noencap-csum6 ", f);
 
-		if (flags & TUNNEL_ENCAP_FLAG_REMCSUM)
-			fputs("encap-remcsum ", f);
-		else
-			fputs("noencap-remcsum ", f);
+			if (flags & TUNNEL_ENCAP_FLAG_REMCSUM)
+				fputs("encap-remcsum ", f);
+			else
+				fputs("noencap-remcsum ", f);
+		}
 	}
 }
 
 static void ip6tunnel_print_help(struct link_util *lu, int argc, char **argv,
-	FILE *f)
+				 FILE *f)
 {
 	print_usage(f);
 }
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH iproute2 json v2 23/27] ip: link_iptnl.c: add json output support
  2017-08-17 17:35 [PATCH iproute2 json v2 00/27] ip: add -json support to 'ip link show' Julien Fortin
                   ` (21 preceding siblings ...)
  2017-08-17 17:36 ` [PATCH iproute2 json v2 22/27] ip: link_ip6tnl.c: " Julien Fortin
@ 2017-08-17 17:36 ` Julien Fortin
  2017-08-17 17:36 ` [PATCH iproute2 json v2 24/27] ip: link_vti.c: " Julien Fortin
                   ` (5 subsequent siblings)
  28 siblings, 0 replies; 33+ messages in thread
From: Julien Fortin @ 2017-08-17 17:36 UTC (permalink / raw)
  To: netdev; +Cc: roopa, nikolay, dsa, Julien Fortin

From: Julien Fortin <julien@cumulusnetworks.com>

Schema
{
    "remote": {
        "type": "string",
        "attr": "IFLA_IPTUN_REMOTE"
    },
    "local": {
        "type": "string",
        "attr": "IFLA_IPTUN_LOCAL"
    },
    "link": {
        "type": "string",
        "attr": "IFLA_IPTUN_LINK",
        "mutually_exclusive": {
            "link_index": {
                "type": "uint",
            }
        }
    },
    "ttl": {
        "type": "int",
        "attr": "IFLA_IPTUN_TTL"
    },
    "tos": {
        "type": "string",
        "attr": "IFLA_IPTUN_TOS"
    },
    "pmtudisc": {
        "type": "bool",
        "attr": "IFLA_IPTUN_PMTUDISC"
    },
    "isatap": {
        "type": "bool",
        "attr": "SIT_ISATAP & IFLA_IPTUN_FLAGS"
    },
    "6rd": {
        "type": "dict",
        "attr": "IFLA_IPTUN_6RD_PREFIXLEN",
        "dict": {
            "prefix": {
                "type": "string"
            },
            "prefixlen": {
                "type": "uint",
                "attr": "IFLA_IPTUN_6RD_PREFIXLEN"
            },
            "relay_prefix": {
                "type": "string"
            },
            "relay_prefixlen": {
                "type": "uint",
                "attr": "IFLA_IPTUN_6RD_PREFIXLEN"
            }
        }
    },
    "encap": {
        "type": "dict",
        "attr": "IFLA_IPTUN_ENCAP_TYPE",
        "dict": {
            "type": {
                "type": "string",
                "attr": "IFLA_IPTUN_ENCAP_TYPE"
            },
            "sport": {
                "type": "uint",
                "attr": "IFLA_IPTUN_ENCAP_SPORT"
            },
            "dport": {
                "type": "uint",
                "attr": "IFLA_IPTUN_ENCAP_DPORT"
            },
            "csum": {
                "type": "bool",
                "attr": "TUNNEL_ENCAP_FLAG_CSUM"
            },
            "csum6": {
                "type": "bool",
                "attr": "TUNNEL_ENCAP_FLAG_CSUM6"
            },
            "remcsum": {
                "type": "bool",
                "attr": "TUNNEL_ENCAP_FLAG_REMCSUM"
            }
        }
    }
}

$ ip tunnel add tun0 mode ipip local 192.0.2.1 remote 198.51.100.3
$ ip link show
10: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN mode DEFAULT group
default
    link/ipip 0.0.0.0 brd 0.0.0.0
11: tun0@NONE: <POINTOPOINT,NOARP> mtu 1480 qdisc noop state DOWN mode
DEFAULT group default
    link/ipip 192.0.2.1 peer 198.51.100.3
$ ip -details -json link show
[{
        "ifindex": 10,
        "ifname": "tunl0",
        "link": null,
        "flags": ["NOARP"],
        "mtu": 1480,
        "qdisc": "noop",
        "operstate": "DOWN",
        "linkmode": "DEFAULT",
        "group": "default",
        "link_type": "ipip",
        "address": "0.0.0.0",
        "broadcast": "0.0.0.0",
        "promiscuity": 0,
        "linkinfo": {
            "info_kind": "ipip",
            "info_data": {
                "remote": "any",
                "local": "any",
                "ttl": 0,
                "pmtudisc": false
            }
        },
        "num_tx_queues": 1,
        "num_rx_queues": 1,
        "gso_max_size": 65536,
        "gso_max_segs": 65535
    },{
        "ifindex": 11,
        "ifname": "tun0",
        "link": null,
        "flags": ["POINTOPOINT","NOARP"],
        "mtu": 1480,
        "qdisc": "noop",
        "operstate": "DOWN",
        "linkmode": "DEFAULT",
        "group": "default",
        "link_type": "ipip",
        "address": "192.0.2.1",
        "link_pointtopoint": true,
        "broadcast": "198.51.100.3",
        "promiscuity": 0,
        "linkinfo": {
            "info_kind": "ipip",
            "info_data": {
                "remote": "198.51.100.3",
                "local": "192.0.2.1",
                "ttl": 0,
                "pmtudisc": true
            }
        },
        "num_tx_queues": 1,
        "num_rx_queues": 1,
        "gso_max_size": 65536,
        "gso_max_segs": 65535
    }
]

Signed-off-by: Julien Fortin <julien@cumulusnetworks.com>
---
 ip/link_iptnl.c | 155 ++++++++++++++++++++++++++++++++++++++------------------
 1 file changed, 106 insertions(+), 49 deletions(-)

diff --git a/ip/link_iptnl.c b/ip/link_iptnl.c
index d24e7376..6a725e91 100644
--- a/ip/link_iptnl.c
+++ b/ip/link_iptnl.c
@@ -398,7 +398,7 @@ static void iptunnel_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[
 			remote = format_host(AF_INET, 4, &addr);
 	}
 
-	fprintf(f, "remote %s ", remote);
+	print_string(PRINT_ANY, "remote", "remote %s ", remote);
 
 	if (tb[IFLA_IPTUN_LOCAL]) {
 		unsigned int addr = rta_getattr_u32(tb[IFLA_IPTUN_LOCAL]);
@@ -407,43 +407,55 @@ static void iptunnel_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[
 			local = format_host(AF_INET, 4, &addr);
 	}
 
-	fprintf(f, "local %s ", local);
+	print_string(PRINT_ANY, "local", "local %s ", local);
 
 	if (tb[IFLA_IPTUN_LINK] && rta_getattr_u32(tb[IFLA_IPTUN_LINK])) {
 		unsigned int link = rta_getattr_u32(tb[IFLA_IPTUN_LINK]);
 		const char *n = if_indextoname(link, s2);
 
 		if (n)
-			fprintf(f, "dev %s ", n);
+			print_string(PRINT_ANY, "link", "dev %s ", n);
 		else
-			fprintf(f, "dev %u ", link);
+			print_int(PRINT_ANY, "link_index", "dev %u ", link);
 	}
 
-	if (tb[IFLA_IPTUN_TTL] && rta_getattr_u8(tb[IFLA_IPTUN_TTL]))
-		fprintf(f, "ttl %d ", rta_getattr_u8(tb[IFLA_IPTUN_TTL]));
-	else
-		fprintf(f, "ttl inherit ");
+	if (tb[IFLA_IPTUN_TTL]) {
+		__u8 ttl = rta_getattr_u8(tb[IFLA_IPTUN_TTL]);
+
+		if (ttl)
+			print_int(PRINT_ANY, "ttl", "ttl %d ", ttl);
+		else
+			print_int(PRINT_JSON, "ttl", NULL, ttl);
+	} else {
+		print_string(PRINT_FP, NULL, "ttl %s ", "inherit");
+	}
 
-	if (tb[IFLA_IPTUN_TOS] && rta_getattr_u8(tb[IFLA_IPTUN_TOS])) {
+	if (tb[IFLA_IPTUN_TOS]) {
 		int tos = rta_getattr_u8(tb[IFLA_IPTUN_TOS]);
 
-		fputs("tos ", f);
-		if (tos == 1)
-			fputs("inherit ", f);
-		else
-			fprintf(f, "0x%x ", tos);
+		if (tos) {
+			if (is_json_context()) {
+				print_0xhex(PRINT_JSON, "tos", "%#x", tos);
+			} else {
+				fputs("tos ", f);
+				if (tos == 1)
+					fputs("inherit ", f);
+				else
+					fprintf(f, "0x%x ", tos);
+			}
+		}
 	}
 
 	if (tb[IFLA_IPTUN_PMTUDISC] && rta_getattr_u8(tb[IFLA_IPTUN_PMTUDISC]))
-		fprintf(f, "pmtudisc ");
+		print_bool(PRINT_ANY, "pmtudisc", "pmtudisc ", true);
 	else
-		fprintf(f, "nopmtudisc ");
+		print_bool(PRINT_ANY, "pmtudisc", "nopmtudisc ", false);
 
 	if (tb[IFLA_IPTUN_FLAGS]) {
 		__u16 iflags = rta_getattr_u16(tb[IFLA_IPTUN_FLAGS]);
 
 		if (iflags & SIT_ISATAP)
-			fprintf(f, "isatap ");
+			print_bool(PRINT_ANY, "isatap", "isatap ", true);
 	}
 
 	if (tb[IFLA_IPTUN_6RD_PREFIXLEN] &&
@@ -453,14 +465,32 @@ static void iptunnel_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[
 		__u32 relayprefix =
 			rta_getattr_u32(tb[IFLA_IPTUN_6RD_RELAY_PREFIX]);
 
-		printf("6rd-prefix %s/%u ",
-		       inet_ntop(AF_INET6, RTA_DATA(tb[IFLA_IPTUN_6RD_PREFIX]),
-				 s1, sizeof(s1)),
-		       prefixlen);
-		if (relayprefix) {
-			printf("6rd-relay_prefix %s/%u ",
-			       format_host(AF_INET, 4, &relayprefix),
-			       relayprefixlen);
+		const char *prefix = inet_ntop(AF_INET6,
+					       RTA_DATA(tb[IFLA_IPTUN_6RD_PREFIX]),
+					       s1, sizeof(s1));
+
+		if (is_json_context()) {
+			print_string(PRINT_JSON, "prefix", NULL, prefix);
+			print_int(PRINT_JSON, "prefixlen", NULL, prefixlen);
+			if (relayprefix) {
+				print_string(PRINT_JSON,
+					     "relay_prefix",
+					     NULL,
+					     format_host(AF_INET,
+							 4,
+							 &relayprefix));
+				print_int(PRINT_JSON,
+					  "relay_prefixlen",
+					  NULL,
+					  relayprefixlen);
+			}
+		} else {
+			printf("6rd-prefix %s/%u ", prefix, prefixlen);
+			if (relayprefix) {
+				printf("6rd-relay_prefix %s/%u ",
+				       format_host(AF_INET, 4, &relayprefix),
+				       relayprefixlen);
+			}
 		}
 	}
 
@@ -470,45 +500,72 @@ static void iptunnel_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[
 		__u16 sport = rta_getattr_u16(tb[IFLA_IPTUN_ENCAP_SPORT]);
 		__u16 dport = rta_getattr_u16(tb[IFLA_IPTUN_ENCAP_DPORT]);
 
-		fputs("encap ", f);
+		print_string(PRINT_FP, NULL, "encap ", NULL);
 		switch (type) {
 		case TUNNEL_ENCAP_FOU:
-			fputs("fou ", f);
+			print_string(PRINT_ANY, "type", "%s ", "fou");
 			break;
 		case TUNNEL_ENCAP_GUE:
-			fputs("gue ", f);
+			print_string(PRINT_ANY, "type", "%s ", "gue");
 			break;
 		default:
-			fputs("unknown ", f);
+			print_null(PRINT_ANY, "type", "unknown ", NULL);
 			break;
 		}
 
-		if (sport == 0)
-			fputs("encap-sport auto ", f);
-		else
-			fprintf(f, "encap-sport %u", ntohs(sport));
+		if (is_json_context()) {
+			print_uint(PRINT_JSON,
+				   "sport",
+				   NULL,
+				   sport ? ntohs(sport) : 0);
+			print_uint(PRINT_JSON, "dport", NULL, ntohs(dport));
+			print_bool(PRINT_JSON,
+				   "csum",
+				   NULL,
+				   flags & TUNNEL_ENCAP_FLAG_CSUM);
+			print_bool(PRINT_JSON,
+				   "csum6",
+				   NULL,
+				   flags & TUNNEL_ENCAP_FLAG_CSUM6);
+			print_bool(PRINT_JSON,
+				   "remcsum",
+				   NULL,
+				   flags & TUNNEL_ENCAP_FLAG_REMCSUM);
+			close_json_object();
+		} else {
+			if (sport == 0)
+				fputs("encap-sport auto ", f);
+			else
+				fprintf(f, "encap-sport %u", ntohs(sport));
 
-		fprintf(f, "encap-dport %u ", ntohs(dport));
+			fprintf(f, "encap-dport %u ", ntohs(dport));
 
-		if (flags & TUNNEL_ENCAP_FLAG_CSUM)
-			fputs("encap-csum ", f);
-		else
-			fputs("noencap-csum ", f);
+			if (flags & TUNNEL_ENCAP_FLAG_CSUM)
+				fputs("encap-csum ", f);
+			else
+				fputs("noencap-csum ", f);
 
-		if (flags & TUNNEL_ENCAP_FLAG_CSUM6)
-			fputs("encap-csum6 ", f);
-		else
-			fputs("noencap-csum6 ", f);
+			if (flags & TUNNEL_ENCAP_FLAG_CSUM6)
+				fputs("encap-csum6 ", f);
+			else
+				fputs("noencap-csum6 ", f);
 
-		if (flags & TUNNEL_ENCAP_FLAG_REMCSUM)
-			fputs("encap-remcsum ", f);
-		else
-			fputs("noencap-remcsum ", f);
+			if (flags & TUNNEL_ENCAP_FLAG_REMCSUM)
+				fputs("encap-remcsum ", f);
+			else
+				fputs("noencap-remcsum ", f);
+		}
 	}
 
-	if (tb[IFLA_IPTUN_FWMARK] && rta_getattr_u32(tb[IFLA_IPTUN_FWMARK]))
-		fprintf(f, "fwmark 0x%x ",
-			rta_getattr_u32(tb[IFLA_IPTUN_FWMARK]));
+	if (tb[IFLA_IPTUN_FWMARK]) {
+		__u32 fwmark = rta_getattr_u32(tb[IFLA_IPTUN_FWMARK]);
+
+		if (fwmark) {
+			snprintf(s2, sizeof(s2), "0x%x", fwmark);
+
+			print_string(PRINT_ANY, "fwmark", "fwmark %s ", s2);
+		}
+	}
 }
 
 static void iptunnel_print_help(struct link_util *lu, int argc, char **argv,
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH iproute2 json v2 24/27] ip: link_vti.c: add json output support
  2017-08-17 17:35 [PATCH iproute2 json v2 00/27] ip: add -json support to 'ip link show' Julien Fortin
                   ` (22 preceding siblings ...)
  2017-08-17 17:36 ` [PATCH iproute2 json v2 23/27] ip: link_iptnl.c: " Julien Fortin
@ 2017-08-17 17:36 ` Julien Fortin
  2017-08-17 17:36 ` [PATCH iproute2 json v2 25/27] ip: link_vti6.c: " Julien Fortin
                   ` (4 subsequent siblings)
  28 siblings, 0 replies; 33+ messages in thread
From: Julien Fortin @ 2017-08-17 17:36 UTC (permalink / raw)
  To: netdev; +Cc: roopa, nikolay, dsa, Julien Fortin

From: Julien Fortin <julien@cumulusnetworks.com>

Schema:
{
    "remote": {
        "type": "string",
        "attr": "IFLA_VTI_REMOTE"
    },
    "local": {
        "type": "string",
        "attr": "IFLA_VTI_LOCAL"
    },
    "link": {
        "type": "string",
        "attr": "IFLA_VTI_LINK",
        "mutually_exclusive": {
            "link_index": {
                "type": "uint",
            }
        }
    },
    "ikey": {
        "type": "string",
        "attr": "IFLA_VTI_IKEY"
    },
    "okey": {
        "type": "string",
        "attr": "IFLA_VTI_OKEY"
    }
}

$ ip tunnel add vti0 mode vti local 192.0.2.1 remote 198.51.100.3
$ ip link show
10: ip_vti0@NONE: <NOARP> mtu 1428 qdisc noop state DOWN mode DEFAULT group
default
    link/ipip 0.0.0.0 brd 0.0.0.0
11: vti0@NONE: <POINTOPOINT,NOARP> mtu 1428 qdisc noop state DOWN mode
DEFAULT group default
    link/ipip 192.0.2.1 peer 198.51.100.3
$ ./ip -details -json link show
[{
        "ifindex": 10,
        "ifname": "ip_vti0",
        "link": null,
        "flags": ["NOARP"],
        "mtu": 1428,
        "qdisc": "noop",
        "operstate": "DOWN",
        "linkmode": "DEFAULT",
        "group": "default",
        "link_type": "ipip",
        "address": "0.0.0.0",
        "broadcast": "0.0.0.0",
        "promiscuity": 0,
        "linkinfo": {
            "info_kind": "vti",
            "info_data": {
                "remote": "any",
                "local": "any",
                "ikey": "0.0.0.0",
                "okey": "0.0.0.0"
            }
        },
        "inet6_addr_gen_mode": "eui64",
        "num_tx_queues": 1,
        "num_rx_queues": 1,
        "gso_max_size": 65536,
        "gso_max_segs": 65535
    },{
        "ifindex": 11,
        "ifname": "vti0",
        "link": null,
        "flags": ["POINTOPOINT","NOARP"],
        "mtu": 1428,
        "qdisc": "noop",
        "operstate": "DOWN",
        "linkmode": "DEFAULT",
        "group": "default",
        "link_type": "ipip",
        "address": "192.0.2.1",
        "link_pointtopoint": true,
        "broadcast": "198.51.100.3",
        "promiscuity": 0,
        "linkinfo": {
            "info_kind": "vti",
            "info_data": {
                "remote": "198.51.100.3",
                "local": "192.0.2.1",
                "ikey": "0.0.0.0",
                "okey": "0.0.0.0"
            }
        },
        "inet6_addr_gen_mode": "eui64",
        "num_tx_queues": 1,
        "num_rx_queues": 1,
        "gso_max_size": 65536,
        "gso_max_segs": 65535
    }
]

Signed-off-by: Julien Fortin <julien@cumulusnetworks.com>
---
 ip/link_vti.c | 24 +++++++++++++++---------
 1 file changed, 15 insertions(+), 9 deletions(-)

diff --git a/ip/link_vti.c b/ip/link_vti.c
index 3ffecfac..8bd4d900 100644
--- a/ip/link_vti.c
+++ b/ip/link_vti.c
@@ -224,7 +224,7 @@ static void vti_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 			remote = format_host(AF_INET, 4, &addr);
 	}
 
-	fprintf(f, "remote %s ", remote);
+	print_string(PRINT_ANY, "remote", "remote %s ", remote);
 
 	if (tb[IFLA_VTI_LOCAL]) {
 		unsigned int addr = rta_getattr_u32(tb[IFLA_VTI_LOCAL]);
@@ -233,30 +233,36 @@ static void vti_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 			local = format_host(AF_INET, 4, &addr);
 	}
 
-	fprintf(f, "local %s ", local);
+	print_string(PRINT_ANY, "local", "local %s ", local);
 
 	if (tb[IFLA_VTI_LINK] &&
 	    (link = rta_getattr_u32(tb[IFLA_VTI_LINK]))) {
 		const char *n = if_indextoname(link, s2);
 
 		if (n)
-			fprintf(f, "dev %s ", n);
+			print_string(PRINT_ANY, "link", "dev %s ", n);
 		else
-			fprintf(f, "dev %u ", link);
+			print_uint(PRINT_ANY, "link_index", "dev %u ", link);
 	}
 
 	if (tb[IFLA_VTI_IKEY] &&
 	    (key = rta_getattr_u32(tb[IFLA_VTI_IKEY])))
-		fprintf(f, "ikey %#x ", ntohl(key));
+		print_0xhex(PRINT_ANY, "ikey", "ikey %#x ", ntohl(key));
 
 
 	if (tb[IFLA_VTI_OKEY] &&
 	    (key = rta_getattr_u32(tb[IFLA_VTI_OKEY])))
-		fprintf(f, "okey %#x ", ntohl(key));
+		print_0xhex(PRINT_ANY, "okey", "okey %#x ", ntohl(key));
 
-	if (tb[IFLA_VTI_FWMARK] && rta_getattr_u32(tb[IFLA_VTI_FWMARK])) {
-		fprintf(f, "fwmark 0x%x ",
-			rta_getattr_u32(tb[IFLA_VTI_FWMARK]));
+	if (tb[IFLA_VTI_FWMARK]) {
+		__u32 fwmark = rta_getattr_u32(tb[IFLA_VTI_FWMARK]);
+
+		if (fwmark) {
+			SPRINT_BUF(b1);
+
+			snprintf(b1, sizeof(b1), "0x%x", fwmark);
+			print_string(PRINT_ANY, "fwmark", "fwmark %s ", s2);
+		}
 	}
 }
 
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH iproute2 json v2 25/27] ip: link_vti6.c: add json output support
  2017-08-17 17:35 [PATCH iproute2 json v2 00/27] ip: add -json support to 'ip link show' Julien Fortin
                   ` (23 preceding siblings ...)
  2017-08-17 17:36 ` [PATCH iproute2 json v2 24/27] ip: link_vti.c: " Julien Fortin
@ 2017-08-17 17:36 ` Julien Fortin
  2017-08-17 17:36 ` [PATCH iproute2 json v2 26/27] ip: link_macvlan.c: " Julien Fortin
                   ` (3 subsequent siblings)
  28 siblings, 0 replies; 33+ messages in thread
From: Julien Fortin @ 2017-08-17 17:36 UTC (permalink / raw)
  To: netdev; +Cc: roopa, nikolay, dsa, Julien Fortin

From: Julien Fortin <julien@cumulusnetworks.com>

Schema:
{
    "remote": {
        "type": "string",
        "attr": "IFLA_VTI_REMOTE"
    },
    "local": {
        "type": "string",
        "attr": "IFLA_VTI_LOCAL"
    },
    "link": {
        "type": "string",
        "attr": "IFLA_VTI_LINK",
        "mutually_exclusive": {
            "link_index": {
                "type": "uint",
            }
        }
    },
    "ikey": {
        "type": "string",
        "attr": "IFLA_VTI_IKEY"
    },
    "okey": {
        "type": "string",
        "attr": "IFLA_VTI_OKEY"
    }
}

➜  ~ ip -6 tunnel add name vti6 mode vti6 local 2001:db8:1::1/64 remote
2001:0db8:85a3:0000:0000:8a2e:0370:7334
➜  ~ ip link show
10: ip6tnl0@NONE: <NOARP> mtu 1452 qdisc noop state DOWN mode DEFAULT
group default
    link/tunnel6 :: brd ::
11: ip6_vti0@NONE: <NOARP> mtu 1500 qdisc noop state DOWN mode DEFAULT
group default
    link/tunnel6 :: brd ::
12: vti6@NONE: <POINTOPOINT,NOARP> mtu 1500 qdisc noop state DOWN mode
DEFAULT group default
    link/tunnel6 2001:db8:1::1 peer 2001:db8:85a3::8a2e:370:7334
➜  ~ ./ip -details -json link show
[{
        "ifindex": 10,
        "ifname": "ip6tnl0",
        "link": null,
        "flags": ["NOARP"],
        "mtu": 1452,
        "qdisc": "noop",
        "operstate": "DOWN",
        "linkmode": "DEFAULT",
        "group": "default",
        "link_type": "tunnel6",
        "address": "::",
        "broadcast": "::",
        "promiscuity": 0,
        "linkinfo": {
            "info_kind": "ip6tnl",
            "info_data": {
                "proto": "ip6ip6",
                "remote": "::",
                "local": "::",
                "encap_limit": 0,
                "ttl": 0,
                "flowinfo_tclass": "0x00",
                "flowlabel": "0x00000",
                "flowinfo": "0x00000000"
            }
        },
        "inet6_addr_gen_mode": "eui64",
        "num_tx_queues": 1,
        "num_rx_queues": 1,
        "gso_max_size": 65536,
        "gso_max_segs": 65535
    },{
        "ifindex": 11,
        "ifname": "ip6_vti0",
        "link": null,
        "flags": ["NOARP"],
        "mtu": 1500,
        "qdisc": "noop",
        "operstate": "DOWN",
        "linkmode": "DEFAULT",
        "group": "default",
        "link_type": "tunnel6",
        "address": "::",
        "broadcast": "::",
        "promiscuity": 0,
        "linkinfo": {
            "info_kind": "vti6",
            "info_data": {
                "remote": "::",
                "local": "::",
                "ikey": "0.0.0.0",
                "okey": "0.0.0.0"
            }
        },
        "inet6_addr_gen_mode": "eui64",
        "num_tx_queues": 1,
        "num_rx_queues": 1,
        "gso_max_size": 65536,
        "gso_max_segs": 65535
    },{
        "ifindex": 12,
        "ifname": "vti6",
        "link": null,
        "flags": ["POINTOPOINT","NOARP"],
        "mtu": 1500,
        "qdisc": "noop",
        "operstate": "DOWN",
        "linkmode": "DEFAULT",
        "group": "default",
        "link_type": "tunnel6",
        "address": "2001:db8:1::1",
        "link_pointtopoint": true,
        "broadcast": "2001:db8:85a3::8a2e:370:7334",
        "promiscuity": 0,
        "linkinfo": {
            "info_kind": "vti6",
            "info_data": {
                "remote": "2001:db8:85a3::8a2e:370:7334",
                "local": "2001:db8:1::1",
                "ikey": "0.0.0.0",
                "okey": "0.0.0.0"
            }
        },
        "inet6_addr_gen_mode": "eui64",
        "num_tx_queues": 1,
        "num_rx_queues": 1,
        "gso_max_size": 65536,
        "gso_max_segs": 65535
    }
]

Signed-off-by: Julien Fortin <julien@cumulusnetworks.com>
---
 ip/link_vti6.c | 22 ++++++++++++++--------
 1 file changed, 14 insertions(+), 8 deletions(-)

diff --git a/ip/link_vti6.c b/ip/link_vti6.c
index 6ea1fc23..8198d468 100644
--- a/ip/link_vti6.c
+++ b/ip/link_vti6.c
@@ -225,7 +225,7 @@ static void vti6_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 		remote = format_host(AF_INET6, 16, &daddr);
 	}
 
-	fprintf(f, "remote %s ", remote);
+	print_string(PRINT_ANY, "remote", "remote %s ", remote);
 
 	if (tb[IFLA_VTI_LOCAL]) {
 		memcpy(&saddr, RTA_DATA(tb[IFLA_VTI_LOCAL]), sizeof(saddr));
@@ -233,29 +233,35 @@ static void vti6_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 		local = format_host(AF_INET6, 16, &saddr);
 	}
 
-	fprintf(f, "local %s ", local);
+	print_string(PRINT_ANY, "local", "local %s ", local);
 
 	if (tb[IFLA_VTI_LINK] && (link = rta_getattr_u32(tb[IFLA_VTI_LINK]))) {
 		const char *n = if_indextoname(link, s2);
 
 		if (n)
-			fprintf(f, "dev %s ", n);
+			print_string(PRINT_ANY, "link", "dev %s ", n);
 		else
-			fprintf(f, "dev %u ", link);
+			print_uint(PRINT_ANY, "link_index", "dev %u ", link);
 	}
 
 	if (tb[IFLA_VTI_IKEY]) {
 		inet_ntop(AF_INET, RTA_DATA(tb[IFLA_VTI_IKEY]), s2, sizeof(s2));
-		fprintf(f, "ikey %s ", s2);
+		print_string(PRINT_ANY, "ikey", "ikey %s ", s2);
 	}
 
 	if (tb[IFLA_VTI_OKEY]) {
 		inet_ntop(AF_INET, RTA_DATA(tb[IFLA_VTI_OKEY]), s2, sizeof(s2));
-		fprintf(f, "okey %s ", s2);
+		print_string(PRINT_ANY, "okey", "okey %s ", s2);
 	}
 
-	if (tb[IFLA_VTI_FWMARK] && rta_getattr_u32(tb[IFLA_VTI_FWMARK])) {
-		fprintf(f, "fwmark 0x%x ", rta_getattr_u32(tb[IFLA_VTI_FWMARK]));
+	if (tb[IFLA_VTI_FWMARK]) {
+		__u32 fwmark = rta_getattr_u32(tb[IFLA_VTI_FWMARK]);
+
+		if (fwmark) {
+			snprintf(s2, sizeof(s2), "0x%x", fwmark);
+
+			print_string(PRINT_ANY, "fwmark", "fwmark %s ", s2);
+		}
 	}
 }
 
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH iproute2 json v2 26/27] ip: link_macvlan.c: add json output support
  2017-08-17 17:35 [PATCH iproute2 json v2 00/27] ip: add -json support to 'ip link show' Julien Fortin
                   ` (24 preceding siblings ...)
  2017-08-17 17:36 ` [PATCH iproute2 json v2 25/27] ip: link_vti6.c: " Julien Fortin
@ 2017-08-17 17:36 ` Julien Fortin
  2017-08-17 17:36 ` [PATCH iproute2 json v2 27/27] ip: iplink_vlan.c: " Julien Fortin
                   ` (2 subsequent siblings)
  28 siblings, 0 replies; 33+ messages in thread
From: Julien Fortin @ 2017-08-17 17:36 UTC (permalink / raw)
  To: netdev; +Cc: roopa, nikolay, dsa, Julien Fortin

From: Julien Fortin <julien@cumulusnetworks.com>

Schema:
{
    "mode": {
        "type": "string",
        "attr": "IFLA_MACVLAN_MODE"
    },
    "nopromisc": {
        "type": "bool",
        "attr": "MACVLAN_FLAG_NOPROMISC"
    },
    "macaddr_count": {
        "type": "int",
        "attr": "IFLA_MACVLAN_MACADDR_COUNT"
    },
    "macaddr_data": {
        "type": "array",
        "attr": "IFLA_MACVLAN_MACADDR_DATA",
        "array": [
            {
                "type": "string"
            }
        ]
    },
}

$ ip link add name peth0 link eth0 type macvlan
$ ip -details -json link show peth0
[{
        "ifindex": 26,
        "ifname": "peth0",
        "link": "eth0",
        "flags": ["BROADCAST","MULTICAST"],
        "mtu": 1500,
        "qdisc": "noop",
        "operstate": "DOWN",
        "linkmode": "DEFAULT",
        "group": "default",
        "link_type": "ether",
        "address": "7a:84:48:3e:7b:1c",
        "broadcast": "ff:ff:ff:ff:ff:ff",
        "promiscuity": 0,
        "linkinfo": {
            "info_kind": "macvlan",
            "info_data": {
                "mode": "vepa"
            }
        },
        "inet6_addr_gen_mode": "eui64",
        "num_tx_queues": 1,
        "num_rx_queues": 1,
        "gso_max_size": 65536,
        "gso_max_segs": 65535
    }
]

Signed-off-by: Julien Fortin <julien@cumulusnetworks.com>
---
 ip/iplink_macvlan.c | 37 +++++++++++++++++++++++++------------
 1 file changed, 25 insertions(+), 12 deletions(-)

diff --git a/ip/iplink_macvlan.c b/ip/iplink_macvlan.c
index 662eb6ff..b966a615 100644
--- a/ip/iplink_macvlan.c
+++ b/ip/iplink_macvlan.c
@@ -193,13 +193,15 @@ static void macvlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]
 		return;
 
 	mode = rta_getattr_u32(tb[IFLA_MACVLAN_MODE]);
-	fprintf(f, "mode %s ",
-		  mode == MACVLAN_MODE_PRIVATE ? "private"
-		: mode == MACVLAN_MODE_VEPA    ? "vepa"
-		: mode == MACVLAN_MODE_BRIDGE  ? "bridge"
-		: mode == MACVLAN_MODE_PASSTHRU  ? "passthru"
-		: mode == MACVLAN_MODE_SOURCE  ? "source"
-		:				 "unknown");
+	print_string(PRINT_ANY,
+		     "mode",
+		     "mode %s ",
+		     mode == MACVLAN_MODE_PRIVATE ? "private"
+		     : mode == MACVLAN_MODE_VEPA    ? "vepa"
+		     : mode == MACVLAN_MODE_BRIDGE  ? "bridge"
+		     : mode == MACVLAN_MODE_PASSTHRU  ? "passthru"
+		     : mode == MACVLAN_MODE_SOURCE  ? "source"
+		     :				 "unknown");
 
 	if (!tb[IFLA_MACVLAN_FLAGS] ||
 	    RTA_PAYLOAD(tb[IFLA_MACVLAN_FLAGS]) < sizeof(__u16))
@@ -208,7 +210,7 @@ static void macvlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]
 		flags = rta_getattr_u16(tb[IFLA_MACVLAN_FLAGS]);
 
 	if (flags & MACVLAN_FLAG_NOPROMISC)
-		fprintf(f, "nopromisc ");
+		print_bool(PRINT_ANY, "nopromisc", "nopromisc ", true);
 
 	/* in source mode, there are more options to print */
 
@@ -220,7 +222,7 @@ static void macvlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]
 		return;
 
 	count = rta_getattr_u32(tb[IFLA_MACVLAN_MACADDR_COUNT]);
-	fprintf(f, "remotes (%d) ", count);
+	print_int(PRINT_ANY, "macaddr_count", "remotes (%d) ", count);
 
 	if (!tb[IFLA_MACVLAN_MACADDR_DATA])
 		return;
@@ -228,18 +230,29 @@ static void macvlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]
 	rta = RTA_DATA(tb[IFLA_MACVLAN_MACADDR_DATA]);
 	len = RTA_PAYLOAD(tb[IFLA_MACVLAN_MACADDR_DATA]);
 
+	open_json_array(PRINT_JSON, "macaddr_data");
 	for (; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
 		if (rta->rta_type != IFLA_MACVLAN_MACADDR ||
 		    RTA_PAYLOAD(rta) < 6)
 			continue;
 		addr = RTA_DATA(rta);
-		fprintf(f, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x ", addr[0],
-			addr[1], addr[2], addr[3], addr[4], addr[5]);
+		if (is_json_context()) {
+			SPRINT_BUF(b1);
+
+			snprintf(b1, sizeof(b1),
+				 "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x", addr[0],
+				 addr[1], addr[2], addr[3], addr[4], addr[5]);
+			print_string(PRINT_JSON, NULL, NULL, b1);
+		} else {
+			fprintf(f, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x ", addr[0],
+				addr[1], addr[2], addr[3], addr[4], addr[5]);
+		}
 	}
+	close_json_array(PRINT_JSON, NULL);
 }
 
 static void macvlan_print_help(struct link_util *lu, int argc, char **argv,
-	FILE *f)
+			       FILE *f)
 {
 	print_explain(lu, f);
 }
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH iproute2 json v2 27/27] ip: iplink_vlan.c: add json output support
  2017-08-17 17:35 [PATCH iproute2 json v2 00/27] ip: add -json support to 'ip link show' Julien Fortin
                   ` (25 preceding siblings ...)
  2017-08-17 17:36 ` [PATCH iproute2 json v2 26/27] ip: link_macvlan.c: " Julien Fortin
@ 2017-08-17 17:36 ` Julien Fortin
  2017-08-18  1:04 ` [PATCH iproute2 json v2 00/27] ip: add -json support to 'ip link show' Stephen Hemminger
  2017-08-23 21:51 ` Jiri Pirko
  28 siblings, 0 replies; 33+ messages in thread
From: Julien Fortin @ 2017-08-17 17:36 UTC (permalink / raw)
  To: netdev; +Cc: roopa, nikolay, dsa, Julien Fortin

From: Julien Fortin <julien@cumulusnetworks.com>

Schema:
{
    "protocol": {
        "type": "string",
        "attr": "IFLA_VLAN_PROTOCOL"
    },
    "id": {
        "type": "uint",
        "attr": "IFLA_VLAN_ID"
    },
    "flags": {
        "type": "array",
        "attr": "IFLA_VLAN_FLAGS",
        "array": [
            {
                "type": "string"
            }
        ]
    },
    "ingress_qos": {
        "type": "array",
        "attr": "IFLA_VLAN_INGRESS_QOS",
        "array": [
            {
                "type": "dict",
                "dict": {
                    "from": {
                        "type": "uint"
                    },
                    "to": {
                        "type": "uint"
                    }
                }
            }
        ]
    },
    "egress_qos": {
        "type": "array",
        "attr": "IFLA_VLAN_EGRESS_QOS",
        "array": [
            {
                "type": "dict",
                "dict": {
                    "from": {
                        "type": "uint"
                    },
                    "to": {
                        "type": "uint"
                    }
                }
            }
        ]
    }
}

$ ip link add name eth0.42 link eth0 type vlan id 42
$ ip -details -json link show
[{
        "ifindex": 30,
        "ifname": "eth0.42",
        "link": "eth0",
        "flags": ["BROADCAST","MULTICAST"],
        "mtu": 1500,
        "qdisc": "noop",
        "operstate": "DOWN",
        "linkmode": "DEFAULT",
        "group": "default",
        "link_type": "ether",
        "address": "08:00:27:db:31:88",
        "broadcast": "ff:ff:ff:ff:ff:ff",
        "promiscuity": 0,
        "linkinfo": {
            "info_kind": "vlan",
            "info_data": {
                "protocol": "802.1Q",
                "id": 42,
                "flags": ["REORDER_HDR"]
            }
        },
        "inet6_addr_gen_mode": "eui64",
        "num_tx_queues": 1,
        "num_rx_queues": 1,
        "gso_max_size": 65536,
        "gso_max_segs": 65535
    }
]

Signed-off-by: Julien Fortin <julien@cumulusnetworks.com>
---
 ip/iplink_vlan.c | 62 ++++++++++++++++++++++++++++++++++++++++----------------
 1 file changed, 44 insertions(+), 18 deletions(-)

diff --git a/ip/iplink_vlan.c b/ip/iplink_vlan.c
index b47236d8..4d78cf9e 100644
--- a/ip/iplink_vlan.c
+++ b/ip/iplink_vlan.c
@@ -164,37 +164,51 @@ static int vlan_parse_opt(struct link_util *lu, int argc, char **argv,
 	return 0;
 }
 
-static void vlan_print_map(FILE *f, char *name, struct rtattr *attr)
+static void vlan_print_map(FILE *f,
+			   const char *name_json,
+			   const char *name_fp,
+			   struct rtattr *attr)
 {
 	struct ifla_vlan_qos_mapping *m;
 	struct rtattr *i;
 	int rem;
 
-	fprintf(f, "\n      %s { ", name);
+	open_json_array(PRINT_JSON, name_json);
+	print_string(PRINT_FP, NULL, "\n      %s { ", name_fp);
 
 	rem = RTA_PAYLOAD(attr);
 	for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
 		m = RTA_DATA(i);
-		fprintf(f, "%u:%u ", m->from, m->to);
+
+		if (is_json_context()) {
+			open_json_object(NULL);
+			print_uint(PRINT_JSON, "from", NULL, m->from);
+			print_uint(PRINT_JSON, "to", NULL, m->to);
+			close_json_object();
+		} else {
+			fprintf(f, "%u:%u ", m->from, m->to);
+		}
 	}
-	fprintf(f, "} ");
+
+	close_json_array(PRINT_JSON, NULL);
+	print_string(PRINT_FP, NULL, "%s ", "}");
 }
 
 static void vlan_print_flags(FILE *fp, __u32 flags)
 {
-	fprintf(fp, "<");
-#define _PF(f)	if (flags & VLAN_FLAG_##f) { \
-			flags &= ~VLAN_FLAG_##f; \
-			fprintf(fp, #f "%s", flags ? "," : ""); \
-		}
+	open_json_array(PRINT_ANY, is_json_context() ? "flags" : "<");
+#define _PF(f)	if (flags & VLAN_FLAG_##f) {				\
+		flags &= ~VLAN_FLAG_##f;				\
+		print_string(PRINT_ANY, NULL, flags ? "%s," : "%s", #f); \
+	}
 	_PF(REORDER_HDR);
 	_PF(GVRP);
 	_PF(MVRP);
 	_PF(LOOSE_BINDING);
 #undef _PF
 	if (flags)
-		fprintf(fp, "%x", flags);
-	fprintf(fp, "> ");
+		print_hex(PRINT_ANY, NULL, "%x", flags);
+	close_json_array(PRINT_ANY, "> ");
 }
 
 static void vlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
@@ -214,13 +228,19 @@ static void vlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 		return;
 
 	if (tb[IFLA_VLAN_PROTOCOL])
-		fprintf(f, "protocol %s ",
-			ll_proto_n2a(rta_getattr_u16(tb[IFLA_VLAN_PROTOCOL]),
+		print_string(PRINT_ANY,
+			     "protocol",
+			     "protocol %s ",
+			     ll_proto_n2a(
+				     rta_getattr_u16(tb[IFLA_VLAN_PROTOCOL]),
 				     b1, sizeof(b1)));
 	else
-		fprintf(f, "protocol 802.1q ");
+		print_string(PRINT_ANY, "protocol", "protocol %s ", "802.1q");
 
-	fprintf(f, "id %u ", rta_getattr_u16(tb[IFLA_VLAN_ID]));
+	print_uint(PRINT_ANY,
+		   "id",
+		   "id %u ",
+		   rta_getattr_u16(tb[IFLA_VLAN_ID]));
 
 	if (tb[IFLA_VLAN_FLAGS]) {
 		if (RTA_PAYLOAD(tb[IFLA_VLAN_FLAGS]) < sizeof(*flags))
@@ -229,13 +249,19 @@ static void vlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 		vlan_print_flags(f, flags->flags);
 	}
 	if (tb[IFLA_VLAN_INGRESS_QOS])
-		vlan_print_map(f, "ingress-qos-map", tb[IFLA_VLAN_INGRESS_QOS]);
+		vlan_print_map(f,
+			       "ingress_qos",
+			       "ingress-qos-map",
+			       tb[IFLA_VLAN_INGRESS_QOS]);
 	if (tb[IFLA_VLAN_EGRESS_QOS])
-		vlan_print_map(f, "egress-qos-map", tb[IFLA_VLAN_EGRESS_QOS]);
+		vlan_print_map(f,
+			       "egress_qos",
+			       "egress-qos-map",
+			       tb[IFLA_VLAN_EGRESS_QOS]);
 }
 
 static void vlan_print_help(struct link_util *lu, int argc, char **argv,
-	FILE *f)
+			    FILE *f)
 {
 	print_explain(f);
 }
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* Re: [PATCH iproute2 json v2 00/27] ip: add -json support to 'ip link show'
  2017-08-17 17:35 [PATCH iproute2 json v2 00/27] ip: add -json support to 'ip link show' Julien Fortin
                   ` (26 preceding siblings ...)
  2017-08-17 17:36 ` [PATCH iproute2 json v2 27/27] ip: iplink_vlan.c: " Julien Fortin
@ 2017-08-18  1:04 ` Stephen Hemminger
  2017-08-18  7:56   ` Julien Fortin
  2017-08-23 21:51 ` Jiri Pirko
  28 siblings, 1 reply; 33+ messages in thread
From: Stephen Hemminger @ 2017-08-18  1:04 UTC (permalink / raw)
  To: Julien Fortin; +Cc: netdev, roopa, nikolay, dsa

On Thu, 17 Aug 2017 10:35:47 -0700
Julien Fortin <julien@cumulusnetworks.com> wrote:

> From: Julien Fortin <julien@cumulusnetworks.com>
> 
> This patch series adds json support to 'ip [-details] link show [dev DEV]'
> Each patch describes the json schema it adds and provides some examples.
> 
> Julien Fortin (27):
>   color: add new COLOR_NONE and disable_color function
>   ip: add new command line argument -json (mutually exclusive with
>     -color)
>   json_writer: add new json handlers (null, float with format, lluint,
>     hu)
>   ip: ip_print: add new API to print JSON or regular format output
>   ip: ipaddress.c: add support for json output
>   ip: iplink.c: open/close json obj for ip -brief -json link show dev
>     DEV
>   ip: iplink_bond.c: add json output support
>   ip: iplink_bond_slave.c: add json output support (info_slave_data)
>   ip: iplink_hsr.c: add json output support
>   ip: iplink_bridge.c: add json output support
>   ip: iplink_bridge_slave.c: add json output support
>   ip: iplink_can.c: add json output support
>   ip: iplink_geneve.c: add json output support
>   ip: iplink_ipoib.c: add json output support
>   ip: iplink_ipvlan.c: add json output support
>   ip: iplink_vrf.c: add json output support
>   ip: iplink_vxlan.c: add json output support
>   ip: iplink_xdp.c: add json output support
>   ip: ipmacsec.c: add json output support
>   ip: link_gre.c: add json output support
>   ip: link_gre6.c: add json output support
>   ip: link_ip6tnl.c: add json output support
>   ip: link_iptnl.c: add json output support
>   ip: link_vti.c: add json output support
>   ip: link_vti6.c: add json output support
>   ip: link_macvlan.c: add json output support
>   ip: iplink_vlan.c: add json output support
> 
>  include/color.h          |    2 +
>  include/json_writer.h    |    9 +
>  include/utils.h          |    1 +
>  ip/Makefile              |    2 +-
>  ip/ip.c                  |    6 +
>  ip/ip_common.h           |   56 +++
>  ip/ip_print.c            |  233 ++++++++++
>  ip/ipaddress.c           | 1064 ++++++++++++++++++++++++++++++++--------------
>  ip/iplink.c              |    2 +
>  ip/iplink_bond.c         |  231 ++++++----
>  ip/iplink_bond_slave.c   |   57 ++-
>  ip/iplink_bridge.c       |  293 ++++++++-----
>  ip/iplink_bridge_slave.c |  185 ++++----
>  ip/iplink_can.c          |  282 ++++++++----
>  ip/iplink_geneve.c       |   86 +++-
>  ip/iplink_hsr.c          |   36 +-
>  ip/iplink_ipoib.c        |   30 +-
>  ip/iplink_ipvlan.c       |    8 +-
>  ip/iplink_macvlan.c      |   37 +-
>  ip/iplink_vlan.c         |   62 ++-
>  ip/iplink_vrf.c          |   13 +-
>  ip/iplink_vxlan.c        |  161 ++++---
>  ip/iplink_xdp.c          |   31 +-
>  ip/ipmacsec.c            |   84 +++-
>  ip/link_gre.c            |  147 ++++---
>  ip/link_gre6.c           |  142 +++++--
>  ip/link_ip6tnl.c         |  172 +++++---
>  ip/link_iptnl.c          |  155 ++++---
>  ip/link_vti.c            |   24 +-
>  ip/link_vti6.c           |   22 +-
>  lib/color.c              |   12 +-
>  lib/json_writer.c        |   44 +-
>  32 files changed, 2663 insertions(+), 1026 deletions(-)
>  create mode 100644 ip/ip_print.c
> 

This looks good, Thanks Julien et all.
Applied to net-next branch.
You may need to make sure that as new features get added that json support
continues to work.

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH iproute2 json v2 00/27] ip: add -json support to 'ip link show'
  2017-08-18  1:04 ` [PATCH iproute2 json v2 00/27] ip: add -json support to 'ip link show' Stephen Hemminger
@ 2017-08-18  7:56   ` Julien Fortin
  0 siblings, 0 replies; 33+ messages in thread
From: Julien Fortin @ 2017-08-18  7:56 UTC (permalink / raw)
  To: Stephen Hemminger; +Cc: netdev, Roopa Prabhu, Nikolay Aleksandrov, David Ahern

Ack, thanks Stephen.

On Thu, Aug 17, 2017 at 6:04 PM, Stephen Hemminger
<stephen@networkplumber.org> wrote:
> On Thu, 17 Aug 2017 10:35:47 -0700
> Julien Fortin <julien@cumulusnetworks.com> wrote:
>
>> From: Julien Fortin <julien@cumulusnetworks.com>
>>
>> This patch series adds json support to 'ip [-details] link show [dev DEV]'
>> Each patch describes the json schema it adds and provides some examples.
>>
>> Julien Fortin (27):
>>   color: add new COLOR_NONE and disable_color function
>>   ip: add new command line argument -json (mutually exclusive with
>>     -color)
>>   json_writer: add new json handlers (null, float with format, lluint,
>>     hu)
>>   ip: ip_print: add new API to print JSON or regular format output
>>   ip: ipaddress.c: add support for json output
>>   ip: iplink.c: open/close json obj for ip -brief -json link show dev
>>     DEV
>>   ip: iplink_bond.c: add json output support
>>   ip: iplink_bond_slave.c: add json output support (info_slave_data)
>>   ip: iplink_hsr.c: add json output support
>>   ip: iplink_bridge.c: add json output support
>>   ip: iplink_bridge_slave.c: add json output support
>>   ip: iplink_can.c: add json output support
>>   ip: iplink_geneve.c: add json output support
>>   ip: iplink_ipoib.c: add json output support
>>   ip: iplink_ipvlan.c: add json output support
>>   ip: iplink_vrf.c: add json output support
>>   ip: iplink_vxlan.c: add json output support
>>   ip: iplink_xdp.c: add json output support
>>   ip: ipmacsec.c: add json output support
>>   ip: link_gre.c: add json output support
>>   ip: link_gre6.c: add json output support
>>   ip: link_ip6tnl.c: add json output support
>>   ip: link_iptnl.c: add json output support
>>   ip: link_vti.c: add json output support
>>   ip: link_vti6.c: add json output support
>>   ip: link_macvlan.c: add json output support
>>   ip: iplink_vlan.c: add json output support
>>
>>  include/color.h          |    2 +
>>  include/json_writer.h    |    9 +
>>  include/utils.h          |    1 +
>>  ip/Makefile              |    2 +-
>>  ip/ip.c                  |    6 +
>>  ip/ip_common.h           |   56 +++
>>  ip/ip_print.c            |  233 ++++++++++
>>  ip/ipaddress.c           | 1064 ++++++++++++++++++++++++++++++++--------------
>>  ip/iplink.c              |    2 +
>>  ip/iplink_bond.c         |  231 ++++++----
>>  ip/iplink_bond_slave.c   |   57 ++-
>>  ip/iplink_bridge.c       |  293 ++++++++-----
>>  ip/iplink_bridge_slave.c |  185 ++++----
>>  ip/iplink_can.c          |  282 ++++++++----
>>  ip/iplink_geneve.c       |   86 +++-
>>  ip/iplink_hsr.c          |   36 +-
>>  ip/iplink_ipoib.c        |   30 +-
>>  ip/iplink_ipvlan.c       |    8 +-
>>  ip/iplink_macvlan.c      |   37 +-
>>  ip/iplink_vlan.c         |   62 ++-
>>  ip/iplink_vrf.c          |   13 +-
>>  ip/iplink_vxlan.c        |  161 ++++---
>>  ip/iplink_xdp.c          |   31 +-
>>  ip/ipmacsec.c            |   84 +++-
>>  ip/link_gre.c            |  147 ++++---
>>  ip/link_gre6.c           |  142 +++++--
>>  ip/link_ip6tnl.c         |  172 +++++---
>>  ip/link_iptnl.c          |  155 ++++---
>>  ip/link_vti.c            |   24 +-
>>  ip/link_vti6.c           |   22 +-
>>  lib/color.c              |   12 +-
>>  lib/json_writer.c        |   44 +-
>>  32 files changed, 2663 insertions(+), 1026 deletions(-)
>>  create mode 100644 ip/ip_print.c
>>
>
> This looks good, Thanks Julien et all.
> Applied to net-next branch.
> You may need to make sure that as new features get added that json support
> continues to work.
>
>

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH iproute2 json v2 00/27] ip: add -json support to 'ip link show'
  2017-08-17 17:35 [PATCH iproute2 json v2 00/27] ip: add -json support to 'ip link show' Julien Fortin
                   ` (27 preceding siblings ...)
  2017-08-18  1:04 ` [PATCH iproute2 json v2 00/27] ip: add -json support to 'ip link show' Stephen Hemminger
@ 2017-08-23 21:51 ` Jiri Pirko
  2017-08-23 22:03   ` Julien Fortin
  28 siblings, 1 reply; 33+ messages in thread
From: Jiri Pirko @ 2017-08-23 21:51 UTC (permalink / raw)
  To: Julien Fortin; +Cc: netdev, roopa, nikolay, dsa

Thu, Aug 17, 2017 at 07:35:47PM CEST, julien@cumulusnetworks.com wrote:
>From: Julien Fortin <julien@cumulusnetworks.com>
>
>This patch series adds json support to 'ip [-details] link show [dev DEV]'
>Each patch describes the json schema it adds and provides some examples.

Any plans to jsonify tc as well?

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH iproute2 json v2 00/27] ip: add -json support to 'ip link show'
  2017-08-23 21:51 ` Jiri Pirko
@ 2017-08-23 22:03   ` Julien Fortin
  0 siblings, 0 replies; 33+ messages in thread
From: Julien Fortin @ 2017-08-23 22:03 UTC (permalink / raw)
  To: Jiri Pirko; +Cc: netdev, Roopa Prabhu, Nikolay Aleksandrov, David Ahern

On Wed, Aug 23, 2017 at 2:51 PM, Jiri Pirko <jiri@resnulli.us> wrote:
> Thu, Aug 17, 2017 at 07:35:47PM CEST, julien@cumulusnetworks.com wrote:
>>From: Julien Fortin <julien@cumulusnetworks.com>
>>
>>This patch series adds json support to 'ip [-details] link show [dev DEV]'
>>Each patch describes the json schema it adds and provides some examples.
>
> Any plans to jsonify tc as well?

Not at the moment.
The next step would be to add support to ip addr show which shares a
lot of codes with the ip link show path (but I don't know if and when
I'd be free to do it).

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH iproute2 json v2 05/27] ip: ipaddress.c: add support for json output
  2017-08-17 17:35 ` [PATCH iproute2 json v2 05/27] ip: ipaddress.c: add support for json output Julien Fortin
@ 2017-09-20 13:33   ` Sabrina Dubroca
  0 siblings, 0 replies; 33+ messages in thread
From: Sabrina Dubroca @ 2017-09-20 13:33 UTC (permalink / raw)
  To: Julien Fortin; +Cc: netdev, roopa, nikolay, dsa

Hi Julien,

2017-08-17, 10:35:52 -0700, Julien Fortin wrote:
> From: Julien Fortin <julien@cumulusnetworks.com>
> 
> This patch converts all output (mostly fprintfs) to the new ip_print api
> which handle both regular and json output.
> Initialize a json_writer and open an array object if -json was specified.
> Note that the JSON attribute naming follows the NETLINK_ATTRIBUTE naming.
[snip]

This patch (commit d0e720111aad) changed the output of "ip addr":

Before:
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever

After:
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128scope host 
       valid_lft forever preferred_lft forever

The space following the mask is missing.

Could you have a look?
Thanks.

-- 
Sabrina

^ permalink raw reply	[flat|nested] 33+ messages in thread

end of thread, other threads:[~2017-09-20 13:34 UTC | newest]

Thread overview: 33+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-08-17 17:35 [PATCH iproute2 json v2 00/27] ip: add -json support to 'ip link show' Julien Fortin
2017-08-17 17:35 ` [PATCH iproute2 json v2 01/27] color: add new COLOR_NONE and disable_color function Julien Fortin
2017-08-17 17:35 ` [PATCH iproute2 json v2 02/27] ip: add new command line argument -json (mutually exclusive with -color) Julien Fortin
2017-08-17 17:35 ` [PATCH iproute2 json v2 03/27] json_writer: add new json handlers (null, float with format, lluint, hu) Julien Fortin
2017-08-17 17:35 ` [PATCH iproute2 json v2 04/27] ip: ip_print: add new API to print JSON or regular format output Julien Fortin
2017-08-17 17:35 ` [PATCH iproute2 json v2 05/27] ip: ipaddress.c: add support for json output Julien Fortin
2017-09-20 13:33   ` Sabrina Dubroca
2017-08-17 17:35 ` [PATCH iproute2 json v2 06/27] ip: iplink.c: open/close json obj for ip -brief -json link show dev DEV Julien Fortin
2017-08-17 17:35 ` [PATCH iproute2 json v2 07/27] ip: iplink_bond.c: add json output support Julien Fortin
2017-08-17 17:35 ` [PATCH iproute2 json v2 08/27] ip: iplink_bond_slave.c: add json output support (info_slave_data) Julien Fortin
2017-08-17 17:35 ` [PATCH iproute2 json v2 09/27] ip: iplink_hsr.c: add json output support Julien Fortin
2017-08-17 17:35 ` [PATCH iproute2 json v2 10/27] ip: iplink_bridge.c: " Julien Fortin
2017-08-17 17:35 ` [PATCH iproute2 json v2 11/27] ip: iplink_bridge_slave.c: " Julien Fortin
2017-08-17 17:35 ` [PATCH iproute2 json v2 12/27] ip: iplink_can.c: " Julien Fortin
2017-08-17 17:36 ` [PATCH iproute2 json v2 13/27] ip: iplink_geneve.c: " Julien Fortin
2017-08-17 17:36 ` [PATCH iproute2 json v2 14/27] ip: iplink_ipoib.c: " Julien Fortin
2017-08-17 17:36 ` [PATCH iproute2 json v2 15/27] ip: iplink_ipvlan.c: " Julien Fortin
2017-08-17 17:36 ` [PATCH iproute2 json v2 16/27] ip: iplink_vrf.c: " Julien Fortin
2017-08-17 17:36 ` [PATCH iproute2 json v2 17/27] ip: iplink_vxlan.c: " Julien Fortin
2017-08-17 17:36 ` [PATCH iproute2 json v2 18/27] ip: iplink_xdp.c: " Julien Fortin
2017-08-17 17:36 ` [PATCH iproute2 json v2 19/27] ip: ipmacsec.c: " Julien Fortin
2017-08-17 17:36 ` [PATCH iproute2 json v2 20/27] ip: link_gre.c: " Julien Fortin
2017-08-17 17:36 ` [PATCH iproute2 json v2 21/27] ip: link_gre6.c: " Julien Fortin
2017-08-17 17:36 ` [PATCH iproute2 json v2 22/27] ip: link_ip6tnl.c: " Julien Fortin
2017-08-17 17:36 ` [PATCH iproute2 json v2 23/27] ip: link_iptnl.c: " Julien Fortin
2017-08-17 17:36 ` [PATCH iproute2 json v2 24/27] ip: link_vti.c: " Julien Fortin
2017-08-17 17:36 ` [PATCH iproute2 json v2 25/27] ip: link_vti6.c: " Julien Fortin
2017-08-17 17:36 ` [PATCH iproute2 json v2 26/27] ip: link_macvlan.c: " Julien Fortin
2017-08-17 17:36 ` [PATCH iproute2 json v2 27/27] ip: iplink_vlan.c: " Julien Fortin
2017-08-18  1:04 ` [PATCH iproute2 json v2 00/27] ip: add -json support to 'ip link show' Stephen Hemminger
2017-08-18  7:56   ` Julien Fortin
2017-08-23 21:51 ` Jiri Pirko
2017-08-23 22:03   ` Julien Fortin

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).