Merge git://git.kernel.org/pub/scm/network/iproute2/iproute2-next

This commit is contained in:
Stephen Hemminger 2022-05-26 17:09:59 -07:00
commit b1521ec002
41 changed files with 2987 additions and 278 deletions

View File

@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
BROBJ = bridge.o fdb.o monitor.o link.o mdb.o vlan.o
BROBJ = bridge.o fdb.o monitor.o link.o mdb.o vlan.o vni.o
include ../config.mk

View File

@ -14,6 +14,7 @@ void print_stp_state(__u8 state);
int parse_stp_state(const char *arg);
int print_vlan_rtm(struct nlmsghdr *n, void *arg, bool monitor,
bool global_only);
int print_vnifilter_rtm(struct nlmsghdr *n, void *arg, bool monitor);
void br_print_router_port_stats(struct rtattr *pattr);
int do_fdb(int argc, char **argv);
@ -21,6 +22,7 @@ int do_mdb(int argc, char **argv);
int do_monitor(int argc, char **argv);
int do_vlan(int argc, char **argv);
int do_link(int argc, char **argv);
int do_vni(int argc, char **argv);
extern int preferred_family;
extern int show_stats;

View File

@ -58,6 +58,7 @@ static const struct cmd {
{ "fdb", do_fdb },
{ "mdb", do_mdb },
{ "vlan", do_vlan },
{ "vni", do_vni },
{ "monitor", do_monitor },
{ "help", do_help },
{ 0 }

View File

@ -31,10 +31,20 @@ static int prefix_banner;
static void usage(void)
{
fprintf(stderr, "Usage: bridge monitor [file | link | fdb | mdb | vlan | all]\n");
fprintf(stderr, "Usage: bridge monitor [file | link | fdb | mdb | vlan | vni | all]\n");
exit(-1);
}
static int print_tunnel_rtm(struct nlmsghdr *n, void *arg, bool monitor)
{
struct tunnel_msg *tmsg = NLMSG_DATA(n);
if (tmsg->family == PF_BRIDGE)
return print_vnifilter_rtm(n, arg, monitor);
return 0;
}
static int accept_msg(struct rtnl_ctrl_data *ctrl,
struct nlmsghdr *n, void *arg)
{
@ -73,6 +83,12 @@ static int accept_msg(struct rtnl_ctrl_data *ctrl,
fprintf(fp, "[VLAN]");
return print_vlan_rtm(n, arg, true, false);
case RTM_NEWTUNNEL:
case RTM_DELTUNNEL:
if (prefix_banner)
fprintf(fp, "[TUNNEL]");
return print_tunnel_rtm(n, arg, true);
default:
return 0;
}
@ -86,6 +102,7 @@ int do_monitor(int argc, char **argv)
int lneigh = 0;
int lmdb = 0;
int lvlan = 0;
int lvni = 0;
rtnl_close(&rth);
@ -105,9 +122,13 @@ int do_monitor(int argc, char **argv)
} else if (matches(*argv, "vlan") == 0) {
lvlan = 1;
groups = 0;
} else if (strcmp(*argv, "vni") == 0) {
lvni = 1;
groups = 0;
} else if (strcmp(*argv, "all") == 0) {
groups = ~RTMGRP_TC;
lvlan = 1;
lvni = 1;
prefix_banner = 1;
} else if (matches(*argv, "help") == 0) {
usage();
@ -151,6 +172,11 @@ int do_monitor(int argc, char **argv)
exit(1);
}
if (lvni && rtnl_add_nl_group(&rth, RTNLGRP_TUNNEL) < 0) {
fprintf(stderr, "Failed to add bridge vni group to list\n");
exit(1);
}
ll_init_map(&rth);
if (rtnl_listen(&rth, accept_msg, stdout) < 0)

View File

@ -1179,7 +1179,8 @@ static int vlan_show(int argc, char **argv, int subject)
__u32 filt_mask;
filt_mask = IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_XSTATS);
if (rtnl_statsdump_req_filter(&rth, AF_UNSPEC, filt_mask) < 0) {
if (rtnl_statsdump_req_filter(&rth, AF_UNSPEC, filt_mask,
NULL, NULL) < 0) {
perror("Cannot send dump request");
exit(1);
}
@ -1194,7 +1195,8 @@ static int vlan_show(int argc, char **argv, int subject)
}
filt_mask = IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_XSTATS_SLAVE);
if (rtnl_statsdump_req_filter(&rth, AF_UNSPEC, filt_mask) < 0) {
if (rtnl_statsdump_req_filter(&rth, AF_UNSPEC, filt_mask,
NULL, NULL) < 0) {
perror("Cannot send slave dump request");
exit(1);
}

439
bridge/vni.c Normal file
View File

@ -0,0 +1,439 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Command to manage vnifiltering on a vxlan device
*
* Authors: Roopa Prabhu <roopa@nvidia.com>
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <linux/if_link.h>
#include <linux/if_bridge.h>
#include <linux/if_ether.h>
#include "json_print.h"
#include "libnetlink.h"
#include "br_common.h"
#include "utils.h"
static unsigned int filter_index;
#define VXLAN_ID_LEN 15
#define __stringify_1(x...) #x
#define __stringify(x...) __stringify_1(x)
static void usage(void)
{
fprintf(stderr,
"Usage: bridge vni { add | del } vni VNI\n"
" [ { group | remote } IP_ADDRESS ]\n"
" [ dev DEV ]\n"
" bridge vni { show }\n"
"\n"
"Where: VNI := 0-16777215\n"
);
exit(-1);
}
static int parse_vni_filter(const char *argv, struct nlmsghdr *n, int reqsize,
inet_prefix *group)
{
char *vnilist = strdupa(argv);
char *vni = strtok(vnilist, ",");
int group_type = AF_UNSPEC;
struct rtattr *nlvlist_e;
char *v;
int i;
if (group && is_addrtype_inet(group))
group_type = (group->family == AF_INET) ? VXLAN_VNIFILTER_ENTRY_GROUP :
VXLAN_VNIFILTER_ENTRY_GROUP6;
for (i = 0; vni; i++) {
__u32 vni_start = 0, vni_end = 0;
v = strchr(vni, '-');
if (v) {
*v = '\0';
v++;
vni_start = atoi(vni);
vni_end = atoi(v);
} else {
vni_start = atoi(vni);
}
nlvlist_e = addattr_nest(n, reqsize, VXLAN_VNIFILTER_ENTRY |
NLA_F_NESTED);
addattr32(n, 1024, VXLAN_VNIFILTER_ENTRY_START, vni_start);
if (vni_end)
addattr32(n, 1024, VXLAN_VNIFILTER_ENTRY_END, vni_end);
if (group)
addattr_l(n, 1024, group_type, group->data, group->bytelen);
addattr_nest_end(n, nlvlist_e);
vni = strtok(NULL, ",");
}
return 0;
}
static int vni_modify(int cmd, int argc, char **argv)
{
struct {
struct nlmsghdr n;
struct tunnel_msg tmsg;
char buf[1024];
} req = {
.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tunnel_msg)),
.n.nlmsg_flags = NLM_F_REQUEST,
.n.nlmsg_type = cmd,
.tmsg.family = PF_BRIDGE,
};
bool group_present = false;
inet_prefix daddr;
char *vni = NULL;
char *d = NULL;
while (argc > 0) {
if (strcmp(*argv, "dev") == 0) {
NEXT_ARG();
d = *argv;
} else if (strcmp(*argv, "vni") == 0) {
NEXT_ARG();
if (vni)
invarg("duplicate vni", *argv);
vni = *argv;
} else if (strcmp(*argv, "group") == 0) {
if (group_present)
invarg("duplicate group", *argv);
if (is_addrtype_inet_not_multi(&daddr)) {
fprintf(stderr, "vxlan: both group and remote");
fprintf(stderr, " cannot be specified\n");
return -1;
}
NEXT_ARG();
get_addr(&daddr, *argv, AF_UNSPEC);
if (!is_addrtype_inet_multi(&daddr))
invarg("invalid group address", *argv);
group_present = true;
} else if (strcmp(*argv, "remote") == 0) {
if (group_present)
invarg("duplicate group", *argv);
NEXT_ARG();
get_addr(&daddr, *argv, AF_UNSPEC);
group_present = true;
} else {
if (strcmp(*argv, "help") == 0)
usage();
}
argc--; argv++;
}
if (d == NULL || vni == NULL) {
fprintf(stderr, "Device and VNI ID are required arguments.\n");
return -1;
}
if (!vni && group_present) {
fprintf(stderr, "Group can only be specified with a vni\n");
return -1;
}
if (vni)
parse_vni_filter(vni, &req.n, sizeof(req),
(group_present ? &daddr : NULL));
req.tmsg.ifindex = ll_name_to_index(d);
if (req.tmsg.ifindex == 0) {
fprintf(stderr, "Cannot find vxlan device \"%s\"\n", d);
return -1;
}
if (rtnl_talk(&rth, &req.n, NULL) < 0)
return -1;
return 0;
}
static void open_vni_port(int ifi_index, const char *fmt)
{
open_json_object(NULL);
print_color_string(PRINT_ANY, COLOR_IFNAME, "ifname",
"%-" __stringify(IFNAMSIZ) "s ",
ll_index_to_name(ifi_index));
open_json_array(PRINT_JSON, "vnis");
}
static void close_vni_port(void)
{
close_json_array(PRINT_JSON, NULL);
close_json_object();
}
static void print_range(const char *name, __u32 start, __u32 id)
{
char end[64];
snprintf(end, sizeof(end), "%sEnd", name);
print_uint(PRINT_ANY, name, " %u", start);
if (start != id)
print_uint(PRINT_ANY, end, "-%-14u ", id);
}
static void print_vnifilter_entry_stats(struct rtattr *stats_attr)
{
struct rtattr *stb[VNIFILTER_ENTRY_STATS_MAX+1];
__u64 stat;
open_json_object("stats");
parse_rtattr_flags(stb, VNIFILTER_ENTRY_STATS_MAX, RTA_DATA(stats_attr),
RTA_PAYLOAD(stats_attr), NLA_F_NESTED);
print_nl();
print_string(PRINT_FP, NULL, "%-" __stringify(IFNAMSIZ) "s ", "");
print_string(PRINT_FP, NULL, "RX: ", "");
if (stb[VNIFILTER_ENTRY_STATS_RX_BYTES]) {
stat = rta_getattr_u64(stb[VNIFILTER_ENTRY_STATS_RX_BYTES]);
print_lluint(PRINT_ANY, "rx_bytes", "bytes %llu ", stat);
}
if (stb[VNIFILTER_ENTRY_STATS_RX_PKTS]) {
stat = rta_getattr_u64(stb[VNIFILTER_ENTRY_STATS_RX_PKTS]);
print_lluint(PRINT_ANY, "rx_pkts", "pkts %llu ", stat);
}
if (stb[VNIFILTER_ENTRY_STATS_RX_DROPS]) {
stat = rta_getattr_u64(stb[VNIFILTER_ENTRY_STATS_RX_DROPS]);
print_lluint(PRINT_ANY, "rx_drops", "drops %llu ", stat);
}
if (stb[VNIFILTER_ENTRY_STATS_RX_ERRORS]) {
stat = rta_getattr_u64(stb[VNIFILTER_ENTRY_STATS_RX_ERRORS]);
print_lluint(PRINT_ANY, "rx_errors", "errors %llu ", stat);
}
print_nl();
print_string(PRINT_FP, NULL, "%-" __stringify(IFNAMSIZ) "s ", "");
print_string(PRINT_FP, NULL, "TX: ", "");
if (stb[VNIFILTER_ENTRY_STATS_TX_BYTES]) {
stat = rta_getattr_u64(stb[VNIFILTER_ENTRY_STATS_TX_BYTES]);
print_lluint(PRINT_ANY, "tx_bytes", "bytes %llu ", stat);
}
if (stb[VNIFILTER_ENTRY_STATS_TX_PKTS]) {
stat = rta_getattr_u64(stb[VNIFILTER_ENTRY_STATS_TX_PKTS]);
print_lluint(PRINT_ANY, "tx_pkts", "pkts %llu ", stat);
}
if (stb[VNIFILTER_ENTRY_STATS_TX_DROPS]) {
stat = rta_getattr_u64(stb[VNIFILTER_ENTRY_STATS_TX_DROPS]);
print_lluint(PRINT_ANY, "tx_drops", "drops %llu ", stat);
}
if (stb[VNIFILTER_ENTRY_STATS_TX_ERRORS]) {
stat = rta_getattr_u64(stb[VNIFILTER_ENTRY_STATS_TX_ERRORS]);
print_lluint(PRINT_ANY, "tx_errors", "errors %llu ", stat);
}
close_json_object();
}
static void print_vni(struct rtattr *t, int ifindex)
{
struct rtattr *ttb[VXLAN_VNIFILTER_ENTRY_MAX+1];
__u32 vni_start = 0;
__u32 vni_end = 0;
parse_rtattr_flags(ttb, VXLAN_VNIFILTER_ENTRY_MAX, RTA_DATA(t),
RTA_PAYLOAD(t), NLA_F_NESTED);
if (ttb[VXLAN_VNIFILTER_ENTRY_START])
vni_start = rta_getattr_u32(ttb[VXLAN_VNIFILTER_ENTRY_START]);
if (ttb[VXLAN_VNIFILTER_ENTRY_END])
vni_end = rta_getattr_u32(ttb[VXLAN_VNIFILTER_ENTRY_END]);
if (vni_end)
print_range("vni", vni_start, vni_end);
else
print_uint(PRINT_ANY, "vni", " %-14u", vni_start);
if (ttb[VXLAN_VNIFILTER_ENTRY_GROUP]) {
__be32 addr = rta_getattr_u32(ttb[VXLAN_VNIFILTER_ENTRY_GROUP]);
if (addr) {
if (IN_MULTICAST(ntohl(addr)))
print_string(PRINT_ANY,
"group",
" %s",
format_host(AF_INET, 4, &addr));
else
print_string(PRINT_ANY,
"remote",
" %s",
format_host(AF_INET, 4, &addr));
}
} else if (ttb[VXLAN_VNIFILTER_ENTRY_GROUP6]) {
struct in6_addr addr;
memcpy(&addr, RTA_DATA(ttb[VXLAN_VNIFILTER_ENTRY_GROUP6]), sizeof(struct in6_addr));
if (!IN6_IS_ADDR_UNSPECIFIED(&addr)) {
if (IN6_IS_ADDR_MULTICAST(&addr))
print_string(PRINT_ANY,
"group",
" %s",
format_host(AF_INET6,
sizeof(struct in6_addr),
&addr));
else
print_string(PRINT_ANY,
"remote",
" %s",
format_host(AF_INET6,
sizeof(struct in6_addr),
&addr));
}
}
if (ttb[VXLAN_VNIFILTER_ENTRY_STATS])
print_vnifilter_entry_stats(ttb[VXLAN_VNIFILTER_ENTRY_STATS]);
close_json_object();
print_string(PRINT_FP, NULL, "%s", _SL_);
}
int print_vnifilter_rtm(struct nlmsghdr *n, void *arg, bool monitor)
{
struct tunnel_msg *tmsg = NLMSG_DATA(n);
int len = n->nlmsg_len;
bool first = true;
struct rtattr *t;
int rem;
if (n->nlmsg_type != RTM_NEWTUNNEL &&
n->nlmsg_type != RTM_DELTUNNEL &&
n->nlmsg_type != RTM_GETTUNNEL) {
fprintf(stderr, "Unknown vni tunnel rtm msg: %08x %08x %08x\n",
n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
return 0;
}
len -= NLMSG_LENGTH(sizeof(*tmsg));
if (len < 0) {
fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
return -1;
}
if (tmsg->family != AF_BRIDGE)
return 0;
if (filter_index && filter_index != tmsg->ifindex)
return 0;
if (n->nlmsg_type == RTM_DELTUNNEL)
print_bool(PRINT_ANY, "deleted", "Deleted ", true);
rem = len;
for (t = TUNNEL_RTA(tmsg); RTA_OK(t, rem); t = RTA_NEXT(t, rem)) {
unsigned short rta_type = t->rta_type & NLA_TYPE_MASK;
if (rta_type != VXLAN_VNIFILTER_ENTRY)
continue;
if (first) {
open_vni_port(tmsg->ifindex, "%s");
open_json_object(NULL);
first = false;
} else {
open_json_object(NULL);
print_string(PRINT_FP, NULL, "%-" __stringify(IFNAMSIZ) "s ", "");
}
print_vni(t, tmsg->ifindex);
}
close_vni_port();
print_string(PRINT_FP, NULL, "%s", _SL_);
fflush(stdout);
return 0;
}
static int print_vnifilter_rtm_filter(struct nlmsghdr *n, void *arg)
{
return print_vnifilter_rtm(n, arg, false);
}
static int vni_show(int argc, char **argv)
{
char *filter_dev = NULL;
__u8 flags = 0;
int ret = 0;
while (argc > 0) {
if (strcmp(*argv, "dev") == 0) {
NEXT_ARG();
if (filter_dev)
duparg("dev", *argv);
filter_dev = *argv;
}
argc--; argv++;
}
if (filter_dev) {
filter_index = ll_name_to_index(filter_dev);
if (!filter_index)
return nodev(filter_dev);
}
new_json_obj(json);
if (show_stats)
flags = TUNNEL_MSG_FLAG_STATS;
if (rtnl_tunneldump_req(&rth, PF_BRIDGE, filter_index, flags) < 0) {
perror("Cannot send dump request");
exit(1);
}
if (!is_json_context()) {
printf("%-" __stringify(IFNAMSIZ) "s %-"
__stringify(VXLAN_ID_LEN) "s %-"
__stringify(15) "s",
"dev", "vni", "group/remote");
printf("\n");
}
ret = rtnl_dump_filter(&rth, print_vnifilter_rtm_filter, NULL);
if (ret < 0) {
fprintf(stderr, "Dump ternminated\n");
exit(1);
}
delete_json_obj();
fflush(stdout);
return 0;
}
int do_vni(int argc, char **argv)
{
ll_init_map(&rth);
if (argc > 0) {
if (strcmp(*argv, "add") == 0)
return vni_modify(RTM_NEWTUNNEL, argc-1, argv+1);
if (strcmp(*argv, "delete") == 0)
return vni_modify(RTM_DELTUNNEL, argc-1, argv+1);
if (strcmp(*argv, "show") == 0 ||
strcmp(*argv, "lst") == 0 ||
strcmp(*argv, "list") == 0)
return vni_show(argc-1, argv+1);
if (strcmp(*argv, "help") == 0)
usage();
} else {
return vni_show(0, NULL);
}
fprintf(stderr, "Command \"%s\" is unknown, try \"bridge vni help\".\n", *argv);
exit(-1);
}

View File

@ -367,6 +367,7 @@ struct dl {
bool pretty_output;
bool verbose;
bool stats;
bool hex;
struct {
bool present;
char *bus_name;
@ -8044,6 +8045,8 @@ static int cmd_health_dump_clear(struct dl *dl)
static int fmsg_value_show(struct dl *dl, int type, struct nlattr *nl_data)
{
const char *num_fmt = dl->hex ? "%#x" : "%u";
const char *num64_fmt = dl->hex ? "%#"PRIx64 : "%"PRIu64;
uint8_t *data;
uint32_t len;
@ -8053,16 +8056,16 @@ static int fmsg_value_show(struct dl *dl, int type, struct nlattr *nl_data)
print_bool(PRINT_ANY, NULL, "%s", mnl_attr_get_u8(nl_data));
break;
case MNL_TYPE_U8:
print_uint(PRINT_ANY, NULL, "%u", mnl_attr_get_u8(nl_data));
print_uint(PRINT_ANY, NULL, num_fmt, mnl_attr_get_u8(nl_data));
break;
case MNL_TYPE_U16:
print_uint(PRINT_ANY, NULL, "%u", mnl_attr_get_u16(nl_data));
print_uint(PRINT_ANY, NULL, num_fmt, mnl_attr_get_u16(nl_data));
break;
case MNL_TYPE_U32:
print_uint(PRINT_ANY, NULL, "%u", mnl_attr_get_u32(nl_data));
print_uint(PRINT_ANY, NULL, num_fmt, mnl_attr_get_u32(nl_data));
break;
case MNL_TYPE_U64:
print_u64(PRINT_ANY, NULL, "%"PRIu64, mnl_attr_get_u64(nl_data));
print_u64(PRINT_ANY, NULL, num64_fmt, mnl_attr_get_u64(nl_data));
break;
case MNL_TYPE_NUL_STRING:
print_string(PRINT_ANY, NULL, "%s", mnl_attr_get_str(nl_data));
@ -8939,7 +8942,7 @@ static void help(void)
pr_err("Usage: devlink [ OPTIONS ] OBJECT { COMMAND | help }\n"
" devlink [ -f[orce] ] -b[atch] filename -N[etns] netnsname\n"
"where OBJECT := { dev | port | sb | monitor | dpipe | resource | region | health | trap }\n"
" OPTIONS := { -V[ersion] | -n[o-nice-names] | -j[son] | -p[retty] | -v[erbose] -s[tatistics] }\n");
" OPTIONS := { -V[ersion] | -n[o-nice-names] | -j[son] | -p[retty] | -v[erbose] -s[tatistics] -[he]x }\n");
}
static int dl_cmd(struct dl *dl, int argc, char **argv)
@ -9053,6 +9056,7 @@ int main(int argc, char **argv)
{ "statistics", no_argument, NULL, 's' },
{ "Netns", required_argument, NULL, 'N' },
{ "iec", no_argument, NULL, 'i' },
{ "hex", no_argument, NULL, 'x' },
{ NULL, 0, NULL, 0 }
};
const char *batch_file = NULL;
@ -9068,7 +9072,7 @@ int main(int argc, char **argv)
return EXIT_FAILURE;
}
while ((opt = getopt_long(argc, argv, "Vfb:njpvsN:i",
while ((opt = getopt_long(argc, argv, "Vfb:njpvsN:ix",
long_options, NULL)) >= 0) {
switch (opt) {
@ -9106,6 +9110,9 @@ int main(int argc, char **argv)
case 'i':
use_iec = true;
break;
case 'x':
dl->hex = true;
break;
default:
pr_err("Unknown option.\n");
help();

View File

@ -37,6 +37,12 @@ struct nlmsg_chain {
struct nlmsg_list *tail;
};
struct ipstats_req {
struct nlmsghdr nlh;
struct if_stats_msg ifsm;
char buf[128];
};
extern int rcvbuf;
int rtnl_open(struct rtnl_handle *rth, unsigned int subscriptions)
@ -88,7 +94,10 @@ int rtnl_fdb_linkdump_req_filter_fn(struct rtnl_handle *rth,
int rtnl_nsiddump_req_filter_fn(struct rtnl_handle *rth, int family,
req_filter_fn_t filter_fn)
__attribute__((warn_unused_result));
int rtnl_statsdump_req_filter(struct rtnl_handle *rth, int fam, __u32 filt_mask)
int rtnl_statsdump_req_filter(struct rtnl_handle *rth, int fam, __u32 filt_mask,
int (*filter_fn)(struct ipstats_req *req,
void *data),
void *filter_data)
__attribute__((warn_unused_result));
int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req,
int len)
@ -103,6 +112,10 @@ int rtnl_nexthop_bucket_dump_req(struct rtnl_handle *rth, int family,
req_filter_fn_t filter_fn)
__attribute__((warn_unused_result));
int rtnl_tunneldump_req(struct rtnl_handle *rth, int family, int ifindex,
__u8 flags)
__attribute__((warn_unused_result));
struct rtnl_ctrl_data {
int nsid;
};
@ -322,6 +335,11 @@ int rtnl_from_file(FILE *, rtnl_listen_filter_t handler,
((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct br_vlan_msg))))
#endif
#ifndef TUNNEL_RTA
#define TUNNEL_RTA(r) \
((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct tunnel_msg))))
#endif
/* User defined nlmsg_type which is used mostly for logging netlink
* messages from dump file */
#define NLMSG_TSTAMP 15

View File

@ -1013,6 +1013,7 @@ enum bpf_link_type {
BPF_LINK_TYPE_XDP = 6,
BPF_LINK_TYPE_PERF_EVENT = 7,
BPF_LINK_TYPE_KPROBE_MULTI = 8,
BPF_LINK_TYPE_STRUCT_OPS = 9,
MAX_BPF_LINK_TYPE,
};
@ -1489,6 +1490,15 @@ union bpf_attr {
__aligned_u64 addrs;
__aligned_u64 cookies;
} kprobe_multi;
struct {
/* this is overlaid with the target_btf_id above. */
__u32 target_btf_id;
/* black box user-provided value passed through
* to BPF program at the execution time and
* accessible through bpf_get_attach_cookie() BPF helper
*/
__u64 cookie;
} tracing;
};
} link_create;
@ -5143,6 +5153,102 @@ union bpf_attr {
* The **hash_algo** is returned on success,
* **-EOPNOTSUP** if the hash calculation failed or **-EINVAL** if
* invalid arguments are passed.
*
* void *bpf_kptr_xchg(void *map_value, void *ptr)
* Description
* Exchange kptr at pointer *map_value* with *ptr*, and return the
* old value. *ptr* can be NULL, otherwise it must be a referenced
* pointer which will be released when this helper is called.
* Return
* The old value of kptr (which can be NULL). The returned pointer
* if not NULL, is a reference which must be released using its
* corresponding release function, or moved into a BPF map before
* program exit.
*
* void *bpf_map_lookup_percpu_elem(struct bpf_map *map, const void *key, u32 cpu)
* Description
* Perform a lookup in *percpu map* for an entry associated to
* *key* on *cpu*.
* Return
* Map value associated to *key* on *cpu*, or **NULL** if no entry
* was found or *cpu* is invalid.
*
* struct mptcp_sock *bpf_skc_to_mptcp_sock(void *sk)
* Description
* Dynamically cast a *sk* pointer to a *mptcp_sock* pointer.
* Return
* *sk* if casting is valid, or **NULL** otherwise.
*
* long bpf_dynptr_from_mem(void *data, u32 size, u64 flags, struct bpf_dynptr *ptr)
* Description
* Get a dynptr to local memory *data*.
*
* *data* must be a ptr to a map value.
* The maximum *size* supported is DYNPTR_MAX_SIZE.
* *flags* is currently unused.
* Return
* 0 on success, -E2BIG if the size exceeds DYNPTR_MAX_SIZE,
* -EINVAL if flags is not 0.
*
* long bpf_ringbuf_reserve_dynptr(void *ringbuf, u32 size, u64 flags, struct bpf_dynptr *ptr)
* Description
* Reserve *size* bytes of payload in a ring buffer *ringbuf*
* through the dynptr interface. *flags* must be 0.
*
* Please note that a corresponding bpf_ringbuf_submit_dynptr or
* bpf_ringbuf_discard_dynptr must be called on *ptr*, even if the
* reservation fails. This is enforced by the verifier.
* Return
* 0 on success, or a negative error in case of failure.
*
* void bpf_ringbuf_submit_dynptr(struct bpf_dynptr *ptr, u64 flags)
* Description
* Submit reserved ring buffer sample, pointed to by *data*,
* through the dynptr interface. This is a no-op if the dynptr is
* invalid/null.
*
* For more information on *flags*, please see
* 'bpf_ringbuf_submit'.
* Return
* Nothing. Always succeeds.
*
* void bpf_ringbuf_discard_dynptr(struct bpf_dynptr *ptr, u64 flags)
* Description
* Discard reserved ring buffer sample through the dynptr
* interface. This is a no-op if the dynptr is invalid/null.
*
* For more information on *flags*, please see
* 'bpf_ringbuf_discard'.
* Return
* Nothing. Always succeeds.
*
* long bpf_dynptr_read(void *dst, u32 len, struct bpf_dynptr *src, u32 offset)
* Description
* Read *len* bytes from *src* into *dst*, starting from *offset*
* into *src*.
* Return
* 0 on success, -E2BIG if *offset* + *len* exceeds the length
* of *src*'s data, -EINVAL if *src* is an invalid dynptr.
*
* long bpf_dynptr_write(struct bpf_dynptr *dst, u32 offset, void *src, u32 len)
* Description
* Write *len* bytes from *src* into *dst*, starting from *offset*
* into *dst*.
* Return
* 0 on success, -E2BIG if *offset* + *len* exceeds the length
* of *dst*'s data, -EINVAL if *dst* is an invalid dynptr or if *dst*
* is a read-only dynptr.
*
* void *bpf_dynptr_data(struct bpf_dynptr *ptr, u32 offset, u32 len)
* Description
* Get a pointer to the underlying dynptr data.
*
* *len* must be a statically known value. The returned data slice
* is invalidated whenever the dynptr is invalidated.
* Return
* Pointer to the underlying dynptr data, NULL if the dynptr is
* read-only, if the dynptr is invalid, or if the offset and length
* is out of bounds.
*/
#define __BPF_FUNC_MAPPER(FN) \
FN(unspec), \
@ -5339,6 +5445,16 @@ union bpf_attr {
FN(copy_from_user_task), \
FN(skb_set_tstamp), \
FN(ima_file_hash), \
FN(kptr_xchg), \
FN(map_lookup_percpu_elem), \
FN(skc_to_mptcp_sock), \
FN(dynptr_from_mem), \
FN(ringbuf_reserve_dynptr), \
FN(ringbuf_submit_dynptr), \
FN(ringbuf_discard_dynptr), \
FN(dynptr_read), \
FN(dynptr_write), \
FN(dynptr_data), \
/* */
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
@ -5592,6 +5708,10 @@ struct bpf_tunnel_key {
__u8 tunnel_ttl;
__u16 tunnel_ext; /* Padding, future use. */
__u32 tunnel_label;
union {
__u32 local_ipv4;
__u32 local_ipv6[4];
};
};
/* user accessible mirror of in-kernel xfrm_state.
@ -6486,6 +6606,11 @@ struct bpf_timer {
__u64 :64;
} __attribute__((aligned(8)));
struct bpf_dynptr {
__u64 :64;
__u64 :64;
} __attribute__((aligned(8)));
struct bpf_sysctl {
__u32 write; /* Sysctl is being read (= 0) or written (= 1).
* Allows 1,2,4-byte read, but no write.

View File

@ -33,8 +33,8 @@ struct btf_type {
/* "info" bits arrangement
* bits 0-15: vlen (e.g. # of struct's members)
* bits 16-23: unused
* bits 24-27: kind (e.g. int, ptr, array...etc)
* bits 28-30: unused
* bits 24-28: kind (e.g. int, ptr, array...etc)
* bits 29-30: unused
* bit 31: kind_flag, currently used by
* struct, union and fwd
*/

View File

@ -131,6 +131,11 @@ enum devlink_command {
DEVLINK_CMD_RATE_NEW,
DEVLINK_CMD_RATE_DEL,
DEVLINK_CMD_LINECARD_GET, /* can dump */
DEVLINK_CMD_LINECARD_SET,
DEVLINK_CMD_LINECARD_NEW,
DEVLINK_CMD_LINECARD_DEL,
/* add new commands above here */
__DEVLINK_CMD_MAX,
DEVLINK_CMD_MAX = __DEVLINK_CMD_MAX - 1
@ -338,6 +343,19 @@ enum devlink_reload_limit {
#define DEVLINK_RELOAD_LIMITS_VALID_MASK (_BITUL(__DEVLINK_RELOAD_LIMIT_MAX) - 1)
enum devlink_linecard_state {
DEVLINK_LINECARD_STATE_UNSPEC,
DEVLINK_LINECARD_STATE_UNPROVISIONED,
DEVLINK_LINECARD_STATE_UNPROVISIONING,
DEVLINK_LINECARD_STATE_PROVISIONING,
DEVLINK_LINECARD_STATE_PROVISIONING_FAILED,
DEVLINK_LINECARD_STATE_PROVISIONED,
DEVLINK_LINECARD_STATE_ACTIVE,
__DEVLINK_LINECARD_STATE_MAX,
DEVLINK_LINECARD_STATE_MAX = __DEVLINK_LINECARD_STATE_MAX - 1
};
enum devlink_attr {
/* don't change the order or add anything between, this is ABI! */
DEVLINK_ATTR_UNSPEC,
@ -553,6 +571,11 @@ enum devlink_attr {
DEVLINK_ATTR_REGION_MAX_SNAPSHOTS, /* u32 */
DEVLINK_ATTR_LINECARD_INDEX, /* u32 */
DEVLINK_ATTR_LINECARD_STATE, /* u8 */
DEVLINK_ATTR_LINECARD_TYPE, /* string */
DEVLINK_ATTR_LINECARD_SUPPORTED_TYPES, /* nested */
/* add new attributes above here, update the policy in devlink.c */
__DEVLINK_ATTR_MAX,

View File

@ -211,6 +211,9 @@ struct rtnl_link_stats {
* @rx_nohandler: Number of packets received on the interface
* but dropped by the networking stack because the device is
* not designated to receive packets (e.g. backup link in a bond).
*
* @rx_otherhost_dropped: Number of packets dropped due to mismatch
* in destination MAC address.
*/
struct rtnl_link_stats64 {
__u64 rx_packets;
@ -243,6 +246,8 @@ struct rtnl_link_stats64 {
__u64 rx_compressed;
__u64 tx_compressed;
__u64 rx_nohandler;
__u64 rx_otherhost_dropped;
};
/* Subset of link stats useful for in-HW collection. Meaning of the fields is as
@ -363,6 +368,8 @@ enum {
IFLA_PARENT_DEV_NAME,
IFLA_PARENT_DEV_BUS_NAME,
IFLA_GRO_MAX_SIZE,
IFLA_TSO_MAX_SIZE,
IFLA_TSO_MAX_SEGS,
__IFLA_MAX
};

View File

@ -53,6 +53,9 @@ enum {
MPTCP_PM_ATTR_ADDR, /* nested address */
MPTCP_PM_ATTR_RCV_ADD_ADDRS, /* u32 */
MPTCP_PM_ATTR_SUBFLOWS, /* u32 */
MPTCP_PM_ATTR_TOKEN, /* u32 */
MPTCP_PM_ATTR_LOC_ID, /* u8 */
MPTCP_PM_ATTR_ADDR_REMOTE, /* nested address */
__MPTCP_PM_ATTR_MAX
};
@ -91,6 +94,10 @@ enum {
MPTCP_PM_CMD_SET_LIMITS,
MPTCP_PM_CMD_GET_LIMITS,
MPTCP_PM_CMD_SET_FLAGS,
MPTCP_PM_CMD_ANNOUNCE,
MPTCP_PM_CMD_REMOVE,
MPTCP_PM_CMD_SUBFLOW_CREATE,
MPTCP_PM_CMD_SUBFLOW_DESTROY,
__MPTCP_PM_CMD_AFTER_LAST
};
@ -186,6 +193,7 @@ enum mptcp_event_attr {
MPTCP_ATTR_IF_IDX, /* s32 */
MPTCP_ATTR_RESET_REASON,/* u32 */
MPTCP_ATTR_RESET_FLAGS, /* u32 */
MPTCP_ATTR_SERVER_SIDE, /* u8 */
__MPTCP_ATTR_AFTER_LAST
};

View File

@ -32,6 +32,8 @@ enum {
NDA_NH_ID,
NDA_FDB_EXT_ATTRS,
NDA_FLAGS_EXT,
NDA_NDM_STATE_MASK,
NDA_NDM_FLAGS_MASK,
__NDA_MAX
};

View File

@ -72,6 +72,7 @@ struct nlmsghdr {
/* Modifiers to DELETE request */
#define NLM_F_NONREC 0x100 /* Do not delete recursively */
#define NLM_F_BULK 0x200 /* Delete multiple objects */
/* Flags for ACK message */
#define NLM_F_CAPPED 0x100 /* request was capped */

View File

@ -587,6 +587,8 @@ enum {
TCA_FLOWER_KEY_HASH, /* u32 */
TCA_FLOWER_KEY_HASH_MASK, /* u32 */
TCA_FLOWER_KEY_NUM_OF_VLANS, /* u8 */
__TCA_FLOWER_MAX,
};

View File

@ -29,6 +29,7 @@
#define SKBEDIT_F_PTYPE 0x8
#define SKBEDIT_F_MASK 0x10
#define SKBEDIT_F_INHERITDSFIELD 0x20
#define SKBEDIT_F_TXQ_SKBHASH 0x40
struct tc_skbedit {
tc_gen;
@ -45,6 +46,7 @@ enum {
TCA_SKBEDIT_PTYPE,
TCA_SKBEDIT_MASK,
TCA_SKBEDIT_FLAGS,
TCA_SKBEDIT_QUEUE_MAPPING_MAX,
__TCA_SKBEDIT_MAX
};
#define TCA_SKBEDIT_MAX (__TCA_SKBEDIT_MAX - 1)

View File

@ -39,6 +39,7 @@
/* TLS socket options */
#define TLS_TX 1 /* Set transmit parameters */
#define TLS_RX 2 /* Set receive parameters */
#define TLS_TX_ZEROCOPY_SENDFILE 3 /* transmit zerocopy sendfile */
/* Supported versions */
#define TLS_VERSION_MINOR(ver) ((ver) & 0xFF)
@ -160,6 +161,7 @@ enum {
TLS_INFO_CIPHER,
TLS_INFO_TXCONF,
TLS_INFO_RXCONF,
TLS_INFO_ZC_SENDFILE,
__TLS_INFO_MAX,
};
#define TLS_INFO_MAX (__TLS_INFO_MAX - 1)

View File

@ -21,6 +21,9 @@
#define __bitwise
#endif
/* The kernel doesn't use this legacy form, but user space does */
#define __bitwise__ __bitwise
typedef __u16 __bitwise __le16;
typedef __u16 __bitwise __be16;
typedef __u32 __bitwise __le32;

View File

@ -12,7 +12,8 @@ IPOBJ=ip.o ipaddress.o ipaddrlabel.o iproute.o iprule.o ipnetns.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 \
ipnexthop.o ipmptcp.o iplink_bareudp.o iplink_wwan.o ipioam6.o \
iplink_amt.o iplink_batadv.o iplink_gtp.o
iplink_amt.o iplink_batadv.o iplink_gtp.o iplink_virt_wifi.o \
ipstats.o
RTMONOBJ=rtmon.o

View File

@ -123,6 +123,7 @@ static const struct cmd {
{ "mptcp", do_mptcp },
{ "ioam", do_ioam6 },
{ "help", do_help },
{ "stats", do_ipstats },
{ 0 }
};

View File

@ -3,6 +3,7 @@
#define _IP_COMMON_H_
#include <stdbool.h>
#include <linux/mpls.h>
#include "json_print.h"
@ -57,6 +58,7 @@ int print_nexthop_bucket(struct nlmsghdr *n, void *arg);
void netns_map_init(void);
void netns_nsid_socket_init(void);
int print_nsid(struct nlmsghdr *n, void *arg);
int ipstats_print(struct nlmsghdr *n, void *arg);
char *get_name_from_nsid(int nsid);
int get_netnsid_from_name(const char *name);
int set_netnsid_from_name(const char *name, int nsid);
@ -90,6 +92,7 @@ int do_seg6(int argc, char **argv);
int do_ipnh(int argc, char **argv);
int do_mptcp(int argc, char **argv);
int do_ioam6(int argc, char **argv);
int do_ipstats(int argc, char **argv);
int iplink_get(char *name, __u32 filt_mask);
int iplink_ifla_xstats(int argc, char **argv);
@ -139,9 +142,14 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req, char **type);
void br_dump_bridge_id(const struct ifla_bridge_id *id, char *buf, size_t len);
int bridge_parse_xstats(struct link_util *lu, int argc, char **argv);
int bridge_print_xstats(struct nlmsghdr *n, void *arg);
extern const struct ipstats_stat_desc ipstats_stat_desc_xstats_bridge_group;
extern const struct ipstats_stat_desc ipstats_stat_desc_xstats_slave_bridge_group;
/* iplink_bond.c */
int bond_parse_xstats(struct link_util *lu, int argc, char **argv);
int bond_print_xstats(struct nlmsghdr *n, void *arg);
extern const struct ipstats_stat_desc ipstats_stat_desc_xstats_bond_group;
extern const struct ipstats_stat_desc ipstats_stat_desc_xstats_slave_bond_group;
/* iproute_lwtunnel.c */
int lwt_parse_encap(struct rtattr *rta, size_t len, int *argcp, char ***argvp,
@ -157,6 +165,46 @@ void xdp_dump(FILE *fp, struct rtattr *tb, bool link, bool details);
__u32 ipvrf_get_table(const char *name);
int name_is_vrf(const char *name);
/* ipstats.c */
enum ipstats_stat_desc_kind {
IPSTATS_STAT_DESC_KIND_LEAF,
IPSTATS_STAT_DESC_KIND_GROUP,
};
struct ipstats_stat_dump_filters;
struct ipstats_stat_show_attrs;
struct ipstats_stat_desc {
const char *name;
enum ipstats_stat_desc_kind kind;
union {
struct {
const struct ipstats_stat_desc **subs;
size_t nsubs;
};
struct {
void (*pack)(struct ipstats_stat_dump_filters *filters,
const struct ipstats_stat_desc *desc);
int (*show)(struct ipstats_stat_show_attrs *attrs,
const struct ipstats_stat_desc *desc);
};
};
};
struct ipstats_stat_desc_xstats {
const struct ipstats_stat_desc desc;
int xstats_at;
int link_type_at;
int inner_max;
int inner_at;
void (*show_cb)(const struct rtattr *at);
};
void ipstats_stat_desc_pack_xstats(struct ipstats_stat_dump_filters *filters,
const struct ipstats_stat_desc *desc);
int ipstats_stat_desc_show_xstats(struct ipstats_stat_show_attrs *attrs,
const struct ipstats_stat_desc *desc);
#ifndef INFINITY_LIFE_TIME
#define INFINITY_LIFE_TIME 0xFFFFFFFFU
#endif
@ -171,4 +219,9 @@ void print_rta_ifidx(FILE *fp, __u32 ifidx, const char *prefix);
void __print_rta_gateway(FILE *fp, unsigned char family, const char *gateway);
void print_rta_gateway(FILE *fp, unsigned char family,
const struct rtattr *rta);
void size_columns(unsigned int cols[], unsigned int n, ...);
void print_stats64(FILE *fp, struct rtnl_link_stats64 *s,
const struct rtattr *carrier_changes, const char *what);
void print_mpls_link_stats(FILE *fp, const struct mpls_link_stats *stats,
const char *indent);
#endif /* _IP_COMMON_H_ */

View File

@ -546,7 +546,7 @@ static void print_vfinfo(FILE *fp, struct ifinfomsg *ifi, struct rtattr *vfinfo)
print_vf_stats64(fp, vf[IFLA_VF_STATS]);
}
static void size_columns(unsigned int cols[], unsigned int n, ...)
void size_columns(unsigned int cols[], unsigned int n, ...)
{
unsigned int i, len;
uint64_t val, powi;
@ -680,10 +680,10 @@ static void print_vf_stats64(FILE *fp, struct rtattr *vfstats)
}
}
static void __print_link_stats(FILE *fp, struct rtattr *tb[])
void print_stats64(FILE *fp, struct rtnl_link_stats64 *s,
const struct rtattr *carrier_changes,
const char *what)
{
const struct rtattr *carrier_changes = tb[IFLA_CARRIER_CHANGES];
struct rtnl_link_stats64 _s, *s = &_s;
unsigned int cols[] = {
strlen("*X errors:"),
strlen("packets"),
@ -693,14 +693,10 @@ static void __print_link_stats(FILE *fp, struct rtattr *tb[])
strlen("overrun"),
strlen("compressed"),
};
int ret;
ret = get_rtnl_link_stats_rta(s, tb);
if (ret < 0)
return;
if (is_json_context()) {
open_json_object((ret == sizeof(*s)) ? "stats64" : "stats");
if (what)
open_json_object(what);
/* RX stats */
open_json_object("rx");
@ -771,7 +767,8 @@ static void __print_link_stats(FILE *fp, struct rtattr *tb[])
}
close_json_object();
close_json_object();
if (what)
close_json_object();
} else {
size_columns(cols, ARRAY_SIZE(cols),
s->rx_bytes, s->rx_packets, s->rx_errors,
@ -870,6 +867,20 @@ static void __print_link_stats(FILE *fp, struct rtattr *tb[])
}
}
static void __print_link_stats(FILE *fp, struct rtattr *tb[])
{
const struct rtattr *carrier_changes = tb[IFLA_CARRIER_CHANGES];
struct rtnl_link_stats64 _s, *s = &_s;
int ret;
ret = get_rtnl_link_stats_rta(s, tb);
if (ret < 0)
return;
print_stats64(fp, s, carrier_changes,
(ret == sizeof(*s)) ? "stats64" : "stats");
}
static void print_link_stats(FILE *fp, struct nlmsghdr *n)
{
struct ifinfomsg *ifi = NLMSG_DATA(n);

View File

@ -54,7 +54,7 @@ void iplink_types_usage(void)
" macsec | macvlan | macvtap |\n"
" netdevsim | nlmon | rmnet | sit | team | team_slave |\n"
" vcan | veth | vlan | vrf | vti | vxcan | vxlan | wwan |\n"
" xfrm }\n");
" xfrm | virt_wifi }\n");
}
void iplink_usage(void)
@ -1514,6 +1514,65 @@ static int do_set(int argc, char **argv)
}
#endif /* IPLINK_IOCTL_COMPAT */
void print_mpls_link_stats(FILE *fp, const struct mpls_link_stats *stats,
const char *indent)
{
unsigned int cols[] = {
strlen("*X: bytes"),
strlen("packets"),
strlen("errors"),
strlen("dropped"),
strlen("noroute"),
};
if (is_json_context()) {
/* RX stats */
open_json_object("rx");
print_u64(PRINT_JSON, "bytes", NULL, stats->rx_bytes);
print_u64(PRINT_JSON, "packets", NULL, stats->rx_packets);
print_u64(PRINT_JSON, "errors", NULL, stats->rx_errors);
print_u64(PRINT_JSON, "dropped", NULL, stats->rx_dropped);
print_u64(PRINT_JSON, "noroute", NULL, stats->rx_noroute);
close_json_object();
/* TX stats */
open_json_object("tx");
print_u64(PRINT_JSON, "bytes", NULL, stats->tx_bytes);
print_u64(PRINT_JSON, "packets", NULL, stats->tx_packets);
print_u64(PRINT_JSON, "errors", NULL, stats->tx_errors);
print_u64(PRINT_JSON, "dropped", NULL, stats->tx_dropped);
close_json_object();
} else {
size_columns(cols, ARRAY_SIZE(cols), stats->rx_bytes,
stats->rx_packets, stats->rx_errors,
stats->rx_dropped, stats->rx_noroute);
size_columns(cols, ARRAY_SIZE(cols), stats->tx_bytes,
stats->tx_packets, stats->tx_errors,
stats->tx_dropped, 0);
fprintf(fp, "%sRX: %*s %*s %*s %*s %*s%s", indent,
cols[0] - 4, "bytes", cols[1], "packets",
cols[2], "errors", cols[3], "dropped",
cols[4], "noroute", _SL_);
fprintf(fp, "%s", indent);
print_num(fp, cols[0], stats->rx_bytes);
print_num(fp, cols[1], stats->rx_packets);
print_num(fp, cols[2], stats->rx_errors);
print_num(fp, cols[3], stats->rx_dropped);
print_num(fp, cols[4], stats->rx_noroute);
fprintf(fp, "\n");
fprintf(fp, "%sTX: %*s %*s %*s %*s%s", indent,
cols[0] - 4, "bytes", cols[1], "packets",
cols[2], "errors", cols[3], "dropped", _SL_);
fprintf(fp, "%s", indent);
print_num(fp, cols[0], stats->tx_bytes);
print_num(fp, cols[1], stats->tx_packets);
print_num(fp, cols[2], stats->tx_errors);
print_num(fp, cols[3], stats->tx_dropped);
}
}
static void print_mpls_stats(FILE *fp, struct rtattr *attr)
{
struct rtattr *mrtb[MPLS_STATS_MAX+1];
@ -1525,22 +1584,8 @@ static void print_mpls_stats(FILE *fp, struct rtattr *attr)
return;
stats = RTA_DATA(mrtb[MPLS_STATS_LINK]);
fprintf(fp, " mpls:\n");
fprintf(fp, " RX: bytes packets errors dropped noroute\n");
fprintf(fp, " ");
print_num(fp, 10, stats->rx_bytes);
print_num(fp, 8, stats->rx_packets);
print_num(fp, 7, stats->rx_errors);
print_num(fp, 8, stats->rx_dropped);
print_num(fp, 7, stats->rx_noroute);
fprintf(fp, "\n");
fprintf(fp, " TX: bytes packets errors dropped\n");
fprintf(fp, " ");
print_num(fp, 10, stats->tx_bytes);
print_num(fp, 8, stats->tx_packets);
print_num(fp, 7, stats->tx_errors);
print_num(fp, 7, stats->tx_dropped);
print_mpls_link_stats(fp, stats, " ");
fprintf(fp, "\n");
}
@ -1641,7 +1686,8 @@ static int iplink_afstats(int argc, char **argv)
}
}
if (rtnl_statsdump_req_filter(&rth, AF_UNSPEC, filt_mask) < 0) {
if (rtnl_statsdump_req_filter(&rth, AF_UNSPEC, filt_mask,
NULL, NULL) < 0) {
perror("Cannont send dump request");
return 1;
}

View File

@ -15,6 +15,7 @@
#include <string.h>
#include <linux/if_bonding.h>
#include "list.h"
#include "rt_names.h"
#include "utils.h"
#include "ip_common.h"
@ -761,7 +762,7 @@ static void bond_print_xstats_help(struct link_util *lu, FILE *f)
fprintf(f, "Usage: ... %s [ 802.3ad ] [ dev DEVICE ]\n", lu->id);
}
static void bond_print_3ad_stats(struct rtattr *lacpattr)
static void bond_print_3ad_stats(const struct rtattr *lacpattr)
{
struct rtattr *lacptb[BOND_3AD_STAT_MAX+1];
__u64 val;
@ -912,7 +913,6 @@ int bond_parse_xstats(struct link_util *lu, int argc, char **argv)
return 0;
}
struct link_util bond_link_util = {
.id = "bond",
.maxattr = IFLA_BOND_MAX,
@ -922,3 +922,54 @@ struct link_util bond_link_util = {
.parse_ifla_xstats = bond_parse_xstats,
.print_ifla_xstats = bond_print_xstats,
};
static const struct ipstats_stat_desc ipstats_stat_desc_bond_tmpl_lacp = {
.name = "802.3ad",
.kind = IPSTATS_STAT_DESC_KIND_LEAF,
.show = &ipstats_stat_desc_show_xstats,
.pack = &ipstats_stat_desc_pack_xstats,
};
static const struct ipstats_stat_desc_xstats
ipstats_stat_desc_xstats_bond_lacp = {
.desc = ipstats_stat_desc_bond_tmpl_lacp,
.xstats_at = IFLA_STATS_LINK_XSTATS,
.link_type_at = LINK_XSTATS_TYPE_BOND,
.inner_max = BOND_XSTATS_MAX,
.inner_at = BOND_XSTATS_3AD,
.show_cb = &bond_print_3ad_stats,
};
static const struct ipstats_stat_desc *
ipstats_stat_desc_xstats_bond_subs[] = {
&ipstats_stat_desc_xstats_bond_lacp.desc,
};
const struct ipstats_stat_desc ipstats_stat_desc_xstats_bond_group = {
.name = "bond",
.kind = IPSTATS_STAT_DESC_KIND_GROUP,
.subs = ipstats_stat_desc_xstats_bond_subs,
.nsubs = ARRAY_SIZE(ipstats_stat_desc_xstats_bond_subs),
};
static const struct ipstats_stat_desc_xstats
ipstats_stat_desc_xstats_slave_bond_lacp = {
.desc = ipstats_stat_desc_bond_tmpl_lacp,
.xstats_at = IFLA_STATS_LINK_XSTATS_SLAVE,
.link_type_at = LINK_XSTATS_TYPE_BOND,
.inner_max = BOND_XSTATS_MAX,
.inner_at = BOND_XSTATS_3AD,
.show_cb = &bond_print_3ad_stats,
};
static const struct ipstats_stat_desc *
ipstats_stat_desc_xstats_slave_bond_subs[] = {
&ipstats_stat_desc_xstats_slave_bond_lacp.desc,
};
const struct ipstats_stat_desc ipstats_stat_desc_xstats_slave_bond_group = {
.name = "bond",
.kind = IPSTATS_STAT_DESC_KIND_GROUP,
.subs = ipstats_stat_desc_xstats_slave_bond_subs,
.nsubs = ARRAY_SIZE(ipstats_stat_desc_xstats_slave_bond_subs),
};

View File

@ -714,11 +714,140 @@ static void bridge_print_xstats_help(struct link_util *lu, FILE *f)
fprintf(f, "Usage: ... %s [ igmp ] [ dev DEVICE ]\n", lu->id);
}
static void bridge_print_stats_mcast(const struct rtattr *attr)
{
struct br_mcast_stats *mstats;
mstats = RTA_DATA(attr);
open_json_object("multicast");
open_json_object("igmp_queries");
print_string(PRINT_FP, NULL,
"%-16s IGMP queries:\n", "");
print_string(PRINT_FP, NULL, "%-16s ", "");
print_u64(PRINT_ANY, "rx_v1", "RX: v1 %llu ",
mstats->igmp_v1queries[BR_MCAST_DIR_RX]);
print_u64(PRINT_ANY, "rx_v2", "v2 %llu ",
mstats->igmp_v2queries[BR_MCAST_DIR_RX]);
print_u64(PRINT_ANY, "rx_v3", "v3 %llu\n",
mstats->igmp_v3queries[BR_MCAST_DIR_RX]);
print_string(PRINT_FP, NULL, "%-16s ", "");
print_u64(PRINT_ANY, "tx_v1", "TX: v1 %llu ",
mstats->igmp_v1queries[BR_MCAST_DIR_TX]);
print_u64(PRINT_ANY, "tx_v2", "v2 %llu ",
mstats->igmp_v2queries[BR_MCAST_DIR_TX]);
print_u64(PRINT_ANY, "tx_v3", "v3 %llu\n",
mstats->igmp_v3queries[BR_MCAST_DIR_TX]);
close_json_object();
open_json_object("igmp_reports");
print_string(PRINT_FP, NULL,
"%-16s IGMP reports:\n", "");
print_string(PRINT_FP, NULL, "%-16s ", "");
print_u64(PRINT_ANY, "rx_v1", "RX: v1 %llu ",
mstats->igmp_v1reports[BR_MCAST_DIR_RX]);
print_u64(PRINT_ANY, "rx_v2", "v2 %llu ",
mstats->igmp_v2reports[BR_MCAST_DIR_RX]);
print_u64(PRINT_ANY, "rx_v3", "v3 %llu\n",
mstats->igmp_v3reports[BR_MCAST_DIR_RX]);
print_string(PRINT_FP, NULL, "%-16s ", "");
print_u64(PRINT_ANY, "tx_v1", "TX: v1 %llu ",
mstats->igmp_v1reports[BR_MCAST_DIR_TX]);
print_u64(PRINT_ANY, "tx_v2", "v2 %llu ",
mstats->igmp_v2reports[BR_MCAST_DIR_TX]);
print_u64(PRINT_ANY, "tx_v3", "v3 %llu\n",
mstats->igmp_v3reports[BR_MCAST_DIR_TX]);
close_json_object();
open_json_object("igmp_leaves");
print_string(PRINT_FP, NULL,
"%-16s IGMP leaves: ", "");
print_u64(PRINT_ANY, "rx", "RX: %llu ",
mstats->igmp_leaves[BR_MCAST_DIR_RX]);
print_u64(PRINT_ANY, "tx", "TX: %llu\n",
mstats->igmp_leaves[BR_MCAST_DIR_TX]);
close_json_object();
print_string(PRINT_FP, NULL,
"%-16s IGMP parse errors: ", "");
print_u64(PRINT_ANY, "igmp_parse_errors", "%llu\n",
mstats->igmp_parse_errors);
open_json_object("mld_queries");
print_string(PRINT_FP, NULL,
"%-16s MLD queries:\n", "");
print_string(PRINT_FP, NULL, "%-16s ", "");
print_u64(PRINT_ANY, "rx_v1", "RX: v1 %llu ",
mstats->mld_v1queries[BR_MCAST_DIR_RX]);
print_u64(PRINT_ANY, "rx_v2", "v2 %llu\n",
mstats->mld_v2queries[BR_MCAST_DIR_RX]);
print_string(PRINT_FP, NULL, "%-16s ", "");
print_u64(PRINT_ANY, "tx_v1", "TX: v1 %llu ",
mstats->mld_v1queries[BR_MCAST_DIR_TX]);
print_u64(PRINT_ANY, "tx_v2", "v2 %llu\n",
mstats->mld_v2queries[BR_MCAST_DIR_TX]);
close_json_object();
open_json_object("mld_reports");
print_string(PRINT_FP, NULL,
"%-16s MLD reports:\n", "");
print_string(PRINT_FP, NULL, "%-16s ", "");
print_u64(PRINT_ANY, "rx_v1", "RX: v1 %llu ",
mstats->mld_v1reports[BR_MCAST_DIR_RX]);
print_u64(PRINT_ANY, "rx_v2", "v2 %llu\n",
mstats->mld_v2reports[BR_MCAST_DIR_RX]);
print_string(PRINT_FP, NULL, "%-16s ", "");
print_u64(PRINT_ANY, "tx_v1", "TX: v1 %llu ",
mstats->mld_v1reports[BR_MCAST_DIR_TX]);
print_u64(PRINT_ANY, "tx_v2", "v2 %llu\n",
mstats->mld_v2reports[BR_MCAST_DIR_TX]);
close_json_object();
open_json_object("mld_leaves");
print_string(PRINT_FP, NULL,
"%-16s MLD leaves: ", "");
print_u64(PRINT_ANY, "rx", "RX: %llu ",
mstats->mld_leaves[BR_MCAST_DIR_RX]);
print_u64(PRINT_ANY, "tx", "TX: %llu\n",
mstats->mld_leaves[BR_MCAST_DIR_TX]);
close_json_object();
print_string(PRINT_FP, NULL,
"%-16s MLD parse errors: ", "");
print_u64(PRINT_ANY, "mld_parse_errors", "%llu\n",
mstats->mld_parse_errors);
close_json_object();
}
static void bridge_print_stats_stp(const struct rtattr *attr)
{
struct bridge_stp_xstats *sstats;
sstats = RTA_DATA(attr);
open_json_object("stp");
print_string(PRINT_FP, NULL,
"%-16s STP BPDU: ", "");
print_u64(PRINT_ANY, "rx_bpdu", "RX: %llu ",
sstats->rx_bpdu);
print_u64(PRINT_ANY, "tx_bpdu", "TX: %llu\n",
sstats->tx_bpdu);
print_string(PRINT_FP, NULL,
"%-16s STP TCN: ", "");
print_u64(PRINT_ANY, "rx_tcn", "RX: %llu ",
sstats->rx_tcn);
print_u64(PRINT_ANY, "tx_tcn", "TX: %llu\n",
sstats->tx_tcn);
print_string(PRINT_FP, NULL,
"%-16s STP Transitions: ", "");
print_u64(PRINT_ANY, "transition_blk", "Blocked: %llu ",
sstats->transition_blk);
print_u64(PRINT_ANY, "transition_fwd", "Forwarding: %llu\n",
sstats->transition_fwd);
close_json_object();
}
static void bridge_print_stats_attr(struct rtattr *attr, int ifindex)
{
struct rtattr *brtb[LINK_XSTATS_TYPE_MAX+1];
struct bridge_stp_xstats *sstats;
struct br_mcast_stats *mstats;
struct rtattr *i, *list;
const char *ifname = "";
int rem;
@ -738,127 +867,10 @@ static void bridge_print_stats_attr(struct rtattr *attr, int ifindex)
continue;
switch (i->rta_type) {
case BRIDGE_XSTATS_MCAST:
mstats = RTA_DATA(i);
open_json_object("multicast");
open_json_object("igmp_queries");
print_string(PRINT_FP, NULL,
"%-16s IGMP queries:\n", "");
print_string(PRINT_FP, NULL, "%-16s ", "");
print_u64(PRINT_ANY, "rx_v1", "RX: v1 %llu ",
mstats->igmp_v1queries[BR_MCAST_DIR_RX]);
print_u64(PRINT_ANY, "rx_v2", "v2 %llu ",
mstats->igmp_v2queries[BR_MCAST_DIR_RX]);
print_u64(PRINT_ANY, "rx_v3", "v3 %llu\n",
mstats->igmp_v3queries[BR_MCAST_DIR_RX]);
print_string(PRINT_FP, NULL, "%-16s ", "");
print_u64(PRINT_ANY, "tx_v1", "TX: v1 %llu ",
mstats->igmp_v1queries[BR_MCAST_DIR_TX]);
print_u64(PRINT_ANY, "tx_v2", "v2 %llu ",
mstats->igmp_v2queries[BR_MCAST_DIR_TX]);
print_u64(PRINT_ANY, "tx_v3", "v3 %llu\n",
mstats->igmp_v3queries[BR_MCAST_DIR_TX]);
close_json_object();
open_json_object("igmp_reports");
print_string(PRINT_FP, NULL,
"%-16s IGMP reports:\n", "");
print_string(PRINT_FP, NULL, "%-16s ", "");
print_u64(PRINT_ANY, "rx_v1", "RX: v1 %llu ",
mstats->igmp_v1reports[BR_MCAST_DIR_RX]);
print_u64(PRINT_ANY, "rx_v2", "v2 %llu ",
mstats->igmp_v2reports[BR_MCAST_DIR_RX]);
print_u64(PRINT_ANY, "rx_v3", "v3 %llu\n",
mstats->igmp_v3reports[BR_MCAST_DIR_RX]);
print_string(PRINT_FP, NULL, "%-16s ", "");
print_u64(PRINT_ANY, "tx_v1", "TX: v1 %llu ",
mstats->igmp_v1reports[BR_MCAST_DIR_TX]);
print_u64(PRINT_ANY, "tx_v2", "v2 %llu ",
mstats->igmp_v2reports[BR_MCAST_DIR_TX]);
print_u64(PRINT_ANY, "tx_v3", "v3 %llu\n",
mstats->igmp_v3reports[BR_MCAST_DIR_TX]);
close_json_object();
open_json_object("igmp_leaves");
print_string(PRINT_FP, NULL,
"%-16s IGMP leaves: ", "");
print_u64(PRINT_ANY, "rx", "RX: %llu ",
mstats->igmp_leaves[BR_MCAST_DIR_RX]);
print_u64(PRINT_ANY, "tx", "TX: %llu\n",
mstats->igmp_leaves[BR_MCAST_DIR_TX]);
close_json_object();
print_string(PRINT_FP, NULL,
"%-16s IGMP parse errors: ", "");
print_u64(PRINT_ANY, "igmp_parse_errors", "%llu\n",
mstats->igmp_parse_errors);
open_json_object("mld_queries");
print_string(PRINT_FP, NULL,
"%-16s MLD queries:\n", "");
print_string(PRINT_FP, NULL, "%-16s ", "");
print_u64(PRINT_ANY, "rx_v1", "RX: v1 %llu ",
mstats->mld_v1queries[BR_MCAST_DIR_RX]);
print_u64(PRINT_ANY, "rx_v2", "v2 %llu\n",
mstats->mld_v2queries[BR_MCAST_DIR_RX]);
print_string(PRINT_FP, NULL, "%-16s ", "");
print_u64(PRINT_ANY, "tx_v1", "TX: v1 %llu ",
mstats->mld_v1queries[BR_MCAST_DIR_TX]);
print_u64(PRINT_ANY, "tx_v2", "v2 %llu\n",
mstats->mld_v2queries[BR_MCAST_DIR_TX]);
close_json_object();
open_json_object("mld_reports");
print_string(PRINT_FP, NULL,
"%-16s MLD reports:\n", "");
print_string(PRINT_FP, NULL, "%-16s ", "");
print_u64(PRINT_ANY, "rx_v1", "RX: v1 %llu ",
mstats->mld_v1reports[BR_MCAST_DIR_RX]);
print_u64(PRINT_ANY, "rx_v2", "v2 %llu\n",
mstats->mld_v2reports[BR_MCAST_DIR_RX]);
print_string(PRINT_FP, NULL, "%-16s ", "");
print_u64(PRINT_ANY, "tx_v1", "TX: v1 %llu ",
mstats->mld_v1reports[BR_MCAST_DIR_TX]);
print_u64(PRINT_ANY, "tx_v2", "v2 %llu\n",
mstats->mld_v2reports[BR_MCAST_DIR_TX]);
close_json_object();
open_json_object("mld_leaves");
print_string(PRINT_FP, NULL,
"%-16s MLD leaves: ", "");
print_u64(PRINT_ANY, "rx", "RX: %llu ",
mstats->mld_leaves[BR_MCAST_DIR_RX]);
print_u64(PRINT_ANY, "tx", "TX: %llu\n",
mstats->mld_leaves[BR_MCAST_DIR_TX]);
close_json_object();
print_string(PRINT_FP, NULL,
"%-16s MLD parse errors: ", "");
print_u64(PRINT_ANY, "mld_parse_errors", "%llu\n",
mstats->mld_parse_errors);
close_json_object();
bridge_print_stats_mcast(i);
break;
case BRIDGE_XSTATS_STP:
sstats = RTA_DATA(i);
open_json_object("stp");
print_string(PRINT_FP, NULL,
"%-16s STP BPDU: ", "");
print_u64(PRINT_ANY, "rx_bpdu", "RX: %llu ",
sstats->rx_bpdu);
print_u64(PRINT_ANY, "tx_bpdu", "TX: %llu\n",
sstats->tx_bpdu);
print_string(PRINT_FP, NULL,
"%-16s STP TCN: ", "");
print_u64(PRINT_ANY, "rx_tcn", "RX: %llu ",
sstats->rx_tcn);
print_u64(PRINT_ANY, "tx_tcn", "TX: %llu\n",
sstats->tx_tcn);
print_string(PRINT_FP, NULL,
"%-16s STP Transitions: ", "");
print_u64(PRINT_ANY, "transition_blk", "Blocked: %llu ",
sstats->transition_blk);
print_u64(PRINT_ANY, "transition_fwd", "Forwarding: %llu\n",
sstats->transition_fwd);
close_json_object();
bridge_print_stats_stp(i);
break;
}
}
@ -924,3 +936,83 @@ struct link_util bridge_link_util = {
.parse_ifla_xstats = bridge_parse_xstats,
.print_ifla_xstats = bridge_print_xstats,
};
static const struct ipstats_stat_desc ipstats_stat_desc_bridge_tmpl_stp = {
.name = "stp",
.kind = IPSTATS_STAT_DESC_KIND_LEAF,
.show = &ipstats_stat_desc_show_xstats,
.pack = &ipstats_stat_desc_pack_xstats,
};
static const struct ipstats_stat_desc ipstats_stat_desc_bridge_tmpl_mcast = {
.name = "mcast",
.kind = IPSTATS_STAT_DESC_KIND_LEAF,
.show = &ipstats_stat_desc_show_xstats,
.pack = &ipstats_stat_desc_pack_xstats,
};
static const struct ipstats_stat_desc_xstats
ipstats_stat_desc_xstats_bridge_stp = {
.desc = ipstats_stat_desc_bridge_tmpl_stp,
.xstats_at = IFLA_STATS_LINK_XSTATS,
.link_type_at = LINK_XSTATS_TYPE_BRIDGE,
.inner_max = BRIDGE_XSTATS_MAX,
.inner_at = BRIDGE_XSTATS_STP,
.show_cb = &bridge_print_stats_stp,
};
static const struct ipstats_stat_desc_xstats
ipstats_stat_desc_xstats_bridge_mcast = {
.desc = ipstats_stat_desc_bridge_tmpl_mcast,
.xstats_at = IFLA_STATS_LINK_XSTATS,
.link_type_at = LINK_XSTATS_TYPE_BRIDGE,
.inner_max = BRIDGE_XSTATS_MAX,
.inner_at = BRIDGE_XSTATS_MCAST,
.show_cb = &bridge_print_stats_mcast,
};
static const struct ipstats_stat_desc *
ipstats_stat_desc_xstats_bridge_subs[] = {
&ipstats_stat_desc_xstats_bridge_stp.desc,
&ipstats_stat_desc_xstats_bridge_mcast.desc,
};
const struct ipstats_stat_desc ipstats_stat_desc_xstats_bridge_group = {
.name = "bridge",
.kind = IPSTATS_STAT_DESC_KIND_GROUP,
.subs = ipstats_stat_desc_xstats_bridge_subs,
.nsubs = ARRAY_SIZE(ipstats_stat_desc_xstats_bridge_subs),
};
static const struct ipstats_stat_desc_xstats
ipstats_stat_desc_xstats_slave_bridge_stp = {
.desc = ipstats_stat_desc_bridge_tmpl_stp,
.xstats_at = IFLA_STATS_LINK_XSTATS_SLAVE,
.link_type_at = LINK_XSTATS_TYPE_BRIDGE,
.inner_max = BRIDGE_XSTATS_MAX,
.inner_at = BRIDGE_XSTATS_STP,
.show_cb = &bridge_print_stats_stp,
};
static const struct ipstats_stat_desc_xstats
ipstats_stat_desc_xstats_slave_bridge_mcast = {
.desc = ipstats_stat_desc_bridge_tmpl_mcast,
.xstats_at = IFLA_STATS_LINK_XSTATS_SLAVE,
.link_type_at = LINK_XSTATS_TYPE_BRIDGE,
.inner_max = BRIDGE_XSTATS_MAX,
.inner_at = BRIDGE_XSTATS_MCAST,
.show_cb = &bridge_print_stats_mcast,
};
static const struct ipstats_stat_desc *
ipstats_stat_desc_xstats_slave_bridge_subs[] = {
&ipstats_stat_desc_xstats_slave_bridge_stp.desc,
&ipstats_stat_desc_xstats_slave_bridge_mcast.desc,
};
const struct ipstats_stat_desc ipstats_stat_desc_xstats_slave_bridge_group = {
.name = "bridge",
.kind = IPSTATS_STAT_DESC_KIND_GROUP,
.subs = ipstats_stat_desc_xstats_slave_bridge_subs,
.nsubs = ARRAY_SIZE(ipstats_stat_desc_xstats_slave_bridge_subs),
};

25
ip/iplink_virt_wifi.c Normal file
View File

@ -0,0 +1,25 @@
/* SPDX-License-Identifier: GPL-2.0
*
* iplink_virt_wifi.c A fake implementation of cfg80211_ops that can be tacked
* on to an ethernet net_device to make it appear as a
* wireless connection.
*
* Authors: Baligh Gasmi <gasmibal@gmail.com>
*/
#include <stdio.h>
#include <stdlib.h>
#include "utils.h"
#include "ip_common.h"
static void virt_wifi_print_help(struct link_util *lu,
int argc, char **argv, FILE *f)
{
fprintf(f, "Usage: ... virt_wifi \n");
}
struct link_util virt_wifi_link_util = {
.id = "virt_wifi",
.print_help = virt_wifi_print_help,
};

View File

@ -48,6 +48,7 @@ static void print_explain(FILE *f)
" [ [no]udp6zerocsumrx ]\n"
" [ [no]remcsumtx ] [ [no]remcsumrx ]\n"
" [ [no]external ] [ gbp ] [ gpe ]\n"
" [ [no]vnifilter ]\n"
"\n"
"Where: VNI := 0-16777215\n"
" ADDR := { IP_ADDRESS | any }\n"
@ -81,6 +82,7 @@ static int vxlan_parse_opt(struct link_util *lu, int argc, char **argv,
__u8 learning = 1;
__u16 dstport = 0;
__u8 metadata = 0;
__u8 vnifilter = 0;
__u64 attrs = 0;
bool set_op = (n->nlmsg_type == RTM_NEWLINK &&
!(n->nlmsg_flags & NLM_F_CREATE));
@ -330,6 +332,15 @@ static int vxlan_parse_opt(struct link_util *lu, int argc, char **argv,
} else if (!matches(*argv, "gpe")) {
check_duparg(&attrs, IFLA_VXLAN_GPE, *argv, *argv);
addattr_l(n, 1024, IFLA_VXLAN_GPE, NULL, 0);
} else if (!strcmp(*argv, "vnifilter")) {
check_duparg(&attrs, IFLA_VXLAN_VNIFILTER,
*argv, *argv);
addattr8(n, 1024, IFLA_VXLAN_VNIFILTER, 1);
vnifilter = 1;
} else if (!strcmp(*argv, "novnifilter")) {
check_duparg(&attrs, IFLA_VXLAN_VNIFILTER,
*argv, *argv);
addattr8(n, 1024, IFLA_VXLAN_VNIFILTER, 0);
} else if (matches(*argv, "help") == 0) {
explain();
return -1;
@ -341,12 +352,17 @@ static int vxlan_parse_opt(struct link_util *lu, int argc, char **argv,
argc--, argv++;
}
if (!metadata && vnifilter) {
fprintf(stderr, "vxlan: vnifilter is valid only when 'external' is set\n");
return -1;
}
if (metadata && VXLAN_ATTRSET(attrs, IFLA_VXLAN_ID)) {
fprintf(stderr, "vxlan: both 'external' and vni cannot be specified\n");
return -1;
}
if (!metadata && !VXLAN_ATTRSET(attrs, IFLA_VXLAN_ID) && !set_op) {
if (!metadata && !vnifilter && !VXLAN_ATTRSET(attrs, IFLA_VXLAN_ID) && !set_op) {
fprintf(stderr, "vxlan: missing virtual network identifier\n");
return -1;
}
@ -420,6 +436,11 @@ static void vxlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
print_bool(PRINT_ANY, "external", "external ", true);
}
if (tb[IFLA_VXLAN_VNIFILTER] &&
rta_getattr_u8(tb[IFLA_VXLAN_VNIFILTER])) {
print_bool(PRINT_ANY, "vnifilter", "vnifilter", true);
}
if (tb[IFLA_VXLAN_ID] &&
RTA_PAYLOAD(tb[IFLA_VXLAN_ID]) >= sizeof(__u32)) {
print_uint(PRINT_ANY, "id", "id %u ", rta_getattr_u32(tb[IFLA_VXLAN_ID]));

View File

@ -65,7 +65,8 @@ int iplink_ifla_xstats(int argc, char **argv)
else
filt_mask = IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_XSTATS);
if (rtnl_statsdump_req_filter(&rth, AF_UNSPEC, filt_mask) < 0) {
if (rtnl_statsdump_req_filter(&rth, AF_UNSPEC, filt_mask,
NULL, NULL) < 0) {
perror("Cannont send dump request");
return -1;
}

View File

@ -34,7 +34,7 @@ static void usage(void)
"Usage: ip monitor [ all | OBJECTS ] [ FILE ] [ label ] [ all-nsid ]\n"
" [ dev DEVICE ]\n"
"OBJECTS := address | link | mroute | neigh | netconf |\n"
" nexthop | nsid | prefix | route | rule\n"
" nexthop | nsid | prefix | route | rule | stats\n"
"FILE := file FILENAME\n");
exit(-1);
}
@ -158,6 +158,11 @@ static int accept_msg(struct rtnl_ctrl_data *ctrl,
print_nsid(n, arg);
return 0;
case RTM_NEWSTATS:
print_headers(fp, "[STATS]", ctrl);
ipstats_print(n, arg);
return 0;
case NLMSG_ERROR:
case NLMSG_NOOP:
case NLMSG_DONE:
@ -185,6 +190,7 @@ int do_ipmonitor(int argc, char **argv)
int lprefix = 0;
int lneigh = 0;
int lnetconf = 0;
int lstats = 0;
int lrule = 0;
int lnsid = 0;
int ifindex = 0;
@ -253,6 +259,9 @@ int do_ipmonitor(int argc, char **argv)
} else if (matches(*argv, "nexthop") == 0) {
lnexthop = 1;
groups = 0;
} else if (strcmp(*argv, "stats") == 0) {
lstats = 1;
groups = 0;
} else if (strcmp(*argv, "all") == 0) {
prefix_banner = 1;
} else if (matches(*argv, "all-nsid") == 0) {
@ -349,6 +358,11 @@ int do_ipmonitor(int argc, char **argv)
exit(1);
}
if (lstats && rtnl_add_nl_group(&rth, RTNLGRP_STATS) < 0) {
fprintf(stderr, "Failed to add stats group to list\n");
exit(1);
}
if (listen_all_nsid && rtnl_listen_all_nsid(&rth) < 0)
exit(1);

1356
ip/ipstats.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -619,12 +619,13 @@ int rtnl_fdb_linkdump_req_filter_fn(struct rtnl_handle *rth,
return send(rth->fd, &req, sizeof(req), 0);
}
int rtnl_statsdump_req_filter(struct rtnl_handle *rth, int fam, __u32 filt_mask)
int rtnl_statsdump_req_filter(struct rtnl_handle *rth, int fam,
__u32 filt_mask,
int (*filter_fn)(struct ipstats_req *req,
void *data),
void *filter_data)
{
struct {
struct nlmsghdr nlh;
struct if_stats_msg ifsm;
} req;
struct ipstats_req req;
memset(&req, 0, sizeof(req));
req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct if_stats_msg));
@ -635,6 +636,14 @@ int rtnl_statsdump_req_filter(struct rtnl_handle *rth, int fam, __u32 filt_mask)
req.ifsm.family = fam;
req.ifsm.filter_mask = filt_mask;
if (filter_fn) {
int err;
err = filter_fn(&req, filter_data);
if (err)
return err;
}
return send(rth->fd, &req, sizeof(req), 0);
}
@ -1600,3 +1609,23 @@ void nl_print_policy(const struct rtattr *attr, FILE *fp)
}
}
}
int rtnl_tunneldump_req(struct rtnl_handle *rth, int family, int ifindex,
__u8 flags)
{
struct {
struct nlmsghdr nlh;
struct tunnel_msg tmsg;
char buf[256];
} req = {
.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tunnel_msg)),
.nlh.nlmsg_type = RTM_GETTUNNEL,
.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
.nlh.nlmsg_seq = rth->dump = ++rth->seq,
.tmsg.family = family,
.tmsg.flags = flags,
.tmsg.ifindex = ifindex,
};
return send(rth->fd, &req, sizeof(req), 0);
}

View File

@ -13,7 +13,7 @@ bridge \- show / manipulate bridge addresses and devices
.ti -8
.IR OBJECT " := { "
.BR link " | " fdb " | " mdb " | " vlan " | " monitor " }"
.BR link " | " fdb " | " mdb " | " vlan " | " vni " | " monitor " }"
.sp
.ti -8
@ -196,6 +196,25 @@ bridge \- show / manipulate bridge addresses and devices
.B vid
.IR VID " ]"
.ti -8
.BR "bridge vlan" " show " [ "
.B dev
.IR DEV " ]"
.ti -8
.BR "bridge vni" " { " add " | " del " } "
.B dev
.I DEV
.B vni
.IR VNI " [ { "
.B group | remote "} "
.IR IPADDR " ] "
.ti -8
.BR "bridge vni" " show " [ "
.B dev
.IR DEV " ]"
.ti -8
.BR "bridge monitor" " [ " all " | " neigh " | " link " | " mdb " | " vlan " ]"
@ -303,6 +322,10 @@ the output.
.B vlan
- VLAN filter list.
.TP
.B vni
- VNI filter list.
.SS
.I COMMAND
@ -1084,6 +1107,58 @@ all bridge interfaces.
the VLAN ID only whose global options should be listed. Default is to list
all vlans.
.SH bridge vni - VNI filter list
.B vni
objects contain known VNI IDs for a dst metadata vxlan link.
.P
The corresponding commands display vni filter entries, add new entries,
and delete old ones.
.SS bridge vni add - add a new vni filter entry
This command creates a new vni filter entry.
.TP
.BI dev " NAME"
the interface with which this vni is associated.
.TP
.BI vni " VNI"
the VNI ID that identifies the vni.
.TP
.BI remote " IPADDR"
specifies the unicast destination IP address to use in outgoing packets
when the destination link layer address is not known in the VXLAN device
forwarding database. This parameter cannot be specified with the group.
.TP
.BI group " IPADDR"
specifies the multicast IP address to join for this VNI
.SS bridge vni del - delete a new vni filter entry
This command removes an existing vni filter entry.
.PP
The arguments are the same as with
.BR "bridge vni add".
.SS bridge vni show - list vni filtering configuration.
This command displays the current vni filter table.
.PP
With the
.B -statistics
option, the command displays per-vni traffic statistics.
.TP
.BI dev " NAME"
shows vni filtering table associated with the vxlan device
.SH bridge monitor - state monitoring
The

View File

@ -63,6 +63,10 @@ Switches to the specified network namespace.
.BR "\-i", " --iec"
Print human readable rates in IEC units (e.g. 1Ki = 1024).
.TP
.BR "\-x", " --hex"
Print dump numbers in hexadecimal format.
.SS
.I OBJECT

View File

@ -209,42 +209,43 @@ ip-link \- network device configuration
.ti -8
.IR TYPE " := [ "
.BR amt " | "
.BR bridge " | "
.BR bareudp " |"
.BR bond " | "
.BR bridge " | "
.BR can " | "
.BR dummy " | "
.BR hsr " | "
.BR ifb " | "
.BR ipoib " |"
.BR macvlan " | "
.BR macvtap " | "
.BR vcan " | "
.BR vxcan " | "
.BR veth " | "
.BR vlan " | "
.BR vxlan " |"
.BR ip6tnl " |"
.BR ipip " |"
.BR sit " |"
.BR erspan " |"
.BR geneve " |"
.BR gre " |"
.BR gretap " |"
.BR erspan " |"
.BR gtp " |"
.BR hsr " | "
.BR ifb " | "
.BR ip6erspan " |"
.BR ip6gre " |"
.BR ip6gretap " |"
.BR ip6erspan " |"
.BR vti " |"
.BR nlmon " |"
.BR ip6tnl " |"
.BR ipip " |"
.BR ipoib " |"
.BR ipvlan " |"
.BR ipvtap " |"
.BR lowpan " |"
.BR geneve " |"
.BR bareudp " |"
.BR vrf " |"
.BR macsec " |"
.BR macvlan " | "
.BR macvtap " | "
.BR netdevsim " |"
.BR nlmon " |"
.BR rmnet " |"
.BR xfrm " |"
.BR gtp " ]"
.BR sit " |"
.BR vcan " | "
.BR veth " | "
.BR virt_wifi " |"
.BR vlan " | "
.BR vrf " |"
.BR vti " |"
.BR vxcan " | "
.BR vxlan " |"
.BR xfrm " ]"
.ti -8
.IR ETYPE " := [ " TYPE " |"
@ -289,62 +290,46 @@ specifies the type of the new device.
Link types:
.in +8
.B bridge
- Ethernet Bridge device
.BR amt
- Automatic Multicast Tunneling (AMT)
.sp
.BR bareudp
- Bare UDP L3 encapsulation support
.sp
.B bond
- Bonding device
.B bridge
- Ethernet Bridge device
.sp
.B can
- Controller Area Network
.sp
.B dummy
- Dummy network interface
.sp
.BR erspan
- Encapsulated Remote SPAN over GRE and IPv4
.sp
.B geneve
- GEneric NEtwork Virtualization Encapsulation
.sp
.B gre
- Virtual tunnel interface GRE over IPv4
.sp
.BR gretap
- Virtual L2 tunnel interface GRE over IPv4
.sp
.BR gtp
- GPRS Tunneling Protocol
.sp
.B hsr
- High-availability Seamless Redundancy device
.sp
.B ifb
- Intermediate Functional Block device
.sp
.B ipoib
- IP over Infiniband device
.sp
.B macvlan
- Virtual interface base on link layer address (MAC)
.sp
.B macvtap
- Virtual interface based on link layer address (MAC) and TAP.
.sp
.B vcan
- Virtual Controller Area Network interface
.sp
.B vxcan
- Virtual Controller Area Network tunnel interface
.sp
.B veth
- Virtual ethernet interface
.sp
.BR vlan
- 802.1q tagged virtual LAN interface
.sp
.BR vxlan
- Virtual eXtended LAN
.sp
.BR ip6tnl
- Virtual tunnel interface IPv4|IPv6 over IPv6
.sp
.BR ipip
- Virtual tunnel interface IPv4 over IPv4
.sp
.BR sit
- Virtual tunnel interface IPv6 over IPv4
.sp
.BR gre
- Virtual tunnel interface GRE over IPv4
.sp
.BR gretap
- Virtual L2 tunnel interface GRE over IPv4
.sp
.BR erspan
- Encapsulated Remote SPAN over GRE and IPv4
.BR ip6erspan
- Encapsulated Remote SPAN over GRE and IPv6
.sp
.BR ip6gre
- Virtual tunnel interface GRE over IPv6
@ -352,14 +337,14 @@ Link types:
.BR ip6gretap
- Virtual L2 tunnel interface GRE over IPv6
.sp
.BR ip6erspan
- Encapsulated Remote SPAN over GRE and IPv6
.BR ip6tnl
- Virtual tunnel interface IPv4|IPv6 over IPv6
.sp
.BR vti
- Virtual tunnel interface
.BR ipip
- Virtual tunnel interface IPv4 over IPv4
.sp
.BR nlmon
- Netlink monitoring device
.B ipoib
- IP over Infiniband device
.sp
.BR ipvlan
- Interface for L3 (IPv6/IPv4) based VLANs
@ -370,32 +355,54 @@ Link types:
.BR lowpan
- Interface for 6LoWPAN (IPv6) over IEEE 802.15.4 / Bluetooth
.sp
.BR geneve
- GEneric NEtwork Virtualization Encapsulation
.sp
.BR bareudp
- Bare UDP L3 encapsulation support
.sp
.BR amt
- Automatic Multicast Tunneling (AMT)
.sp
.BR macsec
- Interface for IEEE 802.1AE MAC Security (MACsec)
.sp
.BR vrf
- Interface for L3 VRF domains
.B macvlan
- Virtual interface base on link layer address (MAC)
.sp
.B macvtap
- Virtual interface based on link layer address (MAC) and TAP.
.sp
.BR netdevsim
- Interface for netdev API tests
.sp
.BR nlmon
- Netlink monitoring device
.sp
.BR rmnet
- Qualcomm rmnet device
.sp
.BR sit
- Virtual tunnel interface IPv6 over IPv4
.sp
.B vcan
- Virtual Controller Area Network interface
.sp
.B veth
- Virtual ethernet interface
.sp
.BR virt_wifi
- rtnetlink wifi simulation device
.sp
.BR vlan
- 802.1q tagged virtual LAN interface
.sp
.BR vrf
- Interface for L3 VRF domains
.sp
.BR vti
- Virtual tunnel interface
.sp
.B vxcan
- Virtual Controller Area Network tunnel interface
.sp
.BR vxlan
- Virtual eXtended LAN
.sp
.BR xfrm
- Virtual xfrm interface
.sp
.BR gtp
- GPRS Tunneling Protocol
.in -8
.TP
@ -594,6 +601,8 @@ the following additional arguments are supported:
.B gbp
] [
.B gpe
] [
.RB [ no ] vnifilter
]
.in +8
@ -705,6 +714,13 @@ are entered into the VXLAN device forwarding database.
.RB "(e.g. " "ip route encap" )
or the internal FDB should be used.
.sp
.RB [ no ] vnifilter
- specifies whether the vxlan device is capable of vni filtering. Only works with a vxlan
device with external flag set. once enabled, bridge vni command is used to manage the
vni filtering table on the device. The device can only receive packets with vni's configured
in the vni filtering table.
.sp
.B gbp
- enables the Group Policy extension (VXLAN-GBP).

View File

@ -55,7 +55,7 @@ command is the first in the command line and then the object list follows:
is the list of object types that we want to monitor.
It may contain
.BR link ", " address ", " route ", " mroute ", " prefix ", "
.BR neigh ", " netconf ", " rule ", " nsid " and " nexthop "."
.BR neigh ", " netconf ", " rule ", " stats ", " nsid " and " nexthop "."
If no
.B file
argument is given,

208
man/man8/ip-stats.8 Normal file
View File

@ -0,0 +1,208 @@
.TH IP\-STATS 8 "16 Mar 2022" "iproute2" "Linux"
.SH NAME
ip-stats \- manage and show interface statistics
.SH SYNOPSIS
.sp
.ad l
.in +8
.ti -8
.B ip
.B stats
.RI " { " COMMAND " | "
.BR help " }"
.sp
.ti -8
.BR "ip stats show"
.RB "[ " dev
.IR DEV " ] "
.RB "[ " group
.IR GROUP " [ "
.BI subgroup " SUBGROUP"
.RB " [ " suite
.IR " SUITE" " ] ... ] ... ] ..."
.ti -8
.BR "ip stats set"
.BI dev " DEV"
.BR l3_stats " { "
.BR on " | " off " }"
.SH DESCRIPTION
.TP
.B ip stats set
is used for toggling whether a certain HW statistics suite is collected on
a given netdevice. The following statistics suites are supported:
.in 21
.ti 14
.B l3_stats
L3 stats reflect traffic that takes place in a HW device on an object that
corresponds to the given software netdevice.
.TP
.B ip stats show
is used for showing stats on a given netdevice, or dumping statistics
across all netdevices. By default, all stats are requested. It is possible
to filter which stats are requested by using the
.B group
and
.B subgroup
keywords.
It is possible to specify several groups, or several subgroups for one
group. When no subgroups are given for a group, all the subgroups are
requested.
The following groups are recognized:
.in 21
.ti 14
.B group link
- Link statistics. The same suite that "ip -s link show" shows.
.ti 14
.B group offload
- A group that contains a number of HW-oriented statistics. See below for
individual subgroups within this group.
.ti 14
.B group xstats
- Extended statistics. A subgroup identifies the type of netdevice to show the
statistics for.
.ti 14
.B group xstats_slave
- Extended statistics for the slave of a netdevice of a given type. A subgroup
identifies the type of master netdevice.
.ti 14
.B group afstats
- A group for address-family specific netdevice statistics.
.TQ
.BR "group offload " subgroups:
.in 21
.ti 14
.B subgroup cpu_hit
- The
.B cpu_hit
statistics suite is useful on hardware netdevices. The
.B link
statistics on these devices reflect both the hardware- and
software-datapath traffic. The
.B cpu_hit
statistics then only reflect software-datapath traffic.
.ti 14
.B subgroup hw_stats_info
- This suite does not include traffic statistics, but rather communicates
the state of other statistics. Through this subgroup, it is possible to
discover whether a given statistic was enabled, and when it was, whether
any device driver actually configured its device to collect these
statistics. For example,
.B l3_stats
was enabled in the following case, but no driver has installed it:
# ip stats show dev swp1 group offload subgroup hw_stats_info
.br
56: swp1: group offload subgroup hw_stats_info
.br
l3_stats on used off
After an L3 address is added to the netdevice, the counter will be
installed:
# ip addr add dev swp1 192.0.2.1/28
.br
# ip stats show dev swp1 group offload subgroup hw_stats_info
.br
56: swp1: group offload subgroup hw_stats_info
.br
l3_stats on used on
.ti 14
.B subgroup l3_stats
- These statistics reflect L3 traffic that takes place in HW on an object
that corresponds to the netdevice. Note that this suite is disabled by
default and needs to be first enabled through
.B ip stats set\fR.
For example:
# ip stats show dev swp2.200 group offload subgroup l3_stats
.br
112: swp2.200: group offload subgroup l3_stats on used on
.br
RX: bytes packets errors dropped mcast
.br
8900 72 2 0 3
.br
TX: bytes packets errors dropped
.br
7176 58 0 0
Note how the l3_stats_info for the selected group is also part of the dump.
.TQ
.BR "group xstats " and " group xstats_slave " subgroups:
.in 21
.ti 14
.B subgroup bridge \fR[\fB suite stp \fR] [\fB suite mcast \fR]
- Statistics for STP and, respectively, IGMP / MLD (under the keyword
\fBmcast\fR) traffic on bridges and their slaves.
.ti 14
.B subgroup bond \fR[\fB suite 802.3ad \fR]
- Statistics for LACP traffic on bond devices and their slaves.
.TQ
.BR "group afstats " subgroups:
.in 21
.ti 14
.B subgroup mpls
- Statistics for MPLS traffic seen on the netdevice. For example:
# ip stats show dev veth01 group afstats subgroup mpls
.br
3: veth01: group afstats subgroup mpls
.br
RX: bytes packets errors dropped noroute
.br
0 0 0 0 0
.br
TX: bytes packets errors dropped
.br
216 2 0 0
.SH EXAMPLES
.PP
# ip stats set dev swp1 l3_stats on
.RS
Enables collection of L3 HW statistics on swp1.
.RE
.PP
# ip stats show group offload
.RS
Shows all offload statistics on all netdevices.
.RE
.PP
# ip stats show dev swp1 group link
.RS
Shows link statistics on the given netdevice.
.RE
.SH SEE ALSO
.br
.BR ip (8),
.BR ip-link (8),
.SH AUTHOR
Manpage by Petr Machata.

View File

@ -22,7 +22,7 @@ ip \- show / manipulate routing, network devices, interfaces and tunnels
.BR link " | " address " | " addrlabel " | " route " | " rule " | " neigh " | "\
ntable " | " tunnel " | " tuntap " | " maddress " | " mroute " | " mrule " | "\
monitor " | " xfrm " | " netns " | " l2tp " | " tcp_metrics " | " token " | "\
macsec " | " vrf " | " mptcp " | " ioam " }"
macsec " | " vrf " | " mptcp " | " ioam " | " stats " }"
.sp
.ti -8
@ -302,6 +302,10 @@ readability.
.B rule
- rule in routing policy database.
.TP
.B stats
- manage and show interface statistics.
.TP
.B tcp_metrics/tcpmetrics
- manage TCP Metrics
@ -419,6 +423,7 @@ was written by Alexey N. Kuznetsov and added in Linux 2.2.
.BR ip-ntable (8),
.BR ip-route (8),
.BR ip-rule (8),
.BR ip-stats (8)
.BR ip-tcp_metrics (8),
.BR ip-token (8),
.BR ip-tunnel (8),

View File

@ -164,6 +164,11 @@ provided in LLADDR format, in which case it is a bitwise mask, or as a
number of high bits to match. If the mask is missing then a match on all
bits is assumed.
.TP
.BI num_of_vlans " NUM"
Match on the number of vlan tags in the packet.
.I NUM
can be 0 or small positive integer. Typically in 0-4 range.
.TP
.BI vlan_id " VID"
Match on vlan tag id.
.I VID

View File

@ -202,7 +202,7 @@ static void load_info(void)
ll_init_map(&rth);
filter_mask = IFLA_STATS_FILTER_BIT(filter_type);
if (rtnl_statsdump_req_filter(&rth, AF_UNSPEC,
filter_mask) < 0) {
filter_mask, NULL, NULL) < 0) {
perror("Cannot send dump request");
exit(1);
}

View File

@ -48,6 +48,7 @@ static void explain(void)
"\n"
"Where: MATCH-LIST := [ MATCH-LIST ] MATCH\n"
" MATCH := { indev DEV-NAME |\n"
" num_of_vlans VLANS_COUNT |\n"
" vlan_id VID |\n"
" vlan_prio PRIORITY |\n"
" vlan_ethtype [ ipv4 | ipv6 | ETH-TYPE ] |\n"
@ -159,21 +160,23 @@ err:
return err;
}
static bool eth_type_vlan(__be16 ethertype)
static bool eth_type_vlan(__be16 ethertype, bool good_num_of_vlans)
{
return ethertype == htons(ETH_P_8021Q) ||
ethertype == htons(ETH_P_8021AD);
ethertype == htons(ETH_P_8021AD) ||
good_num_of_vlans;
}
static int flower_parse_vlan_eth_type(char *str, __be16 eth_type, int type,
__be16 *p_vlan_eth_type,
struct nlmsghdr *n)
struct nlmsghdr *n, bool good_num_of_vlans)
{
__be16 vlan_eth_type;
if (!eth_type_vlan(eth_type)) {
fprintf(stderr, "Can't set \"%s\" if ethertype isn't 802.1Q or 802.1AD\n",
type == TCA_FLOWER_KEY_VLAN_ETH_TYPE ? "vlan_ethtype" : "cvlan_ethtype");
if (!eth_type_vlan(eth_type, good_num_of_vlans)) {
fprintf(stderr, "Can't set \"%s\" if ethertype isn't 802.1Q or 802.1AD and num_of_vlans %s\n",
type == TCA_FLOWER_KEY_VLAN_ETH_TYPE ? "vlan_ethtype" : "cvlan_ethtype",
type == TCA_FLOWER_KEY_VLAN_ETH_TYPE ? "is 0" : "less than 2");
return -1;
}
@ -1424,6 +1427,7 @@ static int flower_parse_opt(struct filter_util *qu, char *handle,
__be16 tc_proto = TC_H_MIN(t->tcm_info);
__be16 eth_type = tc_proto;
__be16 vlan_ethtype = 0;
__u8 num_of_vlans = 0;
__u8 ip_proto = 0xff;
__u32 flags = 0;
__u32 mtf = 0;
@ -1525,12 +1529,22 @@ static int flower_parse_opt(struct filter_util *qu, char *handle,
if (check_ifname(*argv))
invarg("\"indev\" not a valid ifname", *argv);
addattrstrz(n, MAX_MSG, TCA_FLOWER_INDEV, *argv);
} else if (strcmp(*argv, "num_of_vlans") == 0) {
NEXT_ARG();
ret = get_u8(&num_of_vlans, *argv, 10);
if (ret < 0) {
fprintf(stderr, "Illegal \"num_of_vlans\"\n");
return -1;
}
addattr8(n, MAX_MSG,
TCA_FLOWER_KEY_NUM_OF_VLANS, num_of_vlans);
} else if (matches(*argv, "vlan_id") == 0) {
__u16 vid;
NEXT_ARG();
if (!eth_type_vlan(tc_proto)) {
fprintf(stderr, "Can't set \"vlan_id\" if ethertype isn't 802.1Q or 802.1AD\n");
if (!eth_type_vlan(tc_proto, num_of_vlans > 0)) {
fprintf(stderr, "Can't set \"vlan_id\" if ethertype isn't 802.1Q or 802.1AD"
" and num_of_vlans is 0\n");
return -1;
}
ret = get_u16(&vid, *argv, 10);
@ -1543,8 +1557,9 @@ static int flower_parse_opt(struct filter_util *qu, char *handle,
__u8 vlan_prio;
NEXT_ARG();
if (!eth_type_vlan(tc_proto)) {
fprintf(stderr, "Can't set \"vlan_prio\" if ethertype isn't 802.1Q or 802.1AD\n");
if (!eth_type_vlan(tc_proto, num_of_vlans > 0)) {
fprintf(stderr, "Can't set \"vlan_prio\" if ethertype isn't 802.1Q or 802.1AD"
" and num_of_vlans is 0\n");
return -1;
}
ret = get_u8(&vlan_prio, *argv, 10);
@ -1558,7 +1573,7 @@ static int flower_parse_opt(struct filter_util *qu, char *handle,
NEXT_ARG();
ret = flower_parse_vlan_eth_type(*argv, eth_type,
TCA_FLOWER_KEY_VLAN_ETH_TYPE,
&vlan_ethtype, n);
&vlan_ethtype, n, num_of_vlans > 0);
if (ret < 0)
return -1;
/* get new ethtype for later parsing */
@ -1567,8 +1582,9 @@ static int flower_parse_opt(struct filter_util *qu, char *handle,
__u16 vid;
NEXT_ARG();
if (!eth_type_vlan(vlan_ethtype)) {
fprintf(stderr, "Can't set \"cvlan_id\" if inner vlan ethertype isn't 802.1Q or 802.1AD\n");
if (!eth_type_vlan(vlan_ethtype, num_of_vlans > 1)) {
fprintf(stderr, "Can't set \"cvlan_id\" if inner vlan ethertype isn't 802.1Q or 802.1AD"
" and num_of_vlans is less than 2\n");
return -1;
}
ret = get_u16(&vid, *argv, 10);
@ -1581,8 +1597,9 @@ static int flower_parse_opt(struct filter_util *qu, char *handle,
__u8 cvlan_prio;
NEXT_ARG();
if (!eth_type_vlan(vlan_ethtype)) {
fprintf(stderr, "Can't set \"cvlan_prio\" if inner vlan ethertype isn't 802.1Q or 802.1AD\n");
if (!eth_type_vlan(vlan_ethtype, num_of_vlans > 1)) {
fprintf(stderr, "Can't set \"cvlan_prio\" if inner vlan ethertype isn't 802.1Q or 802.1AD"
" and num_of_vlans is less than 2\n");
return -1;
}
ret = get_u8(&cvlan_prio, *argv, 10);
@ -1597,7 +1614,7 @@ static int flower_parse_opt(struct filter_util *qu, char *handle,
/* get new ethtype for later parsing */
ret = flower_parse_vlan_eth_type(*argv, vlan_ethtype,
TCA_FLOWER_KEY_CVLAN_ETH_TYPE,
&eth_type, n);
&eth_type, n, num_of_vlans > 1);
if (ret < 0)
return -1;
} else if (matches(*argv, "mpls") == 0) {
@ -2694,6 +2711,14 @@ static int flower_print_opt(struct filter_util *qu, FILE *f,
open_json_object("keys");
if (tb[TCA_FLOWER_KEY_NUM_OF_VLANS]) {
struct rtattr *attr = tb[TCA_FLOWER_KEY_NUM_OF_VLANS];
print_nl();
print_uint(PRINT_ANY, "num_of_vlans", " num_of_vlans %d",
rta_getattr_u8(attr));
}
if (tb[TCA_FLOWER_KEY_VLAN_ID]) {
struct rtattr *attr = tb[TCA_FLOWER_KEY_VLAN_ID];