Merge branch 'iproute2-master' into iproute2-next

Conflicts:
	bridge/mdb.c

Updated bridge/bridge.c per removal of check_if_color_enabled by commit
1ca4341d2c ("color: disable color when json output is requested")

Signed-off-by: David Ahern <dsahern@gmail.com>
This commit is contained in:
David Ahern 2018-03-13 17:48:10 -07:00
commit e9625d6aea
40 changed files with 1016 additions and 130 deletions

3
README
View File

@ -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/

View File

@ -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);

View File

@ -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)
{
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_indent_inc();
__pr_out_newline();
pr_out("%s:", name);
__pr_out_newline();
__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)
{
struct dpipe_ctx *ctx;
return calloc(1, sizeof(struct dpipe_table));
}
ctx = calloc(1, sizeof(struct dpipe_ctx));
if (!ctx)
static void dpipe_table_free(struct dpipe_table *table)
{
free(table);
}
static struct dpipe_tables *dpipe_tables_alloc(void)
{
struct dpipe_tables *tables;
tables = calloc(1, sizeof(struct dpipe_tables));
if (!tables)
return NULL;
INIT_LIST_HEAD(&tables->table_list);
return tables;
}
static void dpipe_tables_free(struct dpipe_tables *tables)
{
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 ctx;
return 0;
}
static void dpipe_ctx_free(struct dpipe_ctx *ctx)
{
free(ctx);
}
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,14 +3563,17 @@ 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) {
if (ctx->print_tables)
pr_out_handle_start_arr(ctx->dl, tb);
if (dpipe_table_show(ctx, nla_table))
goto err_table_show;
if (ctx->print_tables)
pr_out_handle_end(ctx->dl);
}
return 0;
err_table_show:
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;

View File

@ -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);

View File

@ -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);

View File

@ -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>
*/

View File

@ -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);

View File

@ -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)

View File

@ -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);

View File

@ -416,9 +416,9 @@ static void print_vfinfo(FILE *fp, struct rtattr *vfinfo)
}
if (vf_tx_rate->rate)
print_int(PRINT_ANY,
print_uint(PRINT_ANY,
"tx_rate",
", tx rate %d (Mbps)",
", tx rate %u (Mbps)",
vf_tx_rate->rate);
if (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);
}
}

View File

@ -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,

View File

@ -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");

View File

@ -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;

View File

@ -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>
*/

View File

@ -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 &&

View File

@ -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 (statvfs("/sys", &fsstat) < 0) {
fprintf(stderr, "could not stat /sys (not mounted?): %s\n",strerror(errno));
return -1;
}
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", 0, NULL) < 0) {
}
if (mount(name, "/sys", "sysfs", mountflags, NULL) < 0) {
fprintf(stderr, "mount of /sys failed: %s\n",strerror(errno));
return -1;
}

View File

@ -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 "???";
}

View File

@ -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),

View File

@ -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>

View File

@ -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

View File

@ -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.

View File

@ -81,13 +81,21 @@ tc \- show / manipulate traffic control settings
.B filter show block
\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:

View File

@ -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;
}

View File

@ -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 */

View File

@ -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",

View File

@ -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) {

View File

@ -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) {

View File

@ -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) {

View File

@ -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) {
usage(); /* does not return */
}
if (!matches(*argv, "gact"))
NEXT_ARG_FWD();
if (parse_action_control(&argc, &argv, &p.action, false))
usage(); /* does not return */
#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",

View File

@ -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();

View File

@ -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) {

View File

@ -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();

View File

@ -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();

View File

@ -150,14 +150,17 @@ 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))
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,
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();
@ -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)

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

@ -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;
}