From c0ab80a4907a102e48b3a8f4150e77671e5af81d Mon Sep 17 00:00:00 2001 From: Davide Caratti Date: Tue, 26 Jul 2016 11:03:18 +0200 Subject: [PATCH 1/7] man: macsec: fix macsec related typos - ip-macsec.8: fix wrong 'device' keyword in 'ip link add device eth0'; add missing description of 'validate' keyword; remove spurious bracket near 'encrypt' keyword; add missing reference to configuration of 'port' and 'sci' - ip-link.8 fix wrong 'es' and 'encoding' keywords in MACsec section Signed-off-by: Davide Caratti --- man/man8/ip-link.8.in | 8 ++++---- man/man8/ip-macsec.8 | 11 ++++++++--- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/man/man8/ip-link.8.in b/man/man8/ip-link.8.in index 2cd61337..c91ef95f 100644 --- a/man/man8/ip-link.8.in +++ b/man/man8/ip-link.8.in @@ -925,14 +925,14 @@ the following additional arguments are supported: .BR encrypt " {" .BR on " | " off " } ] [ " .BR send_sci " { " on " | " off " } ] [" -.BR es " { " on " | " off " } ] [" +.BR end_station " { " on " | " off " } ] [" .BR scb " { " on " | " off " } ] [" .BR protect " { " on " | " off " } ] [" .BR replay " { " on " | " off " }" .BR window " { " .IR 0..2^32-1 " } ] [" .BR validate " { " strict " | " check " | " disabled " } ] [" -.BR encoding " { " +.BR encodingsa " { " .IR 0..3 " } ]" .in +8 @@ -957,7 +957,7 @@ the following additional arguments are supported: - specifies whether the SCI is included in every packet, or only when it is necessary. .sp -.BR "es on " or " es off" +.BR "end_station on " or " end_station off" - sets the End Station bit. .sp @@ -985,7 +985,7 @@ the following additional arguments are supported: - sets the validation mode on the device. .sp -.BI encoding " AN " +.BI encodingsa " AN " - sets the active secure association for transmission. .in -8 diff --git a/man/man8/ip-macsec.8 b/man/man8/ip-macsec.8 index e8455d77..f928c43f 100644 --- a/man/man8/ip-macsec.8 +++ b/man/man8/ip-macsec.8 @@ -3,10 +3,14 @@ ip-macsec \- MACsec device configuration .SH "SYNOPSIS" .BI "ip link add link " DEVICE " name " NAME " type macsec " -[ [ +[ +.BI port " PORT" +| +.BI sci " SCI" +] [ [ .BR cipher " { " default " | " gcm-aes-128 " } ] " .BI icvlen " ICVLEN" -] [ [ +] [ .BR encrypt " { " on " | " off " } ] [" .BR send_sci " { " on " | " off " } ] [" .BR end_station " { " on " | " off " } ] [" @@ -15,6 +19,7 @@ ip-macsec \- MACsec device configuration .BR replay " { " on " | " off " } ] [" .BI window " WINDOW" ] [ +.BR validate " { " strict " | " check " | " disabled " } ] [" .BI encodingsa " SA" ] @@ -74,7 +79,7 @@ type. .PP .SS Create a MACsec device on link eth0 .nf -# ip link add device eth0 macsec0 type macsec port 11 encrypt on +# ip link add link eth0 macsec0 type macsec port 11 encrypt on .PP .SS Configure a secure association on that device .nf From fd4df5b21180f9b32259d859e6fcbbb08d0e03c4 Mon Sep 17 00:00:00 2001 From: Davide Caratti Date: Tue, 26 Jul 2016 11:03:19 +0200 Subject: [PATCH 2/7] ip {link,address}: add 'macsec' item to TYPE list fix output of "ip address help" and "ip link help". Update TYPE list in man pages ip-address.8 and ip-link.8 as well. Signed-off-by: Davide Caratti --- ip/ipaddress.c | 2 +- ip/iplink.c | 2 +- man/man8/ip-address.8.in | 3 ++- man/man8/ip-link.8.in | 3 ++- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/ip/ipaddress.c b/ip/ipaddress.c index 60862c57..ab4b1b14 100644 --- a/ip/ipaddress.c +++ b/ip/ipaddress.c @@ -97,7 +97,7 @@ static void usage(void) fprintf(stderr, "TYPE := { vlan | veth | vcan | dummy | ifb | macvlan | macvtap |\n"); fprintf(stderr, " bridge | bond | ipoib | ip6tnl | ipip | sit | vxlan | lowpan |\n"); fprintf(stderr, " gre | gretap | ip6gre | ip6gretap | vti | nlmon | can |\n"); - fprintf(stderr, " bond_slave | ipvlan | geneve | bridge_slave | vrf | hsr}\n"); + fprintf(stderr, " bond_slave | ipvlan | geneve | bridge_slave | vrf | hsr | macsec }\n"); exit(-1); } diff --git a/ip/iplink.c b/ip/iplink.c index ef17fd9d..f9a7e090 100644 --- a/ip/iplink.c +++ b/ip/iplink.c @@ -96,7 +96,7 @@ void iplink_usage(void) fprintf(stderr, "TYPE := { vlan | veth | vcan | dummy | ifb | macvlan | macvtap |\n"); fprintf(stderr, " bridge | bond | ipoib | ip6tnl | ipip | sit | vxlan |\n"); fprintf(stderr, " gre | gretap | ip6gre | ip6gretap | vti | nlmon |\n"); - fprintf(stderr, " bond_slave | ipvlan | geneve | bridge_slave | vrf }\n"); + fprintf(stderr, " bond_slave | ipvlan | geneve | bridge_slave | vrf | macsec }\n"); } exit(-1); } diff --git a/man/man8/ip-address.8.in b/man/man8/ip-address.8.in index 7d6eb9b2..43385813 100644 --- a/man/man8/ip-address.8.in +++ b/man/man8/ip-address.8.in @@ -127,7 +127,8 @@ ip-address \- protocol address management .BR nlmon " |" .BR ipvlan " |" .BR lowpan " |" -.BR geneve " ]" +.BR geneve " |" +.BR macsec " ]" .SH "DESCRIPTION" The diff --git a/man/man8/ip-link.8.in b/man/man8/ip-link.8.in index c91ef95f..ad49c9d2 100644 --- a/man/man8/ip-link.8.in +++ b/man/man8/ip-link.8.in @@ -184,7 +184,8 @@ ip-link \- network device configuration .BR ipvlan " |" .BR lowpan " |" .BR geneve " |" -.BR vrf " ]" +.BR vrf " |" +.BR macsec " ]" .ti -8 .IR ETYPE " := [ " TYPE " |" From 89bb6e673a6ae9dd9b6845ed95358dd6653c167e Mon Sep 17 00:00:00 2001 From: Davide Caratti Date: Tue, 26 Jul 2016 11:03:20 +0200 Subject: [PATCH 3/7] macsec: cipher and icvlen can be set separately since kernel driver has valid default values for 'cipher' and 'icvlen', there is no need for requiring users to specify both of them when a new link is added. Also, prompt an error message and exit with appropriate exit status in case of unsupported cipher suite. Signed-off-by: Davide Caratti --- ip/ipmacsec.c | 54 +++++++++++++++---------------------------- man/man8/ip-link.8.in | 6 +++++ man/man8/ip-macsec.8 | 4 ++-- 3 files changed, 26 insertions(+), 38 deletions(-) diff --git a/ip/ipmacsec.c b/ip/ipmacsec.c index 34ba341a..329be00f 100644 --- a/ip/ipmacsec.c +++ b/ip/ipmacsec.c @@ -1071,34 +1071,6 @@ static void macsec_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) } } - -static int do_cipher_suite(struct cipher_args *cipher, int *argcp, - char ***argvp) -{ - char **argv = *argvp; - int argc = *argcp; - - if (argc == 0) - return -1; - - if (strcmp(*argv, "default") == 0 || - strcmp(*argv, "gcm-aes-128") == 0 || - strcmp(*argv, "GCM-AES-128") == 0) - cipher->id = MACSEC_DEFAULT_CIPHER_ID; - NEXT_ARG(); - - if (strcmp(*argv, "icvlen") == 0) { - NEXT_ARG(); - if (cipher->icv_len != 0) - duparg2("icvlen", "icvlen"); - get_icvlen(&cipher->icv_len, *argv); - } - *argcp = argc; - *argvp = argv; - - return 0; -} - static bool check_txsc_flags(bool es, bool scb, bool sci) { if (sci && (es || scb)) @@ -1112,7 +1084,8 @@ static void usage(FILE *f) { fprintf(f, "Usage: ... macsec [ port PORT | sci SCI ]\n" - " [ cipher CIPHER_SUITE ]\n" + " [ cipher { default | gcm-aes-128 } ]\n" + " [ icvlen { 8..16 } ]\n" " [ encrypt { on | off } ]\n" " [ send_sci { on | off } ]\n" " [ end_station { on | off } ]\n" @@ -1122,7 +1095,6 @@ static void usage(FILE *f) " [ validate { strict | check | disabled } ]\n" " [ encodingsa { 0..3 } ]\n" ); - fprintf(f, "CIPHER_SUITE := [ default = gcm-aes-128 ] icvlen { 8..32 }\n"); } static int macsec_parse_opt(struct link_util *lu, int argc, char **argv, @@ -1154,11 +1126,21 @@ static int macsec_parse_opt(struct link_util *lu, int argc, char **argv, while (argc > 0) { if (strcmp(*argv, "cipher") == 0) { - if (cipher.id) - duparg2("cipher", "cipher"); NEXT_ARG(); - if (do_cipher_suite(&cipher, &argc, &argv)) - return -1; + if (cipher.id) + duparg("cipher", *argv); + if (strcmp(*argv, "default") == 0 || + strcmp(*argv, "gcm-aes-128") == 0 || + strcmp(*argv, "GCM-AES-128") == 0) + cipher.id = MACSEC_DEFAULT_CIPHER_ID; + else + invarg("expected: default or gcm-aes-128", + *argv); + } else if (strcmp(*argv, "icvlen") == 0) { + NEXT_ARG(); + if (cipher.icv_len) + duparg("icvlen", *argv); + get_icvlen(&cipher.icv_len, *argv); } else if (strcmp(*argv, "encrypt") == 0) { NEXT_ARG(); int i; @@ -1264,12 +1246,12 @@ static int macsec_parse_opt(struct link_util *lu, int argc, char **argv, return -1; } - if (cipher.id) { + if (cipher.id) addattr_l(hdr, MACSEC_BUFLEN, IFLA_MACSEC_CIPHER_SUITE, &cipher.id, sizeof(cipher.id)); + if (cipher.icv_len) addattr_l(hdr, MACSEC_BUFLEN, IFLA_MACSEC_ICV_LEN, &cipher.icv_len, sizeof(cipher.icv_len)); - } if (replay_protect != -1) { addattr32(hdr, MACSEC_BUFLEN, IFLA_MACSEC_WINDOW, window); diff --git a/man/man8/ip-link.8.in b/man/man8/ip-link.8.in index ad49c9d2..f4782ee5 100644 --- a/man/man8/ip-link.8.in +++ b/man/man8/ip-link.8.in @@ -923,6 +923,8 @@ the following additional arguments are supported: ] [ .BI cipher " CIPHER_SUITE" ] [ +.BR icvlen " { " +.IR 8..16 " } ] [" .BR encrypt " {" .BR on " | " off " } ] [ " .BR send_sci " { " on " | " off " } ] [" @@ -949,6 +951,10 @@ the following additional arguments are supported: .BI cipher " CIPHER_SUITE " - defines the cipher suite to use. +.sp +.BI icvlen " LENGTH " +- sets the length of the Integrity Check Value (ICV). + .sp .BR "encrypt on " or " encrypt off" - switches between authenticated encryption, or authenticity mode only. diff --git a/man/man8/ip-macsec.8 b/man/man8/ip-macsec.8 index f928c43f..105aeecd 100644 --- a/man/man8/ip-macsec.8 +++ b/man/man8/ip-macsec.8 @@ -7,8 +7,8 @@ ip-macsec \- MACsec device configuration .BI port " PORT" | .BI sci " SCI" -] [ [ -.BR cipher " { " default " | " gcm-aes-128 " } ] " +] [ +.BR cipher " { " default " | " gcm-aes-128 " } ] [" .BI icvlen " ICVLEN" ] [ .BR encrypt " { " on " | " off " } ] [" From 7a9466dbcba1918a1c93de8f93b9ff3d62418fcf Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 22 Jul 2016 18:34:29 +0200 Subject: [PATCH 4/7] devlink: write usage help messages to stderr In order to not confuse reader, write help messages into stderr. Signed-off-by: Jiri Pirko --- devlink/devlink.c | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/devlink/devlink.c b/devlink/devlink.c index ffefa86d..f73ba952 100644 --- a/devlink/devlink.c +++ b/devlink/devlink.c @@ -909,7 +909,7 @@ static bool dl_dump_filter(struct dl *dl, struct nlattr **tb) static void cmd_dev_help(void) { - pr_out("Usage: devlink dev show [ DEV ]\n"); + pr_err("Usage: devlink dev show [ DEV ]\n"); } static void __pr_out_handle(const char *bus_name, const char *dev_name) @@ -1024,10 +1024,10 @@ static int cmd_dev(struct dl *dl) static void cmd_port_help(void) { - pr_out("Usage: devlink port show [ DEV/PORT_INDEX ]\n"); - pr_out(" devlink port set DEV/PORT_INDEX [ type { eth | ib | auto} ]\n"); - pr_out(" devlink port split DEV/PORT_INDEX count COUNT\n"); - pr_out(" devlink port unsplit DEV/PORT_INDEX\n"); + pr_err("Usage: devlink port show [ DEV/PORT_INDEX ]\n"); + pr_err(" devlink port set DEV/PORT_INDEX [ type { eth | ib | auto} ]\n"); + pr_err(" devlink port split DEV/PORT_INDEX count COUNT\n"); + pr_err(" devlink port unsplit DEV/PORT_INDEX\n"); } static const char *port_type_name(uint32_t type) @@ -1174,22 +1174,22 @@ static int cmd_port(struct dl *dl) static void cmd_sb_help(void) { - pr_out("Usage: devlink sb show [ DEV [ sb SB_INDEX ] ]\n"); - pr_out(" devlink sb pool show [ DEV [ sb SB_INDEX ] pool POOL_INDEX ]\n"); - pr_out(" devlink sb pool set DEV [ sb SB_INDEX ] pool POOL_INDEX\n"); - pr_out(" size POOL_SIZE thtype { static | dynamic }\n"); - pr_out(" devlink sb port pool show [ DEV/PORT_INDEX [ sb SB_INDEX ]\n"); - pr_out(" pool POOL_INDEX ]\n"); - pr_out(" devlink sb port pool set DEV/PORT_INDEX [ sb SB_INDEX ]\n"); - pr_out(" pool POOL_INDEX th THRESHOLD\n"); - pr_out(" devlink sb tc bind show [ DEV/PORT_INDEX [ sb SB_INDEX ] tc TC_INDEX\n"); - pr_out(" type { ingress | egress } ]\n"); - pr_out(" devlink sb tc bind set DEV/PORT_INDEX [ sb SB_INDEX ] tc TC_INDEX\n"); - pr_out(" type { ingress | egress } pool POOL_INDEX\n"); - pr_out(" th THRESHOLD\n"); - pr_out(" devlink sb occupancy show { DEV | DEV/PORT_INDEX } [ sb SB_INDEX ]\n"); - pr_out(" devlink sb occupancy snapshot DEV [ sb SB_INDEX ]\n"); - pr_out(" devlink sb occupancy clearmax DEV [ sb SB_INDEX ]\n"); + pr_err("Usage: devlink sb show [ DEV [ sb SB_INDEX ] ]\n"); + pr_err(" devlink sb pool show [ DEV [ sb SB_INDEX ] pool POOL_INDEX ]\n"); + pr_err(" devlink sb pool set DEV [ sb SB_INDEX ] pool POOL_INDEX\n"); + pr_err(" size POOL_SIZE thtype { static | dynamic }\n"); + pr_err(" devlink sb port pool show [ DEV/PORT_INDEX [ sb SB_INDEX ]\n"); + pr_err(" pool POOL_INDEX ]\n"); + pr_err(" devlink sb port pool set DEV/PORT_INDEX [ sb SB_INDEX ]\n"); + pr_err(" pool POOL_INDEX th THRESHOLD\n"); + pr_err(" devlink sb tc bind show [ DEV/PORT_INDEX [ sb SB_INDEX ] tc TC_INDEX\n"); + pr_err(" type { ingress | egress } ]\n"); + pr_err(" devlink sb tc bind set DEV/PORT_INDEX [ sb SB_INDEX ] tc TC_INDEX\n"); + pr_err(" type { ingress | egress } pool POOL_INDEX\n"); + pr_err(" th THRESHOLD\n"); + pr_err(" devlink sb occupancy show { DEV | DEV/PORT_INDEX } [ sb SB_INDEX ]\n"); + pr_err(" devlink sb occupancy snapshot DEV [ sb SB_INDEX ]\n"); + pr_err(" devlink sb occupancy clearmax DEV [ sb SB_INDEX ]\n"); } static void pr_out_sb(struct nlattr **tb) @@ -1991,7 +1991,7 @@ static int cmd_mon_show(struct dl *dl) static void cmd_mon_help(void) { - pr_out("Usage: devlink monitor [ all | OBJECT-LIST ]\n" + pr_err("Usage: devlink monitor [ all | OBJECT-LIST ]\n" "where OBJECT-LIST := { dev | port }\n"); } @@ -2010,7 +2010,7 @@ static int cmd_mon(struct dl *dl) static void help(void) { - pr_out("Usage: devlink [ OPTIONS ] OBJECT { COMMAND | help }\n" + pr_err("Usage: devlink [ OPTIONS ] OBJECT { COMMAND | help }\n" "where OBJECT := { dev | port | sb | monitor }\n" " OPTIONS := { -V[ersion] | -n[no-nice-names] }\n"); } From e3d0f0c0e3d8ac6432884e19aefd169e5c4ae179 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 22 Jul 2016 18:34:30 +0200 Subject: [PATCH 5/7] devlink: add option to generate JSON output For parsing by another app it is convenient to produce output in JSON format. Signed-off-by: Jiri Pirko --- devlink/devlink.c | 459 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 364 insertions(+), 95 deletions(-) diff --git a/devlink/devlink.c b/devlink/devlink.c index f73ba952..84fa51e8 100644 --- a/devlink/devlink.c +++ b/devlink/devlink.c @@ -24,6 +24,7 @@ #include "SNAPSHOT.h" #include "list.h" #include "mnlg.h" +#include "json_writer.h" #define pr_err(args...) fprintf(stderr, ##args) #define pr_out(args...) fprintf(stdout, ##args) @@ -151,6 +152,15 @@ struct dl { char **argv; bool no_nice_names; struct dl_opts opts; + json_writer_t *jw; + bool json_output; + bool pretty_output; + struct { + bool present; + char *bus_name; + char *dev_name; + uint32_t port_index; + } arr_last; }; static int dl_argc(struct dl *dl) @@ -912,52 +922,161 @@ static void cmd_dev_help(void) pr_err("Usage: devlink dev show [ DEV ]\n"); } -static void __pr_out_handle(const char *bus_name, const char *dev_name) +static bool cmp_arr_last_handle(struct dl *dl, const char *bus_name, + const char *dev_name) { - pr_out("%s/%s", bus_name, dev_name); + if (!dl->arr_last.present) + return false; + return strcmp(dl->arr_last.bus_name, bus_name) == 0 && + strcmp(dl->arr_last.dev_name, dev_name) == 0; } -static void pr_out_handle(struct nlattr **tb) +static void arr_last_handle_set(struct dl *dl, const char *bus_name, + const char *dev_name) { - __pr_out_handle(mnl_attr_get_str(tb[DEVLINK_ATTR_BUS_NAME]), - mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME])); + dl->arr_last.present = true; + free(dl->arr_last.dev_name); + free(dl->arr_last.bus_name); + dl->arr_last.bus_name = strdup(bus_name); + dl->arr_last.dev_name = strdup(dev_name); } -static void __pr_out_port_handle(const char *bus_name, const char *dev_name, - uint32_t port_index) +static bool should_arr_last_handle_start(struct dl *dl, const char *bus_name, + const char *dev_name) { - __pr_out_handle(bus_name, dev_name); - pr_out("/%d", port_index); + return !cmp_arr_last_handle(dl, bus_name, dev_name); } -static void pr_out_port_handle(struct nlattr **tb) +static bool should_arr_last_handle_end(struct dl *dl, const char *bus_name, + const char *dev_name) { - __pr_out_port_handle(mnl_attr_get_str(tb[DEVLINK_ATTR_BUS_NAME]), - mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME]), - mnl_attr_get_u32(tb[DEVLINK_ATTR_PORT_INDEX])); + return dl->arr_last.present && + !cmp_arr_last_handle(dl, bus_name, dev_name); } -static void __pr_out_port_handle_nice(struct dl *dl, const char *bus_name, - const char *dev_name, uint32_t port_index) +static void __pr_out_handle_start(struct dl *dl, struct nlattr **tb, + bool content, bool array) { - char *ifname; - int err; + const char *bus_name = mnl_attr_get_str(tb[DEVLINK_ATTR_BUS_NAME]); + const char *dev_name = mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME]); + char buf[32]; - if (dl->no_nice_names) - goto no_nice_names; + sprintf(buf, "%s/%s", bus_name, dev_name); - err = ifname_map_rev_lookup(dl, bus_name, dev_name, - port_index, &ifname); - if (err) - goto no_nice_names; - pr_out("%s", ifname); - return; - -no_nice_names: - __pr_out_port_handle(bus_name, dev_name, port_index); + if (dl->json_output) { + if (array) { + if (should_arr_last_handle_end(dl, bus_name, dev_name)) + jsonw_end_array(dl->jw); + if (should_arr_last_handle_start(dl, bus_name, + dev_name)) { + jsonw_name(dl->jw, buf); + jsonw_start_array(dl->jw); + jsonw_start_object(dl->jw); + arr_last_handle_set(dl, bus_name, dev_name); + } else { + jsonw_start_object(dl->jw); + } + } else { + jsonw_name(dl->jw, buf); + jsonw_start_object(dl->jw); + } + } else { + pr_out("%s%s", buf, content ? ":" : ""); + } } -static void pr_out_port_handle_nice(struct dl *dl, struct nlattr **tb) +static void pr_out_handle_start_arr(struct dl *dl, struct nlattr **tb) +{ + __pr_out_handle_start(dl, tb, true, true); +} + +static void pr_out_handle_end(struct dl *dl) +{ + if (dl->json_output) + jsonw_end_object(dl->jw); + else + pr_out("\n"); +} + +static void pr_out_handle(struct dl *dl, struct nlattr **tb) +{ + __pr_out_handle_start(dl, tb, false, false); + pr_out_handle_end(dl); +} + +static bool cmp_arr_last_port_handle(struct dl *dl, const char *bus_name, + const char *dev_name, uint32_t port_index) +{ + return cmp_arr_last_handle(dl, bus_name, dev_name) && + dl->arr_last.port_index == port_index; +} + +static void arr_last_port_handle_set(struct dl *dl, const char *bus_name, + const char *dev_name, uint32_t port_index) +{ + arr_last_handle_set(dl, bus_name, dev_name); + dl->arr_last.port_index = port_index; +} + +static bool should_arr_last_port_handle_start(struct dl *dl, + const char *bus_name, + const char *dev_name, + uint32_t port_index) +{ + return !cmp_arr_last_port_handle(dl, bus_name, dev_name, port_index); +} + +static bool should_arr_last_port_handle_end(struct dl *dl, + const char *bus_name, + const char *dev_name, + uint32_t port_index) +{ + return dl->arr_last.present && + !cmp_arr_last_port_handle(dl, bus_name, dev_name, port_index); +} + +static void __pr_out_port_handle_start(struct dl *dl, const char *bus_name, + const char *dev_name, + uint32_t port_index, bool try_nice, + bool array) +{ + static char buf[32]; + char *ifname = NULL; + + if (dl->no_nice_names || !try_nice || + ifname_map_rev_lookup(dl, bus_name, dev_name, + port_index, &ifname) != 0) + sprintf(buf, "%s/%s/%d", bus_name, dev_name, port_index); + else + sprintf(buf, "%s", ifname); + + if (dl->json_output) { + if (array) { + if (should_arr_last_port_handle_end(dl, bus_name, + dev_name, + port_index)) + jsonw_end_array(dl->jw); + if (should_arr_last_port_handle_start(dl, bus_name, + dev_name, + port_index)) { + jsonw_name(dl->jw, buf); + jsonw_start_array(dl->jw); + jsonw_start_object(dl->jw); + arr_last_port_handle_set(dl, bus_name, dev_name, + port_index); + } else { + jsonw_start_object(dl->jw); + } + } else { + jsonw_name(dl->jw, buf); + jsonw_start_object(dl->jw); + } + } else { + pr_out("%s:", buf); + } +} + +static void pr_out_port_handle_start(struct dl *dl, struct nlattr **tb, bool try_nice) { const char *bus_name; const char *dev_name; @@ -966,25 +1085,80 @@ static void pr_out_port_handle_nice(struct dl *dl, struct nlattr **tb) bus_name = mnl_attr_get_str(tb[DEVLINK_ATTR_BUS_NAME]); dev_name = mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME]); port_index = mnl_attr_get_u32(tb[DEVLINK_ATTR_PORT_INDEX]); - - __pr_out_port_handle_nice(dl, bus_name, dev_name, port_index); + __pr_out_port_handle_start(dl, bus_name, dev_name, port_index, try_nice, false); } -static void pr_out_dev(struct nlattr **tb) +static void pr_out_port_handle_start_arr(struct dl *dl, struct nlattr **tb, bool try_nice) { - pr_out_handle(tb); - pr_out("\n"); + const char *bus_name; + const char *dev_name; + uint32_t port_index; + + bus_name = mnl_attr_get_str(tb[DEVLINK_ATTR_BUS_NAME]); + dev_name = mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME]); + port_index = mnl_attr_get_u32(tb[DEVLINK_ATTR_PORT_INDEX]); + __pr_out_port_handle_start(dl, bus_name, dev_name, port_index, try_nice, true); +} + +static void pr_out_port_handle_end(struct dl *dl) +{ + if (dl->json_output) + jsonw_end_object(dl->jw); + else + pr_out("\n"); +} + + +static void pr_out_str(struct dl *dl, const char *name, const char *val) +{ + if (dl->json_output) + jsonw_string_field(dl->jw, name, val); + else + pr_out(" %s %s", name, val); +} + +static void pr_out_uint(struct dl *dl, const char *name, unsigned int val) +{ + if (dl->json_output) + jsonw_uint_field(dl->jw, name, val); + else + pr_out(" %s %u", name, val); +} + +static void pr_out_dev(struct dl *dl, struct nlattr **tb) +{ + pr_out_handle(dl, tb); +} + +static void pr_out_section_start(struct dl *dl, const char *name) +{ + if (dl->json_output) { + jsonw_start_object(dl->jw); + jsonw_name(dl->jw, name); + jsonw_start_object(dl->jw); + } +} + +static void pr_out_section_end(struct dl *dl) +{ + if (dl->json_output) { + if (dl->arr_last.present) + jsonw_end_array(dl->jw); + jsonw_end_object(dl->jw); + jsonw_end_object(dl->jw); + } } static int cmd_dev_show_cb(const struct nlmsghdr *nlh, void *data) { + struct dl *dl = data; struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME]) return MNL_CB_ERROR; - pr_out_dev(tb); + pr_out_dev(dl, tb); return MNL_CB_OK; } @@ -1005,7 +1179,10 @@ static int cmd_dev_show(struct dl *dl) return err; } - return _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dev_show_cb, NULL); + pr_out_section_start(dl, "dev"); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dev_show_cb, dl); + pr_out_section_end(dl); + return err; } static int cmd_dev(struct dl *dl) @@ -1041,38 +1218,39 @@ static const char *port_type_name(uint32_t type) } } -static void pr_out_port(struct nlattr **tb) +static void pr_out_port(struct dl *dl, struct nlattr **tb) { struct nlattr *pt_attr = tb[DEVLINK_ATTR_PORT_TYPE]; struct nlattr *dpt_attr = tb[DEVLINK_ATTR_PORT_DESIRED_TYPE]; - pr_out_port_handle(tb); - pr_out(":"); + pr_out_port_handle_start(dl, tb, false); if (pt_attr) { uint16_t port_type = mnl_attr_get_u16(pt_attr); - pr_out(" type %s", port_type_name(port_type)); + pr_out_str(dl, "type", port_type_name(port_type)); if (dpt_attr) { uint16_t des_port_type = mnl_attr_get_u16(dpt_attr); if (port_type != des_port_type) - pr_out("(%s)", port_type_name(des_port_type)); + pr_out_str(dl, "des_type", + port_type_name(des_port_type)); } } if (tb[DEVLINK_ATTR_PORT_NETDEV_NAME]) - pr_out(" netdev %s", - mnl_attr_get_str(tb[DEVLINK_ATTR_PORT_NETDEV_NAME])); + pr_out_str(dl, "netdev", + mnl_attr_get_str(tb[DEVLINK_ATTR_PORT_NETDEV_NAME])); if (tb[DEVLINK_ATTR_PORT_IBDEV_NAME]) - pr_out(" ibdev %s", - mnl_attr_get_str(tb[DEVLINK_ATTR_PORT_IBDEV_NAME])); + pr_out_str(dl, "ibdev", + mnl_attr_get_str(tb[DEVLINK_ATTR_PORT_IBDEV_NAME])); if (tb[DEVLINK_ATTR_PORT_SPLIT_GROUP]) - pr_out(" split_group %u", - mnl_attr_get_u32(tb[DEVLINK_ATTR_PORT_SPLIT_GROUP])); - pr_out("\n"); + pr_out_uint(dl, "split_group", + mnl_attr_get_u32(tb[DEVLINK_ATTR_PORT_SPLIT_GROUP])); + pr_out_port_handle_end(dl); } static int cmd_port_show_cb(const struct nlmsghdr *nlh, void *data) { + struct dl *dl = data; struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); @@ -1080,7 +1258,7 @@ static int cmd_port_show_cb(const struct nlmsghdr *nlh, void *data) if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || !tb[DEVLINK_ATTR_PORT_INDEX]) return MNL_CB_ERROR; - pr_out_port(tb); + pr_out_port(dl, tb); return MNL_CB_OK; } @@ -1101,7 +1279,10 @@ static int cmd_port_show(struct dl *dl) return err; } - return _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_port_show_cb, NULL); + pr_out_section_start(dl, "port"); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_port_show_cb, dl); + pr_out_section_end(dl); + return err; } static int cmd_port_set(struct dl *dl) @@ -1192,20 +1373,27 @@ static void cmd_sb_help(void) pr_err(" devlink sb occupancy clearmax DEV [ sb SB_INDEX ]\n"); } -static void pr_out_sb(struct nlattr **tb) +static void pr_out_sb(struct dl *dl, struct nlattr **tb) { - pr_out_handle(tb); - pr_out(": sb %u size %u ing_pools %u eg_pools %u ing_tcs %u eg_tcs %u\n", - mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX]), - mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_SIZE]), - mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_INGRESS_POOL_COUNT]), - mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_EGRESS_POOL_COUNT]), - mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_INGRESS_TC_COUNT]), - mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_EGRESS_TC_COUNT])); + pr_out_handle_start_arr(dl, tb); + pr_out_uint(dl, "sb", + mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX])); + pr_out_uint(dl, "size", + mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_SIZE])); + pr_out_uint(dl, "ing_pools", + mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_INGRESS_POOL_COUNT])); + pr_out_uint(dl, "eg_pools", + mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_EGRESS_POOL_COUNT])); + pr_out_uint(dl, "ing_tcs", + mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_INGRESS_TC_COUNT])); + pr_out_uint(dl, "eg_tcs", + mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_EGRESS_TC_COUNT])); + pr_out_handle_end(dl); } static int cmd_sb_show_cb(const struct nlmsghdr *nlh, void *data) { + struct dl *dl = data; struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); @@ -1217,7 +1405,7 @@ static int cmd_sb_show_cb(const struct nlmsghdr *nlh, void *data) !tb[DEVLINK_ATTR_SB_INGRESS_TC_COUNT] || !tb[DEVLINK_ATTR_SB_EGRESS_TC_COUNT]) return MNL_CB_ERROR; - pr_out_sb(tb); + pr_out_sb(dl, tb); return MNL_CB_OK; } @@ -1238,7 +1426,10 @@ static int cmd_sb_show(struct dl *dl) return err; } - return _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_sb_show_cb, NULL); + pr_out_section_start(dl, "sb"); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_sb_show_cb, dl); + pr_out_section_end(dl); + return err; } static const char *pool_type_name(uint8_t type) @@ -1259,19 +1450,25 @@ static const char *threshold_type_name(uint8_t type) } } -static void pr_out_sb_pool(struct nlattr **tb) +static void pr_out_sb_pool(struct dl *dl, struct nlattr **tb) { - pr_out_handle(tb); - pr_out(": sb %u pool %u type %s size %u thtype %s\n", - mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX]), - mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX]), - pool_type_name(mnl_attr_get_u8(tb[DEVLINK_ATTR_SB_POOL_TYPE])), - mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_POOL_SIZE]), - threshold_type_name(mnl_attr_get_u8(tb[DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE]))); + pr_out_handle_start_arr(dl, tb); + pr_out_uint(dl, "sb", + mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX])); + pr_out_uint(dl, "pool", + mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX])); + pr_out_str(dl, "type", + pool_type_name(mnl_attr_get_u8(tb[DEVLINK_ATTR_SB_POOL_TYPE]))); + pr_out_uint(dl, "size", + mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_POOL_SIZE])); + pr_out_str(dl, "thtype", + threshold_type_name(mnl_attr_get_u8(tb[DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE]))); + pr_out_handle_end(dl); } static int cmd_sb_pool_show_cb(const struct nlmsghdr *nlh, void *data) { + struct dl *dl = data; struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); @@ -1281,7 +1478,7 @@ static int cmd_sb_pool_show_cb(const struct nlmsghdr *nlh, void *data) !tb[DEVLINK_ATTR_SB_POOL_TYPE] || !tb[DEVLINK_ATTR_SB_POOL_SIZE] || !tb[DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE]) return MNL_CB_ERROR; - pr_out_sb_pool(tb); + pr_out_sb_pool(dl, tb); return MNL_CB_OK; } @@ -1303,7 +1500,10 @@ static int cmd_sb_pool_show(struct dl *dl) return err; } - return _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_sb_pool_show_cb, NULL); + pr_out_section_start(dl, "pool"); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_sb_pool_show_cb, dl); + pr_out_section_end(dl); + return err; } static int cmd_sb_pool_set(struct dl *dl) @@ -1341,11 +1541,14 @@ static int cmd_sb_pool(struct dl *dl) static void pr_out_sb_port_pool(struct dl *dl, struct nlattr **tb) { - pr_out_port_handle_nice(dl, tb); - pr_out(": sb %u pool %u threshold %u\n", - mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX]), - mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX]), - mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_THRESHOLD])); + pr_out_port_handle_start_arr(dl, tb, true); + pr_out_uint(dl, "sb", + mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX])); + pr_out_uint(dl, "pool", + mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX])); + pr_out_uint(dl, "threshold", + mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_THRESHOLD])); + pr_out_port_handle_end(dl); } static int cmd_sb_port_pool_show_cb(const struct nlmsghdr *nlh, void *data) @@ -1382,7 +1585,10 @@ static int cmd_sb_port_pool_show(struct dl *dl) return err; } - return _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_sb_port_pool_show_cb, dl); + pr_out_section_start(dl, "port_pool"); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_sb_port_pool_show_cb, dl); + pr_out_section_end(dl); + return 0; } static int cmd_sb_port_pool_set(struct dl *dl) @@ -1433,13 +1639,18 @@ static int cmd_sb_port(struct dl *dl) static void pr_out_sb_tc_bind(struct dl *dl, struct nlattr **tb) { - pr_out_port_handle_nice(dl, tb); - pr_out(": sb %u tc %u type %s pool %u threshold %u\n", - mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX]), - mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_TC_INDEX]), - pool_type_name(mnl_attr_get_u8(tb[DEVLINK_ATTR_SB_POOL_TYPE])), - mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX]), + pr_out_port_handle_start_arr(dl, tb, true); + pr_out_uint(dl, "sb", + mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX])); + pr_out_uint(dl, "tc", + mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_TC_INDEX])); + pr_out_str(dl, "type", + pool_type_name(mnl_attr_get_u8(tb[DEVLINK_ATTR_SB_POOL_TYPE]))); + pr_out_uint(dl, "pool", + mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX])); + pr_out_uint(dl, "threshold", mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_THRESHOLD])); + pr_out_port_handle_end(dl); } static int cmd_sb_tc_bind_show_cb(const struct nlmsghdr *nlh, void *data) @@ -1476,7 +1687,10 @@ static int cmd_sb_tc_bind_show(struct dl *dl) return err; } - return _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_sb_tc_bind_show_cb, dl); + pr_out_section_start(dl, "tc_bind"); + err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_sb_tc_bind_show_cb, dl); + pr_out_section_end(dl); + return err; } static int cmd_sb_tc_bind_set(struct dl *dl) @@ -1649,11 +1863,44 @@ static void pr_out_occ_show_item_list(const char *label, struct list_head *list, pr_out("\n"); } -static void pr_out_occ_show_port(struct occ_port *occ_port) +static void pr_out_json_occ_show_item_list(struct dl *dl, const char *label, + struct list_head *list, + bool bound_pool) { - pr_out_occ_show_item_list("pool", &occ_port->pool_list, false); - pr_out_occ_show_item_list("itc", &occ_port->ing_tc_list, true); - pr_out_occ_show_item_list("etc", &occ_port->eg_tc_list, true); + struct occ_item *occ_item; + char buf[32]; + + jsonw_name(dl->jw, label); + jsonw_start_object(dl->jw); + list_for_each_entry(occ_item, list, list) { + sprintf(buf, "%u", occ_item->index); + jsonw_name(dl->jw, buf); + jsonw_start_object(dl->jw); + if (bound_pool) + jsonw_uint_field(dl->jw, "bound_pool", + occ_item->bound_pool_index); + jsonw_uint_field(dl->jw, "current", occ_item->cur); + jsonw_uint_field(dl->jw, "max", occ_item->max); + jsonw_end_object(dl->jw); + } + jsonw_end_object(dl->jw); +} + +static void pr_out_occ_show_port(struct dl *dl, struct occ_port *occ_port) +{ + if (dl->json_output) { + pr_out_json_occ_show_item_list(dl, "pool", + &occ_port->pool_list, false); + pr_out_json_occ_show_item_list(dl, "itc", + &occ_port->ing_tc_list, true); + pr_out_json_occ_show_item_list(dl, "etc", + &occ_port->eg_tc_list, true); + } else { + pr_out("\n"); + pr_out_occ_show_item_list("pool", &occ_port->pool_list, false); + pr_out_occ_show_item_list("itc", &occ_port->ing_tc_list, true); + pr_out_occ_show_item_list("etc", &occ_port->eg_tc_list, true); + } } static void pr_out_occ_show(struct occ_show *occ_show) @@ -1663,10 +1910,10 @@ static void pr_out_occ_show(struct occ_show *occ_show) struct occ_port *occ_port; list_for_each_entry(occ_port, &occ_show->port_list, list) { - __pr_out_port_handle_nice(dl, opts->bus_name, opts->dev_name, - occ_port->port_index); - pr_out(":\n"); - pr_out_occ_show_port(occ_port); + __pr_out_port_handle_start(dl, opts->bus_name, opts->dev_name, + occ_port->port_index, true, false); + pr_out_occ_show_port(dl, occ_port); + pr_out_port_handle_end(dl); } } @@ -1793,7 +2040,9 @@ static int cmd_sb_occ_show(struct dl *dl) if (err) goto out; + pr_out_section_start(dl, "occupancy"); pr_out_occ_show(occ_show); + pr_out_section_end(dl); out: occ_show_free(occ_show); @@ -1949,7 +2198,7 @@ static int cmd_mon_show_cb(const struct nlmsghdr *nlh, void *data) if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME]) return MNL_CB_ERROR; pr_out_mon_header(genl->cmd); - pr_out_dev(tb); + pr_out_dev(dl, tb); break; case DEVLINK_CMD_PORT_GET: /* fall through */ case DEVLINK_CMD_PORT_SET: /* fall through */ @@ -1960,7 +2209,7 @@ static int cmd_mon_show_cb(const struct nlmsghdr *nlh, void *data) !tb[DEVLINK_ATTR_PORT_INDEX]) return MNL_CB_ERROR; pr_out_mon_header(genl->cmd); - pr_out_port(tb); + pr_out_port(dl, tb); break; } return MNL_CB_OK; @@ -2055,8 +2304,18 @@ static int dl_init(struct dl *dl, int argc, char **argv) pr_err("Failed to create index map\n"); goto err_ifname_map_create; } + if (dl->json_output) { + dl->jw = jsonw_new(stdout); + if (!dl->jw) { + pr_err("Failed to create JSON writer\n"); + goto err_json_new; + } + jsonw_pretty(dl->jw, dl->pretty_output); + } return 0; +err_json_new: + ifname_map_fini(dl); err_ifname_map_create: mnlg_socket_close(dl->nlg); return err; @@ -2064,6 +2323,8 @@ err_ifname_map_create: static void dl_fini(struct dl *dl) { + if (dl->json_output) + jsonw_destroy(&dl->jw); ifname_map_fini(dl); mnlg_socket_close(dl->nlg); } @@ -2088,6 +2349,8 @@ int main(int argc, char **argv) static const struct option long_options[] = { { "Version", no_argument, NULL, 'V' }, { "no-nice-names", no_argument, NULL, 'n' }, + { "json", no_argument, NULL, 'j' }, + { "pretty", no_argument, NULL, 'p' }, { NULL, 0, NULL, 0 } }; struct dl *dl; @@ -2101,7 +2364,7 @@ int main(int argc, char **argv) return EXIT_FAILURE; } - while ((opt = getopt_long(argc, argv, "Vn", + while ((opt = getopt_long(argc, argv, "Vnjp", long_options, NULL)) >= 0) { switch (opt) { @@ -2111,6 +2374,12 @@ int main(int argc, char **argv) case 'n': dl->no_nice_names = true; break; + case 'j': + dl->json_output = true; + break; + case 'p': + dl->pretty_output = true; + break; default: pr_err("Unknown option.\n"); help(); From 9579afb24e9c56c6fc4c89e2e99fe4b3de304200 Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Wed, 3 Aug 2016 11:43:45 +0200 Subject: [PATCH 6/7] tc: Fix for missing estimator initialization When switching to C99 initializers, I forgot to add this one. This means that when trying to set an estimator value, tc would complain about spurious duplicate estimator parameter. But much worse, the random variable content is sent to the kernel regardless of whether an estimator was given or not. Fixes: d17b136f7d7dd ("Use C99 style initializers everywhere") Reported-by: Stas Nichiporovich Signed-off-by: Phil Sutter --- tc/tc_qdisc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tc/tc_qdisc.c b/tc/tc_qdisc.c index bc87aab1..93c9a4c1 100644 --- a/tc/tc_qdisc.c +++ b/tc/tc_qdisc.c @@ -45,7 +45,7 @@ static int usage(void) static int tc_qdisc_modify(int cmd, unsigned int flags, int argc, char **argv) { struct qdisc_util *q = NULL; - struct tc_estimator est; + struct tc_estimator est = {}; struct { struct tc_sizespec szopts; __u16 *data; From 1eeb6fdac8707c5391f73830582344671310c5a1 Mon Sep 17 00:00:00 2001 From: Roopa Prabhu Date: Sun, 7 Aug 2016 12:37:03 -0700 Subject: [PATCH 7/7] bridge: vlan json: skip ports with empty vlans The non-json output prints 'None' for such vlans. And this can garble json output. Fixes: d82a49ce85f0 ("bridge: add json support for bridge vlan show") Signed-off-by: Roopa Prabhu --- bridge/vlan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bridge/vlan.c b/bridge/vlan.c index f262cc7f..c2e635fc 100644 --- a/bridge/vlan.c +++ b/bridge/vlan.c @@ -212,7 +212,7 @@ static int print_vlan(const struct sockaddr_nl *who, /* if AF_SPEC isn't there, vlan table is not preset for this port */ if (!tb[IFLA_AF_SPEC]) { - if (!filter_vlan) + if (!filter_vlan && !jw_global) fprintf(fp, "%s\tNone\n", ll_index_to_name(ifm->ifi_index)); return 0;