mirror of
https://git.proxmox.com/git/mirror_iproute2
synced 2025-10-18 22:09:55 +00:00
Merge ../iproute2-next
This commit is contained in:
commit
38e9ba9dc9
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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_ */
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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 */
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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
|
||||
|
41
include/uapi/linux/tc_act/tc_ct.h
Normal file
41
include/uapi/linux/tc_act/tc_ct.h
Normal 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 */
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
109
ip/iptunnel.c
109
ip/iptunnel.c
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
47
ip/tunnel.c
47
ip/tunnel.c
@ -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;
|
||||
}
|
||||
|
@ -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
155
lib/bpf.c
@ -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;
|
||||
|
@ -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),
|
||||
|
@ -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
138
man/man8/devlink-trap.8
Normal 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>
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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";
|
||||
}
|
||||
|
@ -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
|
||||
|
276
tc/f_flower.c
276
tc/f_flower.c
@ -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]) {
|
||||
|
@ -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
497
tc/m_ct.c
Normal 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,
|
||||
};
|
149
tc/m_police.c
149
tc/m_police.c
@ -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 ",
|
||||
|
11
tc/q_etf.c
11
tc/q_etf.c
@ -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;
|
||||
}
|
||||
|
@ -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]) {
|
||||
|
29
tc/tc_core.c
29
tc/tc_core.c
@ -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
|
||||
*/
|
||||
|
@ -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);
|
||||
|
44
tc/tc_util.c
44
tc/tc_util.c
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user