summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Ahern <dsahern@gmail.com>2018-03-13 17:48:10 -0700
committerDavid Ahern <dsahern@gmail.com>2018-03-13 17:48:10 -0700
commite9625d6aead11c7ca8b64af3ad06ea451e06ea9e (patch)
treef2736bbf90ddc7b633195385904bd4e3dde8ef52
parenta121129df91104ade8f51ac9ccd6e3a84bbddf27 (diff)
parent527f85141c9e6982f73f043f85949eaf7ff498bc (diff)
Merge branch 'iproute2-master' into iproute2-next
Conflicts: bridge/mdb.c Updated bridge/bridge.c per removal of check_if_color_enabled by commit 1ca4341d2c6b ("color: disable color when json output is requested") Signed-off-by: David Ahern <dsahern@gmail.com>
-rw-r--r--README3
-rw-r--r--bridge/bridge.c4
-rw-r--r--devlink/devlink.c774
-rw-r--r--devlink/mnlg.c53
-rw-r--r--include/color.h1
-rw-r--r--include/json_writer.h6
-rw-r--r--include/libnetlink.h1
-rw-r--r--include/list.h5
-rw-r--r--ip/ip.c7
-rw-r--r--ip/ipaddress.c16
-rw-r--r--ip/iplink.c3
-rw-r--r--lib/bpf.c1
-rw-r--r--lib/color.c8
-rw-r--r--lib/json_writer.c6
-rw-r--r--lib/libnetlink.c7
-rw-r--r--lib/namespace.c22
-rw-r--r--lib/utils.c19
-rw-r--r--man/man8/devlink-dev.815
-rw-r--r--man/man8/devlink-resource.878
-rw-r--r--man/man8/devlink.81
-rw-r--r--man/man8/tc-bpf.89
-rw-r--r--man/man8/tc.831
-rw-r--r--misc/ss.c21
-rw-r--r--rdma/res.c4
-rw-r--r--tc/m_action.c2
-rw-r--r--tc/m_bpf.c1
-rw-r--r--tc/m_connmark.c1
-rw-r--r--tc/m_csum.c1
-rw-r--r--tc/m_gact.c9
-rw-r--r--tc/m_ife.c1
-rw-r--r--tc/m_mirred.c5
-rw-r--r--tc/m_nat.c1
-rw-r--r--tc/m_pedit.c1
-rw-r--r--tc/m_police.c16
-rw-r--r--tc/m_sample.c1
-rw-r--r--tc/m_skbedit.c1
-rw-r--r--tc/m_skbmod.c1
-rw-r--r--tc/m_tunnel_key.c1
-rw-r--r--tc/m_vlan.c1
-rw-r--r--tc/tc_util.c6
40 files changed, 1015 insertions, 129 deletions
diff --git a/README b/README
index 1b7f4427..f66fd5fa 100644
--- a/README
+++ b/README
@@ -1,5 +1,8 @@
This is a set of utilities for Linux networking.
+Information:
+ https://wiki.linuxfoundation.org/networking/iproute2
+
Download:
http://www.kernel.org/pub/linux/utils/net/iproute2/
diff --git a/bridge/bridge.c b/bridge/bridge.c
index e5b4c3c2..7fcfe111 100644
--- a/bridge/bridge.c
+++ b/bridge/bridge.c
@@ -200,8 +200,8 @@ main(int argc, char **argv)
_SL_ = oneline ? "\\" : "\n";
- if (json)
- check_if_color_enabled();
+ if (color && !json)
+ enable_color();
if (batch_file)
return batch(batch_file);
diff --git a/devlink/devlink.c b/devlink/devlink.c
index 220b2bb2..69c3c5d9 100644
--- a/devlink/devlink.c
+++ b/devlink/devlink.c
@@ -35,6 +35,8 @@
#define ESWITCH_INLINE_MODE_NETWORK "network"
#define ESWITCH_INLINE_MODE_TRANSPORT "transport"
+static int g_new_line_count;
+
#define pr_err(args...) fprintf(stderr, ##args)
#define pr_out(args...) \
do { \
@@ -43,6 +45,7 @@
g_indent_newline = false; \
} \
fprintf(stdout, ##args); \
+ g_new_line_count = 0; \
} while (0)
#define pr_out_sp(num, args...) \
@@ -50,6 +53,7 @@
int ret = fprintf(stdout, ##args); \
if (ret < num) \
fprintf(stdout, "%*s", num - ret, ""); \
+ g_new_line_count = 0; \
} while (0)
static int g_indent_level;
@@ -77,8 +81,11 @@ static void __pr_out_indent_dec(void)
static void __pr_out_newline(void)
{
- pr_out("\n");
- g_indent_newline = true;
+ if (g_new_line_count < 1) {
+ pr_out("\n");
+ g_indent_newline = true;
+ }
+ g_new_line_count++;
}
static int _mnlg_socket_recv_run(struct mnlg_socket *nlg,
@@ -178,6 +185,8 @@ static void ifname_map_free(struct ifname_map *ifname_map)
#define DL_OPT_DPIPE_TABLE_NAME BIT(13)
#define DL_OPT_DPIPE_TABLE_COUNTERS BIT(14)
#define DL_OPT_ESWITCH_ENCAP_MODE BIT(15)
+#define DL_OPT_RESOURCE_PATH BIT(16)
+#define DL_OPT_RESOURCE_SIZE BIT(17)
struct dl_opts {
uint32_t present; /* flags of present items */
@@ -198,6 +207,10 @@ struct dl_opts {
const char *dpipe_table_name;
bool dpipe_counters_enable;
bool eswitch_encap_mode;
+ const char *resource_path;
+ uint32_t resource_size;
+ uint32_t resource_id;
+ bool resource_id_valid;
};
struct dl {
@@ -946,6 +959,20 @@ static int dl_argv_parse(struct dl *dl, uint32_t o_required,
if (err)
return err;
o_found |= DL_OPT_ESWITCH_ENCAP_MODE;
+ } else if (dl_argv_match(dl, "path") &&
+ (o_all & DL_OPT_RESOURCE_PATH)) {
+ dl_arg_inc(dl);
+ err = dl_argv_str(dl, &opts->resource_path);
+ if (err)
+ return err;
+ o_found |= DL_OPT_RESOURCE_PATH;
+ } else if (dl_argv_match(dl, "size") &&
+ (o_all & DL_OPT_RESOURCE_SIZE)) {
+ dl_arg_inc(dl);
+ err = dl_argv_uint32_t(dl, &opts->resource_size);
+ if (err)
+ return err;
+ o_found |= DL_OPT_RESOURCE_SIZE;
} else {
pr_err("Unknown option \"%s\"\n", dl_argv(dl));
return -EINVAL;
@@ -1088,6 +1115,12 @@ static void dl_opts_put(struct nlmsghdr *nlh, struct dl *dl)
if (opts->present & DL_OPT_ESWITCH_ENCAP_MODE)
mnl_attr_put_u8(nlh, DEVLINK_ATTR_ESWITCH_ENCAP_MODE,
opts->eswitch_encap_mode);
+ if ((opts->present & DL_OPT_RESOURCE_PATH) && opts->resource_id_valid)
+ mnl_attr_put_u64(nlh, DEVLINK_ATTR_RESOURCE_ID,
+ opts->resource_id);
+ if (opts->present & DL_OPT_RESOURCE_SIZE)
+ mnl_attr_put_u64(nlh, DEVLINK_ATTR_RESOURCE_SIZE,
+ opts->resource_size);
}
static int dl_argv_parse_put(struct nlmsghdr *nlh, struct dl *dl,
@@ -1146,6 +1179,7 @@ static void cmd_dev_help(void)
pr_err(" [ inline-mode { none | link | network | transport } ]\n");
pr_err(" [ encap { disable | enable } ]\n");
pr_err(" devlink dev eswitch show DEV\n");
+ pr_err(" devlink dev reload DEV\n");
}
static bool cmp_arr_last_handle(struct dl *dl, const char *bus_name,
@@ -1401,20 +1435,22 @@ static void pr_out_array_start(struct dl *dl, const char *name)
jsonw_name(dl->jw, name);
jsonw_start_array(dl->jw);
} else {
- if (!g_indent_newline)
- __pr_out_newline();
- pr_out("%s:", name);
+ __pr_out_indent_inc();
__pr_out_newline();
+ pr_out("%s:", name);
__pr_out_indent_inc();
+ __pr_out_newline();
}
}
static void pr_out_array_end(struct dl *dl)
{
- if (dl->json_output)
+ if (dl->json_output) {
jsonw_end_array(dl->jw);
- else
+ } else {
+ __pr_out_indent_dec();
__pr_out_indent_dec();
+ }
}
static void pr_out_entry_start(struct dl *dl)
@@ -1585,6 +1621,31 @@ static int cmd_dev_show(struct dl *dl)
return err;
}
+static void cmd_dev_reload_help(void)
+{
+ pr_err("Usage: devlink dev reload [ DEV ]\n");
+}
+
+static int cmd_dev_reload(struct dl *dl)
+{
+ struct nlmsghdr *nlh;
+ int err;
+
+ if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
+ cmd_dev_reload_help();
+ return 0;
+ }
+
+ nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_RELOAD,
+ NLM_F_REQUEST | NLM_F_ACK);
+
+ err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE, 0);
+ if (err)
+ return err;
+
+ return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
+}
+
static int cmd_dev(struct dl *dl)
{
if (dl_argv_match(dl, "help")) {
@@ -1597,6 +1658,9 @@ static int cmd_dev(struct dl *dl)
} else if (dl_argv_match(dl, "eswitch")) {
dl_arg_inc(dl);
return cmd_dev_eswitch(dl);
+ } else if (dl_argv_match(dl, "reload")) {
+ dl_arg_inc(dl);
+ return cmd_dev_reload(dl);
}
pr_err("Command \"%s\" not found\n", dl_argv(dl));
return -ENOENT;
@@ -2675,12 +2739,112 @@ struct dpipe_header {
unsigned int fields_count;
};
+struct dpipe_table {
+ struct list_head list;
+ char *name;
+ unsigned int resource_id;
+ bool resource_valid;
+};
+
+struct dpipe_tables {
+ struct list_head table_list;
+};
+
+struct resource {
+ char *name;
+ uint64_t size;
+ uint64_t size_new;
+ uint64_t size_min;
+ uint64_t size_max;
+ uint64_t size_gran;
+ enum devlink_resource_unit unit;
+ bool size_valid;
+ uint64_t size_occ;
+ bool occ_valid;
+ uint64_t id;
+ struct list_head list;
+ struct list_head resource_list;
+ struct resource *parent;
+};
+
+struct resources {
+ struct list_head resource_list;
+};
+
+struct resource_ctx {
+ struct dl *dl;
+ int err;
+ struct resources *resources;
+ struct dpipe_tables *tables;
+ bool print_resources;
+ bool pending_change;
+};
+
+static struct resource *resource_alloc(void)
+{
+ struct resource *resource;
+
+ resource = calloc(1, sizeof(struct resource));
+ if (!resource)
+ return NULL;
+ INIT_LIST_HEAD(&resource->resource_list);
+ return resource;
+}
+
+static void resource_free(struct resource *resource)
+{
+ struct resource *child_resource, *tmp;
+
+ list_for_each_entry_safe(child_resource, tmp, &resource->resource_list,
+ list) {
+ free(child_resource->name);
+ resource_free(child_resource);
+ }
+ free(resource);
+}
+
+static struct resources *resources_alloc(void)
+{
+ struct resources *resources;
+
+ resources = calloc(1, sizeof(struct resources));
+ if (!resources)
+ return NULL;
+ INIT_LIST_HEAD(&resources->resource_list);
+ return resources;
+}
+
+static void resources_free(struct resources *resources)
+{
+ struct resource *resource, *tmp;
+
+ list_for_each_entry_safe(resource, tmp, &resources->resource_list, list)
+ resource_free(resource);
+}
+
+static int resource_ctx_init(struct resource_ctx *ctx, struct dl *dl)
+{
+ ctx->resources = resources_alloc();
+ if (!ctx->resources)
+ return -ENOMEM;
+ ctx->dl = dl;
+ return 0;
+}
+
+static void resource_ctx_fini(struct resource_ctx *ctx)
+{
+ resources_free(ctx->resources);
+}
+
struct dpipe_ctx {
struct dl *dl;
int err;
struct list_head global_headers;
struct list_head local_headers;
+ struct dpipe_tables *tables;
+ struct resources *resources;
bool print_headers;
+ bool print_tables;
};
static struct dpipe_header *dpipe_header_alloc(unsigned int fields_count)
@@ -2733,25 +2897,49 @@ static void dpipe_header_del(struct dpipe_header *header)
list_del(&header->list);
}
-static struct dpipe_ctx *dpipe_ctx_alloc(struct dl *dl)
+static struct dpipe_table *dpipe_table_alloc(void)
+{
+ return calloc(1, sizeof(struct dpipe_table));
+}
+
+static void dpipe_table_free(struct dpipe_table *table)
{
- struct dpipe_ctx *ctx;
+ free(table);
+}
- ctx = calloc(1, sizeof(struct dpipe_ctx));
- if (!ctx)
+static struct dpipe_tables *dpipe_tables_alloc(void)
+{
+ struct dpipe_tables *tables;
+
+ tables = calloc(1, sizeof(struct dpipe_tables));
+ if (!tables)
return NULL;
- ctx->dl = dl;
- INIT_LIST_HEAD(&ctx->global_headers);
- INIT_LIST_HEAD(&ctx->local_headers);
- return ctx;
+ INIT_LIST_HEAD(&tables->table_list);
+ return tables;
}
-static void dpipe_ctx_free(struct dpipe_ctx *ctx)
+static void dpipe_tables_free(struct dpipe_tables *tables)
{
- free(ctx);
+ struct dpipe_table *table, *tmp;
+
+ list_for_each_entry_safe(table, tmp, &tables->table_list, list)
+ dpipe_table_free(table);
+ free(tables);
+}
+
+static int dpipe_ctx_init(struct dpipe_ctx *ctx, struct dl *dl)
+{
+ ctx->tables = dpipe_tables_alloc();
+ if (!ctx->tables)
+ return -ENOMEM;
+
+ ctx->dl = dl;
+ INIT_LIST_HEAD(&ctx->global_headers);
+ INIT_LIST_HEAD(&ctx->local_headers);
+ return 0;
}
-static void dpipe_ctx_clear(struct dpipe_ctx *ctx)
+static void dpipe_ctx_fini(struct dpipe_ctx *ctx)
{
struct dpipe_header *header, *tmp;
@@ -2767,6 +2955,7 @@ static void dpipe_ctx_clear(struct dpipe_ctx *ctx)
dpipe_header_clear(header);
dpipe_header_free(header);
}
+ dpipe_tables_free(ctx->tables);
}
static const char *dpipe_header_id2s(struct dpipe_ctx *ctx,
@@ -3022,7 +3211,7 @@ static int cmd_dpipe_header_cb(const struct nlmsghdr *nlh, void *data)
static int cmd_dpipe_headers_show(struct dl *dl)
{
struct nlmsghdr *nlh;
- struct dpipe_ctx *ctx;
+ struct dpipe_ctx ctx = {};
uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
int err;
@@ -3032,20 +3221,19 @@ static int cmd_dpipe_headers_show(struct dl *dl)
if (err)
return err;
- ctx = dpipe_ctx_alloc(dl);
- if (!ctx)
- return -ENOMEM;
+ err = dpipe_ctx_init(&ctx, dl);
+ if (err)
+ return err;
- ctx->print_headers = true;
+ ctx.print_headers = true;
pr_out_section_start(dl, "header");
- err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_header_cb, ctx);
+ err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_header_cb, &ctx);
if (err)
- pr_err("error get headers %s\n", strerror(ctx->err));
+ pr_err("error get headers %s\n", strerror(ctx.err));
pr_out_section_end(dl);
- dpipe_ctx_clear(ctx);
- dpipe_ctx_free(ctx);
+ dpipe_ctx_fini(&ctx);
return err;
}
@@ -3239,11 +3427,73 @@ err_match_parse:
return -EINVAL;
}
+static struct resource *
+resource_find(struct resources *resources, struct resource *resource,
+ uint64_t resource_id)
+{
+ struct list_head *list_head;
+
+ if (!resource)
+ list_head = &resources->resource_list;
+ else
+ list_head = &resource->resource_list;
+
+ list_for_each_entry(resource, list_head, list) {
+ struct resource *child_resource;
+
+ if (resource->id == resource_id)
+ return resource;
+
+ child_resource = resource_find(resources, resource,
+ resource_id);
+ if (child_resource)
+ return child_resource;
+ }
+ return NULL;
+}
+
+static void
+resource_path_print(struct dl *dl, struct resources *resources,
+ uint64_t resource_id)
+{
+ struct resource *resource, *parent_resource;
+ const char del[] = "/";
+ int path_len = 0;
+ char *path;
+
+ resource = resource_find(resources, NULL, resource_id);
+ if (!resource)
+ return;
+
+ for (parent_resource = resource; parent_resource;
+ parent_resource = parent_resource->parent)
+ path_len += strlen(parent_resource->name) + 1;
+
+ path_len++;
+ path = calloc(1, path_len);
+ if (!path)
+ return;
+
+ path += path_len - 1;
+ for (parent_resource = resource; parent_resource;
+ parent_resource = parent_resource->parent) {
+ path -= strlen(parent_resource->name);
+ memcpy(path, parent_resource->name,
+ strlen(parent_resource->name));
+ path -= strlen(del);
+ memcpy(path, del, strlen(del));
+ }
+ pr_out_str(dl, "resource_path", path);
+ free(path);
+}
+
static int dpipe_table_show(struct dpipe_ctx *ctx, struct nlattr *nl)
{
struct nlattr *nla_table[DEVLINK_ATTR_MAX + 1] = {};
+ struct dpipe_table *table;
+ uint32_t resource_units;
bool counters_enabled;
- const char *name;
+ bool resource_valid;
uint32_t size;
int err;
@@ -3259,15 +3509,36 @@ static int dpipe_table_show(struct dpipe_ctx *ctx, struct nlattr *nl)
return -EINVAL;
}
- name = mnl_attr_get_str(nla_table[DEVLINK_ATTR_DPIPE_TABLE_NAME]);
+ table = dpipe_table_alloc();
+ if (!table)
+ return -ENOMEM;
+
+ table->name = strdup(mnl_attr_get_str(nla_table[DEVLINK_ATTR_DPIPE_TABLE_NAME]));
size = mnl_attr_get_u32(nla_table[DEVLINK_ATTR_DPIPE_TABLE_SIZE]);
counters_enabled = !!mnl_attr_get_u8(nla_table[DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED]);
- pr_out_str(ctx->dl, "name", name);
+ resource_valid = !!nla_table[DEVLINK_ATTR_DPIPE_TABLE_RESOURCE_ID];
+ if (resource_valid) {
+ table->resource_id = mnl_attr_get_u64(nla_table[DEVLINK_ATTR_DPIPE_TABLE_RESOURCE_ID]);
+ table->resource_valid = true;
+ }
+
+ list_add_tail(&table->list, &ctx->tables->table_list);
+ if (!ctx->print_tables)
+ return 0;
+
+ pr_out_str(ctx->dl, "name", table->name);
pr_out_uint(ctx->dl, "size", size);
pr_out_str(ctx->dl, "counters_enabled",
counters_enabled ? "true" : "false");
+ if (resource_valid) {
+ resource_units = mnl_attr_get_u32(nla_table[DEVLINK_ATTR_DPIPE_TABLE_RESOURCE_UNITS]);
+ resource_path_print(ctx->dl, ctx->resources,
+ table->resource_id);
+ pr_out_uint(ctx->dl, "resource_units", resource_units);
+ }
+
pr_out_array_start(ctx->dl, "match");
if (dpipe_table_matches_show(ctx, nla_table[DEVLINK_ATTR_DPIPE_TABLE_MATCHES]))
goto err_matches_show;
@@ -3292,15 +3563,18 @@ static int dpipe_tables_show(struct dpipe_ctx *ctx, struct nlattr **tb)
struct nlattr *nla_table;
mnl_attr_for_each_nested(nla_table, nla_tables) {
- pr_out_handle_start_arr(ctx->dl, tb);
+ if (ctx->print_tables)
+ pr_out_handle_start_arr(ctx->dl, tb);
if (dpipe_table_show(ctx, nla_table))
goto err_table_show;
- pr_out_handle_end(ctx->dl);
+ if (ctx->print_tables)
+ pr_out_handle_end(ctx->dl);
}
return 0;
err_table_show:
- pr_out_handle_end(ctx->dl);
+ if (ctx->print_tables)
+ pr_out_handle_end(ctx->dl);
return -EINVAL;
}
@@ -3320,39 +3594,68 @@ static int cmd_dpipe_table_show_cb(const struct nlmsghdr *nlh, void *data)
return MNL_CB_OK;
}
+static int cmd_resource_dump_cb(const struct nlmsghdr *nlh, void *data);
+
static int cmd_dpipe_table_show(struct dl *dl)
{
struct nlmsghdr *nlh;
- struct dpipe_ctx *ctx;
+ struct dpipe_ctx dpipe_ctx = {};
+ struct resource_ctx resource_ctx = {};
uint16_t flags = NLM_F_REQUEST;
int err;
- ctx = dpipe_ctx_alloc(dl);
- if (!ctx)
- return -ENOMEM;
-
err = dl_argv_parse(dl, DL_OPT_HANDLE, DL_OPT_DPIPE_TABLE_NAME);
if (err)
- goto out;
+ return err;
nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_DPIPE_HEADERS_GET, flags);
+
+ err = dpipe_ctx_init(&dpipe_ctx, dl);
+ if (err)
+ return err;
+
+ dpipe_ctx.print_tables = true;
+
dl_opts_put(nlh, dl);
- err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_header_cb, ctx);
+ err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_header_cb,
+ &dpipe_ctx);
if (err) {
- pr_err("error get headers %s\n", strerror(ctx->err));
- goto out;
+ pr_err("error get headers %s\n", strerror(dpipe_ctx.err));
+ goto err_headers_get;
}
+ err = resource_ctx_init(&resource_ctx, dl);
+ if (err)
+ goto err_resource_ctx_init;
+
+ resource_ctx.print_resources = false;
+ nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_RESOURCE_DUMP, flags);
+ dl_opts_put(nlh, dl);
+ err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_resource_dump_cb,
+ &resource_ctx);
+ if (err) {
+ pr_err("error get resources %s\n", strerror(resource_ctx.err));
+ goto err_resource_dump;
+ }
+
+ dpipe_ctx.resources = resource_ctx.resources;
flags = NLM_F_REQUEST | NLM_F_ACK;
nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_DPIPE_TABLE_GET, flags);
dl_opts_put(nlh, dl);
pr_out_section_start(dl, "table");
- _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_table_show_cb, ctx);
+ _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_table_show_cb, &dpipe_ctx);
pr_out_section_end(dl);
-out:
- dpipe_ctx_clear(ctx);
- dpipe_ctx_free(ctx);
+
+ resource_ctx_fini(&resource_ctx);
+ dpipe_ctx_fini(&dpipe_ctx);
+ return 0;
+
+err_resource_dump:
+ resource_ctx_fini(&resource_ctx);
+err_resource_ctx_init:
+err_headers_get:
+ dpipe_ctx_fini(&dpipe_ctx);
return err;
}
@@ -3720,13 +4023,13 @@ static int cmd_dpipe_table_entry_dump_cb(const struct nlmsghdr *nlh, void *data)
static int cmd_dpipe_table_dump(struct dl *dl)
{
struct nlmsghdr *nlh;
- struct dpipe_ctx *ctx;
+ struct dpipe_ctx ctx = {};
uint16_t flags = NLM_F_REQUEST;
int err;
- ctx = dpipe_ctx_alloc(dl);
- if (!ctx)
- return -ENOMEM;
+ err = dpipe_ctx_init(&ctx, dl);
+ if (err)
+ return err;
err = dl_argv_parse(dl, DL_OPT_HANDLE | DL_OPT_DPIPE_TABLE_NAME, 0);
if (err)
@@ -3734,9 +4037,9 @@ static int cmd_dpipe_table_dump(struct dl *dl)
nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_DPIPE_HEADERS_GET, flags);
dl_opts_put(nlh, dl);
- err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_header_cb, ctx);
+ err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_header_cb, &ctx);
if (err) {
- pr_err("error get headers %s\n", strerror(ctx->err));
+ pr_err("error get headers %s\n", strerror(ctx.err));
goto out;
}
@@ -3745,11 +4048,10 @@ static int cmd_dpipe_table_dump(struct dl *dl)
dl_opts_put(nlh, dl);
pr_out_section_start(dl, "table_entry");
- _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_table_entry_dump_cb, ctx);
+ _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_table_entry_dump_cb, &ctx);
pr_out_section_end(dl);
out:
- dpipe_ctx_clear(ctx);
- dpipe_ctx_free(ctx);
+ dpipe_ctx_fini(&ctx);
return err;
}
@@ -3800,11 +4102,368 @@ static int cmd_dpipe(struct dl *dl)
return -ENOENT;
}
+static int
+resource_parse(struct resource_ctx *ctx, struct resource *resource,
+ struct nlattr **nla_resource)
+{
+ if (!nla_resource[DEVLINK_ATTR_RESOURCE_NAME] ||
+ !nla_resource[DEVLINK_ATTR_RESOURCE_SIZE] ||
+ !nla_resource[DEVLINK_ATTR_RESOURCE_ID] ||
+ !nla_resource[DEVLINK_ATTR_RESOURCE_UNIT] ||
+ !nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_MIN] ||
+ !nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_MAX] ||
+ !nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_GRAN]) {
+ return -EINVAL;
+ }
+
+ resource->name = strdup(mnl_attr_get_str(nla_resource[DEVLINK_ATTR_RESOURCE_NAME]));
+ resource->size = mnl_attr_get_u64(nla_resource[DEVLINK_ATTR_RESOURCE_SIZE]);
+ resource->id = mnl_attr_get_u64(nla_resource[DEVLINK_ATTR_RESOURCE_ID]);
+ resource->unit = mnl_attr_get_u8(nla_resource[DEVLINK_ATTR_RESOURCE_UNIT]);
+ resource->size_min = mnl_attr_get_u64(nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_MIN]);
+ resource->size_max = mnl_attr_get_u64(nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_MAX]);
+ resource->size_gran = mnl_attr_get_u64(nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_GRAN]);
+
+ if (nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_NEW])
+ resource->size_new = mnl_attr_get_u64(nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_NEW]);
+ else
+ resource->size_new = resource->size;
+
+ if (nla_resource[DEVLINK_ATTR_RESOURCE_OCC]) {
+ resource->size_occ = mnl_attr_get_u64(nla_resource[DEVLINK_ATTR_RESOURCE_OCC]);
+ resource->occ_valid = true;
+ }
+
+ if (resource->size_new != resource->size)
+ ctx->pending_change = true;
+
+ return 0;
+}
+
+static int
+resource_get(struct resource_ctx *ctx, struct resource *resource,
+ struct resource *parent_resource, struct nlattr *nl)
+{
+ struct nlattr *nla_resource[DEVLINK_ATTR_MAX + 1] = {};
+ struct nlattr *nla_child_resource;
+ struct nlattr *nla_resources;
+ bool top = false;
+ int err;
+
+ if (!resource) {
+ nla_resources = nl;
+ top = true;
+ goto out;
+ }
+
+ err = mnl_attr_parse_nested(nl, attr_cb, nla_resource);
+ if (err != MNL_CB_OK)
+ return -EINVAL;
+
+ err = resource_parse(ctx, resource, nla_resource);
+ if (err)
+ return err;
+
+ resource->parent = parent_resource;
+ if (!nla_resource[DEVLINK_ATTR_RESOURCE_LIST])
+ return 0;
+
+ resource->size_valid = !!mnl_attr_get_u8(nla_resource[DEVLINK_ATTR_RESOURCE_SIZE_VALID]);
+ nla_resources = nla_resource[DEVLINK_ATTR_RESOURCE_LIST];
+out:
+ mnl_attr_for_each_nested(nla_child_resource, nla_resources) {
+ struct resource *child_resource;
+ struct list_head *list;
+
+ child_resource = resource_alloc();
+ if (!child_resource)
+ return -ENOMEM;
+
+ if (top)
+ list = &ctx->resources->resource_list;
+ else
+ list = &resource->resource_list;
+
+ list_add_tail(&child_resource->list, list);
+ err = resource_get(ctx, child_resource, resource,
+ nla_child_resource);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static const char *resource_unit_str_get(enum devlink_resource_unit unit)
+{
+ switch (unit) {
+ case DEVLINK_RESOURCE_UNIT_ENTRY: return "entry";
+ default: return "<unknown unit>";
+ }
+}
+
+static void resource_show(struct resource *resource,
+ struct resource_ctx *ctx)
+{
+ struct resource *child_resource;
+ struct dpipe_table *table;
+ struct dl *dl = ctx->dl;
+ bool array = false;
+
+ pr_out_str(dl, "name", resource->name);
+ if (dl->verbose)
+ resource_path_print(dl, ctx->resources, resource->id);
+ pr_out_uint(dl, "size", resource->size);
+ if (resource->size != resource->size_new)
+ pr_out_uint(dl, "size_new", resource->size_new);
+ if (resource->occ_valid)
+ pr_out_uint(dl, "occ", resource->size_occ);
+ pr_out_str(dl, "unit", resource_unit_str_get(resource->unit));
+
+ if (resource->size_min != resource->size_max) {
+ pr_out_uint(dl, "size_min", resource->size_min);
+ pr_out_uint(dl, "size_max", resource->size_max);
+ pr_out_uint(dl, "size_gran", resource->size_gran);
+ }
+
+ list_for_each_entry(table, &ctx->tables->table_list, list)
+ if (table->resource_id == resource->id &&
+ table->resource_valid)
+ array = true;
+
+ if (array)
+ pr_out_array_start(dl, "dpipe_tables");
+ else
+ pr_out_str(dl, "dpipe_tables", "none");
+
+ list_for_each_entry(table, &ctx->tables->table_list, list) {
+ if (table->resource_id != resource->id ||
+ !table->resource_valid)
+ continue;
+ pr_out_entry_start(dl);
+ pr_out_str(dl, "table_name", table->name);
+ pr_out_entry_end(dl);
+ }
+ if (array)
+ pr_out_array_end(dl);
+
+ if (list_empty(&resource->resource_list))
+ return;
+
+ if (ctx->pending_change)
+ pr_out_str(dl, "size_valid", resource->size_valid ?
+ "true" : "false");
+ pr_out_array_start(dl, "resources");
+ list_for_each_entry(child_resource, &resource->resource_list, list) {
+ pr_out_entry_start(dl);
+ resource_show(child_resource, ctx);
+ pr_out_entry_end(dl);
+ }
+ pr_out_array_end(dl);
+}
+
+static void
+resources_show(struct resource_ctx *ctx, struct nlattr **tb)
+{
+ struct resources *resources = ctx->resources;
+ struct resource *resource;
+
+ list_for_each_entry(resource, &resources->resource_list, list) {
+ pr_out_handle_start_arr(ctx->dl, tb);
+ resource_show(resource, ctx);
+ pr_out_handle_end(ctx->dl);
+ }
+}
+
+static int resources_get(struct resource_ctx *ctx, struct nlattr **tb)
+{
+ return resource_get(ctx, NULL, NULL, tb[DEVLINK_ATTR_RESOURCE_LIST]);
+}
+
+static int cmd_resource_dump_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct resource_ctx *ctx = data;
+ struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
+ struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
+ int err;
+
+ mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
+ if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
+ !tb[DEVLINK_ATTR_RESOURCE_LIST])
+ return MNL_CB_ERROR;
+
+ err = resources_get(ctx, tb);
+ if (err) {
+ ctx->err = err;
+ return MNL_CB_ERROR;
+ }
+
+ if (ctx->print_resources)
+ resources_show(ctx, tb);
+
+ return MNL_CB_OK;
+}
+
+static int cmd_resource_show(struct dl *dl)
+{
+ struct nlmsghdr *nlh;
+ struct dpipe_ctx dpipe_ctx = {};
+ struct resource_ctx resource_ctx = {};
+ int err;
+
+ err = dl_argv_parse(dl, DL_OPT_HANDLE, 0);
+ if (err)
+ return err;
+
+ nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_DPIPE_TABLE_GET,
+ NLM_F_REQUEST);
+ dl_opts_put(nlh, dl);
+
+ err = dpipe_ctx_init(&dpipe_ctx, dl);
+ if (err)
+ return err;
+
+ err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dpipe_table_show_cb,
+ &dpipe_ctx);
+ if (err) {
+ pr_err("error get tables %s\n", strerror(dpipe_ctx.err));
+ goto out;
+ }
+
+ err = resource_ctx_init(&resource_ctx, dl);
+ if (err)
+ goto out;
+
+ resource_ctx.print_resources = true;
+ resource_ctx.tables = dpipe_ctx.tables;
+ nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_RESOURCE_DUMP,
+ NLM_F_REQUEST | NLM_F_ACK);
+ dl_opts_put(nlh, dl);
+ pr_out_section_start(dl, "resources");
+ err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_resource_dump_cb,
+ &resource_ctx);
+ pr_out_section_end(dl);
+ resource_ctx_fini(&resource_ctx);
+out:
+ dpipe_ctx_fini(&dpipe_ctx);
+ return err;
+}
+
+static void cmd_resource_help(void)
+{
+ pr_err("Usage: devlink resource show DEV\n"
+ " devlink resource set DEV path PATH size SIZE\n");
+}
+
+static struct resource *
+resource_find_by_name(struct list_head *list, char *name)
+{
+ struct resource *resource;
+
+ list_for_each_entry(resource, list, list) {
+ if (!strcmp(resource->name, name))
+ return resource;
+ }
+ return NULL;
+}
+
+static int
+resource_path_parse(struct resource_ctx *ctx, const char *resource_path,
+ uint32_t *p_resource_id, bool *p_resource_valid)
+{
+ struct resource *resource;
+ uint32_t resource_id = 0;
+ char *resource_path_dup;
+ struct list_head *list;
+ const char del[] = "/";
+ char *resource_name;
+
+ resource_path_dup = strdup(resource_path);
+ list = &ctx->resources->resource_list;
+ resource_name = strtok(resource_path_dup, del);
+ while (resource_name != NULL) {
+ resource = resource_find_by_name(list, resource_name);
+ if (!resource)
+ goto err_resource_lookup;
+
+ list = &resource->resource_list;
+ resource_name = strtok(NULL, del);
+ resource_id = resource->id;
+ }
+ free(resource_path_dup);
+ *p_resource_valid = true;
+ *p_resource_id = resource_id;
+ return 0;
+
+err_resource_lookup:
+ free(resource_path_dup);
+ return -EINVAL;
+}
+
+static int cmd_resource_set(struct dl *dl)
+{
+ struct nlmsghdr *nlh;
+ struct resource_ctx ctx = {};
+ int err;
+
+ err = resource_ctx_init(&ctx, dl);
+ if (err)
+ return err;
+
+ ctx.print_resources = false;
+ err = dl_argv_parse(dl, DL_OPT_HANDLE | DL_OPT_RESOURCE_PATH |
+ DL_OPT_RESOURCE_SIZE, 0);
+ if (err)
+ goto out;
+
+ nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_RESOURCE_DUMP,
+ NLM_F_REQUEST);
+ dl_opts_put(nlh, dl);
+ err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_resource_dump_cb, &ctx);
+ if (err) {
+ pr_err("error getting resources %s\n", strerror(ctx.err));
+ goto out;
+ }
+
+ err = resource_path_parse(&ctx, dl->opts.resource_path,
+ &dl->opts.resource_id,
+ &dl->opts.resource_id_valid);
+ if (err) {
+ pr_err("error parsing resource path %s\n", strerror(err));
+ goto out;
+ }
+
+ nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_RESOURCE_SET,
+ NLM_F_REQUEST | NLM_F_ACK);
+
+ dl_opts_put(nlh, dl);
+ err = _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
+out:
+ resource_ctx_fini(&ctx);
+ return err;
+}
+
+static int cmd_resource(struct dl *dl)
+{
+ if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
+ cmd_resource_help();
+ return 0;
+ } else if (dl_argv_match(dl, "show")) {
+ dl_arg_inc(dl);
+ return cmd_resource_show(dl);
+ } else if (dl_argv_match(dl, "set")) {
+ dl_arg_inc(dl);
+ return cmd_resource_set(dl);
+ }
+ pr_err("Command \"%s\" not found\n", dl_argv(dl));
+ return -ENOENT;
+}
+
static void help(void)
{
pr_err("Usage: devlink [ OPTIONS ] OBJECT { COMMAND | help }\n"
" devlink [ -f[orce] ] -b[atch] filename\n"
- "where OBJECT := { dev | port | sb | monitor | dpipe }\n"
+ "where OBJECT := { dev | port | sb | monitor | dpipe | resource }\n"
" OPTIONS := { -V[ersion] | -n[no-nice-names] | -j[json] | -p[pretty] | -v[verbose] }\n");
}
@@ -3831,6 +4490,9 @@ static int dl_cmd(struct dl *dl, int argc, char **argv)
} else if (dl_argv_match(dl, "dpipe")) {
dl_arg_inc(dl);
return cmd_dpipe(dl);
+ } else if (dl_argv_match(dl, "resource")) {
+ dl_arg_inc(dl);
+ return cmd_resource(dl);
}
pr_err("Object \"%s\" not found\n", dl_argv(dl));
return -ENOENT;
diff --git a/devlink/mnlg.c b/devlink/mnlg.c
index 9e27de27..3d28453a 100644
--- a/devlink/mnlg.c
+++ b/devlink/mnlg.c
@@ -18,6 +18,8 @@
#include <libmnl/libmnl.h>
#include <linux/genetlink.h>
+#include "libnetlink.h"
+#include "utils.h"
#include "mnlg.h"
struct mnlg_socket {
@@ -60,6 +62,39 @@ int mnlg_socket_send(struct mnlg_socket *nlg, const struct nlmsghdr *nlh)
return mnl_socket_sendto(nlg->nl, nlh, nlh->nlmsg_len);
}
+static int mnlg_cb_noop(const struct nlmsghdr *nlh, void *data)
+{
+ return MNL_CB_OK;
+}
+
+static int mnlg_cb_error(const struct nlmsghdr *nlh, void *data)
+{
+ const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);
+
+ /* Netlink subsystems returns the errno value with different signess */
+ if (err->error < 0)
+ errno = -err->error;
+ else
+ errno = err->error;
+
+ if (nl_dump_ext_ack(nlh, NULL))
+ return MNL_CB_ERROR;
+
+ return err->error == 0 ? MNL_CB_STOP : MNL_CB_ERROR;
+}
+
+static int mnlg_cb_stop(const struct nlmsghdr *nlh, void *data)
+{
+ return MNL_CB_STOP;
+}
+
+static mnl_cb_t mnlg_cb_array[NLMSG_MIN_TYPE] = {
+ [NLMSG_NOOP] = mnlg_cb_noop,
+ [NLMSG_ERROR] = mnlg_cb_error,
+ [NLMSG_DONE] = mnlg_cb_stop,
+ [NLMSG_OVERRUN] = mnlg_cb_noop,
+};
+
int mnlg_socket_recv_run(struct mnlg_socket *nlg, mnl_cb_t data_cb, void *data)
{
int err;
@@ -69,8 +104,9 @@ int mnlg_socket_recv_run(struct mnlg_socket *nlg, mnl_cb_t data_cb, void *data)
MNL_SOCKET_BUFFER_SIZE);
if (err <= 0)
break;
- err = mnl_cb_run(nlg->buf, err, nlg->seq, nlg->portid,
- data_cb, data);
+ err = mnl_cb_run2(nlg->buf, err, nlg->seq, nlg->portid,
+ data_cb, data, mnlg_cb_array,
+ ARRAY_SIZE(mnlg_cb_array));
} while (err > 0);
return err;
@@ -220,6 +256,7 @@ struct mnlg_socket *mnlg_socket_open(const char *family_name, uint8_t version)
{
struct mnlg_socket *nlg;
struct nlmsghdr *nlh;
+ int one = 1;
int err;
nlg = malloc(sizeof(*nlg));
@@ -234,6 +271,16 @@ struct mnlg_socket *mnlg_socket_open(const char *family_name, uint8_t version)
if (!nlg->nl)
goto err_mnl_socket_open;
+ err = mnl_socket_setsockopt(nlg->nl, NETLINK_CAP_ACK, &one,
+ sizeof(one));
+ if (err)
+ goto err_mnl_set_ack;
+
+ err = mnl_socket_setsockopt(nlg->nl, NETLINK_EXT_ACK, &one,
+ sizeof(one));
+ if (err)
+ goto err_mnl_set_ext_ack;
+
err = mnl_socket_bind(nlg->nl, 0, MNL_SOCKET_AUTOPID);
if (err < 0)
goto err_mnl_socket_bind;
@@ -258,6 +305,8 @@ struct mnlg_socket *mnlg_socket_open(const char *family_name, uint8_t version)
err_mnlg_socket_recv_run:
err_mnlg_socket_send:
err_mnl_socket_bind:
+err_mnl_set_ext_ack:
+err_mnl_set_ack:
mnl_socket_close(nlg->nl);
err_mnl_socket_open:
free(nlg->buf);
diff --git a/include/color.h b/include/color.h
index f6c351b7..c80359d3 100644
--- a/include/color.h
+++ b/include/color.h
@@ -13,7 +13,6 @@ 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/include/json_writer.h b/include/json_writer.h
index 1516aafb..45459fa2 100644
--- a/include/json_writer.h
+++ b/include/json_writer.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) */
/*
* Simple streaming JSON writer
*
* This takes care of the annoying bits of JSON syntax like the commas
* after elements
*
- * 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: Stephen Hemminger <stephen@networkplumber.org>
*/
diff --git a/include/libnetlink.h b/include/libnetlink.h
index d6322190..9d9249e6 100644
--- a/include/libnetlink.h
+++ b/include/libnetlink.h
@@ -109,6 +109,7 @@ int rtnl_send(struct rtnl_handle *rth, const void *buf, int)
__attribute__((warn_unused_result));
int rtnl_send_check(struct rtnl_handle *rth, const void *buf, int)
__attribute__((warn_unused_result));
+int nl_dump_ext_ack(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn);
int addattr(struct nlmsghdr *n, int maxlen, int type);
int addattr8(struct nlmsghdr *n, int maxlen, int type, __u8 data);
diff --git a/include/list.h b/include/list.h
index 5af737c7..5d86b131 100644
--- a/include/list.h
+++ b/include/list.h
@@ -108,6 +108,11 @@ static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
n->pprev = &h->first;
}
+static inline int list_empty(const struct list_head *head)
+{
+ return head->next == head;
+}
+
#define hlist_for_each(pos, head) \
for (pos = (head)->first; pos ; pos = pos->next)
diff --git a/ip/ip.c b/ip/ip.c
index 362790a0..164d7260 100644
--- a/ip/ip.c
+++ b/ip/ip.c
@@ -172,6 +172,7 @@ int main(int argc, char **argv)
{
char *basename;
char *batch_file = NULL;
+ int color = 0;
basename = strrchr(argv[0], '/');
if (basename == NULL)
@@ -271,7 +272,7 @@ int main(int argc, char **argv)
}
rcvbuf = size;
} else if (matches(opt, "-color") == 0) {
- enable_color();
+ ++color;
} else if (matches(opt, "-help") == 0) {
usage();
} else if (matches(opt, "-netns") == 0) {
@@ -291,8 +292,8 @@ int main(int argc, char **argv)
_SL_ = oneline ? "\\" : "\n";
- if (json)
- check_if_color_enabled();
+ if (color && !json)
+ enable_color();
if (batch_file)
return batch(batch_file);
diff --git a/ip/ipaddress.c b/ip/ipaddress.c
index 087fa68a..aecc9a1d 100644
--- a/ip/ipaddress.c
+++ b/ip/ipaddress.c
@@ -416,10 +416,10 @@ static void print_vfinfo(FILE *fp, struct rtattr *vfinfo)
}
if (vf_tx_rate->rate)
- print_int(PRINT_ANY,
- "tx_rate",
- ", tx rate %d (Mbps)",
- vf_tx_rate->rate);
+ print_uint(PRINT_ANY,
+ "tx_rate",
+ ", tx rate %u (Mbps)",
+ vf_tx_rate->rate);
if (vf[IFLA_VF_RATE]) {
struct ifla_vf_rate *vf_rate = RTA_DATA(vf[IFLA_VF_RATE]);
@@ -428,14 +428,14 @@ static void print_vfinfo(FILE *fp, struct rtattr *vfinfo)
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);
+ print_uint(PRINT_JSON, "max_tx", NULL, max_tx);
+ print_uint(PRINT_ANY, "min_tx", NULL, min_tx);
close_json_object();
} else {
if (max_tx)
- fprintf(fp, ", max_tx_rate %dMbps", max_tx);
+ fprintf(fp, ", max_tx_rate %uMbps", max_tx);
if (min_tx)
- fprintf(fp, ", min_tx_rate %dMbps", min_tx);
+ fprintf(fp, ", min_tx_rate %uMbps", min_tx);
}
}
diff --git a/ip/iplink.c b/ip/iplink.c
index 02042ed6..29b7062f 100644
--- a/ip/iplink.c
+++ b/ip/iplink.c
@@ -275,8 +275,9 @@ static int nl_get_ll_addr_len(unsigned int dev_index)
return -1;
}
+ len = RTA_PAYLOAD(tb[IFLA_ADDRESS]);
free(answer);
- return RTA_PAYLOAD(tb[IFLA_ADDRESS]);
+ return len;
}
static void iplink_parse_vf_vlan_info(int vf, int *argcp, char ***argvp,
diff --git a/lib/bpf.c b/lib/bpf.c
index 2db151e4..c38d92d8 100644
--- a/lib/bpf.c
+++ b/lib/bpf.c
@@ -2039,6 +2039,7 @@ static int bpf_apply_relo_data(struct bpf_elf_ctx *ctx,
insns[ioff].code != (BPF_LD | BPF_IMM | BPF_DW)) {
fprintf(stderr, "ELF contains relo data for non ld64 instruction at offset %u! Compiler bug?!\n",
ioff);
+ fprintf(stderr, " - Current section: %s\n", data_relo->sec_name);
if (ioff < num_insns &&
insns[ioff].code == (BPF_JMP | BPF_CALL))
fprintf(stderr, " - Try to annotate functions with always_inline attribute!\n");
diff --git a/lib/color.c b/lib/color.c
index a13a4930..da1f516c 100644
--- a/lib/color.c
+++ b/lib/color.c
@@ -92,14 +92,6 @@ 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;
diff --git a/lib/json_writer.c b/lib/json_writer.c
index 0d910dc0..68401ae3 100644
--- a/lib/json_writer.c
+++ b/lib/json_writer.c
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) */
/*
* Simple streaming JSON writer
*
* This takes care of the annoying bits of JSON syntax like the commas
* after elements
*
- * 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: Stephen Hemminger <stephen@networkplumber.org>
*/
diff --git a/lib/libnetlink.c b/lib/libnetlink.c
index 7ca47b22..928de1dd 100644
--- a/lib/libnetlink.c
+++ b/lib/libnetlink.c
@@ -65,7 +65,7 @@ static int err_attr_cb(const struct nlattr *attr, void *data)
}
/* dump netlink extended ack error message */
-static int nl_dump_ext_ack(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn)
+int nl_dump_ext_ack(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn)
{
struct nlattr *tb[NLMSGERR_ATTR_MAX + 1] = {};
const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);
@@ -120,7 +120,7 @@ static int nl_dump_ext_ack(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn)
#warning "libmnl required for error support"
/* No extended error ack without libmnl */
-static int nl_dump_ext_ack(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn)
+int nl_dump_ext_ack(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn)
{
return 0;
}
@@ -670,8 +670,9 @@ next:
free(buf);
if (h->nlmsg_seq == seq)
return 0;
- else
+ else if (i < iovlen)
goto next;
+ return 0;
}
if (rtnl->proto != NETLINK_SOCK_DIAG &&
diff --git a/lib/namespace.c b/lib/namespace.c
index 30b51388..6f3356d0 100644
--- a/lib/namespace.c
+++ b/lib/namespace.c
@@ -7,6 +7,7 @@
* 2 of the License, or (at your option) any later version.
*/
+#include <sys/statvfs.h>
#include <fcntl.h>
#include <dirent.h>
#include <limits.h>
@@ -46,6 +47,8 @@ int netns_switch(char *name)
{
char net_path[PATH_MAX];
int netns;
+ unsigned long mountflags = 0;
+ struct statvfs fsstat;
snprintf(net_path, sizeof(net_path), "%s/%s", NETNS_RUN_DIR, name);
netns = open(net_path, O_RDONLY | O_CLOEXEC);
@@ -73,12 +76,25 @@ int netns_switch(char *name)
strerror(errno));
return -1;
}
+
/* Mount a version of /sys that describes the network namespace */
- if (umount2("/sys", MNT_DETACH) < 0) {
- fprintf(stderr, "umount of /sys failed: %s\n", strerror(errno));
+
+ if (statvfs("/sys", &fsstat) < 0) {
+ fprintf(stderr, "could not stat /sys (not mounted?): %s\n",strerror(errno));
return -1;
}
- if (mount(name, "/sys", "sysfs", 0, NULL) < 0) {
+ if (fsstat.f_flag & ST_RDONLY) {
+ /* If /sys is not writable (e.g. in a container), we can't
+ * unmount the old /sys instance, but we can still mount a new
+ * read-only instance over it. */
+ mountflags = MS_RDONLY;
+ } else {
+ if (umount2("/sys", MNT_DETACH) < 0) {
+ fprintf(stderr, "umount of /sys failed: %s\n", strerror(errno));
+ return -1;
+ }
+ }
+ if (mount(name, "/sys", "sysfs", mountflags, NULL) < 0) {
fprintf(stderr, "mount of /sys failed: %s\n",strerror(errno));
return -1;
}
diff --git a/lib/utils.c b/lib/utils.c
index 2b8e4e8e..d6c2395d 100644
--- a/lib/utils.c
+++ b/lib/utils.c
@@ -1010,6 +1010,25 @@ const char *rt_addr_n2a_r(int af, int len,
}
case AF_PACKET:
return ll_addr_n2a(addr, len, ARPHRD_VOID, buf, buflen);
+ case AF_BRIDGE:
+ {
+ const union {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ } *sa = addr;
+
+ switch (sa->sa.sa_family) {
+ case AF_INET:
+ return inet_ntop(AF_INET, &sa->sin.sin_addr,
+ buf, buflen);
+ case AF_INET6:
+ return inet_ntop(AF_INET6, &sa->sin6.sin6_addr,
+ buf, buflen);
+ }
+
+ /* fallthrough */
+ }
default:
return "???";
}
diff --git a/man/man8/devlink-dev.8 b/man/man8/devlink-dev.8
index b074d57a..7c749dda 100644
--- a/man/man8/devlink-dev.8
+++ b/man/man8/devlink-dev.8
@@ -42,6 +42,10 @@ devlink-dev \- devlink device configuration
.BR "devlink dev eswitch show"
.IR DEV
+.ti -8
+.BR "devlink dev reload"
+.IR DEV
+
.SH "DESCRIPTION"
.SS devlink dev show - display devlink device attributes
@@ -94,6 +98,12 @@ Set eswitch encapsulation support
.I enable
- Enable encapsulation support
+.SS devlink dev reload - perform hot reload of the driver.
+
+.PP
+.I "DEV"
+- Specifies the devlink device to reload.
+
.SH "EXAMPLES"
.PP
devlink dev show
@@ -114,6 +124,11 @@ Shows the eswitch mode of specified devlink device.
devlink dev eswitch set pci/0000:01:00.0 mode switchdev
.RS 4
Sets the eswitch mode of specified devlink device to switchdev.
+.RE
+.PP
+devlink dev reload pci/0000:01:00.0
+.RS 4
+Performs hot reload of specified devlink device.
.SH SEE ALSO
.BR devlink (8),
diff --git a/man/man8/devlink-resource.8 b/man/man8/devlink-resource.8
new file mode 100644
index 00000000..b8f78806
--- /dev/null
+++ b/man/man8/devlink-resource.8
@@ -0,0 +1,78 @@
+.TH DEVLINK\-RESOURCE 8 "11 Feb 2018" "iproute2" "Linux"
+.SH NAME
+devlink-resource \- devlink device resource configuration
+.SH SYNOPSIS
+.sp
+.ad l
+.in +8
+.ti -8
+.B devlink
+.RI "[ " OPTIONS " ]"
+.B resource
+.RI " { " COMMAND " | "
+.BR help " }"
+.sp
+
+.ti -8
+.IR OPTIONS " := { "
+\fB\-v\fR[\fIerbose\fR] }
+
+.ti -8
+.B devlink resource show
+.IR DEV
+
+.ti -8
+.B devlink resource help
+
+.ti -8
+.BR "devlink resource set"
+.IR DEV
+.BI path " RESOURCE_PATH"
+.BI size " RESOURCE_SIZE"
+
+.SH "DESCRIPTION"
+.SS devlink resource show - display devlink device's resosources
+
+.PP
+.I "DEV"
+- specifies the devlink device to show.
+
+.in +4
+Format is:
+.in +2
+BUS_NAME/BUS_ADDRESS
+
+.SS devlink resource set - sets resource size of specific resource
+
+.PP
+.I "DEV"
+- specifies the devlink device.
+
+.TP
+.BI path " RESOURCE_PATH"
+Resource's path.
+
+.TP
+.BI size " RESOURCE_SIZE"
+The new resource's size.
+
+.SH "EXAMPLES"
+.PP
+devlink resource show pci/0000:01:00.0
+.RS 4
+Shows the resources of the specified devlink device.
+.RE
+.PP
+devlink resource set pci/0000:01:00.0 /kvd/linear 98304
+.RS 4
+Sets the size of the specified resource for the specified devlink device.
+
+.SH SEE ALSO
+.BR devlink (8),
+.BR devlink-port (8),
+.BR devlink-sb (8),
+.BR devlink-monitor (8),
+.br
+
+.SH AUTHOR
+Arkadi Sharshevsky <arkadis@mellanox.com>
diff --git a/man/man8/devlink.8 b/man/man8/devlink.8
index a975ef34..b83909da 100644
--- a/man/man8/devlink.8
+++ b/man/man8/devlink.8
@@ -103,6 +103,7 @@ Exit status is 0 if command was successful or a positive integer upon failure.
.BR devlink-port (8),
.BR devlink-monitor (8),
.BR devlink-sb (8),
+.BR devlink-resource (8),
.br
.SH REPORTING BUGS
diff --git a/man/man8/tc-bpf.8 b/man/man8/tc-bpf.8
index 2e9812ed..d311f295 100644
--- a/man/man8/tc-bpf.8
+++ b/man/man8/tc-bpf.8
@@ -14,6 +14,10 @@ CLS_NAME ] [
UDS_FILE ] [
.B verbose
] [
+.B direct-action
+|
+.B da
+] [
.B skip_hw
|
.B skip_sw
@@ -141,6 +145,11 @@ if set, it will dump the eBPF verifier output, even if loading the eBPF
program was successful. By default, only on error, the verifier log is
being emitted to the user.
+.SS direct-action | da
+instructs eBPF classifier to not invoke external TC actions, instead use the
+TC actions return codes (\fBTC_ACT_OK\fR, \fBTC_ACT_SHOT\fR etc.) for
+classifiers.
+
.SS skip_hw | skip_sw
hardware offload control flags. By default TC will try to offload
filters to hardware if possible.
diff --git a/man/man8/tc.8 b/man/man8/tc.8
index a20b06bb..3dc30ee4 100644
--- a/man/man8/tc.8
+++ b/man/man8/tc.8
@@ -82,12 +82,20 @@ tc \- show / manipulate traffic control settings
\fIBLOCK_INDEX\fR
.P
+.B tc
+.RI "[ " OPTIONS " ]"
+.B monitor [ file
+\fIFILENAME\fR
+.B ]
+
+.P
.ti 8
.IR OPTIONS " := {"
\fB[ -force ] -b\fR[\fIatch\fR] \fB[ filename ] \fR|
\fB[ \fB-n\fR[\fIetns\fR] name \fB] \fR|
\fB[ \fB-nm \fR| \fB-nam\fR[\fIes\fR] \fB] \fR|
-\fB[ \fR{ \fB-cf \fR| \fB-c\fR[\fIonf\fR] \fR} \fB[ filename ] \fB] \fR}
+\fB[ \fR{ \fB-cf \fR| \fB-c\fR[\fIonf\fR] \fR} \fB[ filename ] \fB] \fR
+\fB[ -t\fR[imestamp\fR] \fB\] \fR| \fB[ -t\fR[short\fR] \fB]\fR }
.ti 8
.IR FORMAT " := {"
@@ -617,6 +625,17 @@ link
Only available for qdiscs and performs a replace where the node
must exist already.
+.SH MONITOR
+The\fB\ tc\fR\ utility can monitor events generated by the kernel such as
+adding/deleting qdiscs, filters or actions, or modifying existing ones.
+
+The following command is available for\fB\ monitor\fR\ :
+.TP
+\fBfile\fR
+If the file option is given, the \fBtc\fR does not listen to kernel events, but opens
+the given file and dumps its contents. The file has to be in binary
+format and contain netlink messages.
+
.SH OPTIONS
.TP
@@ -654,6 +673,16 @@ to
specifies path to the config file. This option is used in conjunction with other options (e.g.
.BR -nm ")."
+.TP
+.BR "\-t", " \-timestamp"
+When\fB\ tc monitor\fR\ runs, print timestamp before the event message in format:
+ Timestamp: <Day> <Month> <DD> <hh:mm:ss> <YYYY> <usecs> usec
+
+.TP
+.BR "\-ts", " \-tshort"
+When\fB\ tc monitor\fR\ runs, prints short timestamp before the event message in format:
+ [<YYYY>-<MM>-<DD>T<hh:mm:ss>.<ms>]
+
.SH FORMAT
The show command has additional formatting options:
diff --git a/misc/ss.c b/misc/ss.c
index 49f9c49c..1484bcab 100644
--- a/misc/ss.c
+++ b/misc/ss.c
@@ -239,6 +239,7 @@ struct filter {
uint64_t families;
struct ssfilter *f;
bool kill;
+ struct rtnl_handle *rth_for_killing;
};
#define FAMILY_MASK(family) ((uint64_t)1 << (family))
@@ -1196,10 +1197,15 @@ newline:
/* Render buffered output with spacing and delimiters, then free up buffers */
static void render(int screen_width)
{
- struct buf_token *token = (struct buf_token *)buffer.head->data;
+ struct buf_token *token;
int printed, line_started = 0;
struct column *f;
+ if (!buffer.head)
+ return;
+
+ token = (struct buf_token *)buffer.head->data;
+
/* Ensure end alignment of last token, it wasn't necessarily flushed */
buffer.tail->end += buffer.cur->len % 2;
@@ -4265,6 +4271,7 @@ static int generic_show_sock(const struct sockaddr_nl *addr,
switch (r->sdiag_family) {
case AF_INET:
case AF_INET6:
+ inet_arg.rth = inet_arg.f->rth_for_killing;
return show_one_inet_sock(addr, nlh, &inet_arg);
case AF_UNIX:
return unix_show_sock(addr, nlh, arg);
@@ -4283,7 +4290,7 @@ static int handle_follow_request(struct filter *f)
{
int ret = 0;
int groups = 0;
- struct rtnl_handle rth;
+ struct rtnl_handle rth, rth2;
if (f->families & FAMILY_MASK(AF_INET) && f->dbs & (1 << TCP_DB))
groups |= 1 << (SKNLGRP_INET_TCP_DESTROY - 1);
@@ -4303,10 +4310,20 @@ static int handle_follow_request(struct filter *f)
rth.dump = 0;
rth.local.nl_pid = 0;
+ if (f->kill) {
+ if (rtnl_open_byproto(&rth2, groups, NETLINK_SOCK_DIAG)) {
+ rtnl_close(&rth);
+ return -1;
+ }
+ f->rth_for_killing = &rth2;
+ }
+
if (rtnl_dump_filter(&rth, generic_show_sock, f))
ret = -1;
rtnl_close(&rth);
+ if (f->rth_for_killing)
+ rtnl_close(f->rth_for_killing);
return ret;
}
diff --git a/rdma/res.c b/rdma/res.c
index 2a63e712..62f5c544 100644
--- a/rdma/res.c
+++ b/rdma/res.c
@@ -395,8 +395,10 @@ static int res_qp_parse_cb(const struct nlmsghdr *nlh, void *data)
comm = get_task_name(pid);
}
- if (rd_check_is_filtered(rd, "pid", pid))
+ if (rd_check_is_filtered(rd, "pid", pid)) {
+ free(comm);
continue;
+ }
if (nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME])
/* discard const from mnl_attr_get_str */
diff --git a/tc/m_action.c b/tc/m_action.c
index 744bde41..6c3049c7 100644
--- a/tc/m_action.c
+++ b/tc/m_action.c
@@ -365,7 +365,7 @@ tc_print_action(FILE *f, const struct rtattr *arg, unsigned short tot_acts)
return tc_print_action_flush(f, tb[0]);
open_json_array(PRINT_JSON, "actions");
- for (i = 0; i < tot_acts; i++) {
+ for (i = 0; i <= tot_acts; i++) {
if (tb[i]) {
open_json_object(NULL);
print_uint(PRINT_ANY, "order",
diff --git a/tc/m_bpf.c b/tc/m_bpf.c
index 27dcfd7f..7c6f8c29 100644
--- a/tc/m_bpf.c
+++ b/tc/m_bpf.c
@@ -128,7 +128,6 @@ opt_bpf:
parse_action_control_dflt(&argc, &argv, &parm.action,
false, TC_ACT_PIPE);
- NEXT_ARG_FWD();
if (argc) {
if (matches(*argv, "index") == 0) {
diff --git a/tc/m_connmark.c b/tc/m_connmark.c
index 56d1db92..a6ee532d 100644
--- a/tc/m_connmark.c
+++ b/tc/m_connmark.c
@@ -82,7 +82,6 @@ parse_connmark(struct action_util *a, int *argc_p, char ***argv_p, int tca_id,
}
parse_action_control_dflt(&argc, &argv, &sel.action, false, TC_ACT_PIPE);
- NEXT_ARG_FWD();
if (argc) {
if (matches(*argv, "index") == 0) {
diff --git a/tc/m_csum.c b/tc/m_csum.c
index 42d37aba..8391071d 100644
--- a/tc/m_csum.c
+++ b/tc/m_csum.c
@@ -124,7 +124,6 @@ parse_csum(struct action_util *a, int *argc_p,
}
parse_action_control_dflt(&argc, &argv, &sel.action, false, TC_ACT_OK);
- NEXT_ARG_FWD();
if (argc) {
if (matches(*argv, "index") == 0) {
diff --git a/tc/m_gact.c b/tc/m_gact.c
index ccad4f22..a0a3c33d 100644
--- a/tc/m_gact.c
+++ b/tc/m_gact.c
@@ -87,12 +87,10 @@ parse_gact(struct action_util *a, int *argc_p, char ***argv_p,
if (argc < 0)
return -1;
- if (matches(*argv, "gact") != 0 &&
- parse_action_control(&argc, &argv, &p.action, false) == -1) {
+ if (!matches(*argv, "gact"))
+ NEXT_ARG_FWD();
+ if (parse_action_control(&argc, &argv, &p.action, false))
usage(); /* does not return */
- }
-
- NEXT_ARG_FWD();
#ifdef CONFIG_GACT_PROB
if (argc > 0) {
@@ -113,7 +111,6 @@ parse_gact(struct action_util *a, int *argc_p, char ***argv_p,
if (parse_action_control(&argc, &argv,
&pp.paction, false) == -1)
usage();
- NEXT_ARG_FWD();
if (get_u16(&pp.pval, *argv, 10)) {
fprintf(stderr,
"Illegal probability val 0x%x\n",
diff --git a/tc/m_ife.c b/tc/m_ife.c
index 43d89181..d7e61703 100644
--- a/tc/m_ife.c
+++ b/tc/m_ife.c
@@ -159,7 +159,6 @@ static int parse_ife(struct action_util *a, int *argc_p, char ***argv_p,
parse_action_control_dflt(&argc, &argv, &p.action, false, TC_ACT_PIPE);
- NEXT_ARG_FWD();
if (argc) {
if (matches(*argv, "index") == 0) {
NEXT_ARG();
diff --git a/tc/m_mirred.c b/tc/m_mirred.c
index b25b9acc..c7f7318b 100644
--- a/tc/m_mirred.c
+++ b/tc/m_mirred.c
@@ -103,6 +103,7 @@ parse_direction(struct action_util *a, int *argc_p, char ***argv_p,
while (argc > 0) {
if (matches(*argv, "action") == 0) {
+ NEXT_ARG();
break;
} else if (!egress && matches(*argv, "egress") == 0) {
egress = 1;
@@ -200,10 +201,8 @@ parse_direction(struct action_util *a, int *argc_p, char ***argv_p,
}
- if (p.eaction == TCA_EGRESS_MIRROR || p.eaction == TCA_INGRESS_MIRROR) {
+ if (p.eaction == TCA_EGRESS_MIRROR || p.eaction == TCA_INGRESS_MIRROR)
parse_action_control(&argc, &argv, &p.action, false);
- NEXT_ARG_FWD();
- }
if (argc) {
if (iok && matches(*argv, "index") == 0) {
diff --git a/tc/m_nat.c b/tc/m_nat.c
index 1bb6495b..daa1f572 100644
--- a/tc/m_nat.c
+++ b/tc/m_nat.c
@@ -116,7 +116,6 @@ parse_nat(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct
parse_action_control_dflt(&argc, &argv, &sel.action, false, TC_ACT_OK);
- NEXT_ARG_FWD();
if (argc) {
if (matches(*argv, "index") == 0) {
NEXT_ARG();
diff --git a/tc/m_pedit.c b/tc/m_pedit.c
index 2d41f0a0..b9cb5ffb 100644
--- a/tc/m_pedit.c
+++ b/tc/m_pedit.c
@@ -672,7 +672,6 @@ int parse_pedit(struct action_util *a, int *argc_p, char ***argv_p, int tca_id,
parse_action_control_dflt(&argc, &argv, &sel.sel.action, false, TC_ACT_OK);
- NEXT_ARG_FWD();
if (argc) {
if (matches(*argv, "index") == 0) {
NEXT_ARG();
diff --git a/tc/m_police.c b/tc/m_police.c
index f85da49f..f3b07f7b 100644
--- a/tc/m_police.c
+++ b/tc/m_police.c
@@ -150,15 +150,18 @@ int act_parse_police(struct action_util *a, int *argc_p, char ***argv_p,
matches(*argv, "shot") == 0 ||
matches(*argv, "continue") == 0 ||
matches(*argv, "pass") == 0 ||
+ matches(*argv, "ok") == 0 ||
matches(*argv, "pipe") == 0 ||
matches(*argv, "goto") == 0) {
- if (parse_action_control(&argc, &argv, &p.action, false))
- return -1;
+ if (!parse_action_control(&argc, &argv, &p.action, false))
+ goto action_ctrl_ok;
+ return -1;
} else if (strcmp(*argv, "conform-exceed") == 0) {
NEXT_ARG();
- if (parse_action_control_slash(&argc, &argv, &p.action,
- &presult, true))
- return -1;
+ if (!parse_action_control_slash(&argc, &argv, &p.action,
+ &presult, true))
+ goto action_ctrl_ok;
+ return -1;
} else if (matches(*argv, "overhead") == 0) {
NEXT_ARG();
if (get_u16(&overhead, *argv, 10)) {
@@ -174,8 +177,9 @@ int act_parse_police(struct action_util *a, int *argc_p, char ***argv_p,
} else {
break;
}
+ NEXT_ARG_FWD();
+action_ctrl_ok:
ok++;
- argc--; argv++;
}
if (!ok)
diff --git a/tc/m_sample.c b/tc/m_sample.c
index fe892adc..f6e579f8 100644
--- a/tc/m_sample.c
+++ b/tc/m_sample.c
@@ -100,7 +100,6 @@ static int parse_sample(struct action_util *a, int *argc_p, char ***argv_p,
parse_action_control_dflt(&argc, &argv, &p.action, false, TC_ACT_PIPE);
- NEXT_ARG_FWD();
if (argc) {
if (matches(*argv, "index") == 0) {
NEXT_ARG();
diff --git a/tc/m_skbedit.c b/tc/m_skbedit.c
index c1eda30c..db5c64ca 100644
--- a/tc/m_skbedit.c
+++ b/tc/m_skbedit.c
@@ -123,7 +123,6 @@ parse_skbedit(struct action_util *a, int *argc_p, char ***argv_p, int tca_id,
parse_action_control_dflt(&argc, &argv, &sel.action,
false, TC_ACT_PIPE);
- NEXT_ARG_FWD();
if (argc) {
if (matches(*argv, "index") == 0) {
NEXT_ARG();
diff --git a/tc/m_skbmod.c b/tc/m_skbmod.c
index 6721f87d..2dd1bb7e 100644
--- a/tc/m_skbmod.c
+++ b/tc/m_skbmod.c
@@ -124,7 +124,6 @@ static int parse_skbmod(struct action_util *a, int *argc_p, char ***argv_p,
parse_action_control_dflt(&argc, &argv, &p.action, false, TC_ACT_PIPE);
- NEXT_ARG_FWD();
if (argc) {
if (matches(*argv, "index") == 0) {
NEXT_ARG();
diff --git a/tc/m_tunnel_key.c b/tc/m_tunnel_key.c
index 8d4cee12..bac3c07f 100644
--- a/tc/m_tunnel_key.c
+++ b/tc/m_tunnel_key.c
@@ -174,7 +174,6 @@ static int parse_tunnel_key(struct action_util *a, int *argc_p, char ***argv_p,
parse_action_control_dflt(&argc, &argv, &parm.action,
false, TC_ACT_PIPE);
- NEXT_ARG_FWD();
if (argc) {
if (matches(*argv, "index") == 0) {
NEXT_ARG();
diff --git a/tc/m_vlan.c b/tc/m_vlan.c
index 9ee52da2..412f6aa1 100644
--- a/tc/m_vlan.c
+++ b/tc/m_vlan.c
@@ -131,7 +131,6 @@ static int parse_vlan(struct action_util *a, int *argc_p, char ***argv_p,
parse_action_control_dflt(&argc, &argv, &parm.action,
false, TC_ACT_PIPE);
- NEXT_ARG_FWD();
if (argc) {
if (matches(*argv, "index") == 0) {
NEXT_ARG();
diff --git a/tc/tc_util.c b/tc/tc_util.c
index 7f6a8e31..e0c96291 100644
--- a/tc/tc_util.c
+++ b/tc/tc_util.c
@@ -599,6 +599,7 @@ static int __parse_action_control(int *argc_p, char ***argv_p, int *result_p,
}
result |= jump_cnt;
}
+ NEXT_ARG_FWD();
*argc_p = argc;
*argv_p = argv;
*result_p = result;
@@ -695,8 +696,8 @@ out:
int parse_action_control_slash(int *argc_p, char ***argv_p,
int *result1_p, int *result2_p, bool allow_num)
{
+ int result1, result2, argc = *argc_p;
char **argv = *argv_p;
- int result1, result2;
char *p = strchr(*argv, '/');
if (!p)
@@ -715,6 +716,9 @@ int parse_action_control_slash(int *argc_p, char ***argv_p,
*result1_p = result1;
*result2_p = result2;
+ NEXT_ARG_FWD();
+ *argc_p = argc;
+ *argv_p = argv;
return 0;
}