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; diff --git a/devlink/devlink.c b/devlink/devlink.c index ffefa86d..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) @@ -909,55 +919,164 @@ 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) +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) @@ -1024,10 +1201,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) @@ -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) @@ -1174,38 +1355,45 @@ 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) +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; @@ -1991,7 +2240,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 +2259,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"); } @@ -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(); 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/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-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 2cd61337..f4782ee5 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 " |" @@ -922,17 +923,19 @@ 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 " } ] [" -.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 @@ -948,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. @@ -957,7 +964,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 +992,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..105aeecd 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 " -[ [ -.BR cipher " { " default " | " gcm-aes-128 " } ] " +[ +.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 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;