Merge ../iproute2-next

This commit is contained in:
Stephen Hemminger 2019-09-24 12:37:33 -07:00
commit 38e9ba9dc9
37 changed files with 2441 additions and 258 deletions

View File

@ -25,6 +25,7 @@
#include <linux/devlink.h>
#include <libmnl/libmnl.h>
#include <netinet/ether.h>
#include <sys/types.h>
#include "SNAPSHOT.h"
#include "list.h"
@ -96,6 +97,18 @@ pr_out_sp(unsigned int num, const char *fmt, ...)
g_new_line_count = 0; \
}
static void __attribute__((format(printf, 1, 2)))
pr_out_tty(const char *fmt, ...)
{
va_list ap;
if (!isatty(STDOUT_FILENO))
return;
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
}
static void __pr_out_indent_inc(void)
{
if (g_indent_level + INDENT_STR_STEP > INDENT_STR_MAXLEN)
@ -135,9 +148,8 @@ static int _mnlg_socket_recv_run(struct mnlg_socket *nlg,
return 0;
}
static int _mnlg_socket_sndrcv(struct mnlg_socket *nlg,
const struct nlmsghdr *nlh,
mnl_cb_t data_cb, void *data)
static int _mnlg_socket_send(struct mnlg_socket *nlg,
const struct nlmsghdr *nlh)
{
int err;
@ -146,6 +158,18 @@ static int _mnlg_socket_sndrcv(struct mnlg_socket *nlg,
pr_err("Failed to call mnlg_socket_send\n");
return -errno;
}
return 0;
}
static int _mnlg_socket_sndrcv(struct mnlg_socket *nlg,
const struct nlmsghdr *nlh,
mnl_cb_t data_cb, void *data)
{
int err;
err = _mnlg_socket_send(nlg, nlh);
if (err)
return err;
return _mnlg_socket_recv_run(nlg, data_cb, data);
}
@ -233,9 +257,12 @@ static void ifname_map_free(struct ifname_map *ifname_map)
#define DL_OPT_HEALTH_REPORTER_NAME BIT(27)
#define DL_OPT_HEALTH_REPORTER_GRACEFUL_PERIOD BIT(28)
#define DL_OPT_HEALTH_REPORTER_AUTO_RECOVER BIT(29)
#define DL_OPT_TRAP_NAME BIT(30)
#define DL_OPT_TRAP_ACTION BIT(31)
#define DL_OPT_TRAP_GROUP_NAME BIT(32)
struct dl_opts {
uint32_t present; /* flags of present items */
uint64_t present; /* flags of present items */
char *bus_name;
char *dev_name;
uint32_t port_index;
@ -269,6 +296,9 @@ struct dl_opts {
const char *reporter_name;
uint64_t reporter_graceful_period;
bool reporter_auto_recover;
const char *trap_name;
const char *trap_group_name;
enum devlink_trap_action trap_action;
};
struct dl {
@ -282,6 +312,7 @@ struct dl {
bool json_output;
bool pretty_output;
bool verbose;
bool stats;
struct {
bool present;
char *bus_name;
@ -436,6 +467,24 @@ static const enum mnl_attr_data_type devlink_policy[DEVLINK_ATTR_MAX + 1] = {
[DEVLINK_ATTR_HEALTH_REPORTER_RECOVER_COUNT] = MNL_TYPE_U64,
[DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS] = MNL_TYPE_U64,
[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD] = MNL_TYPE_U64,
[DEVLINK_ATTR_FLASH_UPDATE_COMPONENT] = MNL_TYPE_STRING,
[DEVLINK_ATTR_FLASH_UPDATE_STATUS_MSG] = MNL_TYPE_STRING,
[DEVLINK_ATTR_FLASH_UPDATE_STATUS_DONE] = MNL_TYPE_U64,
[DEVLINK_ATTR_FLASH_UPDATE_STATUS_TOTAL] = MNL_TYPE_U64,
[DEVLINK_ATTR_STATS] = MNL_TYPE_NESTED,
[DEVLINK_ATTR_TRAP_NAME] = MNL_TYPE_STRING,
[DEVLINK_ATTR_TRAP_ACTION] = MNL_TYPE_U8,
[DEVLINK_ATTR_TRAP_TYPE] = MNL_TYPE_U8,
[DEVLINK_ATTR_TRAP_GENERIC] = MNL_TYPE_FLAG,
[DEVLINK_ATTR_TRAP_METADATA] = MNL_TYPE_NESTED,
[DEVLINK_ATTR_TRAP_GROUP_NAME] = MNL_TYPE_STRING,
[DEVLINK_ATTR_RELOAD_FAILED] = MNL_TYPE_U8,
};
static const enum mnl_attr_data_type
devlink_stats_policy[DEVLINK_ATTR_STATS_MAX + 1] = {
[DEVLINK_ATTR_STATS_RX_PACKETS] = MNL_TYPE_U64,
[DEVLINK_ATTR_STATS_RX_BYTES] = MNL_TYPE_U64,
};
static int attr_cb(const struct nlattr *attr, void *data)
@ -454,6 +503,25 @@ static int attr_cb(const struct nlattr *attr, void *data)
return MNL_CB_OK;
}
static int attr_stats_cb(const struct nlattr *attr, void *data)
{
const struct nlattr **tb = data;
int type;
/* Allow the tool to work on top of newer kernels that might contain
* more attributes.
*/
if (mnl_attr_type_valid(attr, DEVLINK_ATTR_STATS_MAX) < 0)
return MNL_CB_OK;
type = mnl_attr_get_type(attr);
if (mnl_attr_validate(attr, devlink_stats_policy[type]) < 0)
return MNL_CB_ERROR;
tb[type] = attr;
return MNL_CB_OK;
}
static int ifname_map_cb(const struct nlmsghdr *nlh, void *data)
{
struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
@ -735,7 +803,7 @@ static int dl_argv_handle_port(struct dl *dl, char **p_bus_name,
static int dl_argv_handle_both(struct dl *dl, char **p_bus_name,
char **p_dev_name, uint32_t *p_port_index,
uint32_t *p_handle_bit)
uint64_t *p_handle_bit)
{
char *str = dl_argv_next(dl);
unsigned int slash_count;
@ -1014,8 +1082,22 @@ static int param_cmode_get(const char *cmodestr,
return 0;
}
static int trap_action_get(const char *actionstr,
enum devlink_trap_action *p_action)
{
if (strcmp(actionstr, "drop") == 0) {
*p_action = DEVLINK_TRAP_ACTION_DROP;
} else if (strcmp(actionstr, "trap") == 0) {
*p_action = DEVLINK_TRAP_ACTION_TRAP;
} else {
pr_err("Unknown trap action \"%s\"\n", actionstr);
return -EINVAL;
}
return 0;
}
struct dl_args_metadata {
uint32_t o_flag;
uint64_t o_flag;
char err_msg[DL_ARGS_REQUIRED_MAX_ERR_LEN];
};
@ -1040,12 +1122,14 @@ static const struct dl_args_metadata dl_args_required[] = {
{DL_OPT_REGION_ADDRESS, "Region address value expected."},
{DL_OPT_REGION_LENGTH, "Region length value expected."},
{DL_OPT_HEALTH_REPORTER_NAME, "Reporter's name is expected."},
{DL_OPT_TRAP_NAME, "Trap's name is expected."},
{DL_OPT_TRAP_GROUP_NAME, "Trap group's name is expected."},
};
static int dl_args_finding_required_validate(uint32_t o_required,
uint32_t o_found)
static int dl_args_finding_required_validate(uint64_t o_required,
uint64_t o_found)
{
uint32_t o_flag;
uint64_t o_flag;
int i;
for (i = 0; i < ARRAY_SIZE(dl_args_required); i++) {
@ -1058,16 +1142,16 @@ static int dl_args_finding_required_validate(uint32_t o_required,
return 0;
}
static int dl_argv_parse(struct dl *dl, uint32_t o_required,
uint32_t o_optional)
static int dl_argv_parse(struct dl *dl, uint64_t o_required,
uint64_t o_optional)
{
struct dl_opts *opts = &dl->opts;
uint32_t o_all = o_required | o_optional;
uint32_t o_found = 0;
uint64_t o_all = o_required | o_optional;
uint64_t o_found = 0;
int err;
if (o_required & DL_OPT_HANDLE && o_required & DL_OPT_HANDLEP) {
uint32_t handle_bit;
uint64_t handle_bit;
err = dl_argv_handle_both(dl, &opts->bus_name, &opts->dev_name,
&opts->port_index, &handle_bit);
@ -1329,6 +1413,32 @@ static int dl_argv_parse(struct dl *dl, uint32_t o_required,
if (err)
return err;
o_found |= DL_OPT_HEALTH_REPORTER_AUTO_RECOVER;
} else if (dl_argv_match(dl, "trap") &&
(o_all & DL_OPT_TRAP_NAME)) {
dl_arg_inc(dl);
err = dl_argv_str(dl, &opts->trap_name);
if (err)
return err;
o_found |= DL_OPT_TRAP_NAME;
} else if (dl_argv_match(dl, "group") &&
(o_all & DL_OPT_TRAP_GROUP_NAME)) {
dl_arg_inc(dl);
err = dl_argv_str(dl, &opts->trap_group_name);
if (err)
return err;
o_found |= DL_OPT_TRAP_GROUP_NAME;
} else if (dl_argv_match(dl, "action") &&
(o_all & DL_OPT_TRAP_ACTION)) {
const char *actionstr;
dl_arg_inc(dl);
err = dl_argv_str(dl, &actionstr);
if (err)
return err;
err = trap_action_get(actionstr, &opts->trap_action);
if (err)
return err;
o_found |= DL_OPT_TRAP_ACTION;
} else {
pr_err("Unknown option \"%s\"\n", dl_argv(dl));
return -EINVAL;
@ -1442,11 +1552,20 @@ static void dl_opts_put(struct nlmsghdr *nlh, struct dl *dl)
if (opts->present & DL_OPT_HEALTH_REPORTER_AUTO_RECOVER)
mnl_attr_put_u8(nlh, DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER,
opts->reporter_auto_recover);
if (opts->present & DL_OPT_TRAP_NAME)
mnl_attr_put_strz(nlh, DEVLINK_ATTR_TRAP_NAME,
opts->trap_name);
if (opts->present & DL_OPT_TRAP_GROUP_NAME)
mnl_attr_put_strz(nlh, DEVLINK_ATTR_TRAP_GROUP_NAME,
opts->trap_group_name);
if (opts->present & DL_OPT_TRAP_ACTION)
mnl_attr_put_u8(nlh, DEVLINK_ATTR_TRAP_ACTION,
opts->trap_action);
}
static int dl_argv_parse_put(struct nlmsghdr *nlh, struct dl *dl,
uint32_t o_required, uint32_t o_optional)
uint64_t o_required, uint64_t o_optional)
{
int err;
@ -1859,11 +1978,6 @@ static void pr_out_region_chunk(struct dl *dl, uint8_t *data, uint32_t len,
pr_out_region_chunk_end(dl);
}
static void pr_out_dev(struct dl *dl, struct nlattr **tb)
{
pr_out_handle(dl, tb);
}
static void pr_out_section_start(struct dl *dl, const char *name)
{
if (dl->json_output) {
@ -1945,6 +2059,30 @@ static void pr_out_entry_end(struct dl *dl)
__pr_out_newline();
}
static void pr_out_stats(struct dl *dl, struct nlattr *nla_stats)
{
struct nlattr *tb[DEVLINK_ATTR_STATS_MAX + 1] = {};
int err;
if (!dl->stats)
return;
err = mnl_attr_parse_nested(nla_stats, attr_stats_cb, tb);
if (err != MNL_CB_OK)
return;
pr_out_object_start(dl, "stats");
pr_out_object_start(dl, "rx");
if (tb[DEVLINK_ATTR_STATS_RX_BYTES])
pr_out_u64(dl, "bytes",
mnl_attr_get_u64(tb[DEVLINK_ATTR_STATS_RX_BYTES]));
if (tb[DEVLINK_ATTR_STATS_RX_PACKETS])
pr_out_u64(dl, "packets",
mnl_attr_get_u64(tb[DEVLINK_ATTR_STATS_RX_PACKETS]));
pr_out_object_end(dl);
pr_out_object_end(dl);
}
static const char *param_cmode_name(uint8_t cmode)
{
switch (cmode) {
@ -2139,6 +2277,31 @@ static const struct param_val_conv param_val_conv[] = {
.vstr = "flash",
.vuint = DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_FLASH,
},
{
.name = "reset_dev_on_drv_probe",
.vstr = "unknown",
.vuint = DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_UNKNOWN,
},
{
.name = "fw_load_policy",
.vstr = "unknown",
.vuint = DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_UNKNOWN,
},
{
.name = "reset_dev_on_drv_probe",
.vstr = "always",
.vuint = DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_ALWAYS,
},
{
.name = "reset_dev_on_drv_probe",
.vstr = "never",
.vuint = DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_NEVER,
},
{
.name = "reset_dev_on_drv_probe",
.vstr = "disk",
.vuint = DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_DISK,
},
};
#define PARAM_VAL_CONV_LEN ARRAY_SIZE(param_val_conv)
@ -2515,11 +2678,23 @@ static int cmd_dev_show_cb(const struct nlmsghdr *nlh, void *data)
struct dl *dl = data;
struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
uint8_t reload_failed = 0;
mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME])
return MNL_CB_ERROR;
pr_out_dev(dl, tb);
if (tb[DEVLINK_ATTR_RELOAD_FAILED])
reload_failed = mnl_attr_get_u8(tb[DEVLINK_ATTR_RELOAD_FAILED]);
if (reload_failed) {
__pr_out_handle_start(dl, tb, true, false);
pr_out_bool(dl, "reload_failed", true);
pr_out_handle_end(dl);
} else {
pr_out_handle(dl, tb);
}
return MNL_CB_OK;
}
@ -2712,9 +2887,151 @@ static void cmd_dev_flash_help(void)
pr_err("Usage: devlink dev flash DEV file PATH [ component NAME ]\n");
}
struct cmd_dev_flash_status_ctx {
struct dl *dl;
char *last_msg;
char *last_component;
uint8_t not_first:1,
last_pc:1,
received_end:1,
flash_done:1;
};
static int nullstrcmp(const char *str1, const char *str2)
{
if (str1 && str2)
return strcmp(str1, str2);
if (!str1 && !str2)
return 0;
return str1 ? 1 : -1;
}
static int cmd_dev_flash_status_cb(const struct nlmsghdr *nlh, void *data)
{
struct cmd_dev_flash_status_ctx *ctx = data;
struct dl_opts *opts = &ctx->dl->opts;
struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
const char *component = NULL;
uint64_t done = 0, total = 0;
const char *msg = NULL;
const char *bus_name;
const char *dev_name;
if (genl->cmd != DEVLINK_CMD_FLASH_UPDATE_STATUS &&
genl->cmd != DEVLINK_CMD_FLASH_UPDATE_END)
return MNL_CB_STOP;
mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME])
return MNL_CB_ERROR;
bus_name = mnl_attr_get_str(tb[DEVLINK_ATTR_BUS_NAME]);
dev_name = mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME]);
if (strcmp(bus_name, opts->bus_name) ||
strcmp(dev_name, opts->dev_name))
return MNL_CB_ERROR;
if (genl->cmd == DEVLINK_CMD_FLASH_UPDATE_END && ctx->not_first) {
pr_out("\n");
free(ctx->last_msg);
free(ctx->last_component);
ctx->received_end = 1;
return MNL_CB_STOP;
}
if (tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_MSG])
msg = mnl_attr_get_str(tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_MSG]);
if (tb[DEVLINK_ATTR_FLASH_UPDATE_COMPONENT])
component = mnl_attr_get_str(tb[DEVLINK_ATTR_FLASH_UPDATE_COMPONENT]);
if (tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_DONE])
done = mnl_attr_get_u64(tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_DONE]);
if (tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_TOTAL])
total = mnl_attr_get_u64(tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_TOTAL]);
if (!nullstrcmp(msg, ctx->last_msg) &&
!nullstrcmp(component, ctx->last_component) &&
ctx->last_pc && ctx->not_first) {
pr_out_tty("\b\b\b\b\b"); /* clean percentage */
} else {
if (ctx->not_first)
pr_out("\n");
if (component) {
pr_out("[%s] ", component);
free(ctx->last_component);
ctx->last_component = strdup(component);
}
if (msg) {
pr_out("%s", msg);
free(ctx->last_msg);
ctx->last_msg = strdup(msg);
}
}
if (total) {
pr_out_tty(" %3lu%%", (done * 100) / total);
ctx->last_pc = 1;
} else {
ctx->last_pc = 0;
}
fflush(stdout);
ctx->not_first = 1;
return MNL_CB_STOP;
}
static int cmd_dev_flash_fds_process(struct cmd_dev_flash_status_ctx *ctx,
struct mnlg_socket *nlg_ntf,
int pipe_r)
{
int nlfd = mnlg_socket_get_fd(nlg_ntf);
fd_set fds[3];
int fdmax;
int i;
int err;
int err2;
for (i = 0; i < 3; i++)
FD_ZERO(&fds[i]);
FD_SET(pipe_r, &fds[0]);
fdmax = pipe_r + 1;
FD_SET(nlfd, &fds[0]);
if (nlfd >= fdmax)
fdmax = nlfd + 1;
while (select(fdmax, &fds[0], &fds[1], &fds[2], NULL) < 0) {
if (errno == EINTR)
continue;
pr_err("select() failed\n");
return -errno;
}
if (FD_ISSET(nlfd, &fds[0])) {
err = _mnlg_socket_recv_run(nlg_ntf,
cmd_dev_flash_status_cb, ctx);
if (err)
return err;
}
if (FD_ISSET(pipe_r, &fds[0])) {
err = read(pipe_r, &err2, sizeof(err2));
if (err == -1) {
pr_err("Failed to read pipe\n");
return -errno;
}
if (err2)
return err2;
ctx->flash_done = 1;
}
return 0;
}
static int cmd_dev_flash(struct dl *dl)
{
struct cmd_dev_flash_status_ctx ctx = {.dl = dl,};
struct mnlg_socket *nlg_ntf;
struct nlmsghdr *nlh;
int pipe_r, pipe_w;
int pipe_fds[2];
pid_t pid;
int err;
if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
@ -2730,7 +3047,48 @@ static int cmd_dev_flash(struct dl *dl)
if (err)
return err;
return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
nlg_ntf = mnlg_socket_open(DEVLINK_GENL_NAME, DEVLINK_GENL_VERSION);
if (!nlg_ntf)
return err;
err = _mnlg_socket_group_add(nlg_ntf, DEVLINK_GENL_MCGRP_CONFIG_NAME);
if (err)
return err;
err = pipe(pipe_fds);
if (err == -1)
return -errno;
pipe_r = pipe_fds[0];
pipe_w = pipe_fds[1];
pid = fork();
if (pid == -1) {
close(pipe_r);
close(pipe_w);
return -errno;
} else if (!pid) {
/* In child, just execute the flash and pass returned
* value through pipe once it is done.
*/
close(pipe_r);
err = _mnlg_socket_send(dl->nlg, nlh);
write(pipe_w, &err, sizeof(err));
close(pipe_w);
exit(0);
}
close(pipe_w);
do {
err = cmd_dev_flash_fds_process(&ctx, nlg_ntf, pipe_r);
if (err)
goto out;
} while (!ctx.flash_done || (ctx.not_first && !ctx.received_end));
err = _mnlg_socket_recv_run(dl->nlg, NULL, NULL);
out:
close(pipe_r);
mnlg_socket_close(nlg_ntf);
return err;
}
static int cmd_dev(struct dl *dl)
@ -3764,6 +4122,17 @@ static const char *cmd_name(uint8_t cmd)
case DEVLINK_CMD_REGION_SET: return "set";
case DEVLINK_CMD_REGION_NEW: return "new";
case DEVLINK_CMD_REGION_DEL: return "del";
case DEVLINK_CMD_FLASH_UPDATE: return "begin";
case DEVLINK_CMD_FLASH_UPDATE_END: return "end";
case DEVLINK_CMD_FLASH_UPDATE_STATUS: return "status";
case DEVLINK_CMD_TRAP_GET: return "get";
case DEVLINK_CMD_TRAP_SET: return "set";
case DEVLINK_CMD_TRAP_NEW: return "new";
case DEVLINK_CMD_TRAP_DEL: return "del";
case DEVLINK_CMD_TRAP_GROUP_GET: return "get";
case DEVLINK_CMD_TRAP_GROUP_SET: return "set";
case DEVLINK_CMD_TRAP_GROUP_NEW: return "new";
case DEVLINK_CMD_TRAP_GROUP_DEL: return "del";
default: return "<unknown cmd>";
}
}
@ -3792,6 +4161,20 @@ static const char *cmd_obj(uint8_t cmd)
case DEVLINK_CMD_REGION_NEW:
case DEVLINK_CMD_REGION_DEL:
return "region";
case DEVLINK_CMD_FLASH_UPDATE:
case DEVLINK_CMD_FLASH_UPDATE_END:
case DEVLINK_CMD_FLASH_UPDATE_STATUS:
return "flash";
case DEVLINK_CMD_TRAP_GET:
case DEVLINK_CMD_TRAP_SET:
case DEVLINK_CMD_TRAP_NEW:
case DEVLINK_CMD_TRAP_DEL:
return "trap";
case DEVLINK_CMD_TRAP_GROUP_GET:
case DEVLINK_CMD_TRAP_GROUP_SET:
case DEVLINK_CMD_TRAP_GROUP_NEW:
case DEVLINK_CMD_TRAP_GROUP_DEL:
return "trap-group";
default: return "<unknown obj>";
}
}
@ -3816,7 +4199,32 @@ static bool cmd_filter_check(struct dl *dl, uint8_t cmd)
return false;
}
static void pr_out_flash_update(struct dl *dl, struct nlattr **tb)
{
__pr_out_handle_start(dl, tb, true, false);
if (tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_MSG])
pr_out_str(dl, "msg",
mnl_attr_get_str(tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_MSG]));
if (tb[DEVLINK_ATTR_FLASH_UPDATE_COMPONENT])
pr_out_str(dl, "component",
mnl_attr_get_str(tb[DEVLINK_ATTR_FLASH_UPDATE_COMPONENT]));
if (tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_DONE])
pr_out_u64(dl, "done",
mnl_attr_get_u64(tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_DONE]));
if (tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_TOTAL])
pr_out_u64(dl, "total",
mnl_attr_get_u64(tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_TOTAL]));
pr_out_handle_end(dl);
}
static void pr_out_region(struct dl *dl, struct nlattr **tb);
static void pr_out_trap(struct dl *dl, struct nlattr **tb, bool array);
static void pr_out_trap_group(struct dl *dl, struct nlattr **tb, bool array);
static int cmd_mon_show_cb(const struct nlmsghdr *nlh, void *data)
{
@ -3837,7 +4245,7 @@ static int cmd_mon_show_cb(const struct nlmsghdr *nlh, void *data)
if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME])
return MNL_CB_ERROR;
pr_out_mon_header(genl->cmd);
pr_out_dev(dl, tb);
pr_out_handle(dl, tb);
break;
case DEVLINK_CMD_PORT_GET: /* fall through */
case DEVLINK_CMD_PORT_SET: /* fall through */
@ -3872,6 +4280,43 @@ static int cmd_mon_show_cb(const struct nlmsghdr *nlh, void *data)
pr_out_mon_header(genl->cmd);
pr_out_region(dl, tb);
break;
case DEVLINK_CMD_FLASH_UPDATE: /* fall through */
case DEVLINK_CMD_FLASH_UPDATE_END: /* fall through */
case DEVLINK_CMD_FLASH_UPDATE_STATUS:
mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME])
return MNL_CB_ERROR;
pr_out_mon_header(genl->cmd);
pr_out_flash_update(dl, tb);
break;
case DEVLINK_CMD_TRAP_GET: /* fall through */
case DEVLINK_CMD_TRAP_SET: /* fall through */
case DEVLINK_CMD_TRAP_NEW: /* fall through */
case DEVLINK_CMD_TRAP_DEL:
mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
!tb[DEVLINK_ATTR_TRAP_NAME] ||
!tb[DEVLINK_ATTR_TRAP_TYPE] ||
!tb[DEVLINK_ATTR_TRAP_ACTION] ||
!tb[DEVLINK_ATTR_TRAP_GROUP_NAME] ||
!tb[DEVLINK_ATTR_TRAP_METADATA] ||
!tb[DEVLINK_ATTR_STATS])
return MNL_CB_ERROR;
pr_out_mon_header(genl->cmd);
pr_out_trap(dl, tb, false);
break;
case DEVLINK_CMD_TRAP_GROUP_GET: /* fall through */
case DEVLINK_CMD_TRAP_GROUP_SET: /* fall through */
case DEVLINK_CMD_TRAP_GROUP_NEW: /* fall through */
case DEVLINK_CMD_TRAP_GROUP_DEL:
mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
!tb[DEVLINK_ATTR_TRAP_GROUP_NAME] ||
!tb[DEVLINK_ATTR_STATS])
return MNL_CB_ERROR;
pr_out_mon_header(genl->cmd);
pr_out_trap_group(dl, tb, false);
break;
}
return MNL_CB_OK;
}
@ -3885,7 +4330,9 @@ static int cmd_mon_show(struct dl *dl)
while ((cur_obj = dl_argv_index(dl, index++))) {
if (strcmp(cur_obj, "all") != 0 &&
strcmp(cur_obj, "dev") != 0 &&
strcmp(cur_obj, "port") != 0) {
strcmp(cur_obj, "port") != 0 &&
strcmp(cur_obj, "trap") != 0 &&
strcmp(cur_obj, "trap-group") != 0) {
pr_err("Unknown object \"%s\"\n", cur_obj);
return -EINVAL;
}
@ -3902,7 +4349,7 @@ static int cmd_mon_show(struct dl *dl)
static void cmd_mon_help(void)
{
pr_err("Usage: devlink monitor [ all | OBJECT-LIST ]\n"
"where OBJECT-LIST := { dev | port }\n");
"where OBJECT-LIST := { dev | port | trap | trap-group }\n");
}
static int cmd_mon(struct dl *dl)
@ -6330,12 +6777,255 @@ static int cmd_health(struct dl *dl)
return -ENOENT;
}
static const char *trap_type_name(uint8_t type)
{
switch (type) {
case DEVLINK_TRAP_TYPE_DROP:
return "drop";
case DEVLINK_TRAP_TYPE_EXCEPTION:
return "exception";
default:
return "<unknown type>";
}
}
static const char *trap_action_name(uint8_t action)
{
switch (action) {
case DEVLINK_TRAP_ACTION_DROP:
return "drop";
case DEVLINK_TRAP_ACTION_TRAP:
return "trap";
default:
return "<unknown action>";
}
}
static const char *trap_metadata_name(const struct nlattr *attr)
{
switch (attr->nla_type) {
case DEVLINK_ATTR_TRAP_METADATA_TYPE_IN_PORT:
return "input_port";
default:
return "<unknown metadata type>";
}
}
static void pr_out_trap_metadata(struct dl *dl, struct nlattr *attr)
{
struct nlattr *attr_metadata;
pr_out_array_start(dl, "metadata");
mnl_attr_for_each_nested(attr_metadata, attr)
pr_out_str_value(dl, trap_metadata_name(attr_metadata));
pr_out_array_end(dl);
}
static void pr_out_trap(struct dl *dl, struct nlattr **tb, bool array)
{
uint8_t action = mnl_attr_get_u8(tb[DEVLINK_ATTR_TRAP_ACTION]);
uint8_t type = mnl_attr_get_u8(tb[DEVLINK_ATTR_TRAP_TYPE]);
if (array)
pr_out_handle_start_arr(dl, tb);
else
__pr_out_handle_start(dl, tb, true, false);
pr_out_str(dl, "name", mnl_attr_get_str(tb[DEVLINK_ATTR_TRAP_NAME]));
pr_out_str(dl, "type", trap_type_name(type));
pr_out_bool(dl, "generic", !!tb[DEVLINK_ATTR_TRAP_GENERIC]);
pr_out_str(dl, "action", trap_action_name(action));
pr_out_str(dl, "group",
mnl_attr_get_str(tb[DEVLINK_ATTR_TRAP_GROUP_NAME]));
if (dl->verbose)
pr_out_trap_metadata(dl, tb[DEVLINK_ATTR_TRAP_METADATA]);
pr_out_stats(dl, tb[DEVLINK_ATTR_STATS]);
pr_out_handle_end(dl);
}
static int cmd_trap_show_cb(const struct nlmsghdr *nlh, void *data)
{
struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
struct dl *dl = data;
mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
!tb[DEVLINK_ATTR_TRAP_NAME] || !tb[DEVLINK_ATTR_TRAP_TYPE] ||
!tb[DEVLINK_ATTR_TRAP_ACTION] ||
!tb[DEVLINK_ATTR_TRAP_GROUP_NAME] ||
!tb[DEVLINK_ATTR_TRAP_METADATA] || !tb[DEVLINK_ATTR_STATS])
return MNL_CB_ERROR;
pr_out_trap(dl, tb, true);
return MNL_CB_OK;
}
static void cmd_trap_help(void)
{
pr_err("Usage: devlink trap set DEV trap TRAP [ action { trap | drop } ]\n");
pr_err(" devlink trap show [ DEV trap TRAP ]\n");
pr_err(" devlink trap group set DEV group GROUP [ action { trap | drop } ]\n");
pr_err(" devlink trap group show [ DEV group GROUP ]\n");
}
static int cmd_trap_show(struct dl *dl)
{
uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
struct nlmsghdr *nlh;
int err;
if (dl_argc(dl) == 0)
flags |= NLM_F_DUMP;
nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_TRAP_GET, flags);
if (dl_argc(dl) > 0) {
err = dl_argv_parse_put(nlh, dl,
DL_OPT_HANDLE | DL_OPT_TRAP_NAME, 0);
if (err)
return err;
}
pr_out_section_start(dl, "trap");
err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_trap_show_cb, dl);
pr_out_section_end(dl);
return err;
}
static int cmd_trap_set(struct dl *dl)
{
struct nlmsghdr *nlh;
int err;
nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_TRAP_SET,
NLM_F_REQUEST | NLM_F_ACK);
err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE | DL_OPT_TRAP_NAME,
DL_OPT_TRAP_ACTION);
if (err)
return err;
return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
}
static void pr_out_trap_group(struct dl *dl, struct nlattr **tb, bool array)
{
if (array)
pr_out_handle_start_arr(dl, tb);
else
__pr_out_handle_start(dl, tb, true, false);
pr_out_str(dl, "name",
mnl_attr_get_str(tb[DEVLINK_ATTR_TRAP_GROUP_NAME]));
pr_out_bool(dl, "generic", !!tb[DEVLINK_ATTR_TRAP_GENERIC]);
pr_out_stats(dl, tb[DEVLINK_ATTR_STATS]);
pr_out_handle_end(dl);
}
static int cmd_trap_group_show_cb(const struct nlmsghdr *nlh, void *data)
{
struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
struct dl *dl = data;
mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
!tb[DEVLINK_ATTR_TRAP_GROUP_NAME] || !tb[DEVLINK_ATTR_STATS])
return MNL_CB_ERROR;
pr_out_trap_group(dl, tb, true);
return MNL_CB_OK;
}
static int cmd_trap_group_show(struct dl *dl)
{
uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
struct nlmsghdr *nlh;
int err;
if (dl_argc(dl) == 0)
flags |= NLM_F_DUMP;
nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_TRAP_GROUP_GET, flags);
if (dl_argc(dl) > 0) {
err = dl_argv_parse_put(nlh, dl,
DL_OPT_HANDLE | DL_OPT_TRAP_GROUP_NAME,
0);
if (err)
return err;
}
pr_out_section_start(dl, "trap_group");
err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_trap_group_show_cb, dl);
pr_out_section_end(dl);
return err;
}
static int cmd_trap_group_set(struct dl *dl)
{
struct nlmsghdr *nlh;
int err;
nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_TRAP_GROUP_SET,
NLM_F_REQUEST | NLM_F_ACK);
err = dl_argv_parse_put(nlh, dl,
DL_OPT_HANDLE | DL_OPT_TRAP_GROUP_NAME,
DL_OPT_TRAP_ACTION);
if (err)
return err;
return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
}
static int cmd_trap_group(struct dl *dl)
{
if (dl_argv_match(dl, "help")) {
cmd_trap_help();
return 0;
} else if (dl_argv_match(dl, "show") ||
dl_argv_match(dl, "list") || dl_no_arg(dl)) {
dl_arg_inc(dl);
return cmd_trap_group_show(dl);
} else if (dl_argv_match(dl, "set")) {
dl_arg_inc(dl);
return cmd_trap_group_set(dl);
}
pr_err("Command \"%s\" not found\n", dl_argv(dl));
return -ENOENT;
}
static int cmd_trap(struct dl *dl)
{
if (dl_argv_match(dl, "help")) {
cmd_trap_help();
return 0;
} else if (dl_argv_match(dl, "show") ||
dl_argv_match(dl, "list") || dl_no_arg(dl)) {
dl_arg_inc(dl);
return cmd_trap_show(dl);
} else if (dl_argv_match(dl, "set")) {
dl_arg_inc(dl);
return cmd_trap_set(dl);
} else if (dl_argv_match(dl, "group")) {
dl_arg_inc(dl);
return cmd_trap_group(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 | resource | region | health }\n"
" OPTIONS := { -V[ersion] | -n[o-nice-names] | -j[son] | -p[retty] | -v[erbose] }\n");
"where OBJECT := { dev | port | sb | monitor | dpipe | resource | region | health | trap }\n"
" OPTIONS := { -V[ersion] | -n[o-nice-names] | -j[son] | -p[retty] | -v[erbose] -s[tatistics] }\n");
}
static int dl_cmd(struct dl *dl, int argc, char **argv)
@ -6370,6 +7060,9 @@ static int dl_cmd(struct dl *dl, int argc, char **argv)
} else if (dl_argv_match(dl, "health")) {
dl_arg_inc(dl);
return cmd_health(dl);
} else if (dl_argv_match(dl, "trap")) {
dl_arg_inc(dl);
return cmd_trap(dl);
}
pr_err("Object \"%s\" not found\n", dl_argv(dl));
return -ENOENT;
@ -6479,6 +7172,7 @@ int main(int argc, char **argv)
{ "json", no_argument, NULL, 'j' },
{ "pretty", no_argument, NULL, 'p' },
{ "verbose", no_argument, NULL, 'v' },
{ "statistics", no_argument, NULL, 's' },
{ NULL, 0, NULL, 0 }
};
const char *batch_file = NULL;
@ -6494,7 +7188,7 @@ int main(int argc, char **argv)
return EXIT_FAILURE;
}
while ((opt = getopt_long(argc, argv, "Vfb:njpv",
while ((opt = getopt_long(argc, argv, "Vfb:njpvs",
long_options, NULL)) >= 0) {
switch (opt) {
@ -6520,6 +7214,9 @@ int main(int argc, char **argv)
case 'v':
dl->verbose = true;
break;
case 's':
dl->stats = true;
break;
default:
pr_err("Unknown option.\n");
help();

View File

@ -320,3 +320,8 @@ void mnlg_socket_close(struct mnlg_socket *nlg)
free(nlg->buf);
free(nlg);
}
int mnlg_socket_get_fd(struct mnlg_socket *nlg)
{
return mnl_socket_get_fd(nlg->nl);
}

View File

@ -23,5 +23,6 @@ int mnlg_socket_recv_run(struct mnlg_socket *nlg, mnl_cb_t data_cb, void *data);
int mnlg_socket_group_add(struct mnlg_socket *nlg, const char *group_name);
struct mnlg_socket *mnlg_socket_open(const char *family_name, uint8_t version);
void mnlg_socket_close(struct mnlg_socket *nlg);
int mnlg_socket_get_fd(struct mnlg_socket *nlg);
#endif /* _MNLG_H_ */

View File

@ -106,6 +106,7 @@ enum bpf_cmd {
BPF_TASK_FD_QUERY,
BPF_MAP_LOOKUP_AND_DELETE_ELEM,
BPF_MAP_FREEZE,
BPF_BTF_GET_NEXT_ID,
};
enum bpf_map_type {
@ -134,6 +135,7 @@ enum bpf_map_type {
BPF_MAP_TYPE_QUEUE,
BPF_MAP_TYPE_STACK,
BPF_MAP_TYPE_SK_STORAGE,
BPF_MAP_TYPE_DEVMAP_HASH,
};
/* Note that tracing related programs such as
@ -283,6 +285,9 @@ enum bpf_attach_type {
*/
#define BPF_F_TEST_RND_HI32 (1U << 2)
/* The verifier internal test flag. Behavior is undefined */
#define BPF_F_TEST_STATE_FREQ (1U << 3)
/* When BPF ldimm64's insn[0].src_reg != 0 then this can have
* two extensions:
*
@ -336,6 +341,9 @@ enum bpf_attach_type {
#define BPF_F_RDONLY_PROG (1U << 7)
#define BPF_F_WRONLY_PROG (1U << 8)
/* Clone map from listener for newly accepted socket */
#define BPF_F_CLONE (1U << 9)
/* flags for BPF_PROG_QUERY */
#define BPF_F_QUERY_EFFECTIVE (1U << 0)
@ -575,6 +583,8 @@ union bpf_attr {
* limited to five).
*
* Each time the helper is called, it appends a line to the trace.
* Lines are discarded while *\/sys/kernel/debug/tracing/trace* is
* open, use *\/sys/kernel/debug/tracing/trace_pipe* to avoid this.
* The format of the trace is customizable, and the exact output
* one will get depends on the options set in
* *\/sys/kernel/debug/tracing/trace_options* (see also the
@ -1013,7 +1023,7 @@ union bpf_attr {
* The realm of the route for the packet associated to *skb*, or 0
* if none was found.
*
* int bpf_perf_event_output(struct pt_reg *ctx, struct bpf_map *map, u64 flags, void *data, u64 size)
* int bpf_perf_event_output(struct pt_regs *ctx, struct bpf_map *map, u64 flags, void *data, u64 size)
* Description
* Write raw *data* blob into a special BPF perf event held by
* *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. This perf
@ -1075,7 +1085,7 @@ union bpf_attr {
* Return
* 0 on success, or a negative error in case of failure.
*
* int bpf_get_stackid(struct pt_reg *ctx, struct bpf_map *map, u64 flags)
* int bpf_get_stackid(struct pt_regs *ctx, struct bpf_map *map, u64 flags)
* Description
* Walk a user or a kernel stack and return its id. To achieve
* this, the helper needs *ctx*, which is a pointer to the context
@ -1724,7 +1734,7 @@ union bpf_attr {
* Return
* 0 on success, or a negative error in case of failure.
*
* int bpf_override_return(struct pt_reg *regs, u64 rc)
* int bpf_override_return(struct pt_regs *regs, u64 rc)
* Description
* Used for error injection, this helper uses kprobes to override
* the return value of the probed function, and to set it to *rc*.
@ -2713,6 +2723,33 @@ union bpf_attr {
* **-EPERM** if no permission to send the *sig*.
*
* **-EAGAIN** if bpf program can try again.
*
* s64 bpf_tcp_gen_syncookie(struct bpf_sock *sk, void *iph, u32 iph_len, struct tcphdr *th, u32 th_len)
* Description
* Try to issue a SYN cookie for the packet with corresponding
* IP/TCP headers, *iph* and *th*, on the listening socket in *sk*.
*
* *iph* points to the start of the IPv4 or IPv6 header, while
* *iph_len* contains **sizeof**\ (**struct iphdr**) or
* **sizeof**\ (**struct ip6hdr**).
*
* *th* points to the start of the TCP header, while *th_len*
* contains the length of the TCP header.
*
* Return
* On success, lower 32 bits hold the generated SYN cookie in
* followed by 16 bits which hold the MSS value for that cookie,
* and the top 16 bits are unused.
*
* On failure, the returned value is one of the following:
*
* **-EINVAL** SYN cookie cannot be issued due to error
*
* **-ENOENT** SYN cookie should not be issued (no SYN flood)
*
* **-EOPNOTSUPP** kernel configuration does not enable SYN cookies
*
* **-EPROTONOSUPPORT** IP packet version is not 4 or 6
*/
#define __BPF_FUNC_MAPPER(FN) \
FN(unspec), \
@ -2824,7 +2861,8 @@ union bpf_attr {
FN(strtoul), \
FN(sk_storage_get), \
FN(sk_storage_delete), \
FN(send_signal),
FN(send_signal), \
FN(tcp_gen_syncookie),
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
* function eBPF program intends to call
@ -3507,6 +3545,10 @@ enum bpf_task_fd_type {
BPF_FD_TYPE_URETPROBE, /* filename + offset */
};
#define BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG (1U << 0)
#define BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL (1U << 1)
#define BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP (1U << 2)
struct bpf_flow_keys {
__u16 nhoff;
__u16 thoff;
@ -3528,6 +3570,8 @@ struct bpf_flow_keys {
__u32 ipv6_dst[4]; /* in6_addr; network order */
};
};
__u32 flags;
__be32 flow_label;
};
struct bpf_func_info {

View File

@ -157,7 +157,8 @@ struct canfd_frame {
#define CAN_TP20 4 /* VAG Transport Protocol v2.0 */
#define CAN_MCNET 5 /* Bosch MCNet */
#define CAN_ISOTP 6 /* ISO 15765-2 Transport Protocol */
#define CAN_NPROTO 7
#define CAN_J1939 7 /* SAE J1939 */
#define CAN_NPROTO 8
#define SOL_CAN_BASE 100
@ -174,6 +175,23 @@ struct sockaddr_can {
/* transport protocol class address information (e.g. ISOTP) */
struct { canid_t rx_id, tx_id; } tp;
/* J1939 address information */
struct {
/* 8 byte name when using dynamic addressing */
__u64 name;
/* pgn:
* 8 bit: PS in PDU2 case, else 0
* 8 bit: PF
* 1 bit: DP
* 1 bit: reserved
*/
__u32 pgn;
/* 1 byte address */
__u8 addr;
} j1939;
/* reserved for future CAN protocols address information */
} can_addr;
};

View File

@ -40,15 +40,15 @@ struct can_bittiming {
};
/*
* CAN harware-dependent bit-timing constant
* CAN hardware-dependent bit-timing constant
*
* Used for calculating and checking bit-timing parameters
*/
struct can_bittiming_const {
char name[16]; /* Name of the CAN controller hardware */
__u32 tseg1_min; /* Time segement 1 = prop_seg + phase_seg1 */
__u32 tseg1_min; /* Time segment 1 = prop_seg + phase_seg1 */
__u32 tseg1_max;
__u32 tseg2_min; /* Time segement 2 = phase_seg2 */
__u32 tseg2_min; /* Time segment 2 = phase_seg2 */
__u32 tseg2_max;
__u32 sjw_max; /* Synchronisation jump width */
__u32 brp_min; /* Bit-rate prescaler */

View File

@ -107,6 +107,16 @@ enum devlink_command {
DEVLINK_CMD_FLASH_UPDATE_END, /* notification only */
DEVLINK_CMD_FLASH_UPDATE_STATUS, /* notification only */
DEVLINK_CMD_TRAP_GET, /* can dump */
DEVLINK_CMD_TRAP_SET,
DEVLINK_CMD_TRAP_NEW,
DEVLINK_CMD_TRAP_DEL,
DEVLINK_CMD_TRAP_GROUP_GET, /* can dump */
DEVLINK_CMD_TRAP_GROUP_SET,
DEVLINK_CMD_TRAP_GROUP_NEW,
DEVLINK_CMD_TRAP_GROUP_DEL,
/* add new commands above here */
__DEVLINK_CMD_MAX,
DEVLINK_CMD_MAX = __DEVLINK_CMD_MAX - 1
@ -192,6 +202,56 @@ enum devlink_param_cmode {
enum devlink_param_fw_load_policy_value {
DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_DRIVER,
DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_FLASH,
DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_DISK,
DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_UNKNOWN,
};
enum devlink_param_reset_dev_on_drv_probe_value {
DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_UNKNOWN,
DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_ALWAYS,
DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_NEVER,
DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_DISK,
};
enum {
DEVLINK_ATTR_STATS_RX_PACKETS, /* u64 */
DEVLINK_ATTR_STATS_RX_BYTES, /* u64 */
__DEVLINK_ATTR_STATS_MAX,
DEVLINK_ATTR_STATS_MAX = __DEVLINK_ATTR_STATS_MAX - 1
};
/**
* enum devlink_trap_action - Packet trap action.
* @DEVLINK_TRAP_ACTION_DROP: Packet is dropped by the device and a copy is not
* sent to the CPU.
* @DEVLINK_TRAP_ACTION_TRAP: The sole copy of the packet is sent to the CPU.
*/
enum devlink_trap_action {
DEVLINK_TRAP_ACTION_DROP,
DEVLINK_TRAP_ACTION_TRAP,
};
/**
* enum devlink_trap_type - Packet trap type.
* @DEVLINK_TRAP_TYPE_DROP: Trap reason is a drop. Trapped packets are only
* processed by devlink and not injected to the
* kernel's Rx path.
* @DEVLINK_TRAP_TYPE_EXCEPTION: Trap reason is an exception. Packet was not
* forwarded as intended due to an exception
* (e.g., missing neighbour entry) and trapped to
* control plane for resolution. Trapped packets
* are processed by devlink and injected to
* the kernel's Rx path.
*/
enum devlink_trap_type {
DEVLINK_TRAP_TYPE_DROP,
DEVLINK_TRAP_TYPE_EXCEPTION,
};
enum {
/* Trap can report input port as metadata */
DEVLINK_ATTR_TRAP_METADATA_TYPE_IN_PORT,
};
enum devlink_attr {
@ -348,6 +408,19 @@ enum devlink_attr {
DEVLINK_ATTR_PORT_PCI_PF_NUMBER, /* u16 */
DEVLINK_ATTR_PORT_PCI_VF_NUMBER, /* u16 */
DEVLINK_ATTR_STATS, /* nested */
DEVLINK_ATTR_TRAP_NAME, /* string */
/* enum devlink_trap_action */
DEVLINK_ATTR_TRAP_ACTION, /* u8 */
/* enum devlink_trap_type */
DEVLINK_ATTR_TRAP_TYPE, /* u8 */
DEVLINK_ATTR_TRAP_GENERIC, /* flag */
DEVLINK_ATTR_TRAP_METADATA, /* nested */
DEVLINK_ATTR_TRAP_GROUP_NAME, /* string */
DEVLINK_ATTR_RELOAD_FAILED, /* u8 0 or 1 */
/* add new attributes above here, update the policy in devlink.c */
__DEVLINK_ATTR_MAX,

View File

@ -237,6 +237,7 @@ struct br_mdb_entry {
#define MDB_PERMANENT 1
__u8 state;
#define MDB_FLAGS_OFFLOAD (1 << 0)
#define MDB_FLAGS_FAST_LEAVE (1 << 1)
__u8 flags;
__u16 vid;
struct {

View File

@ -153,11 +153,20 @@ enum {
INET_DIAG_BBRINFO, /* request as INET_DIAG_VEGASINFO */
INET_DIAG_CLASS_ID, /* request as INET_DIAG_TCLASS */
INET_DIAG_MD5SIG,
INET_DIAG_ULP_INFO,
__INET_DIAG_MAX,
};
#define INET_DIAG_MAX (__INET_DIAG_MAX - 1)
enum {
INET_ULP_INFO_UNSPEC,
INET_ULP_INFO_NAME,
INET_ULP_INFO_TLS,
__INET_ULP_INFO_MAX,
};
#define INET_ULP_INFO_MAX (__INET_ULP_INFO_MAX - 1)
/* INET_DIAG_MEM */
struct inet_diag_meminfo {

View File

@ -160,6 +160,8 @@ enum {
TCA_POLICE_RESULT,
TCA_POLICE_TM,
TCA_POLICE_PAD,
TCA_POLICE_RATE64,
TCA_POLICE_PEAKRATE64,
__TCA_POLICE_MAX
#define TCA_POLICE_RESULT TCA_POLICE_RESULT
};

View File

@ -134,6 +134,9 @@ typedef __s32 sctp_assoc_t;
#define SCTP_INTERLEAVING_SUPPORTED 125
#define SCTP_SENDMSG_CONNECT 126
#define SCTP_EVENT 127
#define SCTP_ASCONF_SUPPORTED 128
#define SCTP_AUTH_SUPPORTED 129
#define SCTP_ECN_SUPPORTED 130
/* PR-SCTP policies */
#define SCTP_PR_SCTP_NONE 0x0000

View File

@ -0,0 +1,41 @@
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
#ifndef __UAPI_TC_CT_H
#define __UAPI_TC_CT_H
#include <linux/types.h>
#include <linux/pkt_cls.h>
enum {
TCA_CT_UNSPEC,
TCA_CT_PARMS,
TCA_CT_TM,
TCA_CT_ACTION, /* u16 */
TCA_CT_ZONE, /* u16 */
TCA_CT_MARK, /* u32 */
TCA_CT_MARK_MASK, /* u32 */
TCA_CT_LABELS, /* u128 */
TCA_CT_LABELS_MASK, /* u128 */
TCA_CT_NAT_IPV4_MIN, /* be32 */
TCA_CT_NAT_IPV4_MAX, /* be32 */
TCA_CT_NAT_IPV6_MIN, /* struct in6_addr */
TCA_CT_NAT_IPV6_MAX, /* struct in6_addr */
TCA_CT_NAT_PORT_MIN, /* be16 */
TCA_CT_NAT_PORT_MAX, /* be16 */
TCA_CT_PAD,
__TCA_CT_MAX
};
#define TCA_CT_MAX (__TCA_CT_MAX - 1)
#define TCA_CT_ACT_COMMIT (1 << 0)
#define TCA_CT_ACT_FORCE (1 << 1)
#define TCA_CT_ACT_CLEAR (1 << 2)
#define TCA_CT_ACT_NAT (1 << 3)
#define TCA_CT_ACT_NAT_SRC (1 << 4)
#define TCA_CT_ACT_NAT_DST (1 << 5)
struct tc_ct {
tc_gen;
};
#endif /* __UAPI_TC_CT_H */

View File

@ -71,75 +71,76 @@ static void usage(void)
static void print_tunnel(const void *t)
{
const struct ip6_tnl_parm2 *p = t;
char s1[1024];
char s2[1024];
SPRINT_BUF(b1);
/* Do not use format_host() for local addr,
* symbolic name will not be useful.
*/
printf("%s: %s/ipv6 remote %s local %s",
p->name,
tnl_strproto(p->proto),
format_host_r(AF_INET6, 16, &p->raddr, s1, sizeof(s1)),
rt_addr_n2a_r(AF_INET6, 16, &p->laddr, s2, sizeof(s2)));
open_json_object(NULL);
print_color_string(PRINT_ANY, COLOR_IFNAME, "ifname", "%s: ", p->name);
snprintf(b1, sizeof(b1), "%s/ipv6", tnl_strproto(p->proto));
print_string(PRINT_ANY, "mode", "%s ", b1);
print_string(PRINT_FP, NULL, "%s", "remote ");
print_color_string(PRINT_ANY, COLOR_INET6, "remote", "%s ",
format_host_r(AF_INET6, 16, &p->raddr, b1, sizeof(b1)));
print_string(PRINT_FP, NULL, "%s", "local ");
print_color_string(PRINT_ANY, COLOR_INET6, "local", "%s",
rt_addr_n2a_r(AF_INET6, 16, &p->laddr, b1, sizeof(b1)));
if (p->link) {
const char *n = ll_index_to_name(p->link);
if (n)
printf(" dev %s", n);
print_string(PRINT_ANY, "link", " dev %s", n);
}
if (p->flags & IP6_TNL_F_IGN_ENCAP_LIMIT)
printf(" encaplimit none");
print_null(PRINT_ANY, "ip6_tnl_f_ign_encap_limit",
" encaplimit none", NULL);
else
printf(" encaplimit %u", p->encap_limit);
print_uint(PRINT_ANY, "encap_limit", " encaplimit %u",
p->encap_limit);
if (p->hop_limit)
printf(" hoplimit %u", p->hop_limit);
print_uint(PRINT_ANY, "hoplimit", " hoplimit %u", p->hop_limit);
else
printf(" hoplimit inherit");
print_string(PRINT_FP, "hoplimit", " hoplimit %s", "inherit");
if (p->flags & IP6_TNL_F_USE_ORIG_TCLASS)
printf(" tclass inherit");
else {
if (p->flags & IP6_TNL_F_USE_ORIG_TCLASS) {
print_null(PRINT_ANY, "ip6_tnl_f_use_orig_tclass",
" tclass inherit", NULL);
} else {
__u32 val = ntohl(p->flowinfo & IP6_FLOWINFO_TCLASS);
printf(" tclass 0x%02x", (__u8)(val >> 20));
snprintf(b1, sizeof(b1), "0x%02x", (__u8)(val >> 20));
print_string(PRINT_ANY, "tclass", " tclass %s", b1);
}
if (p->flags & IP6_TNL_F_USE_ORIG_FLOWLABEL)
printf(" flowlabel inherit");
else
printf(" flowlabel 0x%05x", ntohl(p->flowinfo & IP6_FLOWINFO_FLOWLABEL));
if (p->flags & IP6_TNL_F_USE_ORIG_FLOWLABEL) {
print_null(PRINT_ANY, "ip6_tnl_f_use_orig_flowlabel",
" flowlabel inherit", NULL);
} else {
__u32 val = ntohl(p->flowinfo & IP6_FLOWINFO_FLOWLABEL);
printf(" (flowinfo 0x%08x)", ntohl(p->flowinfo));
snprintf(b1, sizeof(b1), "0x%05x", val);
print_string(PRINT_ANY, "flowlabel", " flowlabel %s", b1);
}
snprintf(b1, sizeof(b1), "0x%08x", ntohl(p->flowinfo));
print_string(PRINT_ANY, "flowinfo", " (flowinfo %s)", b1);
if (p->flags & IP6_TNL_F_RCV_DSCP_COPY)
printf(" dscp inherit");
print_null(PRINT_ANY, "ip6_tnl_f_rcv_dscp_copy",
" dscp inherit", NULL);
if (p->flags & IP6_TNL_F_ALLOW_LOCAL_REMOTE)
printf(" allow-localremote");
print_null(PRINT_ANY, "ip6_tnl_f_allow_local_remote",
" allow-localremote", NULL);
if ((p->i_flags & GRE_KEY) && (p->o_flags & GRE_KEY) &&
p->o_key == p->i_key)
printf(" key %u", ntohl(p->i_key));
else {
if (p->i_flags & GRE_KEY)
printf(" ikey %u", ntohl(p->i_key));
if (p->o_flags & GRE_KEY)
printf(" okey %u", ntohl(p->o_key));
}
tnl_print_gre_flags(p->proto, p->i_flags, p->o_flags,
p->i_key, p->o_key);
if (p->proto == IPPROTO_GRE) {
if (p->i_flags & GRE_SEQ)
printf("%s Drop packets out of sequence.", _SL_);
if (p->i_flags & GRE_CSUM)
printf("%s Checksum in received packet is required.", _SL_);
if (p->o_flags & GRE_SEQ)
printf("%s Sequence packets on output.", _SL_);
if (p->o_flags & GRE_CSUM)
printf("%s Checksum output packets.", _SL_);
}
close_json_object();
}
static int parse_args(int argc, char **argv, int cmd, struct ip6_tnl_parm2 *p)
@ -373,7 +374,6 @@ static int do_show(int argc, char **argv)
return -1;
print_tunnel(&p);
fputc('\n', stdout);
return 0;
}

View File

@ -19,6 +19,7 @@ static struct {
unsigned int groups;
unsigned int ifindex;
unsigned int master;
unsigned int proto;
} filter;
enum {
@ -34,7 +35,7 @@ static void usage(void) __attribute__((noreturn));
static void usage(void)
{
fprintf(stderr,
"Usage: ip nexthop { list | flush } SELECTOR\n"
"Usage: ip nexthop { list | flush } [ protocol ID ] SELECTOR\n"
" ip nexthop { add | replace } id ID NH [ protocol ID ]\n"
" ip nexthop { get| del } id ID\n"
"SELECTOR := [ id ID ] [ dev DEV ] [ vrf NAME ] [ master DEV ]\n"
@ -109,6 +110,9 @@ static int flush_nexthop(struct nlmsghdr *nlh, void *arg)
return -1;
}
if (filter.proto && nhm->nh_protocol != filter.proto)
return 0;
parse_rtattr(tb, NHA_MAX, RTM_NHA(nhm), len);
if (tb[NHA_ID])
id = rta_getattr_u32(tb[NHA_ID]);
@ -213,6 +217,9 @@ int print_nexthop(struct nlmsghdr *n, void *arg)
return -1;
}
if (filter.proto && filter.proto != nhm->nh_protocol)
return 0;
parse_rtattr(tb, NHA_MAX, RTM_NHA(nhm), len);
open_json_object(NULL);
@ -473,6 +480,13 @@ static int ipnh_list_flush(int argc, char **argv, int action)
if (get_unsigned(&id, *argv, 0))
invarg("invalid id value", *argv);
return ipnh_get_id(id);
} else if (!matches(*argv, "protocol")) {
__u32 proto;
NEXT_ARG();
if (get_unsigned(&proto, *argv, 0))
invarg("invalid protocol value", *argv);
filter.proto = proto;
} else if (matches(*argv, "help") == 0) {
usage();
} else {

View File

@ -289,17 +289,25 @@ static void print_tunnel(const void *t)
{
const struct ip_tunnel_parm *p = t;
struct ip_tunnel_6rd ip6rd = {};
char s1[1024];
char s2[1024];
SPRINT_BUF(b1);
/* Do not use format_host() for local addr,
* symbolic name will not be useful.
*/
printf("%s: %s/ip remote %s local %s",
p->name,
tnl_strproto(p->iph.protocol),
p->iph.daddr ? format_host_r(AF_INET, 4, &p->iph.daddr, s1, sizeof(s1)) : "any",
p->iph.saddr ? rt_addr_n2a_r(AF_INET, 4, &p->iph.saddr, s2, sizeof(s2)) : "any");
open_json_object(NULL);
print_color_string(PRINT_ANY, COLOR_IFNAME, "ifname", "%s: ", p->name);
snprintf(b1, sizeof(b1), "%s/ip", tnl_strproto(p->iph.protocol));
print_string(PRINT_ANY, "mode", "%s ", b1);
print_null(PRINT_FP, NULL, "remote ", NULL);
print_color_string(PRINT_ANY, COLOR_INET, "remote", "%s ",
p->iph.daddr || is_json_context()
? format_host_r(AF_INET, 4, &p->iph.daddr, b1, sizeof(b1))
: "any");
print_null(PRINT_FP, NULL, "local ", NULL);
print_color_string(PRINT_ANY, COLOR_INET, "local", "%s",
p->iph.saddr || is_json_context()
? rt_addr_n2a_r(AF_INET, 4, &p->iph.saddr, b1, sizeof(b1))
: "any");
if (p->iph.protocol == IPPROTO_IPV6 && (p->i_flags & SIT_ISATAP)) {
struct ip_tunnel_prl prl[16] = {};
@ -308,69 +316,70 @@ static void print_tunnel(const void *t)
prl[0].datalen = sizeof(prl) - sizeof(prl[0]);
prl[0].addr = htonl(INADDR_ANY);
if (!tnl_prl_ioctl(SIOCGETPRL, p->name, prl))
if (!tnl_prl_ioctl(SIOCGETPRL, p->name, prl)) {
for (i = 1; i < ARRAY_SIZE(prl); i++) {
if (prl[i].addr != htonl(INADDR_ANY)) {
printf(" %s %s ",
(prl[i].flags & PRL_DEFAULT) ? "pdr" : "pr",
format_host(AF_INET, 4, &prl[i].addr));
}
if (prl[i].addr == htonl(INADDR_ANY))
continue;
if (prl[i].flags & PRL_DEFAULT)
print_string(PRINT_ANY, "pdr",
" pdr %s",
format_host(AF_INET, 4, &prl[i].addr));
else
print_string(PRINT_ANY, "pr", " pr %s",
format_host(AF_INET, 4, &prl[i].addr));
}
}
}
if (p->link) {
const char *n = ll_index_to_name(p->link);
if (n)
printf(" dev %s", n);
print_string(PRINT_ANY, "dev", " dev %s", n);
}
if (p->iph.ttl)
printf(" ttl %u", p->iph.ttl);
print_uint(PRINT_ANY, "ttl", " ttl %u", p->iph.ttl);
else
printf(" ttl inherit");
print_string(PRINT_FP, "ttl", " ttl %s", "inherit");
if (p->iph.tos) {
SPRINT_BUF(b1);
printf(" tos");
if (p->iph.tos & 1)
printf(" inherit");
if (p->iph.tos & ~1)
printf("%c%s ", p->iph.tos & 1 ? '/' : ' ',
rtnl_dsfield_n2a(p->iph.tos & ~1, b1, sizeof(b1)));
}
SPRINT_BUF(b2);
if (!(p->iph.frag_off & htons(IP_DF)))
printf(" nopmtudisc");
if (p->iph.protocol == IPPROTO_IPV6 && !tnl_ioctl_get_6rd(p->name, &ip6rd) && ip6rd.prefixlen) {
printf(" 6rd-prefix %s/%u",
inet_ntop(AF_INET6, &ip6rd.prefix, s1, sizeof(s1)),
ip6rd.prefixlen);
if (ip6rd.relay_prefix) {
printf(" 6rd-relay_prefix %s/%u",
format_host(AF_INET, 4, &ip6rd.relay_prefix),
ip6rd.relay_prefixlen);
if (p->iph.tos != 1) {
if (!is_json_context() && p->iph.tos & 1)
snprintf(b2, sizeof(b2), "%s%s",
p->iph.tos & 1 ? "inherit/" : "",
rtnl_dsfield_n2a(p->iph.tos & ~1, b1, sizeof(b1)));
else
snprintf(b2, sizeof(b2), "%s",
rtnl_dsfield_n2a(p->iph.tos, b1, sizeof(b1)));
print_string(PRINT_ANY, "tos", " tos %s", b2);
} else {
print_string(PRINT_FP, NULL, " tos %s", "inherit");
}
}
if ((p->i_flags & GRE_KEY) && (p->o_flags & GRE_KEY) && p->o_key == p->i_key)
printf(" key %u", ntohl(p->i_key));
else if ((p->i_flags | p->o_flags) & GRE_KEY) {
if (p->i_flags & GRE_KEY)
printf(" ikey %u", ntohl(p->i_key));
if (p->o_flags & GRE_KEY)
printf(" okey %u", ntohl(p->o_key));
if (!(p->iph.frag_off & htons(IP_DF)))
print_null(PRINT_ANY, "nopmtudisc", " nopmtudisc", NULL);
if (p->iph.protocol == IPPROTO_IPV6 && !tnl_ioctl_get_6rd(p->name, &ip6rd) && ip6rd.prefixlen) {
print_string(PRINT_ANY, "6rd-prefix", " 6rd-prefix %s",
inet_ntop(AF_INET6, &ip6rd.prefix, b1, sizeof(b1)));
print_uint(PRINT_ANY, "6rd-prefixlen", "/%u", ip6rd.prefixlen);
if (ip6rd.relay_prefix) {
print_string(PRINT_ANY, "6rd-relay_prefix",
" 6rd-relay_prefix %s",
format_host(AF_INET, 4, &ip6rd.relay_prefix));
print_uint(PRINT_ANY, "6rd-relay_prefixlen", "/%u",
ip6rd.relay_prefixlen);
}
}
if (p->i_flags & GRE_SEQ)
printf("%s Drop packets out of sequence.", _SL_);
if (p->i_flags & GRE_CSUM)
printf("%s Checksum in received packet is required.", _SL_);
if (p->o_flags & GRE_SEQ)
printf("%s Sequence packets on output.", _SL_);
if (p->o_flags & GRE_CSUM)
printf("%s Checksum output packets.", _SL_);
tnl_print_gre_flags(p->iph.protocol, p->i_flags, p->o_flags,
p->i_key, p->o_key);
close_json_object();
}

View File

@ -308,6 +308,51 @@ void tnl_print_endpoint(const char *name, const struct rtattr *rta, int family)
}
}
void tnl_print_gre_flags(__u8 proto,
__be16 i_flags, __be16 o_flags,
__be32 i_key, __be32 o_key)
{
if ((i_flags & GRE_KEY) && (o_flags & GRE_KEY) &&
o_key == i_key) {
print_uint(PRINT_ANY, "key", " key %u", ntohl(i_key));
} else {
if (i_flags & GRE_KEY)
print_uint(PRINT_ANY, "ikey", " ikey %u", ntohl(i_key));
if (o_flags & GRE_KEY)
print_uint(PRINT_ANY, "okey", " okey %u", ntohl(o_key));
}
if (proto != IPPROTO_GRE)
return;
open_json_array(PRINT_JSON, "flags");
if (i_flags & GRE_SEQ) {
if (is_json_context())
print_string(PRINT_JSON, NULL, "%s", "rx_drop_ooseq");
else
printf("%s Drop packets out of sequence.", _SL_);
}
if (i_flags & GRE_CSUM) {
if (is_json_context())
print_string(PRINT_JSON, NULL, "%s", "rx_csum");
else
printf("%s Checksum in received packet is required.", _SL_);
}
if (o_flags & GRE_SEQ) {
if (is_json_context())
print_string(PRINT_JSON, NULL, "%s", "tx_seq");
else
printf("%s Sequence packets on output.", _SL_);
}
if (o_flags & GRE_CSUM) {
if (is_json_context())
print_string(PRINT_JSON, NULL, "%s", "tx_csum");
else
printf("%s Checksum output packets.", _SL_);
}
close_json_array(PRINT_JSON, NULL);
}
static void tnl_print_stats(const struct rtnl_link_stats64 *s)
{
printf("%s", _SL_);
@ -391,6 +436,7 @@ static int print_nlmsg_tunnel(struct nlmsghdr *n, void *arg)
int do_tunnels_list(struct tnl_print_nlmsg_info *info)
{
new_json_obj(json);
if (rtnl_linkdump_req(&rth, preferred_family) < 0) {
perror("Cannot send dump request\n");
return -1;
@ -400,6 +446,7 @@ int do_tunnels_list(struct tnl_print_nlmsg_info *info)
fprintf(stderr, "Dump terminated\n");
return -1;
}
delete_json_obj();
return 0;
}

View File

@ -55,5 +55,8 @@ void tnl_print_encap(struct rtattr *tb[],
int encap_sport, int encap_dport);
void tnl_print_endpoint(const char *name,
const struct rtattr *rta, int family);
void tnl_print_gre_flags(__u8 proto,
__be16 i_flags, __be16 o_flags,
__be32 i_key, __be32 o_key);
#endif

155
lib/bpf.c
View File

@ -406,13 +406,21 @@ static int bpf_derive_elf_map_from_fdinfo(int fd, struct bpf_elf_map *map,
struct bpf_map_ext *ext)
{
unsigned int val, owner_type = 0, owner_jited = 0;
char file[PATH_MAX], buff[4096];
char *file = NULL;
char buff[4096];
FILE *fp;
int ret;
snprintf(file, sizeof(file), "/proc/%d/fdinfo/%d", getpid(), fd);
ret = asprintf(&file, "/proc/%d/fdinfo/%d", getpid(), fd);
if (ret < 0) {
fprintf(stderr, "asprintf failed: %s\n", strerror(errno));
free(file);
return ret;
}
memset(map, 0, sizeof(*map));
fp = fopen(file, "r");
free(file);
if (!fp) {
fprintf(stderr, "No procfs support?!\n");
return -EIO;
@ -600,8 +608,9 @@ int bpf_trace_pipe(void)
0,
};
int fd_in, fd_out = STDERR_FILENO;
char tpipe[PATH_MAX];
char *tpipe = NULL;
const char *mnt;
int ret;
mnt = bpf_find_mntpt("tracefs", TRACEFS_MAGIC, tracefs_mnt,
sizeof(tracefs_mnt), tracefs_known_mnts);
@ -610,9 +619,15 @@ int bpf_trace_pipe(void)
return -1;
}
snprintf(tpipe, sizeof(tpipe), "%s/trace_pipe", mnt);
ret = asprintf(&tpipe, "%s/trace_pipe", mnt);
if (ret < 0) {
fprintf(stderr, "asprintf failed: %s\n", strerror(errno));
free(tpipe);
return ret;
}
fd_in = open(tpipe, O_RDONLY);
free(tpipe);
if (fd_in < 0)
return -1;
@ -633,37 +648,50 @@ int bpf_trace_pipe(void)
static int bpf_gen_global(const char *bpf_sub_dir)
{
char bpf_glo_dir[PATH_MAX];
char *bpf_glo_dir = NULL;
int ret;
snprintf(bpf_glo_dir, sizeof(bpf_glo_dir), "%s/%s/",
bpf_sub_dir, BPF_DIR_GLOBALS);
ret = asprintf(&bpf_glo_dir, "%s/%s/", bpf_sub_dir, BPF_DIR_GLOBALS);
if (ret < 0) {
fprintf(stderr, "asprintf failed: %s\n", strerror(errno));
goto out;
}
ret = mkdir(bpf_glo_dir, S_IRWXU);
if (ret && errno != EEXIST) {
fprintf(stderr, "mkdir %s failed: %s\n", bpf_glo_dir,
strerror(errno));
return ret;
goto out;
}
return 0;
ret = 0;
out:
free(bpf_glo_dir);
return ret;
}
static int bpf_gen_master(const char *base, const char *name)
{
char bpf_sub_dir[PATH_MAX + NAME_MAX + 1];
char *bpf_sub_dir = NULL;
int ret;
snprintf(bpf_sub_dir, sizeof(bpf_sub_dir), "%s%s/", base, name);
ret = asprintf(&bpf_sub_dir, "%s%s/", base, name);
if (ret < 0) {
fprintf(stderr, "asprintf failed: %s\n", strerror(errno));
goto out;
}
ret = mkdir(bpf_sub_dir, S_IRWXU);
if (ret && errno != EEXIST) {
fprintf(stderr, "mkdir %s failed: %s\n", bpf_sub_dir,
strerror(errno));
return ret;
goto out;
}
return bpf_gen_global(bpf_sub_dir);
ret = bpf_gen_global(bpf_sub_dir);
out:
free(bpf_sub_dir);
return ret;
}
static int bpf_slave_via_bind_mnt(const char *full_name,
@ -692,13 +720,22 @@ static int bpf_slave_via_bind_mnt(const char *full_name,
static int bpf_gen_slave(const char *base, const char *name,
const char *link)
{
char bpf_lnk_dir[PATH_MAX + NAME_MAX + 1];
char bpf_sub_dir[PATH_MAX + NAME_MAX];
char *bpf_lnk_dir = NULL;
char *bpf_sub_dir = NULL;
struct stat sb = {};
int ret;
snprintf(bpf_lnk_dir, sizeof(bpf_lnk_dir), "%s%s/", base, link);
snprintf(bpf_sub_dir, sizeof(bpf_sub_dir), "%s%s", base, name);
ret = asprintf(&bpf_lnk_dir, "%s%s/", base, link);
if (ret < 0) {
fprintf(stderr, "asprintf failed: %s\n", strerror(errno));
goto out;
}
ret = asprintf(&bpf_sub_dir, "%s%s", base, name);
if (ret < 0) {
fprintf(stderr, "asprintf failed: %s\n", strerror(errno));
goto out;
}
ret = symlink(bpf_lnk_dir, bpf_sub_dir);
if (ret) {
@ -706,25 +743,30 @@ static int bpf_gen_slave(const char *base, const char *name,
if (errno != EPERM) {
fprintf(stderr, "symlink %s failed: %s\n",
bpf_sub_dir, strerror(errno));
return ret;
goto out;
}
return bpf_slave_via_bind_mnt(bpf_sub_dir,
bpf_lnk_dir);
ret = bpf_slave_via_bind_mnt(bpf_sub_dir, bpf_lnk_dir);
goto out;
}
ret = lstat(bpf_sub_dir, &sb);
if (ret) {
fprintf(stderr, "lstat %s failed: %s\n",
bpf_sub_dir, strerror(errno));
return ret;
goto out;
}
if ((sb.st_mode & S_IFMT) != S_IFLNK)
return bpf_gen_global(bpf_sub_dir);
if ((sb.st_mode & S_IFMT) != S_IFLNK) {
ret = bpf_gen_global(bpf_sub_dir);
goto out;
}
}
return 0;
out:
free(bpf_lnk_dir);
free(bpf_sub_dir);
return ret;
}
static int bpf_gen_hierarchy(const char *base)
@ -742,7 +784,7 @@ static int bpf_gen_hierarchy(const char *base)
static const char *bpf_get_work_dir(enum bpf_prog_type type)
{
static char bpf_tmp[PATH_MAX] = BPF_DIR_MNT;
static char bpf_wrk_dir[PATH_MAX];
static char *bpf_wrk_dir;
static const char *mnt;
static bool bpf_mnt_cached;
const char *mnt_env = getenv(BPF_ENV_MNT);
@ -781,7 +823,12 @@ static const char *bpf_get_work_dir(enum bpf_prog_type type)
}
}
snprintf(bpf_wrk_dir, sizeof(bpf_wrk_dir), "%s/", mnt);
ret = asprintf(&bpf_wrk_dir, "%s/", mnt);
if (ret < 0) {
fprintf(stderr, "asprintf failed: %s\n", strerror(errno));
free(bpf_wrk_dir);
goto out;
}
ret = bpf_gen_hierarchy(bpf_wrk_dir);
if (ret) {
@ -1438,31 +1485,48 @@ static int bpf_probe_pinned(const char *name, const struct bpf_elf_ctx *ctx,
static int bpf_make_obj_path(const struct bpf_elf_ctx *ctx)
{
char tmp[PATH_MAX];
char *tmp = NULL;
int ret;
snprintf(tmp, sizeof(tmp), "%s/%s", bpf_get_work_dir(ctx->type),
ctx->obj_uid);
ret = asprintf(&tmp, "%s/%s", bpf_get_work_dir(ctx->type), ctx->obj_uid);
if (ret < 0) {
fprintf(stderr, "asprintf failed: %s\n", strerror(errno));
goto out;
}
ret = mkdir(tmp, S_IRWXU);
if (ret && errno != EEXIST) {
fprintf(stderr, "mkdir %s failed: %s\n", tmp, strerror(errno));
return ret;
goto out;
}
return 0;
ret = 0;
out:
free(tmp);
return ret;
}
static int bpf_make_custom_path(const struct bpf_elf_ctx *ctx,
const char *todo)
{
char tmp[PATH_MAX], rem[PATH_MAX], *sub;
char *tmp = NULL;
char *rem = NULL;
char *sub;
int ret;
snprintf(tmp, sizeof(tmp), "%s/../", bpf_get_work_dir(ctx->type));
snprintf(rem, sizeof(rem), "%s/", todo);
sub = strtok(rem, "/");
ret = asprintf(&tmp, "%s/../", bpf_get_work_dir(ctx->type));
if (ret < 0) {
fprintf(stderr, "asprintf failed: %s\n", strerror(errno));
goto out;
}
ret = asprintf(&rem, "%s/", todo);
if (ret < 0) {
fprintf(stderr, "asprintf failed: %s\n", strerror(errno));
goto out;
}
sub = strtok(rem, "/");
while (sub) {
if (strlen(tmp) + strlen(sub) + 2 > PATH_MAX)
return -EINVAL;
@ -1474,13 +1538,17 @@ static int bpf_make_custom_path(const struct bpf_elf_ctx *ctx,
if (ret && errno != EEXIST) {
fprintf(stderr, "mkdir %s failed: %s\n", tmp,
strerror(errno));
return ret;
goto out;
}
sub = strtok(NULL, "/");
}
return 0;
ret = 0;
out:
free(rem);
free(tmp);
return ret;
}
static int bpf_place_pinned(int fd, const char *name,
@ -2587,14 +2655,23 @@ struct bpf_jited_aux {
static int bpf_derive_prog_from_fdinfo(int fd, struct bpf_prog_data *prog)
{
char file[PATH_MAX], buff[4096];
char *file = NULL;
char buff[4096];
unsigned int val;
FILE *fp;
int ret;
ret = asprintf(&file, "/proc/%d/fdinfo/%d", getpid(), fd);
if (ret < 0) {
fprintf(stderr, "asprintf failed: %s\n", strerror(errno));
free(file);
return ret;
}
snprintf(file, sizeof(file), "/proc/%d/fdinfo/%d", getpid(), fd);
memset(prog, 0, sizeof(*prog));
fp = fopen(file, "r");
free(file);
if (!fp) {
fprintf(stderr, "No procfs support?!\n");
return -EIO;

View File

@ -244,6 +244,17 @@ Sets the parameter internal_error_reset of specified devlink device to true.
devlink dev reload pci/0000:01:00.0
.RS 4
Performs hot reload of specified devlink device.
.RE
.PP
devlink dev flash pci/0000:01:00.0 file firmware.bin
.RS 4
Flashes the specified devlink device with provided firmware file name. If the driver supports it, user gets updates about the flash status. For example:
.br
Preparing to flash
.br
Flashing 100%
.br
Flashing done
.SH SEE ALSO
.BR devlink (8),

View File

@ -21,7 +21,7 @@ command is the first in the command line and then the object list.
.I OBJECT-LIST
is the list of object types that we want to monitor.
It may contain
.BR dev ", " port ".
.BR dev ", " port ", " trap ", " trap-group .
.B devlink
opens Devlink Netlink socket, listens on it and dumps state changes.
@ -31,6 +31,7 @@ opens Devlink Netlink socket, listens on it and dumps state changes.
.BR devlink-dev (8),
.BR devlink-sb (8),
.BR devlink-port (8),
.BR devlink-trap (8),
.br
.SH AUTHOR

138
man/man8/devlink-trap.8 Normal file
View File

@ -0,0 +1,138 @@
.TH DEVLINK\-TRAP 8 "2 August 2019" "iproute2" "Linux"
.SH NAME
devlink-trap \- devlink trap configuration
.SH SYNOPSIS
.sp
.ad l
.in +8
.ti -8
.B devlink
.RI "[ " OPTIONS " ]"
.B trap
.RI "{ " COMMAND " |"
.BR help " }"
.sp
.ti -8
.IR OPTIONS " := { "
\fB\-v\fR[\fIerbose\fR] |
\fB\-s\fR[\fItatistics\fR] }
.ti -8
.B "devlink trap show"
.RI "[ " DEV
.B trap
.IR TRAP " ]"
.ti -8
.BI "devlink trap set " DEV " trap " TRAP
.RB "[ " action " { " trap " | " drop " } ]"
.ti -8
.B "devlink trap group show"
.RI "[ " DEV
.B group
.IR GROUP " ]"
.ti -8
.BI "devlink trap group set " DEV " group " GROUP
.RB "[ " action " { " trap " | " drop " } ]"
.ti -8
.B devlink trap help
.SH "DESCRIPTION"
.SS devlink trap show - display available packet traps and their attributes
.PP
.I "DEV"
- specifies the devlink device from which to show packet traps.
If this argument is omitted all packet traps of all devices are listed.
.PP
.BI "trap " TRAP
- specifies the packet trap.
Only applicable if a devlink device is also specified.
.SS devlink trap set - set attributes of a packet trap
.PP
.I "DEV"
- specifies the devlink device the packet trap belongs to.
.PP
.BI "trap " TRAP
- specifies the packet trap.
.TP
.BR action " { " trap " | " drop " } "
packet trap action.
.I trap
- the sole copy of the packet is sent to the CPU.
.I drop
- the packet is dropped by the underlying device and a copy is not sent to the CPU.
.SS devlink trap group show - display available packet trap groups and their attributes
.PP
.I "DEV"
- specifies the devlink device from which to show packet trap groups.
If this argument is omitted all packet trap groups of all devices are listed.
.PP
.BI "group " GROUP
- specifies the packet trap group.
Only applicable if a devlink device is also specified.
.SS devlink trap group set - set attributes of a packet trap group
.PP
.I "DEV"
- specifies the devlink device the packet trap group belongs to.
.PP
.BI "group " GROUP
- specifies the packet trap group.
.TP
.BR action " { " trap " | " drop " } "
packet trap action. The action is set for all the packet traps member in the
trap group. The actions of non-drop traps cannot be changed and are thus
skipped.
.SH "EXAMPLES"
.PP
devlink trap show
.RS 4
List available packet traps.
.RE
.PP
devlink trap group show
.RS 4
List available packet trap groups.
.RE
.PP
devlink -vs trap show pci/0000:01:00.0 trap source_mac_is_multicast
.RS 4
Show attributes and statistics of a specific packet trap.
.RE
.PP
devlink -s trap group show pci/0000:01:00.0 group l2_drops
.RS 4
Show attributes and statistics of a specific packet trap group.
.RE
.PP
devlink trap set pci/0000:01:00.0 trap source_mac_is_multicast action trap
.RS 4
Set the action of a specific packet trap to 'trap'.
.SH SEE ALSO
.BR devlink (8),
.BR devlink-dev (8),
.BR devlink-monitor (8),
.br
.SH AUTHOR
Ido Schimmel <idosch@mellanox.com>

View File

@ -7,7 +7,7 @@ devlink \- Devlink tool
.in +8
.ti -8
.B devlink
.RI "[ " OPTIONS " ] { " dev | port | monitor | sb | resource | region | health " } { " COMMAND " | "
.RI "[ " OPTIONS " ] { " dev | port | monitor | sb | resource | region | health | trap " } { " COMMAND " | "
.BR help " }"
.sp
@ -51,6 +51,10 @@ When combined with -j generate a pretty JSON output.
.BR "\-v" , " --verbose"
Turn on verbose output.
.TP
.BR "\-s" , " --statistics"
Output statistics.
.SS
.I OBJECT
@ -82,6 +86,10 @@ Turn on verbose output.
.B health
- devlink reporting and recovery
.TP
.B trap
- devlink trap configuration
.SS
.I COMMAND
@ -114,6 +122,7 @@ Exit status is 0 if command was successful or a positive integer upon failure.
.BR devlink-resource (8),
.BR devlink-region (8),
.BR devlink-health (8),
.BR devlink-trap (8),
.br
.SH REPORTING BUGS

View File

@ -106,6 +106,16 @@ referred to as "Launch Time" or "Time-Based Scheduling" by the
documentation of network interface controllers.
The default is for this option to be disabled.
.TP
skip_sock_check
.br
.BR etf(8)
currently drops any packet which does not have a socket associated with it or
if the socket does not have SO_TXTIME socket option set. But, this will not
work if the launchtime is set by another entity inside the kernel (e.g. some
other Qdisc). Setting the skip_sock_check will skip checking for a socket
associated with the packet.
.SH EXAMPLES
ETF is used to enforce a Quality of Service. It controls when each

View File

@ -289,6 +289,41 @@ bits is assumed.
.TQ
.BI enc_ttl " NUMBER"
.TQ
.BR
.TP
.BI ct_state " CT_STATE"
.TQ
.BI ct_zone " CT_MASKED_ZONE"
.TQ
.BI ct_mark " CT_MASKED_MARK"
.TQ
.BI ct_label " CT_MASKED_LABEL"
Matches on connection tracking info
.RS
.TP
.I CT_STATE
Match the connection state, and can ne combination of [{+|-}flag] flags, where flag can be one of
.RS
.TP
trk - Tracked connection.
.TP
new - New connection.
.TP
est - Established connection.
.TP
Example: +trk+est
.RE
.TP
.I CT_MASKED_ZONE
Match the connection zone, and can be masked.
.TP
.I CT_MASKED_MARK
32bit match on the connection mark, and can be masked.
.TP
.I CT_MASKED_LABEL
128bit match on the connection label, and can be masked.
.RE
.TP
.BI geneve_opts " OPTIONS"
Match on IP tunnel metadata. Key id
.I NUMBER

View File

@ -112,6 +112,26 @@ means that traffic class 0 is "active" for that schedule entry.
long that state defined by <command> and <gate mask> should be held
before moving to the next entry.
.TP
flags
.br
Specifies different modes for taprio. Currently, only txtime-assist is
supported which can be enabled by setting it to 0x1. In this mode, taprio will
set the transmit timestamp depending on the interval in which the packet needs
to be transmitted. It will then utililize the
.BR etf(8)
qdisc to sort and transmit the packets at the right time. The second example
can be used as a reference to configure this mode.
.TP
txtime-delay
.br
This parameter is specific to the txtime offload mode. It specifies the maximum
time a packet might take to reach the network card from the taprio qdisc. The
value should always be greater than the delta specified in the
.BR etf(8)
qdisc.
.SH EXAMPLES
The following example shows how an traffic schedule with three traffic
@ -137,6 +157,26 @@ reference CLOCK_TAI. The schedule is composed of three entries each of
clockid CLOCK_TAI
.EE
Following is an example to enable the txtime offload mode in taprio. See
.BR etf(8)
for more information about configuring the ETF qdisc.
.EX
# tc qdisc replace dev eth0 parent root handle 100 taprio \\
num_tc 3 \\
map 2 2 1 0 2 2 2 2 2 2 2 2 2 2 2 2 \\
queues 1@0 1@0 1@0 \\
base-time 1528743495910289987 \\
sched-entry S 01 300000 \\
sched-entry S 02 300000 \\
sched-entry S 04 400000 \\
flags 0x1 \\
txtime-delay 200000 \\
clockid CLOCK_TAI
# tc qdisc replace dev $IFACE parent 100:1 etf skip_skb_check \\
offload delta 200000 clockid CLOCK_TAI
.EE
.SH AUTHORS
Vinicius Costa Gomes <vinicius.gomes@intel.com>

View File

@ -148,9 +148,11 @@ const char *qp_types_to_str(uint8_t idx)
"UC", "UD", "RAW_IPV6",
"RAW_ETHERTYPE",
"UNKNOWN", "RAW_PACKET",
"XRC_INI", "XRC_TGT" };
"XRC_INI", "XRC_TGT",
[0xFF] = "DRIVER",
};
if (idx < ARRAY_SIZE(qp_types_str))
if (idx < ARRAY_SIZE(qp_types_str) && qp_types_str[idx])
return qp_types_str[idx];
return "UNKNOWN";
}

View File

@ -53,6 +53,7 @@ TCMODULES += m_ctinfo.o
TCMODULES += m_bpf.o
TCMODULES += m_tunnel_key.o
TCMODULES += m_sample.o
TCMODULES += m_ct.o
TCMODULES += p_ip.o
TCMODULES += p_ip6.o
TCMODULES += p_icmp.o

View File

@ -82,9 +82,14 @@ static void explain(void)
" enc_ttl MASKED-IP_TTL |\n"
" geneve_opts MASKED-OPTIONS |\n"
" ip_flags IP-FLAGS | \n"
" enc_dst_port [ port_number ] }\n"
" enc_dst_port [ port_number ] |\n"
" ct_state MASKED_CT_STATE |\n"
" ct_label MASKED_CT_LABEL |\n"
" ct_mark MASKED_CT_MARK |\n"
" ct_zone MASKED_CT_ZONE }\n"
" FILTERID := X:Y:Z\n"
" MASKED_LLADDR := { LLADDR | LLADDR/MASK | LLADDR/BITS }\n"
" MASKED_CT_STATE := combination of {+|-} and flags trk,est,new\n"
" ACTION-SPEC := ... look at individual actions\n"
"\n"
"NOTE: CLASSID, IP-PROTO are parsed as hexadecimal input.\n"
@ -214,6 +219,159 @@ static int flower_parse_matching_flags(char *str,
return 0;
}
static int flower_parse_u16(char *str, int value_type, int mask_type,
struct nlmsghdr *n)
{
__u16 value, mask;
char *slash;
slash = strchr(str, '/');
if (slash)
*slash = '\0';
if (get_u16(&value, str, 0))
return -1;
if (slash) {
if (get_u16(&mask, slash + 1, 0))
return -1;
} else {
mask = UINT16_MAX;
}
addattr16(n, MAX_MSG, value_type, value);
addattr16(n, MAX_MSG, mask_type, mask);
return 0;
}
static int flower_parse_u32(char *str, int value_type, int mask_type,
struct nlmsghdr *n)
{
__u32 value, mask;
char *slash;
slash = strchr(str, '/');
if (slash)
*slash = '\0';
if (get_u32(&value, str, 0))
return -1;
if (slash) {
if (get_u32(&mask, slash + 1, 0))
return -1;
} else {
mask = UINT32_MAX;
}
addattr32(n, MAX_MSG, value_type, value);
addattr32(n, MAX_MSG, mask_type, mask);
return 0;
}
static int flower_parse_ct_mark(char *str, struct nlmsghdr *n)
{
return flower_parse_u32(str,
TCA_FLOWER_KEY_CT_MARK,
TCA_FLOWER_KEY_CT_MARK_MASK,
n);
}
static int flower_parse_ct_zone(char *str, struct nlmsghdr *n)
{
return flower_parse_u16(str,
TCA_FLOWER_KEY_CT_ZONE,
TCA_FLOWER_KEY_CT_ZONE_MASK,
n);
}
static int flower_parse_ct_labels(char *str, struct nlmsghdr *n)
{
#define LABELS_SIZE 16
uint8_t labels[LABELS_SIZE], lmask[LABELS_SIZE];
char *slash, *mask = NULL;
size_t slen, slen_mask = 0;
slash = index(str, '/');
if (slash) {
*slash = 0;
mask = slash + 1;
slen_mask = strlen(mask);
}
slen = strlen(str);
if (slen > LABELS_SIZE * 2 || slen_mask > LABELS_SIZE * 2) {
char errmsg[128];
snprintf(errmsg, sizeof(errmsg),
"%zd Max allowed size %d",
slen, LABELS_SIZE*2);
invarg(errmsg, str);
}
if (hex2mem(str, labels, slen / 2) < 0)
invarg("labels must be a hex string\n", str);
addattr_l(n, MAX_MSG, TCA_FLOWER_KEY_CT_LABELS, labels, slen / 2);
if (mask) {
if (hex2mem(mask, lmask, slen_mask / 2) < 0)
invarg("labels mask must be a hex string\n", mask);
} else {
memset(lmask, 0xff, sizeof(lmask));
slen_mask = sizeof(lmask) * 2;
}
addattr_l(n, MAX_MSG, TCA_FLOWER_KEY_CT_LABELS_MASK, lmask,
slen_mask / 2);
return 0;
}
static struct flower_ct_states {
char *str;
int flag;
} flower_ct_states[] = {
{ "trk", TCA_FLOWER_KEY_CT_FLAGS_TRACKED },
{ "new", TCA_FLOWER_KEY_CT_FLAGS_NEW },
{ "est", TCA_FLOWER_KEY_CT_FLAGS_ESTABLISHED },
};
static int flower_parse_ct_state(char *str, struct nlmsghdr *n)
{
int flags = 0, mask = 0, len, i;
bool p;
while (*str != '\0') {
if (*str == '+')
p = true;
else if (*str == '-')
p = false;
else
return -1;
for (i = 0; i < ARRAY_SIZE(flower_ct_states); i++) {
len = strlen(flower_ct_states[i].str);
if (strncmp(str + 1, flower_ct_states[i].str, len))
continue;
if (p)
flags |= flower_ct_states[i].flag;
mask |= flower_ct_states[i].flag;
break;
}
if (i == ARRAY_SIZE(flower_ct_states))
return -1;
str += len + 1;
}
addattr16(n, MAX_MSG, TCA_FLOWER_KEY_CT_STATE, flags);
addattr16(n, MAX_MSG, TCA_FLOWER_KEY_CT_STATE_MASK, mask);
return 0;
}
static int flower_parse_ip_proto(char *str, __be16 eth_type, int type,
__u8 *p_ip_proto, struct nlmsghdr *n)
{
@ -898,6 +1056,34 @@ static int flower_parse_opt(struct filter_util *qu, char *handle,
flags |= TCA_CLS_FLAGS_SKIP_HW;
} else if (matches(*argv, "skip_sw") == 0) {
flags |= TCA_CLS_FLAGS_SKIP_SW;
} else if (matches(*argv, "ct_state") == 0) {
NEXT_ARG();
ret = flower_parse_ct_state(*argv, n);
if (ret < 0) {
fprintf(stderr, "Illegal \"ct_state\"\n");
return -1;
}
} else if (matches(*argv, "ct_zone") == 0) {
NEXT_ARG();
ret = flower_parse_ct_zone(*argv, n);
if (ret < 0) {
fprintf(stderr, "Illegal \"ct_zone\"\n");
return -1;
}
} else if (matches(*argv, "ct_mark") == 0) {
NEXT_ARG();
ret = flower_parse_ct_mark(*argv, n);
if (ret < 0) {
fprintf(stderr, "Illegal \"ct_mark\"\n");
return -1;
}
} else if (matches(*argv, "ct_label") == 0) {
NEXT_ARG();
ret = flower_parse_ct_labels(*argv, n);
if (ret < 0) {
fprintf(stderr, "Illegal \"ct_label\"\n");
return -1;
}
} else if (matches(*argv, "indev") == 0) {
NEXT_ARG();
if (check_ifname(*argv))
@ -1590,6 +1776,85 @@ static void flower_print_tcp_flags(const char *name, struct rtattr *flags_attr,
print_string(PRINT_ANY, name, namefrm, out);
}
static void flower_print_ct_state(struct rtattr *flags_attr,
struct rtattr *mask_attr)
{
SPRINT_BUF(out);
uint16_t state;
uint16_t state_mask;
size_t done = 0;
int i;
if (!flags_attr)
return;
state = rta_getattr_u16(flags_attr);
if (mask_attr)
state_mask = rta_getattr_u16(mask_attr);
else
state_mask = UINT16_MAX;
for (i = 0; i < ARRAY_SIZE(flower_ct_states); i++) {
if (!(state_mask & flower_ct_states[i].flag))
continue;
if (state & flower_ct_states[i].flag)
done += sprintf(out + done, "+%s",
flower_ct_states[i].str);
else
done += sprintf(out + done, "-%s",
flower_ct_states[i].str);
}
print_string(PRINT_ANY, "ct_state", "\n ct_state %s", out);
}
static void flower_print_ct_label(struct rtattr *attr,
struct rtattr *mask_attr)
{
const unsigned char *str;
bool print_mask = false;
int data_len, i;
SPRINT_BUF(out);
char *p;
if (!attr)
return;
data_len = RTA_PAYLOAD(attr);
hexstring_n2a(RTA_DATA(attr), data_len, out, sizeof(out));
p = out + data_len*2;
data_len = RTA_PAYLOAD(attr);
str = RTA_DATA(mask_attr);
if (data_len != 16)
print_mask = true;
for (i = 0; !print_mask && i < data_len; i++) {
if (str[i] != 0xff)
print_mask = true;
}
if (print_mask) {
*p++ = '/';
hexstring_n2a(RTA_DATA(mask_attr), data_len, p,
sizeof(out)-(p-out));
p += data_len*2;
}
*p = '\0';
print_string(PRINT_ANY, "ct_label", "\n ct_label %s", out);
}
static void flower_print_ct_zone(struct rtattr *attr,
struct rtattr *mask_attr)
{
print_masked_u16("ct_zone", attr, mask_attr);
}
static void flower_print_ct_mark(struct rtattr *attr,
struct rtattr *mask_attr)
{
print_masked_u32("ct_mark", attr, mask_attr);
}
static void flower_print_key_id(const char *name, struct rtattr *attr)
{
@ -1949,6 +2214,15 @@ static int flower_print_opt(struct filter_util *qu, FILE *f,
tb[TCA_FLOWER_KEY_FLAGS],
tb[TCA_FLOWER_KEY_FLAGS_MASK]);
flower_print_ct_state(tb[TCA_FLOWER_KEY_CT_STATE],
tb[TCA_FLOWER_KEY_CT_STATE_MASK]);
flower_print_ct_zone(tb[TCA_FLOWER_KEY_CT_ZONE],
tb[TCA_FLOWER_KEY_CT_ZONE_MASK]);
flower_print_ct_mark(tb[TCA_FLOWER_KEY_CT_MARK],
tb[TCA_FLOWER_KEY_CT_MARK_MASK]);
flower_print_ct_label(tb[TCA_FLOWER_KEY_CT_LABELS],
tb[TCA_FLOWER_KEY_CT_LABELS_MASK]);
close_json_object();
if (tb[TCA_FLOWER_FLAGS]) {

View File

@ -214,7 +214,8 @@ done0:
tail = addattr_nest(n, MAX_MSG, ++prio);
addattr_l(n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1);
ret = a->parse_aopt(a, &argc, &argv, TCA_ACT_OPTIONS,
ret = a->parse_aopt(a, &argc, &argv,
TCA_ACT_OPTIONS | NLA_F_NESTED,
n);
if (ret < 0) {

497
tc/m_ct.c Normal file
View File

@ -0,0 +1,497 @@
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/* -
* m_ct.c Connection tracking action
*
* Authors: Paul Blakey <paulb@mellanox.com>
* Yossi Kuperman <yossiku@mellanox.com>
* Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "utils.h"
#include "tc_util.h"
#include <linux/tc_act/tc_ct.h>
static void
usage(void)
{
fprintf(stderr,
"Usage: ct clear\n"
" ct commit [force] [zone ZONE] [mark MASKED_MARK] [label MASKED_LABEL] [nat NAT_SPEC]\n"
" ct [nat] [zone ZONE]\n"
"Where: ZONE is the conntrack zone table number\n"
" NAT_SPEC is {src|dst} addr addr1[-addr2] [port port1[-port2]]\n"
"\n");
exit(-1);
}
static int ct_parse_nat_addr_range(const char *str, struct nlmsghdr *n)
{
inet_prefix addr = { .family = AF_UNSPEC, };
char *addr1, *addr2 = 0;
SPRINT_BUF(buffer);
int attr;
int ret;
strncpy(buffer, str, sizeof(buffer) - 1);
addr1 = buffer;
addr2 = strchr(addr1, '-');
if (addr2) {
*addr2 = '\0';
addr2++;
}
ret = get_addr(&addr, addr1, AF_UNSPEC);
if (ret)
return ret;
attr = addr.family == AF_INET ? TCA_CT_NAT_IPV4_MIN :
TCA_CT_NAT_IPV6_MIN;
addattr_l(n, MAX_MSG, attr, addr.data, addr.bytelen);
if (addr2) {
ret = get_addr(&addr, addr2, addr.family);
if (ret)
return ret;
}
attr = addr.family == AF_INET ? TCA_CT_NAT_IPV4_MAX :
TCA_CT_NAT_IPV6_MAX;
addattr_l(n, MAX_MSG, attr, addr.data, addr.bytelen);
return 0;
}
static int ct_parse_nat_port_range(const char *str, struct nlmsghdr *n)
{
char *port1, *port2 = 0;
SPRINT_BUF(buffer);
__be16 port;
int ret;
strncpy(buffer, str, sizeof(buffer) - 1);
port1 = buffer;
port2 = strchr(port1, '-');
if (port2) {
*port2 = '\0';
port2++;
}
ret = get_be16(&port, port1, 10);
if (ret)
return -1;
addattr16(n, MAX_MSG, TCA_CT_NAT_PORT_MIN, port);
if (port2) {
ret = get_be16(&port, port2, 10);
if (ret)
return -1;
}
addattr16(n, MAX_MSG, TCA_CT_NAT_PORT_MAX, port);
return 0;
}
static int ct_parse_u16(char *str, int value_type, int mask_type,
struct nlmsghdr *n)
{
__u16 value, mask;
char *slash = 0;
if (mask_type != TCA_CT_UNSPEC) {
slash = strchr(str, '/');
if (slash)
*slash = '\0';
}
if (get_u16(&value, str, 0))
return -1;
if (slash) {
if (get_u16(&mask, slash + 1, 0))
return -1;
} else {
mask = UINT16_MAX;
}
addattr16(n, MAX_MSG, value_type, value);
if (mask_type != TCA_CT_UNSPEC)
addattr16(n, MAX_MSG, mask_type, mask);
return 0;
}
static int ct_parse_u32(char *str, int value_type, int mask_type,
struct nlmsghdr *n)
{
__u32 value, mask;
char *slash;
slash = strchr(str, '/');
if (slash)
*slash = '\0';
if (get_u32(&value, str, 0))
return -1;
if (slash) {
if (get_u32(&mask, slash + 1, 0))
return -1;
} else {
mask = UINT32_MAX;
}
addattr32(n, MAX_MSG, value_type, value);
addattr32(n, MAX_MSG, mask_type, mask);
return 0;
}
static int ct_parse_mark(char *str, struct nlmsghdr *n)
{
return ct_parse_u32(str, TCA_CT_MARK, TCA_CT_MARK_MASK, n);
}
static int ct_parse_labels(char *str, struct nlmsghdr *n)
{
#define LABELS_SIZE 16
uint8_t labels[LABELS_SIZE], lmask[LABELS_SIZE];
char *slash, *mask = NULL;
size_t slen, slen_mask = 0;
slash = index(str, '/');
if (slash) {
*slash = 0;
mask = slash+1;
slen_mask = strlen(mask);
}
slen = strlen(str);
if (slen > LABELS_SIZE*2 || slen_mask > LABELS_SIZE*2) {
char errmsg[128];
snprintf(errmsg, sizeof(errmsg),
"%zd Max allowed size %d",
slen, LABELS_SIZE*2);
invarg(errmsg, str);
}
if (hex2mem(str, labels, slen/2) < 0)
invarg("ct: labels must be a hex string\n", str);
addattr_l(n, MAX_MSG, TCA_CT_LABELS, labels, slen/2);
if (mask) {
if (hex2mem(mask, lmask, slen_mask/2) < 0)
invarg("ct: labels mask must be a hex string\n", mask);
} else {
memset(lmask, 0xff, sizeof(lmask));
slen_mask = sizeof(lmask)*2;
}
addattr_l(n, MAX_MSG, TCA_CT_LABELS_MASK, lmask, slen_mask/2);
return 0;
}
static int
parse_ct(struct action_util *a, int *argc_p, char ***argv_p, int tca_id,
struct nlmsghdr *n)
{
struct tc_ct sel = {};
char **argv = *argv_p;
struct rtattr *tail;
int argc = *argc_p;
int ct_action = 0;
int ret;
tail = addattr_nest(n, MAX_MSG, tca_id);
if (argc && matches(*argv, "ct") == 0)
NEXT_ARG_FWD();
while (argc > 0) {
if (matches(*argv, "zone") == 0) {
NEXT_ARG();
if (ct_parse_u16(*argv,
TCA_CT_ZONE, TCA_CT_UNSPEC, n)) {
fprintf(stderr, "ct: Illegal \"zone\"\n");
return -1;
}
} else if (matches(*argv, "nat") == 0) {
ct_action |= TCA_CT_ACT_NAT;
NEXT_ARG();
if (matches(*argv, "src") == 0)
ct_action |= TCA_CT_ACT_NAT_SRC;
else if (matches(*argv, "dst") == 0)
ct_action |= TCA_CT_ACT_NAT_DST;
else
continue;
NEXT_ARG();
if (matches(*argv, "addr") != 0)
usage();
NEXT_ARG();
ret = ct_parse_nat_addr_range(*argv, n);
if (ret) {
fprintf(stderr, "ct: Illegal nat address range\n");
return -1;
}
NEXT_ARG_FWD();
if (matches(*argv, "port") != 0)
continue;
NEXT_ARG();
ret = ct_parse_nat_port_range(*argv, n);
if (ret) {
fprintf(stderr, "ct: Illegal nat port range\n");
return -1;
}
} else if (matches(*argv, "clear") == 0) {
ct_action |= TCA_CT_ACT_CLEAR;
} else if (matches(*argv, "commit") == 0) {
ct_action |= TCA_CT_ACT_COMMIT;
} else if (matches(*argv, "force") == 0) {
ct_action |= TCA_CT_ACT_FORCE;
} else if (matches(*argv, "index") == 0) {
NEXT_ARG();
if (get_u32(&sel.index, *argv, 10)) {
fprintf(stderr, "ct: Illegal \"index\"\n");
return -1;
}
} else if (matches(*argv, "mark") == 0) {
NEXT_ARG();
ret = ct_parse_mark(*argv, n);
if (ret) {
fprintf(stderr, "ct: Illegal \"mark\"\n");
return -1;
}
} else if (matches(*argv, "label") == 0) {
NEXT_ARG();
ret = ct_parse_labels(*argv, n);
if (ret) {
fprintf(stderr, "ct: Illegal \"label\"\n");
return -1;
}
} else if (matches(*argv, "help") == 0) {
usage();
} else {
break;
}
NEXT_ARG_FWD();
}
if (ct_action & TCA_CT_ACT_CLEAR &&
ct_action & ~TCA_CT_ACT_CLEAR) {
fprintf(stderr, "ct: clear can only be used alone\n");
return -1;
}
if (ct_action & TCA_CT_ACT_NAT_SRC &&
ct_action & TCA_CT_ACT_NAT_DST) {
fprintf(stderr, "ct: src and dst nat can't be used together\n");
return -1;
}
if ((ct_action & TCA_CT_ACT_COMMIT) &&
(ct_action & TCA_CT_ACT_NAT) &&
!(ct_action & (TCA_CT_ACT_NAT_SRC | TCA_CT_ACT_NAT_DST))) {
fprintf(stderr, "ct: commit and nat must set src or dst\n");
return -1;
}
if (!(ct_action & TCA_CT_ACT_COMMIT) &&
(ct_action & (TCA_CT_ACT_NAT_SRC | TCA_CT_ACT_NAT_DST))) {
fprintf(stderr, "ct: src or dst is only valid if commit is set\n");
return -1;
}
parse_action_control_dflt(&argc, &argv, &sel.action, false,
TC_ACT_PIPE);
NEXT_ARG_FWD();
addattr16(n, MAX_MSG, TCA_CT_ACTION, ct_action);
addattr_l(n, MAX_MSG, TCA_CT_PARMS, &sel, sizeof(sel));
addattr_nest_end(n, tail);
*argc_p = argc;
*argv_p = argv;
return 0;
}
static int ct_sprint_port(char *buf, const char *prefix, struct rtattr *attr)
{
if (!attr)
return 0;
return sprintf(buf, "%s%d", prefix, rta_getattr_be16(attr));
}
static int ct_sprint_ip_addr(char *buf, const char *prefix,
struct rtattr *attr)
{
int family;
size_t len;
if (!attr)
return 0;
len = RTA_PAYLOAD(attr);
if (len == 4)
family = AF_INET;
else if (len == 16)
family = AF_INET6;
else
return 0;
return sprintf(buf, "%s%s", prefix, rt_addr_n2a_rta(family, attr));
}
static void ct_print_nat(int ct_action, struct rtattr **tb)
{
size_t done = 0;
char out[256] = "";
bool nat;
if (!(ct_action & TCA_CT_ACT_NAT))
return;
if (ct_action & TCA_CT_ACT_NAT_SRC) {
nat = true;
done += sprintf(out + done, "src");
} else if (ct_action & TCA_CT_ACT_NAT_DST) {
nat = true;
done += sprintf(out + done, "dst");
}
if (nat) {
done += ct_sprint_ip_addr(out + done, " addr ",
tb[TCA_CT_NAT_IPV4_MIN]);
done += ct_sprint_ip_addr(out + done, " addr ",
tb[TCA_CT_NAT_IPV6_MIN]);
if (tb[TCA_CT_NAT_IPV4_MAX] &&
memcmp(RTA_DATA(tb[TCA_CT_NAT_IPV4_MIN]),
RTA_DATA(tb[TCA_CT_NAT_IPV4_MAX]), 4))
done += ct_sprint_ip_addr(out + done, "-",
tb[TCA_CT_NAT_IPV4_MAX]);
else if (tb[TCA_CT_NAT_IPV6_MAX] &&
memcmp(RTA_DATA(tb[TCA_CT_NAT_IPV6_MIN]),
RTA_DATA(tb[TCA_CT_NAT_IPV6_MAX]), 16))
done += ct_sprint_ip_addr(out + done, "-",
tb[TCA_CT_NAT_IPV6_MAX]);
done += ct_sprint_port(out + done, " port ",
tb[TCA_CT_NAT_PORT_MIN]);
if (tb[TCA_CT_NAT_PORT_MAX] &&
memcmp(RTA_DATA(tb[TCA_CT_NAT_PORT_MIN]),
RTA_DATA(tb[TCA_CT_NAT_PORT_MAX]), 2))
done += ct_sprint_port(out + done, "-",
tb[TCA_CT_NAT_PORT_MAX]);
}
if (done)
print_string(PRINT_ANY, "nat", " nat %s", out);
else
print_string(PRINT_ANY, "nat", " nat", "");
}
static void ct_print_labels(struct rtattr *attr,
struct rtattr *mask_attr)
{
const unsigned char *str;
bool print_mask = false;
char out[256], *p;
int data_len, i;
if (!attr)
return;
data_len = RTA_PAYLOAD(attr);
hexstring_n2a(RTA_DATA(attr), data_len, out, sizeof(out));
p = out + data_len*2;
data_len = RTA_PAYLOAD(attr);
str = RTA_DATA(mask_attr);
if (data_len != 16)
print_mask = true;
for (i = 0; !print_mask && i < data_len; i++) {
if (str[i] != 0xff)
print_mask = true;
}
if (print_mask) {
*p++ = '/';
hexstring_n2a(RTA_DATA(mask_attr), data_len, p,
sizeof(out)-(p-out));
p += data_len*2;
}
*p = '\0';
print_string(PRINT_ANY, "label", " label %s", out);
}
static int print_ct(struct action_util *au, FILE *f, struct rtattr *arg)
{
struct rtattr *tb[TCA_CT_MAX + 1];
const char *commit;
struct tc_ct *p;
int ct_action = 0;
if (arg == NULL)
return -1;
parse_rtattr_nested(tb, TCA_CT_MAX, arg);
if (tb[TCA_CT_PARMS] == NULL) {
print_string(PRINT_FP, NULL, "%s", "[NULL ct parameters]");
return -1;
}
p = RTA_DATA(tb[TCA_CT_PARMS]);
print_string(PRINT_ANY, "kind", "%s", "ct");
if (tb[TCA_CT_ACTION])
ct_action = rta_getattr_u16(tb[TCA_CT_ACTION]);
if (ct_action & TCA_CT_ACT_COMMIT) {
commit = ct_action & TCA_CT_ACT_FORCE ?
"commit force" : "commit";
print_string(PRINT_ANY, "action", " %s", commit);
} else if (ct_action & TCA_CT_ACT_CLEAR) {
print_string(PRINT_ANY, "action", " %s", "clear");
}
print_masked_u32("mark", tb[TCA_CT_MARK], tb[TCA_CT_MARK_MASK]);
print_masked_u16("zone", tb[TCA_CT_ZONE], NULL);
ct_print_labels(tb[TCA_CT_LABELS], tb[TCA_CT_LABELS_MASK]);
ct_print_nat(ct_action, tb);
print_action_control(f, " ", p->action, "");
print_uint(PRINT_ANY, "index", "\n\t index %u", p->index);
print_int(PRINT_ANY, "ref", " ref %d", p->refcnt);
print_int(PRINT_ANY, "bind", " bind %d", p->bindcnt);
if (show_stats) {
if (tb[TCA_CT_TM]) {
struct tcf_t *tm = RTA_DATA(tb[TCA_CT_TM]);
print_tm(f, tm);
}
}
print_string(PRINT_FP, NULL, "%s", "\n ");
return 0;
}
struct action_util ct_action_util = {
.id = "ct",
.parse_aopt = parse_ct,
.print_aopt = print_ct,
};

View File

@ -49,11 +49,6 @@ static void usage(void)
exit(-1);
}
static void explain1(char *arg)
{
fprintf(stderr, "Illegal \"%s\"\n", arg);
}
static int act_parse_police(struct action_util *a, int *argc_p, char ***argv_p,
int tca_id, struct nlmsghdr *n)
{
@ -71,6 +66,7 @@ static int act_parse_police(struct action_util *a, int *argc_p, char ***argv_p,
unsigned int linklayer = LINKLAYER_ETHERNET; /* Assume ethernet */
int Rcell_log = -1, Pcell_log = -1;
struct rtattr *tail;
__u64 rate64 = 0, prate64 = 0;
if (a) /* new way of doing things */
NEXT_ARG();
@ -82,73 +78,47 @@ static int act_parse_police(struct action_util *a, int *argc_p, char ***argv_p,
if (matches(*argv, "index") == 0) {
NEXT_ARG();
if (get_u32(&p.index, *argv, 10)) {
fprintf(stderr, "Illegal \"index\"\n");
return -1;
}
if (get_u32(&p.index, *argv, 10))
invarg("index", *argv);
} else if (matches(*argv, "burst") == 0 ||
strcmp(*argv, "buffer") == 0 ||
strcmp(*argv, "maxburst") == 0) {
NEXT_ARG();
if (buffer) {
fprintf(stderr, "Double \"buffer/burst\" spec\n");
return -1;
}
if (get_size_and_cell(&buffer, &Rcell_log, *argv) < 0) {
explain1("buffer");
return -1;
}
if (buffer)
duparg("buffer/burst", *argv);
if (get_size_and_cell(&buffer, &Rcell_log, *argv) < 0)
invarg("buffer", *argv);
} else if (strcmp(*argv, "mtu") == 0 ||
strcmp(*argv, "minburst") == 0) {
NEXT_ARG();
if (mtu) {
fprintf(stderr, "Double \"mtu/minburst\" spec\n");
return -1;
}
if (get_size_and_cell(&mtu, &Pcell_log, *argv) < 0) {
explain1("mtu");
return -1;
}
if (mtu)
duparg("mtu/minburst", *argv);
if (get_size_and_cell(&mtu, &Pcell_log, *argv) < 0)
invarg("mtu", *argv);
} else if (strcmp(*argv, "mpu") == 0) {
NEXT_ARG();
if (mpu) {
fprintf(stderr, "Double \"mpu\" spec\n");
return -1;
}
if (get_size(&mpu, *argv)) {
explain1("mpu");
return -1;
}
if (mpu)
duparg("mpu", *argv);
if (get_size(&mpu, *argv))
invarg("mpu", *argv);
} else if (strcmp(*argv, "rate") == 0) {
NEXT_ARG();
if (p.rate.rate) {
fprintf(stderr, "Double \"rate\" spec\n");
return -1;
}
if (get_rate(&p.rate.rate, *argv)) {
explain1("rate");
return -1;
}
if (rate64)
duparg("rate", *argv);
if (get_rate64(&rate64, *argv))
invarg("rate", *argv);
} else if (strcmp(*argv, "avrate") == 0) {
NEXT_ARG();
if (avrate) {
fprintf(stderr, "Double \"avrate\" spec\n");
return -1;
}
if (get_rate(&avrate, *argv)) {
explain1("avrate");
return -1;
}
if (avrate)
duparg("avrate", *argv);
if (get_rate(&avrate, *argv))
invarg("avrate", *argv);
} else if (matches(*argv, "peakrate") == 0) {
NEXT_ARG();
if (p.peakrate.rate) {
fprintf(stderr, "Double \"peakrate\" spec\n");
return -1;
}
if (get_rate(&p.peakrate.rate, *argv)) {
explain1("peakrate");
return -1;
}
if (prate64)
duparg("peakrate", *argv);
if (get_rate64(&prate64, *argv))
invarg("peakrate", *argv);
} else if (matches(*argv, "reclassify") == 0 ||
matches(*argv, "drop") == 0 ||
matches(*argv, "shot") == 0 ||
@ -168,14 +138,12 @@ static int act_parse_police(struct action_util *a, int *argc_p, char ***argv_p,
return -1;
} else if (matches(*argv, "overhead") == 0) {
NEXT_ARG();
if (get_u16(&overhead, *argv, 10)) {
explain1("overhead"); return -1;
}
if (get_u16(&overhead, *argv, 10))
invarg("overhead", *argv);
} else if (matches(*argv, "linklayer") == 0) {
NEXT_ARG();
if (get_linklayer(&linklayer, *argv)) {
explain1("linklayer"); return -1;
}
if (get_linklayer(&linklayer, *argv))
invarg("linklayer", *argv);
} else if (strcmp(*argv, "help") == 0) {
usage();
} else {
@ -189,23 +157,23 @@ action_ctrl_ok:
if (!ok)
return -1;
if (p.rate.rate && avrate)
if (rate64 && avrate)
return -1;
/* Must at least do late binding, use TB or ewma policing */
if (!p.rate.rate && !avrate && !p.index) {
if (!rate64 && !avrate && !p.index) {
fprintf(stderr, "\"rate\" or \"avrate\" MUST be specified.\n");
return -1;
}
/* When the TB policer is used, burst is required */
if (p.rate.rate && !buffer && !avrate) {
if (rate64 && !buffer && !avrate) {
fprintf(stderr, "\"burst\" requires \"rate\".\n");
return -1;
}
if (p.peakrate.rate) {
if (!p.rate.rate) {
if (prate64) {
if (!rate64) {
fprintf(stderr, "\"peakrate\" requires \"rate\".\n");
return -1;
}
@ -215,22 +183,24 @@ action_ctrl_ok:
}
}
if (p.rate.rate) {
if (rate64) {
p.rate.rate = (rate64 >= (1ULL << 32)) ? ~0U : rate64;
p.rate.mpu = mpu;
p.rate.overhead = overhead;
if (tc_calc_rtable(&p.rate, rtab, Rcell_log, mtu,
linklayer) < 0) {
if (tc_calc_rtable_64(&p.rate, rtab, Rcell_log, mtu,
linklayer, rate64) < 0) {
fprintf(stderr, "POLICE: failed to calculate rate table.\n");
return -1;
}
p.burst = tc_calc_xmittime(p.rate.rate, buffer);
p.burst = tc_calc_xmittime(rate64, buffer);
}
p.mtu = mtu;
if (p.peakrate.rate) {
if (prate64) {
p.peakrate.rate = (prate64 >= (1ULL << 32)) ? ~0U : prate64;
p.peakrate.mpu = mpu;
p.peakrate.overhead = overhead;
if (tc_calc_rtable(&p.peakrate, ptab, Pcell_log, mtu,
linklayer) < 0) {
if (tc_calc_rtable_64(&p.peakrate, ptab, Pcell_log, mtu,
linklayer, prate64) < 0) {
fprintf(stderr, "POLICE: failed to calculate peak rate table.\n");
return -1;
}
@ -238,10 +208,16 @@ action_ctrl_ok:
tail = addattr_nest(n, MAX_MSG, tca_id);
addattr_l(n, MAX_MSG, TCA_POLICE_TBF, &p, sizeof(p));
if (p.rate.rate)
if (rate64) {
addattr_l(n, MAX_MSG, TCA_POLICE_RATE, rtab, 1024);
if (p.peakrate.rate)
if (rate64 >= (1ULL << 32))
addattr64(n, MAX_MSG, TCA_POLICE_RATE64, rate64);
}
if (prate64) {
addattr_l(n, MAX_MSG, TCA_POLICE_PEAKRATE, ptab, 1024);
if (prate64 >= (1ULL << 32))
addattr64(n, MAX_MSG, TCA_POLICE_PEAKRATE64, prate64);
}
if (avrate)
addattr32(n, MAX_MSG, TCA_POLICE_AVRATE, avrate);
if (presult)
@ -268,6 +244,7 @@ static int print_police(struct action_util *a, FILE *f, struct rtattr *arg)
struct rtattr *tb[TCA_POLICE_MAX+1];
unsigned int buffer;
unsigned int linklayer;
__u64 rate64, prate64;
if (arg == NULL)
return 0;
@ -286,16 +263,26 @@ static int print_police(struct action_util *a, FILE *f, struct rtattr *arg)
#endif
p = RTA_DATA(tb[TCA_POLICE_TBF]);
rate64 = p->rate.rate;
if (tb[TCA_POLICE_RATE64] &&
RTA_PAYLOAD(tb[TCA_POLICE_RATE64]) >= sizeof(rate64))
rate64 = rta_getattr_u64(tb[TCA_POLICE_RATE64]);
fprintf(f, " police 0x%x ", p->index);
fprintf(f, "rate %s ", sprint_rate(p->rate.rate, b1));
buffer = tc_calc_xmitsize(p->rate.rate, p->burst);
fprintf(f, "rate %s ", sprint_rate(rate64, b1));
buffer = tc_calc_xmitsize(rate64, p->burst);
fprintf(f, "burst %s ", sprint_size(buffer, b1));
fprintf(f, "mtu %s ", sprint_size(p->mtu, b1));
if (show_raw)
fprintf(f, "[%08x] ", p->burst);
if (p->peakrate.rate)
fprintf(f, "peakrate %s ", sprint_rate(p->peakrate.rate, b1));
prate64 = p->peakrate.rate;
if (tb[TCA_POLICE_PEAKRATE64] &&
RTA_PAYLOAD(tb[TCA_POLICE_PEAKRATE64]) >= sizeof(prate64))
prate64 = rta_getattr_u64(tb[TCA_POLICE_PEAKRATE64]);
if (prate64)
fprintf(f, "peakrate %s ", sprint_rate(prate64, b1));
if (tb[TCA_POLICE_AVRATE])
fprintf(f, "avrate %s ",

View File

@ -130,6 +130,13 @@ static int etf_parse_opt(struct qdisc_util *qu, int argc,
explain_clockid(*argv);
return -1;
}
} else if (strcmp(*argv, "skip_sock_check") == 0) {
if (opt.flags & TC_ETF_SKIP_SOCK_CHECK) {
fprintf(stderr, "etf: duplicate \"skip_sock_check\" specification\n");
return -1;
}
opt.flags |= TC_ETF_SKIP_SOCK_CHECK;
} else if (strcmp(*argv, "help") == 0) {
explain();
return -1;
@ -171,8 +178,10 @@ static int etf_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
print_uint(PRINT_ANY, "delta", "delta %d ", qopt->delta);
print_string(PRINT_ANY, "offload", "offload %s ",
(qopt->flags & TC_ETF_OFFLOAD_ON) ? "on" : "off");
print_string(PRINT_ANY, "deadline_mode", "deadline_mode %s",
print_string(PRINT_ANY, "deadline_mode", "deadline_mode %s ",
(qopt->flags & TC_ETF_DEADLINE_MODE_ON) ? "on" : "off");
print_string(PRINT_ANY, "skip_sock_check", "skip_sock_check %s",
(qopt->flags & TC_ETF_SKIP_SOCK_CHECK) ? "on" : "off");
return 0;
}

View File

@ -52,7 +52,7 @@ static void explain(void)
" [num_tc NUMBER] [map P0 P1 ...] "
" [queues COUNT@OFFSET COUNT@OFFSET COUNT@OFFSET ...] "
" [ [sched-entry index cmd gate-mask interval] ... ] "
" [base-time time] "
" [base-time time] [txtime-delay delay]"
"\n"
"CLOCKID must be a valid SYS-V id (i.e. CLOCK_TAI)\n");
}
@ -159,6 +159,8 @@ static int taprio_parse_opt(struct qdisc_util *qu, int argc,
__s64 cycle_time_extension = 0;
struct list_head sched_entries;
struct rtattr *tail, *l;
__u32 taprio_flags = 0;
__u32 txtime_delay = 0;
__s64 cycle_time = 0;
__s64 base_time = 0;
int err, idx;
@ -281,6 +283,28 @@ static int taprio_parse_opt(struct qdisc_util *qu, int argc,
explain_clockid(*argv);
return -1;
}
} else if (strcmp(*argv, "flags") == 0) {
NEXT_ARG();
if (taprio_flags) {
fprintf(stderr, "taprio: duplicate \"flags\" specification\n");
return -1;
}
if (get_u32(&taprio_flags, *argv, 0)) {
PREV_ARG();
return -1;
}
} else if (strcmp(*argv, "txtime-delay") == 0) {
NEXT_ARG();
if (txtime_delay != 0) {
fprintf(stderr, "taprio: duplicate \"txtime-delay\" specification\n");
return -1;
}
if (get_u32(&txtime_delay, *argv, 0)) {
PREV_ARG();
return -1;
}
} else if (strcmp(*argv, "help") == 0) {
explain();
return -1;
@ -297,9 +321,15 @@ static int taprio_parse_opt(struct qdisc_util *qu, int argc,
if (clockid != CLOCKID_INVALID)
addattr_l(n, 1024, TCA_TAPRIO_ATTR_SCHED_CLOCKID, &clockid, sizeof(clockid));
if (taprio_flags)
addattr_l(n, 1024, TCA_TAPRIO_ATTR_FLAGS, &taprio_flags, sizeof(taprio_flags));
if (opt.num_tc > 0)
addattr_l(n, 1024, TCA_TAPRIO_ATTR_PRIOMAP, &opt, sizeof(opt));
if (txtime_delay)
addattr_l(n, 1024, TCA_TAPRIO_ATTR_TXTIME_DELAY, &txtime_delay, sizeof(txtime_delay));
if (base_time)
addattr_l(n, 1024, TCA_TAPRIO_ATTR_SCHED_BASE_TIME, &base_time, sizeof(base_time));
@ -442,6 +472,20 @@ static int taprio_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
print_string(PRINT_ANY, "clockid", "clockid %s", get_clock_name(clockid));
if (tb[TCA_TAPRIO_ATTR_FLAGS]) {
__u32 flags;
flags = rta_getattr_u32(tb[TCA_TAPRIO_ATTR_FLAGS]);
print_0xhex(PRINT_ANY, "flags", " flags %#x", flags);
}
if (tb[TCA_TAPRIO_ATTR_TXTIME_DELAY]) {
__u32 txtime_delay;
txtime_delay = rta_getattr_s32(tb[TCA_TAPRIO_ATTR_TXTIME_DELAY]);
print_uint(PRINT_ANY, "txtime_delay", " txtime delay %d", txtime_delay);
}
print_schedule(f, tb);
if (tb[TCA_TAPRIO_ATTR_ADMIN_SCHED]) {

View File

@ -152,6 +152,35 @@ int tc_calc_rtable(struct tc_ratespec *r, __u32 *rtab,
return cell_log;
}
int tc_calc_rtable_64(struct tc_ratespec *r, __u32 *rtab,
int cell_log, unsigned int mtu,
enum link_layer linklayer, __u64 rate)
{
int i;
unsigned int sz;
__u64 bps = rate;
unsigned int mpu = r->mpu;
if (mtu == 0)
mtu = 2047;
if (cell_log < 0) {
cell_log = 0;
while ((mtu >> cell_log) > 255)
cell_log++;
}
for (i = 0; i < 256; i++) {
sz = tc_adjust_size((i + 1) << cell_log, mpu, linklayer);
rtab[i] = tc_calc_xmittime(bps, sz);
}
r->cell_align = -1;
r->cell_log = cell_log;
r->linklayer = (linklayer & TC_LINKLAYER_MASK);
return cell_log;
}
/*
stab[pkt_len>>cell_log] = pkt_xmit_size>>size_log
*/

View File

@ -21,6 +21,9 @@ unsigned tc_calc_xmittime(__u64 rate, unsigned size);
unsigned tc_calc_xmitsize(__u64 rate, unsigned ticks);
int tc_calc_rtable(struct tc_ratespec *r, __u32 *rtab,
int cell_log, unsigned mtu, enum link_layer link_layer);
int tc_calc_rtable_64(struct tc_ratespec *r, __u32 *rtab,
int cell_log, unsigned mtu, enum link_layer link_layer,
__u64 rate);
int tc_calc_size_table(struct tc_sizespec *s, __u16 **stab);
int tc_setup_estimator(unsigned A, unsigned time_const, struct tc_estimator *est);

View File

@ -914,3 +914,47 @@ compat_xstats:
if (tb[TCA_XSTATS] && xstats)
*xstats = tb[TCA_XSTATS];
}
void print_masked_u32(const char *name, struct rtattr *attr,
struct rtattr *mask_attr)
{
__u32 value, mask;
SPRINT_BUF(namefrm);
SPRINT_BUF(out);
size_t done;
if (!attr)
return;
value = rta_getattr_u32(attr);
mask = mask_attr ? rta_getattr_u32(mask_attr) : UINT32_MAX;
done = sprintf(out, "%u", value);
if (mask != UINT32_MAX)
sprintf(out + done, "/0x%x", mask);
sprintf(namefrm, " %s %%s", name);
print_string(PRINT_ANY, name, namefrm, out);
}
void print_masked_u16(const char *name, struct rtattr *attr,
struct rtattr *mask_attr)
{
__u16 value, mask;
SPRINT_BUF(namefrm);
SPRINT_BUF(out);
size_t done;
if (!attr)
return;
value = rta_getattr_u16(attr);
mask = mask_attr ? rta_getattr_u16(mask_attr) : UINT16_MAX;
done = sprintf(out, "%u", value);
if (mask != UINT16_MAX)
sprintf(out + done, "/0x%x", mask);
sprintf(namefrm, " %s %%s", name);
print_string(PRINT_ANY, name, namefrm, out);
}

View File

@ -127,4 +127,8 @@ int action_a2n(char *arg, int *result, bool allow_num);
bool tc_qdisc_block_exists(__u32 block_index);
void print_masked_u32(const char *name, struct rtattr *attr,
struct rtattr *mask_attr);
void print_masked_u16(const char *name, struct rtattr *attr,
struct rtattr *mask_attr);
#endif