Merge git://git.kernel.org/pub/scm/network/iproute2/iproute2-next
This commit is contained in:
commit
0a5dbbeddb
|
|
@ -292,6 +292,7 @@ static void ifname_map_free(struct ifname_map *ifname_map)
|
||||||
#define DL_OPT_TRAP_POLICER_ID BIT(34)
|
#define DL_OPT_TRAP_POLICER_ID BIT(34)
|
||||||
#define DL_OPT_TRAP_POLICER_RATE BIT(35)
|
#define DL_OPT_TRAP_POLICER_RATE BIT(35)
|
||||||
#define DL_OPT_TRAP_POLICER_BURST BIT(36)
|
#define DL_OPT_TRAP_POLICER_BURST BIT(36)
|
||||||
|
#define DL_OPT_HEALTH_REPORTER_AUTO_DUMP BIT(37)
|
||||||
|
|
||||||
struct dl_opts {
|
struct dl_opts {
|
||||||
uint64_t present; /* flags of present items */
|
uint64_t present; /* flags of present items */
|
||||||
|
|
@ -328,6 +329,7 @@ struct dl_opts {
|
||||||
const char *reporter_name;
|
const char *reporter_name;
|
||||||
uint64_t reporter_graceful_period;
|
uint64_t reporter_graceful_period;
|
||||||
bool reporter_auto_recover;
|
bool reporter_auto_recover;
|
||||||
|
bool reporter_auto_dump;
|
||||||
const char *trap_name;
|
const char *trap_name;
|
||||||
const char *trap_group_name;
|
const char *trap_group_name;
|
||||||
enum devlink_trap_action trap_action;
|
enum devlink_trap_action trap_action;
|
||||||
|
|
@ -1474,6 +1476,13 @@ static int dl_argv_parse(struct dl *dl, uint64_t o_required,
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
o_found |= DL_OPT_HEALTH_REPORTER_AUTO_RECOVER;
|
o_found |= DL_OPT_HEALTH_REPORTER_AUTO_RECOVER;
|
||||||
|
} else if (dl_argv_match(dl, "auto_dump") &&
|
||||||
|
(o_all & DL_OPT_HEALTH_REPORTER_AUTO_DUMP)) {
|
||||||
|
dl_arg_inc(dl);
|
||||||
|
err = dl_argv_bool(dl, &opts->reporter_auto_dump);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
o_found |= DL_OPT_HEALTH_REPORTER_AUTO_DUMP;
|
||||||
} else if (dl_argv_match(dl, "trap") &&
|
} else if (dl_argv_match(dl, "trap") &&
|
||||||
(o_all & DL_OPT_TRAP_NAME)) {
|
(o_all & DL_OPT_TRAP_NAME)) {
|
||||||
dl_arg_inc(dl);
|
dl_arg_inc(dl);
|
||||||
|
|
@ -1656,6 +1665,9 @@ static void dl_opts_put(struct nlmsghdr *nlh, struct dl *dl)
|
||||||
if (opts->present & DL_OPT_HEALTH_REPORTER_AUTO_RECOVER)
|
if (opts->present & DL_OPT_HEALTH_REPORTER_AUTO_RECOVER)
|
||||||
mnl_attr_put_u8(nlh, DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER,
|
mnl_attr_put_u8(nlh, DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER,
|
||||||
opts->reporter_auto_recover);
|
opts->reporter_auto_recover);
|
||||||
|
if (opts->present & DL_OPT_HEALTH_REPORTER_AUTO_DUMP)
|
||||||
|
mnl_attr_put_u8(nlh, DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP,
|
||||||
|
opts->reporter_auto_dump);
|
||||||
if (opts->present & DL_OPT_TRAP_NAME)
|
if (opts->present & DL_OPT_TRAP_NAME)
|
||||||
mnl_attr_put_strz(nlh, DEVLINK_ATTR_TRAP_NAME,
|
mnl_attr_put_strz(nlh, DEVLINK_ATTR_TRAP_NAME,
|
||||||
opts->trap_name);
|
opts->trap_name);
|
||||||
|
|
@ -6464,6 +6476,23 @@ static int cmd_region_read(struct dl *dl)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int cmd_region_snapshot_new_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_REGION_NAME] ||
|
||||||
|
!tb[DEVLINK_ATTR_REGION_SNAPSHOT_ID])
|
||||||
|
return MNL_CB_ERROR;
|
||||||
|
|
||||||
|
pr_out_region(dl, tb);
|
||||||
|
|
||||||
|
return MNL_CB_OK;
|
||||||
|
}
|
||||||
|
|
||||||
static int cmd_region_snapshot_new(struct dl *dl)
|
static int cmd_region_snapshot_new(struct dl *dl)
|
||||||
{
|
{
|
||||||
struct nlmsghdr *nlh;
|
struct nlmsghdr *nlh;
|
||||||
|
|
@ -6472,12 +6501,15 @@ static int cmd_region_snapshot_new(struct dl *dl)
|
||||||
nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_REGION_NEW,
|
nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_REGION_NEW,
|
||||||
NLM_F_REQUEST | NLM_F_ACK);
|
NLM_F_REQUEST | NLM_F_ACK);
|
||||||
|
|
||||||
err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE_REGION |
|
err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE_REGION,
|
||||||
DL_OPT_REGION_SNAPSHOT_ID, 0);
|
DL_OPT_REGION_SNAPSHOT_ID);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
|
pr_out_section_start(dl, "regions");
|
||||||
|
err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_region_snapshot_new_cb, dl);
|
||||||
|
pr_out_section_end(dl);
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cmd_region_help(void)
|
static void cmd_region_help(void)
|
||||||
|
|
@ -6525,7 +6557,8 @@ static int cmd_health_set_params(struct dl *dl)
|
||||||
NLM_F_REQUEST | NLM_F_ACK);
|
NLM_F_REQUEST | NLM_F_ACK);
|
||||||
err = dl_argv_parse(dl, DL_OPT_HANDLE | DL_OPT_HEALTH_REPORTER_NAME,
|
err = dl_argv_parse(dl, DL_OPT_HANDLE | DL_OPT_HEALTH_REPORTER_NAME,
|
||||||
DL_OPT_HEALTH_REPORTER_GRACEFUL_PERIOD |
|
DL_OPT_HEALTH_REPORTER_GRACEFUL_PERIOD |
|
||||||
DL_OPT_HEALTH_REPORTER_AUTO_RECOVER);
|
DL_OPT_HEALTH_REPORTER_AUTO_RECOVER |
|
||||||
|
DL_OPT_HEALTH_REPORTER_AUTO_DUMP);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
|
@ -6939,6 +6972,9 @@ static void pr_out_health(struct dl *dl, struct nlattr **tb_health)
|
||||||
if (tb[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER])
|
if (tb[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER])
|
||||||
print_bool(PRINT_ANY, "auto_recover", " auto_recover %s",
|
print_bool(PRINT_ANY, "auto_recover", " auto_recover %s",
|
||||||
mnl_attr_get_u8(tb[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER]));
|
mnl_attr_get_u8(tb[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER]));
|
||||||
|
if (tb[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP])
|
||||||
|
print_bool(PRINT_ANY, "auto_dump", " auto_dump %s",
|
||||||
|
mnl_attr_get_u8(tb[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_DUMP]));
|
||||||
|
|
||||||
__pr_out_indent_dec();
|
__pr_out_indent_dec();
|
||||||
pr_out_handle_end(dl);
|
pr_out_handle_end(dl);
|
||||||
|
|
@ -6995,6 +7031,7 @@ static void cmd_health_help(void)
|
||||||
pr_err(" devlink health set DEV reporter REPORTER_NAME\n");
|
pr_err(" devlink health set DEV reporter REPORTER_NAME\n");
|
||||||
pr_err(" [ grace_period MSEC ]\n");
|
pr_err(" [ grace_period MSEC ]\n");
|
||||||
pr_err(" [ auto_recover { true | false } ]\n");
|
pr_err(" [ auto_recover { true | false } ]\n");
|
||||||
|
pr_err(" [ auto_dump { true | false } ]\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cmd_health(struct dl *dl)
|
static int cmd_health(struct dl *dl)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
#ifndef __CG_MAP_H__
|
||||||
|
#define __CG_MAP_H__
|
||||||
|
|
||||||
|
const char *cg_id_to_path(__u64 id);
|
||||||
|
|
||||||
|
#endif /* __CG_MAP_H__ */
|
||||||
|
|
@ -113,6 +113,9 @@ enum bpf_cmd {
|
||||||
BPF_MAP_DELETE_BATCH,
|
BPF_MAP_DELETE_BATCH,
|
||||||
BPF_LINK_CREATE,
|
BPF_LINK_CREATE,
|
||||||
BPF_LINK_UPDATE,
|
BPF_LINK_UPDATE,
|
||||||
|
BPF_LINK_GET_FD_BY_ID,
|
||||||
|
BPF_LINK_GET_NEXT_ID,
|
||||||
|
BPF_ENABLE_STATS,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum bpf_map_type {
|
enum bpf_map_type {
|
||||||
|
|
@ -220,6 +223,15 @@ enum bpf_attach_type {
|
||||||
|
|
||||||
#define MAX_BPF_ATTACH_TYPE __MAX_BPF_ATTACH_TYPE
|
#define MAX_BPF_ATTACH_TYPE __MAX_BPF_ATTACH_TYPE
|
||||||
|
|
||||||
|
enum bpf_link_type {
|
||||||
|
BPF_LINK_TYPE_UNSPEC = 0,
|
||||||
|
BPF_LINK_TYPE_RAW_TRACEPOINT = 1,
|
||||||
|
BPF_LINK_TYPE_TRACING = 2,
|
||||||
|
BPF_LINK_TYPE_CGROUP = 3,
|
||||||
|
|
||||||
|
MAX_BPF_LINK_TYPE,
|
||||||
|
};
|
||||||
|
|
||||||
/* cgroup-bpf attach flags used in BPF_PROG_ATTACH command
|
/* cgroup-bpf attach flags used in BPF_PROG_ATTACH command
|
||||||
*
|
*
|
||||||
* NONE(default): No further bpf programs allowed in the subtree.
|
* NONE(default): No further bpf programs allowed in the subtree.
|
||||||
|
|
@ -379,6 +391,12 @@ enum {
|
||||||
*/
|
*/
|
||||||
#define BPF_F_QUERY_EFFECTIVE (1U << 0)
|
#define BPF_F_QUERY_EFFECTIVE (1U << 0)
|
||||||
|
|
||||||
|
/* type for BPF_ENABLE_STATS */
|
||||||
|
enum bpf_stats_type {
|
||||||
|
/* enabled run_time_ns and run_cnt */
|
||||||
|
BPF_STATS_RUN_TIME = 0,
|
||||||
|
};
|
||||||
|
|
||||||
enum bpf_stack_build_id_status {
|
enum bpf_stack_build_id_status {
|
||||||
/* user space need an empty entry to identify end of a trace */
|
/* user space need an empty entry to identify end of a trace */
|
||||||
BPF_STACK_BUILD_ID_EMPTY = 0,
|
BPF_STACK_BUILD_ID_EMPTY = 0,
|
||||||
|
|
@ -523,6 +541,7 @@ union bpf_attr {
|
||||||
__u32 prog_id;
|
__u32 prog_id;
|
||||||
__u32 map_id;
|
__u32 map_id;
|
||||||
__u32 btf_id;
|
__u32 btf_id;
|
||||||
|
__u32 link_id;
|
||||||
};
|
};
|
||||||
__u32 next_id;
|
__u32 next_id;
|
||||||
__u32 open_flags;
|
__u32 open_flags;
|
||||||
|
|
@ -589,6 +608,10 @@ union bpf_attr {
|
||||||
__u32 old_prog_fd;
|
__u32 old_prog_fd;
|
||||||
} link_update;
|
} link_update;
|
||||||
|
|
||||||
|
struct { /* struct used by BPF_ENABLE_STATS command */
|
||||||
|
__u32 type;
|
||||||
|
} enable_stats;
|
||||||
|
|
||||||
} __attribute__((aligned(8)));
|
} __attribute__((aligned(8)));
|
||||||
|
|
||||||
/* The description below is an attempt at providing documentation to eBPF
|
/* The description below is an attempt at providing documentation to eBPF
|
||||||
|
|
@ -652,6 +675,8 @@ union bpf_attr {
|
||||||
* u64 bpf_ktime_get_ns(void)
|
* u64 bpf_ktime_get_ns(void)
|
||||||
* Description
|
* Description
|
||||||
* Return the time elapsed since system boot, in nanoseconds.
|
* Return the time elapsed since system boot, in nanoseconds.
|
||||||
|
* Does not include time the system was suspended.
|
||||||
|
* See: clock_gettime(CLOCK_MONOTONIC)
|
||||||
* Return
|
* Return
|
||||||
* Current *ktime*.
|
* Current *ktime*.
|
||||||
*
|
*
|
||||||
|
|
@ -1562,7 +1587,7 @@ union bpf_attr {
|
||||||
* Return
|
* Return
|
||||||
* 0
|
* 0
|
||||||
*
|
*
|
||||||
* int bpf_setsockopt(struct bpf_sock_ops *bpf_socket, int level, int optname, void *optval, int optlen)
|
* int bpf_setsockopt(void *bpf_socket, int level, int optname, void *optval, int optlen)
|
||||||
* Description
|
* Description
|
||||||
* Emulate a call to **setsockopt()** on the socket associated to
|
* Emulate a call to **setsockopt()** on the socket associated to
|
||||||
* *bpf_socket*, which must be a full socket. The *level* at
|
* *bpf_socket*, which must be a full socket. The *level* at
|
||||||
|
|
@ -1570,6 +1595,11 @@ union bpf_attr {
|
||||||
* must be specified, see **setsockopt(2)** for more information.
|
* must be specified, see **setsockopt(2)** for more information.
|
||||||
* The option value of length *optlen* is pointed by *optval*.
|
* The option value of length *optlen* is pointed by *optval*.
|
||||||
*
|
*
|
||||||
|
* *bpf_socket* should be one of the following:
|
||||||
|
* * **struct bpf_sock_ops** for **BPF_PROG_TYPE_SOCK_OPS**.
|
||||||
|
* * **struct bpf_sock_addr** for **BPF_CGROUP_INET4_CONNECT**
|
||||||
|
* and **BPF_CGROUP_INET6_CONNECT**.
|
||||||
|
*
|
||||||
* This helper actually implements a subset of **setsockopt()**.
|
* This helper actually implements a subset of **setsockopt()**.
|
||||||
* It supports the following *level*\ s:
|
* It supports the following *level*\ s:
|
||||||
*
|
*
|
||||||
|
|
@ -1764,7 +1794,7 @@ union bpf_attr {
|
||||||
* Return
|
* Return
|
||||||
* 0 on success, or a negative error in case of failure.
|
* 0 on success, or a negative error in case of failure.
|
||||||
*
|
*
|
||||||
* int bpf_getsockopt(struct bpf_sock_ops *bpf_socket, int level, int optname, void *optval, int optlen)
|
* int bpf_getsockopt(void *bpf_socket, int level, int optname, void *optval, int optlen)
|
||||||
* Description
|
* Description
|
||||||
* Emulate a call to **getsockopt()** on the socket associated to
|
* Emulate a call to **getsockopt()** on the socket associated to
|
||||||
* *bpf_socket*, which must be a full socket. The *level* at
|
* *bpf_socket*, which must be a full socket. The *level* at
|
||||||
|
|
@ -1773,6 +1803,11 @@ union bpf_attr {
|
||||||
* The retrieved value is stored in the structure pointed by
|
* The retrieved value is stored in the structure pointed by
|
||||||
* *opval* and of length *optlen*.
|
* *opval* and of length *optlen*.
|
||||||
*
|
*
|
||||||
|
* *bpf_socket* should be one of the following:
|
||||||
|
* * **struct bpf_sock_ops** for **BPF_PROG_TYPE_SOCK_OPS**.
|
||||||
|
* * **struct bpf_sock_addr** for **BPF_CGROUP_INET4_CONNECT**
|
||||||
|
* and **BPF_CGROUP_INET6_CONNECT**.
|
||||||
|
*
|
||||||
* This helper actually implements a subset of **getsockopt()**.
|
* This helper actually implements a subset of **getsockopt()**.
|
||||||
* It supports the following *level*\ s:
|
* It supports the following *level*\ s:
|
||||||
*
|
*
|
||||||
|
|
@ -3025,6 +3060,14 @@ union bpf_attr {
|
||||||
* * **-EOPNOTSUPP** Unsupported operation, for example a
|
* * **-EOPNOTSUPP** Unsupported operation, for example a
|
||||||
* call from outside of TC ingress.
|
* call from outside of TC ingress.
|
||||||
* * **-ESOCKTNOSUPPORT** Socket type not supported (reuseport).
|
* * **-ESOCKTNOSUPPORT** Socket type not supported (reuseport).
|
||||||
|
*
|
||||||
|
* u64 bpf_ktime_get_boot_ns(void)
|
||||||
|
* Description
|
||||||
|
* Return the time elapsed since system boot, in nanoseconds.
|
||||||
|
* Does include the time the system was suspended.
|
||||||
|
* See: clock_gettime(CLOCK_BOOTTIME)
|
||||||
|
* Return
|
||||||
|
* Current *ktime*.
|
||||||
*/
|
*/
|
||||||
#define __BPF_FUNC_MAPPER(FN) \
|
#define __BPF_FUNC_MAPPER(FN) \
|
||||||
FN(unspec), \
|
FN(unspec), \
|
||||||
|
|
@ -3151,7 +3194,8 @@ union bpf_attr {
|
||||||
FN(xdp_output), \
|
FN(xdp_output), \
|
||||||
FN(get_netns_cookie), \
|
FN(get_netns_cookie), \
|
||||||
FN(get_current_ancestor_cgroup_id), \
|
FN(get_current_ancestor_cgroup_id), \
|
||||||
FN(sk_assign),
|
FN(sk_assign), \
|
||||||
|
FN(ktime_get_boot_ns),
|
||||||
|
|
||||||
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
|
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
|
||||||
* function eBPF program intends to call
|
* function eBPF program intends to call
|
||||||
|
|
@ -3598,6 +3642,25 @@ struct bpf_btf_info {
|
||||||
__u32 id;
|
__u32 id;
|
||||||
} __attribute__((aligned(8)));
|
} __attribute__((aligned(8)));
|
||||||
|
|
||||||
|
struct bpf_link_info {
|
||||||
|
__u32 type;
|
||||||
|
__u32 id;
|
||||||
|
__u32 prog_id;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
__aligned_u64 tp_name; /* in/out: tp_name buffer ptr */
|
||||||
|
__u32 tp_name_len; /* in/out: tp_name buffer len */
|
||||||
|
} raw_tracepoint;
|
||||||
|
struct {
|
||||||
|
__u32 attach_type;
|
||||||
|
} tracing;
|
||||||
|
struct {
|
||||||
|
__u64 cgroup_id;
|
||||||
|
__u32 attach_type;
|
||||||
|
} cgroup;
|
||||||
|
};
|
||||||
|
} __attribute__((aligned(8)));
|
||||||
|
|
||||||
/* User bpf_sock_addr struct to access socket fields and sockaddr struct passed
|
/* User bpf_sock_addr struct to access socket fields and sockaddr struct passed
|
||||||
* by user and intended to be used by socket (e.g. to bind to, depends on
|
* by user and intended to be used by socket (e.g. to bind to, depends on
|
||||||
* attach attach type).
|
* attach attach type).
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ enum {
|
||||||
CTRL_CMD_NEWMCAST_GRP,
|
CTRL_CMD_NEWMCAST_GRP,
|
||||||
CTRL_CMD_DELMCAST_GRP,
|
CTRL_CMD_DELMCAST_GRP,
|
||||||
CTRL_CMD_GETMCAST_GRP, /* unused */
|
CTRL_CMD_GETMCAST_GRP, /* unused */
|
||||||
|
CTRL_CMD_GETPOLICY,
|
||||||
__CTRL_CMD_MAX,
|
__CTRL_CMD_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -62,6 +63,7 @@ enum {
|
||||||
CTRL_ATTR_MAXATTR,
|
CTRL_ATTR_MAXATTR,
|
||||||
CTRL_ATTR_OPS,
|
CTRL_ATTR_OPS,
|
||||||
CTRL_ATTR_MCAST_GROUPS,
|
CTRL_ATTR_MCAST_GROUPS,
|
||||||
|
CTRL_ATTR_POLICY,
|
||||||
__CTRL_ATTR_MAX,
|
__CTRL_ATTR_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -176,6 +176,7 @@ enum {
|
||||||
enum {
|
enum {
|
||||||
IF_LINK_MODE_DEFAULT,
|
IF_LINK_MODE_DEFAULT,
|
||||||
IF_LINK_MODE_DORMANT, /* limit upward transition to dormant */
|
IF_LINK_MODE_DORMANT, /* limit upward transition to dormant */
|
||||||
|
IF_LINK_MODE_TESTING, /* limit upward transition to testing */
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -120,6 +120,7 @@ enum {
|
||||||
IFLA_BRIDGE_MODE,
|
IFLA_BRIDGE_MODE,
|
||||||
IFLA_BRIDGE_VLAN_INFO,
|
IFLA_BRIDGE_VLAN_INFO,
|
||||||
IFLA_BRIDGE_VLAN_TUNNEL_INFO,
|
IFLA_BRIDGE_VLAN_TUNNEL_INFO,
|
||||||
|
IFLA_BRIDGE_MRP,
|
||||||
__IFLA_BRIDGE_MAX,
|
__IFLA_BRIDGE_MAX,
|
||||||
};
|
};
|
||||||
#define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1)
|
#define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1)
|
||||||
|
|
@ -157,6 +158,47 @@ struct bridge_vlan_xstats {
|
||||||
__u32 pad2;
|
__u32 pad2;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
IFLA_BRIDGE_MRP_UNSPEC,
|
||||||
|
IFLA_BRIDGE_MRP_INSTANCE,
|
||||||
|
IFLA_BRIDGE_MRP_PORT_STATE,
|
||||||
|
IFLA_BRIDGE_MRP_PORT_ROLE,
|
||||||
|
IFLA_BRIDGE_MRP_RING_STATE,
|
||||||
|
IFLA_BRIDGE_MRP_RING_ROLE,
|
||||||
|
IFLA_BRIDGE_MRP_START_TEST,
|
||||||
|
__IFLA_BRIDGE_MRP_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct br_mrp_instance {
|
||||||
|
__u32 ring_id;
|
||||||
|
__u32 p_ifindex;
|
||||||
|
__u32 s_ifindex;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct br_mrp_port_role {
|
||||||
|
__u32 ring_id;
|
||||||
|
__u32 role;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct br_mrp_ring_state {
|
||||||
|
__u32 ring_id;
|
||||||
|
__u32 ring_state;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct br_mrp_ring_role {
|
||||||
|
__u32 ring_id;
|
||||||
|
__u32 ring_role;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct br_mrp_start_test {
|
||||||
|
__u32 ring_id;
|
||||||
|
__u32 interval;
|
||||||
|
__u32 max_miss;
|
||||||
|
__u32 period;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define IFLA_BRIDGE_MRP_MAX (__IFLA_BRIDGE_MRP_MAX - 1)
|
||||||
|
|
||||||
struct bridge_stp_xstats {
|
struct bridge_stp_xstats {
|
||||||
__u64 transition_blk;
|
__u64 transition_blk;
|
||||||
__u64 transition_fwd;
|
__u64 transition_fwd;
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,7 @@
|
||||||
#define ETH_P_PREAUTH 0x88C7 /* 802.11 Preauthentication */
|
#define ETH_P_PREAUTH 0x88C7 /* 802.11 Preauthentication */
|
||||||
#define ETH_P_TIPC 0x88CA /* TIPC */
|
#define ETH_P_TIPC 0x88CA /* TIPC */
|
||||||
#define ETH_P_LLDP 0x88CC /* Link Layer Discovery Protocol */
|
#define ETH_P_LLDP 0x88CC /* Link Layer Discovery Protocol */
|
||||||
|
#define ETH_P_MRP 0x88E3 /* Media Redundancy Protocol */
|
||||||
#define ETH_P_MACSEC 0x88E5 /* 802.1ae MACsec */
|
#define ETH_P_MACSEC 0x88E5 /* 802.1ae MACsec */
|
||||||
#define ETH_P_8021AH 0x88E7 /* 802.1ah Backbone Service Tag */
|
#define ETH_P_8021AH 0x88E7 /* 802.1ah Backbone Service Tag */
|
||||||
#define ETH_P_MVRP 0x88F5 /* 802.1Q MVRP */
|
#define ETH_P_MVRP 0x88F5 /* 802.1Q MVRP */
|
||||||
|
|
|
||||||
|
|
@ -341,6 +341,7 @@ enum {
|
||||||
IFLA_BRPORT_NEIGH_SUPPRESS,
|
IFLA_BRPORT_NEIGH_SUPPRESS,
|
||||||
IFLA_BRPORT_ISOLATED,
|
IFLA_BRPORT_ISOLATED,
|
||||||
IFLA_BRPORT_BACKUP_PORT,
|
IFLA_BRPORT_BACKUP_PORT,
|
||||||
|
IFLA_BRPORT_MRP_RING_OPEN,
|
||||||
__IFLA_BRPORT_MAX
|
__IFLA_BRPORT_MAX
|
||||||
};
|
};
|
||||||
#define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1)
|
#define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1)
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,7 @@ enum {
|
||||||
INET_DIAG_BC_MARK_COND,
|
INET_DIAG_BC_MARK_COND,
|
||||||
INET_DIAG_BC_S_EQ,
|
INET_DIAG_BC_S_EQ,
|
||||||
INET_DIAG_BC_D_EQ,
|
INET_DIAG_BC_D_EQ,
|
||||||
|
INET_DIAG_BC_CGROUP_COND, /* u64 cgroup v2 ID */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct inet_diag_hostcond {
|
struct inet_diag_hostcond {
|
||||||
|
|
@ -157,6 +158,7 @@ enum {
|
||||||
INET_DIAG_MD5SIG,
|
INET_DIAG_MD5SIG,
|
||||||
INET_DIAG_ULP_INFO,
|
INET_DIAG_ULP_INFO,
|
||||||
INET_DIAG_SK_BPF_STORAGES,
|
INET_DIAG_SK_BPF_STORAGES,
|
||||||
|
INET_DIAG_CGROUP_ID,
|
||||||
__INET_DIAG_MAX,
|
__INET_DIAG_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
|
||||||
|
#ifndef _MPTCP_H
|
||||||
|
#define _MPTCP_H
|
||||||
|
|
||||||
|
#include <linux/const.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
#define MPTCP_SUBFLOW_FLAG_MCAP_REM _BITUL(0)
|
||||||
|
#define MPTCP_SUBFLOW_FLAG_MCAP_LOC _BITUL(1)
|
||||||
|
#define MPTCP_SUBFLOW_FLAG_JOIN_REM _BITUL(2)
|
||||||
|
#define MPTCP_SUBFLOW_FLAG_JOIN_LOC _BITUL(3)
|
||||||
|
#define MPTCP_SUBFLOW_FLAG_BKUP_REM _BITUL(4)
|
||||||
|
#define MPTCP_SUBFLOW_FLAG_BKUP_LOC _BITUL(5)
|
||||||
|
#define MPTCP_SUBFLOW_FLAG_FULLY_ESTABLISHED _BITUL(6)
|
||||||
|
#define MPTCP_SUBFLOW_FLAG_CONNECTED _BITUL(7)
|
||||||
|
#define MPTCP_SUBFLOW_FLAG_MAPVALID _BITUL(8)
|
||||||
|
|
||||||
|
enum {
|
||||||
|
MPTCP_SUBFLOW_ATTR_UNSPEC,
|
||||||
|
MPTCP_SUBFLOW_ATTR_TOKEN_REM,
|
||||||
|
MPTCP_SUBFLOW_ATTR_TOKEN_LOC,
|
||||||
|
MPTCP_SUBFLOW_ATTR_RELWRITE_SEQ,
|
||||||
|
MPTCP_SUBFLOW_ATTR_MAP_SEQ,
|
||||||
|
MPTCP_SUBFLOW_ATTR_MAP_SFSEQ,
|
||||||
|
MPTCP_SUBFLOW_ATTR_SSN_OFFSET,
|
||||||
|
MPTCP_SUBFLOW_ATTR_MAP_DATALEN,
|
||||||
|
MPTCP_SUBFLOW_ATTR_FLAGS,
|
||||||
|
MPTCP_SUBFLOW_ATTR_ID_REM,
|
||||||
|
MPTCP_SUBFLOW_ATTR_ID_LOC,
|
||||||
|
MPTCP_SUBFLOW_ATTR_PAD,
|
||||||
|
__MPTCP_SUBFLOW_ATTR_MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MPTCP_SUBFLOW_ATTR_MAX (__MPTCP_SUBFLOW_ATTR_MAX - 1)
|
||||||
|
|
||||||
|
/* netlink interface */
|
||||||
|
#define MPTCP_PM_NAME "mptcp_pm"
|
||||||
|
#define MPTCP_PM_CMD_GRP_NAME "mptcp_pm_cmds"
|
||||||
|
#define MPTCP_PM_VER 0x1
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ATTR types defined for MPTCP
|
||||||
|
*/
|
||||||
|
enum {
|
||||||
|
MPTCP_PM_ATTR_UNSPEC,
|
||||||
|
|
||||||
|
MPTCP_PM_ATTR_ADDR, /* nested address */
|
||||||
|
MPTCP_PM_ATTR_RCV_ADD_ADDRS, /* u32 */
|
||||||
|
MPTCP_PM_ATTR_SUBFLOWS, /* u32 */
|
||||||
|
|
||||||
|
__MPTCP_PM_ATTR_MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MPTCP_PM_ATTR_MAX (__MPTCP_PM_ATTR_MAX - 1)
|
||||||
|
|
||||||
|
enum {
|
||||||
|
MPTCP_PM_ADDR_ATTR_UNSPEC,
|
||||||
|
|
||||||
|
MPTCP_PM_ADDR_ATTR_FAMILY, /* u16 */
|
||||||
|
MPTCP_PM_ADDR_ATTR_ID, /* u8 */
|
||||||
|
MPTCP_PM_ADDR_ATTR_ADDR4, /* struct in_addr */
|
||||||
|
MPTCP_PM_ADDR_ATTR_ADDR6, /* struct in6_addr */
|
||||||
|
MPTCP_PM_ADDR_ATTR_PORT, /* u16 */
|
||||||
|
MPTCP_PM_ADDR_ATTR_FLAGS, /* u32 */
|
||||||
|
MPTCP_PM_ADDR_ATTR_IF_IDX, /* s32 */
|
||||||
|
|
||||||
|
__MPTCP_PM_ADDR_ATTR_MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MPTCP_PM_ADDR_ATTR_MAX (__MPTCP_PM_ADDR_ATTR_MAX - 1)
|
||||||
|
|
||||||
|
#define MPTCP_PM_ADDR_FLAG_SIGNAL (1 << 0)
|
||||||
|
#define MPTCP_PM_ADDR_FLAG_SUBFLOW (1 << 1)
|
||||||
|
#define MPTCP_PM_ADDR_FLAG_BACKUP (1 << 2)
|
||||||
|
|
||||||
|
enum {
|
||||||
|
MPTCP_PM_CMD_UNSPEC,
|
||||||
|
|
||||||
|
MPTCP_PM_CMD_ADD_ADDR,
|
||||||
|
MPTCP_PM_CMD_DEL_ADDR,
|
||||||
|
MPTCP_PM_CMD_GET_ADDR,
|
||||||
|
MPTCP_PM_CMD_FLUSH_ADDRS,
|
||||||
|
MPTCP_PM_CMD_SET_LIMITS,
|
||||||
|
MPTCP_PM_CMD_GET_LIMITS,
|
||||||
|
|
||||||
|
__MPTCP_PM_CMD_AFTER_LAST
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _MPTCP_H */
|
||||||
|
|
@ -245,4 +245,107 @@ struct nla_bitfield32 {
|
||||||
__u32 selector;
|
__u32 selector;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* policy descriptions - it's specific to each family how this is used
|
||||||
|
* Normally, it should be retrieved via a dump inside another attribute
|
||||||
|
* specifying where it applies.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* enum netlink_attribute_type - type of an attribute
|
||||||
|
* @NL_ATTR_TYPE_INVALID: unused
|
||||||
|
* @NL_ATTR_TYPE_FLAG: flag attribute (present/not present)
|
||||||
|
* @NL_ATTR_TYPE_U8: 8-bit unsigned attribute
|
||||||
|
* @NL_ATTR_TYPE_U16: 16-bit unsigned attribute
|
||||||
|
* @NL_ATTR_TYPE_U32: 32-bit unsigned attribute
|
||||||
|
* @NL_ATTR_TYPE_U64: 64-bit unsigned attribute
|
||||||
|
* @NL_ATTR_TYPE_S8: 8-bit signed attribute
|
||||||
|
* @NL_ATTR_TYPE_S16: 16-bit signed attribute
|
||||||
|
* @NL_ATTR_TYPE_S32: 32-bit signed attribute
|
||||||
|
* @NL_ATTR_TYPE_S64: 64-bit signed attribute
|
||||||
|
* @NL_ATTR_TYPE_BINARY: binary data, min/max length may be specified
|
||||||
|
* @NL_ATTR_TYPE_STRING: string, min/max length may be specified
|
||||||
|
* @NL_ATTR_TYPE_NUL_STRING: NUL-terminated string,
|
||||||
|
* min/max length may be specified
|
||||||
|
* @NL_ATTR_TYPE_NESTED: nested, i.e. the content of this attribute
|
||||||
|
* consists of sub-attributes. The nested policy and maxtype
|
||||||
|
* inside may be specified.
|
||||||
|
* @NL_ATTR_TYPE_NESTED_ARRAY: nested array, i.e. the content of this
|
||||||
|
* attribute contains sub-attributes whose type is irrelevant
|
||||||
|
* (just used to separate the array entries) and each such array
|
||||||
|
* entry has attributes again, the policy for those inner ones
|
||||||
|
* and the corresponding maxtype may be specified.
|
||||||
|
* @NL_ATTR_TYPE_BITFIELD32: &struct nla_bitfield32 attribute
|
||||||
|
*/
|
||||||
|
enum netlink_attribute_type {
|
||||||
|
NL_ATTR_TYPE_INVALID,
|
||||||
|
|
||||||
|
NL_ATTR_TYPE_FLAG,
|
||||||
|
|
||||||
|
NL_ATTR_TYPE_U8,
|
||||||
|
NL_ATTR_TYPE_U16,
|
||||||
|
NL_ATTR_TYPE_U32,
|
||||||
|
NL_ATTR_TYPE_U64,
|
||||||
|
|
||||||
|
NL_ATTR_TYPE_S8,
|
||||||
|
NL_ATTR_TYPE_S16,
|
||||||
|
NL_ATTR_TYPE_S32,
|
||||||
|
NL_ATTR_TYPE_S64,
|
||||||
|
|
||||||
|
NL_ATTR_TYPE_BINARY,
|
||||||
|
NL_ATTR_TYPE_STRING,
|
||||||
|
NL_ATTR_TYPE_NUL_STRING,
|
||||||
|
|
||||||
|
NL_ATTR_TYPE_NESTED,
|
||||||
|
NL_ATTR_TYPE_NESTED_ARRAY,
|
||||||
|
|
||||||
|
NL_ATTR_TYPE_BITFIELD32,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* enum netlink_policy_type_attr - policy type attributes
|
||||||
|
* @NL_POLICY_TYPE_ATTR_UNSPEC: unused
|
||||||
|
* @NL_POLICY_TYPE_ATTR_TYPE: type of the attribute,
|
||||||
|
* &enum netlink_attribute_type (U32)
|
||||||
|
* @NL_POLICY_TYPE_ATTR_MIN_VALUE_S: minimum value for signed
|
||||||
|
* integers (S64)
|
||||||
|
* @NL_POLICY_TYPE_ATTR_MAX_VALUE_S: maximum value for signed
|
||||||
|
* integers (S64)
|
||||||
|
* @NL_POLICY_TYPE_ATTR_MIN_VALUE_U: minimum value for unsigned
|
||||||
|
* integers (U64)
|
||||||
|
* @NL_POLICY_TYPE_ATTR_MAX_VALUE_U: maximum value for unsigned
|
||||||
|
* integers (U64)
|
||||||
|
* @NL_POLICY_TYPE_ATTR_MIN_LENGTH: minimum length for binary
|
||||||
|
* attributes, no minimum if not given (U32)
|
||||||
|
* @NL_POLICY_TYPE_ATTR_MAX_LENGTH: maximum length for binary
|
||||||
|
* attributes, no maximum if not given (U32)
|
||||||
|
* @NL_POLICY_TYPE_ATTR_POLICY_IDX: sub policy for nested and
|
||||||
|
* nested array types (U32)
|
||||||
|
* @NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE: maximum sub policy
|
||||||
|
* attribute for nested and nested array types, this can
|
||||||
|
* in theory be < the size of the policy pointed to by
|
||||||
|
* the index, if limited inside the nesting (U32)
|
||||||
|
* @NL_POLICY_TYPE_ATTR_BITFIELD32_MASK: valid mask for the
|
||||||
|
* bitfield32 type (U32)
|
||||||
|
* @NL_POLICY_TYPE_ATTR_PAD: pad attribute for 64-bit alignment
|
||||||
|
*/
|
||||||
|
enum netlink_policy_type_attr {
|
||||||
|
NL_POLICY_TYPE_ATTR_UNSPEC,
|
||||||
|
NL_POLICY_TYPE_ATTR_TYPE,
|
||||||
|
NL_POLICY_TYPE_ATTR_MIN_VALUE_S,
|
||||||
|
NL_POLICY_TYPE_ATTR_MAX_VALUE_S,
|
||||||
|
NL_POLICY_TYPE_ATTR_MIN_VALUE_U,
|
||||||
|
NL_POLICY_TYPE_ATTR_MAX_VALUE_U,
|
||||||
|
NL_POLICY_TYPE_ATTR_MIN_LENGTH,
|
||||||
|
NL_POLICY_TYPE_ATTR_MAX_LENGTH,
|
||||||
|
NL_POLICY_TYPE_ATTR_POLICY_IDX,
|
||||||
|
NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE,
|
||||||
|
NL_POLICY_TYPE_ATTR_BITFIELD32_MASK,
|
||||||
|
NL_POLICY_TYPE_ATTR_PAD,
|
||||||
|
|
||||||
|
/* keep last */
|
||||||
|
__NL_POLICY_TYPE_ATTR_MAX,
|
||||||
|
NL_POLICY_TYPE_ATTR_MAX = __NL_POLICY_TYPE_ATTR_MAX - 1
|
||||||
|
};
|
||||||
|
|
||||||
#endif /* __LINUX_NETLINK_H */
|
#endif /* __LINUX_NETLINK_H */
|
||||||
|
|
|
||||||
|
|
@ -134,6 +134,7 @@ enum tca_id {
|
||||||
TCA_ID_CTINFO,
|
TCA_ID_CTINFO,
|
||||||
TCA_ID_MPLS,
|
TCA_ID_MPLS,
|
||||||
TCA_ID_CT,
|
TCA_ID_CT,
|
||||||
|
TCA_ID_GATE,
|
||||||
/* other actions go here */
|
/* other actions go here */
|
||||||
__TCA_ID_MAX = 255
|
__TCA_ID_MAX = 255
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -913,6 +913,10 @@ enum {
|
||||||
|
|
||||||
TCA_FQ_TIMER_SLACK, /* timer slack */
|
TCA_FQ_TIMER_SLACK, /* timer slack */
|
||||||
|
|
||||||
|
TCA_FQ_HORIZON, /* time horizon in us */
|
||||||
|
|
||||||
|
TCA_FQ_HORIZON_DROP, /* drop packets beyond horizon, or cap their EDT */
|
||||||
|
|
||||||
__TCA_FQ_MAX
|
__TCA_FQ_MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -932,6 +936,8 @@ struct tc_fq_qd_stats {
|
||||||
__u32 throttled_flows;
|
__u32 throttled_flows;
|
||||||
__u32 unthrottle_latency_ns;
|
__u32 unthrottle_latency_ns;
|
||||||
__u64 ce_mark; /* packets above ce_threshold */
|
__u64 ce_mark; /* packets above ce_threshold */
|
||||||
|
__u64 horizon_drops;
|
||||||
|
__u64 horizon_caps;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Heavy-Hitter Filter */
|
/* Heavy-Hitter Filter */
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
|
||||||
|
/*
|
||||||
|
* IPv6 RPL-SR implementation
|
||||||
|
*
|
||||||
|
* Author:
|
||||||
|
* (C) 2020 Alexander Aring <alex.aring@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _LINUX_RPL_H
|
||||||
|
#define _LINUX_RPL_H
|
||||||
|
|
||||||
|
#include <asm/byteorder.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/in6.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RPL SR Header
|
||||||
|
*/
|
||||||
|
struct ipv6_rpl_sr_hdr {
|
||||||
|
__u8 nexthdr;
|
||||||
|
__u8 hdrlen;
|
||||||
|
__u8 type;
|
||||||
|
__u8 segments_left;
|
||||||
|
#if defined(__LITTLE_ENDIAN_BITFIELD)
|
||||||
|
__u32 cmpre:4,
|
||||||
|
cmpri:4,
|
||||||
|
reserved:4,
|
||||||
|
pad:4,
|
||||||
|
reserved1:16;
|
||||||
|
#elif defined(__BIG_ENDIAN_BITFIELD)
|
||||||
|
__u32 reserved:20,
|
||||||
|
pad:4,
|
||||||
|
cmpri:4,
|
||||||
|
cmpre:4;
|
||||||
|
#else
|
||||||
|
#error "Please fix <asm/byteorder.h>"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
union {
|
||||||
|
struct in6_addr addr[0];
|
||||||
|
__u8 data[0];
|
||||||
|
} segments;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
#define rpl_segaddr segments.addr
|
||||||
|
#define rpl_segdata segments.data
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
|
||||||
|
/*
|
||||||
|
* IPv6 RPL-SR implementation
|
||||||
|
*
|
||||||
|
* Author:
|
||||||
|
* (C) 2020 Alexander Aring <alex.aring@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _LINUX_RPL_IPTUNNEL_H
|
||||||
|
#define _LINUX_RPL_IPTUNNEL_H
|
||||||
|
|
||||||
|
enum {
|
||||||
|
RPL_IPTUNNEL_UNSPEC,
|
||||||
|
RPL_IPTUNNEL_SRH,
|
||||||
|
__RPL_IPTUNNEL_MAX,
|
||||||
|
};
|
||||||
|
#define RPL_IPTUNNEL_MAX (__RPL_IPTUNNEL_MAX - 1)
|
||||||
|
|
||||||
|
#define RPL_IPTUNNEL_SRH_SIZE(srh) (((srh)->hdrlen + 1) << 3)
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
|
||||||
|
/* Copyright 2020 NXP */
|
||||||
|
|
||||||
|
#ifndef __LINUX_TC_GATE_H
|
||||||
|
#define __LINUX_TC_GATE_H
|
||||||
|
|
||||||
|
#include <linux/pkt_cls.h>
|
||||||
|
|
||||||
|
struct tc_gate {
|
||||||
|
tc_gen;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
TCA_GATE_ENTRY_UNSPEC,
|
||||||
|
TCA_GATE_ENTRY_INDEX,
|
||||||
|
TCA_GATE_ENTRY_GATE,
|
||||||
|
TCA_GATE_ENTRY_INTERVAL,
|
||||||
|
TCA_GATE_ENTRY_IPV,
|
||||||
|
TCA_GATE_ENTRY_MAX_OCTETS,
|
||||||
|
__TCA_GATE_ENTRY_MAX,
|
||||||
|
};
|
||||||
|
#define TCA_GATE_ENTRY_MAX (__TCA_GATE_ENTRY_MAX - 1)
|
||||||
|
|
||||||
|
enum {
|
||||||
|
TCA_GATE_ONE_ENTRY_UNSPEC,
|
||||||
|
TCA_GATE_ONE_ENTRY,
|
||||||
|
__TCA_GATE_ONE_ENTRY_MAX,
|
||||||
|
};
|
||||||
|
#define TCA_GATE_ONE_ENTRY_MAX (__TCA_GATE_ONE_ENTRY_MAX - 1)
|
||||||
|
|
||||||
|
enum {
|
||||||
|
TCA_GATE_UNSPEC,
|
||||||
|
TCA_GATE_TM,
|
||||||
|
TCA_GATE_PARMS,
|
||||||
|
TCA_GATE_PAD,
|
||||||
|
TCA_GATE_PRIORITY,
|
||||||
|
TCA_GATE_ENTRY_LIST,
|
||||||
|
TCA_GATE_BASE_TIME,
|
||||||
|
TCA_GATE_CYCLE_TIME,
|
||||||
|
TCA_GATE_CYCLE_TIME_EXT,
|
||||||
|
TCA_GATE_FLAGS,
|
||||||
|
TCA_GATE_CLOCKID,
|
||||||
|
__TCA_GATE_MAX,
|
||||||
|
};
|
||||||
|
#define TCA_GATE_MAX (__TCA_GATE_MAX - 1)
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -302,7 +302,9 @@ int get_real_family(int rtm_type, int rtm_family);
|
||||||
int cmd_exec(const char *cmd, char **argv, bool do_fork,
|
int cmd_exec(const char *cmd, char **argv, bool do_fork,
|
||||||
int (*setup)(void *), void *arg);
|
int (*setup)(void *), void *arg);
|
||||||
int make_path(const char *path, mode_t mode);
|
int make_path(const char *path, mode_t mode);
|
||||||
char *find_cgroup2_mount(void);
|
char *find_cgroup2_mount(bool do_mount);
|
||||||
|
__u64 get_cgroup2_id(const char *path);
|
||||||
|
char *get_cgroup2_path(__u64 id, bool full);
|
||||||
int get_command_name(const char *pid, char *comm, size_t len);
|
int get_command_name(const char *pid, char *comm, size_t len);
|
||||||
|
|
||||||
int get_rtnl_link_stats_rta(struct rtnl_link_stats64 *stats64,
|
int get_rtnl_link_stats_rta(struct rtnl_link_stats64 *stats64,
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ IPOBJ=ip.o ipaddress.o ipaddrlabel.o iproute.o iprule.o ipnetns.o \
|
||||||
iplink_bridge.o iplink_bridge_slave.o ipfou.o iplink_ipvlan.o \
|
iplink_bridge.o iplink_bridge_slave.o ipfou.o iplink_ipvlan.o \
|
||||||
iplink_geneve.o iplink_vrf.o iproute_lwtunnel.o ipmacsec.o ipila.o \
|
iplink_geneve.o iplink_vrf.o iproute_lwtunnel.o ipmacsec.o ipila.o \
|
||||||
ipvrf.o iplink_xstats.o ipseg6.o iplink_netdevsim.o iplink_rmnet.o \
|
ipvrf.o iplink_xstats.o ipseg6.o iplink_netdevsim.o iplink_rmnet.o \
|
||||||
ipnexthop.o
|
ipnexthop.o ipmptcp.o
|
||||||
|
|
||||||
RTMONOBJ=rtmon.o
|
RTMONOBJ=rtmon.o
|
||||||
|
|
||||||
|
|
|
||||||
3
ip/ip.c
3
ip/ip.c
|
|
@ -51,7 +51,7 @@ static void usage(void)
|
||||||
"where OBJECT := { link | address | addrlabel | route | rule | neigh | ntable |\n"
|
"where OBJECT := { link | address | addrlabel | route | rule | neigh | ntable |\n"
|
||||||
" tunnel | tuntap | maddress | mroute | mrule | monitor | xfrm |\n"
|
" tunnel | tuntap | maddress | mroute | mrule | monitor | xfrm |\n"
|
||||||
" netns | l2tp | fou | macsec | tcp_metrics | token | netconf | ila |\n"
|
" netns | l2tp | fou | macsec | tcp_metrics | token | netconf | ila |\n"
|
||||||
" vrf | sr | nexthop }\n"
|
" vrf | sr | nexthop | mptcp }\n"
|
||||||
" OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] | -r[esolve] |\n"
|
" OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] | -r[esolve] |\n"
|
||||||
" -h[uman-readable] | -iec | -j[son] | -p[retty] |\n"
|
" -h[uman-readable] | -iec | -j[son] | -p[retty] |\n"
|
||||||
" -f[amily] { inet | inet6 | mpls | bridge | link } |\n"
|
" -f[amily] { inet | inet6 | mpls | bridge | link } |\n"
|
||||||
|
|
@ -103,6 +103,7 @@ static const struct cmd {
|
||||||
{ "vrf", do_ipvrf},
|
{ "vrf", do_ipvrf},
|
||||||
{ "sr", do_seg6 },
|
{ "sr", do_seg6 },
|
||||||
{ "nexthop", do_ipnh },
|
{ "nexthop", do_ipnh },
|
||||||
|
{ "mptcp", do_mptcp },
|
||||||
{ "help", do_help },
|
{ "help", do_help },
|
||||||
{ 0 }
|
{ 0 }
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,7 @@ void vrf_reset(void);
|
||||||
int netns_identify_pid(const char *pidstr, char *name, int len);
|
int netns_identify_pid(const char *pidstr, char *name, int len);
|
||||||
int do_seg6(int argc, char **argv);
|
int do_seg6(int argc, char **argv);
|
||||||
int do_ipnh(int argc, char **argv);
|
int do_ipnh(int argc, char **argv);
|
||||||
|
int do_mptcp(int argc, char **argv);
|
||||||
|
|
||||||
int iplink_get(char *name, __u32 filt_mask);
|
int iplink_get(char *name, __u32 filt_mask);
|
||||||
int iplink_ifla_xstats(int argc, char **argv);
|
int iplink_ifla_xstats(int argc, char **argv);
|
||||||
|
|
|
||||||
112
ip/ipaddress.c
112
ip/ipaddress.c
|
|
@ -1233,52 +1233,63 @@ static unsigned int get_ifa_flags(struct ifaddrmsg *ifa,
|
||||||
ifa->ifa_flags;
|
ifa->ifa_flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Mapping from argument to address flag mask */
|
/* Mapping from argument to address flag mask and attributes */
|
||||||
static const struct {
|
static const struct ifa_flag_data_t {
|
||||||
const char *name;
|
const char *name;
|
||||||
unsigned long value;
|
unsigned long mask;
|
||||||
} ifa_flag_names[] = {
|
bool readonly;
|
||||||
{ "secondary", IFA_F_SECONDARY },
|
bool v6only;
|
||||||
{ "temporary", IFA_F_SECONDARY },
|
} ifa_flag_data[] = {
|
||||||
{ "nodad", IFA_F_NODAD },
|
{ .name = "secondary", .mask = IFA_F_SECONDARY, .readonly = true, .v6only = false},
|
||||||
{ "optimistic", IFA_F_OPTIMISTIC },
|
{ .name = "temporary", .mask = IFA_F_SECONDARY, .readonly = true, .v6only = false},
|
||||||
{ "dadfailed", IFA_F_DADFAILED },
|
{ .name = "nodad", .mask = IFA_F_NODAD, .readonly = false, .v6only = true},
|
||||||
{ "home", IFA_F_HOMEADDRESS },
|
{ .name = "optimistic", .mask = IFA_F_OPTIMISTIC, .readonly = false, .v6only = true},
|
||||||
{ "deprecated", IFA_F_DEPRECATED },
|
{ .name = "dadfailed", .mask = IFA_F_DADFAILED, .readonly = true, .v6only = true},
|
||||||
{ "tentative", IFA_F_TENTATIVE },
|
{ .name = "home", .mask = IFA_F_HOMEADDRESS, .readonly = false, .v6only = true},
|
||||||
{ "permanent", IFA_F_PERMANENT },
|
{ .name = "deprecated", .mask = IFA_F_DEPRECATED, .readonly = true, .v6only = true},
|
||||||
{ "mngtmpaddr", IFA_F_MANAGETEMPADDR },
|
{ .name = "tentative", .mask = IFA_F_TENTATIVE, .readonly = true, .v6only = true},
|
||||||
{ "noprefixroute", IFA_F_NOPREFIXROUTE },
|
{ .name = "permanent", .mask = IFA_F_PERMANENT, .readonly = true, .v6only = true},
|
||||||
{ "autojoin", IFA_F_MCAUTOJOIN },
|
{ .name = "mngtmpaddr", .mask = IFA_F_MANAGETEMPADDR, .readonly = false, .v6only = true},
|
||||||
{ "stable-privacy", IFA_F_STABLE_PRIVACY },
|
{ .name = "noprefixroute", .mask = IFA_F_NOPREFIXROUTE, .readonly = false, .v6only = true},
|
||||||
|
{ .name = "autojoin", .mask = IFA_F_MCAUTOJOIN, .readonly = false, .v6only = true},
|
||||||
|
{ .name = "stable-privacy", .mask = IFA_F_STABLE_PRIVACY, .readonly = true, .v6only = true},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Returns a pointer to the data structure for a particular interface flag, or null if no flag could be found */
|
||||||
|
static const struct ifa_flag_data_t* lookup_flag_data_by_name(const char* flag_name) {
|
||||||
|
for (int i = 0; i < ARRAY_SIZE(ifa_flag_data); ++i) {
|
||||||
|
if (strcmp(flag_name, ifa_flag_data[i].name) == 0)
|
||||||
|
return &ifa_flag_data[i];
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void print_ifa_flags(FILE *fp, const struct ifaddrmsg *ifa,
|
static void print_ifa_flags(FILE *fp, const struct ifaddrmsg *ifa,
|
||||||
unsigned int flags)
|
unsigned int flags)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(ifa_flag_names); i++) {
|
for (i = 0; i < ARRAY_SIZE(ifa_flag_data); i++) {
|
||||||
unsigned long mask = ifa_flag_names[i].value;
|
const struct ifa_flag_data_t* flag_data = &ifa_flag_data[i];
|
||||||
|
|
||||||
if (mask == IFA_F_PERMANENT) {
|
if (flag_data->mask == IFA_F_PERMANENT) {
|
||||||
if (!(flags & mask))
|
if (!(flags & flag_data->mask))
|
||||||
print_bool(PRINT_ANY,
|
print_bool(PRINT_ANY,
|
||||||
"dynamic", "dynamic ", true);
|
"dynamic", "dynamic ", true);
|
||||||
} else if (flags & mask) {
|
} else if (flags & flag_data->mask) {
|
||||||
if (mask == IFA_F_SECONDARY &&
|
if (flag_data->mask == IFA_F_SECONDARY &&
|
||||||
ifa->ifa_family == AF_INET6) {
|
ifa->ifa_family == AF_INET6) {
|
||||||
print_bool(PRINT_ANY,
|
print_bool(PRINT_ANY,
|
||||||
"temporary", "temporary ", true);
|
"temporary", "temporary ", true);
|
||||||
} else {
|
} else {
|
||||||
print_string(PRINT_FP, NULL,
|
print_string(PRINT_FP, NULL,
|
||||||
"%s ", ifa_flag_names[i].name);
|
"%s ", flag_data->name);
|
||||||
print_bool(PRINT_JSON,
|
print_bool(PRINT_JSON,
|
||||||
ifa_flag_names[i].name, NULL, true);
|
flag_data->name, NULL, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
flags &= ~mask;
|
flags &= ~flag_data->mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags) {
|
if (flags) {
|
||||||
|
|
@ -1297,7 +1308,6 @@ static void print_ifa_flags(FILE *fp, const struct ifaddrmsg *ifa,
|
||||||
static int get_filter(const char *arg)
|
static int get_filter(const char *arg)
|
||||||
{
|
{
|
||||||
bool inv = false;
|
bool inv = false;
|
||||||
unsigned int i;
|
|
||||||
|
|
||||||
if (arg[0] == '-') {
|
if (arg[0] == '-') {
|
||||||
inv = true;
|
inv = true;
|
||||||
|
|
@ -1313,18 +1323,16 @@ static int get_filter(const char *arg)
|
||||||
arg = "secondary";
|
arg = "secondary";
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(ifa_flag_names); i++) {
|
const struct ifa_flag_data_t* flag_data = lookup_flag_data_by_name(arg);
|
||||||
if (strcmp(arg, ifa_flag_names[i].name))
|
if (flag_data == NULL)
|
||||||
continue;
|
return -1;
|
||||||
|
|
||||||
if (inv)
|
if (inv)
|
||||||
filter.flags &= ~ifa_flag_names[i].value;
|
filter.flags &= ~flag_data->mask;
|
||||||
else
|
else
|
||||||
filter.flags |= ifa_flag_names[i].value;
|
filter.flags |= flag_data->mask;
|
||||||
filter.flagmask |= ifa_flag_names[i].value;
|
filter.flagmask |= flag_data->mask;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ifa_label_match_rta(int ifindex, const struct rtattr *rta)
|
static int ifa_label_match_rta(int ifindex, const struct rtattr *rta)
|
||||||
|
|
@ -2330,25 +2338,15 @@ static int ipaddr_modify(int cmd, int flags, int argc, char **argv)
|
||||||
preferred_lftp = *argv;
|
preferred_lftp = *argv;
|
||||||
if (set_lifetime(&preferred_lft, *argv))
|
if (set_lifetime(&preferred_lft, *argv))
|
||||||
invarg("preferred_lft value", *argv);
|
invarg("preferred_lft value", *argv);
|
||||||
} else if (strcmp(*argv, "home") == 0) {
|
} else if (lookup_flag_data_by_name(*argv)) {
|
||||||
if (req.ifa.ifa_family == AF_INET6)
|
const struct ifa_flag_data_t* flag_data = lookup_flag_data_by_name(*argv);
|
||||||
ifa_flags |= IFA_F_HOMEADDRESS;
|
if (flag_data->readonly) {
|
||||||
else
|
fprintf(stderr, "Warning: %s option is not mutable from userspace\n", flag_data->name);
|
||||||
fprintf(stderr, "Warning: home option can be set only for IPv6 addresses\n");
|
} else if (flag_data->v6only && req.ifa.ifa_family != AF_INET6) {
|
||||||
} else if (strcmp(*argv, "nodad") == 0) {
|
fprintf(stderr, "Warning: %s option can be set only for IPv6 addresses\n", flag_data->name);
|
||||||
if (req.ifa.ifa_family == AF_INET6)
|
} else {
|
||||||
ifa_flags |= IFA_F_NODAD;
|
ifa_flags |= flag_data->mask;
|
||||||
else
|
}
|
||||||
fprintf(stderr, "Warning: nodad option can be set only for IPv6 addresses\n");
|
|
||||||
} else if (strcmp(*argv, "mngtmpaddr") == 0) {
|
|
||||||
if (req.ifa.ifa_family == AF_INET6)
|
|
||||||
ifa_flags |= IFA_F_MANAGETEMPADDR;
|
|
||||||
else
|
|
||||||
fprintf(stderr, "Warning: mngtmpaddr option can be set only for IPv6 addresses\n");
|
|
||||||
} else if (strcmp(*argv, "noprefixroute") == 0) {
|
|
||||||
ifa_flags |= IFA_F_NOPREFIXROUTE;
|
|
||||||
} else if (strcmp(*argv, "autojoin") == 0) {
|
|
||||||
ifa_flags |= IFA_F_MCAUTOJOIN;
|
|
||||||
} else {
|
} else {
|
||||||
if (strcmp(*argv, "local") == 0)
|
if (strcmp(*argv, "local") == 0)
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ static const char * const validate_str[] = {
|
||||||
static const char * const offload_str[] = {
|
static const char * const offload_str[] = {
|
||||||
[MACSEC_OFFLOAD_OFF] = "off",
|
[MACSEC_OFFLOAD_OFF] = "off",
|
||||||
[MACSEC_OFFLOAD_PHY] = "phy",
|
[MACSEC_OFFLOAD_PHY] = "phy",
|
||||||
|
[MACSEC_OFFLOAD_MAC] = "mac",
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sci {
|
struct sci {
|
||||||
|
|
@ -98,7 +99,7 @@ static void ipmacsec_usage(void)
|
||||||
" ip macsec del DEV rx SCI sa { 0..3 }\n"
|
" ip macsec del DEV rx SCI sa { 0..3 }\n"
|
||||||
" ip macsec show\n"
|
" ip macsec show\n"
|
||||||
" ip macsec show DEV\n"
|
" ip macsec show DEV\n"
|
||||||
" ip macsec offload DEV [ off | phy ]\n"
|
" ip macsec offload DEV [ off | phy | mac ]\n"
|
||||||
"where OPTS := [ pn <u32> ] [ on | off ]\n"
|
"where OPTS := [ pn <u32> ] [ on | off ]\n"
|
||||||
" ID := 128-bit hex string\n"
|
" ID := 128-bit hex string\n"
|
||||||
" KEY := 128-bit or 256-bit hex string\n"
|
" KEY := 128-bit or 256-bit hex string\n"
|
||||||
|
|
@ -1219,6 +1220,15 @@ static void macsec_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
|
||||||
validate_to_str(val));
|
validate_to_str(val));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tb[IFLA_MACSEC_OFFLOAD]) {
|
||||||
|
__u8 val = rta_getattr_u8(tb[IFLA_MACSEC_OFFLOAD]);
|
||||||
|
|
||||||
|
print_string(PRINT_ANY,
|
||||||
|
"offload",
|
||||||
|
"offload %s ",
|
||||||
|
offload_to_str(val));
|
||||||
|
}
|
||||||
|
|
||||||
const char *inc_sci, *es, *replay;
|
const char *inc_sci, *es, *replay;
|
||||||
|
|
||||||
if (is_json_context()) {
|
if (is_json_context()) {
|
||||||
|
|
@ -1267,6 +1277,7 @@ static void usage(FILE *f)
|
||||||
" [ replay { on | off} window { 0..2^32-1 } ]\n"
|
" [ replay { on | off} window { 0..2^32-1 } ]\n"
|
||||||
" [ validate { strict | check | disabled } ]\n"
|
" [ validate { strict | check | disabled } ]\n"
|
||||||
" [ encodingsa { 0..3 } ]\n"
|
" [ encodingsa { 0..3 } ]\n"
|
||||||
|
" [ offload { mac | phy | off } ]\n"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1276,6 +1287,7 @@ static int macsec_parse_opt(struct link_util *lu, int argc, char **argv,
|
||||||
int ret;
|
int ret;
|
||||||
__u8 encoding_sa = 0xff;
|
__u8 encoding_sa = 0xff;
|
||||||
__u32 window = -1;
|
__u32 window = -1;
|
||||||
|
enum macsec_offload offload;
|
||||||
struct cipher_args cipher = {0};
|
struct cipher_args cipher = {0};
|
||||||
enum macsec_validation_type validate;
|
enum macsec_validation_type validate;
|
||||||
bool es = false, scb = false, send_sci = false;
|
bool es = false, scb = false, send_sci = false;
|
||||||
|
|
@ -1397,6 +1409,15 @@ static int macsec_parse_opt(struct link_util *lu, int argc, char **argv,
|
||||||
ret = get_an(&encoding_sa, *argv);
|
ret = get_an(&encoding_sa, *argv);
|
||||||
if (ret)
|
if (ret)
|
||||||
invarg("expected an { 0..3 }", *argv);
|
invarg("expected an { 0..3 }", *argv);
|
||||||
|
} else if (strcmp(*argv, "offload") == 0) {
|
||||||
|
NEXT_ARG();
|
||||||
|
ret = one_of("offload", *argv,
|
||||||
|
offload_str, ARRAY_SIZE(offload_str),
|
||||||
|
(int *)&offload);
|
||||||
|
if (ret != 0)
|
||||||
|
return ret;
|
||||||
|
addattr8(n, MACSEC_BUFLEN,
|
||||||
|
IFLA_MACSEC_OFFLOAD, offload);
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "macsec: unknown command \"%s\"?\n",
|
fprintf(stderr, "macsec: unknown command \"%s\"?\n",
|
||||||
*argv);
|
*argv);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,436 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <rt_names.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <linux/genetlink.h>
|
||||||
|
#include <linux/mptcp.h>
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
#include "ip_common.h"
|
||||||
|
#include "libgenl.h"
|
||||||
|
#include "json_print.h"
|
||||||
|
|
||||||
|
static void usage(void)
|
||||||
|
{
|
||||||
|
fprintf(stderr,
|
||||||
|
"Usage: ip mptcp endpoint add ADDRESS [ dev NAME ] [ id ID ]\n"
|
||||||
|
" [ FLAG-LIST ]\n"
|
||||||
|
" ip mptcp endpoint delete id ID\n"
|
||||||
|
" ip mptcp endpoint show [ id ID ]\n"
|
||||||
|
" ip mptcp endpoint flush\n"
|
||||||
|
" ip mptcp limits set [ subflows NR ] [ add_addr_accepted NR ]\n"
|
||||||
|
" ip mptcp limits show\n"
|
||||||
|
"FLAG-LIST := [ FLAG-LIST ] FLAG\n"
|
||||||
|
"FLAG := [ signal | subflow | backup ]\n");
|
||||||
|
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* netlink socket */
|
||||||
|
static struct rtnl_handle genl_rth = { .fd = -1 };
|
||||||
|
static int genl_family = -1;
|
||||||
|
|
||||||
|
#define MPTCP_BUFLEN 4096
|
||||||
|
#define MPTCP_REQUEST(_req, _cmd, _flags) \
|
||||||
|
GENL_REQUEST(_req, MPTCP_BUFLEN, genl_family, 0, \
|
||||||
|
MPTCP_PM_VER, _cmd, _flags)
|
||||||
|
|
||||||
|
/* Mapping from argument to address flag mask */
|
||||||
|
static const struct {
|
||||||
|
const char *name;
|
||||||
|
unsigned long value;
|
||||||
|
} mptcp_addr_flag_names[] = {
|
||||||
|
{ "signal", MPTCP_PM_ADDR_FLAG_SIGNAL },
|
||||||
|
{ "subflow", MPTCP_PM_ADDR_FLAG_SUBFLOW },
|
||||||
|
{ "backup", MPTCP_PM_ADDR_FLAG_BACKUP },
|
||||||
|
};
|
||||||
|
|
||||||
|
static void print_mptcp_addr_flags(unsigned int flags)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(mptcp_addr_flag_names); i++) {
|
||||||
|
unsigned long mask = mptcp_addr_flag_names[i].value;
|
||||||
|
|
||||||
|
if (flags & mask) {
|
||||||
|
print_string(PRINT_FP, NULL, "%s ",
|
||||||
|
mptcp_addr_flag_names[i].name);
|
||||||
|
print_bool(PRINT_JSON,
|
||||||
|
mptcp_addr_flag_names[i].name, NULL, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
flags &= ~mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags) {
|
||||||
|
/* unknown flags */
|
||||||
|
SPRINT_BUF(b1);
|
||||||
|
|
||||||
|
snprintf(b1, sizeof(b1), "%02x", flags);
|
||||||
|
print_string(PRINT_ANY, "rawflags", "rawflags %s ", b1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_flags(const char *arg, __u32 *flags)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(mptcp_addr_flag_names); i++) {
|
||||||
|
if (strcmp(arg, mptcp_addr_flag_names[i].name))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
*flags |= mptcp_addr_flag_names[i].value;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mptcp_parse_opt(int argc, char **argv, struct nlmsghdr *n,
|
||||||
|
bool adding)
|
||||||
|
{
|
||||||
|
struct rtattr *attr_addr;
|
||||||
|
bool addr_set = false;
|
||||||
|
inet_prefix address;
|
||||||
|
bool id_set = false;
|
||||||
|
__u32 index = 0;
|
||||||
|
__u32 flags = 0;
|
||||||
|
__u8 id = 0;
|
||||||
|
|
||||||
|
ll_init_map(&rth);
|
||||||
|
while (argc > 0) {
|
||||||
|
if (get_flags(*argv, &flags) == 0) {
|
||||||
|
} else if (matches(*argv, "id") == 0) {
|
||||||
|
NEXT_ARG();
|
||||||
|
|
||||||
|
if (get_u8(&id, *argv, 0))
|
||||||
|
invarg("invalid ID\n", *argv);
|
||||||
|
id_set = true;
|
||||||
|
} else if (matches(*argv, "dev") == 0) {
|
||||||
|
const char *ifname;
|
||||||
|
|
||||||
|
NEXT_ARG();
|
||||||
|
|
||||||
|
ifname = *argv;
|
||||||
|
|
||||||
|
if (check_ifname(ifname))
|
||||||
|
invarg("invalid interface name\n", ifname);
|
||||||
|
|
||||||
|
index = ll_name_to_index(ifname);
|
||||||
|
|
||||||
|
if (!index)
|
||||||
|
invarg("device does not exist\n", ifname);
|
||||||
|
|
||||||
|
} else if (get_addr(&address, *argv, AF_UNSPEC) == 0) {
|
||||||
|
addr_set = true;
|
||||||
|
} else {
|
||||||
|
invarg("unknown argument", *argv);
|
||||||
|
}
|
||||||
|
NEXT_ARG_FWD();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!addr_set && adding)
|
||||||
|
missarg("ADDRESS");
|
||||||
|
|
||||||
|
if (!id_set && !adding)
|
||||||
|
missarg("ID");
|
||||||
|
|
||||||
|
attr_addr = addattr_nest(n, MPTCP_BUFLEN,
|
||||||
|
MPTCP_PM_ATTR_ADDR | NLA_F_NESTED);
|
||||||
|
if (id_set)
|
||||||
|
addattr8(n, MPTCP_BUFLEN, MPTCP_PM_ADDR_ATTR_ID, id);
|
||||||
|
if (flags)
|
||||||
|
addattr32(n, MPTCP_BUFLEN, MPTCP_PM_ADDR_ATTR_FLAGS, flags);
|
||||||
|
if (index)
|
||||||
|
addattr32(n, MPTCP_BUFLEN, MPTCP_PM_ADDR_ATTR_IF_IDX, index);
|
||||||
|
if (addr_set) {
|
||||||
|
int type;
|
||||||
|
|
||||||
|
addattr16(n, MPTCP_BUFLEN, MPTCP_PM_ADDR_ATTR_FAMILY,
|
||||||
|
address.family);
|
||||||
|
type = address.family == AF_INET ? MPTCP_PM_ADDR_ATTR_ADDR4 :
|
||||||
|
MPTCP_PM_ADDR_ATTR_ADDR6;
|
||||||
|
addattr_l(n, MPTCP_BUFLEN, type, &address.data,
|
||||||
|
address.bytelen);
|
||||||
|
}
|
||||||
|
|
||||||
|
addattr_nest_end(n, attr_addr);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mptcp_addr_modify(int argc, char **argv, int cmd)
|
||||||
|
{
|
||||||
|
MPTCP_REQUEST(req, cmd, NLM_F_REQUEST);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = mptcp_parse_opt(argc, argv, &req.n, cmd == MPTCP_PM_CMD_ADD_ADDR);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (rtnl_talk(&genl_rth, &req.n, NULL) < 0)
|
||||||
|
return -2;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int print_mptcp_addrinfo(struct rtattr *addrinfo)
|
||||||
|
{
|
||||||
|
struct rtattr *tb[MPTCP_PM_ADDR_ATTR_MAX + 1];
|
||||||
|
__u8 family = AF_UNSPEC, addr_attr_type;
|
||||||
|
const char *ifname;
|
||||||
|
unsigned int flags;
|
||||||
|
int index;
|
||||||
|
__u16 id;
|
||||||
|
|
||||||
|
parse_rtattr_nested(tb, MPTCP_PM_ADDR_ATTR_MAX, addrinfo);
|
||||||
|
|
||||||
|
open_json_object(NULL);
|
||||||
|
if (tb[MPTCP_PM_ADDR_ATTR_FAMILY])
|
||||||
|
family = rta_getattr_u8(tb[MPTCP_PM_ADDR_ATTR_FAMILY]);
|
||||||
|
|
||||||
|
addr_attr_type = family == AF_INET ? MPTCP_PM_ADDR_ATTR_ADDR4 :
|
||||||
|
MPTCP_PM_ADDR_ATTR_ADDR6;
|
||||||
|
if (tb[addr_attr_type]) {
|
||||||
|
print_string(PRINT_ANY, "address", "%s ",
|
||||||
|
format_host_rta(family, tb[addr_attr_type]));
|
||||||
|
}
|
||||||
|
if (tb[MPTCP_PM_ADDR_ATTR_ID]) {
|
||||||
|
id = rta_getattr_u8(tb[MPTCP_PM_ADDR_ATTR_ID]);
|
||||||
|
print_uint(PRINT_ANY, "id", "id %u ", id);
|
||||||
|
}
|
||||||
|
if (tb[MPTCP_PM_ADDR_ATTR_FLAGS]) {
|
||||||
|
flags = rta_getattr_u32(tb[MPTCP_PM_ADDR_ATTR_FLAGS]);
|
||||||
|
print_mptcp_addr_flags(flags);
|
||||||
|
}
|
||||||
|
if (tb[MPTCP_PM_ADDR_ATTR_IF_IDX]) {
|
||||||
|
index = rta_getattr_s32(tb[MPTCP_PM_ADDR_ATTR_IF_IDX]);
|
||||||
|
ifname = index ? ll_index_to_name(index) : NULL;
|
||||||
|
|
||||||
|
if (ifname)
|
||||||
|
print_string(PRINT_ANY, "dev", "dev %s ", ifname);
|
||||||
|
}
|
||||||
|
|
||||||
|
close_json_object();
|
||||||
|
print_string(PRINT_FP, NULL, "\n", NULL);
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int print_mptcp_addr(struct nlmsghdr *n, void *arg)
|
||||||
|
{
|
||||||
|
struct rtattr *tb[MPTCP_PM_ATTR_MAX + 1];
|
||||||
|
struct genlmsghdr *ghdr;
|
||||||
|
struct rtattr *addrinfo;
|
||||||
|
int len = n->nlmsg_len;
|
||||||
|
|
||||||
|
if (n->nlmsg_type != genl_family)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
len -= NLMSG_LENGTH(GENL_HDRLEN);
|
||||||
|
if (len < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
ghdr = NLMSG_DATA(n);
|
||||||
|
parse_rtattr_flags(tb, MPTCP_PM_ATTR_MAX, (void *) ghdr + GENL_HDRLEN,
|
||||||
|
len, NLA_F_NESTED);
|
||||||
|
addrinfo = tb[MPTCP_PM_ATTR_ADDR];
|
||||||
|
if (!addrinfo)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
ll_init_map(&rth);
|
||||||
|
return print_mptcp_addrinfo(addrinfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mptcp_addr_dump(void)
|
||||||
|
{
|
||||||
|
MPTCP_REQUEST(req, MPTCP_PM_CMD_GET_ADDR, NLM_F_REQUEST | NLM_F_DUMP);
|
||||||
|
|
||||||
|
if (rtnl_send(&genl_rth, &req.n, req.n.nlmsg_len) < 0) {
|
||||||
|
perror("Cannot send show request");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
new_json_obj(json);
|
||||||
|
|
||||||
|
if (rtnl_dump_filter(&genl_rth, print_mptcp_addr, stdout) < 0) {
|
||||||
|
fprintf(stderr, "Dump terminated\n");
|
||||||
|
delete_json_obj();
|
||||||
|
fflush(stdout);
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
close_json_object();
|
||||||
|
fflush(stdout);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mptcp_addr_show(int argc, char **argv)
|
||||||
|
{
|
||||||
|
MPTCP_REQUEST(req, MPTCP_PM_CMD_GET_ADDR, NLM_F_REQUEST);
|
||||||
|
struct nlmsghdr *answer;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!argv)
|
||||||
|
return mptcp_addr_dump();
|
||||||
|
|
||||||
|
ret = mptcp_parse_opt(argc, argv, &req.n, false);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (rtnl_talk(&genl_rth, &req.n, &answer) < 0)
|
||||||
|
return -2;
|
||||||
|
|
||||||
|
return print_mptcp_addr(answer, stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mptcp_addr_flush(int argc, char **argv)
|
||||||
|
{
|
||||||
|
MPTCP_REQUEST(req, MPTCP_PM_CMD_FLUSH_ADDRS, NLM_F_REQUEST);
|
||||||
|
|
||||||
|
if (rtnl_talk(&genl_rth, &req.n, NULL) < 0)
|
||||||
|
return -2;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mptcp_parse_limit(int argc, char **argv, struct nlmsghdr *n)
|
||||||
|
{
|
||||||
|
bool set_rcv_add_addrs = false;
|
||||||
|
bool set_subflows = false;
|
||||||
|
__u32 rcv_add_addrs = 0;
|
||||||
|
__u32 subflows = 0;
|
||||||
|
|
||||||
|
while (argc > 0) {
|
||||||
|
if (matches(*argv, "subflows") == 0) {
|
||||||
|
NEXT_ARG();
|
||||||
|
|
||||||
|
if (get_u32(&subflows, *argv, 0))
|
||||||
|
invarg("invalid subflows\n", *argv);
|
||||||
|
set_subflows = true;
|
||||||
|
} else if (matches(*argv, "add_addr_accepted") == 0) {
|
||||||
|
NEXT_ARG();
|
||||||
|
|
||||||
|
if (get_u32(&rcv_add_addrs, *argv, 0))
|
||||||
|
invarg("invalid add_addr_accepted\n", *argv);
|
||||||
|
set_rcv_add_addrs = true;
|
||||||
|
} else {
|
||||||
|
invarg("unknown limit", *argv);
|
||||||
|
}
|
||||||
|
NEXT_ARG_FWD();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (set_rcv_add_addrs)
|
||||||
|
addattr32(n, MPTCP_BUFLEN, MPTCP_PM_ATTR_RCV_ADD_ADDRS,
|
||||||
|
rcv_add_addrs);
|
||||||
|
if (set_subflows)
|
||||||
|
addattr32(n, MPTCP_BUFLEN, MPTCP_PM_ATTR_SUBFLOWS, subflows);
|
||||||
|
return set_rcv_add_addrs || set_subflows;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int print_mptcp_limit(struct nlmsghdr *n, void *arg)
|
||||||
|
{
|
||||||
|
struct rtattr *tb[MPTCP_PM_ATTR_MAX + 1];
|
||||||
|
struct genlmsghdr *ghdr;
|
||||||
|
int len = n->nlmsg_len;
|
||||||
|
__u32 val;
|
||||||
|
|
||||||
|
if (n->nlmsg_type != genl_family)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
len -= NLMSG_LENGTH(GENL_HDRLEN);
|
||||||
|
if (len < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
ghdr = NLMSG_DATA(n);
|
||||||
|
parse_rtattr(tb, MPTCP_PM_ATTR_MAX, (void *) ghdr + GENL_HDRLEN, len);
|
||||||
|
|
||||||
|
open_json_object(NULL);
|
||||||
|
if (tb[MPTCP_PM_ATTR_RCV_ADD_ADDRS]) {
|
||||||
|
val = rta_getattr_u32(tb[MPTCP_PM_ATTR_RCV_ADD_ADDRS]);
|
||||||
|
|
||||||
|
print_uint(PRINT_ANY, "add_addr_accepted",
|
||||||
|
"add_addr_accepted %d ", val);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tb[MPTCP_PM_ATTR_SUBFLOWS]) {
|
||||||
|
val = rta_getattr_u32(tb[MPTCP_PM_ATTR_SUBFLOWS]);
|
||||||
|
|
||||||
|
print_uint(PRINT_ANY, "subflows", "subflows %d ", val);
|
||||||
|
}
|
||||||
|
print_string(PRINT_FP, NULL, "%s", "\n");
|
||||||
|
fflush(stdout);
|
||||||
|
close_json_object();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mptcp_limit_get_set(int argc, char **argv, int cmd)
|
||||||
|
{
|
||||||
|
bool do_get = cmd == MPTCP_PM_CMD_GET_LIMITS;
|
||||||
|
MPTCP_REQUEST(req, cmd, NLM_F_REQUEST);
|
||||||
|
struct nlmsghdr *answer;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = mptcp_parse_limit(argc, argv, &req.n);
|
||||||
|
if (ret < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (rtnl_talk(&genl_rth, &req.n, do_get ? &answer : NULL) < 0)
|
||||||
|
return -2;
|
||||||
|
|
||||||
|
if (do_get)
|
||||||
|
return print_mptcp_limit(answer, stdout);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int do_mptcp(int argc, char **argv)
|
||||||
|
{
|
||||||
|
if (argc == 0)
|
||||||
|
usage();
|
||||||
|
|
||||||
|
if (matches(*argv, "help") == 0)
|
||||||
|
usage();
|
||||||
|
|
||||||
|
if (genl_init_handle(&genl_rth, MPTCP_PM_NAME, &genl_family))
|
||||||
|
exit(1);
|
||||||
|
|
||||||
|
if (matches(*argv, "endpoint") == 0) {
|
||||||
|
NEXT_ARG_FWD();
|
||||||
|
if (argc == 0)
|
||||||
|
return mptcp_addr_show(0, NULL);
|
||||||
|
|
||||||
|
if (matches(*argv, "add") == 0)
|
||||||
|
return mptcp_addr_modify(argc-1, argv+1,
|
||||||
|
MPTCP_PM_CMD_ADD_ADDR);
|
||||||
|
if (matches(*argv, "delete") == 0)
|
||||||
|
return mptcp_addr_modify(argc-1, argv+1,
|
||||||
|
MPTCP_PM_CMD_DEL_ADDR);
|
||||||
|
if (matches(*argv, "show") == 0)
|
||||||
|
return mptcp_addr_show(argc-1, argv+1);
|
||||||
|
if (matches(*argv, "flush") == 0)
|
||||||
|
return mptcp_addr_flush(argc-1, argv+1);
|
||||||
|
|
||||||
|
goto unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matches(*argv, "limits") == 0) {
|
||||||
|
NEXT_ARG_FWD();
|
||||||
|
if (argc == 0)
|
||||||
|
return mptcp_limit_get_set(0, NULL,
|
||||||
|
MPTCP_PM_CMD_GET_LIMITS);
|
||||||
|
|
||||||
|
if (matches(*argv, "set") == 0)
|
||||||
|
return mptcp_limit_get_set(argc-1, argv+1,
|
||||||
|
MPTCP_PM_CMD_SET_LIMITS);
|
||||||
|
if (matches(*argv, "show") == 0)
|
||||||
|
return mptcp_limit_get_set(argc-1, argv+1,
|
||||||
|
MPTCP_PM_CMD_GET_LIMITS);
|
||||||
|
}
|
||||||
|
|
||||||
|
unknown:
|
||||||
|
fprintf(stderr, "Command \"%s\" is unknown, try \"ip mptcp help\".\n",
|
||||||
|
*argv);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
@ -101,7 +101,7 @@ static void usage(void)
|
||||||
"TIME := NUMBER[s|ms]\n"
|
"TIME := NUMBER[s|ms]\n"
|
||||||
"BOOL := [1|0]\n"
|
"BOOL := [1|0]\n"
|
||||||
"FEATURES := ecn\n"
|
"FEATURES := ecn\n"
|
||||||
"ENCAPTYPE := [ mpls | ip | ip6 | seg6 | seg6local ]\n"
|
"ENCAPTYPE := [ mpls | ip | ip6 | seg6 | seg6local | rpl ]\n"
|
||||||
"ENCAPHDR := [ MPLSLABEL | SEG6HDR ]\n"
|
"ENCAPHDR := [ MPLSLABEL | SEG6HDR ]\n"
|
||||||
"SEG6HDR := [ mode SEGMODE ] segs ADDR1,ADDRi,ADDRn [hmac HMACKEYID] [cleanup]\n"
|
"SEG6HDR := [ mode SEGMODE ] segs ADDR1,ADDRi,ADDRn [hmac HMACKEYID] [cleanup]\n"
|
||||||
"SEGMODE := [ encap | inline ]\n"
|
"SEGMODE := [ encap | inline ]\n"
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,8 @@
|
||||||
|
|
||||||
#include <linux/seg6.h>
|
#include <linux/seg6.h>
|
||||||
#include <linux/seg6_iptunnel.h>
|
#include <linux/seg6_iptunnel.h>
|
||||||
|
#include <linux/rpl.h>
|
||||||
|
#include <linux/rpl_iptunnel.h>
|
||||||
#include <linux/seg6_hmac.h>
|
#include <linux/seg6_hmac.h>
|
||||||
#include <linux/seg6_local.h>
|
#include <linux/seg6_local.h>
|
||||||
#include <linux/if_tunnel.h>
|
#include <linux/if_tunnel.h>
|
||||||
|
|
@ -50,6 +52,8 @@ static const char *format_encap_type(int type)
|
||||||
return "seg6";
|
return "seg6";
|
||||||
case LWTUNNEL_ENCAP_SEG6_LOCAL:
|
case LWTUNNEL_ENCAP_SEG6_LOCAL:
|
||||||
return "seg6local";
|
return "seg6local";
|
||||||
|
case LWTUNNEL_ENCAP_RPL:
|
||||||
|
return "rpl";
|
||||||
default:
|
default:
|
||||||
return "unknown";
|
return "unknown";
|
||||||
}
|
}
|
||||||
|
|
@ -84,6 +88,8 @@ static int read_encap_type(const char *name)
|
||||||
return LWTUNNEL_ENCAP_SEG6;
|
return LWTUNNEL_ENCAP_SEG6;
|
||||||
else if (strcmp(name, "seg6local") == 0)
|
else if (strcmp(name, "seg6local") == 0)
|
||||||
return LWTUNNEL_ENCAP_SEG6_LOCAL;
|
return LWTUNNEL_ENCAP_SEG6_LOCAL;
|
||||||
|
else if (strcmp(name, "rpl") == 0)
|
||||||
|
return LWTUNNEL_ENCAP_RPL;
|
||||||
else if (strcmp(name, "help") == 0)
|
else if (strcmp(name, "help") == 0)
|
||||||
encap_type_usage();
|
encap_type_usage();
|
||||||
|
|
||||||
|
|
@ -162,6 +168,42 @@ static void print_encap_seg6(FILE *fp, struct rtattr *encap)
|
||||||
print_srh(fp, tuninfo->srh);
|
print_srh(fp, tuninfo->srh);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void print_rpl_srh(FILE *fp, struct ipv6_rpl_sr_hdr *srh)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (is_json_context())
|
||||||
|
open_json_array(PRINT_JSON, "segs");
|
||||||
|
else
|
||||||
|
fprintf(fp, "segs %d [ ", srh->segments_left);
|
||||||
|
|
||||||
|
for (i = srh->segments_left - 1; i >= 0; i--) {
|
||||||
|
print_color_string(PRINT_ANY, COLOR_INET6,
|
||||||
|
NULL, "%s ",
|
||||||
|
rt_addr_n2a(AF_INET6, 16, &srh->rpl_segaddr[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_json_context())
|
||||||
|
close_json_array(PRINT_JSON, NULL);
|
||||||
|
else
|
||||||
|
fprintf(fp, "] ");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_encap_rpl(FILE *fp, struct rtattr *encap)
|
||||||
|
{
|
||||||
|
struct rtattr *tb[RPL_IPTUNNEL_MAX + 1];
|
||||||
|
struct ipv6_rpl_sr_hdr *srh;
|
||||||
|
|
||||||
|
parse_rtattr_nested(tb, RPL_IPTUNNEL_MAX, encap);
|
||||||
|
|
||||||
|
if (!tb[RPL_IPTUNNEL_SRH])
|
||||||
|
return;
|
||||||
|
|
||||||
|
srh = RTA_DATA(tb[RPL_IPTUNNEL_SRH]);
|
||||||
|
|
||||||
|
print_rpl_srh(fp, srh);
|
||||||
|
}
|
||||||
|
|
||||||
static const char *seg6_action_names[SEG6_LOCAL_ACTION_MAX + 1] = {
|
static const char *seg6_action_names[SEG6_LOCAL_ACTION_MAX + 1] = {
|
||||||
[SEG6_LOCAL_ACTION_END] = "End",
|
[SEG6_LOCAL_ACTION_END] = "End",
|
||||||
[SEG6_LOCAL_ACTION_END_X] = "End.X",
|
[SEG6_LOCAL_ACTION_END_X] = "End.X",
|
||||||
|
|
@ -294,6 +336,110 @@ static void print_encap_mpls(FILE *fp, struct rtattr *encap)
|
||||||
rta_getattr_u8(tb[MPLS_IPTUNNEL_TTL]));
|
rta_getattr_u8(tb[MPLS_IPTUNNEL_TTL]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void lwtunnel_print_geneve_opts(struct rtattr *attr)
|
||||||
|
{
|
||||||
|
struct rtattr *tb[LWTUNNEL_IP_OPT_GENEVE_MAX + 1];
|
||||||
|
struct rtattr *i = RTA_DATA(attr);
|
||||||
|
int rem = RTA_PAYLOAD(attr);
|
||||||
|
char *name = "geneve_opts";
|
||||||
|
int data_len, offset = 0;
|
||||||
|
char data[rem * 2 + 1];
|
||||||
|
__u16 class;
|
||||||
|
__u8 type;
|
||||||
|
|
||||||
|
print_nl();
|
||||||
|
print_string(PRINT_FP, name, "\t%s ", name);
|
||||||
|
open_json_array(PRINT_JSON, name);
|
||||||
|
|
||||||
|
while (rem) {
|
||||||
|
parse_rtattr(tb, LWTUNNEL_IP_OPT_GENEVE_MAX, i, rem);
|
||||||
|
class = rta_getattr_be16(tb[LWTUNNEL_IP_OPT_GENEVE_CLASS]);
|
||||||
|
type = rta_getattr_u8(tb[LWTUNNEL_IP_OPT_GENEVE_TYPE]);
|
||||||
|
data_len = RTA_PAYLOAD(tb[LWTUNNEL_IP_OPT_GENEVE_DATA]);
|
||||||
|
hexstring_n2a(RTA_DATA(tb[LWTUNNEL_IP_OPT_GENEVE_DATA]),
|
||||||
|
data_len, data, sizeof(data));
|
||||||
|
offset += data_len + 20;
|
||||||
|
rem -= data_len + 20;
|
||||||
|
i = RTA_DATA(attr) + offset;
|
||||||
|
|
||||||
|
open_json_object(NULL);
|
||||||
|
print_uint(PRINT_ANY, "class", "%u", class);
|
||||||
|
print_uint(PRINT_ANY, "type", ":%u", type);
|
||||||
|
if (rem)
|
||||||
|
print_string(PRINT_ANY, "data", ":%s,", data);
|
||||||
|
else
|
||||||
|
print_string(PRINT_ANY, "data", ":%s ", data);
|
||||||
|
close_json_object();
|
||||||
|
}
|
||||||
|
|
||||||
|
close_json_array(PRINT_JSON, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lwtunnel_print_vxlan_opts(struct rtattr *attr)
|
||||||
|
{
|
||||||
|
struct rtattr *tb[LWTUNNEL_IP_OPT_VXLAN_MAX + 1];
|
||||||
|
struct rtattr *i = RTA_DATA(attr);
|
||||||
|
int rem = RTA_PAYLOAD(attr);
|
||||||
|
char *name = "vxlan_opts";
|
||||||
|
__u32 gbp;
|
||||||
|
|
||||||
|
parse_rtattr(tb, LWTUNNEL_IP_OPT_VXLAN_MAX, i, rem);
|
||||||
|
gbp = rta_getattr_u32(tb[LWTUNNEL_IP_OPT_VXLAN_GBP]);
|
||||||
|
|
||||||
|
print_nl();
|
||||||
|
print_string(PRINT_FP, name, "\t%s ", name);
|
||||||
|
open_json_array(PRINT_JSON, name);
|
||||||
|
open_json_object(NULL);
|
||||||
|
print_uint(PRINT_ANY, "gbp", "%u ", gbp);
|
||||||
|
close_json_object();
|
||||||
|
close_json_array(PRINT_JSON, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lwtunnel_print_erspan_opts(struct rtattr *attr)
|
||||||
|
{
|
||||||
|
struct rtattr *tb[LWTUNNEL_IP_OPT_ERSPAN_MAX + 1];
|
||||||
|
struct rtattr *i = RTA_DATA(attr);
|
||||||
|
char *name = "erspan_opts";
|
||||||
|
__u8 ver, hwid, dir;
|
||||||
|
__u32 idx;
|
||||||
|
|
||||||
|
parse_rtattr(tb, LWTUNNEL_IP_OPT_ERSPAN_MAX, i, RTA_PAYLOAD(attr));
|
||||||
|
ver = rta_getattr_u8(tb[LWTUNNEL_IP_OPT_ERSPAN_VER]);
|
||||||
|
if (ver == 1) {
|
||||||
|
idx = rta_getattr_be32(tb[LWTUNNEL_IP_OPT_ERSPAN_INDEX]);
|
||||||
|
dir = 0;
|
||||||
|
hwid = 0;
|
||||||
|
} else {
|
||||||
|
idx = 0;
|
||||||
|
dir = rta_getattr_u8(tb[LWTUNNEL_IP_OPT_ERSPAN_DIR]);
|
||||||
|
hwid = rta_getattr_u8(tb[LWTUNNEL_IP_OPT_ERSPAN_HWID]);
|
||||||
|
}
|
||||||
|
|
||||||
|
print_nl();
|
||||||
|
print_string(PRINT_FP, name, "\t%s ", name);
|
||||||
|
open_json_array(PRINT_JSON, name);
|
||||||
|
open_json_object(NULL);
|
||||||
|
print_uint(PRINT_ANY, "ver", "%u", ver);
|
||||||
|
print_uint(PRINT_ANY, "index", ":%u", idx);
|
||||||
|
print_uint(PRINT_ANY, "dir", ":%u", dir);
|
||||||
|
print_uint(PRINT_ANY, "hwid", ":%u ", hwid);
|
||||||
|
close_json_object();
|
||||||
|
close_json_array(PRINT_JSON, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lwtunnel_print_opts(struct rtattr *attr)
|
||||||
|
{
|
||||||
|
struct rtattr *tb_opt[LWTUNNEL_IP_OPTS_MAX + 1];
|
||||||
|
|
||||||
|
parse_rtattr_nested(tb_opt, LWTUNNEL_IP_OPTS_MAX, attr);
|
||||||
|
if (tb_opt[LWTUNNEL_IP_OPTS_GENEVE])
|
||||||
|
lwtunnel_print_geneve_opts(tb_opt[LWTUNNEL_IP_OPTS_GENEVE]);
|
||||||
|
else if (tb_opt[LWTUNNEL_IP_OPTS_VXLAN])
|
||||||
|
lwtunnel_print_vxlan_opts(tb_opt[LWTUNNEL_IP_OPTS_VXLAN]);
|
||||||
|
else if (tb_opt[LWTUNNEL_IP_OPTS_ERSPAN])
|
||||||
|
lwtunnel_print_erspan_opts(tb_opt[LWTUNNEL_IP_OPTS_ERSPAN]);
|
||||||
|
}
|
||||||
|
|
||||||
static void print_encap_ip(FILE *fp, struct rtattr *encap)
|
static void print_encap_ip(FILE *fp, struct rtattr *encap)
|
||||||
{
|
{
|
||||||
struct rtattr *tb[LWTUNNEL_IP_MAX+1];
|
struct rtattr *tb[LWTUNNEL_IP_MAX+1];
|
||||||
|
|
@ -332,6 +478,9 @@ static void print_encap_ip(FILE *fp, struct rtattr *encap)
|
||||||
if (flags & TUNNEL_SEQ)
|
if (flags & TUNNEL_SEQ)
|
||||||
print_bool(PRINT_ANY, "seq", "seq ", true);
|
print_bool(PRINT_ANY, "seq", "seq ", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tb[LWTUNNEL_IP_OPTS])
|
||||||
|
lwtunnel_print_opts(tb[LWTUNNEL_IP_OPTS]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_encap_ila(FILE *fp, struct rtattr *encap)
|
static void print_encap_ila(FILE *fp, struct rtattr *encap)
|
||||||
|
|
@ -404,6 +553,9 @@ static void print_encap_ip6(FILE *fp, struct rtattr *encap)
|
||||||
if (flags & TUNNEL_SEQ)
|
if (flags & TUNNEL_SEQ)
|
||||||
print_bool(PRINT_ANY, "seq", "seq ", true);
|
print_bool(PRINT_ANY, "seq", "seq ", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tb[LWTUNNEL_IP6_OPTS])
|
||||||
|
lwtunnel_print_opts(tb[LWTUNNEL_IP6_OPTS]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_encap_bpf(FILE *fp, struct rtattr *encap)
|
static void print_encap_bpf(FILE *fp, struct rtattr *encap)
|
||||||
|
|
@ -457,6 +609,9 @@ void lwt_print_encap(FILE *fp, struct rtattr *encap_type,
|
||||||
case LWTUNNEL_ENCAP_SEG6_LOCAL:
|
case LWTUNNEL_ENCAP_SEG6_LOCAL:
|
||||||
print_encap_seg6local(fp, encap);
|
print_encap_seg6local(fp, encap);
|
||||||
break;
|
break;
|
||||||
|
case LWTUNNEL_ENCAP_RPL:
|
||||||
|
print_encap_rpl(fp, encap);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -580,6 +735,79 @@ out:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct ipv6_rpl_sr_hdr *parse_rpl_srh(char *segbuf)
|
||||||
|
{
|
||||||
|
struct ipv6_rpl_sr_hdr *srh;
|
||||||
|
int nsegs = 0;
|
||||||
|
int srhlen;
|
||||||
|
char *s;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
s = segbuf;
|
||||||
|
for (i = 0; *s; *s++ == ',' ? i++ : *s);
|
||||||
|
nsegs = i + 1;
|
||||||
|
|
||||||
|
srhlen = 8 + 16 * nsegs;
|
||||||
|
|
||||||
|
srh = calloc(1, srhlen);
|
||||||
|
|
||||||
|
srh->hdrlen = (srhlen >> 3) - 1;
|
||||||
|
srh->type = 3;
|
||||||
|
srh->segments_left = nsegs;
|
||||||
|
|
||||||
|
for (s = strtok(segbuf, ","); s; s = strtok(NULL, ",")) {
|
||||||
|
inet_prefix addr;
|
||||||
|
|
||||||
|
get_addr(&addr, s, AF_INET6);
|
||||||
|
memcpy(&srh->rpl_segaddr[i], addr.data, sizeof(struct in6_addr));
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return srh;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_encap_rpl(struct rtattr *rta, size_t len, int *argcp,
|
||||||
|
char ***argvp)
|
||||||
|
{
|
||||||
|
struct ipv6_rpl_sr_hdr *srh;
|
||||||
|
char **argv = *argvp;
|
||||||
|
char segbuf[1024] = "";
|
||||||
|
int argc = *argcp;
|
||||||
|
int segs_ok = 0;
|
||||||
|
int ret = 0;
|
||||||
|
int srhlen;
|
||||||
|
|
||||||
|
while (argc > 0) {
|
||||||
|
if (strcmp(*argv, "segs") == 0) {
|
||||||
|
NEXT_ARG();
|
||||||
|
if (segs_ok++)
|
||||||
|
duparg2("segs", *argv);
|
||||||
|
|
||||||
|
strlcpy(segbuf, *argv, 1024);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
argc--; argv++;
|
||||||
|
}
|
||||||
|
|
||||||
|
srh = parse_rpl_srh(segbuf);
|
||||||
|
srhlen = (srh->hdrlen + 1) << 3;
|
||||||
|
|
||||||
|
if (rta_addattr_l(rta, len, RPL_IPTUNNEL_SRH, srh,
|
||||||
|
srhlen)) {
|
||||||
|
ret = -1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
*argcp = argc + 1;
|
||||||
|
*argvp = argv - 1;
|
||||||
|
|
||||||
|
out:
|
||||||
|
free(srh);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
struct lwt_x {
|
struct lwt_x {
|
||||||
struct rtattr *rta;
|
struct rtattr *rta;
|
||||||
size_t len;
|
size_t len;
|
||||||
|
|
@ -798,11 +1026,189 @@ static int parse_encap_mpls(struct rtattr *rta, size_t len,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int lwtunnel_parse_geneve_opt(char *str, size_t len, struct rtattr *rta)
|
||||||
|
{
|
||||||
|
struct rtattr *nest;
|
||||||
|
char *token;
|
||||||
|
int i, err;
|
||||||
|
|
||||||
|
nest = rta_nest(rta, len, LWTUNNEL_IP_OPTS_GENEVE | NLA_F_NESTED);
|
||||||
|
i = 1;
|
||||||
|
token = strsep(&str, ":");
|
||||||
|
while (token) {
|
||||||
|
switch (i) {
|
||||||
|
case LWTUNNEL_IP_OPT_GENEVE_CLASS:
|
||||||
|
{
|
||||||
|
__be16 opt_class;
|
||||||
|
|
||||||
|
if (!strlen(token))
|
||||||
|
break;
|
||||||
|
err = get_be16(&opt_class, token, 0);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
rta_addattr16(rta, len, i, opt_class);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWTUNNEL_IP_OPT_GENEVE_TYPE:
|
||||||
|
{
|
||||||
|
__u8 opt_type;
|
||||||
|
|
||||||
|
if (!strlen(token))
|
||||||
|
break;
|
||||||
|
err = get_u8(&opt_type, token, 0);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
rta_addattr8(rta, len, i, opt_type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWTUNNEL_IP_OPT_GENEVE_DATA:
|
||||||
|
{
|
||||||
|
size_t token_len = strlen(token);
|
||||||
|
__u8 *opts;
|
||||||
|
|
||||||
|
if (!token_len)
|
||||||
|
break;
|
||||||
|
opts = malloc(token_len / 2);
|
||||||
|
if (!opts)
|
||||||
|
return -1;
|
||||||
|
if (hex2mem(token, opts, token_len / 2) < 0) {
|
||||||
|
free(opts);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
rta_addattr_l(rta, len, i, opts, token_len / 2);
|
||||||
|
free(opts);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "Unknown \"geneve_opts\" type\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
token = strsep(&str, ":");
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
rta_nest_end(rta, nest);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int lwtunnel_parse_geneve_opts(char *str, size_t len, struct rtattr *rta)
|
||||||
|
{
|
||||||
|
char *token;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
token = strsep(&str, ",");
|
||||||
|
while (token) {
|
||||||
|
err = lwtunnel_parse_geneve_opt(token, len, rta);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
token = strsep(&str, ",");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int lwtunnel_parse_vxlan_opts(char *str, size_t len, struct rtattr *rta)
|
||||||
|
{
|
||||||
|
struct rtattr *nest;
|
||||||
|
__u32 gbp;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
nest = rta_nest(rta, len, LWTUNNEL_IP_OPTS_VXLAN | NLA_F_NESTED);
|
||||||
|
err = get_u32(&gbp, str, 0);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
rta_addattr32(rta, len, LWTUNNEL_IP_OPT_VXLAN_GBP, gbp);
|
||||||
|
|
||||||
|
rta_nest_end(rta, nest);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int lwtunnel_parse_erspan_opts(char *str, size_t len, struct rtattr *rta)
|
||||||
|
{
|
||||||
|
struct rtattr *nest;
|
||||||
|
char *token;
|
||||||
|
int i, err;
|
||||||
|
|
||||||
|
nest = rta_nest(rta, len, LWTUNNEL_IP_OPTS_ERSPAN | NLA_F_NESTED);
|
||||||
|
i = 1;
|
||||||
|
token = strsep(&str, ":");
|
||||||
|
while (token) {
|
||||||
|
switch (i) {
|
||||||
|
case LWTUNNEL_IP_OPT_ERSPAN_VER:
|
||||||
|
{
|
||||||
|
__u8 opt_type;
|
||||||
|
|
||||||
|
if (!strlen(token))
|
||||||
|
break;
|
||||||
|
err = get_u8(&opt_type, token, 0);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
rta_addattr8(rta, len, i, opt_type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWTUNNEL_IP_OPT_ERSPAN_INDEX:
|
||||||
|
{
|
||||||
|
__be32 opt_class;
|
||||||
|
|
||||||
|
if (!strlen(token))
|
||||||
|
break;
|
||||||
|
err = get_be32(&opt_class, token, 0);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
rta_addattr32(rta, len, i, opt_class);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWTUNNEL_IP_OPT_ERSPAN_DIR:
|
||||||
|
{
|
||||||
|
__u8 opt_type;
|
||||||
|
|
||||||
|
if (!strlen(token))
|
||||||
|
break;
|
||||||
|
err = get_u8(&opt_type, token, 0);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
rta_addattr8(rta, len, i, opt_type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWTUNNEL_IP_OPT_ERSPAN_HWID:
|
||||||
|
{
|
||||||
|
__u8 opt_type;
|
||||||
|
|
||||||
|
if (!strlen(token))
|
||||||
|
break;
|
||||||
|
err = get_u8(&opt_type, token, 0);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
rta_addattr8(rta, len, i, opt_type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "Unknown \"geneve_opts\" type\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
token = strsep(&str, ":");
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
rta_nest_end(rta, nest);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int parse_encap_ip(struct rtattr *rta, size_t len,
|
static int parse_encap_ip(struct rtattr *rta, size_t len,
|
||||||
int *argcp, char ***argvp)
|
int *argcp, char ***argvp)
|
||||||
{
|
{
|
||||||
int id_ok = 0, dst_ok = 0, src_ok = 0, tos_ok = 0, ttl_ok = 0;
|
int id_ok = 0, dst_ok = 0, src_ok = 0, tos_ok = 0, ttl_ok = 0;
|
||||||
int key_ok = 0, csum_ok = 0, seq_ok = 0;
|
int key_ok = 0, csum_ok = 0, seq_ok = 0, opts_ok = 0;
|
||||||
char **argv = *argvp;
|
char **argv = *argvp;
|
||||||
int argc = *argcp;
|
int argc = *argcp;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
@ -854,6 +1260,51 @@ static int parse_encap_ip(struct rtattr *rta, size_t len,
|
||||||
if (get_u8(&ttl, *argv, 0))
|
if (get_u8(&ttl, *argv, 0))
|
||||||
invarg("\"ttl\" value is invalid\n", *argv);
|
invarg("\"ttl\" value is invalid\n", *argv);
|
||||||
ret = rta_addattr8(rta, len, LWTUNNEL_IP_TTL, ttl);
|
ret = rta_addattr8(rta, len, LWTUNNEL_IP_TTL, ttl);
|
||||||
|
} else if (strcmp(*argv, "geneve_opts") == 0) {
|
||||||
|
struct rtattr *nest;
|
||||||
|
|
||||||
|
if (opts_ok++)
|
||||||
|
duparg2("opts", *argv);
|
||||||
|
|
||||||
|
NEXT_ARG();
|
||||||
|
|
||||||
|
nest = rta_nest(rta, len,
|
||||||
|
LWTUNNEL_IP_OPTS | NLA_F_NESTED);
|
||||||
|
ret = lwtunnel_parse_geneve_opts(*argv, len, rta);
|
||||||
|
if (ret)
|
||||||
|
invarg("\"geneve_opts\" value is invalid\n",
|
||||||
|
*argv);
|
||||||
|
rta_nest_end(rta, nest);
|
||||||
|
} else if (strcmp(*argv, "vxlan_opts") == 0) {
|
||||||
|
struct rtattr *nest;
|
||||||
|
|
||||||
|
if (opts_ok++)
|
||||||
|
duparg2("opts", *argv);
|
||||||
|
|
||||||
|
NEXT_ARG();
|
||||||
|
|
||||||
|
nest = rta_nest(rta, len,
|
||||||
|
LWTUNNEL_IP_OPTS | NLA_F_NESTED);
|
||||||
|
ret = lwtunnel_parse_vxlan_opts(*argv, len, rta);
|
||||||
|
if (ret)
|
||||||
|
invarg("\"vxlan_opts\" value is invalid\n",
|
||||||
|
*argv);
|
||||||
|
rta_nest_end(rta, nest);
|
||||||
|
} else if (strcmp(*argv, "erspan_opts") == 0) {
|
||||||
|
struct rtattr *nest;
|
||||||
|
|
||||||
|
if (opts_ok++)
|
||||||
|
duparg2("opts", *argv);
|
||||||
|
|
||||||
|
NEXT_ARG();
|
||||||
|
|
||||||
|
nest = rta_nest(rta, len,
|
||||||
|
LWTUNNEL_IP_OPTS | NLA_F_NESTED);
|
||||||
|
ret = lwtunnel_parse_erspan_opts(*argv, len, rta);
|
||||||
|
if (ret)
|
||||||
|
invarg("\"erspan_opts\" value is invalid\n",
|
||||||
|
*argv);
|
||||||
|
rta_nest_end(rta, nest);
|
||||||
} else if (strcmp(*argv, "key") == 0) {
|
} else if (strcmp(*argv, "key") == 0) {
|
||||||
if (key_ok++)
|
if (key_ok++)
|
||||||
duparg2("key", *argv);
|
duparg2("key", *argv);
|
||||||
|
|
@ -969,7 +1420,7 @@ static int parse_encap_ip6(struct rtattr *rta, size_t len,
|
||||||
int *argcp, char ***argvp)
|
int *argcp, char ***argvp)
|
||||||
{
|
{
|
||||||
int id_ok = 0, dst_ok = 0, src_ok = 0, tos_ok = 0, ttl_ok = 0;
|
int id_ok = 0, dst_ok = 0, src_ok = 0, tos_ok = 0, ttl_ok = 0;
|
||||||
int key_ok = 0, csum_ok = 0, seq_ok = 0;
|
int key_ok = 0, csum_ok = 0, seq_ok = 0, opts_ok = 0;
|
||||||
char **argv = *argvp;
|
char **argv = *argvp;
|
||||||
int argc = *argcp;
|
int argc = *argcp;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
@ -1023,6 +1474,51 @@ static int parse_encap_ip6(struct rtattr *rta, size_t len,
|
||||||
*argv);
|
*argv);
|
||||||
ret = rta_addattr8(rta, len, LWTUNNEL_IP6_HOPLIMIT,
|
ret = rta_addattr8(rta, len, LWTUNNEL_IP6_HOPLIMIT,
|
||||||
hoplimit);
|
hoplimit);
|
||||||
|
} else if (strcmp(*argv, "geneve_opts") == 0) {
|
||||||
|
struct rtattr *nest;
|
||||||
|
|
||||||
|
if (opts_ok++)
|
||||||
|
duparg2("opts", *argv);
|
||||||
|
|
||||||
|
NEXT_ARG();
|
||||||
|
|
||||||
|
nest = rta_nest(rta, len,
|
||||||
|
LWTUNNEL_IP_OPTS | NLA_F_NESTED);
|
||||||
|
ret = lwtunnel_parse_geneve_opts(*argv, len, rta);
|
||||||
|
if (ret)
|
||||||
|
invarg("\"geneve_opts\" value is invalid\n",
|
||||||
|
*argv);
|
||||||
|
rta_nest_end(rta, nest);
|
||||||
|
} else if (strcmp(*argv, "vxlan_opts") == 0) {
|
||||||
|
struct rtattr *nest;
|
||||||
|
|
||||||
|
if (opts_ok++)
|
||||||
|
duparg2("opts", *argv);
|
||||||
|
|
||||||
|
NEXT_ARG();
|
||||||
|
|
||||||
|
nest = rta_nest(rta, len,
|
||||||
|
LWTUNNEL_IP_OPTS | NLA_F_NESTED);
|
||||||
|
ret = lwtunnel_parse_vxlan_opts(*argv, len, rta);
|
||||||
|
if (ret)
|
||||||
|
invarg("\"vxlan_opts\" value is invalid\n",
|
||||||
|
*argv);
|
||||||
|
rta_nest_end(rta, nest);
|
||||||
|
} else if (strcmp(*argv, "erspan_opts") == 0) {
|
||||||
|
struct rtattr *nest;
|
||||||
|
|
||||||
|
if (opts_ok++)
|
||||||
|
duparg2("opts", *argv);
|
||||||
|
|
||||||
|
NEXT_ARG();
|
||||||
|
|
||||||
|
nest = rta_nest(rta, len,
|
||||||
|
LWTUNNEL_IP_OPTS | NLA_F_NESTED);
|
||||||
|
ret = lwtunnel_parse_erspan_opts(*argv, len, rta);
|
||||||
|
if (ret)
|
||||||
|
invarg("\"erspan_opts\" value is invalid\n",
|
||||||
|
*argv);
|
||||||
|
rta_nest_end(rta, nest);
|
||||||
} else if (strcmp(*argv, "key") == 0) {
|
} else if (strcmp(*argv, "key") == 0) {
|
||||||
if (key_ok++)
|
if (key_ok++)
|
||||||
duparg2("key", *argv);
|
duparg2("key", *argv);
|
||||||
|
|
@ -1159,6 +1655,9 @@ int lwt_parse_encap(struct rtattr *rta, size_t len, int *argcp, char ***argvp,
|
||||||
case LWTUNNEL_ENCAP_SEG6_LOCAL:
|
case LWTUNNEL_ENCAP_SEG6_LOCAL:
|
||||||
ret = parse_encap_seg6local(rta, len, &argc, &argv);
|
ret = parse_encap_seg6local(rta, len, &argc, &argv);
|
||||||
break;
|
break;
|
||||||
|
case LWTUNNEL_ENCAP_RPL:
|
||||||
|
ret = parse_encap_rpl(rta, len, &argc, &argv);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "Error: unsupported encap type\n");
|
fprintf(stderr, "Error: unsupported encap type\n");
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -225,7 +225,7 @@ static int ipvrf_pids(int argc, char **argv)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
mnt = find_cgroup2_mount();
|
mnt = find_cgroup2_mount(true);
|
||||||
if (!mnt)
|
if (!mnt)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
|
@ -366,7 +366,7 @@ static int vrf_switch(const char *name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mnt = find_cgroup2_mount();
|
mnt = find_cgroup2_mount(true);
|
||||||
if (!mnt)
|
if (!mnt)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -354,8 +354,8 @@ get_failed:
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
if (get_u8(&erspan_ver, *argv, 0))
|
if (get_u8(&erspan_ver, *argv, 0))
|
||||||
invarg("invalid erspan version\n", *argv);
|
invarg("invalid erspan version\n", *argv);
|
||||||
if (erspan_ver != 1 && erspan_ver != 2)
|
if (erspan_ver > 2)
|
||||||
invarg("erspan version must be 1 or 2\n", *argv);
|
invarg("erspan version must be 0/1/2\n", *argv);
|
||||||
} else if (is_erspan && strcmp(*argv, "erspan_dir") == 0) {
|
} else if (is_erspan && strcmp(*argv, "erspan_dir") == 0) {
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
if (matches(*argv, "ingress") == 0)
|
if (matches(*argv, "ingress") == 0)
|
||||||
|
|
|
||||||
|
|
@ -389,8 +389,8 @@ get_failed:
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
if (get_u8(&erspan_ver, *argv, 0))
|
if (get_u8(&erspan_ver, *argv, 0))
|
||||||
invarg("invalid erspan version\n", *argv);
|
invarg("invalid erspan version\n", *argv);
|
||||||
if (erspan_ver != 1 && erspan_ver != 2)
|
if (erspan_ver > 2)
|
||||||
invarg("erspan version must be 1 or 2\n", *argv);
|
invarg("erspan version must be 0/1/2\n", *argv);
|
||||||
} else if (strcmp(*argv, "erspan_dir") == 0) {
|
} else if (strcmp(*argv, "erspan_dir") == 0) {
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
if (matches(*argv, "ingress") == 0)
|
if (matches(*argv, "ingress") == 0)
|
||||||
|
|
@ -430,7 +430,7 @@ get_failed:
|
||||||
addattr_l(n, 1024, IFLA_GRE_FLOWINFO, &flowinfo, 4);
|
addattr_l(n, 1024, IFLA_GRE_FLOWINFO, &flowinfo, 4);
|
||||||
addattr32(n, 1024, IFLA_GRE_FLAGS, flags);
|
addattr32(n, 1024, IFLA_GRE_FLAGS, flags);
|
||||||
addattr32(n, 1024, IFLA_GRE_FWMARK, fwmark);
|
addattr32(n, 1024, IFLA_GRE_FWMARK, fwmark);
|
||||||
if (erspan_ver) {
|
if (erspan_ver <= 2) {
|
||||||
addattr8(n, 1024, IFLA_GRE_ERSPAN_VER, erspan_ver);
|
addattr8(n, 1024, IFLA_GRE_ERSPAN_VER, erspan_ver);
|
||||||
if (erspan_ver == 1 && erspan_idx != 0) {
|
if (erspan_ver == 1 && erspan_idx != 0) {
|
||||||
addattr32(n, 1024, IFLA_GRE_ERSPAN_INDEX, erspan_idx);
|
addattr32(n, 1024, IFLA_GRE_ERSPAN_INDEX, erspan_idx);
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ CFLAGS += -fPIC
|
||||||
|
|
||||||
UTILOBJ = utils.o rt_names.o ll_map.o ll_types.o ll_proto.o ll_addr.o \
|
UTILOBJ = utils.o rt_names.o ll_map.o ll_types.o ll_proto.o ll_addr.o \
|
||||||
inet_proto.o namespace.o json_writer.o json_print.o \
|
inet_proto.o namespace.o json_writer.o json_print.o \
|
||||||
names.o color.o bpf.o exec.o fs.o
|
names.o color.o bpf.o exec.o fs.o cg_map.o
|
||||||
|
|
||||||
NLOBJ=libgenl.o libnetlink.o
|
NLOBJ=libgenl.o libnetlink.o
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,135 @@
|
||||||
|
/*
|
||||||
|
* cg_map.c cgroup v2 cache
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version
|
||||||
|
* 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Authors: Dmitry Yakunin <zeil@yandex-team.ru>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/limits.h>
|
||||||
|
#include <ftw.h>
|
||||||
|
|
||||||
|
#include "cg_map.h"
|
||||||
|
#include "list.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
struct cg_cache {
|
||||||
|
struct hlist_node id_hash;
|
||||||
|
__u64 id;
|
||||||
|
char path[];
|
||||||
|
};
|
||||||
|
|
||||||
|
#define IDMAP_SIZE 1024
|
||||||
|
static struct hlist_head id_head[IDMAP_SIZE];
|
||||||
|
|
||||||
|
static struct cg_cache *cg_get_by_id(__u64 id)
|
||||||
|
{
|
||||||
|
unsigned int h = id & (IDMAP_SIZE - 1);
|
||||||
|
struct hlist_node *n;
|
||||||
|
|
||||||
|
hlist_for_each(n, &id_head[h]) {
|
||||||
|
struct cg_cache *cg;
|
||||||
|
|
||||||
|
cg = container_of(n, struct cg_cache, id_hash);
|
||||||
|
if (cg->id == id)
|
||||||
|
return cg;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct cg_cache *cg_entry_create(__u64 id, const char *path)
|
||||||
|
{
|
||||||
|
unsigned int h = id & (IDMAP_SIZE - 1);
|
||||||
|
struct cg_cache *cg;
|
||||||
|
|
||||||
|
cg = malloc(sizeof(*cg) + strlen(path) + 1);
|
||||||
|
if (!cg) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"Failed to allocate memory for cgroup2 cache entry");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
cg->id = id;
|
||||||
|
strcpy(cg->path, path);
|
||||||
|
|
||||||
|
hlist_add_head(&cg->id_hash, &id_head[h]);
|
||||||
|
|
||||||
|
return cg;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mntlen;
|
||||||
|
|
||||||
|
static int nftw_fn(const char *fpath, const struct stat *sb,
|
||||||
|
int typeflag, struct FTW *ftw)
|
||||||
|
{
|
||||||
|
const char *path;
|
||||||
|
__u64 id;
|
||||||
|
|
||||||
|
if (typeflag != FTW_D)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
id = get_cgroup2_id(fpath);
|
||||||
|
if (!id)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
path = fpath + mntlen;
|
||||||
|
if (*path == '\0')
|
||||||
|
/* root cgroup */
|
||||||
|
path = "/";
|
||||||
|
if (!cg_entry_create(id, path))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cg_init_map(void)
|
||||||
|
{
|
||||||
|
char *mnt;
|
||||||
|
|
||||||
|
mnt = find_cgroup2_mount(false);
|
||||||
|
if (!mnt)
|
||||||
|
exit(1);
|
||||||
|
|
||||||
|
mntlen = strlen(mnt);
|
||||||
|
if (nftw(mnt, nftw_fn, 1024, FTW_MOUNT) < 0)
|
||||||
|
exit(1);
|
||||||
|
|
||||||
|
free(mnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *cg_id_to_path(__u64 id)
|
||||||
|
{
|
||||||
|
static int initialized;
|
||||||
|
static char buf[64];
|
||||||
|
|
||||||
|
const struct cg_cache *cg;
|
||||||
|
char *path;
|
||||||
|
|
||||||
|
if (!initialized) {
|
||||||
|
cg_init_map();
|
||||||
|
initialized = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
cg = cg_get_by_id(id);
|
||||||
|
if (cg)
|
||||||
|
return cg->path;
|
||||||
|
|
||||||
|
path = get_cgroup2_path(id, false);
|
||||||
|
if (path) {
|
||||||
|
cg = cg_entry_create(id, path);
|
||||||
|
free(path);
|
||||||
|
if (cg)
|
||||||
|
return cg->path;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(buf, sizeof(buf), "unreachable:%llx", id);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
137
lib/fs.c
137
lib/fs.c
|
|
@ -59,13 +59,18 @@ static char *find_fs_mount(const char *fs_to_find)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* caller needs to free string returned */
|
/* caller needs to free string returned */
|
||||||
char *find_cgroup2_mount(void)
|
char *find_cgroup2_mount(bool do_mount)
|
||||||
{
|
{
|
||||||
char *mnt = find_fs_mount(CGROUP2_FS_NAME);
|
char *mnt = find_fs_mount(CGROUP2_FS_NAME);
|
||||||
|
|
||||||
if (mnt)
|
if (mnt)
|
||||||
return mnt;
|
return mnt;
|
||||||
|
|
||||||
|
if (!do_mount) {
|
||||||
|
fprintf(stderr, "Failed to find cgroup2 mount\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
mnt = strdup(MNT_CGRP2_PATH);
|
mnt = strdup(MNT_CGRP2_PATH);
|
||||||
if (!mnt) {
|
if (!mnt) {
|
||||||
fprintf(stderr, "Failed to allocate memory for cgroup2 path\n");
|
fprintf(stderr, "Failed to allocate memory for cgroup2 path\n");
|
||||||
|
|
@ -74,7 +79,7 @@ char *find_cgroup2_mount(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (make_path(mnt, 0755)) {
|
if (make_path(mnt, 0755)) {
|
||||||
fprintf(stderr, "Failed to setup vrf cgroup2 directory\n");
|
fprintf(stderr, "Failed to setup cgroup2 directory\n");
|
||||||
free(mnt);
|
free(mnt);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
@ -99,6 +104,134 @@ out:
|
||||||
return mnt;
|
return mnt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__u64 get_cgroup2_id(const char *path)
|
||||||
|
{
|
||||||
|
char fh_buf[sizeof(struct file_handle) + sizeof(__u64)] = { 0 };
|
||||||
|
struct file_handle *fhp = (struct file_handle *)fh_buf;
|
||||||
|
union {
|
||||||
|
__u64 id;
|
||||||
|
unsigned char bytes[sizeof(__u64)];
|
||||||
|
} cg_id = { .id = 0 };
|
||||||
|
char *mnt = NULL;
|
||||||
|
int mnt_fd = -1;
|
||||||
|
int mnt_id;
|
||||||
|
|
||||||
|
if (!path) {
|
||||||
|
fprintf(stderr, "Invalid cgroup2 path\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fhp->handle_bytes = sizeof(__u64);
|
||||||
|
if (name_to_handle_at(AT_FDCWD, path, fhp, &mnt_id, 0) < 0) {
|
||||||
|
/* try at cgroup2 mount */
|
||||||
|
|
||||||
|
while (*path == '/')
|
||||||
|
path++;
|
||||||
|
if (*path == '\0') {
|
||||||
|
fprintf(stderr, "Invalid cgroup2 path\n");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
mnt = find_cgroup2_mount(false);
|
||||||
|
if (!mnt)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
mnt_fd = open(mnt, O_RDONLY);
|
||||||
|
if (mnt_fd < 0) {
|
||||||
|
fprintf(stderr, "Failed to open cgroup2 mount\n");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
fhp->handle_bytes = sizeof(__u64);
|
||||||
|
if (name_to_handle_at(mnt_fd, path, fhp, &mnt_id, 0) < 0) {
|
||||||
|
fprintf(stderr, "Failed to get cgroup2 ID: %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (fhp->handle_bytes != sizeof(__u64)) {
|
||||||
|
fprintf(stderr, "Invalid size of cgroup2 ID\n");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(cg_id.bytes, fhp->f_handle, sizeof(__u64));
|
||||||
|
|
||||||
|
out:
|
||||||
|
close(mnt_fd);
|
||||||
|
free(mnt);
|
||||||
|
|
||||||
|
return cg_id.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define FILEID_INO32_GEN 1
|
||||||
|
|
||||||
|
/* caller needs to free string returned */
|
||||||
|
char *get_cgroup2_path(__u64 id, bool full)
|
||||||
|
{
|
||||||
|
char fh_buf[sizeof(struct file_handle) + sizeof(__u64)] = { 0 };
|
||||||
|
struct file_handle *fhp = (struct file_handle *)fh_buf;
|
||||||
|
union {
|
||||||
|
__u64 id;
|
||||||
|
unsigned char bytes[sizeof(__u64)];
|
||||||
|
} cg_id = { .id = id };
|
||||||
|
int mnt_fd = -1, fd = -1;
|
||||||
|
char link_buf[PATH_MAX];
|
||||||
|
char *path = NULL;
|
||||||
|
char fd_path[64];
|
||||||
|
int link_len;
|
||||||
|
char *mnt;
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
fprintf(stderr, "Invalid cgroup2 ID\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
mnt = find_cgroup2_mount(false);
|
||||||
|
if (!mnt)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
mnt_fd = open(mnt, O_RDONLY);
|
||||||
|
if (mnt_fd < 0) {
|
||||||
|
fprintf(stderr, "Failed to open cgroup2 mount\n");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
fhp->handle_bytes = sizeof(__u64);
|
||||||
|
fhp->handle_type = FILEID_INO32_GEN;
|
||||||
|
memcpy(fhp->f_handle, cg_id.bytes, sizeof(__u64));
|
||||||
|
|
||||||
|
fd = open_by_handle_at(mnt_fd, fhp, 0);
|
||||||
|
if (fd < 0) {
|
||||||
|
fprintf(stderr, "Failed to open cgroup2 by ID\n");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(fd_path, sizeof(fd_path), "/proc/self/fd/%d", fd);
|
||||||
|
link_len = readlink(fd_path, link_buf, sizeof(link_buf) - 1);
|
||||||
|
if (link_len < 0) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"Failed to read value of symbolic link %s\n",
|
||||||
|
fd_path);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
link_buf[link_len] = '\0';
|
||||||
|
|
||||||
|
if (full)
|
||||||
|
path = strdup(link_buf);
|
||||||
|
else
|
||||||
|
path = strdup(link_buf + strlen(mnt));
|
||||||
|
if (!path)
|
||||||
|
fprintf(stderr,
|
||||||
|
"Failed to allocate memory for cgroup2 path\n");
|
||||||
|
|
||||||
|
out:
|
||||||
|
close(fd);
|
||||||
|
close(mnt_fd);
|
||||||
|
free(mnt);
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
int make_path(const char *path, mode_t mode)
|
int make_path(const char *path, mode_t mode)
|
||||||
{
|
{
|
||||||
char *dir, *delim;
|
char *dir, *delim;
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,9 @@ devlink-health \- devlink health reporting and recovery
|
||||||
.RI "[ "
|
.RI "[ "
|
||||||
.BR auto_recover " { " true " | " false " } "
|
.BR auto_recover " { " true " | " false " } "
|
||||||
.RI "]"
|
.RI "]"
|
||||||
|
.RI "[ "
|
||||||
|
.BR auto_dump " { " true " | " false " } "
|
||||||
|
.RI "]"
|
||||||
|
|
||||||
.ti -8
|
.ti -8
|
||||||
.B devlink health help
|
.B devlink health help
|
||||||
|
|
@ -131,8 +134,8 @@ the next "devlink health dump show" command.
|
||||||
- specifies the reporter's name registered on the devlink device.
|
- specifies the reporter's name registered on the devlink device.
|
||||||
|
|
||||||
.SS devlink health set - Configure health reporter.
|
.SS devlink health set - Configure health reporter.
|
||||||
Please note that this command is not supported on a reporter which
|
Please note that some params are not supported on a reporter which
|
||||||
doesn't support a recovery method.
|
doesn't support a recovery or dump method.
|
||||||
|
|
||||||
.PP
|
.PP
|
||||||
.I "DEV"
|
.I "DEV"
|
||||||
|
|
@ -150,6 +153,10 @@ Time interval between consecutive auto recoveries.
|
||||||
.BR auto_recover " { " true " | " false " } "
|
.BR auto_recover " { " true " | " false " } "
|
||||||
Indicates whether the devlink should execute automatic recover on error.
|
Indicates whether the devlink should execute automatic recover on error.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR auto_dump " { " true " | " false " } "
|
||||||
|
Indicates whether the devlink should execute automatic dump on error.
|
||||||
|
|
||||||
.SH "EXAMPLES"
|
.SH "EXAMPLES"
|
||||||
.PP
|
.PP
|
||||||
devlink health show
|
devlink health show
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ ip-address \- protocol address management
|
||||||
|
|
||||||
.ti -8
|
.ti -8
|
||||||
.IR CONFFLAG " := "
|
.IR CONFFLAG " := "
|
||||||
.RB "[ " home " | " mngtmpaddr " | " nodad " | " noprefixroute " | " autojoin " ]"
|
.RB "[ " home " | " mngtmpaddr " | " nodad " | " optimstic " | " noprefixroute " | " autojoin " ]"
|
||||||
|
|
||||||
.ti -8
|
.ti -8
|
||||||
.IR LIFETIME " := [ "
|
.IR LIFETIME " := [ "
|
||||||
|
|
@ -258,6 +258,11 @@ stateless auto-configuration was active.
|
||||||
(IPv6 only) do not perform Duplicate Address Detection (RFC 4862) when
|
(IPv6 only) do not perform Duplicate Address Detection (RFC 4862) when
|
||||||
adding this address.
|
adding this address.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B optimistic
|
||||||
|
(IPv6 only) When performing Duplicate Address Detection, use the RFC 4429
|
||||||
|
optimistic variant.
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B noprefixroute
|
.B noprefixroute
|
||||||
Do not automatically create a route for the network prefix of the added
|
Do not automatically create a route for the network prefix of the added
|
||||||
|
|
|
||||||
|
|
@ -1163,8 +1163,8 @@ It must be an address on another interface on this host.
|
||||||
.BR erspan_ver " \fIversion "
|
.BR erspan_ver " \fIversion "
|
||||||
- specifies the ERSPAN version number.
|
- specifies the ERSPAN version number.
|
||||||
.IR version
|
.IR version
|
||||||
indicates the ERSPAN version to be created: 1 for version 1 (type II)
|
indicates the ERSPAN version to be created: 0 for version 0 type I,
|
||||||
or 2 for version 2 (type III).
|
1 for version 1 (type II) or 2 for version 2 (type III).
|
||||||
|
|
||||||
.sp
|
.sp
|
||||||
.BR erspan " \fIIDX "
|
.BR erspan " \fIIDX "
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,8 @@ ip-macsec \- MACsec device configuration
|
||||||
] [
|
] [
|
||||||
.BR validate " { " strict " | " check " | " disabled " } ] ["
|
.BR validate " { " strict " | " check " | " disabled " } ] ["
|
||||||
.BI encodingsa " SA"
|
.BI encodingsa " SA"
|
||||||
|
] [
|
||||||
|
.BR offload " { " off " | " phy " | " mac " }"
|
||||||
]
|
]
|
||||||
|
|
||||||
.BI "ip macsec add " DEV " tx sa"
|
.BI "ip macsec add " DEV " tx sa"
|
||||||
|
|
@ -54,7 +56,7 @@ ip-macsec \- MACsec device configuration
|
||||||
.RI "{ " 0..3 " }"
|
.RI "{ " 0..3 " }"
|
||||||
|
|
||||||
.BI "ip macsec offload " DEV
|
.BI "ip macsec offload " DEV
|
||||||
.RB "{ " off " | " phy " }"
|
.RB "{ " off " | " phy " | " mac " }"
|
||||||
|
|
||||||
.B ip macsec show
|
.B ip macsec show
|
||||||
.RI [ " DEV " ]
|
.RI [ " DEV " ]
|
||||||
|
|
@ -86,7 +88,7 @@ type.
|
||||||
|
|
||||||
.SH EXAMPLES
|
.SH EXAMPLES
|
||||||
.PP
|
.PP
|
||||||
.SS Create a MACsec device on link eth0
|
.SS Create a MACsec device on link eth0 (offload is disabled by default)
|
||||||
.nf
|
.nf
|
||||||
# ip link add link eth0 macsec0 type macsec port 11 encrypt on
|
# ip link add link eth0 macsec0 type macsec port 11 encrypt on
|
||||||
.PP
|
.PP
|
||||||
|
|
@ -109,6 +111,10 @@ type.
|
||||||
.SS Configure offloading on an interface
|
.SS Configure offloading on an interface
|
||||||
.nf
|
.nf
|
||||||
# ip macsec offload macsec0 phy
|
# ip macsec offload macsec0 phy
|
||||||
|
.PP
|
||||||
|
.SS Configure offloading upon MACsec device creation
|
||||||
|
.nf
|
||||||
|
# ip link add link eth0 macsec0 type macsec port 11 encrypt on offload mac
|
||||||
|
|
||||||
.SH NOTES
|
.SH NOTES
|
||||||
This tool can be used to configure the 802.1AE keys of the interface. Note that 802.1AE uses GCM-AES
|
This tool can be used to configure the 802.1AE keys of the interface. Note that 802.1AE uses GCM-AES
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,141 @@
|
||||||
|
.TH IP\-MPTCP 8 "4 Apr 2020" "iproute2" "Linux"
|
||||||
|
.SH "NAME"
|
||||||
|
ip-mptcp \- MPTCP path manager configuration
|
||||||
|
.SH "SYNOPSIS"
|
||||||
|
.ad l
|
||||||
|
.in +8
|
||||||
|
.ti -8
|
||||||
|
.B ip
|
||||||
|
.RI "[ " OPTIONS " ]"
|
||||||
|
.B mptcp
|
||||||
|
.RB "{ "
|
||||||
|
.B endpoint
|
||||||
|
.RB " | "
|
||||||
|
.B limits
|
||||||
|
.RB " | "
|
||||||
|
.B help
|
||||||
|
.RB " }"
|
||||||
|
.sp
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.BR "ip mptcp endpoint add "
|
||||||
|
.IR IFADDR
|
||||||
|
.RB "[ " dev
|
||||||
|
.IR IFNAME " ]"
|
||||||
|
.RB "[ " id
|
||||||
|
.I ID
|
||||||
|
.RB "] [ "
|
||||||
|
.I FLAG-LIST
|
||||||
|
.RB "] "
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.BR "ip mptcp endpoint del id "
|
||||||
|
.I ID
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.BR "ip mptcp endpoint show "
|
||||||
|
.RB "[ " id
|
||||||
|
.I ID
|
||||||
|
.RB "]"
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.BR "ip mptcp endpoint flush"
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.IR FLAG-LIST " := [ " FLAG-LIST " ] " FLAG
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.IR FLAG " := ["
|
||||||
|
.B signal
|
||||||
|
.RB "|"
|
||||||
|
.B subflow
|
||||||
|
.RB "|"
|
||||||
|
.B backup
|
||||||
|
.RB "]"
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.BR "ip mptcp limits set "
|
||||||
|
.RB "[ "
|
||||||
|
.B subflow
|
||||||
|
.IR SUBFLOW_NR " ]"
|
||||||
|
.RB "[ "
|
||||||
|
.B add_addr_accepted
|
||||||
|
.IR ADD_ADDR_ACCEPTED_NR " ]"
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.BR "ip mptcp limits show"
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
|
||||||
|
MPTCP is a transport protocol built on top of TCP that allows TCP
|
||||||
|
connections to use multiple paths to maximize resource usage and increase
|
||||||
|
redundancy. The ip-mptcp sub-commands allow configuring several aspects of the
|
||||||
|
MPTCP path manager, which is in charge of subflows creation:
|
||||||
|
|
||||||
|
.P
|
||||||
|
The
|
||||||
|
.B endpoint
|
||||||
|
object specifies the IP addresses that will be used and/or announced for
|
||||||
|
additional subflows:
|
||||||
|
|
||||||
|
.TS
|
||||||
|
l l.
|
||||||
|
ip mptcp endpoint add add new MPTCP endpoint
|
||||||
|
ip mptcp endpoint delete delete existing MPTCP endpoint
|
||||||
|
ip mptcp endpoint show get existing MPTCP endpoint
|
||||||
|
ip mptcp endpoint flush flush all existing MPTCP endpoints
|
||||||
|
.TE
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.IR ID
|
||||||
|
is a unique numeric identifier for the given endpoint
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR signal
|
||||||
|
the endpoint will be announced/signalled to each peer via an ADD_ADDR MPTCP
|
||||||
|
sub-option
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR subflow
|
||||||
|
if additional subflow creation is allowed by MPTCP limits, the endpoint will
|
||||||
|
be used as the source address to create an additional subflow after that
|
||||||
|
the MPTCP connection is established.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR backup
|
||||||
|
the endpoint will be announced as a backup address, if this is a
|
||||||
|
.BR signal
|
||||||
|
endpoint, or the subflow will be created as a backup one if this is a
|
||||||
|
.BR subflow
|
||||||
|
endpoint
|
||||||
|
|
||||||
|
.sp
|
||||||
|
.PP
|
||||||
|
The
|
||||||
|
.B limits
|
||||||
|
object specifies the constraints for subflow creations:
|
||||||
|
|
||||||
|
.TS
|
||||||
|
l l.
|
||||||
|
ip mptcp limits show get current MPTCP subflow creation limits
|
||||||
|
ip mptcp limits set change the MPTCP subflow creation limits
|
||||||
|
.TE
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.IR SUBFLOW_NR
|
||||||
|
specifies the maximum number of additional subflows allowed for each MPTCP
|
||||||
|
connection. Additional subflows can be created due to: incoming accepted
|
||||||
|
ADD_ADDR option, local
|
||||||
|
.BR subflow
|
||||||
|
endpoints, additional subflows started by the peer.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.IR ADD_ADDR_ACCEPTED_NR
|
||||||
|
specifies the maximum number of ADD_ADDR suboptions accepted for each MPTCP
|
||||||
|
connection. The MPTCP path manager will try to create a new subflow for
|
||||||
|
each accepted ADD_ADDR option, respecting the
|
||||||
|
.IR SUBFLOW_NR
|
||||||
|
limit.
|
||||||
|
|
||||||
|
.SH AUTHOR
|
||||||
|
Original Manpage by Paolo Abeni <pabeni@redhat.com>
|
||||||
|
|
@ -22,7 +22,7 @@ ip \- show / manipulate routing, network devices, interfaces and tunnels
|
||||||
.BR link " | " address " | " addrlabel " | " route " | " rule " | " neigh " | "\
|
.BR link " | " address " | " addrlabel " | " route " | " rule " | " neigh " | "\
|
||||||
ntable " | " tunnel " | " tuntap " | " maddress " | " mroute " | " mrule " | "\
|
ntable " | " tunnel " | " tuntap " | " maddress " | " mroute " | " mrule " | "\
|
||||||
monitor " | " xfrm " | " netns " | " l2tp " | " tcp_metrics " | " token " | "\
|
monitor " | " xfrm " | " netns " | " l2tp " | " tcp_metrics " | " token " | "\
|
||||||
macsec " | " vrf " }"
|
macsec " | " vrf " | " mptcp " }"
|
||||||
.sp
|
.sp
|
||||||
|
|
||||||
.ti -8
|
.ti -8
|
||||||
|
|
@ -268,6 +268,10 @@ readability.
|
||||||
.B monitor
|
.B monitor
|
||||||
- watch for netlink messages.
|
- watch for netlink messages.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B mptcp
|
||||||
|
- manage MPTCP path manager.
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B mroute
|
.B mroute
|
||||||
- multicast routing cache entry.
|
- multicast routing cache entry.
|
||||||
|
|
@ -405,6 +409,7 @@ was written by Alexey N. Kuznetsov and added in Linux 2.2.
|
||||||
.BR ip-link (8),
|
.BR ip-link (8),
|
||||||
.BR ip-maddress (8),
|
.BR ip-maddress (8),
|
||||||
.BR ip-monitor (8),
|
.BR ip-monitor (8),
|
||||||
|
.BR ip-mptcp (8),
|
||||||
.BR ip-mroute (8),
|
.BR ip-mroute (8),
|
||||||
.BR ip-neighbour (8),
|
.BR ip-neighbour (8),
|
||||||
.BR ip-netns (8),
|
.BR ip-netns (8),
|
||||||
|
|
|
||||||
|
|
@ -261,6 +261,11 @@ the pacing rate and max pacing rate
|
||||||
.TP
|
.TP
|
||||||
.B rcv_space:<rcv_space>
|
.B rcv_space:<rcv_space>
|
||||||
a helper variable for TCP internal auto tuning socket receive buffer
|
a helper variable for TCP internal auto tuning socket receive buffer
|
||||||
|
.P
|
||||||
|
.TP
|
||||||
|
.B tcp-ulp-mptcp flags:[MmBbJjecv] token:<rem_token(rem_id)/loc_token(loc_id)> seq:<sn> sfseq:<ssn> ssnoff:<off> maplen:<maplen>
|
||||||
|
MPTCP subflow information
|
||||||
|
.P
|
||||||
.RE
|
.RE
|
||||||
.TP
|
.TP
|
||||||
.B \-\-tos
|
.B \-\-tos
|
||||||
|
|
@ -281,6 +286,15 @@ Class id set by net_cls cgroup. If class is zero this shows priority
|
||||||
set by SO_PRIORITY.
|
set by SO_PRIORITY.
|
||||||
.RE
|
.RE
|
||||||
.TP
|
.TP
|
||||||
|
.B \-\-cgroup
|
||||||
|
Show cgroup information. Below fields may appear:
|
||||||
|
.RS
|
||||||
|
.P
|
||||||
|
.TP
|
||||||
|
.B cgroup
|
||||||
|
Cgroup v2 pathname. This pathname is relative to the mount point of the hierarchy.
|
||||||
|
.RE
|
||||||
|
.TP
|
||||||
.B \-K, \-\-kill
|
.B \-K, \-\-kill
|
||||||
Attempts to forcibly close sockets. This option displays sockets that are
|
Attempts to forcibly close sockets. This option displays sockets that are
|
||||||
successfully closed and silently skips sockets that the kernel does not support
|
successfully closed and silently skips sockets that the kernel does not support
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,13 @@ flower \- flow based traffic control filter
|
||||||
.IR TOS " | "
|
.IR TOS " | "
|
||||||
.B enc_ttl
|
.B enc_ttl
|
||||||
.IR TTL " | "
|
.IR TTL " | "
|
||||||
|
{
|
||||||
.B geneve_opts
|
.B geneve_opts
|
||||||
|
|
|
||||||
|
.B vxlan_opts
|
||||||
|
|
|
||||||
|
.B erspan_opts
|
||||||
|
}
|
||||||
.IR OPTIONS " | "
|
.IR OPTIONS " | "
|
||||||
.BR ip_flags
|
.BR ip_flags
|
||||||
.IR IP_FLAGS
|
.IR IP_FLAGS
|
||||||
|
|
@ -332,6 +338,10 @@ Match the connection zone, and can be masked.
|
||||||
.RE
|
.RE
|
||||||
.TP
|
.TP
|
||||||
.BI geneve_opts " OPTIONS"
|
.BI geneve_opts " OPTIONS"
|
||||||
|
.TQ
|
||||||
|
.BI vxlan_opts " OPTIONS"
|
||||||
|
.TQ
|
||||||
|
.BI erspan_opts " OPTIONS"
|
||||||
Match on IP tunnel metadata. Key id
|
Match on IP tunnel metadata. Key id
|
||||||
.I NUMBER
|
.I NUMBER
|
||||||
is a 32 bit tunnel key id (e.g. VNI for VXLAN tunnel).
|
is a 32 bit tunnel key id (e.g. VNI for VXLAN tunnel).
|
||||||
|
|
@ -352,6 +362,21 @@ the masks is missing, \fBtc\fR assumes a full-length match. The options can
|
||||||
be described in the form CLASS:TYPE:DATA/CLASS_MASK:TYPE_MASK:DATA_MASK,
|
be described in the form CLASS:TYPE:DATA/CLASS_MASK:TYPE_MASK:DATA_MASK,
|
||||||
where CLASS is represented as a 16bit hexadecimal value, TYPE as an 8bit
|
where CLASS is represented as a 16bit hexadecimal value, TYPE as an 8bit
|
||||||
hexadecimal value and DATA as a variable length hexadecimal value.
|
hexadecimal value and DATA as a variable length hexadecimal value.
|
||||||
|
vxlan_opts
|
||||||
|
.I OPTIONS
|
||||||
|
doesn't support multiple options, and it consists of a key followed by a slash
|
||||||
|
and corresponding mask. If the mask is missing, \fBtc\fR assumes a full-length
|
||||||
|
match. The option can be described in the form GBP/GBP_MASK, where GBP is
|
||||||
|
represented as a 32bit number.
|
||||||
|
erspan_opts
|
||||||
|
.I OPTIONS
|
||||||
|
doesn't support multiple options, and it consists of a key followed by a slash
|
||||||
|
and corresponding mask. If the mask is missing, \fBtc\fR assumes a full-length
|
||||||
|
match. The option can be described in the form
|
||||||
|
VERSION:INDEX:DIR:HWID/VERSION:INDEX_MASK:DIR_MASK:HWID_MASK, where VERSION is
|
||||||
|
represented as a 8bit number, INDEX as an 32bit number, DIR and HWID as a 8bit
|
||||||
|
number. Multiple options is not supported. Note INDEX/INDEX_MASK is used when
|
||||||
|
VERSION is 1, and DIR/DIR_MASK and HWID/HWID_MASK are used when VERSION is 2.
|
||||||
.TP
|
.TP
|
||||||
.BI ip_flags " IP_FLAGS"
|
.BI ip_flags " IP_FLAGS"
|
||||||
.I IP_FLAGS
|
.I IP_FLAGS
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,123 @@
|
||||||
|
.TH GATE 8 "12 Mar 2020" "iproute2" "Linux"
|
||||||
|
.SH NAME
|
||||||
|
gate \- Stream Gate Action
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B tc " ... " action gate
|
||||||
|
.ti +8
|
||||||
|
.B [ base-time
|
||||||
|
BASETIME ]
|
||||||
|
.B [ clockid
|
||||||
|
CLOCKID ]
|
||||||
|
.ti +8
|
||||||
|
.B sched-entry
|
||||||
|
<gate state> <interval 1> [ <internal priority> <max octets> ]
|
||||||
|
.ti +8
|
||||||
|
.B sched-entry
|
||||||
|
<gate state> <interval 2> [ <internal priority> <max octets> ]
|
||||||
|
.ti +8
|
||||||
|
.B sched-entry
|
||||||
|
<gate state> <interval 3> [ <internal priority> <max octets> ]
|
||||||
|
.ti +8
|
||||||
|
.B ......
|
||||||
|
.ti +8
|
||||||
|
.B sched-entry
|
||||||
|
<gate state> <interval N> [ <internal priority> <max octets> ]
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
GATE action allows specified ingress frames can be passed at
|
||||||
|
specific time slot, or be dropped at specific time slot. Tc filter
|
||||||
|
filters the ingress frames, then tc gate action would specify which time
|
||||||
|
slot and how many bytes these frames can be passed to device and
|
||||||
|
which time slot frames would be dropped.
|
||||||
|
Gate action also assign a base-time to tell when the entry list start.
|
||||||
|
Then gate action would start to repeat the gate entry list cyclically
|
||||||
|
at the start base-time.
|
||||||
|
For the software simulation, gate action requires the user assign reference
|
||||||
|
time clock type.
|
||||||
|
|
||||||
|
.SH PARAMETERS
|
||||||
|
|
||||||
|
.TP
|
||||||
|
base-time
|
||||||
|
.br
|
||||||
|
Specifies the instant in nanoseconds, defining the time when the schedule
|
||||||
|
starts. If 'base-time' is a time in the past, the schedule will start at
|
||||||
|
|
||||||
|
base-time + (N * cycle-time)
|
||||||
|
|
||||||
|
where N is the smallest integer so the resulting time is greater than
|
||||||
|
"now", and "cycle-time" is the sum of all the intervals of the entries
|
||||||
|
in the schedule. Without base-time specified, will default to be 0.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
clockid
|
||||||
|
.br
|
||||||
|
Specifies the clock to be used by qdisc's internal timer for measuring
|
||||||
|
time and scheduling events. Not valid if gate action is used for offloading
|
||||||
|
filter.
|
||||||
|
For example, tc filter command with
|
||||||
|
.B skip_sw
|
||||||
|
parameter.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
sched-entry
|
||||||
|
.br
|
||||||
|
There may multiple
|
||||||
|
.B sched-entry
|
||||||
|
parameters in a single schedule. Each one has the format:
|
||||||
|
|
||||||
|
sched-entry <gate state> <interval> [ <internal priority> <max octets> ]
|
||||||
|
|
||||||
|
.br
|
||||||
|
<gate state> means gate states. 'open' keep gate open, 'close' keep gate close.
|
||||||
|
.br
|
||||||
|
<interval> means how much nano seconds for this time slot.
|
||||||
|
.br
|
||||||
|
<internal priority> means internal priority value. Present of the
|
||||||
|
internal receiving queue for this stream. "-1" means wildcard.
|
||||||
|
<internal priority> and <max octets> can be omit default to be "-1" which both
|
||||||
|
value to be "-1" for this <sched-entry>.
|
||||||
|
.br
|
||||||
|
<max octets> means how many octets size could pass in this time slot. Dropped
|
||||||
|
if overlimited. "-1" means wildcard. <max octets> can be omit default to be
|
||||||
|
"-1" which value to be "-1" for this <sched-entry>.
|
||||||
|
.br
|
||||||
|
Note that <internal priority> and <max octets> are nothing meaning for gate state
|
||||||
|
is "close" in a "sched-entry". All frames are dropped when "sched-entry" with
|
||||||
|
"close" state.
|
||||||
|
|
||||||
|
.SH EXAMPLES
|
||||||
|
|
||||||
|
The following example shows tc filter frames source ip match to the
|
||||||
|
192.168.0.20 will keep the gate open for 200ms and limit the traffic to 8MB
|
||||||
|
in this sched-entry. Then keep the traffic gate to be close for 100ms.
|
||||||
|
Frames arrived at gate close state would be dropped. Then the cycle would
|
||||||
|
run the gate entries periodically. The schedule will start at instant 200.0s
|
||||||
|
using the reference CLOCK_TAI. The schedule is composed of two entries
|
||||||
|
each of 300ms duration.
|
||||||
|
|
||||||
|
.EX
|
||||||
|
# tc qdisc add dev eth0 ingress
|
||||||
|
# tc filter add dev eth0 parent ffff: protocol ip \\
|
||||||
|
flower skip_hw src_ip 192.168.0.20 \\
|
||||||
|
action gate index 2 clockid CLOCK_TAI \\
|
||||||
|
base-time 200000000000ns \\
|
||||||
|
sched-entry open 200000000ns -1 8000000b \\
|
||||||
|
sched-entry close 100000000ns
|
||||||
|
|
||||||
|
.EE
|
||||||
|
|
||||||
|
Following commands is an example to filter a stream source mac match to the
|
||||||
|
10:00:80:00:00:00 icmp frames will be dropped at any time with cycle 200ms.
|
||||||
|
With a default basetime 0 and clockid is CLOCK_TAI as default.
|
||||||
|
|
||||||
|
.EX
|
||||||
|
# tc qdisc add dev eth0 ingress
|
||||||
|
# tc filter add dev eth0 parent ffff: protocol ip \\
|
||||||
|
flower ip_proto icmp dst_mac 10:00:80:00:00:00 \\
|
||||||
|
action gate index 12 sched-entry close 200000000ns
|
||||||
|
|
||||||
|
.EE
|
||||||
|
|
||||||
|
.SH AUTHORS
|
||||||
|
Po Liu <Po.Liu@nxp.com>
|
||||||
|
|
@ -66,8 +66,12 @@ options.
|
||||||
.B id
|
.B id
|
||||||
,
|
,
|
||||||
.B dst_port
|
.B dst_port
|
||||||
and
|
,
|
||||||
.B geneve_opts
|
.B geneve_opts
|
||||||
|
,
|
||||||
|
.B vxlan_opts
|
||||||
|
and
|
||||||
|
.B erspan_opts
|
||||||
are optional.
|
are optional.
|
||||||
.RS
|
.RS
|
||||||
.TP
|
.TP
|
||||||
|
|
@ -91,6 +95,20 @@ is specified in the form CLASS:TYPE:DATA, where CLASS is represented as a
|
||||||
variable length hexadecimal value. Additionally multiple options may be
|
variable length hexadecimal value. Additionally multiple options may be
|
||||||
listed using a comma delimiter.
|
listed using a comma delimiter.
|
||||||
.TP
|
.TP
|
||||||
|
.B vxlan_opts
|
||||||
|
Vxlan metatdata options.
|
||||||
|
.B vxlan_opts
|
||||||
|
is specified in the form GBP, as a 32bit number. Multiple options is not
|
||||||
|
supported.
|
||||||
|
.TP
|
||||||
|
.B erspan_opts
|
||||||
|
Erspan metatdata options.
|
||||||
|
.B erspan_opts
|
||||||
|
is specified in the form VERSION:INDEX:DIR:HWID, where VERSION is represented
|
||||||
|
as a 8bit number, INDEX as an 32bit number, DIR and HWID as a 8bit number.
|
||||||
|
Multiple options is not supported. Note INDEX is used when VERSION is 1,
|
||||||
|
and DIR and HWID are used when VERSION is 2.
|
||||||
|
.TP
|
||||||
.B tos
|
.B tos
|
||||||
Outer header TOS
|
Outer header TOS
|
||||||
.TP
|
.TP
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
# SPDX-License-Identifier: GPL-2.0
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
SSOBJ=ss.o ssfilter.tab.o
|
SSOBJ=ss.o ssfilter_check.o ssfilter.tab.o
|
||||||
LNSTATOBJ=lnstat.o lnstat_util.o
|
LNSTATOBJ=lnstat.o lnstat_util.o
|
||||||
|
|
||||||
TARGETS=ss nstat ifstat rtacct lnstat
|
TARGETS=ss nstat ifstat rtacct lnstat
|
||||||
|
|
|
||||||
140
misc/ss.c
140
misc/ss.c
|
|
@ -29,6 +29,7 @@
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#include "ss_util.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "rt_names.h"
|
#include "rt_names.h"
|
||||||
#include "ll_map.h"
|
#include "ll_map.h"
|
||||||
|
|
@ -36,10 +37,9 @@
|
||||||
#include "namespace.h"
|
#include "namespace.h"
|
||||||
#include "SNAPSHOT.h"
|
#include "SNAPSHOT.h"
|
||||||
#include "rt_names.h"
|
#include "rt_names.h"
|
||||||
|
#include "cg_map.h"
|
||||||
|
|
||||||
#include <linux/tcp.h>
|
#include <linux/tcp.h>
|
||||||
#include <linux/sock_diag.h>
|
|
||||||
#include <linux/inet_diag.h>
|
|
||||||
#include <linux/unix_diag.h>
|
#include <linux/unix_diag.h>
|
||||||
#include <linux/netdevice.h> /* for MAX_ADDR_LEN */
|
#include <linux/netdevice.h> /* for MAX_ADDR_LEN */
|
||||||
#include <linux/filter.h>
|
#include <linux/filter.h>
|
||||||
|
|
@ -53,6 +53,7 @@
|
||||||
#include <linux/tipc_netlink.h>
|
#include <linux/tipc_netlink.h>
|
||||||
#include <linux/tipc_sockets_diag.h>
|
#include <linux/tipc_sockets_diag.h>
|
||||||
#include <linux/tls.h>
|
#include <linux/tls.h>
|
||||||
|
#include <linux/mptcp.h>
|
||||||
|
|
||||||
/* AF_VSOCK/PF_VSOCK is only provided since glibc 2.18 */
|
/* AF_VSOCK/PF_VSOCK is only provided since glibc 2.18 */
|
||||||
#ifndef PF_VSOCK
|
#ifndef PF_VSOCK
|
||||||
|
|
@ -62,24 +63,10 @@
|
||||||
#define AF_VSOCK PF_VSOCK
|
#define AF_VSOCK PF_VSOCK
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define MAGIC_SEQ 123456
|
|
||||||
#define BUF_CHUNK (1024 * 1024) /* Buffer chunk allocation size */
|
#define BUF_CHUNK (1024 * 1024) /* Buffer chunk allocation size */
|
||||||
#define BUF_CHUNKS_MAX 5 /* Maximum number of allocated buffer chunks */
|
#define BUF_CHUNKS_MAX 5 /* Maximum number of allocated buffer chunks */
|
||||||
#define LEN_ALIGN(x) (((x) + 1) & ~1)
|
#define LEN_ALIGN(x) (((x) + 1) & ~1)
|
||||||
|
|
||||||
#define DIAG_REQUEST(_req, _r) \
|
|
||||||
struct { \
|
|
||||||
struct nlmsghdr nlh; \
|
|
||||||
_r; \
|
|
||||||
} _req = { \
|
|
||||||
.nlh = { \
|
|
||||||
.nlmsg_type = SOCK_DIAG_BY_FAMILY, \
|
|
||||||
.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST,\
|
|
||||||
.nlmsg_seq = MAGIC_SEQ, \
|
|
||||||
.nlmsg_len = sizeof(_req), \
|
|
||||||
}, \
|
|
||||||
}
|
|
||||||
|
|
||||||
#if HAVE_SELINUX
|
#if HAVE_SELINUX
|
||||||
#include <selinux/selinux.h>
|
#include <selinux/selinux.h>
|
||||||
#else
|
#else
|
||||||
|
|
@ -122,6 +109,7 @@ static int follow_events;
|
||||||
static int sctp_ino;
|
static int sctp_ino;
|
||||||
static int show_tipcinfo;
|
static int show_tipcinfo;
|
||||||
static int show_tos;
|
static int show_tos;
|
||||||
|
static int show_cgroup;
|
||||||
int oneline;
|
int oneline;
|
||||||
|
|
||||||
enum col_id {
|
enum col_id {
|
||||||
|
|
@ -797,6 +785,7 @@ struct sockstat {
|
||||||
char *name;
|
char *name;
|
||||||
char *peer_name;
|
char *peer_name;
|
||||||
__u32 mark;
|
__u32 mark;
|
||||||
|
__u64 cgroup_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct dctcpstat {
|
struct dctcpstat {
|
||||||
|
|
@ -1417,6 +1406,9 @@ static void sock_details_print(struct sockstat *s)
|
||||||
|
|
||||||
if (s->mark)
|
if (s->mark)
|
||||||
out(" fwmark:0x%x", s->mark);
|
out(" fwmark:0x%x", s->mark);
|
||||||
|
|
||||||
|
if (s->cgroup_id)
|
||||||
|
out(" cgroup:%s", cg_id_to_path(s->cgroup_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sock_addr_print(const char *addr, char *delim, const char *port,
|
static void sock_addr_print(const char *addr, char *delim, const char *port,
|
||||||
|
|
@ -1643,6 +1635,7 @@ struct aafilter {
|
||||||
unsigned int iface;
|
unsigned int iface;
|
||||||
__u32 mark;
|
__u32 mark;
|
||||||
__u32 mask;
|
__u32 mask;
|
||||||
|
__u64 cgroup_id;
|
||||||
struct aafilter *next;
|
struct aafilter *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1770,6 +1763,12 @@ static int run_ssfilter(struct ssfilter *f, struct sockstat *s)
|
||||||
struct aafilter *a = (void *)f->pred;
|
struct aafilter *a = (void *)f->pred;
|
||||||
|
|
||||||
return (s->mark & a->mask) == a->mark;
|
return (s->mark & a->mask) == a->mark;
|
||||||
|
}
|
||||||
|
case SSF_CGROUPCOND:
|
||||||
|
{
|
||||||
|
struct aafilter *a = (void *)f->pred;
|
||||||
|
|
||||||
|
return s->cgroup_id == a->cgroup_id;
|
||||||
}
|
}
|
||||||
/* Yup. It is recursion. Sorry. */
|
/* Yup. It is recursion. Sorry. */
|
||||||
case SSF_AND:
|
case SSF_AND:
|
||||||
|
|
@ -1961,6 +1960,23 @@ static int ssfilter_bytecompile(struct ssfilter *f, char **bytecode)
|
||||||
{ a->mark, a->mask},
|
{ a->mark, a->mask},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return inslen;
|
||||||
|
}
|
||||||
|
case SSF_CGROUPCOND:
|
||||||
|
{
|
||||||
|
struct aafilter *a = (void *)f->pred;
|
||||||
|
struct instr {
|
||||||
|
struct inet_diag_bc_op op;
|
||||||
|
__u64 cgroup_id;
|
||||||
|
} __attribute__((packed));
|
||||||
|
int inslen = sizeof(struct instr);
|
||||||
|
|
||||||
|
if (!(*bytecode = malloc(inslen))) abort();
|
||||||
|
((struct instr *)*bytecode)[0] = (struct instr) {
|
||||||
|
{ INET_DIAG_BC_CGROUP_COND, inslen, inslen + 4 },
|
||||||
|
a->cgroup_id,
|
||||||
|
};
|
||||||
|
|
||||||
return inslen;
|
return inslen;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|
@ -2300,6 +2316,22 @@ void *parse_markmask(const char *markmask)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void *parse_cgroupcond(const char *path)
|
||||||
|
{
|
||||||
|
struct aafilter *res;
|
||||||
|
__u64 id;
|
||||||
|
|
||||||
|
id = get_cgroup2_id(path);
|
||||||
|
if (!id)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
res = malloc(sizeof(*res));
|
||||||
|
if (res)
|
||||||
|
res->cgroup_id = id;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
static void proc_ctx_print(struct sockstat *s)
|
static void proc_ctx_print(struct sockstat *s)
|
||||||
{
|
{
|
||||||
char *buf;
|
char *buf;
|
||||||
|
|
@ -2845,6 +2877,59 @@ static void tcp_tls_conf(const char *name, struct rtattr *attr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void mptcp_subflow_info(struct rtattr *tb[])
|
||||||
|
{
|
||||||
|
u_int32_t flags = 0;
|
||||||
|
|
||||||
|
if (tb[MPTCP_SUBFLOW_ATTR_FLAGS]) {
|
||||||
|
char caps[32 + 1] = { 0 }, *cap = &caps[0];
|
||||||
|
|
||||||
|
flags = rta_getattr_u32(tb[MPTCP_SUBFLOW_ATTR_FLAGS]);
|
||||||
|
|
||||||
|
if (flags & MPTCP_SUBFLOW_FLAG_MCAP_REM)
|
||||||
|
*cap++ = 'M';
|
||||||
|
if (flags & MPTCP_SUBFLOW_FLAG_MCAP_LOC)
|
||||||
|
*cap++ = 'm';
|
||||||
|
if (flags & MPTCP_SUBFLOW_FLAG_JOIN_REM)
|
||||||
|
*cap++ = 'J';
|
||||||
|
if (flags & MPTCP_SUBFLOW_FLAG_JOIN_LOC)
|
||||||
|
*cap++ = 'j';
|
||||||
|
if (flags & MPTCP_SUBFLOW_FLAG_BKUP_REM)
|
||||||
|
*cap++ = 'B';
|
||||||
|
if (flags & MPTCP_SUBFLOW_FLAG_BKUP_LOC)
|
||||||
|
*cap++ = 'b';
|
||||||
|
if (flags & MPTCP_SUBFLOW_FLAG_FULLY_ESTABLISHED)
|
||||||
|
*cap++ = 'e';
|
||||||
|
if (flags & MPTCP_SUBFLOW_FLAG_CONNECTED)
|
||||||
|
*cap++ = 'c';
|
||||||
|
if (flags & MPTCP_SUBFLOW_FLAG_MAPVALID)
|
||||||
|
*cap++ = 'v';
|
||||||
|
if (flags)
|
||||||
|
out(" flags:%s", caps);
|
||||||
|
}
|
||||||
|
if (tb[MPTCP_SUBFLOW_ATTR_TOKEN_REM] &&
|
||||||
|
tb[MPTCP_SUBFLOW_ATTR_TOKEN_LOC] &&
|
||||||
|
tb[MPTCP_SUBFLOW_ATTR_ID_REM] &&
|
||||||
|
tb[MPTCP_SUBFLOW_ATTR_ID_LOC])
|
||||||
|
out(" token:%04x(id:%hhu)/%04x(id:%hhu)",
|
||||||
|
rta_getattr_u32(tb[MPTCP_SUBFLOW_ATTR_TOKEN_REM]),
|
||||||
|
rta_getattr_u8(tb[MPTCP_SUBFLOW_ATTR_ID_REM]),
|
||||||
|
rta_getattr_u32(tb[MPTCP_SUBFLOW_ATTR_TOKEN_LOC]),
|
||||||
|
rta_getattr_u8(tb[MPTCP_SUBFLOW_ATTR_ID_LOC]));
|
||||||
|
if (tb[MPTCP_SUBFLOW_ATTR_MAP_SEQ])
|
||||||
|
out(" seq:%llx",
|
||||||
|
rta_getattr_u64(tb[MPTCP_SUBFLOW_ATTR_MAP_SEQ]));
|
||||||
|
if (tb[MPTCP_SUBFLOW_ATTR_MAP_SFSEQ])
|
||||||
|
out(" sfseq:%x",
|
||||||
|
rta_getattr_u32(tb[MPTCP_SUBFLOW_ATTR_MAP_SFSEQ]));
|
||||||
|
if (tb[MPTCP_SUBFLOW_ATTR_SSN_OFFSET])
|
||||||
|
out(" ssnoff:%x",
|
||||||
|
rta_getattr_u32(tb[MPTCP_SUBFLOW_ATTR_SSN_OFFSET]));
|
||||||
|
if (tb[MPTCP_SUBFLOW_ATTR_MAP_DATALEN])
|
||||||
|
out(" maplen:%x",
|
||||||
|
rta_getattr_u32(tb[MPTCP_SUBFLOW_ATTR_MAP_DATALEN]));
|
||||||
|
}
|
||||||
|
|
||||||
#define TCPI_HAS_OPT(info, opt) !!(info->tcpi_options & (opt))
|
#define TCPI_HAS_OPT(info, opt) !!(info->tcpi_options & (opt))
|
||||||
|
|
||||||
static void tcp_show_info(const struct nlmsghdr *nlh, struct inet_diag_msg *r,
|
static void tcp_show_info(const struct nlmsghdr *nlh, struct inet_diag_msg *r,
|
||||||
|
|
@ -3021,6 +3106,14 @@ static void tcp_show_info(const struct nlmsghdr *nlh, struct inet_diag_msg *r,
|
||||||
tcp_tls_conf("rxconf", tlsinfo[TLS_INFO_RXCONF]);
|
tcp_tls_conf("rxconf", tlsinfo[TLS_INFO_RXCONF]);
|
||||||
tcp_tls_conf("txconf", tlsinfo[TLS_INFO_TXCONF]);
|
tcp_tls_conf("txconf", tlsinfo[TLS_INFO_TXCONF]);
|
||||||
}
|
}
|
||||||
|
if (ulpinfo[INET_ULP_INFO_MPTCP]) {
|
||||||
|
struct rtattr *sfinfo[MPTCP_SUBFLOW_ATTR_MAX + 1] =
|
||||||
|
{ 0 };
|
||||||
|
|
||||||
|
parse_rtattr_nested(sfinfo, MPTCP_SUBFLOW_ATTR_MAX,
|
||||||
|
ulpinfo[INET_ULP_INFO_MPTCP]);
|
||||||
|
mptcp_subflow_info(sfinfo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3104,6 +3197,9 @@ static void parse_diag_msg(struct nlmsghdr *nlh, struct sockstat *s)
|
||||||
s->mark = 0;
|
s->mark = 0;
|
||||||
if (tb[INET_DIAG_MARK])
|
if (tb[INET_DIAG_MARK])
|
||||||
s->mark = rta_getattr_u32(tb[INET_DIAG_MARK]);
|
s->mark = rta_getattr_u32(tb[INET_DIAG_MARK]);
|
||||||
|
s->cgroup_id = 0;
|
||||||
|
if (tb[INET_DIAG_CGROUP_ID])
|
||||||
|
s->cgroup_id = rta_getattr_u64(tb[INET_DIAG_CGROUP_ID]);
|
||||||
if (tb[INET_DIAG_PROTOCOL])
|
if (tb[INET_DIAG_PROTOCOL])
|
||||||
s->raw_prot = rta_getattr_u8(tb[INET_DIAG_PROTOCOL]);
|
s->raw_prot = rta_getattr_u8(tb[INET_DIAG_PROTOCOL]);
|
||||||
else
|
else
|
||||||
|
|
@ -3171,6 +3267,11 @@ static int inet_show_sock(struct nlmsghdr *nlh,
|
||||||
out(" class_id:%#x", rta_getattr_u32(tb[INET_DIAG_CLASS_ID]));
|
out(" class_id:%#x", rta_getattr_u32(tb[INET_DIAG_CLASS_ID]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (show_cgroup) {
|
||||||
|
if (tb[INET_DIAG_CGROUP_ID])
|
||||||
|
out(" cgroup:%s", cg_id_to_path(rta_getattr_u64(tb[INET_DIAG_CGROUP_ID])));
|
||||||
|
}
|
||||||
|
|
||||||
if (show_mem || (show_tcpinfo && s->type != IPPROTO_UDP)) {
|
if (show_mem || (show_tcpinfo && s->type != IPPROTO_UDP)) {
|
||||||
if (!oneline)
|
if (!oneline)
|
||||||
out("\n\t");
|
out("\n\t");
|
||||||
|
|
@ -4996,6 +5097,7 @@ static void _usage(FILE *dest)
|
||||||
" --tipcinfo show internal tipc socket information\n"
|
" --tipcinfo show internal tipc socket information\n"
|
||||||
" -s, --summary show socket usage summary\n"
|
" -s, --summary show socket usage summary\n"
|
||||||
" --tos show tos and priority information\n"
|
" --tos show tos and priority information\n"
|
||||||
|
" --cgroup show cgroup information\n"
|
||||||
" -b, --bpf show bpf filter socket information\n"
|
" -b, --bpf show bpf filter socket information\n"
|
||||||
" -E, --events continually display sockets as they are destroyed\n"
|
" -E, --events continually display sockets as they are destroyed\n"
|
||||||
" -Z, --context display process SELinux security contexts\n"
|
" -Z, --context display process SELinux security contexts\n"
|
||||||
|
|
@ -5106,6 +5208,8 @@ static int scan_state(const char *state)
|
||||||
/* Values of 'x' are already used so a non-character is used */
|
/* Values of 'x' are already used so a non-character is used */
|
||||||
#define OPT_XDPSOCK 260
|
#define OPT_XDPSOCK 260
|
||||||
|
|
||||||
|
#define OPT_CGROUP 261
|
||||||
|
|
||||||
static const struct option long_opts[] = {
|
static const struct option long_opts[] = {
|
||||||
{ "numeric", 0, 0, 'n' },
|
{ "numeric", 0, 0, 'n' },
|
||||||
{ "resolve", 0, 0, 'r' },
|
{ "resolve", 0, 0, 'r' },
|
||||||
|
|
@ -5142,6 +5246,7 @@ static const struct option long_opts[] = {
|
||||||
{ "net", 1, 0, 'N' },
|
{ "net", 1, 0, 'N' },
|
||||||
{ "tipcinfo", 0, 0, OPT_TIPCINFO},
|
{ "tipcinfo", 0, 0, OPT_TIPCINFO},
|
||||||
{ "tos", 0, 0, OPT_TOS },
|
{ "tos", 0, 0, OPT_TOS },
|
||||||
|
{ "cgroup", 0, 0, OPT_CGROUP },
|
||||||
{ "kill", 0, 0, 'K' },
|
{ "kill", 0, 0, 'K' },
|
||||||
{ "no-header", 0, 0, 'H' },
|
{ "no-header", 0, 0, 'H' },
|
||||||
{ "xdp", 0, 0, OPT_XDPSOCK},
|
{ "xdp", 0, 0, OPT_XDPSOCK},
|
||||||
|
|
@ -5329,6 +5434,9 @@ int main(int argc, char *argv[])
|
||||||
case OPT_TOS:
|
case OPT_TOS:
|
||||||
show_tos = 1;
|
show_tos = 1;
|
||||||
break;
|
break;
|
||||||
|
case OPT_CGROUP:
|
||||||
|
show_cgroup = 1;
|
||||||
|
break;
|
||||||
case 'K':
|
case 'K':
|
||||||
current_filter.kill = 1;
|
current_filter.kill = 1;
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
#ifndef __SS_UTIL_H__
|
||||||
|
#define __SS_UTIL_H__
|
||||||
|
|
||||||
|
#include <linux/sock_diag.h>
|
||||||
|
#include <linux/inet_diag.h>
|
||||||
|
|
||||||
|
#define MAGIC_SEQ 123456
|
||||||
|
|
||||||
|
#define DIAG_REQUEST(_req, _r) \
|
||||||
|
struct { \
|
||||||
|
struct nlmsghdr nlh; \
|
||||||
|
_r; \
|
||||||
|
} _req = { \
|
||||||
|
.nlh = { \
|
||||||
|
.nlmsg_type = SOCK_DIAG_BY_FAMILY, \
|
||||||
|
.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST,\
|
||||||
|
.nlmsg_seq = MAGIC_SEQ, \
|
||||||
|
.nlmsg_len = sizeof(_req), \
|
||||||
|
}, \
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* __SS_UTIL_H__ */
|
||||||
|
|
@ -1,19 +1,24 @@
|
||||||
/* SPDX-License-Identifier: GPL-2.0 */
|
|
||||||
#define SSF_DCOND 0
|
|
||||||
#define SSF_SCOND 1
|
|
||||||
#define SSF_OR 2
|
|
||||||
#define SSF_AND 3
|
|
||||||
#define SSF_NOT 4
|
|
||||||
#define SSF_D_GE 5
|
|
||||||
#define SSF_D_LE 6
|
|
||||||
#define SSF_S_GE 7
|
|
||||||
#define SSF_S_LE 8
|
|
||||||
#define SSF_S_AUTO 9
|
|
||||||
#define SSF_DEVCOND 10
|
|
||||||
#define SSF_MARKMASK 11
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
enum {
|
||||||
|
SSF_DCOND,
|
||||||
|
SSF_SCOND,
|
||||||
|
SSF_OR,
|
||||||
|
SSF_AND,
|
||||||
|
SSF_NOT,
|
||||||
|
SSF_D_GE,
|
||||||
|
SSF_D_LE,
|
||||||
|
SSF_S_GE,
|
||||||
|
SSF_S_LE,
|
||||||
|
SSF_S_AUTO,
|
||||||
|
SSF_DEVCOND,
|
||||||
|
SSF_MARKMASK,
|
||||||
|
SSF_CGROUPCOND,
|
||||||
|
SSF__MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
bool ssfilter_is_supported(int type);
|
||||||
|
|
||||||
struct ssfilter
|
struct ssfilter
|
||||||
{
|
{
|
||||||
int type;
|
int type;
|
||||||
|
|
@ -25,3 +30,4 @@ int ssfilter_parse(struct ssfilter **f, int argc, char **argv, FILE *fp);
|
||||||
void *parse_hostcond(char *addr, bool is_port);
|
void *parse_hostcond(char *addr, bool is_port);
|
||||||
void *parse_devcond(char *name);
|
void *parse_devcond(char *name);
|
||||||
void *parse_markmask(const char *markmask);
|
void *parse_markmask(const char *markmask);
|
||||||
|
void *parse_cgroupcond(const char *path);
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,14 @@ typedef struct ssfilter * ssfilter_t;
|
||||||
|
|
||||||
static struct ssfilter * alloc_node(int type, void *pred)
|
static struct ssfilter * alloc_node(int type, void *pred)
|
||||||
{
|
{
|
||||||
struct ssfilter *n = malloc(sizeof(*n));
|
struct ssfilter *n;
|
||||||
|
|
||||||
|
if (!ssfilter_is_supported(type)) {
|
||||||
|
fprintf(stderr, "It looks like such filter is not supported! Too old kernel?\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
n = malloc(sizeof(*n));
|
||||||
if (n == NULL)
|
if (n == NULL)
|
||||||
abort();
|
abort();
|
||||||
n->type = type;
|
n->type = type;
|
||||||
|
|
@ -36,7 +43,7 @@ static void yyerror(char *s)
|
||||||
|
|
||||||
%}
|
%}
|
||||||
|
|
||||||
%token HOSTCOND DCOND SCOND DPORT SPORT LEQ GEQ NEQ AUTOBOUND DEVCOND DEVNAME MARKMASK FWMARK
|
%token HOSTCOND DCOND SCOND DPORT SPORT LEQ GEQ NEQ AUTOBOUND DEVCOND DEVNAME MARKMASK FWMARK CGROUPCOND CGROUPPATH
|
||||||
%left '|'
|
%left '|'
|
||||||
%left '&'
|
%left '&'
|
||||||
%nonassoc '!'
|
%nonassoc '!'
|
||||||
|
|
@ -156,6 +163,14 @@ expr: '(' exprlist ')'
|
||||||
{
|
{
|
||||||
$$ = alloc_node(SSF_NOT, alloc_node(SSF_MARKMASK, $3));
|
$$ = alloc_node(SSF_NOT, alloc_node(SSF_MARKMASK, $3));
|
||||||
}
|
}
|
||||||
|
| CGROUPPATH eq CGROUPCOND
|
||||||
|
{
|
||||||
|
$$ = alloc_node(SSF_CGROUPCOND, $3);
|
||||||
|
}
|
||||||
|
| CGROUPPATH NEQ CGROUPCOND
|
||||||
|
{
|
||||||
|
$$ = alloc_node(SSF_NOT, alloc_node(SSF_CGROUPCOND, $3));
|
||||||
|
}
|
||||||
| AUTOBOUND
|
| AUTOBOUND
|
||||||
{
|
{
|
||||||
$$ = alloc_node(SSF_S_AUTO, NULL);
|
$$ = alloc_node(SSF_S_AUTO, NULL);
|
||||||
|
|
@ -276,6 +291,10 @@ int yylex(void)
|
||||||
tok_type = FWMARK;
|
tok_type = FWMARK;
|
||||||
return FWMARK;
|
return FWMARK;
|
||||||
}
|
}
|
||||||
|
if (strcmp(curtok, "cgroup") == 0) {
|
||||||
|
tok_type = CGROUPPATH;
|
||||||
|
return CGROUPPATH;
|
||||||
|
}
|
||||||
if (strcmp(curtok, ">=") == 0 ||
|
if (strcmp(curtok, ">=") == 0 ||
|
||||||
strcmp(curtok, "ge") == 0 ||
|
strcmp(curtok, "ge") == 0 ||
|
||||||
strcmp(curtok, "geq") == 0)
|
strcmp(curtok, "geq") == 0)
|
||||||
|
|
@ -318,6 +337,14 @@ int yylex(void)
|
||||||
}
|
}
|
||||||
return MARKMASK;
|
return MARKMASK;
|
||||||
}
|
}
|
||||||
|
if (tok_type == CGROUPPATH) {
|
||||||
|
yylval = (void*)parse_cgroupcond(curtok);
|
||||||
|
if (yylval == NULL) {
|
||||||
|
fprintf(stderr, "Cannot parse cgroup %s.\n", curtok);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
return CGROUPCOND;
|
||||||
|
}
|
||||||
yylval = (void*)parse_hostcond(curtok, tok_type == SPORT || tok_type == DPORT);
|
yylval = (void*)parse_hostcond(curtok, tok_type == SPORT || tok_type == DPORT);
|
||||||
if (yylval == NULL) {
|
if (yylval == NULL) {
|
||||||
fprintf(stderr, "Cannot parse dst/src address.\n");
|
fprintf(stderr, "Cannot parse dst/src address.\n");
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,103 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include "libnetlink.h"
|
||||||
|
#include "ssfilter.h"
|
||||||
|
#include "ss_util.h"
|
||||||
|
|
||||||
|
static int dummy_filter(struct nlmsghdr *n, void *arg)
|
||||||
|
{
|
||||||
|
/* just stops rtnl_dump_filter() */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool cgroup_filter_check(void)
|
||||||
|
{
|
||||||
|
struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK };
|
||||||
|
DIAG_REQUEST(req, struct inet_diag_req_v2 r);
|
||||||
|
struct instr {
|
||||||
|
struct inet_diag_bc_op op;
|
||||||
|
__u64 cgroup_id;
|
||||||
|
} __attribute__((packed));
|
||||||
|
int inslen = sizeof(struct instr);
|
||||||
|
struct instr instr = {
|
||||||
|
{ INET_DIAG_BC_CGROUP_COND, inslen, inslen + 4 },
|
||||||
|
0
|
||||||
|
};
|
||||||
|
struct rtnl_handle rth;
|
||||||
|
struct iovec iov[3];
|
||||||
|
struct msghdr msg;
|
||||||
|
struct rtattr rta;
|
||||||
|
int ret = false;
|
||||||
|
int iovlen = 3;
|
||||||
|
|
||||||
|
if (rtnl_open_byproto(&rth, 0, NETLINK_SOCK_DIAG))
|
||||||
|
return false;
|
||||||
|
rth.dump = MAGIC_SEQ;
|
||||||
|
rth.flags = RTNL_HANDLE_F_SUPPRESS_NLERR;
|
||||||
|
|
||||||
|
memset(&req.r, 0, sizeof(req.r));
|
||||||
|
req.r.sdiag_family = AF_INET;
|
||||||
|
req.r.sdiag_protocol = IPPROTO_TCP;
|
||||||
|
req.nlh.nlmsg_len += RTA_LENGTH(inslen);
|
||||||
|
|
||||||
|
rta.rta_type = INET_DIAG_REQ_BYTECODE;
|
||||||
|
rta.rta_len = RTA_LENGTH(inslen);
|
||||||
|
|
||||||
|
iov[0] = (struct iovec) { &req, sizeof(req) };
|
||||||
|
iov[1] = (struct iovec) { &rta, sizeof(rta) };
|
||||||
|
iov[2] = (struct iovec) { &instr, inslen };
|
||||||
|
|
||||||
|
msg = (struct msghdr) {
|
||||||
|
.msg_name = (void *)&nladdr,
|
||||||
|
.msg_namelen = sizeof(nladdr),
|
||||||
|
.msg_iov = iov,
|
||||||
|
.msg_iovlen = iovlen,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (sendmsg(rth.fd, &msg, 0) < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (rtnl_dump_filter(&rth, dummy_filter, NULL) < 0) {
|
||||||
|
ret = (errno != EINVAL);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = true;
|
||||||
|
|
||||||
|
out:
|
||||||
|
rtnl_close(&rth);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct filter_check_t {
|
||||||
|
bool (*check)(void);
|
||||||
|
int checked:1,
|
||||||
|
supported:1;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct filter_check_t filter_checks[SSF__MAX] = {
|
||||||
|
[SSF_CGROUPCOND] = { cgroup_filter_check, 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
bool ssfilter_is_supported(int type)
|
||||||
|
{
|
||||||
|
struct filter_check_t f;
|
||||||
|
|
||||||
|
if (type >= SSF__MAX)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
f = filter_checks[type];
|
||||||
|
if (!f.check)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (!f.checked) {
|
||||||
|
f.supported = f.check();
|
||||||
|
f.checked = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return f.supported;
|
||||||
|
}
|
||||||
|
|
@ -54,6 +54,7 @@ TCMODULES += m_bpf.o
|
||||||
TCMODULES += m_tunnel_key.o
|
TCMODULES += m_tunnel_key.o
|
||||||
TCMODULES += m_sample.o
|
TCMODULES += m_sample.o
|
||||||
TCMODULES += m_ct.o
|
TCMODULES += m_ct.o
|
||||||
|
TCMODULES += m_gate.o
|
||||||
TCMODULES += p_ip.o
|
TCMODULES += p_ip.o
|
||||||
TCMODULES += p_ip6.o
|
TCMODULES += p_ip6.o
|
||||||
TCMODULES += p_icmp.o
|
TCMODULES += p_icmp.o
|
||||||
|
|
|
||||||
29
tc/f_bpf.c
29
tc/f_bpf.c
|
|
@ -203,22 +203,24 @@ static int bpf_print_opt(struct filter_util *qu, FILE *f,
|
||||||
parse_rtattr_nested(tb, TCA_BPF_MAX, opt);
|
parse_rtattr_nested(tb, TCA_BPF_MAX, opt);
|
||||||
|
|
||||||
if (handle)
|
if (handle)
|
||||||
fprintf(f, "handle 0x%x ", handle);
|
print_0xhex(PRINT_ANY, "handle", "handle %#llx ", handle);
|
||||||
|
|
||||||
if (tb[TCA_BPF_CLASSID]) {
|
if (tb[TCA_BPF_CLASSID]) {
|
||||||
SPRINT_BUF(b1);
|
SPRINT_BUF(b1);
|
||||||
fprintf(f, "flowid %s ",
|
print_string(PRINT_ANY, "flowid", "flowid %s ",
|
||||||
sprint_tc_classid(rta_getattr_u32(tb[TCA_BPF_CLASSID]), b1));
|
sprint_tc_classid(rta_getattr_u32(tb[TCA_BPF_CLASSID]), b1));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tb[TCA_BPF_NAME])
|
if (tb[TCA_BPF_NAME])
|
||||||
fprintf(f, "%s ", rta_getattr_str(tb[TCA_BPF_NAME]));
|
print_string(PRINT_ANY, "bpf_name", "%s ",
|
||||||
|
rta_getattr_str(tb[TCA_BPF_NAME]));
|
||||||
|
|
||||||
if (tb[TCA_BPF_FLAGS]) {
|
if (tb[TCA_BPF_FLAGS]) {
|
||||||
unsigned int flags = rta_getattr_u32(tb[TCA_BPF_FLAGS]);
|
unsigned int flags = rta_getattr_u32(tb[TCA_BPF_FLAGS]);
|
||||||
|
|
||||||
if (flags & TCA_BPF_FLAG_ACT_DIRECT)
|
if (flags & TCA_BPF_FLAG_ACT_DIRECT)
|
||||||
fprintf(f, "direct-action ");
|
print_bool(PRINT_ANY,
|
||||||
|
"direct-action", "direct-action ", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tb[TCA_BPF_FLAGS_GEN]) {
|
if (tb[TCA_BPF_FLAGS_GEN]) {
|
||||||
|
|
@ -226,14 +228,14 @@ static int bpf_print_opt(struct filter_util *qu, FILE *f,
|
||||||
rta_getattr_u32(tb[TCA_BPF_FLAGS_GEN]);
|
rta_getattr_u32(tb[TCA_BPF_FLAGS_GEN]);
|
||||||
|
|
||||||
if (flags & TCA_CLS_FLAGS_SKIP_HW)
|
if (flags & TCA_CLS_FLAGS_SKIP_HW)
|
||||||
fprintf(f, "skip_hw ");
|
print_bool(PRINT_ANY, "skip_hw", "skip_hw ", true);
|
||||||
if (flags & TCA_CLS_FLAGS_SKIP_SW)
|
if (flags & TCA_CLS_FLAGS_SKIP_SW)
|
||||||
fprintf(f, "skip_sw ");
|
print_bool(PRINT_ANY, "skip_sw", "skip_sw ", true);
|
||||||
|
|
||||||
if (flags & TCA_CLS_FLAGS_IN_HW)
|
if (flags & TCA_CLS_FLAGS_IN_HW)
|
||||||
fprintf(f, "in_hw ");
|
print_bool(PRINT_ANY, "in_hw", "in_hw ", true);
|
||||||
else if (flags & TCA_CLS_FLAGS_NOT_IN_HW)
|
else if (flags & TCA_CLS_FLAGS_NOT_IN_HW)
|
||||||
fprintf(f, "not_in_hw ");
|
print_bool(PRINT_ANY,
|
||||||
|
"not_in_hw", "not_in_hw ", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tb[TCA_BPF_OPS] && tb[TCA_BPF_OPS_LEN])
|
if (tb[TCA_BPF_OPS] && tb[TCA_BPF_OPS_LEN])
|
||||||
|
|
@ -245,14 +247,13 @@ static int bpf_print_opt(struct filter_util *qu, FILE *f,
|
||||||
if (!dump_ok && tb[TCA_BPF_TAG]) {
|
if (!dump_ok && tb[TCA_BPF_TAG]) {
|
||||||
SPRINT_BUF(b);
|
SPRINT_BUF(b);
|
||||||
|
|
||||||
fprintf(f, "tag %s ",
|
print_string(PRINT_ANY, "tag", "tag %s ",
|
||||||
hexstring_n2a(RTA_DATA(tb[TCA_BPF_TAG]),
|
hexstring_n2a(RTA_DATA(tb[TCA_BPF_TAG]),
|
||||||
RTA_PAYLOAD(tb[TCA_BPF_TAG]),
|
RTA_PAYLOAD(tb[TCA_BPF_TAG]), b, sizeof(b)));
|
||||||
b, sizeof(b)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tb[TCA_BPF_POLICE]) {
|
if (tb[TCA_BPF_POLICE]) {
|
||||||
fprintf(f, "\n");
|
print_nl();
|
||||||
tc_print_police(f, tb[TCA_BPF_POLICE]);
|
tc_print_police(f, tb[TCA_BPF_POLICE]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
301
tc/f_flower.c
301
tc/f_flower.c
|
|
@ -81,6 +81,8 @@ static void explain(void)
|
||||||
" enc_tos MASKED-IP_TOS |\n"
|
" enc_tos MASKED-IP_TOS |\n"
|
||||||
" enc_ttl MASKED-IP_TTL |\n"
|
" enc_ttl MASKED-IP_TTL |\n"
|
||||||
" geneve_opts MASKED-OPTIONS |\n"
|
" geneve_opts MASKED-OPTIONS |\n"
|
||||||
|
" vxlan_opts MASKED-OPTIONS |\n"
|
||||||
|
" erspan_opts MASKED-OPTIONS |\n"
|
||||||
" ip_flags IP-FLAGS | \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_state MASKED_CT_STATE |\n"
|
||||||
|
|
@ -847,7 +849,7 @@ static int flower_parse_enc_port(char *str, int type, struct nlmsghdr *n)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int flower_parse_geneve_opts(char *str, struct nlmsghdr *n)
|
static int flower_parse_geneve_opt(char *str, struct nlmsghdr *n)
|
||||||
{
|
{
|
||||||
struct rtattr *nest;
|
struct rtattr *nest;
|
||||||
char *token;
|
char *token;
|
||||||
|
|
@ -917,14 +919,111 @@ static int flower_parse_geneve_opts(char *str, struct nlmsghdr *n)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int flower_parse_enc_opt_part(char *str, struct nlmsghdr *n)
|
static int flower_parse_vxlan_opt(char *str, struct nlmsghdr *n)
|
||||||
|
{
|
||||||
|
struct rtattr *nest;
|
||||||
|
__u32 gbp;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
nest = addattr_nest(n, MAX_MSG,
|
||||||
|
TCA_FLOWER_KEY_ENC_OPTS_VXLAN | NLA_F_NESTED);
|
||||||
|
|
||||||
|
err = get_u32(&gbp, str, 0);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
addattr32(n, MAX_MSG, TCA_FLOWER_KEY_ENC_OPT_VXLAN_GBP, gbp);
|
||||||
|
|
||||||
|
addattr_nest_end(n, nest);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int flower_parse_erspan_opt(char *str, struct nlmsghdr *n)
|
||||||
|
{
|
||||||
|
struct rtattr *nest;
|
||||||
|
char *token;
|
||||||
|
int i, err;
|
||||||
|
|
||||||
|
nest = addattr_nest(n, MAX_MSG,
|
||||||
|
TCA_FLOWER_KEY_ENC_OPTS_ERSPAN | NLA_F_NESTED);
|
||||||
|
|
||||||
|
i = 1;
|
||||||
|
token = strsep(&str, ":");
|
||||||
|
while (token) {
|
||||||
|
switch (i) {
|
||||||
|
case TCA_FLOWER_KEY_ENC_OPT_ERSPAN_VER:
|
||||||
|
{
|
||||||
|
__u8 opt_type;
|
||||||
|
|
||||||
|
if (!strlen(token))
|
||||||
|
break;
|
||||||
|
err = get_u8(&opt_type, token, 0);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
addattr8(n, MAX_MSG, i, opt_type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TCA_FLOWER_KEY_ENC_OPT_ERSPAN_INDEX:
|
||||||
|
{
|
||||||
|
__be32 opt_index;
|
||||||
|
|
||||||
|
if (!strlen(token))
|
||||||
|
break;
|
||||||
|
err = get_be32(&opt_index, token, 0);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
addattr32(n, MAX_MSG, i, opt_index);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TCA_FLOWER_KEY_ENC_OPT_ERSPAN_DIR:
|
||||||
|
{
|
||||||
|
__u8 opt_type;
|
||||||
|
|
||||||
|
if (!strlen(token))
|
||||||
|
break;
|
||||||
|
err = get_u8(&opt_type, token, 0);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
addattr8(n, MAX_MSG, i, opt_type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TCA_FLOWER_KEY_ENC_OPT_ERSPAN_HWID:
|
||||||
|
{
|
||||||
|
__u8 opt_type;
|
||||||
|
|
||||||
|
if (!strlen(token))
|
||||||
|
break;
|
||||||
|
err = get_u8(&opt_type, token, 0);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
addattr8(n, MAX_MSG, i, opt_type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "Unknown \"geneve_opts\" type\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
token = strsep(&str, ":");
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
addattr_nest_end(n, nest);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int flower_parse_geneve_opts(char *str, struct nlmsghdr *n)
|
||||||
{
|
{
|
||||||
char *token;
|
char *token;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
token = strsep(&str, ",");
|
token = strsep(&str, ",");
|
||||||
while (token) {
|
while (token) {
|
||||||
err = flower_parse_geneve_opts(token, n);
|
err = flower_parse_geneve_opt(token, n);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
|
@ -954,7 +1053,7 @@ static int flower_check_enc_opt_key(char *key)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int flower_parse_enc_opts(char *str, struct nlmsghdr *n)
|
static int flower_parse_enc_opts_geneve(char *str, struct nlmsghdr *n)
|
||||||
{
|
{
|
||||||
char key[XATTR_SIZE_MAX], mask[XATTR_SIZE_MAX];
|
char key[XATTR_SIZE_MAX], mask[XATTR_SIZE_MAX];
|
||||||
int data_len, key_len, mask_len, err;
|
int data_len, key_len, mask_len, err;
|
||||||
|
|
@ -1006,13 +1105,93 @@ static int flower_parse_enc_opts(char *str, struct nlmsghdr *n)
|
||||||
mask[mask_len - 1] = '\0';
|
mask[mask_len - 1] = '\0';
|
||||||
|
|
||||||
nest = addattr_nest(n, MAX_MSG, TCA_FLOWER_KEY_ENC_OPTS);
|
nest = addattr_nest(n, MAX_MSG, TCA_FLOWER_KEY_ENC_OPTS);
|
||||||
err = flower_parse_enc_opt_part(key, n);
|
err = flower_parse_geneve_opts(key, n);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
addattr_nest_end(n, nest);
|
addattr_nest_end(n, nest);
|
||||||
|
|
||||||
nest = addattr_nest(n, MAX_MSG, TCA_FLOWER_KEY_ENC_OPTS_MASK);
|
nest = addattr_nest(n, MAX_MSG, TCA_FLOWER_KEY_ENC_OPTS_MASK);
|
||||||
err = flower_parse_enc_opt_part(mask, n);
|
err = flower_parse_geneve_opts(mask, n);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
addattr_nest_end(n, nest);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int flower_parse_enc_opts_vxlan(char *str, struct nlmsghdr *n)
|
||||||
|
{
|
||||||
|
char key[XATTR_SIZE_MAX], mask[XATTR_SIZE_MAX];
|
||||||
|
struct rtattr *nest;
|
||||||
|
char *slash;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
slash = strchr(str, '/');
|
||||||
|
if (slash) {
|
||||||
|
*slash++ = '\0';
|
||||||
|
if (strlen(slash) > XATTR_SIZE_MAX)
|
||||||
|
return -1;
|
||||||
|
strcpy(mask, slash);
|
||||||
|
} else {
|
||||||
|
strcpy(mask, "0xffffffff");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen(str) > XATTR_SIZE_MAX)
|
||||||
|
return -1;
|
||||||
|
strcpy(key, str);
|
||||||
|
|
||||||
|
nest = addattr_nest(n, MAX_MSG, TCA_FLOWER_KEY_ENC_OPTS | NLA_F_NESTED);
|
||||||
|
err = flower_parse_vxlan_opt(str, n);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
addattr_nest_end(n, nest);
|
||||||
|
|
||||||
|
nest = addattr_nest(n, MAX_MSG,
|
||||||
|
TCA_FLOWER_KEY_ENC_OPTS_MASK | NLA_F_NESTED);
|
||||||
|
err = flower_parse_vxlan_opt(mask, n);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
addattr_nest_end(n, nest);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int flower_parse_enc_opts_erspan(char *str, struct nlmsghdr *n)
|
||||||
|
{
|
||||||
|
char key[XATTR_SIZE_MAX], mask[XATTR_SIZE_MAX];
|
||||||
|
struct rtattr *nest;
|
||||||
|
char *slash;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
|
||||||
|
slash = strchr(str, '/');
|
||||||
|
if (slash) {
|
||||||
|
*slash++ = '\0';
|
||||||
|
if (strlen(slash) > XATTR_SIZE_MAX)
|
||||||
|
return -1;
|
||||||
|
strcpy(mask, slash);
|
||||||
|
} else {
|
||||||
|
int index;
|
||||||
|
|
||||||
|
slash = strchr(str, ':');
|
||||||
|
index = (int)(slash - str);
|
||||||
|
memcpy(mask, str, index);
|
||||||
|
strcpy(mask + index, ":0xffffffff:0xff:0xff");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen(str) > XATTR_SIZE_MAX)
|
||||||
|
return -1;
|
||||||
|
strcpy(key, str);
|
||||||
|
|
||||||
|
nest = addattr_nest(n, MAX_MSG, TCA_FLOWER_KEY_ENC_OPTS | NLA_F_NESTED);
|
||||||
|
err = flower_parse_erspan_opt(key, n);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
addattr_nest_end(n, nest);
|
||||||
|
|
||||||
|
nest = addattr_nest(n, MAX_MSG,
|
||||||
|
TCA_FLOWER_KEY_ENC_OPTS_MASK | NLA_F_NESTED);
|
||||||
|
err = flower_parse_erspan_opt(mask, n);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
addattr_nest_end(n, nest);
|
addattr_nest_end(n, nest);
|
||||||
|
|
@ -1502,11 +1681,25 @@ static int flower_parse_opt(struct filter_util *qu, char *handle,
|
||||||
}
|
}
|
||||||
} else if (matches(*argv, "geneve_opts") == 0) {
|
} else if (matches(*argv, "geneve_opts") == 0) {
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
ret = flower_parse_enc_opts(*argv, n);
|
ret = flower_parse_enc_opts_geneve(*argv, n);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
fprintf(stderr, "Illegal \"geneve_opts\"\n");
|
fprintf(stderr, "Illegal \"geneve_opts\"\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
} else if (matches(*argv, "vxlan_opts") == 0) {
|
||||||
|
NEXT_ARG();
|
||||||
|
ret = flower_parse_enc_opts_vxlan(*argv, n);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "Illegal \"vxlan_opts\"\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} else if (matches(*argv, "erspan_opts") == 0) {
|
||||||
|
NEXT_ARG();
|
||||||
|
ret = flower_parse_enc_opts_erspan(*argv, n);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "Illegal \"erspan_opts\"\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
} else if (matches(*argv, "action") == 0) {
|
} else if (matches(*argv, "action") == 0) {
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
ret = parse_action(&argc, &argv, TCA_FLOWER_ACT, n);
|
ret = parse_action(&argc, &argv, TCA_FLOWER_ACT, n);
|
||||||
|
|
@ -1940,10 +2133,61 @@ static void flower_print_geneve_opts(const char *name, struct rtattr *attr,
|
||||||
close_json_array(PRINT_JSON, name);
|
close_json_array(PRINT_JSON, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void flower_print_geneve_parts(const char *name, struct rtattr *attr,
|
static void flower_print_vxlan_opts(const char *name, struct rtattr *attr,
|
||||||
char *key, char *mask)
|
char *strbuf)
|
||||||
|
{
|
||||||
|
struct rtattr *tb[TCA_FLOWER_KEY_ENC_OPT_VXLAN_MAX + 1];
|
||||||
|
struct rtattr *i = RTA_DATA(attr);
|
||||||
|
int rem = RTA_PAYLOAD(attr);
|
||||||
|
__u32 gbp;
|
||||||
|
|
||||||
|
parse_rtattr(tb, TCA_FLOWER_KEY_ENC_OPT_VXLAN_MAX, i, rem);
|
||||||
|
gbp = rta_getattr_u32(tb[TCA_FLOWER_KEY_ENC_OPT_VXLAN_GBP]);
|
||||||
|
|
||||||
|
open_json_array(PRINT_JSON, name);
|
||||||
|
open_json_object(NULL);
|
||||||
|
print_uint(PRINT_JSON, "gbp", NULL, gbp);
|
||||||
|
close_json_object();
|
||||||
|
close_json_array(PRINT_JSON, name);
|
||||||
|
|
||||||
|
sprintf(strbuf, "%u", gbp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void flower_print_erspan_opts(const char *name, struct rtattr *attr,
|
||||||
|
char *strbuf)
|
||||||
|
{
|
||||||
|
struct rtattr *tb[TCA_FLOWER_KEY_ENC_OPT_ERSPAN_MAX + 1];
|
||||||
|
__u8 ver, hwid, dir;
|
||||||
|
__u32 idx;
|
||||||
|
|
||||||
|
parse_rtattr(tb, TCA_FLOWER_KEY_ENC_OPT_ERSPAN_MAX, RTA_DATA(attr),
|
||||||
|
RTA_PAYLOAD(attr));
|
||||||
|
ver = rta_getattr_u8(tb[TCA_FLOWER_KEY_ENC_OPT_ERSPAN_VER]);
|
||||||
|
if (ver == 1) {
|
||||||
|
idx = rta_getattr_be32(tb[TCA_FLOWER_KEY_ENC_OPT_ERSPAN_INDEX]);
|
||||||
|
hwid = 0;
|
||||||
|
dir = 0;
|
||||||
|
} else {
|
||||||
|
idx = 0;
|
||||||
|
hwid = rta_getattr_u8(tb[TCA_FLOWER_KEY_ENC_OPT_ERSPAN_HWID]);
|
||||||
|
dir = rta_getattr_u8(tb[TCA_FLOWER_KEY_ENC_OPT_ERSPAN_DIR]);
|
||||||
|
}
|
||||||
|
|
||||||
|
open_json_array(PRINT_JSON, name);
|
||||||
|
open_json_object(NULL);
|
||||||
|
print_uint(PRINT_JSON, "ver", NULL, ver);
|
||||||
|
print_uint(PRINT_JSON, "index", NULL, idx);
|
||||||
|
print_uint(PRINT_JSON, "dir", NULL, dir);
|
||||||
|
print_uint(PRINT_JSON, "hwid", NULL, hwid);
|
||||||
|
close_json_object();
|
||||||
|
close_json_array(PRINT_JSON, name);
|
||||||
|
|
||||||
|
sprintf(strbuf, "%u:%u:%u:%u", ver, idx, dir, hwid);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void flower_print_enc_parts(const char *name, const char *namefrm,
|
||||||
|
struct rtattr *attr, char *key, char *mask)
|
||||||
{
|
{
|
||||||
char *namefrm = " geneve_opt %s";
|
|
||||||
char *key_token, *mask_token, *out;
|
char *key_token, *mask_token, *out;
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
|
|
@ -1985,14 +2229,39 @@ static void flower_print_enc_opts(const char *name, struct rtattr *attr,
|
||||||
goto err_key_free;
|
goto err_key_free;
|
||||||
|
|
||||||
parse_rtattr_nested(key_tb, TCA_FLOWER_KEY_ENC_OPTS_MAX, attr);
|
parse_rtattr_nested(key_tb, TCA_FLOWER_KEY_ENC_OPTS_MAX, attr);
|
||||||
flower_print_geneve_opts("geneve_opt_key",
|
|
||||||
key_tb[TCA_FLOWER_KEY_ENC_OPTS_GENEVE], key);
|
|
||||||
|
|
||||||
parse_rtattr_nested(msk_tb, TCA_FLOWER_KEY_ENC_OPTS_MAX, mask_attr);
|
parse_rtattr_nested(msk_tb, TCA_FLOWER_KEY_ENC_OPTS_MAX, mask_attr);
|
||||||
flower_print_geneve_opts("geneve_opt_mask",
|
|
||||||
msk_tb[TCA_FLOWER_KEY_ENC_OPTS_GENEVE], msk);
|
|
||||||
|
|
||||||
flower_print_geneve_parts(name, attr, key, msk);
|
if (key_tb[TCA_FLOWER_KEY_ENC_OPTS_GENEVE]) {
|
||||||
|
flower_print_geneve_opts("geneve_opt_key",
|
||||||
|
key_tb[TCA_FLOWER_KEY_ENC_OPTS_GENEVE], key);
|
||||||
|
|
||||||
|
if (msk_tb[TCA_FLOWER_KEY_ENC_OPTS_GENEVE])
|
||||||
|
flower_print_geneve_opts("geneve_opt_mask",
|
||||||
|
msk_tb[TCA_FLOWER_KEY_ENC_OPTS_GENEVE], msk);
|
||||||
|
|
||||||
|
flower_print_enc_parts(name, " geneve_opts %s", attr, key,
|
||||||
|
msk);
|
||||||
|
} else if (key_tb[TCA_FLOWER_KEY_ENC_OPTS_VXLAN]) {
|
||||||
|
flower_print_vxlan_opts("vxlan_opt_key",
|
||||||
|
key_tb[TCA_FLOWER_KEY_ENC_OPTS_VXLAN], key);
|
||||||
|
|
||||||
|
if (msk_tb[TCA_FLOWER_KEY_ENC_OPTS_VXLAN])
|
||||||
|
flower_print_vxlan_opts("vxlan_opt_mask",
|
||||||
|
msk_tb[TCA_FLOWER_KEY_ENC_OPTS_VXLAN], msk);
|
||||||
|
|
||||||
|
flower_print_enc_parts(name, " vxlan_opts %s", attr, key,
|
||||||
|
msk);
|
||||||
|
} else if (key_tb[TCA_FLOWER_KEY_ENC_OPTS_ERSPAN]) {
|
||||||
|
flower_print_erspan_opts("erspan_opt_key",
|
||||||
|
key_tb[TCA_FLOWER_KEY_ENC_OPTS_ERSPAN], key);
|
||||||
|
|
||||||
|
if (msk_tb[TCA_FLOWER_KEY_ENC_OPTS_ERSPAN])
|
||||||
|
flower_print_erspan_opts("erspan_opt_mask",
|
||||||
|
msk_tb[TCA_FLOWER_KEY_ENC_OPTS_ERSPAN], msk);
|
||||||
|
|
||||||
|
flower_print_enc_parts(name, " erspan_opts %s", attr, key,
|
||||||
|
msk);
|
||||||
|
}
|
||||||
|
|
||||||
free(msk);
|
free(msk);
|
||||||
err_key_free:
|
err_key_free:
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,580 @@
|
||||||
|
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
|
||||||
|
/* Copyright 2020 NXP */
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <linux/if_ether.h>
|
||||||
|
#include "utils.h"
|
||||||
|
#include "rt_names.h"
|
||||||
|
#include "tc_util.h"
|
||||||
|
#include "list.h"
|
||||||
|
#include <linux/tc_act/tc_gate.h>
|
||||||
|
|
||||||
|
struct gate_entry {
|
||||||
|
struct list_head list;
|
||||||
|
uint8_t gate_state;
|
||||||
|
uint32_t interval;
|
||||||
|
int32_t ipv;
|
||||||
|
int32_t maxoctets;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define CLOCKID_INVALID (-1)
|
||||||
|
static const struct clockid_table {
|
||||||
|
const char *name;
|
||||||
|
clockid_t clockid;
|
||||||
|
} clockt_map[] = {
|
||||||
|
{ "REALTIME", CLOCK_REALTIME },
|
||||||
|
{ "TAI", CLOCK_TAI },
|
||||||
|
{ "BOOTTIME", CLOCK_BOOTTIME },
|
||||||
|
{ "MONOTONIC", CLOCK_MONOTONIC },
|
||||||
|
{ NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
static void explain(void)
|
||||||
|
{
|
||||||
|
fprintf(stderr,
|
||||||
|
"Usage: gate [ priority PRIO-SPEC ] [ base-time BASE-TIME ]\n"
|
||||||
|
" [ cycle-time CYCLE-TIME ]\n"
|
||||||
|
" [ cycle-time-ext CYCLE-TIME-EXT ]\n"
|
||||||
|
" [ clockid CLOCKID ] [flags FLAGS]\n"
|
||||||
|
" [ sched-entry GATE0 INTERVAL [ INTERNAL-PRIO-VALUE MAX-OCTETS ] ]\n"
|
||||||
|
" [ sched-entry GATE1 INTERVAL [ INTERNAL-PRIO-VALUE MAX-OCTETS ] ]\n"
|
||||||
|
" ......\n"
|
||||||
|
" [ sched-entry GATEn INTERVAL [ INTERNAL-PRIO-VALUE MAX-OCTETS ] ]\n"
|
||||||
|
" [ CONTROL ]\n"
|
||||||
|
" GATEn := open | close\n"
|
||||||
|
" INTERVAL : nanoseconds period of gate slot\n"
|
||||||
|
" INTERNAL-PRIO-VALUE : internal priority decide which\n"
|
||||||
|
" rx queue number direct to.\n"
|
||||||
|
" default to be -1 which means wildcard.\n"
|
||||||
|
" MAX-OCTETS : maximum number of MSDU octets that are\n"
|
||||||
|
" permitted to pas the gate during the\n"
|
||||||
|
" specified TimeInterval.\n"
|
||||||
|
" default to be -1 which means wildcard.\n"
|
||||||
|
" CONTROL := pipe | drop | continue | pass |\n"
|
||||||
|
" goto chain <CHAIN_INDEX>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void usage(void)
|
||||||
|
{
|
||||||
|
explain();
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void explain_entry_format(void)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Usage: sched-entry <open | close> <interval> [ <interval ipv> <octets max bytes> ]\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_gate(struct action_util *a, int *argc_p, char ***argv_p,
|
||||||
|
int tca_id, struct nlmsghdr *n);
|
||||||
|
static int print_gate(struct action_util *au, FILE *f, struct rtattr *arg);
|
||||||
|
|
||||||
|
struct action_util gate_action_util = {
|
||||||
|
.id = "gate",
|
||||||
|
.parse_aopt = parse_gate,
|
||||||
|
.print_aopt = print_gate,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int get_clockid(__s32 *val, const char *arg)
|
||||||
|
{
|
||||||
|
const struct clockid_table *c;
|
||||||
|
|
||||||
|
if (strcasestr(arg, "CLOCK_") != NULL)
|
||||||
|
arg += sizeof("CLOCK_") - 1;
|
||||||
|
|
||||||
|
for (c = clockt_map; c->name; c++) {
|
||||||
|
if (strcasecmp(c->name, arg) == 0) {
|
||||||
|
*val = c->clockid;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *get_clock_name(clockid_t clockid)
|
||||||
|
{
|
||||||
|
const struct clockid_table *c;
|
||||||
|
|
||||||
|
for (c = clockt_map; c->name; c++) {
|
||||||
|
if (clockid == c->clockid)
|
||||||
|
return c->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "invalid";
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_gate_state(__u8 *val, const char *arg)
|
||||||
|
{
|
||||||
|
if (!strcasecmp("OPEN", arg)) {
|
||||||
|
*val = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcasecmp("CLOSE", arg)) {
|
||||||
|
*val = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct gate_entry *create_gate_entry(uint8_t gate_state,
|
||||||
|
uint32_t interval,
|
||||||
|
int32_t ipv,
|
||||||
|
int32_t maxoctets)
|
||||||
|
{
|
||||||
|
struct gate_entry *e;
|
||||||
|
|
||||||
|
e = calloc(1, sizeof(*e));
|
||||||
|
if (!e)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
e->gate_state = gate_state;
|
||||||
|
e->interval = interval;
|
||||||
|
e->ipv = ipv;
|
||||||
|
e->maxoctets = maxoctets;
|
||||||
|
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int add_gate_list(struct list_head *gate_entries, struct nlmsghdr *n)
|
||||||
|
{
|
||||||
|
struct gate_entry *e;
|
||||||
|
|
||||||
|
list_for_each_entry(e, gate_entries, list) {
|
||||||
|
struct rtattr *a;
|
||||||
|
|
||||||
|
a = addattr_nest(n, 1024, TCA_GATE_ONE_ENTRY | NLA_F_NESTED);
|
||||||
|
|
||||||
|
if (e->gate_state)
|
||||||
|
addattr(n, MAX_MSG, TCA_GATE_ENTRY_GATE);
|
||||||
|
|
||||||
|
addattr_l(n, MAX_MSG, TCA_GATE_ENTRY_INTERVAL,
|
||||||
|
&e->interval, sizeof(e->interval));
|
||||||
|
addattr_l(n, MAX_MSG, TCA_GATE_ENTRY_IPV,
|
||||||
|
&e->ipv, sizeof(e->ipv));
|
||||||
|
addattr_l(n, MAX_MSG, TCA_GATE_ENTRY_MAX_OCTETS,
|
||||||
|
&e->maxoctets, sizeof(e->maxoctets));
|
||||||
|
|
||||||
|
addattr_nest_end(n, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void free_entries(struct list_head *gate_entries)
|
||||||
|
{
|
||||||
|
struct gate_entry *e, *n;
|
||||||
|
|
||||||
|
list_for_each_entry_safe(e, n, gate_entries, list) {
|
||||||
|
list_del(&e->list);
|
||||||
|
free(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_gate(struct action_util *a, int *argc_p, char ***argv_p,
|
||||||
|
int tca_id, struct nlmsghdr *n)
|
||||||
|
{
|
||||||
|
struct tc_gate parm = {.action = TC_ACT_PIPE};
|
||||||
|
struct list_head gate_entries;
|
||||||
|
__s32 clockid = CLOCKID_INVALID;
|
||||||
|
struct rtattr *tail, *nle;
|
||||||
|
char **argv = *argv_p;
|
||||||
|
int argc = *argc_p;
|
||||||
|
__s64 base_time = 0;
|
||||||
|
__s64 cycle_time = 0;
|
||||||
|
__s64 cycle_time_ext = 0;
|
||||||
|
int entry_num = 0;
|
||||||
|
char *invalidarg;
|
||||||
|
__u32 flags = 0;
|
||||||
|
int prio = -1;
|
||||||
|
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (matches(*argv, "gate") != 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
NEXT_ARG();
|
||||||
|
if (argc <= 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
INIT_LIST_HEAD(&gate_entries);
|
||||||
|
|
||||||
|
while (argc > 0) {
|
||||||
|
if (matches(*argv, "index") == 0) {
|
||||||
|
NEXT_ARG();
|
||||||
|
if (get_u32(&parm.index, *argv, 10)) {
|
||||||
|
invalidarg = "index";
|
||||||
|
goto err_arg;
|
||||||
|
}
|
||||||
|
} else if (matches(*argv, "priority") == 0) {
|
||||||
|
NEXT_ARG();
|
||||||
|
if (get_s32(&prio, *argv, 0)) {
|
||||||
|
invalidarg = "priority";
|
||||||
|
goto err_arg;
|
||||||
|
}
|
||||||
|
} else if (matches(*argv, "base-time") == 0) {
|
||||||
|
NEXT_ARG();
|
||||||
|
if (get_s64(&base_time, *argv, 10) &&
|
||||||
|
get_time64(&base_time, *argv)) {
|
||||||
|
invalidarg = "base-time";
|
||||||
|
goto err_arg;
|
||||||
|
}
|
||||||
|
} else if (matches(*argv, "cycle-time") == 0) {
|
||||||
|
NEXT_ARG();
|
||||||
|
if (get_s64(&cycle_time, *argv, 10) &&
|
||||||
|
get_time64(&cycle_time, *argv)) {
|
||||||
|
invalidarg = "cycle-time";
|
||||||
|
goto err_arg;
|
||||||
|
}
|
||||||
|
} else if (matches(*argv, "cycle-time-ext") == 0) {
|
||||||
|
NEXT_ARG();
|
||||||
|
if (get_s64(&cycle_time_ext, *argv, 10) &&
|
||||||
|
get_time64(&cycle_time_ext, *argv)) {
|
||||||
|
invalidarg = "cycle-time-ext";
|
||||||
|
goto err_arg;
|
||||||
|
}
|
||||||
|
} else if (matches(*argv, "clockid") == 0) {
|
||||||
|
NEXT_ARG();
|
||||||
|
if (get_clockid(&clockid, *argv)) {
|
||||||
|
invalidarg = "clockid";
|
||||||
|
goto err_arg;
|
||||||
|
}
|
||||||
|
} else if (matches(*argv, "flags") == 0) {
|
||||||
|
NEXT_ARG();
|
||||||
|
if (get_u32(&flags, *argv, 0)) {
|
||||||
|
invalidarg = "flags";
|
||||||
|
goto err_arg;
|
||||||
|
}
|
||||||
|
} else if (matches(*argv, "sched-entry") == 0) {
|
||||||
|
unsigned int maxoctets_uint = 0;
|
||||||
|
int32_t maxoctets = -1;
|
||||||
|
struct gate_entry *e;
|
||||||
|
uint8_t gate_state = 0;
|
||||||
|
__s64 interval_s64 = 0;
|
||||||
|
uint32_t interval = 0;
|
||||||
|
int32_t ipv = -1;
|
||||||
|
|
||||||
|
if (!NEXT_ARG_OK()) {
|
||||||
|
explain_entry_format();
|
||||||
|
fprintf(stderr, "\"sched-entry\" is imcomplete\n");
|
||||||
|
free_entries(&gate_entries);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
NEXT_ARG();
|
||||||
|
|
||||||
|
if (get_gate_state(&gate_state, *argv)) {
|
||||||
|
explain_entry_format();
|
||||||
|
fprintf(stderr, "\"sched-entry\" is imcomplete\n");
|
||||||
|
free_entries(&gate_entries);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!NEXT_ARG_OK()) {
|
||||||
|
explain_entry_format();
|
||||||
|
fprintf(stderr, "\"sched-entry\" is imcomplete\n");
|
||||||
|
free_entries(&gate_entries);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
NEXT_ARG();
|
||||||
|
|
||||||
|
if (get_u32(&interval, *argv, 0) &&
|
||||||
|
get_time64(&interval_s64, *argv)) {
|
||||||
|
explain_entry_format();
|
||||||
|
fprintf(stderr, "\"sched-entry\" is imcomplete\n");
|
||||||
|
free_entries(&gate_entries);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (interval_s64 > UINT_MAX) {
|
||||||
|
fprintf(stderr, "\"interval\" is too large\n");
|
||||||
|
free_entries(&gate_entries);
|
||||||
|
return -1;
|
||||||
|
} else if (interval_s64) {
|
||||||
|
interval = interval_s64;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!NEXT_ARG_OK())
|
||||||
|
goto create_entry;
|
||||||
|
|
||||||
|
NEXT_ARG();
|
||||||
|
|
||||||
|
if (get_s32(&ipv, *argv, 0)) {
|
||||||
|
PREV_ARG();
|
||||||
|
goto create_entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gate_state)
|
||||||
|
ipv = -1;
|
||||||
|
|
||||||
|
if (!NEXT_ARG_OK())
|
||||||
|
goto create_entry;
|
||||||
|
|
||||||
|
NEXT_ARG();
|
||||||
|
|
||||||
|
if (get_s32(&maxoctets, *argv, 0) &&
|
||||||
|
get_size(&maxoctets_uint, *argv))
|
||||||
|
PREV_ARG();
|
||||||
|
|
||||||
|
if (maxoctets_uint > INT_MAX) {
|
||||||
|
fprintf(stderr, "\"maxoctets\" is too large\n");
|
||||||
|
free_entries(&gate_entries);
|
||||||
|
return -1;
|
||||||
|
} else if (maxoctets_uint ) {
|
||||||
|
maxoctets = maxoctets_uint;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gate_state)
|
||||||
|
maxoctets = -1;
|
||||||
|
|
||||||
|
create_entry:
|
||||||
|
e = create_gate_entry(gate_state, interval,
|
||||||
|
ipv, maxoctets);
|
||||||
|
if (!e) {
|
||||||
|
fprintf(stderr, "gate: not enough memory\n");
|
||||||
|
free_entries(&gate_entries);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_add_tail(&e->list, &gate_entries);
|
||||||
|
entry_num++;
|
||||||
|
} else if (matches(*argv, "help") == 0) {
|
||||||
|
usage();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
argc--;
|
||||||
|
argv++;
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_action_control_dflt(&argc, &argv, &parm.action,
|
||||||
|
false, TC_ACT_PIPE);
|
||||||
|
|
||||||
|
if (!entry_num && !parm.index) {
|
||||||
|
fprintf(stderr, "gate: must add at least one entry\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
tail = addattr_nest(n, MAX_MSG, tca_id | NLA_F_NESTED);
|
||||||
|
addattr_l(n, MAX_MSG, TCA_GATE_PARMS, &parm, sizeof(parm));
|
||||||
|
|
||||||
|
if (prio != -1)
|
||||||
|
addattr_l(n, MAX_MSG, TCA_GATE_PRIORITY, &prio, sizeof(prio));
|
||||||
|
|
||||||
|
if (flags)
|
||||||
|
addattr_l(n, MAX_MSG, TCA_GATE_FLAGS, &flags, sizeof(flags));
|
||||||
|
|
||||||
|
if (base_time)
|
||||||
|
addattr_l(n, MAX_MSG, TCA_GATE_BASE_TIME,
|
||||||
|
&base_time, sizeof(base_time));
|
||||||
|
|
||||||
|
if (cycle_time)
|
||||||
|
addattr_l(n, MAX_MSG, TCA_GATE_CYCLE_TIME,
|
||||||
|
&cycle_time, sizeof(cycle_time));
|
||||||
|
|
||||||
|
if (cycle_time_ext)
|
||||||
|
addattr_l(n, MAX_MSG, TCA_GATE_CYCLE_TIME_EXT,
|
||||||
|
&cycle_time_ext, sizeof(cycle_time_ext));
|
||||||
|
|
||||||
|
if (clockid != CLOCKID_INVALID)
|
||||||
|
addattr_l(n, MAX_MSG, TCA_GATE_CLOCKID,
|
||||||
|
&clockid, sizeof(clockid));
|
||||||
|
|
||||||
|
nle = addattr_nest(n, MAX_MSG, TCA_GATE_ENTRY_LIST | NLA_F_NESTED);
|
||||||
|
err = add_gate_list(&gate_entries, n);
|
||||||
|
if (err < 0) {
|
||||||
|
fprintf(stderr, "Could not add entries to netlink message\n");
|
||||||
|
free_entries(&gate_entries);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
addattr_nest_end(n, nle);
|
||||||
|
addattr_nest_end(n, tail);
|
||||||
|
free_entries(&gate_entries);
|
||||||
|
*argc_p = argc;
|
||||||
|
*argv_p = argv;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
err_arg:
|
||||||
|
invarg(invalidarg, *argv);
|
||||||
|
free_entries(&gate_entries);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int print_gate_list(struct rtattr *list)
|
||||||
|
{
|
||||||
|
struct rtattr *item;
|
||||||
|
int rem;
|
||||||
|
|
||||||
|
rem = RTA_PAYLOAD(list);
|
||||||
|
|
||||||
|
print_string(PRINT_FP, NULL, "%s", _SL_);
|
||||||
|
print_string(PRINT_FP, NULL, "\tschedule:%s", _SL_);
|
||||||
|
open_json_array(PRINT_JSON, "schedule");
|
||||||
|
|
||||||
|
for (item = RTA_DATA(list);
|
||||||
|
RTA_OK(item, rem);
|
||||||
|
item = RTA_NEXT(item, rem)) {
|
||||||
|
struct rtattr *tb[TCA_GATE_ENTRY_MAX + 1];
|
||||||
|
__u32 index = 0, interval = 0;
|
||||||
|
__u8 gate_state = 0;
|
||||||
|
__s32 ipv = -1, maxoctets = -1;
|
||||||
|
char buf[22];
|
||||||
|
|
||||||
|
parse_rtattr_nested(tb, TCA_GATE_ENTRY_MAX, item);
|
||||||
|
|
||||||
|
if (tb[TCA_GATE_ENTRY_INDEX])
|
||||||
|
index = rta_getattr_u32(tb[TCA_GATE_ENTRY_INDEX]);
|
||||||
|
|
||||||
|
if (tb[TCA_GATE_ENTRY_GATE])
|
||||||
|
gate_state = 1;
|
||||||
|
|
||||||
|
if (tb[TCA_GATE_ENTRY_INTERVAL])
|
||||||
|
interval = rta_getattr_u32(tb[TCA_GATE_ENTRY_INTERVAL]);
|
||||||
|
|
||||||
|
if (tb[TCA_GATE_ENTRY_IPV])
|
||||||
|
ipv = rta_getattr_s32(tb[TCA_GATE_ENTRY_IPV]);
|
||||||
|
|
||||||
|
if (tb[TCA_GATE_ENTRY_MAX_OCTETS])
|
||||||
|
maxoctets = rta_getattr_s32(tb[TCA_GATE_ENTRY_MAX_OCTETS]);
|
||||||
|
|
||||||
|
open_json_object(NULL);
|
||||||
|
print_uint(PRINT_ANY, "number", "\t number %4u", index);
|
||||||
|
print_string(PRINT_ANY, "gate_state", "\tgate-state %s ",
|
||||||
|
gate_state ? "open" : "close");
|
||||||
|
|
||||||
|
print_uint(PRINT_JSON, "interval", NULL, interval);
|
||||||
|
|
||||||
|
memset(buf, 0, sizeof(buf));
|
||||||
|
print_string(PRINT_FP, NULL, "\tinterval %s",
|
||||||
|
sprint_time64(interval, buf));
|
||||||
|
|
||||||
|
if (ipv != -1) {
|
||||||
|
print_uint(PRINT_ANY, "ipv", "\t ipv %-10u", ipv);
|
||||||
|
} else {
|
||||||
|
print_int(PRINT_JSON, "ipv", NULL, ipv);
|
||||||
|
print_string(PRINT_FP, NULL, "\t ipv %s", "wildcard");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxoctets != -1) {
|
||||||
|
memset(buf, 0, sizeof(buf));
|
||||||
|
print_uint(PRINT_JSON, "max_octets", NULL, maxoctets);
|
||||||
|
print_string(PRINT_FP, NULL, "\t max-octets %s",
|
||||||
|
sprint_size(maxoctets, buf));
|
||||||
|
} else {
|
||||||
|
print_string(PRINT_FP, NULL,
|
||||||
|
"\t max-octets %s", "wildcard");
|
||||||
|
print_int(PRINT_JSON, "max_octets", NULL, maxoctets);
|
||||||
|
}
|
||||||
|
|
||||||
|
close_json_object();
|
||||||
|
print_string(PRINT_FP, NULL, "%s", _SL_);
|
||||||
|
}
|
||||||
|
|
||||||
|
close_json_array(PRINT_ANY, "");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int print_gate(struct action_util *au, FILE *f, struct rtattr *arg)
|
||||||
|
{
|
||||||
|
struct tc_gate *parm;
|
||||||
|
struct rtattr *tb[TCA_GATE_MAX + 1];
|
||||||
|
__s32 clockid = CLOCKID_INVALID;
|
||||||
|
__s64 base_time = 0;
|
||||||
|
__s64 cycle_time = 0;
|
||||||
|
__s64 cycle_time_ext = 0;
|
||||||
|
char buf[22];
|
||||||
|
int prio = -1;
|
||||||
|
|
||||||
|
if (arg == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
parse_rtattr_nested(tb, TCA_GATE_MAX, arg);
|
||||||
|
|
||||||
|
if (!tb[TCA_GATE_PARMS]) {
|
||||||
|
fprintf(stderr, "Missing gate parameters\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
print_string(PRINT_FP, NULL, "%s", "\n");
|
||||||
|
|
||||||
|
parm = RTA_DATA(tb[TCA_GATE_PARMS]);
|
||||||
|
|
||||||
|
if (tb[TCA_GATE_PRIORITY])
|
||||||
|
prio = rta_getattr_s32(tb[TCA_GATE_PRIORITY]);
|
||||||
|
|
||||||
|
if (prio != -1) {
|
||||||
|
print_int(PRINT_ANY, "priority", "\tpriority %-8d", prio);
|
||||||
|
} else {
|
||||||
|
print_string(PRINT_FP, NULL, "\tpriority %s", "wildcard");
|
||||||
|
print_int(PRINT_JSON, "priority", NULL, prio);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tb[TCA_GATE_CLOCKID])
|
||||||
|
clockid = rta_getattr_s32(tb[TCA_GATE_CLOCKID]);
|
||||||
|
print_string(PRINT_ANY, "clockid", "\tclockid %s",
|
||||||
|
get_clock_name(clockid));
|
||||||
|
|
||||||
|
if (tb[TCA_GATE_FLAGS]) {
|
||||||
|
__u32 flags;
|
||||||
|
|
||||||
|
flags = rta_getattr_u32(tb[TCA_GATE_FLAGS]);
|
||||||
|
print_0xhex(PRINT_ANY, "flags", "\tflags %#x", flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
print_string(PRINT_FP, NULL, "%s", "\n");
|
||||||
|
|
||||||
|
if (tb[TCA_GATE_BASE_TIME])
|
||||||
|
base_time = rta_getattr_s64(tb[TCA_GATE_BASE_TIME]);
|
||||||
|
|
||||||
|
memset(buf, 0, sizeof(buf));
|
||||||
|
print_string(PRINT_FP, NULL, "\tbase-time %s",
|
||||||
|
sprint_time64(base_time, buf));
|
||||||
|
print_lluint(PRINT_JSON, "base_time", NULL, base_time);
|
||||||
|
|
||||||
|
if (tb[TCA_GATE_CYCLE_TIME])
|
||||||
|
cycle_time = rta_getattr_s64(tb[TCA_GATE_CYCLE_TIME]);
|
||||||
|
|
||||||
|
memset(buf, 0, sizeof(buf));
|
||||||
|
print_string(PRINT_FP, NULL,
|
||||||
|
"\tcycle-time %s", sprint_time64(cycle_time, buf));
|
||||||
|
print_lluint(PRINT_JSON, "cycle_time", NULL, cycle_time);
|
||||||
|
|
||||||
|
if (tb[TCA_GATE_CYCLE_TIME_EXT])
|
||||||
|
cycle_time_ext = rta_getattr_s64(tb[TCA_GATE_CYCLE_TIME_EXT]);
|
||||||
|
|
||||||
|
memset(buf, 0, sizeof(buf));
|
||||||
|
print_string(PRINT_FP, NULL, "\tcycle-time-ext %s",
|
||||||
|
sprint_time64(cycle_time_ext, buf));
|
||||||
|
print_lluint(PRINT_JSON, "cycle_time_ext", NULL, cycle_time_ext);
|
||||||
|
|
||||||
|
if (tb[TCA_GATE_ENTRY_LIST])
|
||||||
|
print_gate_list(tb[TCA_GATE_ENTRY_LIST]);
|
||||||
|
|
||||||
|
print_action_control(f, "\t", parm->action, "");
|
||||||
|
|
||||||
|
print_uint(PRINT_ANY, "index", "\n\t index %u", parm->index);
|
||||||
|
print_int(PRINT_ANY, "ref", " ref %d", parm->refcnt);
|
||||||
|
print_int(PRINT_ANY, "bind", " bind %d", parm->bindcnt);
|
||||||
|
|
||||||
|
if (show_stats) {
|
||||||
|
if (tb[TCA_GATE_TM]) {
|
||||||
|
struct tcf_t *tm = RTA_DATA(tb[TCA_GATE_TM]);
|
||||||
|
|
||||||
|
print_tm(f, tm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print_string(PRINT_FP, NULL, "%s", "\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
67
tc/m_pedit.c
67
tc/m_pedit.c
|
|
@ -714,20 +714,28 @@ static const char * const pedit_htype_str[] = {
|
||||||
[TCA_PEDIT_KEY_EX_HDR_TYPE_UDP] = "udp",
|
[TCA_PEDIT_KEY_EX_HDR_TYPE_UDP] = "udp",
|
||||||
};
|
};
|
||||||
|
|
||||||
static void print_pedit_location(FILE *f,
|
static int print_pedit_location(FILE *f,
|
||||||
enum pedit_header_type htype, __u32 off)
|
enum pedit_header_type htype, __u32 off)
|
||||||
{
|
{
|
||||||
if (htype == TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK) {
|
char *buf = NULL;
|
||||||
fprintf(f, "%d", (unsigned int)off);
|
int rc;
|
||||||
return;
|
|
||||||
|
if (htype != TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK) {
|
||||||
|
if (htype < ARRAY_SIZE(pedit_htype_str))
|
||||||
|
rc = asprintf(&buf, "%s", pedit_htype_str[htype]);
|
||||||
|
else
|
||||||
|
rc = asprintf(&buf, "unknown(%d)", htype);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
print_string(PRINT_ANY, "htype", "%s", buf);
|
||||||
|
print_int(PRINT_ANY, "offset", "%+d", off);
|
||||||
|
} else {
|
||||||
|
print_string(PRINT_JSON, "htype", NULL, "network");
|
||||||
|
print_int(PRINT_ANY, "offset", "%d", off);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (htype < ARRAY_SIZE(pedit_htype_str))
|
free(buf);
|
||||||
fprintf(f, "%s", pedit_htype_str[htype]);
|
return 0;
|
||||||
else
|
|
||||||
fprintf(f, "unknown(%d)", htype);
|
|
||||||
|
|
||||||
fprintf(f, "%c%d", (int)off >= 0 ? '+' : '-', abs((int)off));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int print_pedit(struct action_util *au, FILE *f, struct rtattr *arg)
|
static int print_pedit(struct action_util *au, FILE *f, struct rtattr *arg)
|
||||||
|
|
@ -735,6 +743,7 @@ static int print_pedit(struct action_util *au, FILE *f, struct rtattr *arg)
|
||||||
struct tc_pedit_sel *sel;
|
struct tc_pedit_sel *sel;
|
||||||
struct rtattr *tb[TCA_PEDIT_MAX + 1];
|
struct rtattr *tb[TCA_PEDIT_MAX + 1];
|
||||||
struct m_pedit_key_ex *keys_ex = NULL;
|
struct m_pedit_key_ex *keys_ex = NULL;
|
||||||
|
int err;
|
||||||
|
|
||||||
if (arg == NULL)
|
if (arg == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -774,11 +783,12 @@ static int print_pedit(struct action_util *au, FILE *f, struct rtattr *arg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(f, " pedit ");
|
print_string(PRINT_ANY, "kind", " %s ", "pedit");
|
||||||
print_action_control(f, "action ", sel->action, " ");
|
print_action_control(f, "action ", sel->action, " ");
|
||||||
fprintf(f,"keys %d\n ", sel->nkeys);
|
print_uint(PRINT_ANY, "nkeys", "keys %d\n", sel->nkeys);
|
||||||
fprintf(f, "\t index %u ref %d bind %d", sel->index, sel->refcnt,
|
print_uint(PRINT_ANY, "index", " \t index %u", sel->index);
|
||||||
sel->bindcnt);
|
print_int(PRINT_ANY, "ref", " ref %d", sel->refcnt);
|
||||||
|
print_int(PRINT_ANY, "bind", " bind %d", sel->bindcnt);
|
||||||
|
|
||||||
if (show_stats) {
|
if (show_stats) {
|
||||||
if (tb[TCA_PEDIT_TM]) {
|
if (tb[TCA_PEDIT_TM]) {
|
||||||
|
|
@ -787,6 +797,7 @@ static int print_pedit(struct action_util *au, FILE *f, struct rtattr *arg)
|
||||||
print_tm(f, tm);
|
print_tm(f, tm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
open_json_array(PRINT_JSON, "keys");
|
||||||
if (sel->nkeys) {
|
if (sel->nkeys) {
|
||||||
int i;
|
int i;
|
||||||
struct tc_pedit_key *key = sel->keys;
|
struct tc_pedit_key *key = sel->keys;
|
||||||
|
|
@ -804,21 +815,31 @@ static int print_pedit(struct action_util *au, FILE *f, struct rtattr *arg)
|
||||||
key_ex++;
|
key_ex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(f, "\n\t key #%d", i);
|
open_json_object(NULL);
|
||||||
|
print_uint(PRINT_FP, NULL, "\n\t key #%d at ", i);
|
||||||
|
|
||||||
fprintf(f, " at ");
|
err = print_pedit_location(f, htype, key->off);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
print_pedit_location(f, htype, key->off);
|
/* In FP, report the "set" command as "val" to keep
|
||||||
|
* backward compatibility. Report the true name in JSON.
|
||||||
fprintf(f, ": %s %08x mask %08x",
|
*/
|
||||||
cmd ? "add" : "val",
|
print_string(PRINT_FP, NULL, ": %s",
|
||||||
(unsigned int)ntohl(key->val),
|
cmd ? "add" : "val");
|
||||||
(unsigned int)ntohl(key->mask));
|
print_string(PRINT_JSON, "cmd", NULL,
|
||||||
|
cmd ? "add" : "set");
|
||||||
|
print_hex(PRINT_ANY, "val", " %08x",
|
||||||
|
(unsigned int)ntohl(key->val));
|
||||||
|
print_hex(PRINT_ANY, "mask", " mask %08x",
|
||||||
|
(unsigned int)ntohl(key->mask));
|
||||||
|
close_json_object();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fprintf(f, "\npedit %x keys %d is not LEGIT", sel->index,
|
fprintf(f, "\npedit %x keys %d is not LEGIT", sel->index,
|
||||||
sel->nkeys);
|
sel->nkeys);
|
||||||
}
|
}
|
||||||
|
close_json_array(PRINT_JSON, " ");
|
||||||
|
|
||||||
print_nl();
|
print_nl();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ static void explain(void)
|
||||||
"src_ip <IP> (mandatory)\n"
|
"src_ip <IP> (mandatory)\n"
|
||||||
"dst_ip <IP> (mandatory)\n"
|
"dst_ip <IP> (mandatory)\n"
|
||||||
"dst_port <UDP_PORT>\n"
|
"dst_port <UDP_PORT>\n"
|
||||||
"geneve_opts <OPTIONS>\n"
|
"geneve_opts | vxlan_opts | erspan_opts <OPTIONS>\n"
|
||||||
"csum | nocsum (default is \"csum\")\n");
|
"csum | nocsum (default is \"csum\")\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -97,6 +97,21 @@ static int tunnel_key_parse_be16(char *str, int base, int type,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int tunnel_key_parse_be32(char *str, int base, int type,
|
||||||
|
struct nlmsghdr *n)
|
||||||
|
{
|
||||||
|
__be32 value;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = get_be32(&value, str, base);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
addattr32(n, MAX_MSG, type, value);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int tunnel_key_parse_u8(char *str, int base, int type,
|
static int tunnel_key_parse_u8(char *str, int base, int type,
|
||||||
struct nlmsghdr *n)
|
struct nlmsghdr *n)
|
||||||
{
|
{
|
||||||
|
|
@ -112,6 +127,21 @@ static int tunnel_key_parse_u8(char *str, int base, int type,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int tunnel_key_parse_u32(char *str, int base, int type,
|
||||||
|
struct nlmsghdr *n)
|
||||||
|
{
|
||||||
|
__u32 value;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = get_u32(&value, str, base);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
addattr32(n, MAX_MSG, type, value);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int tunnel_key_parse_geneve_opt(char *str, struct nlmsghdr *n)
|
static int tunnel_key_parse_geneve_opt(char *str, struct nlmsghdr *n)
|
||||||
{
|
{
|
||||||
char *token, *saveptr = NULL;
|
char *token, *saveptr = NULL;
|
||||||
|
|
@ -190,6 +220,84 @@ static int tunnel_key_parse_geneve_opts(char *str, struct nlmsghdr *n)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int tunnel_key_parse_vxlan_opt(char *str, struct nlmsghdr *n)
|
||||||
|
{
|
||||||
|
struct rtattr *encap, *nest;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
encap = addattr_nest(n, MAX_MSG,
|
||||||
|
TCA_TUNNEL_KEY_ENC_OPTS | NLA_F_NESTED);
|
||||||
|
nest = addattr_nest(n, MAX_MSG,
|
||||||
|
TCA_TUNNEL_KEY_ENC_OPTS_VXLAN | NLA_F_NESTED);
|
||||||
|
|
||||||
|
ret = tunnel_key_parse_u32(str, 0,
|
||||||
|
TCA_TUNNEL_KEY_ENC_OPT_VXLAN_GBP, n);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
addattr_nest_end(n, nest);
|
||||||
|
addattr_nest_end(n, encap);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tunnel_key_parse_erspan_opt(char *str, struct nlmsghdr *n)
|
||||||
|
{
|
||||||
|
char *token, *saveptr = NULL;
|
||||||
|
struct rtattr *encap, *nest;
|
||||||
|
int i, ret;
|
||||||
|
|
||||||
|
encap = addattr_nest(n, MAX_MSG,
|
||||||
|
TCA_TUNNEL_KEY_ENC_OPTS | NLA_F_NESTED);
|
||||||
|
nest = addattr_nest(n, MAX_MSG,
|
||||||
|
TCA_TUNNEL_KEY_ENC_OPTS_ERSPAN | NLA_F_NESTED);
|
||||||
|
|
||||||
|
token = strtok_r(str, ":", &saveptr);
|
||||||
|
i = 1;
|
||||||
|
while (token) {
|
||||||
|
switch (i) {
|
||||||
|
case TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_VER:
|
||||||
|
{
|
||||||
|
ret = tunnel_key_parse_u8(token, 0, i, n);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_INDEX:
|
||||||
|
{
|
||||||
|
ret = tunnel_key_parse_be32(token, 0, i, n);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_DIR:
|
||||||
|
{
|
||||||
|
ret = tunnel_key_parse_u8(token, 0, i, n);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_HWID:
|
||||||
|
{
|
||||||
|
ret = tunnel_key_parse_u8(token, 0, i, n);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
token = strtok_r(NULL, ":", &saveptr);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
addattr_nest_end(n, nest);
|
||||||
|
addattr_nest_end(n, encap);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int tunnel_key_parse_tos_ttl(char *str, int type, struct nlmsghdr *n)
|
static int tunnel_key_parse_tos_ttl(char *str, int type, struct nlmsghdr *n)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
@ -287,6 +395,20 @@ static int parse_tunnel_key(struct action_util *a, int *argc_p, char ***argv_p,
|
||||||
fprintf(stderr, "Illegal \"geneve_opts\"\n");
|
fprintf(stderr, "Illegal \"geneve_opts\"\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
} else if (matches(*argv, "vxlan_opts") == 0) {
|
||||||
|
NEXT_ARG();
|
||||||
|
|
||||||
|
if (tunnel_key_parse_vxlan_opt(*argv, n)) {
|
||||||
|
fprintf(stderr, "Illegal \"vxlan_opts\"\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} else if (matches(*argv, "erspan_opts") == 0) {
|
||||||
|
NEXT_ARG();
|
||||||
|
|
||||||
|
if (tunnel_key_parse_erspan_opt(*argv, n)) {
|
||||||
|
fprintf(stderr, "Illegal \"erspan_opts\"\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
} else if (matches(*argv, "tos") == 0) {
|
} else if (matches(*argv, "tos") == 0) {
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
ret = tunnel_key_parse_tos_ttl(*argv,
|
ret = tunnel_key_parse_tos_ttl(*argv,
|
||||||
|
|
@ -406,13 +528,13 @@ static void tunnel_key_print_flag(FILE *f, const char *name_on,
|
||||||
rta_getattr_u8(attr) ? name_on : name_off);
|
rta_getattr_u8(attr) ? name_on : name_off);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tunnel_key_print_geneve_options(const char *name,
|
static void tunnel_key_print_geneve_options(struct rtattr *attr)
|
||||||
struct rtattr *attr)
|
|
||||||
{
|
{
|
||||||
struct rtattr *tb[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_MAX + 1];
|
struct rtattr *tb[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_MAX + 1];
|
||||||
struct rtattr *i = RTA_DATA(attr);
|
struct rtattr *i = RTA_DATA(attr);
|
||||||
int ii, data_len = 0, offset = 0;
|
int ii, data_len = 0, offset = 0;
|
||||||
int rem = RTA_PAYLOAD(attr);
|
int rem = RTA_PAYLOAD(attr);
|
||||||
|
char *name = "geneve_opts";
|
||||||
char strbuf[rem * 2 + 1];
|
char strbuf[rem * 2 + 1];
|
||||||
char data[rem * 2 + 1];
|
char data[rem * 2 + 1];
|
||||||
uint8_t data_r[rem];
|
uint8_t data_r[rem];
|
||||||
|
|
@ -421,7 +543,7 @@ static void tunnel_key_print_geneve_options(const char *name,
|
||||||
|
|
||||||
open_json_array(PRINT_JSON, name);
|
open_json_array(PRINT_JSON, name);
|
||||||
print_nl();
|
print_nl();
|
||||||
print_string(PRINT_FP, name, "\t%s ", "geneve_opt");
|
print_string(PRINT_FP, name, "\t%s ", name);
|
||||||
|
|
||||||
while (rem) {
|
while (rem) {
|
||||||
parse_rtattr(tb, TCA_TUNNEL_KEY_ENC_OPT_GENEVE_MAX, i, rem);
|
parse_rtattr(tb, TCA_TUNNEL_KEY_ENC_OPT_GENEVE_MAX, i, rem);
|
||||||
|
|
@ -454,7 +576,60 @@ static void tunnel_key_print_geneve_options(const char *name,
|
||||||
close_json_array(PRINT_JSON, name);
|
close_json_array(PRINT_JSON, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tunnel_key_print_key_opt(const char *name, struct rtattr *attr)
|
static void tunnel_key_print_vxlan_options(struct rtattr *attr)
|
||||||
|
{
|
||||||
|
struct rtattr *tb[TCA_TUNNEL_KEY_ENC_OPT_VXLAN_MAX + 1];
|
||||||
|
struct rtattr *i = RTA_DATA(attr);
|
||||||
|
int rem = RTA_PAYLOAD(attr);
|
||||||
|
char *name = "vxlan_opts";
|
||||||
|
__u32 gbp;
|
||||||
|
|
||||||
|
parse_rtattr(tb, TCA_TUNNEL_KEY_ENC_OPT_VXLAN_MAX, i, rem);
|
||||||
|
gbp = rta_getattr_u32(tb[TCA_TUNNEL_KEY_ENC_OPT_VXLAN_GBP]);
|
||||||
|
|
||||||
|
print_nl();
|
||||||
|
print_string(PRINT_FP, name, "\t%s ", name);
|
||||||
|
open_json_array(PRINT_JSON, name);
|
||||||
|
open_json_object(NULL);
|
||||||
|
print_uint(PRINT_ANY, "gbp", "%u", gbp);
|
||||||
|
close_json_object();
|
||||||
|
close_json_array(PRINT_JSON, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tunnel_key_print_erspan_options(struct rtattr *attr)
|
||||||
|
{
|
||||||
|
struct rtattr *tb[TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_MAX + 1];
|
||||||
|
struct rtattr *i = RTA_DATA(attr);
|
||||||
|
int rem = RTA_PAYLOAD(attr);
|
||||||
|
char *name = "erspan_opts";
|
||||||
|
__u8 ver, hwid, dir;
|
||||||
|
__u32 idx;
|
||||||
|
|
||||||
|
parse_rtattr(tb, TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_MAX, i, rem);
|
||||||
|
ver = rta_getattr_u8(tb[TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_VER]);
|
||||||
|
if (ver == 1) {
|
||||||
|
idx = rta_getattr_be32(tb[TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_INDEX]);
|
||||||
|
dir = 0;
|
||||||
|
hwid = 0;
|
||||||
|
} else {
|
||||||
|
idx = 0;
|
||||||
|
dir = rta_getattr_u8(tb[TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_DIR]);
|
||||||
|
hwid = rta_getattr_u8(tb[TCA_TUNNEL_KEY_ENC_OPT_ERSPAN_HWID]);
|
||||||
|
}
|
||||||
|
|
||||||
|
print_nl();
|
||||||
|
print_string(PRINT_FP, name, "\t%s ", name);
|
||||||
|
open_json_array(PRINT_JSON, name);
|
||||||
|
open_json_object(NULL);
|
||||||
|
print_uint(PRINT_ANY, "ver", "%u", ver);
|
||||||
|
print_uint(PRINT_ANY, "index", ":%u", idx);
|
||||||
|
print_uint(PRINT_ANY, "dir", ":%u", dir);
|
||||||
|
print_uint(PRINT_ANY, "hwid", ":%u", hwid);
|
||||||
|
close_json_object();
|
||||||
|
close_json_array(PRINT_JSON, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tunnel_key_print_key_opt(struct rtattr *attr)
|
||||||
{
|
{
|
||||||
struct rtattr *tb[TCA_TUNNEL_KEY_ENC_OPTS_MAX + 1];
|
struct rtattr *tb[TCA_TUNNEL_KEY_ENC_OPTS_MAX + 1];
|
||||||
|
|
||||||
|
|
@ -462,8 +637,15 @@ static void tunnel_key_print_key_opt(const char *name, struct rtattr *attr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
parse_rtattr_nested(tb, TCA_TUNNEL_KEY_ENC_OPTS_MAX, attr);
|
parse_rtattr_nested(tb, TCA_TUNNEL_KEY_ENC_OPTS_MAX, attr);
|
||||||
tunnel_key_print_geneve_options(name,
|
if (tb[TCA_TUNNEL_KEY_ENC_OPTS_GENEVE])
|
||||||
tb[TCA_TUNNEL_KEY_ENC_OPTS_GENEVE]);
|
tunnel_key_print_geneve_options(
|
||||||
|
tb[TCA_TUNNEL_KEY_ENC_OPTS_GENEVE]);
|
||||||
|
else if (tb[TCA_TUNNEL_KEY_ENC_OPTS_VXLAN])
|
||||||
|
tunnel_key_print_vxlan_options(
|
||||||
|
tb[TCA_TUNNEL_KEY_ENC_OPTS_VXLAN]);
|
||||||
|
else if (tb[TCA_TUNNEL_KEY_ENC_OPTS_ERSPAN])
|
||||||
|
tunnel_key_print_erspan_options(
|
||||||
|
tb[TCA_TUNNEL_KEY_ENC_OPTS_ERSPAN]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tunnel_key_print_tos_ttl(FILE *f, char *name,
|
static void tunnel_key_print_tos_ttl(FILE *f, char *name,
|
||||||
|
|
@ -519,8 +701,7 @@ static int print_tunnel_key(struct action_util *au, FILE *f, struct rtattr *arg)
|
||||||
tb[TCA_TUNNEL_KEY_ENC_KEY_ID]);
|
tb[TCA_TUNNEL_KEY_ENC_KEY_ID]);
|
||||||
tunnel_key_print_dst_port(f, "dst_port",
|
tunnel_key_print_dst_port(f, "dst_port",
|
||||||
tb[TCA_TUNNEL_KEY_ENC_DST_PORT]);
|
tb[TCA_TUNNEL_KEY_ENC_DST_PORT]);
|
||||||
tunnel_key_print_key_opt("geneve_opts",
|
tunnel_key_print_key_opt(tb[TCA_TUNNEL_KEY_ENC_OPTS]);
|
||||||
tb[TCA_TUNNEL_KEY_ENC_OPTS]);
|
|
||||||
tunnel_key_print_flag(f, "nocsum", "csum",
|
tunnel_key_print_flag(f, "nocsum", "csum",
|
||||||
tb[TCA_TUNNEL_KEY_NO_CSUM]);
|
tb[TCA_TUNNEL_KEY_NO_CSUM]);
|
||||||
tunnel_key_print_tos_ttl(f, "tos",
|
tunnel_key_print_tos_ttl(f, "tos",
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ static int mqprio_parse_opt(struct qdisc_util *qu, int argc,
|
||||||
__u64 max_rate64[TC_QOPT_MAX_QUEUE] = {0};
|
__u64 max_rate64[TC_QOPT_MAX_QUEUE] = {0};
|
||||||
__u16 shaper = TC_MQPRIO_SHAPER_DCB;
|
__u16 shaper = TC_MQPRIO_SHAPER_DCB;
|
||||||
__u16 mode = TC_MQPRIO_MODE_DCB;
|
__u16 mode = TC_MQPRIO_MODE_DCB;
|
||||||
|
int cnt_off_pairs = 0;
|
||||||
struct rtattr *tail;
|
struct rtattr *tail;
|
||||||
__u32 flags = 0;
|
__u32 flags = 0;
|
||||||
|
|
||||||
|
|
@ -94,6 +95,7 @@ static int mqprio_parse_opt(struct qdisc_util *qu, int argc,
|
||||||
}
|
}
|
||||||
free(tmp);
|
free(tmp);
|
||||||
idx++;
|
idx++;
|
||||||
|
cnt_off_pairs++;
|
||||||
}
|
}
|
||||||
} else if (strcmp(*argv, "hw") == 0) {
|
} else if (strcmp(*argv, "hw") == 0) {
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
|
|
@ -173,6 +175,12 @@ static int mqprio_parse_opt(struct qdisc_util *qu, int argc,
|
||||||
argc--; argv++;
|
argc--; argv++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cnt_off_pairs > opt.num_tc) {
|
||||||
|
fprintf(stderr, "queues count/offset pair count %d can not be higher than given num_tc %d\n",
|
||||||
|
cnt_off_pairs, opt.num_tc);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
tail = NLMSG_TAIL(n);
|
tail = NLMSG_TAIL(n);
|
||||||
addattr_l(n, 1024, TCA_OPTIONS, &opt, sizeof(opt));
|
addattr_l(n, 1024, TCA_OPTIONS, &opt, sizeof(opt));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -758,6 +758,10 @@ void print_tm(FILE *f, const struct tcf_t *tm)
|
||||||
print_uint(PRINT_ANY, "last_used", " used %u sec",
|
print_uint(PRINT_ANY, "last_used", " used %u sec",
|
||||||
tm->lastuse / hz);
|
tm->lastuse / hz);
|
||||||
|
|
||||||
|
if (tm->firstuse != 0)
|
||||||
|
print_uint(PRINT_ANY, "first_used", " firstused %u sec",
|
||||||
|
tm->firstuse / hz);
|
||||||
|
|
||||||
if (tm->expires != 0)
|
if (tm->expires != 0)
|
||||||
print_uint(PRINT_ANY, "expires", " expires %u sec",
|
print_uint(PRINT_ANY, "expires", " expires %u sec",
|
||||||
tm->expires / hz);
|
tm->expires / hz);
|
||||||
|
|
|
||||||
25
tipc/link.c
25
tipc/link.c
|
|
@ -334,7 +334,7 @@ static int _show_link_stat(const char *name, struct nlattr *attrs[],
|
||||||
|
|
||||||
open_json_object(NULL);
|
open_json_object(NULL);
|
||||||
|
|
||||||
print_string(PRINT_ANY, "link", "\nLink <%s>\n", name);
|
print_string(PRINT_ANY, "link", "Link <%s>\n", name);
|
||||||
print_string(PRINT_JSON, "state", "", NULL);
|
print_string(PRINT_JSON, "state", "", NULL);
|
||||||
open_json_array(PRINT_JSON, NULL);
|
open_json_array(PRINT_JSON, NULL);
|
||||||
if (attrs[TIPC_NLA_LINK_ACTIVE])
|
if (attrs[TIPC_NLA_LINK_ACTIVE])
|
||||||
|
|
@ -433,7 +433,7 @@ static int _show_link_stat(const char *name, struct nlattr *attrs[],
|
||||||
mnl_attr_get_u32(stats[TIPC_NLA_STATS_LINK_CONGS]));
|
mnl_attr_get_u32(stats[TIPC_NLA_STATS_LINK_CONGS]));
|
||||||
print_uint(PRINT_ANY, "send queue max", " Send queue max:%u",
|
print_uint(PRINT_ANY, "send queue max", " Send queue max:%u",
|
||||||
mnl_attr_get_u32(stats[TIPC_NLA_STATS_MAX_QUEUE]));
|
mnl_attr_get_u32(stats[TIPC_NLA_STATS_MAX_QUEUE]));
|
||||||
print_uint(PRINT_ANY, "avg", " avg:%u\n",
|
print_uint(PRINT_ANY, "avg", " avg:%u\n\n",
|
||||||
mnl_attr_get_u32(stats[TIPC_NLA_STATS_AVG_QUEUE]));
|
mnl_attr_get_u32(stats[TIPC_NLA_STATS_AVG_QUEUE]));
|
||||||
|
|
||||||
close_json_object();
|
close_json_object();
|
||||||
|
|
@ -496,7 +496,7 @@ static int _show_bc_link_stat(const char *name, struct nlattr *prop[],
|
||||||
mnl_attr_get_u32(stats[TIPC_NLA_STATS_LINK_CONGS]));
|
mnl_attr_get_u32(stats[TIPC_NLA_STATS_LINK_CONGS]));
|
||||||
print_uint(PRINT_ANY, "send queue max", " Send queue max:%u",
|
print_uint(PRINT_ANY, "send queue max", " Send queue max:%u",
|
||||||
mnl_attr_get_u32(stats[TIPC_NLA_STATS_MAX_QUEUE]));
|
mnl_attr_get_u32(stats[TIPC_NLA_STATS_MAX_QUEUE]));
|
||||||
print_uint(PRINT_ANY, "avg", " avg:%u\n",
|
print_uint(PRINT_ANY, "avg", " avg:%u\n\n",
|
||||||
mnl_attr_get_u32(stats[TIPC_NLA_STATS_AVG_QUEUE]));
|
mnl_attr_get_u32(stats[TIPC_NLA_STATS_AVG_QUEUE]));
|
||||||
close_json_object();
|
close_json_object();
|
||||||
|
|
||||||
|
|
@ -527,8 +527,10 @@ static int link_stat_show_cb(const struct nlmsghdr *nlh, void *data)
|
||||||
|
|
||||||
name = mnl_attr_get_str(attrs[TIPC_NLA_LINK_NAME]);
|
name = mnl_attr_get_str(attrs[TIPC_NLA_LINK_NAME]);
|
||||||
|
|
||||||
/* If a link is passed, skip all but that link */
|
/* If a link is passed, skip all but that link.
|
||||||
if (link && (strcmp(name, link) != 0))
|
* Support a substring matching as well.
|
||||||
|
*/
|
||||||
|
if (link && !strstr(name, link))
|
||||||
return MNL_CB_OK;
|
return MNL_CB_OK;
|
||||||
|
|
||||||
if (attrs[TIPC_NLA_LINK_BROADCAST]) {
|
if (attrs[TIPC_NLA_LINK_BROADCAST]) {
|
||||||
|
|
@ -540,7 +542,7 @@ static int link_stat_show_cb(const struct nlmsghdr *nlh, void *data)
|
||||||
|
|
||||||
static void cmd_link_stat_show_help(struct cmdl *cmdl)
|
static void cmd_link_stat_show_help(struct cmdl *cmdl)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Usage: %s link stat show [ link LINK ]\n",
|
fprintf(stderr, "Usage: %s link stat show [ link { LINK | SUBSTRING | all } ]\n",
|
||||||
cmdl->argv[0]);
|
cmdl->argv[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -554,6 +556,7 @@ static int cmd_link_stat_show(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
{ "link", OPT_KEYVAL, NULL },
|
{ "link", OPT_KEYVAL, NULL },
|
||||||
{ NULL }
|
{ NULL }
|
||||||
};
|
};
|
||||||
|
struct nlattr *attrs;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
if (help_flag) {
|
if (help_flag) {
|
||||||
|
|
@ -571,8 +574,14 @@ static int cmd_link_stat_show(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
opt = get_opt(opts, "link");
|
opt = get_opt(opts, "link");
|
||||||
if (opt)
|
if (opt) {
|
||||||
link = opt->val;
|
if (strcmp(opt->val, "all"))
|
||||||
|
link = opt->val;
|
||||||
|
/* Set the flag to dump all bc links */
|
||||||
|
attrs = mnl_attr_nest_start(nlh, TIPC_NLA_LINK);
|
||||||
|
mnl_attr_put(nlh, TIPC_NLA_LINK_BROADCAST, 0, NULL);
|
||||||
|
mnl_attr_nest_end(nlh, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
new_json_obj(json);
|
new_json_obj(json);
|
||||||
err = msg_dumpit(nlh, link_stat_show_cb, link);
|
err = msg_dumpit(nlh, link_stat_show_cb, link);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue