Merge git://git.kernel.org/pub/scm/network/iproute2/iproute2-next into main
This commit is contained in:
commit
2639bdc176
2
Makefile
2
Makefile
|
|
@ -55,7 +55,7 @@ WFLAGS += -Wmissing-declarations -Wold-style-definition -Wformat=2
|
||||||
CFLAGS := $(WFLAGS) $(CCOPTS) -I../include -I../include/uapi $(DEFINES) $(CFLAGS)
|
CFLAGS := $(WFLAGS) $(CCOPTS) -I../include -I../include/uapi $(DEFINES) $(CFLAGS)
|
||||||
YACCFLAGS = -d -t -v
|
YACCFLAGS = -d -t -v
|
||||||
|
|
||||||
SUBDIRS=lib ip tc bridge misc netem genl tipc devlink rdma man
|
SUBDIRS=lib ip tc bridge misc netem genl tipc devlink rdma dcb man
|
||||||
|
|
||||||
LIBNETLINK=../lib/libutil.a ../lib/libnetlink.a
|
LIBNETLINK=../lib/libutil.a ../lib/libnetlink.a
|
||||||
LDLIBS += $(LIBNETLINK)
|
LDLIBS += $(LIBNETLINK)
|
||||||
|
|
|
||||||
|
|
@ -77,20 +77,14 @@ static int do_cmd(const char *argv0, int argc, char **argv)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int br_batch_cmd(int argc, char *argv[], void *data)
|
||||||
|
{
|
||||||
|
return do_cmd(argv[0], argc, argv);
|
||||||
|
}
|
||||||
|
|
||||||
static int batch(const char *name)
|
static int batch(const char *name)
|
||||||
{
|
{
|
||||||
char *line = NULL;
|
int ret;
|
||||||
size_t len = 0;
|
|
||||||
int ret = EXIT_SUCCESS;
|
|
||||||
|
|
||||||
if (name && strcmp(name, "-") != 0) {
|
|
||||||
if (freopen(name, "r", stdin) == NULL) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"Cannot open file \"%s\" for reading: %s\n",
|
|
||||||
name, strerror(errno));
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rtnl_open(&rth, 0) < 0) {
|
if (rtnl_open(&rth, 0) < 0) {
|
||||||
fprintf(stderr, "Cannot open rtnetlink\n");
|
fprintf(stderr, "Cannot open rtnetlink\n");
|
||||||
|
|
@ -99,25 +93,7 @@ static int batch(const char *name)
|
||||||
|
|
||||||
rtnl_set_strict_dump(&rth);
|
rtnl_set_strict_dump(&rth);
|
||||||
|
|
||||||
cmdlineno = 0;
|
ret = do_batch(name, force, br_batch_cmd, NULL);
|
||||||
while (getcmdline(&line, &len, stdin) != -1) {
|
|
||||||
char *largv[100];
|
|
||||||
int largc;
|
|
||||||
|
|
||||||
largc = makeargs(line, largv, 100);
|
|
||||||
if (largc == 0)
|
|
||||||
continue; /* blank line */
|
|
||||||
|
|
||||||
if (do_cmd(largv[0], largc, largv)) {
|
|
||||||
fprintf(stderr, "Command failed %s:%d\n",
|
|
||||||
name, cmdlineno);
|
|
||||||
ret = EXIT_FAILURE;
|
|
||||||
if (!force)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (line)
|
|
||||||
free(line);
|
|
||||||
|
|
||||||
rtnl_close(&rth);
|
rtnl_close(&rth);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
||||||
135
bridge/link.c
135
bridge/link.c
|
|
@ -78,14 +78,6 @@ static void print_portstate(__u8 state)
|
||||||
"state (%d) ", state);
|
"state (%d) ", state);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_onoff(FILE *fp, const char *flag, __u8 val)
|
|
||||||
{
|
|
||||||
if (is_json_context())
|
|
||||||
print_bool(PRINT_JSON, flag, NULL, val);
|
|
||||||
else
|
|
||||||
fprintf(fp, "%s %s ", flag, val ? "on" : "off");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void print_hwmode(__u16 mode)
|
static void print_hwmode(__u16 mode)
|
||||||
{
|
{
|
||||||
if (mode >= ARRAY_SIZE(hw_mode))
|
if (mode >= ARRAY_SIZE(hw_mode))
|
||||||
|
|
@ -123,38 +115,38 @@ static void print_protinfo(FILE *fp, struct rtattr *attr)
|
||||||
fprintf(fp, "%s ", _SL_);
|
fprintf(fp, "%s ", _SL_);
|
||||||
|
|
||||||
if (prtb[IFLA_BRPORT_MODE])
|
if (prtb[IFLA_BRPORT_MODE])
|
||||||
print_onoff(fp, "hairpin",
|
print_on_off(PRINT_ANY, "hairpin", "hairpin %s ",
|
||||||
rta_getattr_u8(prtb[IFLA_BRPORT_MODE]));
|
rta_getattr_u8(prtb[IFLA_BRPORT_MODE]));
|
||||||
if (prtb[IFLA_BRPORT_GUARD])
|
if (prtb[IFLA_BRPORT_GUARD])
|
||||||
print_onoff(fp, "guard",
|
print_on_off(PRINT_ANY, "guard", "guard %s ",
|
||||||
rta_getattr_u8(prtb[IFLA_BRPORT_GUARD]));
|
rta_getattr_u8(prtb[IFLA_BRPORT_GUARD]));
|
||||||
if (prtb[IFLA_BRPORT_PROTECT])
|
if (prtb[IFLA_BRPORT_PROTECT])
|
||||||
print_onoff(fp, "root_block",
|
print_on_off(PRINT_ANY, "root_block", "root_block %s ",
|
||||||
rta_getattr_u8(prtb[IFLA_BRPORT_PROTECT]));
|
rta_getattr_u8(prtb[IFLA_BRPORT_PROTECT]));
|
||||||
if (prtb[IFLA_BRPORT_FAST_LEAVE])
|
if (prtb[IFLA_BRPORT_FAST_LEAVE])
|
||||||
print_onoff(fp, "fastleave",
|
print_on_off(PRINT_ANY, "fastleave", "fastleave %s ",
|
||||||
rta_getattr_u8(prtb[IFLA_BRPORT_FAST_LEAVE]));
|
rta_getattr_u8(prtb[IFLA_BRPORT_FAST_LEAVE]));
|
||||||
if (prtb[IFLA_BRPORT_LEARNING])
|
if (prtb[IFLA_BRPORT_LEARNING])
|
||||||
print_onoff(fp, "learning",
|
print_on_off(PRINT_ANY, "learning", "learning %s ",
|
||||||
rta_getattr_u8(prtb[IFLA_BRPORT_LEARNING]));
|
rta_getattr_u8(prtb[IFLA_BRPORT_LEARNING]));
|
||||||
if (prtb[IFLA_BRPORT_LEARNING_SYNC])
|
if (prtb[IFLA_BRPORT_LEARNING_SYNC])
|
||||||
print_onoff(fp, "learning_sync",
|
print_on_off(PRINT_ANY, "learning_sync", "learning_sync %s ",
|
||||||
rta_getattr_u8(prtb[IFLA_BRPORT_LEARNING_SYNC]));
|
rta_getattr_u8(prtb[IFLA_BRPORT_LEARNING_SYNC]));
|
||||||
if (prtb[IFLA_BRPORT_UNICAST_FLOOD])
|
if (prtb[IFLA_BRPORT_UNICAST_FLOOD])
|
||||||
print_onoff(fp, "flood",
|
print_on_off(PRINT_ANY, "flood", "flood %s ",
|
||||||
rta_getattr_u8(prtb[IFLA_BRPORT_UNICAST_FLOOD]));
|
rta_getattr_u8(prtb[IFLA_BRPORT_UNICAST_FLOOD]));
|
||||||
if (prtb[IFLA_BRPORT_MCAST_FLOOD])
|
if (prtb[IFLA_BRPORT_MCAST_FLOOD])
|
||||||
print_onoff(fp, "mcast_flood",
|
print_on_off(PRINT_ANY, "mcast_flood", "mcast_flood %s ",
|
||||||
rta_getattr_u8(prtb[IFLA_BRPORT_MCAST_FLOOD]));
|
rta_getattr_u8(prtb[IFLA_BRPORT_MCAST_FLOOD]));
|
||||||
if (prtb[IFLA_BRPORT_MCAST_TO_UCAST])
|
if (prtb[IFLA_BRPORT_MCAST_TO_UCAST])
|
||||||
print_onoff(fp, "mcast_to_unicast",
|
print_on_off(PRINT_ANY, "mcast_to_unicast", "mcast_to_unicast %s ",
|
||||||
rta_getattr_u8(prtb[IFLA_BRPORT_MCAST_TO_UCAST]));
|
rta_getattr_u8(prtb[IFLA_BRPORT_MCAST_TO_UCAST]));
|
||||||
if (prtb[IFLA_BRPORT_NEIGH_SUPPRESS])
|
if (prtb[IFLA_BRPORT_NEIGH_SUPPRESS])
|
||||||
print_onoff(fp, "neigh_suppress",
|
print_on_off(PRINT_ANY, "neigh_suppress", "neigh_suppress %s ",
|
||||||
rta_getattr_u8(prtb[IFLA_BRPORT_NEIGH_SUPPRESS]));
|
rta_getattr_u8(prtb[IFLA_BRPORT_NEIGH_SUPPRESS]));
|
||||||
if (prtb[IFLA_BRPORT_VLAN_TUNNEL])
|
if (prtb[IFLA_BRPORT_VLAN_TUNNEL])
|
||||||
print_onoff(fp, "vlan_tunnel",
|
print_on_off(PRINT_ANY, "vlan_tunnel", "vlan_tunnel %s ",
|
||||||
rta_getattr_u8(prtb[IFLA_BRPORT_VLAN_TUNNEL]));
|
rta_getattr_u8(prtb[IFLA_BRPORT_VLAN_TUNNEL]));
|
||||||
|
|
||||||
if (prtb[IFLA_BRPORT_BACKUP_PORT]) {
|
if (prtb[IFLA_BRPORT_BACKUP_PORT]) {
|
||||||
int ifidx;
|
int ifidx;
|
||||||
|
|
@ -166,8 +158,8 @@ static void print_protinfo(FILE *fp, struct rtattr *attr)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prtb[IFLA_BRPORT_ISOLATED])
|
if (prtb[IFLA_BRPORT_ISOLATED])
|
||||||
print_onoff(fp, "isolated",
|
print_on_off(PRINT_ANY, "isolated", "isolated %s ",
|
||||||
rta_getattr_u8(prtb[IFLA_BRPORT_ISOLATED]));
|
rta_getattr_u8(prtb[IFLA_BRPORT_ISOLATED]));
|
||||||
} else
|
} else
|
||||||
print_portstate(rta_getattr_u8(attr));
|
print_portstate(rta_getattr_u8(attr));
|
||||||
}
|
}
|
||||||
|
|
@ -275,22 +267,6 @@ static void usage(void)
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool on_off(char *arg, __s8 *attr, char *val)
|
|
||||||
{
|
|
||||||
if (strcmp(val, "on") == 0)
|
|
||||||
*attr = 1;
|
|
||||||
else if (strcmp(val, "off") == 0)
|
|
||||||
*attr = 0;
|
|
||||||
else {
|
|
||||||
fprintf(stderr,
|
|
||||||
"Error: argument of \"%s\" must be \"on\" or \"off\"\n",
|
|
||||||
arg);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int brlink_modify(int argc, char **argv)
|
static int brlink_modify(int argc, char **argv)
|
||||||
{
|
{
|
||||||
struct {
|
struct {
|
||||||
|
|
@ -323,6 +299,7 @@ static int brlink_modify(int argc, char **argv)
|
||||||
__s16 mode = -1;
|
__s16 mode = -1;
|
||||||
__u16 flags = 0;
|
__u16 flags = 0;
|
||||||
struct rtattr *nest;
|
struct rtattr *nest;
|
||||||
|
int ret;
|
||||||
|
|
||||||
while (argc > 0) {
|
while (argc > 0) {
|
||||||
if (strcmp(*argv, "dev") == 0) {
|
if (strcmp(*argv, "dev") == 0) {
|
||||||
|
|
@ -330,40 +307,49 @@ static int brlink_modify(int argc, char **argv)
|
||||||
d = *argv;
|
d = *argv;
|
||||||
} else if (strcmp(*argv, "guard") == 0) {
|
} else if (strcmp(*argv, "guard") == 0) {
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
if (!on_off("guard", &bpdu_guard, *argv))
|
bpdu_guard = parse_on_off("guard", *argv, &ret);
|
||||||
return -1;
|
if (ret)
|
||||||
|
return ret;
|
||||||
} else if (strcmp(*argv, "hairpin") == 0) {
|
} else if (strcmp(*argv, "hairpin") == 0) {
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
if (!on_off("hairpin", &hairpin, *argv))
|
hairpin = parse_on_off("hairpin", *argv, &ret);
|
||||||
return -1;
|
if (ret)
|
||||||
|
return ret;
|
||||||
} else if (strcmp(*argv, "fastleave") == 0) {
|
} else if (strcmp(*argv, "fastleave") == 0) {
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
if (!on_off("fastleave", &fast_leave, *argv))
|
fast_leave = parse_on_off("fastleave", *argv, &ret);
|
||||||
return -1;
|
if (ret)
|
||||||
|
return ret;
|
||||||
} else if (strcmp(*argv, "root_block") == 0) {
|
} else if (strcmp(*argv, "root_block") == 0) {
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
if (!on_off("root_block", &root_block, *argv))
|
root_block = parse_on_off("root_block", *argv, &ret);
|
||||||
return -1;
|
if (ret)
|
||||||
|
return ret;
|
||||||
} else if (strcmp(*argv, "learning") == 0) {
|
} else if (strcmp(*argv, "learning") == 0) {
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
if (!on_off("learning", &learning, *argv))
|
learning = parse_on_off("learning", *argv, &ret);
|
||||||
return -1;
|
if (ret)
|
||||||
|
return ret;
|
||||||
} else if (strcmp(*argv, "learning_sync") == 0) {
|
} else if (strcmp(*argv, "learning_sync") == 0) {
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
if (!on_off("learning_sync", &learning_sync, *argv))
|
learning_sync = parse_on_off("learning_sync", *argv, &ret);
|
||||||
return -1;
|
if (ret)
|
||||||
|
return ret;
|
||||||
} else if (strcmp(*argv, "flood") == 0) {
|
} else if (strcmp(*argv, "flood") == 0) {
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
if (!on_off("flood", &flood, *argv))
|
flood = parse_on_off("flood", *argv, &ret);
|
||||||
return -1;
|
if (ret)
|
||||||
|
return ret;
|
||||||
} else if (strcmp(*argv, "mcast_flood") == 0) {
|
} else if (strcmp(*argv, "mcast_flood") == 0) {
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
if (!on_off("mcast_flood", &mcast_flood, *argv))
|
mcast_flood = parse_on_off("mcast_flood", *argv, &ret);
|
||||||
return -1;
|
if (ret)
|
||||||
|
return ret;
|
||||||
} else if (strcmp(*argv, "mcast_to_unicast") == 0) {
|
} else if (strcmp(*argv, "mcast_to_unicast") == 0) {
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
if (!on_off("mcast_to_unicast", &mcast_to_unicast, *argv))
|
mcast_to_unicast = parse_on_off("mcast_to_unicast", *argv, &ret);
|
||||||
return -1;
|
if (ret)
|
||||||
|
return ret;
|
||||||
} else if (strcmp(*argv, "cost") == 0) {
|
} else if (strcmp(*argv, "cost") == 0) {
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
cost = atoi(*argv);
|
cost = atoi(*argv);
|
||||||
|
|
@ -404,18 +390,19 @@ static int brlink_modify(int argc, char **argv)
|
||||||
flags |= BRIDGE_FLAGS_MASTER;
|
flags |= BRIDGE_FLAGS_MASTER;
|
||||||
} else if (strcmp(*argv, "neigh_suppress") == 0) {
|
} else if (strcmp(*argv, "neigh_suppress") == 0) {
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
if (!on_off("neigh_suppress", &neigh_suppress,
|
neigh_suppress = parse_on_off("neigh_suppress", *argv, &ret);
|
||||||
*argv))
|
if (ret)
|
||||||
return -1;
|
return ret;
|
||||||
} else if (strcmp(*argv, "vlan_tunnel") == 0) {
|
} else if (strcmp(*argv, "vlan_tunnel") == 0) {
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
if (!on_off("vlan_tunnel", &vlan_tunnel,
|
vlan_tunnel = parse_on_off("vlan_tunnel", *argv, &ret);
|
||||||
*argv))
|
if (ret)
|
||||||
return -1;
|
return ret;
|
||||||
} else if (strcmp(*argv, "isolated") == 0) {
|
} else if (strcmp(*argv, "isolated") == 0) {
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
if (!on_off("isolated", &isolated, *argv))
|
isolated = parse_on_off("isolated", *argv, &ret);
|
||||||
return -1;
|
if (ret)
|
||||||
|
return ret;
|
||||||
} else if (strcmp(*argv, "backup_port") == 0) {
|
} else if (strcmp(*argv, "backup_port") == 0) {
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
backup_port_idx = ll_name_to_index(*argv);
|
backup_port_idx = ll_name_to_index(*argv);
|
||||||
|
|
|
||||||
54
bridge/mdb.c
54
bridge/mdb.c
|
|
@ -149,6 +149,7 @@ static void print_mdb_entry(FILE *f, int ifindex, const struct br_mdb_entry *e,
|
||||||
struct nlmsghdr *n, struct rtattr **tb)
|
struct nlmsghdr *n, struct rtattr **tb)
|
||||||
{
|
{
|
||||||
const void *grp, *src;
|
const void *grp, *src;
|
||||||
|
const char *addr;
|
||||||
SPRINT_BUF(abuf);
|
SPRINT_BUF(abuf);
|
||||||
const char *dev;
|
const char *dev;
|
||||||
int af;
|
int af;
|
||||||
|
|
@ -156,9 +157,16 @@ static void print_mdb_entry(FILE *f, int ifindex, const struct br_mdb_entry *e,
|
||||||
if (filter_vlan && e->vid != filter_vlan)
|
if (filter_vlan && e->vid != filter_vlan)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
af = e->addr.proto == htons(ETH_P_IP) ? AF_INET : AF_INET6;
|
if (!e->addr.proto) {
|
||||||
grp = af == AF_INET ? (const void *)&e->addr.u.ip4 :
|
af = AF_PACKET;
|
||||||
(const void *)&e->addr.u.ip6;
|
grp = &e->addr.u.mac_addr;
|
||||||
|
} else if (e->addr.proto == htons(ETH_P_IP)) {
|
||||||
|
af = AF_INET;
|
||||||
|
grp = &e->addr.u.ip4;
|
||||||
|
} else {
|
||||||
|
af = AF_INET6;
|
||||||
|
grp = &e->addr.u.ip6;
|
||||||
|
}
|
||||||
dev = ll_index_to_name(ifindex);
|
dev = ll_index_to_name(ifindex);
|
||||||
|
|
||||||
open_json_object(NULL);
|
open_json_object(NULL);
|
||||||
|
|
@ -168,9 +176,14 @@ static void print_mdb_entry(FILE *f, int ifindex, const struct br_mdb_entry *e,
|
||||||
print_string(PRINT_ANY, "port", " port %s",
|
print_string(PRINT_ANY, "port", " port %s",
|
||||||
ll_index_to_name(e->ifindex));
|
ll_index_to_name(e->ifindex));
|
||||||
|
|
||||||
|
/* The ETH_ALEN argument is ignored for all cases but AF_PACKET */
|
||||||
|
addr = rt_addr_n2a_r(af, ETH_ALEN, grp, abuf, sizeof(abuf));
|
||||||
|
if (!addr)
|
||||||
|
return;
|
||||||
|
|
||||||
print_color_string(PRINT_ANY, ifa_family_color(af),
|
print_color_string(PRINT_ANY, ifa_family_color(af),
|
||||||
"grp", " grp %s",
|
"grp", " grp %s", addr);
|
||||||
inet_ntop(af, grp, abuf, sizeof(abuf)));
|
|
||||||
if (tb && tb[MDBA_MDB_EATTR_SOURCE]) {
|
if (tb && tb[MDBA_MDB_EATTR_SOURCE]) {
|
||||||
src = (const void *)RTA_DATA(tb[MDBA_MDB_EATTR_SOURCE]);
|
src = (const void *)RTA_DATA(tb[MDBA_MDB_EATTR_SOURCE]);
|
||||||
print_color_string(PRINT_ANY, ifa_family_color(af),
|
print_color_string(PRINT_ANY, ifa_family_color(af),
|
||||||
|
|
@ -440,6 +453,25 @@ static int mdb_show(int argc, char **argv)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int mdb_parse_grp(const char *grp, struct br_mdb_entry *e)
|
||||||
|
{
|
||||||
|
if (inet_pton(AF_INET, grp, &e->addr.u.ip4)) {
|
||||||
|
e->addr.proto = htons(ETH_P_IP);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (inet_pton(AF_INET6, grp, &e->addr.u.ip6)) {
|
||||||
|
e->addr.proto = htons(ETH_P_IPV6);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (ll_addr_a2n((char *)e->addr.u.mac_addr, sizeof(e->addr.u.mac_addr),
|
||||||
|
grp) == ETH_ALEN) {
|
||||||
|
e->addr.proto = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
static int mdb_modify(int cmd, int flags, int argc, char **argv)
|
static int mdb_modify(int cmd, int flags, int argc, char **argv)
|
||||||
{
|
{
|
||||||
struct {
|
struct {
|
||||||
|
|
@ -497,14 +529,10 @@ static int mdb_modify(int cmd, int flags, int argc, char **argv)
|
||||||
if (!entry.ifindex)
|
if (!entry.ifindex)
|
||||||
return nodev(p);
|
return nodev(p);
|
||||||
|
|
||||||
if (!inet_pton(AF_INET, grp, &entry.addr.u.ip4)) {
|
if (mdb_parse_grp(grp, &entry)) {
|
||||||
if (!inet_pton(AF_INET6, grp, &entry.addr.u.ip6)) {
|
fprintf(stderr, "Invalid address \"%s\"\n", grp);
|
||||||
fprintf(stderr, "Invalid address \"%s\"\n", grp);
|
return -1;
|
||||||
return -1;
|
}
|
||||||
} else
|
|
||||||
entry.addr.proto = htons(ETH_P_IPV6);
|
|
||||||
} else
|
|
||||||
entry.addr.proto = htons(ETH_P_IP);
|
|
||||||
|
|
||||||
entry.vid = vid;
|
entry.vid = vid;
|
||||||
addattr_l(&req.n, sizeof(req), MDBA_SET_ENTRY, &entry, sizeof(entry));
|
addattr_l(&req.n, sizeof(req), MDBA_SET_ENTRY, &entry, sizeof(entry));
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,11 @@
|
||||||
# SPDX-License-Identifier: GPL-2.0
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
# This is not an autoconf generated configure
|
# This is not an autoconf generated configure
|
||||||
#
|
#
|
||||||
|
# Influential LIBBPF environment variables:
|
||||||
|
# LIBBPF_FORCE={on,off} on: require link against libbpf;
|
||||||
|
# off: disable libbpf probing
|
||||||
|
# LIBBPF_DIR Path to libbpf DESTDIR to use
|
||||||
|
|
||||||
INCLUDE=${1:-"$PWD/include"}
|
INCLUDE=${1:-"$PWD/include"}
|
||||||
|
|
||||||
# Output file which is input to Makefile
|
# Output file which is input to Makefile
|
||||||
|
|
@ -240,6 +245,111 @@ check_elf()
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
have_libbpf_basic()
|
||||||
|
{
|
||||||
|
cat >$TMPDIR/libbpf_test.c <<EOF
|
||||||
|
#include <bpf/libbpf.h>
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
bpf_program__set_autoload(NULL, false);
|
||||||
|
bpf_map__ifindex(NULL);
|
||||||
|
bpf_map__set_pin_path(NULL, NULL);
|
||||||
|
bpf_object__open_file(NULL, NULL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
$CC -o $TMPDIR/libbpf_test $TMPDIR/libbpf_test.c $LIBBPF_CFLAGS $LIBBPF_LDLIBS >/dev/null 2>&1
|
||||||
|
local ret=$?
|
||||||
|
|
||||||
|
rm -f $TMPDIR/libbpf_test.c $TMPDIR/libbpf_test
|
||||||
|
return $ret
|
||||||
|
}
|
||||||
|
|
||||||
|
have_libbpf_sec_name()
|
||||||
|
{
|
||||||
|
cat >$TMPDIR/libbpf_sec_test.c <<EOF
|
||||||
|
#include <bpf/libbpf.h>
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
void *ptr;
|
||||||
|
bpf_program__section_name(NULL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
$CC -o $TMPDIR/libbpf_sec_test $TMPDIR/libbpf_sec_test.c $LIBBPF_CFLAGS $LIBBPF_LDLIBS >/dev/null 2>&1
|
||||||
|
local ret=$?
|
||||||
|
|
||||||
|
rm -f $TMPDIR/libbpf_sec_test.c $TMPDIR/libbpf_sec_test
|
||||||
|
return $ret
|
||||||
|
}
|
||||||
|
|
||||||
|
check_force_libbpf_on()
|
||||||
|
{
|
||||||
|
# if set LIBBPF_FORCE=on but no libbpf support, just exist the config
|
||||||
|
# process to make sure we don't build without libbpf.
|
||||||
|
if [ "$LIBBPF_FORCE" = on ]; then
|
||||||
|
echo " LIBBPF_FORCE=on set, but couldn't find a usable libbpf"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
check_libbpf()
|
||||||
|
{
|
||||||
|
# if set LIBBPF_FORCE=off, disable libbpf entirely
|
||||||
|
if [ "$LIBBPF_FORCE" = off ]; then
|
||||||
|
echo "no"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! ${PKG_CONFIG} libbpf --exists && [ -z "$LIBBPF_DIR" ] ; then
|
||||||
|
echo "no"
|
||||||
|
check_force_libbpf_on
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $(uname -m) = x86_64 ]; then
|
||||||
|
local LIBBPF_LIBDIR="${LIBBPF_DIR}/usr/lib64"
|
||||||
|
else
|
||||||
|
local LIBBPF_LIBDIR="${LIBBPF_DIR}/usr/lib"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$LIBBPF_DIR" ]; then
|
||||||
|
LIBBPF_CFLAGS="-I${LIBBPF_DIR}/usr/include"
|
||||||
|
LIBBPF_LDLIBS="${LIBBPF_LIBDIR}/libbpf.a -lz -lelf"
|
||||||
|
LIBBPF_VERSION=$(PKG_CONFIG_LIBDIR=${LIBBPF_LIBDIR}/pkgconfig ${PKG_CONFIG} libbpf --modversion)
|
||||||
|
else
|
||||||
|
LIBBPF_CFLAGS=$(${PKG_CONFIG} libbpf --cflags)
|
||||||
|
LIBBPF_LDLIBS=$(${PKG_CONFIG} libbpf --libs)
|
||||||
|
LIBBPF_VERSION=$(${PKG_CONFIG} libbpf --modversion)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! have_libbpf_basic; then
|
||||||
|
echo "no"
|
||||||
|
echo " libbpf version $LIBBPF_VERSION is too low, please update it to at least 0.1.0"
|
||||||
|
check_force_libbpf_on
|
||||||
|
return
|
||||||
|
else
|
||||||
|
echo "HAVE_LIBBPF:=y" >> $CONFIG
|
||||||
|
echo 'CFLAGS += -DHAVE_LIBBPF ' $LIBBPF_CFLAGS >> $CONFIG
|
||||||
|
echo "CFLAGS += -DLIBBPF_VERSION=\\\"$LIBBPF_VERSION\\\"" >> $CONFIG
|
||||||
|
echo 'LDLIBS += ' $LIBBPF_LDLIBS >> $CONFIG
|
||||||
|
|
||||||
|
if [ -z "$LIBBPF_DIR" ]; then
|
||||||
|
echo "CFLAGS += -DLIBBPF_DYNAMIC" >> $CONFIG
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# bpf_program__title() is deprecated since libbpf 0.2.0, use
|
||||||
|
# bpf_program__section_name() instead if we support
|
||||||
|
if have_libbpf_sec_name; then
|
||||||
|
echo "HAVE_LIBBPF_SECTION_NAME:=y" >> $CONFIG
|
||||||
|
echo 'CFLAGS += -DHAVE_LIBBPF_SECTION_NAME ' >> $CONFIG
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "yes"
|
||||||
|
echo " libbpf version $LIBBPF_VERSION"
|
||||||
|
}
|
||||||
|
|
||||||
check_selinux()
|
check_selinux()
|
||||||
# SELinux is a compile time option in the ss utility
|
# SELinux is a compile time option in the ss utility
|
||||||
{
|
{
|
||||||
|
|
@ -385,6 +495,9 @@ check_setns
|
||||||
echo -n "SELinux support: "
|
echo -n "SELinux support: "
|
||||||
check_selinux
|
check_selinux
|
||||||
|
|
||||||
|
echo -n "libbpf support: "
|
||||||
|
check_libbpf
|
||||||
|
|
||||||
echo -n "ELF support: "
|
echo -n "ELF support: "
|
||||||
check_elf
|
check_elf
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
dcb
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
|
include ../config.mk
|
||||||
|
|
||||||
|
TARGETS :=
|
||||||
|
|
||||||
|
ifeq ($(HAVE_MNL),y)
|
||||||
|
|
||||||
|
DCBOBJ = dcb.o dcb_buffer.o dcb_ets.o dcb_maxrate.o dcb_pfc.o
|
||||||
|
TARGETS += dcb
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
|
all: $(TARGETS) $(LIBS)
|
||||||
|
|
||||||
|
dcb: $(DCBOBJ) $(LIBNETLINK)
|
||||||
|
$(QUIET_LINK)$(CC) $^ $(LDFLAGS) $(LDLIBS) -o $@
|
||||||
|
|
||||||
|
install: all
|
||||||
|
for i in $(TARGETS); \
|
||||||
|
do install -m 0755 $$i $(DESTDIR)$(SBINDIR); \
|
||||||
|
done
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(DCBOBJ) $(TARGETS)
|
||||||
|
|
@ -0,0 +1,468 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <linux/dcbnl.h>
|
||||||
|
#include <libmnl/libmnl.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
|
||||||
|
#include "dcb.h"
|
||||||
|
#include "mnl_utils.h"
|
||||||
|
#include "namespace.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "version.h"
|
||||||
|
|
||||||
|
static int dcb_init(struct dcb *dcb)
|
||||||
|
{
|
||||||
|
dcb->buf = malloc(MNL_SOCKET_BUFFER_SIZE);
|
||||||
|
if (dcb->buf == NULL) {
|
||||||
|
perror("Netlink buffer allocation");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
dcb->nl = mnlu_socket_open(NETLINK_ROUTE);
|
||||||
|
if (dcb->nl == NULL) {
|
||||||
|
perror("Open netlink socket");
|
||||||
|
goto err_socket_open;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_json_obj_plain(dcb->json_output);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_socket_open:
|
||||||
|
free(dcb->buf);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dcb_fini(struct dcb *dcb)
|
||||||
|
{
|
||||||
|
delete_json_obj_plain();
|
||||||
|
mnl_socket_close(dcb->nl);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct dcb *dcb_alloc(void)
|
||||||
|
{
|
||||||
|
struct dcb *dcb;
|
||||||
|
|
||||||
|
dcb = calloc(1, sizeof(*dcb));
|
||||||
|
if (!dcb)
|
||||||
|
return NULL;
|
||||||
|
return dcb;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dcb_free(struct dcb *dcb)
|
||||||
|
{
|
||||||
|
free(dcb);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct dcb_get_attribute {
|
||||||
|
struct dcb *dcb;
|
||||||
|
int attr;
|
||||||
|
void *data;
|
||||||
|
size_t data_len;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int dcb_get_attribute_attr_ieee_cb(const struct nlattr *attr, void *data)
|
||||||
|
{
|
||||||
|
struct dcb_get_attribute *ga = data;
|
||||||
|
uint16_t len;
|
||||||
|
|
||||||
|
if (mnl_attr_get_type(attr) != ga->attr)
|
||||||
|
return MNL_CB_OK;
|
||||||
|
|
||||||
|
len = mnl_attr_get_payload_len(attr);
|
||||||
|
if (len != ga->data_len) {
|
||||||
|
fprintf(stderr, "Wrong len %d, expected %zd\n", len, ga->data_len);
|
||||||
|
return MNL_CB_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(ga->data, mnl_attr_get_payload(attr), ga->data_len);
|
||||||
|
return MNL_CB_STOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dcb_get_attribute_attr_cb(const struct nlattr *attr, void *data)
|
||||||
|
{
|
||||||
|
if (mnl_attr_get_type(attr) != DCB_ATTR_IEEE)
|
||||||
|
return MNL_CB_OK;
|
||||||
|
|
||||||
|
return mnl_attr_parse_nested(attr, dcb_get_attribute_attr_ieee_cb, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dcb_get_attribute_cb(const struct nlmsghdr *nlh, void *data)
|
||||||
|
{
|
||||||
|
return mnl_attr_parse(nlh, sizeof(struct dcbmsg), dcb_get_attribute_attr_cb, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dcb_set_attribute_attr_cb(const struct nlattr *attr, void *data)
|
||||||
|
{
|
||||||
|
uint16_t len;
|
||||||
|
uint8_t err;
|
||||||
|
|
||||||
|
if (mnl_attr_get_type(attr) != DCB_ATTR_IEEE)
|
||||||
|
return MNL_CB_OK;
|
||||||
|
|
||||||
|
len = mnl_attr_get_payload_len(attr);
|
||||||
|
if (len != 1) {
|
||||||
|
fprintf(stderr, "Response attribute expected to have size 1, not %d\n", len);
|
||||||
|
return MNL_CB_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = mnl_attr_get_u8(attr);
|
||||||
|
if (err) {
|
||||||
|
fprintf(stderr, "Error when attempting to set attribute: %s\n",
|
||||||
|
strerror(err));
|
||||||
|
return MNL_CB_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return MNL_CB_STOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dcb_set_attribute_cb(const struct nlmsghdr *nlh, void *data)
|
||||||
|
{
|
||||||
|
return mnl_attr_parse(nlh, sizeof(struct dcbmsg), dcb_set_attribute_attr_cb, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dcb_talk(struct dcb *dcb, struct nlmsghdr *nlh, mnl_cb_t cb, void *data)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = mnl_socket_sendto(dcb->nl, nlh, nlh->nlmsg_len);
|
||||||
|
if (ret < 0) {
|
||||||
|
perror("mnl_socket_sendto");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mnlu_socket_recv_run(dcb->nl, nlh->nlmsg_seq, dcb->buf, MNL_SOCKET_BUFFER_SIZE,
|
||||||
|
cb, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct nlmsghdr *dcb_prepare(struct dcb *dcb, const char *dev,
|
||||||
|
uint32_t nlmsg_type, uint8_t dcb_cmd)
|
||||||
|
{
|
||||||
|
struct dcbmsg dcbm = {
|
||||||
|
.cmd = dcb_cmd,
|
||||||
|
};
|
||||||
|
struct nlmsghdr *nlh;
|
||||||
|
|
||||||
|
nlh = mnlu_msg_prepare(dcb->buf, nlmsg_type, NLM_F_REQUEST, &dcbm, sizeof(dcbm));
|
||||||
|
mnl_attr_put_strz(nlh, DCB_ATTR_IFNAME, dev);
|
||||||
|
return nlh;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dcb_get_attribute(struct dcb *dcb, const char *dev, int attr, void *data, size_t data_len)
|
||||||
|
{
|
||||||
|
struct dcb_get_attribute ga;
|
||||||
|
struct nlmsghdr *nlh;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
nlh = dcb_prepare(dcb, dev, RTM_GETDCB, DCB_CMD_IEEE_GET);
|
||||||
|
|
||||||
|
ga = (struct dcb_get_attribute) {
|
||||||
|
.dcb = dcb,
|
||||||
|
.attr = attr,
|
||||||
|
.data = data,
|
||||||
|
.data_len = data_len,
|
||||||
|
};
|
||||||
|
ret = dcb_talk(dcb, nlh, dcb_get_attribute_cb, &ga);
|
||||||
|
if (ret) {
|
||||||
|
perror("Attribute read");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dcb_set_attribute(struct dcb *dcb, const char *dev, int attr, const void *data, size_t data_len)
|
||||||
|
{
|
||||||
|
struct nlmsghdr *nlh;
|
||||||
|
struct nlattr *nest;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
nlh = dcb_prepare(dcb, dev, RTM_GETDCB, DCB_CMD_IEEE_SET);
|
||||||
|
|
||||||
|
nest = mnl_attr_nest_start(nlh, DCB_ATTR_IEEE);
|
||||||
|
mnl_attr_put(nlh, attr, data_len, data);
|
||||||
|
mnl_attr_nest_end(nlh, nest);
|
||||||
|
|
||||||
|
ret = dcb_talk(dcb, nlh, dcb_set_attribute_cb, NULL);
|
||||||
|
if (ret) {
|
||||||
|
perror("Attribute write");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dcb_print_array_u8(const __u8 *array, size_t size)
|
||||||
|
{
|
||||||
|
SPRINT_BUF(b);
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < size; i++) {
|
||||||
|
snprintf(b, sizeof(b), "%zd:%%d ", i);
|
||||||
|
print_uint(PRINT_ANY, NULL, b, array[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void dcb_print_array_u64(const __u64 *array, size_t size)
|
||||||
|
{
|
||||||
|
SPRINT_BUF(b);
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < size; i++) {
|
||||||
|
snprintf(b, sizeof(b), "%zd:%%" PRIu64 " ", i);
|
||||||
|
print_u64(PRINT_ANY, NULL, b, array[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void dcb_print_array_on_off(const __u8 *array, size_t size)
|
||||||
|
{
|
||||||
|
SPRINT_BUF(b);
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < size; i++) {
|
||||||
|
snprintf(b, sizeof(b), "%zd:%%s ", i);
|
||||||
|
print_on_off(PRINT_ANY, NULL, b, array[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void dcb_print_array_kw(const __u8 *array, size_t array_size,
|
||||||
|
const char *const kw[], size_t kw_size)
|
||||||
|
{
|
||||||
|
SPRINT_BUF(b);
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < array_size; i++) {
|
||||||
|
__u8 emt = array[i];
|
||||||
|
|
||||||
|
snprintf(b, sizeof(b), "%zd:%%s ", i);
|
||||||
|
if (emt < kw_size && kw[emt])
|
||||||
|
print_string(PRINT_ANY, NULL, b, kw[emt]);
|
||||||
|
else
|
||||||
|
print_string(PRINT_ANY, NULL, b, "???");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void dcb_print_named_array(const char *json_name, const char *fp_name,
|
||||||
|
const __u8 *array, size_t size,
|
||||||
|
void (*print_array)(const __u8 *, size_t))
|
||||||
|
{
|
||||||
|
open_json_array(PRINT_JSON, json_name);
|
||||||
|
print_string(PRINT_FP, NULL, "%s ", fp_name);
|
||||||
|
print_array(array, size);
|
||||||
|
close_json_array(PRINT_JSON, json_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
int dcb_parse_mapping(const char *what_key, __u32 key, __u32 max_key,
|
||||||
|
const char *what_value, __u64 value, __u64 max_value,
|
||||||
|
void (*set_array)(__u32 index, __u64 value, void *data),
|
||||||
|
void *set_array_data)
|
||||||
|
{
|
||||||
|
bool is_all = key == (__u32) -1;
|
||||||
|
|
||||||
|
if (!is_all && key > max_key) {
|
||||||
|
fprintf(stderr, "In %s:%s mapping, %s is expected to be 0..%d\n",
|
||||||
|
what_key, what_value, what_key, max_key);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value > max_value) {
|
||||||
|
fprintf(stderr, "In %s:%s mapping, %s is expected to be 0..%llu\n",
|
||||||
|
what_key, what_value, what_value, max_value);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_all) {
|
||||||
|
for (key = 0; key <= max_key; key++)
|
||||||
|
set_array(key, value, set_array_data);
|
||||||
|
} else {
|
||||||
|
set_array(key, value, set_array_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dcb_set_u8(__u32 key, __u64 value, void *data)
|
||||||
|
{
|
||||||
|
__u8 *array = data;
|
||||||
|
|
||||||
|
array[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dcb_set_u32(__u32 key, __u64 value, void *data)
|
||||||
|
{
|
||||||
|
__u32 *array = data;
|
||||||
|
|
||||||
|
array[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dcb_set_u64(__u32 key, __u64 value, void *data)
|
||||||
|
{
|
||||||
|
__u64 *array = data;
|
||||||
|
|
||||||
|
array[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dcb_cmd_parse_dev(struct dcb *dcb, int argc, char **argv,
|
||||||
|
int (*and_then)(struct dcb *dcb, const char *dev,
|
||||||
|
int argc, char **argv),
|
||||||
|
void (*help)(void))
|
||||||
|
{
|
||||||
|
const char *dev;
|
||||||
|
|
||||||
|
if (!argc || matches(*argv, "help") == 0) {
|
||||||
|
help();
|
||||||
|
return 0;
|
||||||
|
} else if (matches(*argv, "dev") == 0) {
|
||||||
|
NEXT_ARG();
|
||||||
|
dev = *argv;
|
||||||
|
if (check_ifname(dev)) {
|
||||||
|
invarg("not a valid ifname", *argv);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
NEXT_ARG_FWD();
|
||||||
|
return and_then(dcb, dev, argc, argv);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Expected `dev DEV', not `%s'", *argv);
|
||||||
|
help();
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dcb_help(void)
|
||||||
|
{
|
||||||
|
fprintf(stderr,
|
||||||
|
"Usage: dcb [ OPTIONS ] OBJECT { COMMAND | help }\n"
|
||||||
|
" dcb [ -f | --force ] { -b | --batch } filename [ -N | --Netns ] netnsname\n"
|
||||||
|
"where OBJECT := { buffer | ets | maxrate | pfc }\n"
|
||||||
|
" OPTIONS := [ -V | --Version | -i | --iec | -j | --json\n"
|
||||||
|
" | -p | --pretty | -s | --statistics | -v | --verbose]\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dcb_cmd(struct dcb *dcb, int argc, char **argv)
|
||||||
|
{
|
||||||
|
if (!argc || matches(*argv, "help") == 0) {
|
||||||
|
dcb_help();
|
||||||
|
return 0;
|
||||||
|
} else if (matches(*argv, "buffer") == 0) {
|
||||||
|
return dcb_cmd_buffer(dcb, argc - 1, argv + 1);
|
||||||
|
} else if (matches(*argv, "ets") == 0) {
|
||||||
|
return dcb_cmd_ets(dcb, argc - 1, argv + 1);
|
||||||
|
} else if (matches(*argv, "maxrate") == 0) {
|
||||||
|
return dcb_cmd_maxrate(dcb, argc - 1, argv + 1);
|
||||||
|
} else if (matches(*argv, "pfc") == 0) {
|
||||||
|
return dcb_cmd_pfc(dcb, argc - 1, argv + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "Object \"%s\" is unknown\n", *argv);
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dcb_batch_cmd(int argc, char *argv[], void *data)
|
||||||
|
{
|
||||||
|
struct dcb *dcb = data;
|
||||||
|
|
||||||
|
return dcb_cmd(dcb, argc, argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dcb_batch(struct dcb *dcb, const char *name, bool force)
|
||||||
|
{
|
||||||
|
return do_batch(name, force, dcb_batch_cmd, dcb);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
static const struct option long_options[] = {
|
||||||
|
{ "Version", no_argument, NULL, 'V' },
|
||||||
|
{ "force", no_argument, NULL, 'f' },
|
||||||
|
{ "batch", required_argument, NULL, 'b' },
|
||||||
|
{ "iec", no_argument, NULL, 'i' },
|
||||||
|
{ "json", no_argument, NULL, 'j' },
|
||||||
|
{ "pretty", no_argument, NULL, 'p' },
|
||||||
|
{ "statistics", no_argument, NULL, 's' },
|
||||||
|
{ "Netns", required_argument, NULL, 'N' },
|
||||||
|
{ "help", no_argument, NULL, 'h' },
|
||||||
|
{ NULL, 0, NULL, 0 }
|
||||||
|
};
|
||||||
|
const char *batch_file = NULL;
|
||||||
|
bool force = false;
|
||||||
|
struct dcb *dcb;
|
||||||
|
int opt;
|
||||||
|
int err;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
dcb = dcb_alloc();
|
||||||
|
if (!dcb) {
|
||||||
|
fprintf(stderr, "Failed to allocate memory for dcb\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((opt = getopt_long(argc, argv, "b:fhijpsvN:V",
|
||||||
|
long_options, NULL)) >= 0) {
|
||||||
|
|
||||||
|
switch (opt) {
|
||||||
|
case 'V':
|
||||||
|
printf("dcb utility, iproute2-%s\n", version);
|
||||||
|
ret = EXIT_SUCCESS;
|
||||||
|
goto dcb_free;
|
||||||
|
case 'f':
|
||||||
|
force = true;
|
||||||
|
break;
|
||||||
|
case 'b':
|
||||||
|
batch_file = optarg;
|
||||||
|
break;
|
||||||
|
case 'j':
|
||||||
|
dcb->json_output = true;
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
pretty = true;
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
dcb->stats = true;
|
||||||
|
break;
|
||||||
|
case 'N':
|
||||||
|
if (netns_switch(optarg)) {
|
||||||
|
ret = EXIT_FAILURE;
|
||||||
|
goto dcb_free;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'i':
|
||||||
|
dcb->use_iec = true;
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
dcb_help();
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "Unknown option.\n");
|
||||||
|
dcb_help();
|
||||||
|
ret = EXIT_FAILURE;
|
||||||
|
goto dcb_free;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
argc -= optind;
|
||||||
|
argv += optind;
|
||||||
|
|
||||||
|
err = dcb_init(dcb);
|
||||||
|
if (err) {
|
||||||
|
ret = EXIT_FAILURE;
|
||||||
|
goto dcb_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (batch_file)
|
||||||
|
err = dcb_batch(dcb, batch_file, force);
|
||||||
|
else
|
||||||
|
err = dcb_cmd(dcb, argc, argv);
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
ret = EXIT_FAILURE;
|
||||||
|
goto dcb_fini;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = EXIT_SUCCESS;
|
||||||
|
|
||||||
|
dcb_fini:
|
||||||
|
dcb_fini(dcb);
|
||||||
|
dcb_free:
|
||||||
|
dcb_free(dcb);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
|
#ifndef __DCB_H__
|
||||||
|
#define __DCB_H__ 1
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
/* dcb.c */
|
||||||
|
|
||||||
|
struct dcb {
|
||||||
|
char *buf;
|
||||||
|
struct mnl_socket *nl;
|
||||||
|
bool json_output;
|
||||||
|
bool stats;
|
||||||
|
bool use_iec;
|
||||||
|
};
|
||||||
|
|
||||||
|
int dcb_parse_mapping(const char *what_key, __u32 key, __u32 max_key,
|
||||||
|
const char *what_value, __u64 value, __u64 max_value,
|
||||||
|
void (*set_array)(__u32 index, __u64 value, void *data),
|
||||||
|
void *set_array_data);
|
||||||
|
int dcb_cmd_parse_dev(struct dcb *dcb, int argc, char **argv,
|
||||||
|
int (*and_then)(struct dcb *dcb, const char *dev,
|
||||||
|
int argc, char **argv),
|
||||||
|
void (*help)(void));
|
||||||
|
|
||||||
|
void dcb_set_u8(__u32 key, __u64 value, void *data);
|
||||||
|
void dcb_set_u32(__u32 key, __u64 value, void *data);
|
||||||
|
void dcb_set_u64(__u32 key, __u64 value, void *data);
|
||||||
|
|
||||||
|
int dcb_get_attribute(struct dcb *dcb, const char *dev, int attr,
|
||||||
|
void *data, size_t data_len);
|
||||||
|
int dcb_set_attribute(struct dcb *dcb, const char *dev, int attr,
|
||||||
|
const void *data, size_t data_len);
|
||||||
|
|
||||||
|
void dcb_print_named_array(const char *json_name, const char *fp_name,
|
||||||
|
const __u8 *array, size_t size,
|
||||||
|
void (*print_array)(const __u8 *, size_t));
|
||||||
|
void dcb_print_array_u8(const __u8 *array, size_t size);
|
||||||
|
void dcb_print_array_u64(const __u64 *array, size_t size);
|
||||||
|
void dcb_print_array_on_off(const __u8 *array, size_t size);
|
||||||
|
void dcb_print_array_kw(const __u8 *array, size_t array_size,
|
||||||
|
const char *const kw[], size_t kw_size);
|
||||||
|
|
||||||
|
/* dcb_buffer.c */
|
||||||
|
|
||||||
|
int dcb_cmd_buffer(struct dcb *dcb, int argc, char **argv);
|
||||||
|
|
||||||
|
/* dcb_ets.c */
|
||||||
|
|
||||||
|
int dcb_cmd_ets(struct dcb *dcb, int argc, char **argv);
|
||||||
|
|
||||||
|
/* dcb_maxrate.c */
|
||||||
|
|
||||||
|
int dcb_cmd_maxrate(struct dcb *dcb, int argc, char **argv);
|
||||||
|
|
||||||
|
/* dcb_pfc.c */
|
||||||
|
|
||||||
|
int dcb_cmd_pfc(struct dcb *dcb, int argc, char **argv);
|
||||||
|
|
||||||
|
#endif /* __DCB_H__ */
|
||||||
|
|
@ -0,0 +1,235 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <linux/dcbnl.h>
|
||||||
|
|
||||||
|
#include "dcb.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
static void dcb_buffer_help_set(void)
|
||||||
|
{
|
||||||
|
fprintf(stderr,
|
||||||
|
"Usage: dcb buffer set dev STRING\n"
|
||||||
|
" [ prio-buffer PRIO-MAP ]\n"
|
||||||
|
" [ buffer-size SIZE-MAP ]\n"
|
||||||
|
"\n"
|
||||||
|
" where PRIO-MAP := [ PRIO-MAP ] PRIO-MAPPING\n"
|
||||||
|
" PRIO-MAPPING := { all | PRIO }:BUFFER\n"
|
||||||
|
" SIZE-MAP := [ SIZE-MAP ] SIZE-MAPPING\n"
|
||||||
|
" SIZE-MAPPING := { all | BUFFER }:INTEGER\n"
|
||||||
|
" PRIO := { 0 .. 7 }\n"
|
||||||
|
" BUFFER := { 0 .. 7 }\n"
|
||||||
|
"\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dcb_buffer_help_show(void)
|
||||||
|
{
|
||||||
|
fprintf(stderr,
|
||||||
|
"Usage: dcb buffer show dev STRING\n"
|
||||||
|
" [ prio-buffer ] [ buffer-size ] [ total-size ]\n"
|
||||||
|
"\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dcb_buffer_help(void)
|
||||||
|
{
|
||||||
|
fprintf(stderr,
|
||||||
|
"Usage: dcb buffer help\n"
|
||||||
|
"\n"
|
||||||
|
);
|
||||||
|
dcb_buffer_help_show();
|
||||||
|
dcb_buffer_help_set();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dcb_buffer_parse_mapping_prio_buffer(__u32 key, char *value, void *data)
|
||||||
|
{
|
||||||
|
struct dcbnl_buffer *buffer = data;
|
||||||
|
__u8 buf;
|
||||||
|
|
||||||
|
if (get_u8(&buf, value, 0))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
return dcb_parse_mapping("PRIO", key, IEEE_8021Q_MAX_PRIORITIES - 1,
|
||||||
|
"BUFFER", buf, DCBX_MAX_BUFFERS - 1,
|
||||||
|
dcb_set_u8, buffer->prio2buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dcb_buffer_parse_mapping_buffer_size(__u32 key, char *value, void *data)
|
||||||
|
{
|
||||||
|
struct dcbnl_buffer *buffer = data;
|
||||||
|
unsigned int size;
|
||||||
|
|
||||||
|
if (get_size(&size, value)) {
|
||||||
|
fprintf(stderr, "%d:%s: Illegal value for buffer size\n", key, value);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dcb_parse_mapping("BUFFER", key, DCBX_MAX_BUFFERS - 1,
|
||||||
|
"INTEGER", size, -1,
|
||||||
|
dcb_set_u32, buffer->buffer_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dcb_buffer_print_total_size(const struct dcbnl_buffer *buffer)
|
||||||
|
{
|
||||||
|
print_size(PRINT_ANY, "total_size", "total-size %s ", buffer->total_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dcb_buffer_print_prio_buffer(const struct dcbnl_buffer *buffer)
|
||||||
|
{
|
||||||
|
dcb_print_named_array("prio_buffer", "prio-buffer",
|
||||||
|
buffer->prio2buffer, ARRAY_SIZE(buffer->prio2buffer),
|
||||||
|
dcb_print_array_u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dcb_buffer_print_buffer_size(const struct dcbnl_buffer *buffer)
|
||||||
|
{
|
||||||
|
size_t size = ARRAY_SIZE(buffer->buffer_size);
|
||||||
|
SPRINT_BUF(b);
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
open_json_array(PRINT_JSON, "buffer_size");
|
||||||
|
print_string(PRINT_FP, NULL, "buffer-size ", NULL);
|
||||||
|
|
||||||
|
for (i = 0; i < size; i++) {
|
||||||
|
snprintf(b, sizeof(b), "%zd:%%s ", i);
|
||||||
|
print_size(PRINT_ANY, NULL, b, buffer->buffer_size[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
close_json_array(PRINT_JSON, "buffer_size");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dcb_buffer_print(const struct dcbnl_buffer *buffer)
|
||||||
|
{
|
||||||
|
dcb_buffer_print_prio_buffer(buffer);
|
||||||
|
print_nl();
|
||||||
|
|
||||||
|
dcb_buffer_print_buffer_size(buffer);
|
||||||
|
print_nl();
|
||||||
|
|
||||||
|
dcb_buffer_print_total_size(buffer);
|
||||||
|
print_nl();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dcb_buffer_get(struct dcb *dcb, const char *dev, struct dcbnl_buffer *buffer)
|
||||||
|
{
|
||||||
|
return dcb_get_attribute(dcb, dev, DCB_ATTR_DCB_BUFFER, buffer, sizeof(*buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dcb_buffer_set(struct dcb *dcb, const char *dev, const struct dcbnl_buffer *buffer)
|
||||||
|
{
|
||||||
|
return dcb_set_attribute(dcb, dev, DCB_ATTR_DCB_BUFFER, buffer, sizeof(*buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dcb_cmd_buffer_set(struct dcb *dcb, const char *dev, int argc, char **argv)
|
||||||
|
{
|
||||||
|
struct dcbnl_buffer buffer;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!argc) {
|
||||||
|
dcb_buffer_help_set();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = dcb_buffer_get(dcb, dev, &buffer);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (matches(*argv, "help") == 0) {
|
||||||
|
dcb_buffer_help_set();
|
||||||
|
return 0;
|
||||||
|
} else if (matches(*argv, "prio-buffer") == 0) {
|
||||||
|
NEXT_ARG();
|
||||||
|
ret = parse_mapping(&argc, &argv, true,
|
||||||
|
&dcb_buffer_parse_mapping_prio_buffer, &buffer);
|
||||||
|
if (ret) {
|
||||||
|
fprintf(stderr, "Invalid priority mapping %s\n", *argv);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
} else if (matches(*argv, "buffer-size") == 0) {
|
||||||
|
NEXT_ARG();
|
||||||
|
ret = parse_mapping(&argc, &argv, true,
|
||||||
|
&dcb_buffer_parse_mapping_buffer_size, &buffer);
|
||||||
|
if (ret) {
|
||||||
|
fprintf(stderr, "Invalid buffer size mapping %s\n", *argv);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "What is \"%s\"?\n", *argv);
|
||||||
|
dcb_buffer_help_set();
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
NEXT_ARG_FWD();
|
||||||
|
} while (argc > 0);
|
||||||
|
|
||||||
|
return dcb_buffer_set(dcb, dev, &buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dcb_cmd_buffer_show(struct dcb *dcb, const char *dev, int argc, char **argv)
|
||||||
|
{
|
||||||
|
struct dcbnl_buffer buffer;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = dcb_buffer_get(dcb, dev, &buffer);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
open_json_object(NULL);
|
||||||
|
|
||||||
|
if (!argc) {
|
||||||
|
dcb_buffer_print(&buffer);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (matches(*argv, "help") == 0) {
|
||||||
|
dcb_buffer_help_show();
|
||||||
|
return 0;
|
||||||
|
} else if (matches(*argv, "prio-buffer") == 0) {
|
||||||
|
dcb_buffer_print_prio_buffer(&buffer);
|
||||||
|
print_nl();
|
||||||
|
} else if (matches(*argv, "buffer-size") == 0) {
|
||||||
|
dcb_buffer_print_buffer_size(&buffer);
|
||||||
|
print_nl();
|
||||||
|
} else if (matches(*argv, "total-size") == 0) {
|
||||||
|
dcb_buffer_print_total_size(&buffer);
|
||||||
|
print_nl();
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "What is \"%s\"?\n", *argv);
|
||||||
|
dcb_buffer_help_show();
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
NEXT_ARG_FWD();
|
||||||
|
} while (argc > 0);
|
||||||
|
|
||||||
|
out:
|
||||||
|
close_json_object();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dcb_cmd_buffer(struct dcb *dcb, int argc, char **argv)
|
||||||
|
{
|
||||||
|
if (!argc || matches(*argv, "help") == 0) {
|
||||||
|
dcb_buffer_help();
|
||||||
|
return 0;
|
||||||
|
} else if (matches(*argv, "show") == 0) {
|
||||||
|
NEXT_ARG_FWD();
|
||||||
|
return dcb_cmd_parse_dev(dcb, argc, argv,
|
||||||
|
dcb_cmd_buffer_show, dcb_buffer_help_show);
|
||||||
|
} else if (matches(*argv, "set") == 0) {
|
||||||
|
NEXT_ARG_FWD();
|
||||||
|
return dcb_cmd_parse_dev(dcb, argc, argv,
|
||||||
|
dcb_cmd_buffer_set, dcb_buffer_help_set);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "What is \"%s\"?\n", *argv);
|
||||||
|
dcb_buffer_help();
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,435 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <linux/dcbnl.h>
|
||||||
|
|
||||||
|
#include "dcb.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
static void dcb_ets_help_set(void)
|
||||||
|
{
|
||||||
|
fprintf(stderr,
|
||||||
|
"Usage: dcb ets set dev STRING\n"
|
||||||
|
" [ willing { on | off } ]\n"
|
||||||
|
" [ { tc-tsa | reco-tc-tsa } TSA-MAP ]\n"
|
||||||
|
" [ { pg-bw | tc-bw | reco-tc-bw } BW-MAP ]\n"
|
||||||
|
" [ { prio-tc | reco-prio-tc } PRIO-MAP ]\n"
|
||||||
|
"\n"
|
||||||
|
" where TSA-MAP := [ TSA-MAP ] TSA-MAPPING\n"
|
||||||
|
" TSA-MAPPING := { all | TC }:{ strict | cbs | ets | vendor }\n"
|
||||||
|
" BW-MAP := [ BW-MAP ] BW-MAPPING\n"
|
||||||
|
" BW-MAPPING := { all | TC }:INTEGER\n"
|
||||||
|
" PRIO-MAP := [ PRIO-MAP ] PRIO-MAPPING\n"
|
||||||
|
" PRIO-MAPPING := { all | PRIO }:TC\n"
|
||||||
|
" TC := { 0 .. 7 }\n"
|
||||||
|
" PRIO := { 0 .. 7 }\n"
|
||||||
|
"\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dcb_ets_help_show(void)
|
||||||
|
{
|
||||||
|
fprintf(stderr,
|
||||||
|
"Usage: dcb ets show dev STRING\n"
|
||||||
|
" [ willing ] [ ets-cap ] [ cbs ] [ tc-tsa ]\n"
|
||||||
|
" [ reco-tc-tsa ] [ pg-bw ] [ tc-bw ] [ reco-tc-bw ]\n"
|
||||||
|
" [ prio-tc ] [ reco-prio-tc ]\n"
|
||||||
|
"\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dcb_ets_help(void)
|
||||||
|
{
|
||||||
|
fprintf(stderr,
|
||||||
|
"Usage: dcb ets help\n"
|
||||||
|
"\n"
|
||||||
|
);
|
||||||
|
dcb_ets_help_show();
|
||||||
|
dcb_ets_help_set();
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *const tsa_names[] = {
|
||||||
|
[IEEE_8021QAZ_TSA_STRICT] = "strict",
|
||||||
|
[IEEE_8021QAZ_TSA_CB_SHAPER] = "cbs",
|
||||||
|
[IEEE_8021QAZ_TSA_ETS] = "ets",
|
||||||
|
[IEEE_8021QAZ_TSA_VENDOR] = "vendor",
|
||||||
|
};
|
||||||
|
|
||||||
|
static int dcb_ets_parse_mapping_tc_tsa(__u32 key, char *value, void *data)
|
||||||
|
{
|
||||||
|
__u8 tsa;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
tsa = parse_one_of("TSA", value, tsa_names, ARRAY_SIZE(tsa_names), &ret);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return dcb_parse_mapping("TC", key, IEEE_8021QAZ_MAX_TCS - 1,
|
||||||
|
"TSA", tsa, -1U,
|
||||||
|
dcb_set_u8, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dcb_ets_parse_mapping_tc_bw(__u32 key, char *value, void *data)
|
||||||
|
{
|
||||||
|
__u8 bw;
|
||||||
|
|
||||||
|
if (get_u8(&bw, value, 0))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
return dcb_parse_mapping("TC", key, IEEE_8021QAZ_MAX_TCS - 1,
|
||||||
|
"BW", bw, 100,
|
||||||
|
dcb_set_u8, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dcb_ets_parse_mapping_prio_tc(unsigned int key, char *value, void *data)
|
||||||
|
{
|
||||||
|
__u8 tc;
|
||||||
|
|
||||||
|
if (get_u8(&tc, value, 0))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
return dcb_parse_mapping("PRIO", key, IEEE_8021QAZ_MAX_TCS - 1,
|
||||||
|
"TC", tc, IEEE_8021QAZ_MAX_TCS - 1,
|
||||||
|
dcb_set_u8, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dcb_print_array_tsa(const __u8 *array, size_t size)
|
||||||
|
{
|
||||||
|
dcb_print_array_kw(array, size, tsa_names, ARRAY_SIZE(tsa_names));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dcb_ets_print_willing(const struct ieee_ets *ets)
|
||||||
|
{
|
||||||
|
print_on_off(PRINT_ANY, "willing", "willing %s ", ets->willing);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dcb_ets_print_ets_cap(const struct ieee_ets *ets)
|
||||||
|
{
|
||||||
|
print_uint(PRINT_ANY, "ets_cap", "ets-cap %d ", ets->ets_cap);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dcb_ets_print_cbs(const struct ieee_ets *ets)
|
||||||
|
{
|
||||||
|
print_on_off(PRINT_ANY, "cbs", "cbs %s ", ets->cbs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dcb_ets_print_tc_bw(const struct ieee_ets *ets)
|
||||||
|
{
|
||||||
|
dcb_print_named_array("tc_bw", "tc-bw",
|
||||||
|
ets->tc_tx_bw, ARRAY_SIZE(ets->tc_tx_bw),
|
||||||
|
dcb_print_array_u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dcb_ets_print_pg_bw(const struct ieee_ets *ets)
|
||||||
|
{
|
||||||
|
dcb_print_named_array("pg_bw", "pg-bw",
|
||||||
|
ets->tc_rx_bw, ARRAY_SIZE(ets->tc_rx_bw),
|
||||||
|
dcb_print_array_u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dcb_ets_print_tc_tsa(const struct ieee_ets *ets)
|
||||||
|
{
|
||||||
|
dcb_print_named_array("tc_tsa", "tc-tsa",
|
||||||
|
ets->tc_tsa, ARRAY_SIZE(ets->tc_tsa),
|
||||||
|
dcb_print_array_tsa);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dcb_ets_print_prio_tc(const struct ieee_ets *ets)
|
||||||
|
{
|
||||||
|
dcb_print_named_array("prio_tc", "prio-tc",
|
||||||
|
ets->prio_tc, ARRAY_SIZE(ets->prio_tc),
|
||||||
|
dcb_print_array_u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dcb_ets_print_reco_tc_bw(const struct ieee_ets *ets)
|
||||||
|
{
|
||||||
|
dcb_print_named_array("reco_tc_bw", "reco-tc-bw",
|
||||||
|
ets->tc_reco_bw, ARRAY_SIZE(ets->tc_reco_bw),
|
||||||
|
dcb_print_array_u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dcb_ets_print_reco_tc_tsa(const struct ieee_ets *ets)
|
||||||
|
{
|
||||||
|
dcb_print_named_array("reco_tc_tsa", "reco-tc-tsa",
|
||||||
|
ets->tc_reco_tsa, ARRAY_SIZE(ets->tc_reco_tsa),
|
||||||
|
dcb_print_array_tsa);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dcb_ets_print_reco_prio_tc(const struct ieee_ets *ets)
|
||||||
|
{
|
||||||
|
dcb_print_named_array("reco_prio_tc", "reco-prio-tc",
|
||||||
|
ets->reco_prio_tc, ARRAY_SIZE(ets->reco_prio_tc),
|
||||||
|
dcb_print_array_u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dcb_ets_print(const struct ieee_ets *ets)
|
||||||
|
{
|
||||||
|
dcb_ets_print_willing(ets);
|
||||||
|
dcb_ets_print_ets_cap(ets);
|
||||||
|
dcb_ets_print_cbs(ets);
|
||||||
|
print_nl();
|
||||||
|
|
||||||
|
dcb_ets_print_tc_bw(ets);
|
||||||
|
print_nl();
|
||||||
|
|
||||||
|
dcb_ets_print_pg_bw(ets);
|
||||||
|
print_nl();
|
||||||
|
|
||||||
|
dcb_ets_print_tc_tsa(ets);
|
||||||
|
print_nl();
|
||||||
|
|
||||||
|
dcb_ets_print_prio_tc(ets);
|
||||||
|
print_nl();
|
||||||
|
|
||||||
|
dcb_ets_print_reco_tc_bw(ets);
|
||||||
|
print_nl();
|
||||||
|
|
||||||
|
dcb_ets_print_reco_tc_tsa(ets);
|
||||||
|
print_nl();
|
||||||
|
|
||||||
|
dcb_ets_print_reco_prio_tc(ets);
|
||||||
|
print_nl();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dcb_ets_get(struct dcb *dcb, const char *dev, struct ieee_ets *ets)
|
||||||
|
{
|
||||||
|
return dcb_get_attribute(dcb, dev, DCB_ATTR_IEEE_ETS, ets, sizeof(*ets));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dcb_ets_validate_bw(const __u8 bw[], const __u8 tsa[], const char *what)
|
||||||
|
{
|
||||||
|
bool has_ets = false;
|
||||||
|
unsigned int total = 0;
|
||||||
|
unsigned int tc;
|
||||||
|
|
||||||
|
for (tc = 0; tc < IEEE_8021QAZ_MAX_TCS; tc++) {
|
||||||
|
if (tsa[tc] == IEEE_8021QAZ_TSA_ETS) {
|
||||||
|
has_ets = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TC bandwidth is only intended for ETS, but 802.1Q-2018 only requires
|
||||||
|
* that the sum be 100, and individual entries 0..100. It explicitly
|
||||||
|
* notes that non-ETS TCs can have non-0 TC bandwidth during
|
||||||
|
* reconfiguration.
|
||||||
|
*/
|
||||||
|
for (tc = 0; tc < IEEE_8021QAZ_MAX_TCS; tc++) {
|
||||||
|
if (bw[tc] > 100) {
|
||||||
|
fprintf(stderr, "%d%% for TC %d of %s is not a valid bandwidth percentage, expected 0..100%%\n",
|
||||||
|
bw[tc], tc, what);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
total += bw[tc];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is what 802.1Q-2018 requires. */
|
||||||
|
if (total == 100)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* But this requirement does not make sense for all-strict
|
||||||
|
* configurations. Anything else than 0 does not make sense: either BW
|
||||||
|
* has not been reconfigured for the all-strict allocation yet, at which
|
||||||
|
* point we expect sum of 100. Or it has already been reconfigured, at
|
||||||
|
* which point accept 0.
|
||||||
|
*/
|
||||||
|
if (!has_ets && total == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fprintf(stderr, "Bandwidth percentages in %s sum to %d%%, expected %d%%\n",
|
||||||
|
what, total, has_ets ? 100 : 0);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dcb_ets_set(struct dcb *dcb, const char *dev, const struct ieee_ets *ets)
|
||||||
|
{
|
||||||
|
/* Do not validate pg-bw, which is not standard and has unclear
|
||||||
|
* meaning.
|
||||||
|
*/
|
||||||
|
if (dcb_ets_validate_bw(ets->tc_tx_bw, ets->tc_tsa, "tc-bw") ||
|
||||||
|
dcb_ets_validate_bw(ets->tc_reco_bw, ets->tc_reco_tsa, "reco-tc-bw"))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
return dcb_set_attribute(dcb, dev, DCB_ATTR_IEEE_ETS, ets, sizeof(*ets));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dcb_cmd_ets_set(struct dcb *dcb, const char *dev, int argc, char **argv)
|
||||||
|
{
|
||||||
|
struct ieee_ets ets;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!argc) {
|
||||||
|
dcb_ets_help_set();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = dcb_ets_get(dcb, dev, &ets);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (matches(*argv, "help") == 0) {
|
||||||
|
dcb_ets_help_set();
|
||||||
|
return 0;
|
||||||
|
} else if (matches(*argv, "willing") == 0) {
|
||||||
|
NEXT_ARG();
|
||||||
|
ets.willing = parse_on_off("willing", *argv, &ret);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
} else if (matches(*argv, "tc-tsa") == 0) {
|
||||||
|
NEXT_ARG();
|
||||||
|
ret = parse_mapping(&argc, &argv, true, &dcb_ets_parse_mapping_tc_tsa,
|
||||||
|
ets.tc_tsa);
|
||||||
|
if (ret) {
|
||||||
|
fprintf(stderr, "Invalid tc-tsa mapping %s\n", *argv);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
} else if (matches(*argv, "reco-tc-tsa") == 0) {
|
||||||
|
NEXT_ARG();
|
||||||
|
ret = parse_mapping(&argc, &argv, true, &dcb_ets_parse_mapping_tc_tsa,
|
||||||
|
ets.tc_reco_tsa);
|
||||||
|
if (ret) {
|
||||||
|
fprintf(stderr, "Invalid reco-tc-tsa mapping %s\n", *argv);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
} else if (matches(*argv, "tc-bw") == 0) {
|
||||||
|
NEXT_ARG();
|
||||||
|
ret = parse_mapping(&argc, &argv, true, &dcb_ets_parse_mapping_tc_bw,
|
||||||
|
ets.tc_tx_bw);
|
||||||
|
if (ret) {
|
||||||
|
fprintf(stderr, "Invalid tc-bw mapping %s\n", *argv);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
} else if (matches(*argv, "pg-bw") == 0) {
|
||||||
|
NEXT_ARG();
|
||||||
|
ret = parse_mapping(&argc, &argv, true, &dcb_ets_parse_mapping_tc_bw,
|
||||||
|
ets.tc_rx_bw);
|
||||||
|
if (ret) {
|
||||||
|
fprintf(stderr, "Invalid pg-bw mapping %s\n", *argv);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
} else if (matches(*argv, "reco-tc-bw") == 0) {
|
||||||
|
NEXT_ARG();
|
||||||
|
ret = parse_mapping(&argc, &argv, true, &dcb_ets_parse_mapping_tc_bw,
|
||||||
|
ets.tc_reco_bw);
|
||||||
|
if (ret) {
|
||||||
|
fprintf(stderr, "Invalid reco-tc-bw mapping %s\n", *argv);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
} else if (matches(*argv, "prio-tc") == 0) {
|
||||||
|
NEXT_ARG();
|
||||||
|
ret = parse_mapping(&argc, &argv, true, &dcb_ets_parse_mapping_prio_tc,
|
||||||
|
ets.prio_tc);
|
||||||
|
if (ret) {
|
||||||
|
fprintf(stderr, "Invalid prio-tc mapping %s\n", *argv);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
} else if (matches(*argv, "reco-prio-tc") == 0) {
|
||||||
|
NEXT_ARG();
|
||||||
|
ret = parse_mapping(&argc, &argv, true, &dcb_ets_parse_mapping_prio_tc,
|
||||||
|
ets.reco_prio_tc);
|
||||||
|
if (ret) {
|
||||||
|
fprintf(stderr, "Invalid reco-prio-tc mapping %s\n", *argv);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "What is \"%s\"?\n", *argv);
|
||||||
|
dcb_ets_help_set();
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
NEXT_ARG_FWD();
|
||||||
|
} while (argc > 0);
|
||||||
|
|
||||||
|
return dcb_ets_set(dcb, dev, &ets);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dcb_cmd_ets_show(struct dcb *dcb, const char *dev, int argc, char **argv)
|
||||||
|
{
|
||||||
|
struct ieee_ets ets;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = dcb_ets_get(dcb, dev, &ets);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
open_json_object(NULL);
|
||||||
|
|
||||||
|
if (!argc) {
|
||||||
|
dcb_ets_print(&ets);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (matches(*argv, "help") == 0) {
|
||||||
|
dcb_ets_help_show();
|
||||||
|
return 0;
|
||||||
|
} else if (matches(*argv, "willing") == 0) {
|
||||||
|
dcb_ets_print_willing(&ets);
|
||||||
|
print_nl();
|
||||||
|
} else if (matches(*argv, "ets-cap") == 0) {
|
||||||
|
dcb_ets_print_ets_cap(&ets);
|
||||||
|
print_nl();
|
||||||
|
} else if (matches(*argv, "cbs") == 0) {
|
||||||
|
dcb_ets_print_cbs(&ets);
|
||||||
|
print_nl();
|
||||||
|
} else if (matches(*argv, "tc-tsa") == 0) {
|
||||||
|
dcb_ets_print_tc_tsa(&ets);
|
||||||
|
print_nl();
|
||||||
|
} else if (matches(*argv, "reco-tc-tsa") == 0) {
|
||||||
|
dcb_ets_print_reco_tc_tsa(&ets);
|
||||||
|
print_nl();
|
||||||
|
} else if (matches(*argv, "tc-bw") == 0) {
|
||||||
|
dcb_ets_print_tc_bw(&ets);
|
||||||
|
print_nl();
|
||||||
|
} else if (matches(*argv, "pg-bw") == 0) {
|
||||||
|
dcb_ets_print_pg_bw(&ets);
|
||||||
|
print_nl();
|
||||||
|
} else if (matches(*argv, "reco-tc-bw") == 0) {
|
||||||
|
dcb_ets_print_reco_tc_bw(&ets);
|
||||||
|
print_nl();
|
||||||
|
} else if (matches(*argv, "prio-tc") == 0) {
|
||||||
|
dcb_ets_print_prio_tc(&ets);
|
||||||
|
print_nl();
|
||||||
|
} else if (matches(*argv, "reco-prio-tc") == 0) {
|
||||||
|
dcb_ets_print_reco_prio_tc(&ets);
|
||||||
|
print_nl();
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "What is \"%s\"?\n", *argv);
|
||||||
|
dcb_ets_help_show();
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
NEXT_ARG_FWD();
|
||||||
|
} while (argc > 0);
|
||||||
|
|
||||||
|
out:
|
||||||
|
close_json_object();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dcb_cmd_ets(struct dcb *dcb, int argc, char **argv)
|
||||||
|
{
|
||||||
|
if (!argc || matches(*argv, "help") == 0) {
|
||||||
|
dcb_ets_help();
|
||||||
|
return 0;
|
||||||
|
} else if (matches(*argv, "show") == 0) {
|
||||||
|
NEXT_ARG_FWD();
|
||||||
|
return dcb_cmd_parse_dev(dcb, argc, argv, dcb_cmd_ets_show, dcb_ets_help_show);
|
||||||
|
} else if (matches(*argv, "set") == 0) {
|
||||||
|
NEXT_ARG_FWD();
|
||||||
|
return dcb_cmd_parse_dev(dcb, argc, argv, dcb_cmd_ets_set, dcb_ets_help_set);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "What is \"%s\"?\n", *argv);
|
||||||
|
dcb_ets_help();
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,182 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <linux/dcbnl.h>
|
||||||
|
|
||||||
|
#include "dcb.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
static void dcb_maxrate_help_set(void)
|
||||||
|
{
|
||||||
|
fprintf(stderr,
|
||||||
|
"Usage: dcb maxrate set dev STRING\n"
|
||||||
|
" [ tc-maxrate RATE-MAP ]\n"
|
||||||
|
"\n"
|
||||||
|
" where RATE-MAP := [ RATE-MAP ] RATE-MAPPING\n"
|
||||||
|
" RATE-MAPPING := { all | TC }:RATE\n"
|
||||||
|
" TC := { 0 .. 7 }\n"
|
||||||
|
"\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dcb_maxrate_help_show(void)
|
||||||
|
{
|
||||||
|
fprintf(stderr,
|
||||||
|
"Usage: dcb [ -i ] maxrate show dev STRING\n"
|
||||||
|
" [ tc-maxrate ]\n"
|
||||||
|
"\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dcb_maxrate_help(void)
|
||||||
|
{
|
||||||
|
fprintf(stderr,
|
||||||
|
"Usage: dcb maxrate help\n"
|
||||||
|
"\n"
|
||||||
|
);
|
||||||
|
dcb_maxrate_help_show();
|
||||||
|
dcb_maxrate_help_set();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dcb_maxrate_parse_mapping_tc_maxrate(__u32 key, char *value, void *data)
|
||||||
|
{
|
||||||
|
__u64 rate;
|
||||||
|
|
||||||
|
if (get_rate64(&rate, value))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
return dcb_parse_mapping("TC", key, IEEE_8021QAZ_MAX_TCS - 1,
|
||||||
|
"RATE", rate, -1,
|
||||||
|
dcb_set_u64, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dcb_maxrate_print_tc_maxrate(struct dcb *dcb, const struct ieee_maxrate *maxrate)
|
||||||
|
{
|
||||||
|
size_t size = ARRAY_SIZE(maxrate->tc_maxrate);
|
||||||
|
SPRINT_BUF(b);
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
open_json_array(PRINT_JSON, "tc_maxrate");
|
||||||
|
print_string(PRINT_FP, NULL, "tc-maxrate ", NULL);
|
||||||
|
|
||||||
|
for (i = 0; i < size; i++) {
|
||||||
|
snprintf(b, sizeof(b), "%zd:%%s ", i);
|
||||||
|
print_rate(dcb->use_iec, PRINT_ANY, NULL, b, maxrate->tc_maxrate[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
close_json_array(PRINT_JSON, "tc_maxrate");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dcb_maxrate_print(struct dcb *dcb, const struct ieee_maxrate *maxrate)
|
||||||
|
{
|
||||||
|
dcb_maxrate_print_tc_maxrate(dcb, maxrate);
|
||||||
|
print_nl();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dcb_maxrate_get(struct dcb *dcb, const char *dev, struct ieee_maxrate *maxrate)
|
||||||
|
{
|
||||||
|
return dcb_get_attribute(dcb, dev, DCB_ATTR_IEEE_MAXRATE, maxrate, sizeof(*maxrate));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dcb_maxrate_set(struct dcb *dcb, const char *dev, const struct ieee_maxrate *maxrate)
|
||||||
|
{
|
||||||
|
return dcb_set_attribute(dcb, dev, DCB_ATTR_IEEE_MAXRATE, maxrate, sizeof(*maxrate));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dcb_cmd_maxrate_set(struct dcb *dcb, const char *dev, int argc, char **argv)
|
||||||
|
{
|
||||||
|
struct ieee_maxrate maxrate;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!argc) {
|
||||||
|
dcb_maxrate_help_set();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = dcb_maxrate_get(dcb, dev, &maxrate);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (matches(*argv, "help") == 0) {
|
||||||
|
dcb_maxrate_help_set();
|
||||||
|
return 0;
|
||||||
|
} else if (matches(*argv, "tc-maxrate") == 0) {
|
||||||
|
NEXT_ARG();
|
||||||
|
ret = parse_mapping(&argc, &argv, true,
|
||||||
|
&dcb_maxrate_parse_mapping_tc_maxrate, &maxrate);
|
||||||
|
if (ret) {
|
||||||
|
fprintf(stderr, "Invalid mapping %s\n", *argv);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "What is \"%s\"?\n", *argv);
|
||||||
|
dcb_maxrate_help_set();
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
NEXT_ARG_FWD();
|
||||||
|
} while (argc > 0);
|
||||||
|
|
||||||
|
return dcb_maxrate_set(dcb, dev, &maxrate);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dcb_cmd_maxrate_show(struct dcb *dcb, const char *dev, int argc, char **argv)
|
||||||
|
{
|
||||||
|
struct ieee_maxrate maxrate;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = dcb_maxrate_get(dcb, dev, &maxrate);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
open_json_object(NULL);
|
||||||
|
|
||||||
|
if (!argc) {
|
||||||
|
dcb_maxrate_print(dcb, &maxrate);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (matches(*argv, "help") == 0) {
|
||||||
|
dcb_maxrate_help_show();
|
||||||
|
return 0;
|
||||||
|
} else if (matches(*argv, "tc-maxrate") == 0) {
|
||||||
|
dcb_maxrate_print_tc_maxrate(dcb, &maxrate);
|
||||||
|
print_nl();
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "What is \"%s\"?\n", *argv);
|
||||||
|
dcb_maxrate_help_show();
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
NEXT_ARG_FWD();
|
||||||
|
} while (argc > 0);
|
||||||
|
|
||||||
|
out:
|
||||||
|
close_json_object();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dcb_cmd_maxrate(struct dcb *dcb, int argc, char **argv)
|
||||||
|
{
|
||||||
|
if (!argc || matches(*argv, "help") == 0) {
|
||||||
|
dcb_maxrate_help();
|
||||||
|
return 0;
|
||||||
|
} else if (matches(*argv, "show") == 0) {
|
||||||
|
NEXT_ARG_FWD();
|
||||||
|
return dcb_cmd_parse_dev(dcb, argc, argv,
|
||||||
|
dcb_cmd_maxrate_show, dcb_maxrate_help_show);
|
||||||
|
} else if (matches(*argv, "set") == 0) {
|
||||||
|
NEXT_ARG_FWD();
|
||||||
|
return dcb_cmd_parse_dev(dcb, argc, argv,
|
||||||
|
dcb_cmd_maxrate_set, dcb_maxrate_help_set);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "What is \"%s\"?\n", *argv);
|
||||||
|
dcb_maxrate_help();
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,286 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <linux/dcbnl.h>
|
||||||
|
|
||||||
|
#include "dcb.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
static void dcb_pfc_help_set(void)
|
||||||
|
{
|
||||||
|
fprintf(stderr,
|
||||||
|
"Usage: dcb pfc set dev STRING\n"
|
||||||
|
" [ prio-pfc PFC-MAP ]\n"
|
||||||
|
" [ macsec-bypass { on | off } ]\n"
|
||||||
|
" [ delay INTEGER ]\n"
|
||||||
|
"\n"
|
||||||
|
" where PFC-MAP := [ PFC-MAP ] PFC-MAPPING\n"
|
||||||
|
" PFC-MAPPING := { all | TC }:PFC\n"
|
||||||
|
" TC := { 0 .. 7 }\n"
|
||||||
|
" PFC := { on | off }\n"
|
||||||
|
"\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dcb_pfc_help_show(void)
|
||||||
|
{
|
||||||
|
fprintf(stderr,
|
||||||
|
"Usage: dcb [ -s ] pfc show dev STRING\n"
|
||||||
|
" [ pfc-cap ] [ prio-pfc ] [ macsec-bypass ]\n"
|
||||||
|
" [ delay ] [ requests ] [ indications ]\n"
|
||||||
|
"\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dcb_pfc_help(void)
|
||||||
|
{
|
||||||
|
fprintf(stderr,
|
||||||
|
"Usage: dcb pfc help\n"
|
||||||
|
"\n"
|
||||||
|
);
|
||||||
|
dcb_pfc_help_show();
|
||||||
|
dcb_pfc_help_set();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dcb_pfc_to_array(__u8 array[IEEE_8021QAZ_MAX_TCS], __u8 pfc_en)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
|
||||||
|
array[i] = !!(pfc_en & (1 << i));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dcb_pfc_from_array(__u8 array[IEEE_8021QAZ_MAX_TCS], __u8 *pfc_en_p)
|
||||||
|
{
|
||||||
|
__u8 pfc_en = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
|
||||||
|
if (array[i])
|
||||||
|
pfc_en |= 1 << i;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pfc_en_p = pfc_en;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dcb_pfc_parse_mapping_prio_pfc(__u32 key, char *value, void *data)
|
||||||
|
{
|
||||||
|
struct ieee_pfc *pfc = data;
|
||||||
|
__u8 pfc_en[IEEE_8021QAZ_MAX_TCS];
|
||||||
|
bool enabled;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
dcb_pfc_to_array(pfc_en, pfc->pfc_en);
|
||||||
|
|
||||||
|
enabled = parse_on_off("PFC", value, &ret);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = dcb_parse_mapping("PRIO", key, IEEE_8021QAZ_MAX_TCS - 1,
|
||||||
|
"PFC", enabled, -1,
|
||||||
|
dcb_set_u8, pfc_en);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
dcb_pfc_from_array(pfc_en, &pfc->pfc_en);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dcb_pfc_print_pfc_cap(const struct ieee_pfc *pfc)
|
||||||
|
{
|
||||||
|
print_uint(PRINT_ANY, "pfc_cap", "pfc-cap %d ", pfc->pfc_cap);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dcb_pfc_print_macsec_bypass(const struct ieee_pfc *pfc)
|
||||||
|
{
|
||||||
|
print_on_off(PRINT_ANY, "macsec_bypass", "macsec-bypass %s ", pfc->mbc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dcb_pfc_print_delay(const struct ieee_pfc *pfc)
|
||||||
|
{
|
||||||
|
print_uint(PRINT_ANY, "delay", "delay %d ", pfc->delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dcb_pfc_print_prio_pfc(const struct ieee_pfc *pfc)
|
||||||
|
{
|
||||||
|
__u8 pfc_en[IEEE_8021QAZ_MAX_TCS];
|
||||||
|
|
||||||
|
dcb_pfc_to_array(pfc_en, pfc->pfc_en);
|
||||||
|
dcb_print_named_array("prio_pfc", "prio-pfc",
|
||||||
|
pfc_en, ARRAY_SIZE(pfc_en), &dcb_print_array_on_off);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dcb_pfc_print_requests(const struct ieee_pfc *pfc)
|
||||||
|
{
|
||||||
|
open_json_array(PRINT_JSON, "requests");
|
||||||
|
print_string(PRINT_FP, NULL, "requests ", NULL);
|
||||||
|
dcb_print_array_u64(pfc->requests, ARRAY_SIZE(pfc->requests));
|
||||||
|
close_json_array(PRINT_JSON, "requests");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dcb_pfc_print_indications(const struct ieee_pfc *pfc)
|
||||||
|
{
|
||||||
|
open_json_array(PRINT_JSON, "indications");
|
||||||
|
print_string(PRINT_FP, NULL, "indications ", NULL);
|
||||||
|
dcb_print_array_u64(pfc->indications, ARRAY_SIZE(pfc->indications));
|
||||||
|
close_json_array(PRINT_JSON, "indications");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dcb_pfc_print(const struct dcb *dcb, const struct ieee_pfc *pfc)
|
||||||
|
{
|
||||||
|
dcb_pfc_print_pfc_cap(pfc);
|
||||||
|
dcb_pfc_print_macsec_bypass(pfc);
|
||||||
|
dcb_pfc_print_delay(pfc);
|
||||||
|
print_nl();
|
||||||
|
|
||||||
|
dcb_pfc_print_prio_pfc(pfc);
|
||||||
|
print_nl();
|
||||||
|
|
||||||
|
if (dcb->stats) {
|
||||||
|
dcb_pfc_print_requests(pfc);
|
||||||
|
print_nl();
|
||||||
|
|
||||||
|
dcb_pfc_print_indications(pfc);
|
||||||
|
print_nl();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dcb_pfc_get(struct dcb *dcb, const char *dev, struct ieee_pfc *pfc)
|
||||||
|
{
|
||||||
|
return dcb_get_attribute(dcb, dev, DCB_ATTR_IEEE_PFC, pfc, sizeof(*pfc));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dcb_pfc_set(struct dcb *dcb, const char *dev, const struct ieee_pfc *pfc)
|
||||||
|
{
|
||||||
|
return dcb_set_attribute(dcb, dev, DCB_ATTR_IEEE_PFC, pfc, sizeof(*pfc));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dcb_cmd_pfc_set(struct dcb *dcb, const char *dev, int argc, char **argv)
|
||||||
|
{
|
||||||
|
struct ieee_pfc pfc;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!argc) {
|
||||||
|
dcb_pfc_help_set();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = dcb_pfc_get(dcb, dev, &pfc);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (matches(*argv, "help") == 0) {
|
||||||
|
dcb_pfc_help_set();
|
||||||
|
return 0;
|
||||||
|
} else if (matches(*argv, "prio-pfc") == 0) {
|
||||||
|
NEXT_ARG();
|
||||||
|
ret = parse_mapping(&argc, &argv, true,
|
||||||
|
&dcb_pfc_parse_mapping_prio_pfc, &pfc);
|
||||||
|
if (ret) {
|
||||||
|
fprintf(stderr, "Invalid pfc mapping %s\n", *argv);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
} else if (matches(*argv, "macsec-bypass") == 0) {
|
||||||
|
NEXT_ARG();
|
||||||
|
pfc.mbc = parse_on_off("macsec-bypass", *argv, &ret);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
} else if (matches(*argv, "delay") == 0) {
|
||||||
|
NEXT_ARG();
|
||||||
|
/* Do not support the size notations for delay.
|
||||||
|
* Delay is specified in "bit times", not bits, so
|
||||||
|
* it is not applicable. At the same time it would
|
||||||
|
* be confusing that 10Kbit does not mean 10240,
|
||||||
|
* but 1280.
|
||||||
|
*/
|
||||||
|
if (get_u16(&pfc.delay, *argv, 0)) {
|
||||||
|
fprintf(stderr, "Invalid delay `%s', expected an integer 0..65535\n",
|
||||||
|
*argv);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "What is \"%s\"?\n", *argv);
|
||||||
|
dcb_pfc_help_set();
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
NEXT_ARG_FWD();
|
||||||
|
} while (argc > 0);
|
||||||
|
|
||||||
|
return dcb_pfc_set(dcb, dev, &pfc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dcb_cmd_pfc_show(struct dcb *dcb, const char *dev, int argc, char **argv)
|
||||||
|
{
|
||||||
|
struct ieee_pfc pfc;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = dcb_pfc_get(dcb, dev, &pfc);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
open_json_object(NULL);
|
||||||
|
|
||||||
|
if (!argc) {
|
||||||
|
dcb_pfc_print(dcb, &pfc);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (matches(*argv, "help") == 0) {
|
||||||
|
dcb_pfc_help_show();
|
||||||
|
return 0;
|
||||||
|
} else if (matches(*argv, "prio-pfc") == 0) {
|
||||||
|
dcb_pfc_print_prio_pfc(&pfc);
|
||||||
|
print_nl();
|
||||||
|
} else if (matches(*argv, "pfc-cap") == 0) {
|
||||||
|
dcb_pfc_print_pfc_cap(&pfc);
|
||||||
|
print_nl();
|
||||||
|
} else if (matches(*argv, "macsec-bypass") == 0) {
|
||||||
|
dcb_pfc_print_macsec_bypass(&pfc);
|
||||||
|
print_nl();
|
||||||
|
} else if (matches(*argv, "delay") == 0) {
|
||||||
|
dcb_pfc_print_delay(&pfc);
|
||||||
|
print_nl();
|
||||||
|
} else if (matches(*argv, "requests") == 0) {
|
||||||
|
dcb_pfc_print_requests(&pfc);
|
||||||
|
print_nl();
|
||||||
|
} else if (matches(*argv, "indications") == 0) {
|
||||||
|
dcb_pfc_print_indications(&pfc);
|
||||||
|
print_nl();
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "What is \"%s\"?\n", *argv);
|
||||||
|
dcb_pfc_help_show();
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
NEXT_ARG_FWD();
|
||||||
|
} while (argc > 0);
|
||||||
|
|
||||||
|
out:
|
||||||
|
close_json_object();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dcb_cmd_pfc(struct dcb *dcb, int argc, char **argv)
|
||||||
|
{
|
||||||
|
if (!argc || matches(*argv, "help") == 0) {
|
||||||
|
dcb_pfc_help();
|
||||||
|
return 0;
|
||||||
|
} else if (matches(*argv, "show") == 0) {
|
||||||
|
NEXT_ARG_FWD();
|
||||||
|
return dcb_cmd_parse_dev(dcb, argc, argv,
|
||||||
|
dcb_cmd_pfc_show, dcb_pfc_help_show);
|
||||||
|
} else if (matches(*argv, "set") == 0) {
|
||||||
|
NEXT_ARG_FWD();
|
||||||
|
return dcb_cmd_parse_dev(dcb, argc, argv,
|
||||||
|
dcb_cmd_pfc_set, dcb_pfc_help_set);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "What is \"%s\"?\n", *argv);
|
||||||
|
dcb_pfc_help();
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -12,7 +12,7 @@ endif
|
||||||
|
|
||||||
all: $(TARGETS) $(LIBS)
|
all: $(TARGETS) $(LIBS)
|
||||||
|
|
||||||
devlink: $(DEVLINKOBJ)
|
devlink: $(DEVLINKOBJ) $(LIBNETLINK)
|
||||||
$(QUIET_LINK)$(CC) $^ $(LDFLAGS) $(LDLIBS) -o $@
|
$(QUIET_LINK)$(CC) $^ $(LDFLAGS) $(LDLIBS) -o $@
|
||||||
|
|
||||||
install: all
|
install: all
|
||||||
|
|
|
||||||
|
|
@ -304,6 +304,8 @@ static void ifname_map_free(struct ifname_map *ifname_map)
|
||||||
#define DL_OPT_HEALTH_REPORTER_AUTO_DUMP BIT(37)
|
#define DL_OPT_HEALTH_REPORTER_AUTO_DUMP BIT(37)
|
||||||
#define DL_OPT_PORT_FUNCTION_HW_ADDR BIT(38)
|
#define DL_OPT_PORT_FUNCTION_HW_ADDR BIT(38)
|
||||||
#define DL_OPT_FLASH_OVERWRITE BIT(39)
|
#define DL_OPT_FLASH_OVERWRITE BIT(39)
|
||||||
|
#define DL_OPT_RELOAD_ACTION BIT(40)
|
||||||
|
#define DL_OPT_RELOAD_LIMIT BIT(41)
|
||||||
|
|
||||||
struct dl_opts {
|
struct dl_opts {
|
||||||
uint64_t present; /* flags of present items */
|
uint64_t present; /* flags of present items */
|
||||||
|
|
@ -352,6 +354,8 @@ struct dl_opts {
|
||||||
char port_function_hw_addr[MAX_ADDR_LEN];
|
char port_function_hw_addr[MAX_ADDR_LEN];
|
||||||
uint32_t port_function_hw_addr_len;
|
uint32_t port_function_hw_addr_len;
|
||||||
uint32_t overwrite_mask;
|
uint32_t overwrite_mask;
|
||||||
|
enum devlink_reload_action reload_action;
|
||||||
|
enum devlink_reload_limit reload_limit;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct dl {
|
struct dl {
|
||||||
|
|
@ -678,6 +682,15 @@ static const enum mnl_attr_data_type devlink_policy[DEVLINK_ATTR_MAX + 1] = {
|
||||||
[DEVLINK_ATTR_TRAP_METADATA] = MNL_TYPE_NESTED,
|
[DEVLINK_ATTR_TRAP_METADATA] = MNL_TYPE_NESTED,
|
||||||
[DEVLINK_ATTR_TRAP_GROUP_NAME] = MNL_TYPE_STRING,
|
[DEVLINK_ATTR_TRAP_GROUP_NAME] = MNL_TYPE_STRING,
|
||||||
[DEVLINK_ATTR_RELOAD_FAILED] = MNL_TYPE_U8,
|
[DEVLINK_ATTR_RELOAD_FAILED] = MNL_TYPE_U8,
|
||||||
|
[DEVLINK_ATTR_DEV_STATS] = MNL_TYPE_NESTED,
|
||||||
|
[DEVLINK_ATTR_RELOAD_STATS] = MNL_TYPE_NESTED,
|
||||||
|
[DEVLINK_ATTR_RELOAD_STATS_ENTRY] = MNL_TYPE_NESTED,
|
||||||
|
[DEVLINK_ATTR_RELOAD_ACTION] = MNL_TYPE_U8,
|
||||||
|
[DEVLINK_ATTR_RELOAD_STATS_LIMIT] = MNL_TYPE_U8,
|
||||||
|
[DEVLINK_ATTR_RELOAD_STATS_VALUE] = MNL_TYPE_U32,
|
||||||
|
[DEVLINK_ATTR_REMOTE_RELOAD_STATS] = MNL_TYPE_NESTED,
|
||||||
|
[DEVLINK_ATTR_RELOAD_ACTION_INFO] = MNL_TYPE_NESTED,
|
||||||
|
[DEVLINK_ATTR_RELOAD_ACTION_STATS] = MNL_TYPE_NESTED,
|
||||||
[DEVLINK_ATTR_TRAP_POLICER_ID] = MNL_TYPE_U32,
|
[DEVLINK_ATTR_TRAP_POLICER_ID] = MNL_TYPE_U32,
|
||||||
[DEVLINK_ATTR_TRAP_POLICER_RATE] = MNL_TYPE_U64,
|
[DEVLINK_ATTR_TRAP_POLICER_RATE] = MNL_TYPE_U64,
|
||||||
[DEVLINK_ATTR_TRAP_POLICER_BURST] = MNL_TYPE_U64,
|
[DEVLINK_ATTR_TRAP_POLICER_BURST] = MNL_TYPE_U64,
|
||||||
|
|
@ -1344,6 +1357,32 @@ static int hw_addr_parse(const char *addrstr, char *hw_addr, uint32_t *len)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int reload_action_get(struct dl *dl, const char *actionstr,
|
||||||
|
enum devlink_reload_action *action)
|
||||||
|
{
|
||||||
|
if (strcmp(actionstr, "driver_reinit") == 0) {
|
||||||
|
*action = DEVLINK_RELOAD_ACTION_DRIVER_REINIT;
|
||||||
|
} else if (strcmp(actionstr, "fw_activate") == 0) {
|
||||||
|
*action = DEVLINK_RELOAD_ACTION_FW_ACTIVATE;
|
||||||
|
} else {
|
||||||
|
pr_err("Unknown reload action \"%s\"\n", actionstr);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int reload_limit_get(struct dl *dl, const char *limitstr,
|
||||||
|
enum devlink_reload_limit *limit)
|
||||||
|
{
|
||||||
|
if (strcmp(limitstr, "no_reset") == 0) {
|
||||||
|
*limit = DEVLINK_RELOAD_LIMIT_NO_RESET;
|
||||||
|
} else {
|
||||||
|
pr_err("Unknown reload limit \"%s\"\n", limitstr);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
struct dl_args_metadata {
|
struct dl_args_metadata {
|
||||||
uint64_t o_flag;
|
uint64_t o_flag;
|
||||||
char err_msg[DL_ARGS_REQUIRED_MAX_ERR_LEN];
|
char err_msg[DL_ARGS_REQUIRED_MAX_ERR_LEN];
|
||||||
|
|
@ -1730,6 +1769,30 @@ static int dl_argv_parse(struct dl *dl, uint64_t o_required,
|
||||||
opts->netns_is_pid = true;
|
opts->netns_is_pid = true;
|
||||||
}
|
}
|
||||||
o_found |= DL_OPT_NETNS;
|
o_found |= DL_OPT_NETNS;
|
||||||
|
} else if (dl_argv_match(dl, "action") &&
|
||||||
|
(o_all & DL_OPT_RELOAD_ACTION)) {
|
||||||
|
const char *actionstr;
|
||||||
|
|
||||||
|
dl_arg_inc(dl);
|
||||||
|
err = dl_argv_str(dl, &actionstr);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
err = reload_action_get(dl, actionstr, &opts->reload_action);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
o_found |= DL_OPT_RELOAD_ACTION;
|
||||||
|
} else if (dl_argv_match(dl, "limit") &&
|
||||||
|
(o_all & DL_OPT_RELOAD_LIMIT)) {
|
||||||
|
const char *limitstr;
|
||||||
|
|
||||||
|
dl_arg_inc(dl);
|
||||||
|
err = dl_argv_str(dl, &limitstr);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
err = reload_limit_get(dl, limitstr, &opts->reload_limit);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
o_found |= DL_OPT_RELOAD_LIMIT;
|
||||||
} else if (dl_argv_match(dl, "policer") &&
|
} else if (dl_argv_match(dl, "policer") &&
|
||||||
(o_all & DL_OPT_TRAP_POLICER_ID)) {
|
(o_all & DL_OPT_TRAP_POLICER_ID)) {
|
||||||
dl_arg_inc(dl);
|
dl_arg_inc(dl);
|
||||||
|
|
@ -1810,6 +1873,16 @@ dl_flash_update_overwrite_put(struct nlmsghdr *nlh, const struct dl_opts *opts)
|
||||||
sizeof(overwrite_mask), &overwrite_mask);
|
sizeof(overwrite_mask), &overwrite_mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
dl_reload_limits_put(struct nlmsghdr *nlh, const struct dl_opts *opts)
|
||||||
|
{
|
||||||
|
struct nla_bitfield32 limits;
|
||||||
|
|
||||||
|
limits.selector = DEVLINK_RELOAD_LIMITS_VALID_MASK;
|
||||||
|
limits.value = BIT(opts->reload_limit);
|
||||||
|
mnl_attr_put(nlh, DEVLINK_ATTR_RELOAD_LIMITS, sizeof(limits), &limits);
|
||||||
|
}
|
||||||
|
|
||||||
static void dl_opts_put(struct nlmsghdr *nlh, struct dl *dl)
|
static void dl_opts_put(struct nlmsghdr *nlh, struct dl *dl)
|
||||||
{
|
{
|
||||||
struct dl_opts *opts = &dl->opts;
|
struct dl_opts *opts = &dl->opts;
|
||||||
|
|
@ -1926,6 +1999,11 @@ static void dl_opts_put(struct nlmsghdr *nlh, struct dl *dl)
|
||||||
opts->netns_is_pid ? DEVLINK_ATTR_NETNS_PID :
|
opts->netns_is_pid ? DEVLINK_ATTR_NETNS_PID :
|
||||||
DEVLINK_ATTR_NETNS_FD,
|
DEVLINK_ATTR_NETNS_FD,
|
||||||
opts->netns);
|
opts->netns);
|
||||||
|
if (opts->present & DL_OPT_RELOAD_ACTION)
|
||||||
|
mnl_attr_put_u8(nlh, DEVLINK_ATTR_RELOAD_ACTION,
|
||||||
|
opts->reload_action);
|
||||||
|
if (opts->present & DL_OPT_RELOAD_LIMIT)
|
||||||
|
dl_reload_limits_put(nlh, opts);
|
||||||
if (opts->present & DL_OPT_TRAP_POLICER_ID)
|
if (opts->present & DL_OPT_TRAP_POLICER_ID)
|
||||||
mnl_attr_put_u32(nlh, DEVLINK_ATTR_TRAP_POLICER_ID,
|
mnl_attr_put_u32(nlh, DEVLINK_ATTR_TRAP_POLICER_ID,
|
||||||
opts->trap_policer_id);
|
opts->trap_policer_id);
|
||||||
|
|
@ -1998,6 +2076,7 @@ static void cmd_dev_help(void)
|
||||||
pr_err(" devlink dev param set DEV name PARAMETER value VALUE cmode { permanent | driverinit | runtime }\n");
|
pr_err(" devlink dev param set DEV name PARAMETER value VALUE cmode { permanent | driverinit | runtime }\n");
|
||||||
pr_err(" devlink dev param show [DEV name PARAMETER]\n");
|
pr_err(" devlink dev param show [DEV name PARAMETER]\n");
|
||||||
pr_err(" devlink dev reload DEV [ netns { PID | NAME | ID } ]\n");
|
pr_err(" devlink dev reload DEV [ netns { PID | NAME | ID } ]\n");
|
||||||
|
pr_err(" [ action { driver_reinit | fw_activate } ] [ limit no_reset ]\n");
|
||||||
pr_err(" devlink dev info [ DEV ]\n");
|
pr_err(" devlink dev info [ DEV ]\n");
|
||||||
pr_err(" devlink dev flash DEV file PATH [ component NAME ] [ overwrite SECTION ]\n");
|
pr_err(" devlink dev flash DEV file PATH [ component NAME ] [ overwrite SECTION ]\n");
|
||||||
}
|
}
|
||||||
|
|
@ -2289,6 +2368,30 @@ static const char *param_cmode_name(uint8_t cmode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *reload_action_name(uint8_t reload_action)
|
||||||
|
{
|
||||||
|
switch (reload_action) {
|
||||||
|
case DEVLINK_RELOAD_ACTION_DRIVER_REINIT:
|
||||||
|
return "driver_reinit";
|
||||||
|
case DEVLINK_RELOAD_ACTION_FW_ACTIVATE:
|
||||||
|
return "fw_activate";
|
||||||
|
default:
|
||||||
|
return "<unknown reload action>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *reload_limit_name(uint8_t reload_limit)
|
||||||
|
{
|
||||||
|
switch (reload_limit) {
|
||||||
|
case DEVLINK_RELOAD_LIMIT_UNSPEC:
|
||||||
|
return "unspecified";
|
||||||
|
case DEVLINK_RELOAD_LIMIT_NO_RESET:
|
||||||
|
return "no_reset";
|
||||||
|
default:
|
||||||
|
return "<unknown reload action>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static const char *eswitch_mode_name(uint32_t mode)
|
static const char *eswitch_mode_name(uint32_t mode)
|
||||||
{
|
{
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
|
|
@ -2893,29 +2996,119 @@ static int cmd_dev_param(struct dl *dl)
|
||||||
pr_err("Command \"%s\" not found\n", dl_argv(dl));
|
pr_err("Command \"%s\" not found\n", dl_argv(dl));
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
static int cmd_dev_show_cb(const struct nlmsghdr *nlh, void *data)
|
|
||||||
{
|
|
||||||
struct dl *dl = data;
|
|
||||||
struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
|
|
||||||
struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
|
|
||||||
uint8_t reload_failed = 0;
|
|
||||||
|
|
||||||
mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
|
static void pr_out_action_stats(struct dl *dl, struct nlattr *action_stats)
|
||||||
if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME])
|
{
|
||||||
return MNL_CB_ERROR;
|
struct nlattr *tb_stats_entry[DEVLINK_ATTR_MAX + 1] = {};
|
||||||
|
struct nlattr *nla_reload_stats_entry, *nla_limit, *nla_value;
|
||||||
|
enum devlink_reload_limit limit;
|
||||||
|
uint32_t value;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
mnl_attr_for_each_nested(nla_reload_stats_entry, action_stats) {
|
||||||
|
err = mnl_attr_parse_nested(nla_reload_stats_entry, attr_cb,
|
||||||
|
tb_stats_entry);
|
||||||
|
if (err != MNL_CB_OK)
|
||||||
|
return;
|
||||||
|
|
||||||
|
nla_limit = tb_stats_entry[DEVLINK_ATTR_RELOAD_STATS_LIMIT];
|
||||||
|
nla_value = tb_stats_entry[DEVLINK_ATTR_RELOAD_STATS_VALUE];
|
||||||
|
if (!nla_limit || !nla_value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
check_indent_newline(dl);
|
||||||
|
limit = mnl_attr_get_u8(nla_limit);
|
||||||
|
value = mnl_attr_get_u32(nla_value);
|
||||||
|
print_uint_name_value(reload_limit_name(limit), value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pr_out_reload_stats(struct dl *dl, struct nlattr *reload_stats)
|
||||||
|
{
|
||||||
|
struct nlattr *nla_action_info, *nla_action, *nla_action_stats;
|
||||||
|
struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
|
||||||
|
enum devlink_reload_action action;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
mnl_attr_for_each_nested(nla_action_info, reload_stats) {
|
||||||
|
err = mnl_attr_parse_nested(nla_action_info, attr_cb, tb);
|
||||||
|
if (err != MNL_CB_OK)
|
||||||
|
return;
|
||||||
|
nla_action = tb[DEVLINK_ATTR_RELOAD_ACTION];
|
||||||
|
nla_action_stats = tb[DEVLINK_ATTR_RELOAD_ACTION_STATS];
|
||||||
|
if (!nla_action || !nla_action_stats)
|
||||||
|
return;
|
||||||
|
|
||||||
|
action = mnl_attr_get_u8(nla_action);
|
||||||
|
pr_out_object_start(dl, reload_action_name(action));
|
||||||
|
pr_out_action_stats(dl, nla_action_stats);
|
||||||
|
pr_out_object_end(dl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pr_out_reload_data(struct dl *dl, struct nlattr **tb)
|
||||||
|
{
|
||||||
|
struct nlattr *nla_reload_stats, *nla_remote_reload_stats;
|
||||||
|
struct nlattr *tb_stats[DEVLINK_ATTR_MAX + 1] = {};
|
||||||
|
uint8_t reload_failed = 0;
|
||||||
|
int err;
|
||||||
|
|
||||||
if (tb[DEVLINK_ATTR_RELOAD_FAILED])
|
if (tb[DEVLINK_ATTR_RELOAD_FAILED])
|
||||||
reload_failed = mnl_attr_get_u8(tb[DEVLINK_ATTR_RELOAD_FAILED]);
|
reload_failed = mnl_attr_get_u8(tb[DEVLINK_ATTR_RELOAD_FAILED]);
|
||||||
|
|
||||||
if (reload_failed) {
|
if (reload_failed) {
|
||||||
__pr_out_handle_start(dl, tb, true, false);
|
|
||||||
check_indent_newline(dl);
|
check_indent_newline(dl);
|
||||||
print_bool(PRINT_ANY, "reload_failed", "reload_failed %s", true);
|
print_bool(PRINT_ANY, "reload_failed", "reload_failed %s", true);
|
||||||
|
}
|
||||||
|
if (!tb[DEVLINK_ATTR_DEV_STATS] || !dl->stats)
|
||||||
|
return;
|
||||||
|
err = mnl_attr_parse_nested(tb[DEVLINK_ATTR_DEV_STATS], attr_cb,
|
||||||
|
tb_stats);
|
||||||
|
if (err != MNL_CB_OK)
|
||||||
|
return;
|
||||||
|
|
||||||
|
pr_out_object_start(dl, "stats");
|
||||||
|
|
||||||
|
nla_reload_stats = tb_stats[DEVLINK_ATTR_RELOAD_STATS];
|
||||||
|
if (nla_reload_stats) {
|
||||||
|
pr_out_object_start(dl, "reload");
|
||||||
|
pr_out_reload_stats(dl, nla_reload_stats);
|
||||||
|
pr_out_object_end(dl);
|
||||||
|
}
|
||||||
|
nla_remote_reload_stats = tb_stats[DEVLINK_ATTR_REMOTE_RELOAD_STATS];
|
||||||
|
if (nla_remote_reload_stats) {
|
||||||
|
pr_out_object_start(dl, "remote_reload");
|
||||||
|
pr_out_reload_stats(dl, nla_remote_reload_stats);
|
||||||
|
pr_out_object_end(dl);
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_out_object_end(dl);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void pr_out_dev(struct dl *dl, struct nlattr **tb)
|
||||||
|
{
|
||||||
|
if ((tb[DEVLINK_ATTR_RELOAD_FAILED] && mnl_attr_get_u8(tb[DEVLINK_ATTR_RELOAD_FAILED])) ||
|
||||||
|
(tb[DEVLINK_ATTR_DEV_STATS] && dl->stats)) {
|
||||||
|
__pr_out_handle_start(dl, tb, true, false);
|
||||||
|
pr_out_reload_data(dl, tb);
|
||||||
pr_out_handle_end(dl);
|
pr_out_handle_end(dl);
|
||||||
} else {
|
} else {
|
||||||
pr_out_handle(dl, tb);
|
pr_out_handle(dl, tb);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_dev_show_cb(const struct nlmsghdr *nlh, void *data)
|
||||||
|
{
|
||||||
|
struct dl *dl = data;
|
||||||
|
struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
|
||||||
|
struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
|
||||||
|
|
||||||
|
mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
|
||||||
|
if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME])
|
||||||
|
return MNL_CB_ERROR;
|
||||||
|
|
||||||
|
pr_out_dev(dl, tb);
|
||||||
return MNL_CB_OK;
|
return MNL_CB_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2942,6 +3135,57 @@ static int cmd_dev_show(struct dl *dl)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void pr_out_reload_actions_performed(struct dl *dl, struct nlattr **tb)
|
||||||
|
{
|
||||||
|
struct nlattr *nla_actions_performed;
|
||||||
|
struct nla_bitfield32 *actions;
|
||||||
|
uint32_t actions_performed;
|
||||||
|
uint16_t len;
|
||||||
|
int action;
|
||||||
|
|
||||||
|
if (!tb[DEVLINK_ATTR_RELOAD_ACTIONS_PERFORMED])
|
||||||
|
return;
|
||||||
|
|
||||||
|
nla_actions_performed = tb[DEVLINK_ATTR_RELOAD_ACTIONS_PERFORMED];
|
||||||
|
len = mnl_attr_get_payload_len(nla_actions_performed);
|
||||||
|
if (len != sizeof(*actions))
|
||||||
|
return;
|
||||||
|
actions = mnl_attr_get_payload(nla_actions_performed);
|
||||||
|
if (!actions)
|
||||||
|
return;
|
||||||
|
g_new_line_count = 1; /* Avoid extra new line in non-json print */
|
||||||
|
pr_out_array_start(dl, "reload_actions_performed");
|
||||||
|
actions_performed = actions->value & actions->selector;
|
||||||
|
for (action = 0; action <= DEVLINK_RELOAD_ACTION_MAX; action++) {
|
||||||
|
if (BIT(action) & actions_performed) {
|
||||||
|
check_indent_newline(dl);
|
||||||
|
print_string(PRINT_ANY, NULL, "%s",
|
||||||
|
reload_action_name(action));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pr_out_array_end(dl);
|
||||||
|
if (!dl->json_output)
|
||||||
|
__pr_out_newline();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_dev_reload_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_RELOAD_ACTIONS_PERFORMED])
|
||||||
|
return MNL_CB_ERROR;
|
||||||
|
|
||||||
|
pr_out_section_start(dl, "reload");
|
||||||
|
pr_out_reload_actions_performed(dl, tb);
|
||||||
|
pr_out_section_end(dl);
|
||||||
|
|
||||||
|
return MNL_CB_OK;
|
||||||
|
}
|
||||||
|
|
||||||
static int cmd_dev_reload(struct dl *dl)
|
static int cmd_dev_reload(struct dl *dl)
|
||||||
{
|
{
|
||||||
struct nlmsghdr *nlh;
|
struct nlmsghdr *nlh;
|
||||||
|
|
@ -2955,11 +3199,13 @@ static int cmd_dev_reload(struct dl *dl)
|
||||||
nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_RELOAD,
|
nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_RELOAD,
|
||||||
NLM_F_REQUEST | NLM_F_ACK);
|
NLM_F_REQUEST | NLM_F_ACK);
|
||||||
|
|
||||||
err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE, DL_OPT_NETNS);
|
err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE,
|
||||||
|
DL_OPT_NETNS | DL_OPT_RELOAD_ACTION |
|
||||||
|
DL_OPT_RELOAD_LIMIT);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
|
return _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_dev_reload_cb, dl);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pr_out_versions_single(struct dl *dl, const struct nlmsghdr *nlh,
|
static void pr_out_versions_single(struct dl *dl, const struct nlmsghdr *nlh,
|
||||||
|
|
@ -4711,7 +4957,8 @@ static int cmd_mon_show_cb(const struct nlmsghdr *nlh, void *data)
|
||||||
if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME])
|
if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME])
|
||||||
return MNL_CB_ERROR;
|
return MNL_CB_ERROR;
|
||||||
pr_out_mon_header(genl->cmd);
|
pr_out_mon_header(genl->cmd);
|
||||||
pr_out_handle(dl, tb);
|
dl->stats = true;
|
||||||
|
pr_out_dev(dl, tb);
|
||||||
pr_out_mon_footer();
|
pr_out_mon_footer();
|
||||||
break;
|
break;
|
||||||
case DEVLINK_CMD_PORT_GET: /* fall through */
|
case DEVLINK_CMD_PORT_GET: /* fall through */
|
||||||
|
|
@ -7915,43 +8162,16 @@ static void dl_free(struct dl *dl)
|
||||||
free(dl);
|
free(dl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int dl_batch_cmd(int argc, char *argv[], void *data)
|
||||||
|
{
|
||||||
|
struct dl *dl = data;
|
||||||
|
|
||||||
|
return dl_cmd(dl, argc, argv);
|
||||||
|
}
|
||||||
|
|
||||||
static int dl_batch(struct dl *dl, const char *name, bool force)
|
static int dl_batch(struct dl *dl, const char *name, bool force)
|
||||||
{
|
{
|
||||||
char *line = NULL;
|
return do_batch(name, force, dl_batch_cmd, dl);
|
||||||
size_t len = 0;
|
|
||||||
int ret = EXIT_SUCCESS;
|
|
||||||
|
|
||||||
if (name && strcmp(name, "-") != 0) {
|
|
||||||
if (freopen(name, "r", stdin) == NULL) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"Cannot open file \"%s\" for reading: %s\n",
|
|
||||||
name, strerror(errno));
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cmdlineno = 0;
|
|
||||||
while (getcmdline(&line, &len, stdin) != -1) {
|
|
||||||
char *largv[100];
|
|
||||||
int largc;
|
|
||||||
|
|
||||||
largc = makeargs(line, largv, 100);
|
|
||||||
if (!largc)
|
|
||||||
continue; /* blank line */
|
|
||||||
|
|
||||||
if (dl_cmd(dl, largc, largv)) {
|
|
||||||
fprintf(stderr, "Command failed %s:%d\n",
|
|
||||||
name, cmdlineno);
|
|
||||||
ret = EXIT_FAILURE;
|
|
||||||
if (!force)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (line)
|
|
||||||
free(line);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
|
|
|
||||||
|
|
@ -14,11 +14,11 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <time.h>
|
|
||||||
#include <libmnl/libmnl.h>
|
#include <libmnl/libmnl.h>
|
||||||
#include <linux/genetlink.h>
|
#include <linux/genetlink.h>
|
||||||
|
|
||||||
#include "libnetlink.h"
|
#include "libnetlink.h"
|
||||||
|
#include "mnl_utils.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "mnlg.h"
|
#include "mnlg.h"
|
||||||
|
|
||||||
|
|
@ -28,26 +28,20 @@ struct mnlg_socket {
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
uint8_t version;
|
uint8_t version;
|
||||||
unsigned int seq;
|
unsigned int seq;
|
||||||
unsigned int portid;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct nlmsghdr *__mnlg_msg_prepare(struct mnlg_socket *nlg, uint8_t cmd,
|
static struct nlmsghdr *__mnlg_msg_prepare(struct mnlg_socket *nlg, uint8_t cmd,
|
||||||
uint16_t flags, uint32_t id,
|
uint16_t flags, uint32_t id,
|
||||||
uint8_t version)
|
uint8_t version)
|
||||||
{
|
{
|
||||||
|
struct genlmsghdr genl = {
|
||||||
|
.cmd = cmd,
|
||||||
|
.version = version,
|
||||||
|
};
|
||||||
struct nlmsghdr *nlh;
|
struct nlmsghdr *nlh;
|
||||||
struct genlmsghdr *genl;
|
|
||||||
|
|
||||||
nlh = mnl_nlmsg_put_header(nlg->buf);
|
|
||||||
nlh->nlmsg_type = id;
|
|
||||||
nlh->nlmsg_flags = flags;
|
|
||||||
nlg->seq = time(NULL);
|
|
||||||
nlh->nlmsg_seq = nlg->seq;
|
|
||||||
|
|
||||||
genl = mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr));
|
|
||||||
genl->cmd = cmd;
|
|
||||||
genl->version = version;
|
|
||||||
|
|
||||||
|
nlh = mnlu_msg_prepare(nlg->buf, id, flags, &genl, sizeof(genl));
|
||||||
|
nlg->seq = nlh->nlmsg_seq;
|
||||||
return nlh;
|
return nlh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -62,61 +56,10 @@ int mnlg_socket_send(struct mnlg_socket *nlg, const struct nlmsghdr *nlh)
|
||||||
return mnl_socket_sendto(nlg->nl, nlh, nlh->nlmsg_len);
|
return mnl_socket_sendto(nlg->nl, nlh, nlh->nlmsg_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mnlg_cb_noop(const struct nlmsghdr *nlh, void *data)
|
|
||||||
{
|
|
||||||
return MNL_CB_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int mnlg_cb_error(const struct nlmsghdr *nlh, void *data)
|
|
||||||
{
|
|
||||||
const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);
|
|
||||||
|
|
||||||
/* Netlink subsystems returns the errno value with different signess */
|
|
||||||
if (err->error < 0)
|
|
||||||
errno = -err->error;
|
|
||||||
else
|
|
||||||
errno = err->error;
|
|
||||||
|
|
||||||
if (nl_dump_ext_ack(nlh, NULL))
|
|
||||||
return MNL_CB_ERROR;
|
|
||||||
|
|
||||||
return err->error == 0 ? MNL_CB_STOP : MNL_CB_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int mnlg_cb_stop(const struct nlmsghdr *nlh, void *data)
|
|
||||||
{
|
|
||||||
int len = *(int *)NLMSG_DATA(nlh);
|
|
||||||
|
|
||||||
if (len < 0) {
|
|
||||||
errno = -len;
|
|
||||||
nl_dump_ext_ack_done(nlh, len);
|
|
||||||
return MNL_CB_ERROR;
|
|
||||||
}
|
|
||||||
return MNL_CB_STOP;
|
|
||||||
}
|
|
||||||
|
|
||||||
static mnl_cb_t mnlg_cb_array[NLMSG_MIN_TYPE] = {
|
|
||||||
[NLMSG_NOOP] = mnlg_cb_noop,
|
|
||||||
[NLMSG_ERROR] = mnlg_cb_error,
|
|
||||||
[NLMSG_DONE] = mnlg_cb_stop,
|
|
||||||
[NLMSG_OVERRUN] = mnlg_cb_noop,
|
|
||||||
};
|
|
||||||
|
|
||||||
int mnlg_socket_recv_run(struct mnlg_socket *nlg, mnl_cb_t data_cb, void *data)
|
int mnlg_socket_recv_run(struct mnlg_socket *nlg, mnl_cb_t data_cb, void *data)
|
||||||
{
|
{
|
||||||
int err;
|
return mnlu_socket_recv_run(nlg->nl, nlg->seq, nlg->buf, MNL_SOCKET_BUFFER_SIZE,
|
||||||
|
data_cb, data);
|
||||||
do {
|
|
||||||
err = mnl_socket_recvfrom(nlg->nl, nlg->buf,
|
|
||||||
MNL_SOCKET_BUFFER_SIZE);
|
|
||||||
if (err <= 0)
|
|
||||||
break;
|
|
||||||
err = mnl_cb_run2(nlg->buf, err, nlg->seq, nlg->portid,
|
|
||||||
data_cb, data, mnlg_cb_array,
|
|
||||||
ARRAY_SIZE(mnlg_cb_array));
|
|
||||||
} while (err > 0);
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct group_info {
|
struct group_info {
|
||||||
|
|
@ -263,7 +206,6 @@ struct mnlg_socket *mnlg_socket_open(const char *family_name, uint8_t version)
|
||||||
{
|
{
|
||||||
struct mnlg_socket *nlg;
|
struct mnlg_socket *nlg;
|
||||||
struct nlmsghdr *nlh;
|
struct nlmsghdr *nlh;
|
||||||
int one = 1;
|
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
nlg = malloc(sizeof(*nlg));
|
nlg = malloc(sizeof(*nlg));
|
||||||
|
|
@ -274,19 +216,9 @@ struct mnlg_socket *mnlg_socket_open(const char *family_name, uint8_t version)
|
||||||
if (!nlg->buf)
|
if (!nlg->buf)
|
||||||
goto err_buf_alloc;
|
goto err_buf_alloc;
|
||||||
|
|
||||||
nlg->nl = mnl_socket_open(NETLINK_GENERIC);
|
nlg->nl = mnlu_socket_open(NETLINK_GENERIC);
|
||||||
if (!nlg->nl)
|
if (!nlg->nl)
|
||||||
goto err_mnl_socket_open;
|
goto err_socket_open;
|
||||||
|
|
||||||
/* Older kernels may no support capped/extended ACK reporting */
|
|
||||||
mnl_socket_setsockopt(nlg->nl, NETLINK_CAP_ACK, &one, sizeof(one));
|
|
||||||
mnl_socket_setsockopt(nlg->nl, NETLINK_EXT_ACK, &one, sizeof(one));
|
|
||||||
|
|
||||||
err = mnl_socket_bind(nlg->nl, 0, MNL_SOCKET_AUTOPID);
|
|
||||||
if (err < 0)
|
|
||||||
goto err_mnl_socket_bind;
|
|
||||||
|
|
||||||
nlg->portid = mnl_socket_get_portid(nlg->nl);
|
|
||||||
|
|
||||||
nlh = __mnlg_msg_prepare(nlg, CTRL_CMD_GETFAMILY,
|
nlh = __mnlg_msg_prepare(nlg, CTRL_CMD_GETFAMILY,
|
||||||
NLM_F_REQUEST | NLM_F_ACK, GENL_ID_CTRL, 1);
|
NLM_F_REQUEST | NLM_F_ACK, GENL_ID_CTRL, 1);
|
||||||
|
|
@ -305,9 +237,8 @@ struct mnlg_socket *mnlg_socket_open(const char *family_name, uint8_t version)
|
||||||
|
|
||||||
err_mnlg_socket_recv_run:
|
err_mnlg_socket_recv_run:
|
||||||
err_mnlg_socket_send:
|
err_mnlg_socket_send:
|
||||||
err_mnl_socket_bind:
|
|
||||||
mnl_socket_close(nlg->nl);
|
mnl_socket_close(nlg->nl);
|
||||||
err_mnl_socket_open:
|
err_socket_open:
|
||||||
free(nlg->buf);
|
free(nlg->buf);
|
||||||
err_buf_alloc:
|
err_buf_alloc:
|
||||||
free(nlg);
|
free(nlg);
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,18 @@
|
||||||
eBPF toy code examples (running in kernel) to familiarize yourself
|
eBPF toy code examples (running in kernel) to familiarize yourself
|
||||||
with syntax and features:
|
with syntax and features:
|
||||||
|
|
||||||
- bpf_shared.c -> Ingress/egress map sharing example
|
- BTF defined map examples
|
||||||
- bpf_tailcall.c -> Using tail call chains
|
|
||||||
- bpf_cyclic.c -> Simple cycle as tail calls
|
|
||||||
- bpf_graft.c -> Demo on altering runtime behaviour
|
- bpf_graft.c -> Demo on altering runtime behaviour
|
||||||
- bpf_map_in_map.c -> Using map in map example
|
- bpf_shared.c -> Ingress/egress map sharing example
|
||||||
|
- bpf_map_in_map.c -> Using map in map example
|
||||||
|
|
||||||
|
- legacy struct bpf_elf_map defined map examples
|
||||||
|
- legacy/bpf_shared.c -> Ingress/egress map sharing example
|
||||||
|
- legacy/bpf_tailcall.c -> Using tail call chains
|
||||||
|
- legacy/bpf_cyclic.c -> Simple cycle as tail calls
|
||||||
|
- legacy/bpf_graft.c -> Demo on altering runtime behaviour
|
||||||
|
- legacy/bpf_map_in_map.c -> Using map in map example
|
||||||
|
|
||||||
|
Note: Users should use new BTF way to defined the maps, the examples
|
||||||
|
in legacy folder which is using struct bpf_elf_map defined maps is not
|
||||||
|
recommanded.
|
||||||
|
|
|
||||||
|
|
@ -33,13 +33,13 @@
|
||||||
* [...]
|
* [...]
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct bpf_elf_map __section_maps jmp_tc = {
|
struct {
|
||||||
.type = BPF_MAP_TYPE_PROG_ARRAY,
|
__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
|
||||||
.size_key = sizeof(uint32_t),
|
__uint(key_size, sizeof(uint32_t));
|
||||||
.size_value = sizeof(uint32_t),
|
__uint(value_size, sizeof(uint32_t));
|
||||||
.pinning = PIN_GLOBAL_NS,
|
__uint(max_entries, 1);
|
||||||
.max_elem = 1,
|
__uint(pinning, LIBBPF_PIN_BY_NAME);
|
||||||
};
|
} jmp_tc __section(".maps");
|
||||||
|
|
||||||
__section("aaa")
|
__section("aaa")
|
||||||
int cls_aaa(struct __sk_buff *skb)
|
int cls_aaa(struct __sk_buff *skb)
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,23 @@
|
||||||
#include "../../include/bpf_api.h"
|
#include "../../include/bpf_api.h"
|
||||||
|
|
||||||
#define MAP_INNER_ID 42
|
struct inner_map {
|
||||||
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
|
__uint(key_size, sizeof(uint32_t));
|
||||||
|
__uint(value_size, sizeof(uint32_t));
|
||||||
|
__uint(max_entries, 1);
|
||||||
|
} map_inner __section(".maps");
|
||||||
|
|
||||||
struct bpf_elf_map __section_maps map_inner = {
|
struct {
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
__uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
|
||||||
.size_key = sizeof(uint32_t),
|
__uint(key_size, sizeof(uint32_t));
|
||||||
.size_value = sizeof(uint32_t),
|
__uint(value_size, sizeof(uint32_t));
|
||||||
.id = MAP_INNER_ID,
|
__uint(max_entries, 1);
|
||||||
.inner_idx = 0,
|
__uint(pinning, LIBBPF_PIN_BY_NAME);
|
||||||
.pinning = PIN_GLOBAL_NS,
|
__array(values, struct inner_map);
|
||||||
.max_elem = 1,
|
} map_outer __section(".maps") = {
|
||||||
};
|
.values = {
|
||||||
|
[0] = &map_inner,
|
||||||
struct bpf_elf_map __section_maps map_outer = {
|
},
|
||||||
.type = BPF_MAP_TYPE_ARRAY_OF_MAPS,
|
|
||||||
.size_key = sizeof(uint32_t),
|
|
||||||
.size_value = sizeof(uint32_t),
|
|
||||||
.inner_id = MAP_INNER_ID,
|
|
||||||
.pinning = PIN_GLOBAL_NS,
|
|
||||||
.max_elem = 1,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
__section("egress")
|
__section("egress")
|
||||||
|
|
|
||||||
|
|
@ -18,13 +18,13 @@
|
||||||
* instance is being created.
|
* instance is being created.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct bpf_elf_map __section_maps map_sh = {
|
struct {
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
.size_key = sizeof(uint32_t),
|
__uint(key_size, sizeof(uint32_t));
|
||||||
.size_value = sizeof(uint32_t),
|
__uint(value_size, sizeof(uint32_t));
|
||||||
.pinning = PIN_OBJECT_NS, /* or PIN_GLOBAL_NS, or PIN_NONE */
|
__uint(max_entries, 1);
|
||||||
.max_elem = 1,
|
__uint(pinning, LIBBPF_PIN_BY_NAME); /* or LIBBPF_PIN_NONE */
|
||||||
};
|
} map_sh __section(".maps");
|
||||||
|
|
||||||
__section("egress")
|
__section("egress")
|
||||||
int emain(struct __sk_buff *skb)
|
int emain(struct __sk_buff *skb)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
#include "../../include/bpf_api.h"
|
#include "../../../include/bpf_api.h"
|
||||||
|
|
||||||
/* Cyclic dependency example to test the kernel's runtime upper
|
/* Cyclic dependency example to test the kernel's runtime upper
|
||||||
* bound on loops. Also demonstrates on how to use direct-actions,
|
* bound on loops. Also demonstrates on how to use direct-actions,
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
#include "../../../include/bpf_api.h"
|
||||||
|
|
||||||
|
/* This example demonstrates how classifier run-time behaviour
|
||||||
|
* can be altered with tail calls. We start out with an empty
|
||||||
|
* jmp_tc array, then add section aaa to the array slot 0, and
|
||||||
|
* later on atomically replace it with section bbb. Note that
|
||||||
|
* as shown in other examples, the tc loader can prepopulate
|
||||||
|
* tail called sections, here we start out with an empty one
|
||||||
|
* on purpose to show it can also be done this way.
|
||||||
|
*
|
||||||
|
* tc filter add dev foo parent ffff: bpf obj graft.o
|
||||||
|
* tc exec bpf dbg
|
||||||
|
* [...]
|
||||||
|
* Socket Thread-20229 [001] ..s. 138993.003923: : fallthrough
|
||||||
|
* <idle>-0 [001] ..s. 138993.202265: : fallthrough
|
||||||
|
* Socket Thread-20229 [001] ..s. 138994.004149: : fallthrough
|
||||||
|
* [...]
|
||||||
|
*
|
||||||
|
* tc exec bpf graft m:globals/jmp_tc key 0 obj graft.o sec aaa
|
||||||
|
* tc exec bpf dbg
|
||||||
|
* [...]
|
||||||
|
* Socket Thread-19818 [002] ..s. 139012.053587: : aaa
|
||||||
|
* <idle>-0 [002] ..s. 139012.172359: : aaa
|
||||||
|
* Socket Thread-19818 [001] ..s. 139012.173556: : aaa
|
||||||
|
* [...]
|
||||||
|
*
|
||||||
|
* tc exec bpf graft m:globals/jmp_tc key 0 obj graft.o sec bbb
|
||||||
|
* tc exec bpf dbg
|
||||||
|
* [...]
|
||||||
|
* Socket Thread-19818 [002] ..s. 139022.102967: : bbb
|
||||||
|
* <idle>-0 [002] ..s. 139022.155640: : bbb
|
||||||
|
* Socket Thread-19818 [001] ..s. 139022.156730: : bbb
|
||||||
|
* [...]
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct bpf_elf_map __section_maps jmp_tc = {
|
||||||
|
.type = BPF_MAP_TYPE_PROG_ARRAY,
|
||||||
|
.size_key = sizeof(uint32_t),
|
||||||
|
.size_value = sizeof(uint32_t),
|
||||||
|
.pinning = PIN_GLOBAL_NS,
|
||||||
|
.max_elem = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
__section("aaa")
|
||||||
|
int cls_aaa(struct __sk_buff *skb)
|
||||||
|
{
|
||||||
|
printt("aaa\n");
|
||||||
|
return TC_H_MAKE(1, 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
__section("bbb")
|
||||||
|
int cls_bbb(struct __sk_buff *skb)
|
||||||
|
{
|
||||||
|
printt("bbb\n");
|
||||||
|
return TC_H_MAKE(1, 43);
|
||||||
|
}
|
||||||
|
|
||||||
|
__section_cls_entry
|
||||||
|
int cls_entry(struct __sk_buff *skb)
|
||||||
|
{
|
||||||
|
tail_call(skb, &jmp_tc, 0);
|
||||||
|
printt("fallthrough\n");
|
||||||
|
return BPF_H_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
BPF_LICENSE("GPL");
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
#include "../../../include/bpf_api.h"
|
||||||
|
|
||||||
|
#define MAP_INNER_ID 42
|
||||||
|
|
||||||
|
struct bpf_elf_map __section_maps map_inner = {
|
||||||
|
.type = BPF_MAP_TYPE_ARRAY,
|
||||||
|
.size_key = sizeof(uint32_t),
|
||||||
|
.size_value = sizeof(uint32_t),
|
||||||
|
.id = MAP_INNER_ID,
|
||||||
|
.inner_idx = 0,
|
||||||
|
.pinning = PIN_GLOBAL_NS,
|
||||||
|
.max_elem = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct bpf_elf_map __section_maps map_outer = {
|
||||||
|
.type = BPF_MAP_TYPE_ARRAY_OF_MAPS,
|
||||||
|
.size_key = sizeof(uint32_t),
|
||||||
|
.size_value = sizeof(uint32_t),
|
||||||
|
.inner_id = MAP_INNER_ID,
|
||||||
|
.pinning = PIN_GLOBAL_NS,
|
||||||
|
.max_elem = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
__section("egress")
|
||||||
|
int emain(struct __sk_buff *skb)
|
||||||
|
{
|
||||||
|
struct bpf_elf_map *map_inner;
|
||||||
|
int key = 0, *val;
|
||||||
|
|
||||||
|
map_inner = map_lookup_elem(&map_outer, &key);
|
||||||
|
if (map_inner) {
|
||||||
|
val = map_lookup_elem(map_inner, &key);
|
||||||
|
if (val)
|
||||||
|
lock_xadd(val, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return BPF_H_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
__section("ingress")
|
||||||
|
int imain(struct __sk_buff *skb)
|
||||||
|
{
|
||||||
|
struct bpf_elf_map *map_inner;
|
||||||
|
int key = 0, *val;
|
||||||
|
|
||||||
|
map_inner = map_lookup_elem(&map_outer, &key);
|
||||||
|
if (map_inner) {
|
||||||
|
val = map_lookup_elem(map_inner, &key);
|
||||||
|
if (val)
|
||||||
|
printt("map val: %d\n", *val);
|
||||||
|
}
|
||||||
|
|
||||||
|
return BPF_H_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
BPF_LICENSE("GPL");
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
#include "../../../include/bpf_api.h"
|
||||||
|
|
||||||
|
/* Minimal, stand-alone toy map pinning example:
|
||||||
|
*
|
||||||
|
* clang -target bpf -O2 [...] -o bpf_shared.o -c bpf_shared.c
|
||||||
|
* tc filter add dev foo parent 1: bpf obj bpf_shared.o sec egress
|
||||||
|
* tc filter add dev foo parent ffff: bpf obj bpf_shared.o sec ingress
|
||||||
|
*
|
||||||
|
* Both classifier will share the very same map instance in this example,
|
||||||
|
* so map content can be accessed from ingress *and* egress side!
|
||||||
|
*
|
||||||
|
* This example has a pinning of PIN_OBJECT_NS, so it's private and
|
||||||
|
* thus shared among various program sections within the object.
|
||||||
|
*
|
||||||
|
* A setting of PIN_GLOBAL_NS would place it into a global namespace,
|
||||||
|
* so that it can be shared among different object files. A setting
|
||||||
|
* of PIN_NONE (= 0) means no sharing, so each tc invocation a new map
|
||||||
|
* instance is being created.
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct bpf_elf_map __section_maps map_sh = {
|
||||||
|
.type = BPF_MAP_TYPE_ARRAY,
|
||||||
|
.size_key = sizeof(uint32_t),
|
||||||
|
.size_value = sizeof(uint32_t),
|
||||||
|
.pinning = PIN_OBJECT_NS, /* or PIN_GLOBAL_NS, or PIN_NONE */
|
||||||
|
.max_elem = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
__section("egress")
|
||||||
|
int emain(struct __sk_buff *skb)
|
||||||
|
{
|
||||||
|
int key = 0, *val;
|
||||||
|
|
||||||
|
val = map_lookup_elem(&map_sh, &key);
|
||||||
|
if (val)
|
||||||
|
lock_xadd(val, 1);
|
||||||
|
|
||||||
|
return BPF_H_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
__section("ingress")
|
||||||
|
int imain(struct __sk_buff *skb)
|
||||||
|
{
|
||||||
|
int key = 0, *val;
|
||||||
|
|
||||||
|
val = map_lookup_elem(&map_sh, &key);
|
||||||
|
if (val)
|
||||||
|
printt("map val: %d\n", *val);
|
||||||
|
|
||||||
|
return BPF_H_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
BPF_LICENSE("GPL");
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/* SPDX-License-Identifier: GPL-2.0 */
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
#include "../../include/bpf_api.h"
|
#include "../../../include/bpf_api.h"
|
||||||
|
|
||||||
#define ENTRY_INIT 3
|
#define ENTRY_INIT 3
|
||||||
#define ENTRY_0 0
|
#define ENTRY_0 0
|
||||||
|
|
@ -19,6 +19,19 @@
|
||||||
|
|
||||||
#include "bpf_elf.h"
|
#include "bpf_elf.h"
|
||||||
|
|
||||||
|
/** libbpf pin type. */
|
||||||
|
enum libbpf_pin_type {
|
||||||
|
LIBBPF_PIN_NONE,
|
||||||
|
/* PIN_BY_NAME: pin maps by name (in /sys/fs/bpf by default) */
|
||||||
|
LIBBPF_PIN_BY_NAME,
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Type helper macros. */
|
||||||
|
|
||||||
|
#define __uint(name, val) int (*name)[val]
|
||||||
|
#define __type(name, val) typeof(val) *name
|
||||||
|
#define __array(name, val) typeof(val) *name[]
|
||||||
|
|
||||||
/** Misc macros. */
|
/** Misc macros. */
|
||||||
|
|
||||||
#ifndef __stringify
|
#ifndef __stringify
|
||||||
|
|
|
||||||
|
|
@ -274,12 +274,16 @@ int bpf_trace_pipe(void);
|
||||||
|
|
||||||
void bpf_print_ops(struct rtattr *bpf_ops, __u16 len);
|
void bpf_print_ops(struct rtattr *bpf_ops, __u16 len);
|
||||||
|
|
||||||
int bpf_prog_load(enum bpf_prog_type type, const struct bpf_insn *insns,
|
int bpf_prog_load_dev(enum bpf_prog_type type, const struct bpf_insn *insns,
|
||||||
size_t size_insns, const char *license, char *log,
|
size_t size_insns, const char *license, __u32 ifindex,
|
||||||
size_t size_log);
|
char *log, size_t size_log);
|
||||||
|
int bpf_program_load(enum bpf_prog_type type, const struct bpf_insn *insns,
|
||||||
|
size_t size_insns, const char *license, char *log,
|
||||||
|
size_t size_log);
|
||||||
|
|
||||||
int bpf_prog_attach_fd(int prog_fd, int target_fd, enum bpf_attach_type type);
|
int bpf_prog_attach_fd(int prog_fd, int target_fd, enum bpf_attach_type type);
|
||||||
int bpf_prog_detach_fd(int target_fd, enum bpf_attach_type type);
|
int bpf_prog_detach_fd(int target_fd, enum bpf_attach_type type);
|
||||||
|
int bpf_program_attach(int prog_fd, int target_fd, enum bpf_attach_type type);
|
||||||
|
|
||||||
int bpf_dump_prog_info(FILE *f, uint32_t id);
|
int bpf_dump_prog_info(FILE *f, uint32_t id);
|
||||||
|
|
||||||
|
|
@ -287,6 +291,16 @@ int bpf_dump_prog_info(FILE *f, uint32_t id);
|
||||||
int bpf_send_map_fds(const char *path, const char *obj);
|
int bpf_send_map_fds(const char *path, const char *obj);
|
||||||
int bpf_recv_map_fds(const char *path, int *fds, struct bpf_map_aux *aux,
|
int bpf_recv_map_fds(const char *path, int *fds, struct bpf_map_aux *aux,
|
||||||
unsigned int entries);
|
unsigned int entries);
|
||||||
|
#ifdef HAVE_LIBBPF
|
||||||
|
int iproute2_bpf_elf_ctx_init(struct bpf_cfg_in *cfg);
|
||||||
|
int iproute2_bpf_fetch_ancillary(void);
|
||||||
|
int iproute2_get_root_path(char *root_path, size_t len);
|
||||||
|
bool iproute2_is_pin_map(const char *libbpf_map_name, char *pathname);
|
||||||
|
bool iproute2_is_map_in_map(const char *libbpf_map_name, struct bpf_elf_map *imap,
|
||||||
|
struct bpf_elf_map *omap, char *omap_name);
|
||||||
|
int iproute2_find_map_name_by_id(unsigned int map_id, char *name);
|
||||||
|
int iproute2_load_libbpf(struct bpf_cfg_in *cfg);
|
||||||
|
#endif /* HAVE_LIBBPF */
|
||||||
#else
|
#else
|
||||||
static inline int bpf_send_map_fds(const char *path, const char *obj)
|
static inline int bpf_send_map_fds(const char *path, const char *obj)
|
||||||
{
|
{
|
||||||
|
|
@ -299,5 +313,15 @@ static inline int bpf_recv_map_fds(const char *path, int *fds,
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
#ifdef HAVE_LIBBPF
|
||||||
|
static inline int iproute2_load_libbpf(struct bpf_cfg_in *cfg)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "No ELF library support compiled in.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#endif /* HAVE_LIBBPF */
|
||||||
#endif /* HAVE_ELF */
|
#endif /* HAVE_ELF */
|
||||||
|
|
||||||
|
const char *get_libbpf_version(void);
|
||||||
|
|
||||||
#endif /* __BPF_UTIL__ */
|
#endif /* __BPF_UTIL__ */
|
||||||
|
|
|
||||||
|
|
@ -65,9 +65,11 @@ void print_nl(void);
|
||||||
_PRINT_FUNC(int, int)
|
_PRINT_FUNC(int, int)
|
||||||
_PRINT_FUNC(s64, int64_t)
|
_PRINT_FUNC(s64, int64_t)
|
||||||
_PRINT_FUNC(bool, bool)
|
_PRINT_FUNC(bool, bool)
|
||||||
|
_PRINT_FUNC(on_off, bool)
|
||||||
_PRINT_FUNC(null, const char*)
|
_PRINT_FUNC(null, const char*)
|
||||||
_PRINT_FUNC(string, const char*)
|
_PRINT_FUNC(string, const char*)
|
||||||
_PRINT_FUNC(uint, unsigned int)
|
_PRINT_FUNC(uint, unsigned int)
|
||||||
|
_PRINT_FUNC(size, __u32)
|
||||||
_PRINT_FUNC(u64, uint64_t)
|
_PRINT_FUNC(u64, uint64_t)
|
||||||
_PRINT_FUNC(hhu, unsigned char)
|
_PRINT_FUNC(hhu, unsigned char)
|
||||||
_PRINT_FUNC(hu, unsigned short)
|
_PRINT_FUNC(hu, unsigned short)
|
||||||
|
|
@ -85,4 +87,17 @@ _PRINT_NAME_VALUE_FUNC(uint, unsigned int, u);
|
||||||
_PRINT_NAME_VALUE_FUNC(string, const char*, s);
|
_PRINT_NAME_VALUE_FUNC(string, const char*, s);
|
||||||
#undef _PRINT_NAME_VALUE_FUNC
|
#undef _PRINT_NAME_VALUE_FUNC
|
||||||
|
|
||||||
|
int print_color_rate(bool use_iec, enum output_type t, enum color_attr color,
|
||||||
|
const char *key, const char *fmt, unsigned long long rate);
|
||||||
|
|
||||||
|
static inline int print_rate(bool use_iec, enum output_type t,
|
||||||
|
const char *key, const char *fmt,
|
||||||
|
unsigned long long rate)
|
||||||
|
{
|
||||||
|
return print_color_rate(use_iec, t, COLOR_NONE, key, fmt, rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A backdoor to the size formatter. Please use print_size() instead. */
|
||||||
|
char *sprint_size(__u32 sz, char *buf);
|
||||||
|
|
||||||
#endif /* _JSON_PRINT_H_ */
|
#endif /* _JSON_PRINT_H_ */
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
|
#ifndef __MNL_UTILS_H__
|
||||||
|
#define __MNL_UTILS_H__ 1
|
||||||
|
|
||||||
|
struct mnl_socket *mnlu_socket_open(int bus);
|
||||||
|
struct nlmsghdr *mnlu_msg_prepare(void *buf, uint32_t nlmsg_type, uint16_t flags,
|
||||||
|
void *extra_header, size_t extra_header_size);
|
||||||
|
int mnlu_socket_recv_run(struct mnl_socket *nl, unsigned int seq, void *buf, size_t buf_size,
|
||||||
|
mnl_cb_t cb, void *data);
|
||||||
|
|
||||||
|
#endif /* __MNL_UTILS_H__ */
|
||||||
|
|
@ -157,6 +157,7 @@ enum bpf_map_type {
|
||||||
BPF_MAP_TYPE_STRUCT_OPS,
|
BPF_MAP_TYPE_STRUCT_OPS,
|
||||||
BPF_MAP_TYPE_RINGBUF,
|
BPF_MAP_TYPE_RINGBUF,
|
||||||
BPF_MAP_TYPE_INODE_STORAGE,
|
BPF_MAP_TYPE_INODE_STORAGE,
|
||||||
|
BPF_MAP_TYPE_TASK_STORAGE,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Note that tracing related programs such as
|
/* Note that tracing related programs such as
|
||||||
|
|
@ -556,7 +557,12 @@ union bpf_attr {
|
||||||
__aligned_u64 line_info; /* line info */
|
__aligned_u64 line_info; /* line info */
|
||||||
__u32 line_info_cnt; /* number of bpf_line_info records */
|
__u32 line_info_cnt; /* number of bpf_line_info records */
|
||||||
__u32 attach_btf_id; /* in-kernel BTF type id to attach to */
|
__u32 attach_btf_id; /* in-kernel BTF type id to attach to */
|
||||||
__u32 attach_prog_fd; /* 0 to attach to vmlinux */
|
union {
|
||||||
|
/* valid prog_fd to attach to bpf prog */
|
||||||
|
__u32 attach_prog_fd;
|
||||||
|
/* or valid module BTF object fd or 0 to attach to vmlinux */
|
||||||
|
__u32 attach_btf_obj_fd;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct { /* anonymous struct used by BPF_OBJ_* commands */
|
struct { /* anonymous struct used by BPF_OBJ_* commands */
|
||||||
|
|
@ -3742,6 +3748,80 @@ union bpf_attr {
|
||||||
* Return
|
* Return
|
||||||
* The helper returns **TC_ACT_REDIRECT** on success or
|
* The helper returns **TC_ACT_REDIRECT** on success or
|
||||||
* **TC_ACT_SHOT** on error.
|
* **TC_ACT_SHOT** on error.
|
||||||
|
*
|
||||||
|
* void *bpf_task_storage_get(struct bpf_map *map, struct task_struct *task, void *value, u64 flags)
|
||||||
|
* Description
|
||||||
|
* Get a bpf_local_storage from the *task*.
|
||||||
|
*
|
||||||
|
* Logically, it could be thought of as getting the value from
|
||||||
|
* a *map* with *task* as the **key**. From this
|
||||||
|
* perspective, the usage is not much different from
|
||||||
|
* **bpf_map_lookup_elem**\ (*map*, **&**\ *task*) except this
|
||||||
|
* helper enforces the key must be an task_struct and the map must also
|
||||||
|
* be a **BPF_MAP_TYPE_TASK_STORAGE**.
|
||||||
|
*
|
||||||
|
* Underneath, the value is stored locally at *task* instead of
|
||||||
|
* the *map*. The *map* is used as the bpf-local-storage
|
||||||
|
* "type". The bpf-local-storage "type" (i.e. the *map*) is
|
||||||
|
* searched against all bpf_local_storage residing at *task*.
|
||||||
|
*
|
||||||
|
* An optional *flags* (**BPF_LOCAL_STORAGE_GET_F_CREATE**) can be
|
||||||
|
* used such that a new bpf_local_storage will be
|
||||||
|
* created if one does not exist. *value* can be used
|
||||||
|
* together with **BPF_LOCAL_STORAGE_GET_F_CREATE** to specify
|
||||||
|
* the initial value of a bpf_local_storage. If *value* is
|
||||||
|
* **NULL**, the new bpf_local_storage will be zero initialized.
|
||||||
|
* Return
|
||||||
|
* A bpf_local_storage pointer is returned on success.
|
||||||
|
*
|
||||||
|
* **NULL** if not found or there was an error in adding
|
||||||
|
* a new bpf_local_storage.
|
||||||
|
*
|
||||||
|
* long bpf_task_storage_delete(struct bpf_map *map, struct task_struct *task)
|
||||||
|
* Description
|
||||||
|
* Delete a bpf_local_storage from a *task*.
|
||||||
|
* Return
|
||||||
|
* 0 on success.
|
||||||
|
*
|
||||||
|
* **-ENOENT** if the bpf_local_storage cannot be found.
|
||||||
|
*
|
||||||
|
* struct task_struct *bpf_get_current_task_btf(void)
|
||||||
|
* Description
|
||||||
|
* Return a BTF pointer to the "current" task.
|
||||||
|
* This pointer can also be used in helpers that accept an
|
||||||
|
* *ARG_PTR_TO_BTF_ID* of type *task_struct*.
|
||||||
|
* Return
|
||||||
|
* Pointer to the current task.
|
||||||
|
*
|
||||||
|
* long bpf_bprm_opts_set(struct linux_binprm *bprm, u64 flags)
|
||||||
|
* Description
|
||||||
|
* Set or clear certain options on *bprm*:
|
||||||
|
*
|
||||||
|
* **BPF_F_BPRM_SECUREEXEC** Set the secureexec bit
|
||||||
|
* which sets the **AT_SECURE** auxv for glibc. The bit
|
||||||
|
* is cleared if the flag is not specified.
|
||||||
|
* Return
|
||||||
|
* **-EINVAL** if invalid *flags* are passed, zero otherwise.
|
||||||
|
*
|
||||||
|
* u64 bpf_ktime_get_coarse_ns(void)
|
||||||
|
* Description
|
||||||
|
* Return a coarse-grained version of the time elapsed since
|
||||||
|
* system boot, in nanoseconds. Does not include time the system
|
||||||
|
* was suspended.
|
||||||
|
*
|
||||||
|
* See: **clock_gettime**\ (**CLOCK_MONOTONIC_COARSE**)
|
||||||
|
* Return
|
||||||
|
* Current *ktime*.
|
||||||
|
*
|
||||||
|
* long bpf_ima_inode_hash(struct inode *inode, void *dst, u32 size)
|
||||||
|
* Description
|
||||||
|
* Returns the stored IMA hash of the *inode* (if it's avaialable).
|
||||||
|
* If the hash is larger than *size*, then only *size*
|
||||||
|
* bytes will be copied to *dst*
|
||||||
|
* Return
|
||||||
|
* The **hash_algo** is returned on success,
|
||||||
|
* **-EOPNOTSUP** if IMA is disabled or **-EINVAL** if
|
||||||
|
* invalid arguments are passed.
|
||||||
*/
|
*/
|
||||||
#define __BPF_FUNC_MAPPER(FN) \
|
#define __BPF_FUNC_MAPPER(FN) \
|
||||||
FN(unspec), \
|
FN(unspec), \
|
||||||
|
|
@ -3900,6 +3980,12 @@ union bpf_attr {
|
||||||
FN(per_cpu_ptr), \
|
FN(per_cpu_ptr), \
|
||||||
FN(this_cpu_ptr), \
|
FN(this_cpu_ptr), \
|
||||||
FN(redirect_peer), \
|
FN(redirect_peer), \
|
||||||
|
FN(task_storage_get), \
|
||||||
|
FN(task_storage_delete), \
|
||||||
|
FN(get_current_task_btf), \
|
||||||
|
FN(bprm_opts_set), \
|
||||||
|
FN(ktime_get_coarse_ns), \
|
||||||
|
FN(ima_inode_hash), \
|
||||||
/* */
|
/* */
|
||||||
|
|
||||||
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
|
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
|
||||||
|
|
@ -4071,6 +4157,11 @@ enum bpf_lwt_encap_mode {
|
||||||
BPF_LWT_ENCAP_IP,
|
BPF_LWT_ENCAP_IP,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Flags for bpf_bprm_opts_set helper */
|
||||||
|
enum {
|
||||||
|
BPF_F_BPRM_SECUREEXEC = (1ULL << 0),
|
||||||
|
};
|
||||||
|
|
||||||
#define __bpf_md_ptr(type, name) \
|
#define __bpf_md_ptr(type, name) \
|
||||||
union { \
|
union { \
|
||||||
type name; \
|
type name; \
|
||||||
|
|
@ -4418,6 +4509,9 @@ struct bpf_btf_info {
|
||||||
__aligned_u64 btf;
|
__aligned_u64 btf;
|
||||||
__u32 btf_size;
|
__u32 btf_size;
|
||||||
__u32 id;
|
__u32 id;
|
||||||
|
__aligned_u64 name;
|
||||||
|
__u32 name_len;
|
||||||
|
__u32 kernel_btf;
|
||||||
} __attribute__((aligned(8)));
|
} __attribute__((aligned(8)));
|
||||||
|
|
||||||
struct bpf_link_info {
|
struct bpf_link_info {
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,7 @@ typedef __u32 can_err_mask_t;
|
||||||
|
|
||||||
/* CAN payload length and DLC definitions according to ISO 11898-1 */
|
/* CAN payload length and DLC definitions according to ISO 11898-1 */
|
||||||
#define CAN_MAX_DLC 8
|
#define CAN_MAX_DLC 8
|
||||||
|
#define CAN_MAX_RAW_DLC 15
|
||||||
#define CAN_MAX_DLEN 8
|
#define CAN_MAX_DLEN 8
|
||||||
|
|
||||||
/* CAN FD payload length and DLC definitions according to ISO 11898-7 */
|
/* CAN FD payload length and DLC definitions according to ISO 11898-7 */
|
||||||
|
|
@ -91,23 +92,32 @@ typedef __u32 can_err_mask_t;
|
||||||
#define CANFD_MAX_DLEN 64
|
#define CANFD_MAX_DLEN 64
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct can_frame - basic CAN frame structure
|
* struct can_frame - Classical CAN frame structure (aka CAN 2.0B)
|
||||||
* @can_id: CAN ID of the frame and CAN_*_FLAG flags, see canid_t definition
|
* @can_id: CAN ID of the frame and CAN_*_FLAG flags, see canid_t definition
|
||||||
* @can_dlc: frame payload length in byte (0 .. 8) aka data length code
|
* @len: CAN frame payload length in byte (0 .. 8)
|
||||||
* N.B. the DLC field from ISO 11898-1 Chapter 8.4.2.3 has a 1:1
|
* @can_dlc: deprecated name for CAN frame payload length in byte (0 .. 8)
|
||||||
* mapping of the 'data length code' to the real payload length
|
* @__pad: padding
|
||||||
* @__pad: padding
|
* @__res0: reserved / padding
|
||||||
* @__res0: reserved / padding
|
* @len8_dlc: optional DLC value (9 .. 15) at 8 byte payload length
|
||||||
* @__res1: reserved / padding
|
* len8_dlc contains values from 9 .. 15 when the payload length is
|
||||||
* @data: CAN frame payload (up to 8 byte)
|
* 8 bytes but the DLC value (see ISO 11898-1) is greater then 8.
|
||||||
|
* CAN_CTRLMODE_CC_LEN8_DLC flag has to be enabled in CAN driver.
|
||||||
|
* @data: CAN frame payload (up to 8 byte)
|
||||||
*/
|
*/
|
||||||
struct can_frame {
|
struct can_frame {
|
||||||
canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
|
canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
|
||||||
__u8 can_dlc; /* frame payload length in byte (0 .. CAN_MAX_DLEN) */
|
union {
|
||||||
__u8 __pad; /* padding */
|
/* CAN frame payload length in byte (0 .. CAN_MAX_DLEN)
|
||||||
__u8 __res0; /* reserved / padding */
|
* was previously named can_dlc so we need to carry that
|
||||||
__u8 __res1; /* reserved / padding */
|
* name for legacy support
|
||||||
__u8 data[CAN_MAX_DLEN] __attribute__((aligned(8)));
|
*/
|
||||||
|
__u8 len;
|
||||||
|
__u8 can_dlc; /* deprecated */
|
||||||
|
};
|
||||||
|
__u8 __pad; /* padding */
|
||||||
|
__u8 __res0; /* reserved / padding */
|
||||||
|
__u8 len8_dlc; /* optional DLC for 8 byte payload length (9 .. 15) */
|
||||||
|
__u8 data[CAN_MAX_DLEN] __attribute__((aligned(8)));
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,7 @@ struct can_ctrlmode {
|
||||||
#define CAN_CTRLMODE_FD 0x20 /* CAN FD mode */
|
#define CAN_CTRLMODE_FD 0x20 /* CAN FD mode */
|
||||||
#define CAN_CTRLMODE_PRESUME_ACK 0x40 /* Ignore missing CAN ACKs */
|
#define CAN_CTRLMODE_PRESUME_ACK 0x40 /* Ignore missing CAN ACKs */
|
||||||
#define CAN_CTRLMODE_FD_NON_ISO 0x80 /* CAN FD in non-ISO mode */
|
#define CAN_CTRLMODE_FD_NON_ISO 0x80 /* CAN FD in non-ISO mode */
|
||||||
|
#define CAN_CTRLMODE_CC_LEN8_DLC 0x100 /* Classic CAN DLC option */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* CAN device statistics
|
* CAN device statistics
|
||||||
|
|
|
||||||
|
|
@ -121,6 +121,7 @@ enum {
|
||||||
IFLA_BRIDGE_VLAN_INFO,
|
IFLA_BRIDGE_VLAN_INFO,
|
||||||
IFLA_BRIDGE_VLAN_TUNNEL_INFO,
|
IFLA_BRIDGE_VLAN_TUNNEL_INFO,
|
||||||
IFLA_BRIDGE_MRP,
|
IFLA_BRIDGE_MRP,
|
||||||
|
IFLA_BRIDGE_CFM,
|
||||||
__IFLA_BRIDGE_MAX,
|
__IFLA_BRIDGE_MAX,
|
||||||
};
|
};
|
||||||
#define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1)
|
#define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1)
|
||||||
|
|
@ -328,6 +329,130 @@ struct br_mrp_start_in_test {
|
||||||
__u16 in_id;
|
__u16 in_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
IFLA_BRIDGE_CFM_UNSPEC,
|
||||||
|
IFLA_BRIDGE_CFM_MEP_CREATE,
|
||||||
|
IFLA_BRIDGE_CFM_MEP_DELETE,
|
||||||
|
IFLA_BRIDGE_CFM_MEP_CONFIG,
|
||||||
|
IFLA_BRIDGE_CFM_CC_CONFIG,
|
||||||
|
IFLA_BRIDGE_CFM_CC_PEER_MEP_ADD,
|
||||||
|
IFLA_BRIDGE_CFM_CC_PEER_MEP_REMOVE,
|
||||||
|
IFLA_BRIDGE_CFM_CC_RDI,
|
||||||
|
IFLA_BRIDGE_CFM_CC_CCM_TX,
|
||||||
|
IFLA_BRIDGE_CFM_MEP_CREATE_INFO,
|
||||||
|
IFLA_BRIDGE_CFM_MEP_CONFIG_INFO,
|
||||||
|
IFLA_BRIDGE_CFM_CC_CONFIG_INFO,
|
||||||
|
IFLA_BRIDGE_CFM_CC_RDI_INFO,
|
||||||
|
IFLA_BRIDGE_CFM_CC_CCM_TX_INFO,
|
||||||
|
IFLA_BRIDGE_CFM_CC_PEER_MEP_INFO,
|
||||||
|
IFLA_BRIDGE_CFM_MEP_STATUS_INFO,
|
||||||
|
IFLA_BRIDGE_CFM_CC_PEER_STATUS_INFO,
|
||||||
|
__IFLA_BRIDGE_CFM_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define IFLA_BRIDGE_CFM_MAX (__IFLA_BRIDGE_CFM_MAX - 1)
|
||||||
|
|
||||||
|
enum {
|
||||||
|
IFLA_BRIDGE_CFM_MEP_CREATE_UNSPEC,
|
||||||
|
IFLA_BRIDGE_CFM_MEP_CREATE_INSTANCE,
|
||||||
|
IFLA_BRIDGE_CFM_MEP_CREATE_DOMAIN,
|
||||||
|
IFLA_BRIDGE_CFM_MEP_CREATE_DIRECTION,
|
||||||
|
IFLA_BRIDGE_CFM_MEP_CREATE_IFINDEX,
|
||||||
|
__IFLA_BRIDGE_CFM_MEP_CREATE_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define IFLA_BRIDGE_CFM_MEP_CREATE_MAX (__IFLA_BRIDGE_CFM_MEP_CREATE_MAX - 1)
|
||||||
|
|
||||||
|
enum {
|
||||||
|
IFLA_BRIDGE_CFM_MEP_DELETE_UNSPEC,
|
||||||
|
IFLA_BRIDGE_CFM_MEP_DELETE_INSTANCE,
|
||||||
|
__IFLA_BRIDGE_CFM_MEP_DELETE_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define IFLA_BRIDGE_CFM_MEP_DELETE_MAX (__IFLA_BRIDGE_CFM_MEP_DELETE_MAX - 1)
|
||||||
|
|
||||||
|
enum {
|
||||||
|
IFLA_BRIDGE_CFM_MEP_CONFIG_UNSPEC,
|
||||||
|
IFLA_BRIDGE_CFM_MEP_CONFIG_INSTANCE,
|
||||||
|
IFLA_BRIDGE_CFM_MEP_CONFIG_UNICAST_MAC,
|
||||||
|
IFLA_BRIDGE_CFM_MEP_CONFIG_MDLEVEL,
|
||||||
|
IFLA_BRIDGE_CFM_MEP_CONFIG_MEPID,
|
||||||
|
__IFLA_BRIDGE_CFM_MEP_CONFIG_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define IFLA_BRIDGE_CFM_MEP_CONFIG_MAX (__IFLA_BRIDGE_CFM_MEP_CONFIG_MAX - 1)
|
||||||
|
|
||||||
|
enum {
|
||||||
|
IFLA_BRIDGE_CFM_CC_CONFIG_UNSPEC,
|
||||||
|
IFLA_BRIDGE_CFM_CC_CONFIG_INSTANCE,
|
||||||
|
IFLA_BRIDGE_CFM_CC_CONFIG_ENABLE,
|
||||||
|
IFLA_BRIDGE_CFM_CC_CONFIG_EXP_INTERVAL,
|
||||||
|
IFLA_BRIDGE_CFM_CC_CONFIG_EXP_MAID,
|
||||||
|
__IFLA_BRIDGE_CFM_CC_CONFIG_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define IFLA_BRIDGE_CFM_CC_CONFIG_MAX (__IFLA_BRIDGE_CFM_CC_CONFIG_MAX - 1)
|
||||||
|
|
||||||
|
enum {
|
||||||
|
IFLA_BRIDGE_CFM_CC_PEER_MEP_UNSPEC,
|
||||||
|
IFLA_BRIDGE_CFM_CC_PEER_MEP_INSTANCE,
|
||||||
|
IFLA_BRIDGE_CFM_CC_PEER_MEPID,
|
||||||
|
__IFLA_BRIDGE_CFM_CC_PEER_MEP_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define IFLA_BRIDGE_CFM_CC_PEER_MEP_MAX (__IFLA_BRIDGE_CFM_CC_PEER_MEP_MAX - 1)
|
||||||
|
|
||||||
|
enum {
|
||||||
|
IFLA_BRIDGE_CFM_CC_RDI_UNSPEC,
|
||||||
|
IFLA_BRIDGE_CFM_CC_RDI_INSTANCE,
|
||||||
|
IFLA_BRIDGE_CFM_CC_RDI_RDI,
|
||||||
|
__IFLA_BRIDGE_CFM_CC_RDI_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define IFLA_BRIDGE_CFM_CC_RDI_MAX (__IFLA_BRIDGE_CFM_CC_RDI_MAX - 1)
|
||||||
|
|
||||||
|
enum {
|
||||||
|
IFLA_BRIDGE_CFM_CC_CCM_TX_UNSPEC,
|
||||||
|
IFLA_BRIDGE_CFM_CC_CCM_TX_INSTANCE,
|
||||||
|
IFLA_BRIDGE_CFM_CC_CCM_TX_DMAC,
|
||||||
|
IFLA_BRIDGE_CFM_CC_CCM_TX_SEQ_NO_UPDATE,
|
||||||
|
IFLA_BRIDGE_CFM_CC_CCM_TX_PERIOD,
|
||||||
|
IFLA_BRIDGE_CFM_CC_CCM_TX_IF_TLV,
|
||||||
|
IFLA_BRIDGE_CFM_CC_CCM_TX_IF_TLV_VALUE,
|
||||||
|
IFLA_BRIDGE_CFM_CC_CCM_TX_PORT_TLV,
|
||||||
|
IFLA_BRIDGE_CFM_CC_CCM_TX_PORT_TLV_VALUE,
|
||||||
|
__IFLA_BRIDGE_CFM_CC_CCM_TX_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define IFLA_BRIDGE_CFM_CC_CCM_TX_MAX (__IFLA_BRIDGE_CFM_CC_CCM_TX_MAX - 1)
|
||||||
|
|
||||||
|
enum {
|
||||||
|
IFLA_BRIDGE_CFM_MEP_STATUS_UNSPEC,
|
||||||
|
IFLA_BRIDGE_CFM_MEP_STATUS_INSTANCE,
|
||||||
|
IFLA_BRIDGE_CFM_MEP_STATUS_OPCODE_UNEXP_SEEN,
|
||||||
|
IFLA_BRIDGE_CFM_MEP_STATUS_VERSION_UNEXP_SEEN,
|
||||||
|
IFLA_BRIDGE_CFM_MEP_STATUS_RX_LEVEL_LOW_SEEN,
|
||||||
|
__IFLA_BRIDGE_CFM_MEP_STATUS_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define IFLA_BRIDGE_CFM_MEP_STATUS_MAX (__IFLA_BRIDGE_CFM_MEP_STATUS_MAX - 1)
|
||||||
|
|
||||||
|
enum {
|
||||||
|
IFLA_BRIDGE_CFM_CC_PEER_STATUS_UNSPEC,
|
||||||
|
IFLA_BRIDGE_CFM_CC_PEER_STATUS_INSTANCE,
|
||||||
|
IFLA_BRIDGE_CFM_CC_PEER_STATUS_PEER_MEPID,
|
||||||
|
IFLA_BRIDGE_CFM_CC_PEER_STATUS_CCM_DEFECT,
|
||||||
|
IFLA_BRIDGE_CFM_CC_PEER_STATUS_RDI,
|
||||||
|
IFLA_BRIDGE_CFM_CC_PEER_STATUS_PORT_TLV_VALUE,
|
||||||
|
IFLA_BRIDGE_CFM_CC_PEER_STATUS_IF_TLV_VALUE,
|
||||||
|
IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEEN,
|
||||||
|
IFLA_BRIDGE_CFM_CC_PEER_STATUS_TLV_SEEN,
|
||||||
|
IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEQ_UNEXP_SEEN,
|
||||||
|
__IFLA_BRIDGE_CFM_CC_PEER_STATUS_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define IFLA_BRIDGE_CFM_CC_PEER_STATUS_MAX (__IFLA_BRIDGE_CFM_CC_PEER_STATUS_MAX - 1)
|
||||||
|
|
||||||
struct bridge_stp_xstats {
|
struct bridge_stp_xstats {
|
||||||
__u64 transition_blk;
|
__u64 transition_blk;
|
||||||
__u64 transition_fwd;
|
__u64 transition_fwd;
|
||||||
|
|
@ -526,6 +651,7 @@ struct br_mdb_entry {
|
||||||
union {
|
union {
|
||||||
__be32 ip4;
|
__be32 ip4;
|
||||||
struct in6_addr ip6;
|
struct in6_addr ip6;
|
||||||
|
unsigned char mac_addr[ETH_ALEN];
|
||||||
} u;
|
} u;
|
||||||
__be16 proto;
|
__be16 proto;
|
||||||
} addr;
|
} addr;
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,7 @@
|
||||||
#define ETH_P_1588 0x88F7 /* IEEE 1588 Timesync */
|
#define ETH_P_1588 0x88F7 /* IEEE 1588 Timesync */
|
||||||
#define ETH_P_NCSI 0x88F8 /* NCSI protocol */
|
#define ETH_P_NCSI 0x88F8 /* NCSI protocol */
|
||||||
#define ETH_P_PRP 0x88FB /* IEC 62439-3 PRP/HSRv0 */
|
#define ETH_P_PRP 0x88FB /* IEC 62439-3 PRP/HSRv0 */
|
||||||
|
#define ETH_P_CFM 0x8902 /* Connectivity Fault Management */
|
||||||
#define ETH_P_FCOE 0x8906 /* Fibre Channel over Ethernet */
|
#define ETH_P_FCOE 0x8906 /* Fibre Channel over Ethernet */
|
||||||
#define ETH_P_IBOE 0x8915 /* Infiniband over Ethernet */
|
#define ETH_P_IBOE 0x8915 /* Infiniband over Ethernet */
|
||||||
#define ETH_P_TDLS 0x890D /* TDLS */
|
#define ETH_P_TDLS 0x890D /* TDLS */
|
||||||
|
|
|
||||||
|
|
@ -586,6 +586,8 @@ enum {
|
||||||
IFLA_MACVLAN_MACADDR,
|
IFLA_MACVLAN_MACADDR,
|
||||||
IFLA_MACVLAN_MACADDR_DATA,
|
IFLA_MACVLAN_MACADDR_DATA,
|
||||||
IFLA_MACVLAN_MACADDR_COUNT,
|
IFLA_MACVLAN_MACADDR_COUNT,
|
||||||
|
IFLA_MACVLAN_BC_QUEUE_LEN,
|
||||||
|
IFLA_MACVLAN_BC_QUEUE_LEN_USED,
|
||||||
__IFLA_MACVLAN_MAX,
|
__IFLA_MACVLAN_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
#ifndef __LINUX_IF_PACKET_H
|
#ifndef __LINUX_IF_PACKET_H
|
||||||
#define __LINUX_IF_PACKET_H
|
#define __LINUX_IF_PACKET_H
|
||||||
|
|
||||||
|
#include <asm/byteorder.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
|
|
||||||
struct sockaddr_pkt {
|
struct sockaddr_pkt {
|
||||||
|
|
@ -296,6 +297,17 @@ struct packet_mreq {
|
||||||
unsigned char mr_address[8];
|
unsigned char mr_address[8];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct fanout_args {
|
||||||
|
#if defined(__LITTLE_ENDIAN_BITFIELD)
|
||||||
|
__u16 id;
|
||||||
|
__u16 type_flags;
|
||||||
|
#else
|
||||||
|
__u16 type_flags;
|
||||||
|
__u16 id;
|
||||||
|
#endif
|
||||||
|
__u32 max_num_members;
|
||||||
|
};
|
||||||
|
|
||||||
#define PACKET_MR_MULTICAST 0
|
#define PACKET_MR_MULTICAST 0
|
||||||
#define PACKET_MR_PROMISC 1
|
#define PACKET_MR_PROMISC 1
|
||||||
#define PACKET_MR_ALLMULTI 2
|
#define PACKET_MR_ALLMULTI 2
|
||||||
|
|
|
||||||
|
|
@ -92,11 +92,11 @@ enum {
|
||||||
/* Reserve empty slots */
|
/* Reserve empty slots */
|
||||||
IPSET_ATTR_CADT_MAX = 16,
|
IPSET_ATTR_CADT_MAX = 16,
|
||||||
/* Create-only specific attributes */
|
/* Create-only specific attributes */
|
||||||
IPSET_ATTR_GC,
|
IPSET_ATTR_INITVAL, /* was unused IPSET_ATTR_GC */
|
||||||
IPSET_ATTR_HASHSIZE,
|
IPSET_ATTR_HASHSIZE,
|
||||||
IPSET_ATTR_MAXELEM,
|
IPSET_ATTR_MAXELEM,
|
||||||
IPSET_ATTR_NETMASK,
|
IPSET_ATTR_NETMASK,
|
||||||
IPSET_ATTR_PROBES,
|
IPSET_ATTR_BUCKETSIZE, /* was unused IPSET_ATTR_PROBES */
|
||||||
IPSET_ATTR_RESIZE,
|
IPSET_ATTR_RESIZE,
|
||||||
IPSET_ATTR_SIZE,
|
IPSET_ATTR_SIZE,
|
||||||
/* Kernel-only */
|
/* Kernel-only */
|
||||||
|
|
@ -214,6 +214,8 @@ enum ipset_cadt_flags {
|
||||||
enum ipset_create_flags {
|
enum ipset_create_flags {
|
||||||
IPSET_CREATE_FLAG_BIT_FORCEADD = 0,
|
IPSET_CREATE_FLAG_BIT_FORCEADD = 0,
|
||||||
IPSET_CREATE_FLAG_FORCEADD = (1 << IPSET_CREATE_FLAG_BIT_FORCEADD),
|
IPSET_CREATE_FLAG_FORCEADD = (1 << IPSET_CREATE_FLAG_BIT_FORCEADD),
|
||||||
|
IPSET_CREATE_FLAG_BIT_BUCKETSIZE = 1,
|
||||||
|
IPSET_CREATE_FLAG_BUCKETSIZE = (1 << IPSET_CREATE_FLAG_BIT_BUCKETSIZE),
|
||||||
IPSET_CREATE_FLAG_BIT_MAX = 7,
|
IPSET_CREATE_FLAG_BIT_MAX = 7,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -396,11 +396,13 @@ struct rtnexthop {
|
||||||
#define RTNH_F_DEAD 1 /* Nexthop is dead (used by multipath) */
|
#define RTNH_F_DEAD 1 /* Nexthop is dead (used by multipath) */
|
||||||
#define RTNH_F_PERVASIVE 2 /* Do recursive gateway lookup */
|
#define RTNH_F_PERVASIVE 2 /* Do recursive gateway lookup */
|
||||||
#define RTNH_F_ONLINK 4 /* Gateway is forced on link */
|
#define RTNH_F_ONLINK 4 /* Gateway is forced on link */
|
||||||
#define RTNH_F_OFFLOAD 8 /* offloaded route */
|
#define RTNH_F_OFFLOAD 8 /* Nexthop is offloaded */
|
||||||
#define RTNH_F_LINKDOWN 16 /* carrier-down on nexthop */
|
#define RTNH_F_LINKDOWN 16 /* carrier-down on nexthop */
|
||||||
#define RTNH_F_UNRESOLVED 32 /* The entry is unresolved (ipmr) */
|
#define RTNH_F_UNRESOLVED 32 /* The entry is unresolved (ipmr) */
|
||||||
|
#define RTNH_F_TRAP 64 /* Nexthop is trapping packets */
|
||||||
|
|
||||||
#define RTNH_COMPARE_MASK (RTNH_F_DEAD | RTNH_F_LINKDOWN | RTNH_F_OFFLOAD)
|
#define RTNH_COMPARE_MASK (RTNH_F_DEAD | RTNH_F_LINKDOWN | \
|
||||||
|
RTNH_F_OFFLOAD | RTNH_F_TRAP)
|
||||||
|
|
||||||
/* Macros to handle hexthops */
|
/* Macros to handle hexthops */
|
||||||
|
|
||||||
|
|
@ -764,12 +766,18 @@ enum {
|
||||||
#define TA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct tcamsg))
|
#define TA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct tcamsg))
|
||||||
/* tcamsg flags stored in attribute TCA_ROOT_FLAGS
|
/* tcamsg flags stored in attribute TCA_ROOT_FLAGS
|
||||||
*
|
*
|
||||||
* TCA_FLAG_LARGE_DUMP_ON user->kernel to request for larger than TCA_ACT_MAX_PRIO
|
* TCA_ACT_FLAG_LARGE_DUMP_ON user->kernel to request for larger than
|
||||||
* actions in a dump. All dump responses will contain the number of actions
|
* TCA_ACT_MAX_PRIO actions in a dump. All dump responses will contain the
|
||||||
* being dumped stored in for user app's consumption in TCA_ROOT_COUNT
|
* number of actions being dumped stored in for user app's consumption in
|
||||||
|
* TCA_ROOT_COUNT
|
||||||
|
*
|
||||||
|
* TCA_ACT_FLAG_TERSE_DUMP user->kernel to request terse (brief) dump that only
|
||||||
|
* includes essential action info (kind, index, etc.)
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#define TCA_FLAG_LARGE_DUMP_ON (1 << 0)
|
#define TCA_FLAG_LARGE_DUMP_ON (1 << 0)
|
||||||
|
#define TCA_ACT_FLAG_LARGE_DUMP_ON TCA_FLAG_LARGE_DUMP_ON
|
||||||
|
#define TCA_ACT_FLAG_TERSE_DUMP (1 << 1)
|
||||||
|
|
||||||
/* New extended info filters for IFLA_EXT_MASK */
|
/* New extended info filters for IFLA_EXT_MASK */
|
||||||
#define RTEXT_FILTER_VF (1 << 0)
|
#define RTEXT_FILTER_VF (1 << 0)
|
||||||
|
|
@ -777,6 +785,8 @@ enum {
|
||||||
#define RTEXT_FILTER_BRVLAN_COMPRESSED (1 << 2)
|
#define RTEXT_FILTER_BRVLAN_COMPRESSED (1 << 2)
|
||||||
#define RTEXT_FILTER_SKIP_STATS (1 << 3)
|
#define RTEXT_FILTER_SKIP_STATS (1 << 3)
|
||||||
#define RTEXT_FILTER_MRP (1 << 4)
|
#define RTEXT_FILTER_MRP (1 << 4)
|
||||||
|
#define RTEXT_FILTER_CFM_CONFIG (1 << 5)
|
||||||
|
#define RTEXT_FILTER_CFM_STATUS (1 << 6)
|
||||||
|
|
||||||
/* End of information exported to user level */
|
/* End of information exported to user level */
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -140,6 +140,7 @@ typedef __s32 sctp_assoc_t;
|
||||||
#define SCTP_ECN_SUPPORTED 130
|
#define SCTP_ECN_SUPPORTED 130
|
||||||
#define SCTP_EXPOSE_POTENTIALLY_FAILED_STATE 131
|
#define SCTP_EXPOSE_POTENTIALLY_FAILED_STATE 131
|
||||||
#define SCTP_EXPOSE_PF_STATE SCTP_EXPOSE_POTENTIALLY_FAILED_STATE
|
#define SCTP_EXPOSE_PF_STATE SCTP_EXPOSE_POTENTIALLY_FAILED_STATE
|
||||||
|
#define SCTP_REMOTE_UDP_ENCAPS_PORT 132
|
||||||
|
|
||||||
/* PR-SCTP policies */
|
/* PR-SCTP policies */
|
||||||
#define SCTP_PR_SCTP_NONE 0x0000
|
#define SCTP_PR_SCTP_NONE 0x0000
|
||||||
|
|
@ -1191,6 +1192,12 @@ struct sctp_event {
|
||||||
uint8_t se_on;
|
uint8_t se_on;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct sctp_udpencaps {
|
||||||
|
sctp_assoc_t sue_assoc_id;
|
||||||
|
struct sockaddr_storage sue_address;
|
||||||
|
uint16_t sue_port;
|
||||||
|
};
|
||||||
|
|
||||||
/* SCTP Stream schedulers */
|
/* SCTP Stream schedulers */
|
||||||
enum sctp_sched_type {
|
enum sctp_sched_type {
|
||||||
SCTP_SS_FCFS,
|
SCTP_SS_FCFS,
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ enum {
|
||||||
SEG6_LOCAL_IIF,
|
SEG6_LOCAL_IIF,
|
||||||
SEG6_LOCAL_OIF,
|
SEG6_LOCAL_OIF,
|
||||||
SEG6_LOCAL_BPF,
|
SEG6_LOCAL_BPF,
|
||||||
|
SEG6_LOCAL_VRFTABLE,
|
||||||
__SEG6_LOCAL_MAX,
|
__SEG6_LOCAL_MAX,
|
||||||
};
|
};
|
||||||
#define SEG6_LOCAL_MAX (__SEG6_LOCAL_MAX - 1)
|
#define SEG6_LOCAL_MAX (__SEG6_LOCAL_MAX - 1)
|
||||||
|
|
|
||||||
|
|
@ -159,6 +159,7 @@ enum
|
||||||
UDP_MIB_SNDBUFERRORS, /* SndbufErrors */
|
UDP_MIB_SNDBUFERRORS, /* SndbufErrors */
|
||||||
UDP_MIB_CSUMERRORS, /* InCsumErrors */
|
UDP_MIB_CSUMERRORS, /* InCsumErrors */
|
||||||
UDP_MIB_IGNOREDMULTI, /* IgnoredMulti */
|
UDP_MIB_IGNOREDMULTI, /* IgnoredMulti */
|
||||||
|
UDP_MIB_MEMERRORS, /* MemErrors */
|
||||||
__UDP_MIB_MAX
|
__UDP_MIB_MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -343,11 +343,15 @@ struct tcp_diag_md5sig {
|
||||||
|
|
||||||
/* setsockopt(fd, IPPROTO_TCP, TCP_ZEROCOPY_RECEIVE, ...) */
|
/* setsockopt(fd, IPPROTO_TCP, TCP_ZEROCOPY_RECEIVE, ...) */
|
||||||
|
|
||||||
|
#define TCP_RECEIVE_ZEROCOPY_FLAG_TLB_CLEAN_HINT 0x1
|
||||||
struct tcp_zerocopy_receive {
|
struct tcp_zerocopy_receive {
|
||||||
__u64 address; /* in: address of mapping */
|
__u64 address; /* in: address of mapping */
|
||||||
__u32 length; /* in/out: number of bytes to map/mapped */
|
__u32 length; /* in/out: number of bytes to map/mapped */
|
||||||
__u32 recv_skip_hint; /* out: amount of bytes to skip */
|
__u32 recv_skip_hint; /* out: amount of bytes to skip */
|
||||||
__u32 inq; /* out: amount of bytes in read queue */
|
__u32 inq; /* out: amount of bytes in read queue */
|
||||||
__s32 err; /* out: socket error */
|
__s32 err; /* out: socket error */
|
||||||
|
__u64 copybuf_address; /* in: copybuf address (small reads) */
|
||||||
|
__s32 copybuf_len; /* in/out: copybuf bytes avail/used or error */
|
||||||
|
__u32 flags; /* in: flags */
|
||||||
};
|
};
|
||||||
#endif /* _LINUX_TCP_H */
|
#endif /* _LINUX_TCP_H */
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,13 @@
|
||||||
#define TLS_CIPHER_AES_CCM_128_TAG_SIZE 16
|
#define TLS_CIPHER_AES_CCM_128_TAG_SIZE 16
|
||||||
#define TLS_CIPHER_AES_CCM_128_REC_SEQ_SIZE 8
|
#define TLS_CIPHER_AES_CCM_128_REC_SEQ_SIZE 8
|
||||||
|
|
||||||
|
#define TLS_CIPHER_CHACHA20_POLY1305 54
|
||||||
|
#define TLS_CIPHER_CHACHA20_POLY1305_IV_SIZE 12
|
||||||
|
#define TLS_CIPHER_CHACHA20_POLY1305_KEY_SIZE 32
|
||||||
|
#define TLS_CIPHER_CHACHA20_POLY1305_SALT_SIZE 0
|
||||||
|
#define TLS_CIPHER_CHACHA20_POLY1305_TAG_SIZE 16
|
||||||
|
#define TLS_CIPHER_CHACHA20_POLY1305_REC_SEQ_SIZE 8
|
||||||
|
|
||||||
#define TLS_SET_RECORD_TYPE 1
|
#define TLS_SET_RECORD_TYPE 1
|
||||||
#define TLS_GET_RECORD_TYPE 2
|
#define TLS_GET_RECORD_TYPE 2
|
||||||
|
|
||||||
|
|
@ -109,6 +116,14 @@ struct tls12_crypto_info_aes_ccm_128 {
|
||||||
unsigned char rec_seq[TLS_CIPHER_AES_CCM_128_REC_SEQ_SIZE];
|
unsigned char rec_seq[TLS_CIPHER_AES_CCM_128_REC_SEQ_SIZE];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct tls12_crypto_info_chacha20_poly1305 {
|
||||||
|
struct tls_crypto_info info;
|
||||||
|
unsigned char iv[TLS_CIPHER_CHACHA20_POLY1305_IV_SIZE];
|
||||||
|
unsigned char key[TLS_CIPHER_CHACHA20_POLY1305_KEY_SIZE];
|
||||||
|
unsigned char salt[TLS_CIPHER_CHACHA20_POLY1305_SALT_SIZE];
|
||||||
|
unsigned char rec_seq[TLS_CIPHER_CHACHA20_POLY1305_REC_SEQ_SIZE];
|
||||||
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
TLS_INFO_UNSPEC,
|
TLS_INFO_UNSPEC,
|
||||||
TLS_INFO_VERSION,
|
TLS_INFO_VERSION,
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@
|
||||||
|
|
||||||
extern int preferred_family;
|
extern int preferred_family;
|
||||||
extern int human_readable;
|
extern int human_readable;
|
||||||
extern int use_iec;
|
|
||||||
extern int show_stats;
|
extern int show_stats;
|
||||||
extern int show_details;
|
extern int show_details;
|
||||||
extern int show_raw;
|
extern int show_raw;
|
||||||
|
|
@ -163,6 +162,9 @@ int get_be64(__be64 *val, const char *arg, int base);
|
||||||
int get_be32(__be32 *val, const char *arg, int base);
|
int get_be32(__be32 *val, const char *arg, int base);
|
||||||
int get_be16(__be16 *val, const char *arg, int base);
|
int get_be16(__be16 *val, const char *arg, int base);
|
||||||
int get_addr64(__u64 *ap, const char *cp);
|
int get_addr64(__u64 *ap, const char *cp);
|
||||||
|
int get_rate(unsigned int *rate, const char *str);
|
||||||
|
int get_rate64(__u64 *rate, const char *str);
|
||||||
|
int get_size(unsigned int *size, const char *str);
|
||||||
|
|
||||||
int hex2mem(const char *buf, uint8_t *mem, int count);
|
int hex2mem(const char *buf, uint8_t *mem, int count);
|
||||||
char *hexstring_n2a(const __u8 *str, int len, char *buf, int blen);
|
char *hexstring_n2a(const __u8 *str, int len, char *buf, int blen);
|
||||||
|
|
@ -322,4 +324,15 @@ int get_time64(__s64 *time, const char *str);
|
||||||
char *sprint_time(__u32 time, char *buf);
|
char *sprint_time(__u32 time, char *buf);
|
||||||
char *sprint_time64(__s64 time, char *buf);
|
char *sprint_time64(__s64 time, char *buf);
|
||||||
|
|
||||||
|
int do_batch(const char *name, bool force,
|
||||||
|
int (*cmd)(int argc, char *argv[], void *user), void *user);
|
||||||
|
|
||||||
|
int parse_one_of(const char *msg, const char *realval, const char * const *list,
|
||||||
|
size_t len, int *p_err);
|
||||||
|
bool parse_on_off(const char *msg, const char *realval, int *p_err);
|
||||||
|
|
||||||
|
int parse_mapping(int *argcp, char ***argvp, bool allow_all,
|
||||||
|
int (*mapping_cb)(__u32 key, char *value, void *data),
|
||||||
|
void *mapping_cb_data);
|
||||||
|
|
||||||
#endif /* __UTILS_H__ */
|
#endif /* __UTILS_H__ */
|
||||||
|
|
|
||||||
71
ip/ip.c
71
ip/ip.c
|
|
@ -24,6 +24,11 @@
|
||||||
#include "namespace.h"
|
#include "namespace.h"
|
||||||
#include "color.h"
|
#include "color.h"
|
||||||
#include "rt_names.h"
|
#include "rt_names.h"
|
||||||
|
#include "bpf_util.h"
|
||||||
|
|
||||||
|
#ifndef LIBDIR
|
||||||
|
#define LIBDIR "/usr/lib"
|
||||||
|
#endif
|
||||||
|
|
||||||
int preferred_family = AF_UNSPEC;
|
int preferred_family = AF_UNSPEC;
|
||||||
int human_readable;
|
int human_readable;
|
||||||
|
|
@ -41,6 +46,17 @@ bool do_all;
|
||||||
|
|
||||||
struct rtnl_handle rth = { .fd = -1 };
|
struct rtnl_handle rth = { .fd = -1 };
|
||||||
|
|
||||||
|
const char *get_ip_lib_dir(void)
|
||||||
|
{
|
||||||
|
const char *lib_dir;
|
||||||
|
|
||||||
|
lib_dir = getenv("IP_LIB_DIR");
|
||||||
|
if (!lib_dir)
|
||||||
|
lib_dir = LIBDIR "/ip";
|
||||||
|
|
||||||
|
return lib_dir;
|
||||||
|
}
|
||||||
|
|
||||||
static void usage(void) __attribute__((noreturn));
|
static void usage(void) __attribute__((noreturn));
|
||||||
|
|
||||||
static void usage(void)
|
static void usage(void)
|
||||||
|
|
@ -121,60 +137,35 @@ static int do_cmd(const char *argv0, int argc, char **argv)
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ip_batch_cmd(int argc, char *argv[], void *data)
|
||||||
|
{
|
||||||
|
const int *orig_family = data;
|
||||||
|
|
||||||
|
preferred_family = *orig_family;
|
||||||
|
return do_cmd(argv[0], argc, argv);
|
||||||
|
}
|
||||||
|
|
||||||
static int batch(const char *name)
|
static int batch(const char *name)
|
||||||
{
|
{
|
||||||
char *line = NULL;
|
|
||||||
size_t len = 0;
|
|
||||||
int ret = EXIT_SUCCESS;
|
|
||||||
int orig_family = preferred_family;
|
int orig_family = preferred_family;
|
||||||
|
int ret;
|
||||||
batch_mode = 1;
|
|
||||||
|
|
||||||
if (name && strcmp(name, "-") != 0) {
|
|
||||||
if (freopen(name, "r", stdin) == NULL) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"Cannot open file \"%s\" for reading: %s\n",
|
|
||||||
name, strerror(errno));
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rtnl_open(&rth, 0) < 0) {
|
if (rtnl_open(&rth, 0) < 0) {
|
||||||
fprintf(stderr, "Cannot open rtnetlink\n");
|
fprintf(stderr, "Cannot open rtnetlink\n");
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
cmdlineno = 0;
|
ret = do_batch(name, force, ip_batch_cmd, &orig_family);
|
||||||
while (getcmdline(&line, &len, stdin) != -1) {
|
|
||||||
char *largv[100];
|
|
||||||
int largc;
|
|
||||||
|
|
||||||
preferred_family = orig_family;
|
|
||||||
|
|
||||||
largc = makeargs(line, largv, 100);
|
|
||||||
if (largc == 0)
|
|
||||||
continue; /* blank line */
|
|
||||||
|
|
||||||
if (do_cmd(largv[0], largc, largv)) {
|
|
||||||
fprintf(stderr, "Command failed %s:%d\n",
|
|
||||||
name, cmdlineno);
|
|
||||||
ret = EXIT_FAILURE;
|
|
||||||
if (!force)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (line)
|
|
||||||
free(line);
|
|
||||||
|
|
||||||
rtnl_close(&rth);
|
rtnl_close(&rth);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
char *basename;
|
const char *libbpf_version;
|
||||||
char *batch_file = NULL;
|
char *batch_file = NULL;
|
||||||
|
char *basename;
|
||||||
int color = 0;
|
int color = 0;
|
||||||
|
|
||||||
/* to run vrf exec without root, capabilities might be set, drop them
|
/* to run vrf exec without root, capabilities might be set, drop them
|
||||||
|
|
@ -255,7 +246,11 @@ int main(int argc, char **argv)
|
||||||
++timestamp;
|
++timestamp;
|
||||||
++timestamp_short;
|
++timestamp_short;
|
||||||
} else if (matches(opt, "-Version") == 0) {
|
} else if (matches(opt, "-Version") == 0) {
|
||||||
printf("ip utility, iproute2-%s\n", version);
|
printf("ip utility, iproute2-%s", version);
|
||||||
|
libbpf_version = get_libbpf_version();
|
||||||
|
if (libbpf_version)
|
||||||
|
printf(", libbpf %s", libbpf_version);
|
||||||
|
printf("\n");
|
||||||
exit(0);
|
exit(0);
|
||||||
} else if (matches(opt, "-force") == 0) {
|
} else if (matches(opt, "-force") == 0) {
|
||||||
++force;
|
++force;
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@
|
||||||
|
|
||||||
#include "json_print.h"
|
#include "json_print.h"
|
||||||
|
|
||||||
|
extern int use_iec;
|
||||||
|
|
||||||
struct link_filter {
|
struct link_filter {
|
||||||
int ifindex;
|
int ifindex;
|
||||||
int family;
|
int family;
|
||||||
|
|
@ -27,6 +29,8 @@ struct link_filter {
|
||||||
int target_nsid;
|
int target_nsid;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const char *get_ip_lib_dir(void);
|
||||||
|
|
||||||
int get_operstate(const char *name);
|
int get_operstate(const char *name);
|
||||||
int print_linkinfo(struct nlmsghdr *n, void *arg);
|
int print_linkinfo(struct nlmsghdr *n, void *arg);
|
||||||
int print_addrinfo(struct nlmsghdr *n, void *arg);
|
int print_addrinfo(struct nlmsghdr *n, void *arg);
|
||||||
|
|
|
||||||
52
ip/iplink.c
52
ip/iplink.c
|
|
@ -34,9 +34,6 @@
|
||||||
#include "namespace.h"
|
#include "namespace.h"
|
||||||
|
|
||||||
#define IPLINK_IOCTL_COMPAT 1
|
#define IPLINK_IOCTL_COMPAT 1
|
||||||
#ifndef LIBDIR
|
|
||||||
#define LIBDIR "/usr/lib"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef GSO_MAX_SIZE
|
#ifndef GSO_MAX_SIZE
|
||||||
#define GSO_MAX_SIZE 65536
|
#define GSO_MAX_SIZE 65536
|
||||||
|
|
@ -157,7 +154,7 @@ struct link_util *get_link_kind(const char *id)
|
||||||
if (strcmp(l->id, id) == 0)
|
if (strcmp(l->id, id) == 0)
|
||||||
return l;
|
return l;
|
||||||
|
|
||||||
snprintf(buf, sizeof(buf), LIBDIR "/ip/link_%s.so", id);
|
snprintf(buf, sizeof(buf), "%s/link_%s.so", get_ip_lib_dir(), id);
|
||||||
dlh = dlopen(buf, RTLD_LAZY);
|
dlh = dlopen(buf, RTLD_LAZY);
|
||||||
if (dlh == NULL) {
|
if (dlh == NULL) {
|
||||||
/* look in current binary, only open once */
|
/* look in current binary, only open once */
|
||||||
|
|
@ -352,6 +349,7 @@ static int iplink_parse_vf(int vf, int *argcp, char ***argvp,
|
||||||
int len, argc = *argcp;
|
int len, argc = *argcp;
|
||||||
char **argv = *argvp;
|
char **argv = *argvp;
|
||||||
struct rtattr *vfinfo;
|
struct rtattr *vfinfo;
|
||||||
|
int ret;
|
||||||
|
|
||||||
tivt.min_tx_rate = -1;
|
tivt.min_tx_rate = -1;
|
||||||
tivt.max_tx_rate = -1;
|
tivt.max_tx_rate = -1;
|
||||||
|
|
@ -464,12 +462,9 @@ static int iplink_parse_vf(int vf, int *argcp, char ***argvp,
|
||||||
struct ifla_vf_spoofchk ivs;
|
struct ifla_vf_spoofchk ivs;
|
||||||
|
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
if (matches(*argv, "on") == 0)
|
ivs.setting = parse_on_off("spoofchk", *argv, &ret);
|
||||||
ivs.setting = 1;
|
if (ret)
|
||||||
else if (matches(*argv, "off") == 0)
|
return ret;
|
||||||
ivs.setting = 0;
|
|
||||||
else
|
|
||||||
return on_off("spoofchk", *argv);
|
|
||||||
ivs.vf = vf;
|
ivs.vf = vf;
|
||||||
addattr_l(&req->n, sizeof(*req), IFLA_VF_SPOOFCHK,
|
addattr_l(&req->n, sizeof(*req), IFLA_VF_SPOOFCHK,
|
||||||
&ivs, sizeof(ivs));
|
&ivs, sizeof(ivs));
|
||||||
|
|
@ -478,12 +473,9 @@ static int iplink_parse_vf(int vf, int *argcp, char ***argvp,
|
||||||
struct ifla_vf_rss_query_en ivs;
|
struct ifla_vf_rss_query_en ivs;
|
||||||
|
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
if (matches(*argv, "on") == 0)
|
ivs.setting = parse_on_off("query_rss", *argv, &ret);
|
||||||
ivs.setting = 1;
|
if (ret)
|
||||||
else if (matches(*argv, "off") == 0)
|
return ret;
|
||||||
ivs.setting = 0;
|
|
||||||
else
|
|
||||||
return on_off("query_rss", *argv);
|
|
||||||
ivs.vf = vf;
|
ivs.vf = vf;
|
||||||
addattr_l(&req->n, sizeof(*req), IFLA_VF_RSS_QUERY_EN,
|
addattr_l(&req->n, sizeof(*req), IFLA_VF_RSS_QUERY_EN,
|
||||||
&ivs, sizeof(ivs));
|
&ivs, sizeof(ivs));
|
||||||
|
|
@ -492,12 +484,9 @@ static int iplink_parse_vf(int vf, int *argcp, char ***argvp,
|
||||||
struct ifla_vf_trust ivt;
|
struct ifla_vf_trust ivt;
|
||||||
|
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
if (matches(*argv, "on") == 0)
|
ivt.setting = parse_on_off("trust", *argv, &ret);
|
||||||
ivt.setting = 1;
|
if (ret)
|
||||||
else if (matches(*argv, "off") == 0)
|
return ret;
|
||||||
ivt.setting = 0;
|
|
||||||
else
|
|
||||||
invarg("Invalid \"trust\" value\n", *argv);
|
|
||||||
ivt.vf = vf;
|
ivt.vf = vf;
|
||||||
addattr_l(&req->n, sizeof(*req), IFLA_VF_TRUST,
|
addattr_l(&req->n, sizeof(*req), IFLA_VF_TRUST,
|
||||||
&ivt, sizeof(ivt));
|
&ivt, sizeof(ivt));
|
||||||
|
|
@ -595,6 +584,7 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req, char **type)
|
||||||
int index = 0;
|
int index = 0;
|
||||||
int group = -1;
|
int group = -1;
|
||||||
int addr_len = 0;
|
int addr_len = 0;
|
||||||
|
int err;
|
||||||
|
|
||||||
ret = argc;
|
ret = argc;
|
||||||
|
|
||||||
|
|
@ -738,12 +728,9 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req, char **type)
|
||||||
int carrier;
|
int carrier;
|
||||||
|
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
if (strcmp(*argv, "on") == 0)
|
carrier = parse_on_off("carrier", *argv, &err);
|
||||||
carrier = 1;
|
if (err)
|
||||||
else if (strcmp(*argv, "off") == 0)
|
return err;
|
||||||
carrier = 0;
|
|
||||||
else
|
|
||||||
return on_off("carrier", *argv);
|
|
||||||
|
|
||||||
addattr8(&req->n, sizeof(*req), IFLA_CARRIER, carrier);
|
addattr8(&req->n, sizeof(*req), IFLA_CARRIER, carrier);
|
||||||
} else if (strcmp(*argv, "vf") == 0) {
|
} else if (strcmp(*argv, "vf") == 0) {
|
||||||
|
|
@ -896,12 +883,9 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req, char **type)
|
||||||
unsigned int proto_down;
|
unsigned int proto_down;
|
||||||
|
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
if (strcmp(*argv, "on") == 0)
|
proto_down = parse_on_off("protodown", *argv, &err);
|
||||||
proto_down = 1;
|
if (err)
|
||||||
else if (strcmp(*argv, "off") == 0)
|
return err;
|
||||||
proto_down = 0;
|
|
||||||
else
|
|
||||||
return on_off("protodown", *argv);
|
|
||||||
addattr8(&req->n, sizeof(*req), IFLA_PROTO_DOWN,
|
addattr8(&req->n, sizeof(*req), IFLA_PROTO_DOWN,
|
||||||
proto_down);
|
proto_down);
|
||||||
} else if (strcmp(*argv, "protodown_reason") == 0) {
|
} else if (strcmp(*argv, "protodown_reason") == 0) {
|
||||||
|
|
|
||||||
|
|
@ -76,14 +76,6 @@ static void print_portstate(FILE *f, __u8 state)
|
||||||
print_int(PRINT_ANY, "state_index", "state (%d) ", state);
|
print_int(PRINT_ANY, "state_index", "state (%d) ", state);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _print_onoff(FILE *f, char *json_flag, char *flag, __u8 val)
|
|
||||||
{
|
|
||||||
if (is_json_context())
|
|
||||||
print_bool(PRINT_JSON, flag, NULL, val);
|
|
||||||
else
|
|
||||||
fprintf(f, "%s %s ", flag, val ? "on" : "off");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _print_timer(FILE *f, const char *attr, struct rtattr *timer)
|
static void _print_timer(FILE *f, const char *attr, struct rtattr *timer)
|
||||||
{
|
{
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
|
|
@ -145,27 +137,27 @@ static void bridge_slave_print_opt(struct link_util *lu, FILE *f,
|
||||||
rta_getattr_u32(tb[IFLA_BRPORT_COST]));
|
rta_getattr_u32(tb[IFLA_BRPORT_COST]));
|
||||||
|
|
||||||
if (tb[IFLA_BRPORT_MODE])
|
if (tb[IFLA_BRPORT_MODE])
|
||||||
_print_onoff(f, "mode", "hairpin",
|
print_on_off(PRINT_ANY, "hairpin", "hairpin %s ",
|
||||||
rta_getattr_u8(tb[IFLA_BRPORT_MODE]));
|
rta_getattr_u8(tb[IFLA_BRPORT_MODE]));
|
||||||
|
|
||||||
if (tb[IFLA_BRPORT_GUARD])
|
if (tb[IFLA_BRPORT_GUARD])
|
||||||
_print_onoff(f, "guard", "guard",
|
print_on_off(PRINT_ANY, "guard", "guard %s ",
|
||||||
rta_getattr_u8(tb[IFLA_BRPORT_GUARD]));
|
rta_getattr_u8(tb[IFLA_BRPORT_GUARD]));
|
||||||
|
|
||||||
if (tb[IFLA_BRPORT_PROTECT])
|
if (tb[IFLA_BRPORT_PROTECT])
|
||||||
_print_onoff(f, "protect", "root_block",
|
print_on_off(PRINT_ANY, "root_block", "root_block %s ",
|
||||||
rta_getattr_u8(tb[IFLA_BRPORT_PROTECT]));
|
rta_getattr_u8(tb[IFLA_BRPORT_PROTECT]));
|
||||||
|
|
||||||
if (tb[IFLA_BRPORT_FAST_LEAVE])
|
if (tb[IFLA_BRPORT_FAST_LEAVE])
|
||||||
_print_onoff(f, "fast_leave", "fastleave",
|
print_on_off(PRINT_ANY, "fastleave", "fastleave %s ",
|
||||||
rta_getattr_u8(tb[IFLA_BRPORT_FAST_LEAVE]));
|
rta_getattr_u8(tb[IFLA_BRPORT_FAST_LEAVE]));
|
||||||
|
|
||||||
if (tb[IFLA_BRPORT_LEARNING])
|
if (tb[IFLA_BRPORT_LEARNING])
|
||||||
_print_onoff(f, "learning", "learning",
|
print_on_off(PRINT_ANY, "learning", "learning %s ",
|
||||||
rta_getattr_u8(tb[IFLA_BRPORT_LEARNING]));
|
rta_getattr_u8(tb[IFLA_BRPORT_LEARNING]));
|
||||||
|
|
||||||
if (tb[IFLA_BRPORT_UNICAST_FLOOD])
|
if (tb[IFLA_BRPORT_UNICAST_FLOOD])
|
||||||
_print_onoff(f, "unicast_flood", "flood",
|
print_on_off(PRINT_ANY, "flood", "flood %s ",
|
||||||
rta_getattr_u8(tb[IFLA_BRPORT_UNICAST_FLOOD]));
|
rta_getattr_u8(tb[IFLA_BRPORT_UNICAST_FLOOD]));
|
||||||
|
|
||||||
if (tb[IFLA_BRPORT_ID])
|
if (tb[IFLA_BRPORT_ID])
|
||||||
|
|
@ -233,11 +225,11 @@ static void bridge_slave_print_opt(struct link_util *lu, FILE *f,
|
||||||
rta_getattr_u8(tb[IFLA_BRPORT_CONFIG_PENDING]));
|
rta_getattr_u8(tb[IFLA_BRPORT_CONFIG_PENDING]));
|
||||||
|
|
||||||
if (tb[IFLA_BRPORT_PROXYARP])
|
if (tb[IFLA_BRPORT_PROXYARP])
|
||||||
_print_onoff(f, "proxyarp", "proxy_arp",
|
print_on_off(PRINT_ANY, "proxy_arp", "proxy_arp %s ",
|
||||||
rta_getattr_u8(tb[IFLA_BRPORT_PROXYARP]));
|
rta_getattr_u8(tb[IFLA_BRPORT_PROXYARP]));
|
||||||
|
|
||||||
if (tb[IFLA_BRPORT_PROXYARP_WIFI])
|
if (tb[IFLA_BRPORT_PROXYARP_WIFI])
|
||||||
_print_onoff(f, "proxyarp_wifi", "proxy_arp_wifi",
|
print_on_off(PRINT_ANY, "proxy_arp_wifi", "proxy_arp_wifi %s ",
|
||||||
rta_getattr_u8(tb[IFLA_BRPORT_PROXYARP_WIFI]));
|
rta_getattr_u8(tb[IFLA_BRPORT_PROXYARP_WIFI]));
|
||||||
|
|
||||||
if (tb[IFLA_BRPORT_MULTICAST_ROUTER])
|
if (tb[IFLA_BRPORT_MULTICAST_ROUTER])
|
||||||
|
|
@ -255,15 +247,15 @@ static void bridge_slave_print_opt(struct link_util *lu, FILE *f,
|
||||||
rta_getattr_u8(tb[IFLA_BRPORT_FAST_LEAVE]) ? "on" : "off");
|
rta_getattr_u8(tb[IFLA_BRPORT_FAST_LEAVE]) ? "on" : "off");
|
||||||
|
|
||||||
if (tb[IFLA_BRPORT_MCAST_FLOOD])
|
if (tb[IFLA_BRPORT_MCAST_FLOOD])
|
||||||
_print_onoff(f, "mcast_flood", "mcast_flood",
|
print_on_off(PRINT_ANY, "mcast_flood", "mcast_flood %s ",
|
||||||
rta_getattr_u8(tb[IFLA_BRPORT_MCAST_FLOOD]));
|
rta_getattr_u8(tb[IFLA_BRPORT_MCAST_FLOOD]));
|
||||||
|
|
||||||
if (tb[IFLA_BRPORT_MCAST_TO_UCAST])
|
if (tb[IFLA_BRPORT_MCAST_TO_UCAST])
|
||||||
_print_onoff(f, "mcast_to_unicast", "mcast_to_unicast",
|
print_on_off(PRINT_ANY, "mcast_to_unicast", "mcast_to_unicast %s ",
|
||||||
rta_getattr_u8(tb[IFLA_BRPORT_MCAST_TO_UCAST]));
|
rta_getattr_u8(tb[IFLA_BRPORT_MCAST_TO_UCAST]));
|
||||||
|
|
||||||
if (tb[IFLA_BRPORT_NEIGH_SUPPRESS])
|
if (tb[IFLA_BRPORT_NEIGH_SUPPRESS])
|
||||||
_print_onoff(f, "neigh_suppress", "neigh_suppress",
|
print_on_off(PRINT_ANY, "neigh_suppress", "neigh_suppress %s ",
|
||||||
rta_getattr_u8(tb[IFLA_BRPORT_NEIGH_SUPPRESS]));
|
rta_getattr_u8(tb[IFLA_BRPORT_NEIGH_SUPPRESS]));
|
||||||
|
|
||||||
if (tb[IFLA_BRPORT_GROUP_FWD_MASK]) {
|
if (tb[IFLA_BRPORT_GROUP_FWD_MASK]) {
|
||||||
|
|
@ -279,11 +271,11 @@ static void bridge_slave_print_opt(struct link_util *lu, FILE *f,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tb[IFLA_BRPORT_VLAN_TUNNEL])
|
if (tb[IFLA_BRPORT_VLAN_TUNNEL])
|
||||||
_print_onoff(f, "vlan_tunnel", "vlan_tunnel",
|
print_on_off(PRINT_ANY, "vlan_tunnel", "vlan_tunnel %s ",
|
||||||
rta_getattr_u8(tb[IFLA_BRPORT_VLAN_TUNNEL]));
|
rta_getattr_u8(tb[IFLA_BRPORT_VLAN_TUNNEL]));
|
||||||
|
|
||||||
if (tb[IFLA_BRPORT_ISOLATED])
|
if (tb[IFLA_BRPORT_ISOLATED])
|
||||||
_print_onoff(f, "isolated", "isolated",
|
print_on_off(PRINT_ANY, "isolated", "isolated %s ",
|
||||||
rta_getattr_u8(tb[IFLA_BRPORT_ISOLATED]));
|
rta_getattr_u8(tb[IFLA_BRPORT_ISOLATED]));
|
||||||
|
|
||||||
if (tb[IFLA_BRPORT_BACKUP_PORT]) {
|
if (tb[IFLA_BRPORT_BACKUP_PORT]) {
|
||||||
|
|
@ -297,15 +289,11 @@ static void bridge_slave_print_opt(struct link_util *lu, FILE *f,
|
||||||
static void bridge_slave_parse_on_off(char *arg_name, char *arg_val,
|
static void bridge_slave_parse_on_off(char *arg_name, char *arg_val,
|
||||||
struct nlmsghdr *n, int type)
|
struct nlmsghdr *n, int type)
|
||||||
{
|
{
|
||||||
__u8 val;
|
int ret;
|
||||||
|
__u8 val = parse_on_off(arg_name, arg_val, &ret);
|
||||||
if (strcmp(arg_val, "on") == 0)
|
|
||||||
val = 1;
|
|
||||||
else if (strcmp(arg_val, "off") == 0)
|
|
||||||
val = 0;
|
|
||||||
else
|
|
||||||
invarg("should be \"on\" or \"off\"", arg_name);
|
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
exit(1);
|
||||||
addattr8(n, 1024, type, val);
|
addattr8(n, 1024, type, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,12 +30,13 @@
|
||||||
static void print_explain(struct link_util *lu, FILE *f)
|
static void print_explain(struct link_util *lu, FILE *f)
|
||||||
{
|
{
|
||||||
fprintf(f,
|
fprintf(f,
|
||||||
"Usage: ... %s mode MODE [flag MODE_FLAG] MODE_OPTS\n"
|
"Usage: ... %s mode MODE [flag MODE_FLAG] MODE_OPTS [bcqueuelen BC_QUEUE_LEN]\n"
|
||||||
"\n"
|
"\n"
|
||||||
"MODE: private | vepa | bridge | passthru | source\n"
|
"MODE: private | vepa | bridge | passthru | source\n"
|
||||||
"MODE_FLAG: null | nopromisc\n"
|
"MODE_FLAG: null | nopromisc\n"
|
||||||
"MODE_OPTS: for mode \"source\":\n"
|
"MODE_OPTS: for mode \"source\":\n"
|
||||||
"\tmacaddr { { add | del } <macaddr> | set [ <macaddr> [ <macaddr> ... ] ] | flush }\n",
|
"\tmacaddr { { add | del } <macaddr> | set [ <macaddr> [ <macaddr> ... ] ] | flush }\n"
|
||||||
|
"BC_QUEUE_LEN: Length of the rx queue for broadcast/multicast: [0-4294967295]\n",
|
||||||
lu->id
|
lu->id
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -62,6 +63,14 @@ static int flag_arg(const char *arg)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int bc_queue_len_arg(const char *arg)
|
||||||
|
{
|
||||||
|
fprintf(stderr,
|
||||||
|
"Error: argument of \"bcqueuelen\" must be a positive integer [0-4294967295], not \"%s\"\n",
|
||||||
|
arg);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
static int macvlan_parse_opt(struct link_util *lu, int argc, char **argv,
|
static int macvlan_parse_opt(struct link_util *lu, int argc, char **argv,
|
||||||
struct nlmsghdr *n)
|
struct nlmsghdr *n)
|
||||||
{
|
{
|
||||||
|
|
@ -150,6 +159,14 @@ static int macvlan_parse_opt(struct link_util *lu, int argc, char **argv,
|
||||||
} else if (matches(*argv, "nopromisc") == 0) {
|
} else if (matches(*argv, "nopromisc") == 0) {
|
||||||
flags |= MACVLAN_FLAG_NOPROMISC;
|
flags |= MACVLAN_FLAG_NOPROMISC;
|
||||||
has_flags = 1;
|
has_flags = 1;
|
||||||
|
} else if (matches(*argv, "bcqueuelen") == 0) {
|
||||||
|
__u32 bc_queue_len;
|
||||||
|
NEXT_ARG();
|
||||||
|
|
||||||
|
if (get_u32(&bc_queue_len, *argv, 0)) {
|
||||||
|
return bc_queue_len_arg(*argv);
|
||||||
|
}
|
||||||
|
addattr32(n, 1024, IFLA_MACVLAN_BC_QUEUE_LEN, bc_queue_len);
|
||||||
} else if (matches(*argv, "help") == 0) {
|
} else if (matches(*argv, "help") == 0) {
|
||||||
explain(lu);
|
explain(lu);
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -212,6 +229,18 @@ static void macvlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]
|
||||||
if (flags & MACVLAN_FLAG_NOPROMISC)
|
if (flags & MACVLAN_FLAG_NOPROMISC)
|
||||||
print_bool(PRINT_ANY, "nopromisc", "nopromisc ", true);
|
print_bool(PRINT_ANY, "nopromisc", "nopromisc ", true);
|
||||||
|
|
||||||
|
if (tb[IFLA_MACVLAN_BC_QUEUE_LEN] &&
|
||||||
|
RTA_PAYLOAD(tb[IFLA_MACVLAN_BC_QUEUE_LEN]) >= sizeof(__u32)) {
|
||||||
|
__u32 bc_queue_len = rta_getattr_u32(tb[IFLA_MACVLAN_BC_QUEUE_LEN]);
|
||||||
|
print_luint(PRINT_ANY, "bcqueuelen", "bcqueuelen %lu ", bc_queue_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tb[IFLA_MACVLAN_BC_QUEUE_LEN_USED] &&
|
||||||
|
RTA_PAYLOAD(tb[IFLA_MACVLAN_BC_QUEUE_LEN_USED]) >= sizeof(__u32)) {
|
||||||
|
__u32 bc_queue_len = rta_getattr_u32(tb[IFLA_MACVLAN_BC_QUEUE_LEN_USED]);
|
||||||
|
print_luint(PRINT_ANY, "usedbcqueuelen", "usedbcqueuelen %lu ", bc_queue_len);
|
||||||
|
}
|
||||||
|
|
||||||
/* in source mode, there are more options to print */
|
/* in source mode, there are more options to print */
|
||||||
|
|
||||||
if (mode != MACVLAN_MODE_SOURCE)
|
if (mode != MACVLAN_MODE_SOURCE)
|
||||||
|
|
|
||||||
|
|
@ -49,36 +49,30 @@ static int on_off(const char *msg, const char *arg)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int parse_qos_mapping(__u32 key, char *value, void *data)
|
||||||
|
{
|
||||||
|
struct nlmsghdr *n = data;
|
||||||
|
struct ifla_vlan_qos_mapping m = {
|
||||||
|
.from = key,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (get_u32(&m.to, value, 0))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return addattr_l(n, 1024, IFLA_VLAN_QOS_MAPPING, &m, sizeof(m));
|
||||||
|
}
|
||||||
|
|
||||||
static int vlan_parse_qos_map(int *argcp, char ***argvp, struct nlmsghdr *n,
|
static int vlan_parse_qos_map(int *argcp, char ***argvp, struct nlmsghdr *n,
|
||||||
int attrtype)
|
int attrtype)
|
||||||
{
|
{
|
||||||
int argc = *argcp;
|
|
||||||
char **argv = *argvp;
|
|
||||||
struct ifla_vlan_qos_mapping m;
|
|
||||||
struct rtattr *tail;
|
struct rtattr *tail;
|
||||||
|
|
||||||
tail = addattr_nest(n, 1024, attrtype);
|
tail = addattr_nest(n, 1024, attrtype);
|
||||||
|
|
||||||
while (argc > 0) {
|
if (parse_mapping(argcp, argvp, false, &parse_qos_mapping, n))
|
||||||
char *colon = strchr(*argv, ':');
|
return 1;
|
||||||
|
|
||||||
if (!colon)
|
|
||||||
break;
|
|
||||||
*colon = '\0';
|
|
||||||
|
|
||||||
if (get_u32(&m.from, *argv, 0))
|
|
||||||
return 1;
|
|
||||||
if (get_u32(&m.to, colon + 1, 0))
|
|
||||||
return 1;
|
|
||||||
argc--, argv++;
|
|
||||||
|
|
||||||
addattr_l(n, 1024, IFLA_VLAN_QOS_MAPPING, &m, sizeof(m));
|
|
||||||
}
|
|
||||||
|
|
||||||
addattr_nest_end(n, tail);
|
addattr_nest_end(n, tail);
|
||||||
|
|
||||||
*argcp = argc;
|
|
||||||
*argvp = argv;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,6 @@
|
||||||
#include "ll_map.h"
|
#include "ll_map.h"
|
||||||
#include "libgenl.h"
|
#include "libgenl.h"
|
||||||
|
|
||||||
static const char * const values_on_off[] = { "off", "on" };
|
|
||||||
|
|
||||||
static const char * const validate_str[] = {
|
static const char * const validate_str[] = {
|
||||||
[MACSEC_VALIDATE_DISABLED] = "disabled",
|
[MACSEC_VALIDATE_DISABLED] = "disabled",
|
||||||
[MACSEC_VALIDATE_CHECK] = "check",
|
[MACSEC_VALIDATE_CHECK] = "check",
|
||||||
|
|
@ -108,25 +106,6 @@ static void ipmacsec_usage(void)
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int one_of(const char *msg, const char *realval, const char * const *list,
|
|
||||||
size_t len, int *index)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < len; i++) {
|
|
||||||
if (matches(realval, list[i]) == 0) {
|
|
||||||
*index = i;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(stderr, "Error: argument of \"%s\" must be one of ", msg);
|
|
||||||
for (i = 0; i < len; i++)
|
|
||||||
fprintf(stderr, "\"%s\", ", list[i]);
|
|
||||||
fprintf(stderr, "not \"%s\"\n", realval);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int get_an(__u8 *val, const char *arg)
|
static int get_an(__u8 *val, const char *arg)
|
||||||
{
|
{
|
||||||
int ret = get_u8(val, arg, 0);
|
int ret = get_u8(val, arg, 0);
|
||||||
|
|
@ -559,8 +538,7 @@ static int do_offload(enum cmd c, int argc, char **argv)
|
||||||
if (argc == 0)
|
if (argc == 0)
|
||||||
ipmacsec_usage();
|
ipmacsec_usage();
|
||||||
|
|
||||||
ret = one_of("offload", *argv, offload_str, ARRAY_SIZE(offload_str),
|
offload = parse_one_of("offload", *argv, offload_str, ARRAY_SIZE(offload_str), &ret);
|
||||||
(int *)&offload);
|
|
||||||
if (ret)
|
if (ret)
|
||||||
ipmacsec_usage();
|
ipmacsec_usage();
|
||||||
|
|
||||||
|
|
@ -1334,8 +1312,7 @@ static int macsec_parse_opt(struct link_util *lu, int argc, char **argv,
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
ret = one_of("encrypt", *argv, values_on_off,
|
i = parse_on_off("encrypt", *argv, &ret);
|
||||||
ARRAY_SIZE(values_on_off), &i);
|
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
return ret;
|
return ret;
|
||||||
addattr8(n, MACSEC_BUFLEN, IFLA_MACSEC_ENCRYPT, i);
|
addattr8(n, MACSEC_BUFLEN, IFLA_MACSEC_ENCRYPT, i);
|
||||||
|
|
@ -1343,8 +1320,7 @@ static int macsec_parse_opt(struct link_util *lu, int argc, char **argv,
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
ret = one_of("send_sci", *argv, values_on_off,
|
i = parse_on_off("send_sci", *argv, &ret);
|
||||||
ARRAY_SIZE(values_on_off), &i);
|
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
return ret;
|
return ret;
|
||||||
send_sci = i;
|
send_sci = i;
|
||||||
|
|
@ -1354,8 +1330,7 @@ static int macsec_parse_opt(struct link_util *lu, int argc, char **argv,
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
ret = one_of("end_station", *argv, values_on_off,
|
i = parse_on_off("end_station", *argv, &ret);
|
||||||
ARRAY_SIZE(values_on_off), &i);
|
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
return ret;
|
return ret;
|
||||||
es = i;
|
es = i;
|
||||||
|
|
@ -1364,8 +1339,7 @@ static int macsec_parse_opt(struct link_util *lu, int argc, char **argv,
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
ret = one_of("scb", *argv, values_on_off,
|
i = parse_on_off("scb", *argv, &ret);
|
||||||
ARRAY_SIZE(values_on_off), &i);
|
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
return ret;
|
return ret;
|
||||||
scb = i;
|
scb = i;
|
||||||
|
|
@ -1374,8 +1348,7 @@ static int macsec_parse_opt(struct link_util *lu, int argc, char **argv,
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
ret = one_of("protect", *argv, values_on_off,
|
i = parse_on_off("protect", *argv, &ret);
|
||||||
ARRAY_SIZE(values_on_off), &i);
|
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
return ret;
|
return ret;
|
||||||
addattr8(n, MACSEC_BUFLEN, IFLA_MACSEC_PROTECT, i);
|
addattr8(n, MACSEC_BUFLEN, IFLA_MACSEC_PROTECT, i);
|
||||||
|
|
@ -1383,8 +1356,7 @@ static int macsec_parse_opt(struct link_util *lu, int argc, char **argv,
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
ret = one_of("replay", *argv, values_on_off,
|
i = parse_on_off("replay", *argv, &ret);
|
||||||
ARRAY_SIZE(values_on_off), &i);
|
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
return ret;
|
return ret;
|
||||||
replay_protect = !!i;
|
replay_protect = !!i;
|
||||||
|
|
@ -1395,9 +1367,8 @@ static int macsec_parse_opt(struct link_util *lu, int argc, char **argv,
|
||||||
invarg("expected replay window size", *argv);
|
invarg("expected replay window size", *argv);
|
||||||
} else if (strcmp(*argv, "validate") == 0) {
|
} else if (strcmp(*argv, "validate") == 0) {
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
ret = one_of("validate", *argv,
|
validate = parse_one_of("validate", *argv, validate_str,
|
||||||
validate_str, ARRAY_SIZE(validate_str),
|
ARRAY_SIZE(validate_str), &ret);
|
||||||
(int *)&validate);
|
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
return ret;
|
return ret;
|
||||||
addattr8(n, MACSEC_BUFLEN,
|
addattr8(n, MACSEC_BUFLEN,
|
||||||
|
|
@ -1411,9 +1382,8 @@ static int macsec_parse_opt(struct link_util *lu, int argc, char **argv,
|
||||||
invarg("expected an { 0..3 }", *argv);
|
invarg("expected an { 0..3 }", *argv);
|
||||||
} else if (strcmp(*argv, "offload") == 0) {
|
} else if (strcmp(*argv, "offload") == 0) {
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
ret = one_of("offload", *argv,
|
offload = parse_one_of("offload", *argv, offload_str,
|
||||||
offload_str, ARRAY_SIZE(offload_str),
|
ARRAY_SIZE(offload_str), &ret);
|
||||||
(int *)&offload);
|
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
return ret;
|
return ret;
|
||||||
addattr8(n, MACSEC_BUFLEN,
|
addattr8(n, MACSEC_BUFLEN,
|
||||||
|
|
|
||||||
|
|
@ -41,14 +41,6 @@ static void usage(void)
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_onoff(FILE *fp, const char *flag, __u32 val)
|
|
||||||
{
|
|
||||||
if (is_json_context())
|
|
||||||
print_bool(PRINT_JSON, flag, NULL, val);
|
|
||||||
else
|
|
||||||
fprintf(fp, "%s %s ", flag, val ? "on" : "off");
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct rtattr *netconf_rta(struct netconfmsg *ncm)
|
static struct rtattr *netconf_rta(struct netconfmsg *ncm)
|
||||||
{
|
{
|
||||||
return (struct rtattr *)((char *)ncm
|
return (struct rtattr *)((char *)ncm
|
||||||
|
|
@ -117,8 +109,8 @@ int print_netconf(struct rtnl_ctrl_data *ctrl, struct nlmsghdr *n, void *arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tb[NETCONFA_FORWARDING])
|
if (tb[NETCONFA_FORWARDING])
|
||||||
print_onoff(fp, "forwarding",
|
print_on_off(PRINT_ANY, "forwarding", "forwarding %s ",
|
||||||
rta_getattr_u32(tb[NETCONFA_FORWARDING]));
|
rta_getattr_u32(tb[NETCONFA_FORWARDING]));
|
||||||
|
|
||||||
if (tb[NETCONFA_RP_FILTER]) {
|
if (tb[NETCONFA_RP_FILTER]) {
|
||||||
__u32 rp_filter = rta_getattr_u32(tb[NETCONFA_RP_FILTER]);
|
__u32 rp_filter = rta_getattr_u32(tb[NETCONFA_RP_FILTER]);
|
||||||
|
|
@ -133,19 +125,21 @@ int print_netconf(struct rtnl_ctrl_data *ctrl, struct nlmsghdr *n, void *arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tb[NETCONFA_MC_FORWARDING])
|
if (tb[NETCONFA_MC_FORWARDING])
|
||||||
print_onoff(fp, "mc_forwarding",
|
print_on_off(PRINT_ANY, "mc_forwarding", "mc_forwarding %s ",
|
||||||
rta_getattr_u32(tb[NETCONFA_MC_FORWARDING]));
|
rta_getattr_u32(tb[NETCONFA_MC_FORWARDING]));
|
||||||
|
|
||||||
if (tb[NETCONFA_PROXY_NEIGH])
|
if (tb[NETCONFA_PROXY_NEIGH])
|
||||||
print_onoff(fp, "proxy_neigh",
|
print_on_off(PRINT_ANY, "proxy_neigh", "proxy_neigh %s ",
|
||||||
rta_getattr_u32(tb[NETCONFA_PROXY_NEIGH]));
|
rta_getattr_u32(tb[NETCONFA_PROXY_NEIGH]));
|
||||||
|
|
||||||
if (tb[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN])
|
if (tb[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN])
|
||||||
print_onoff(fp, "ignore_routes_with_linkdown",
|
print_on_off(PRINT_ANY, "ignore_routes_with_linkdown",
|
||||||
rta_getattr_u32(tb[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]));
|
"ignore_routes_with_linkdown %s ",
|
||||||
|
rta_getattr_u32(tb[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]));
|
||||||
|
|
||||||
if (tb[NETCONFA_INPUT])
|
if (tb[NETCONFA_INPUT])
|
||||||
print_onoff(fp, "input", rta_getattr_u32(tb[NETCONFA_INPUT]));
|
print_on_off(PRINT_ANY, "input", "input %s ",
|
||||||
|
rta_getattr_u32(tb[NETCONFA_INPUT]));
|
||||||
|
|
||||||
close_json_object();
|
close_json_object();
|
||||||
print_string(PRINT_FP, NULL, "\n", NULL);
|
print_string(PRINT_FP, NULL, "\n", NULL);
|
||||||
|
|
|
||||||
|
|
@ -263,8 +263,7 @@ int print_nexthop(struct nlmsghdr *n, void *arg)
|
||||||
rtnl_rtprot_n2a(nhm->nh_protocol, b1, sizeof(b1)));
|
rtnl_rtprot_n2a(nhm->nh_protocol, b1, sizeof(b1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tb[NHA_OIF])
|
print_rt_flags(fp, nhm->nh_flags);
|
||||||
print_rt_flags(fp, nhm->nh_flags);
|
|
||||||
|
|
||||||
if (tb[NHA_FDB])
|
if (tb[NHA_FDB])
|
||||||
print_null(PRINT_ANY, "fdb", "fdb", NULL);
|
print_null(PRINT_ANY, "fdb", "fdb", NULL);
|
||||||
|
|
|
||||||
|
|
@ -362,6 +362,8 @@ void print_rt_flags(FILE *fp, unsigned int flags)
|
||||||
print_string(PRINT_ANY, NULL, "%s ", "pervasive");
|
print_string(PRINT_ANY, NULL, "%s ", "pervasive");
|
||||||
if (flags & RTNH_F_OFFLOAD)
|
if (flags & RTNH_F_OFFLOAD)
|
||||||
print_string(PRINT_ANY, NULL, "%s ", "offload");
|
print_string(PRINT_ANY, NULL, "%s ", "offload");
|
||||||
|
if (flags & RTNH_F_TRAP)
|
||||||
|
print_string(PRINT_ANY, NULL, "%s ", "trap");
|
||||||
if (flags & RTM_F_NOTIFY)
|
if (flags & RTM_F_NOTIFY)
|
||||||
print_string(PRINT_ANY, NULL, "%s ", "notify");
|
print_string(PRINT_ANY, NULL, "%s ", "notify");
|
||||||
if (flags & RTNH_F_LINKDOWN)
|
if (flags & RTNH_F_LINKDOWN)
|
||||||
|
|
|
||||||
|
|
@ -294,6 +294,11 @@ static void print_encap_seg6local(FILE *fp, struct rtattr *encap)
|
||||||
rtnl_rttable_n2a(rta_getattr_u32(tb[SEG6_LOCAL_TABLE]),
|
rtnl_rttable_n2a(rta_getattr_u32(tb[SEG6_LOCAL_TABLE]),
|
||||||
b1, sizeof(b1)));
|
b1, sizeof(b1)));
|
||||||
|
|
||||||
|
if (tb[SEG6_LOCAL_VRFTABLE])
|
||||||
|
print_string(PRINT_ANY, "vrftable", "vrftable %s ",
|
||||||
|
rtnl_rttable_n2a(rta_getattr_u32(tb[SEG6_LOCAL_VRFTABLE]),
|
||||||
|
b1, sizeof(b1)));
|
||||||
|
|
||||||
if (tb[SEG6_LOCAL_NH4]) {
|
if (tb[SEG6_LOCAL_NH4]) {
|
||||||
print_string(PRINT_ANY, "nh4",
|
print_string(PRINT_ANY, "nh4",
|
||||||
"nh4 %s ", rt_addr_n2a_rta(AF_INET, tb[SEG6_LOCAL_NH4]));
|
"nh4 %s ", rt_addr_n2a_rta(AF_INET, tb[SEG6_LOCAL_NH4]));
|
||||||
|
|
@ -860,9 +865,10 @@ static int lwt_parse_bpf(struct rtattr *rta, size_t len,
|
||||||
static int parse_encap_seg6local(struct rtattr *rta, size_t len, int *argcp,
|
static int parse_encap_seg6local(struct rtattr *rta, size_t len, int *argcp,
|
||||||
char ***argvp)
|
char ***argvp)
|
||||||
{
|
{
|
||||||
int segs_ok = 0, hmac_ok = 0, table_ok = 0, nh4_ok = 0, nh6_ok = 0;
|
int segs_ok = 0, hmac_ok = 0, table_ok = 0, vrftable_ok = 0;
|
||||||
int iif_ok = 0, oif_ok = 0, action_ok = 0, srh_ok = 0, bpf_ok = 0;
|
int nh4_ok = 0, nh6_ok = 0, iif_ok = 0, oif_ok = 0;
|
||||||
__u32 action = 0, table, iif, oif;
|
__u32 action = 0, table, vrftable, iif, oif;
|
||||||
|
int action_ok = 0, srh_ok = 0, bpf_ok = 0;
|
||||||
struct ipv6_sr_hdr *srh;
|
struct ipv6_sr_hdr *srh;
|
||||||
char **argv = *argvp;
|
char **argv = *argvp;
|
||||||
int argc = *argcp;
|
int argc = *argcp;
|
||||||
|
|
@ -887,6 +893,13 @@ static int parse_encap_seg6local(struct rtattr *rta, size_t len, int *argcp,
|
||||||
duparg2("table", *argv);
|
duparg2("table", *argv);
|
||||||
rtnl_rttable_a2n(&table, *argv);
|
rtnl_rttable_a2n(&table, *argv);
|
||||||
ret = rta_addattr32(rta, len, SEG6_LOCAL_TABLE, table);
|
ret = rta_addattr32(rta, len, SEG6_LOCAL_TABLE, table);
|
||||||
|
} else if (strcmp(*argv, "vrftable") == 0) {
|
||||||
|
NEXT_ARG();
|
||||||
|
if (vrftable_ok++)
|
||||||
|
duparg2("vrftable", *argv);
|
||||||
|
rtnl_rttable_a2n(&vrftable, *argv);
|
||||||
|
ret = rta_addattr32(rta, len, SEG6_LOCAL_VRFTABLE,
|
||||||
|
vrftable);
|
||||||
} else if (strcmp(*argv, "nh4") == 0) {
|
} else if (strcmp(*argv, "nh4") == 0) {
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
if (nh4_ok++)
|
if (nh4_ok++)
|
||||||
|
|
|
||||||
|
|
@ -541,14 +541,6 @@ static void print_mq(FILE *f, struct rtattr *tb[])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_onoff(FILE *f, const char *flag, __u8 val)
|
|
||||||
{
|
|
||||||
if (is_json_context())
|
|
||||||
print_bool(PRINT_JSON, flag, NULL, !!val);
|
|
||||||
else
|
|
||||||
fprintf(f, "%s %s ", flag, val ? "on" : "off");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void print_type(FILE *f, __u8 type)
|
static void print_type(FILE *f, __u8 type)
|
||||||
{
|
{
|
||||||
SPRINT_BUF(buf);
|
SPRINT_BUF(buf);
|
||||||
|
|
@ -573,17 +565,19 @@ static void tun_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
|
||||||
print_type(f, rta_getattr_u8(tb[IFLA_TUN_TYPE]));
|
print_type(f, rta_getattr_u8(tb[IFLA_TUN_TYPE]));
|
||||||
|
|
||||||
if (tb[IFLA_TUN_PI])
|
if (tb[IFLA_TUN_PI])
|
||||||
print_onoff(f, "pi", rta_getattr_u8(tb[IFLA_TUN_PI]));
|
print_on_off(PRINT_ANY, "pi", "pi %s ",
|
||||||
|
rta_getattr_u8(tb[IFLA_TUN_PI]));
|
||||||
|
|
||||||
if (tb[IFLA_TUN_VNET_HDR]) {
|
if (tb[IFLA_TUN_VNET_HDR]) {
|
||||||
print_onoff(f, "vnet_hdr",
|
print_on_off(PRINT_ANY, "vnet_hdr", "vnet_hdr %s ",
|
||||||
rta_getattr_u8(tb[IFLA_TUN_VNET_HDR]));
|
rta_getattr_u8(tb[IFLA_TUN_VNET_HDR]));
|
||||||
}
|
}
|
||||||
|
|
||||||
print_mq(f, tb);
|
print_mq(f, tb);
|
||||||
|
|
||||||
if (tb[IFLA_TUN_PERSIST])
|
if (tb[IFLA_TUN_PERSIST])
|
||||||
print_onoff(f, "persist", rta_getattr_u8(tb[IFLA_TUN_PERSIST]));
|
print_on_off(PRINT_ANY, "persist", "persist %s ",
|
||||||
|
rta_getattr_u8(tb[IFLA_TUN_PERSIST]));
|
||||||
|
|
||||||
if (tb[IFLA_TUN_OWNER])
|
if (tb[IFLA_TUN_OWNER])
|
||||||
print_owner(f, rta_getattr_u32(tb[IFLA_TUN_OWNER]));
|
print_owner(f, rta_getattr_u32(tb[IFLA_TUN_OWNER]));
|
||||||
|
|
|
||||||
|
|
@ -256,8 +256,8 @@ static int prog_load(int idx)
|
||||||
BPF_EXIT_INSN(),
|
BPF_EXIT_INSN(),
|
||||||
};
|
};
|
||||||
|
|
||||||
return bpf_prog_load(BPF_PROG_TYPE_CGROUP_SOCK, prog, sizeof(prog),
|
return bpf_program_load(BPF_PROG_TYPE_CGROUP_SOCK, prog, sizeof(prog),
|
||||||
"GPL", bpf_log_buf, sizeof(bpf_log_buf));
|
"GPL", bpf_log_buf, sizeof(bpf_log_buf));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vrf_configure_cgroup(const char *path, int ifindex)
|
static int vrf_configure_cgroup(const char *path, int ifindex)
|
||||||
|
|
@ -288,7 +288,7 @@ static int vrf_configure_cgroup(const char *path, int ifindex)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bpf_prog_attach_fd(prog_fd, cg_fd, BPF_CGROUP_INET_SOCK_CREATE)) {
|
if (bpf_program_attach(prog_fd, cg_fd, BPF_CGROUP_INET_SOCK_CREATE)) {
|
||||||
fprintf(stderr, "Failed to attach prog to cgroup: '%s'\n",
|
fprintf(stderr, "Failed to attach prog to cgroup: '%s'\n",
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
goto out;
|
goto out;
|
||||||
|
|
|
||||||
11
lib/Makefile
11
lib/Makefile
|
|
@ -5,9 +5,18 @@ 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 cg_map.o
|
names.o color.o bpf_legacy.o bpf_glue.o exec.o fs.o cg_map.o
|
||||||
|
|
||||||
|
ifeq ($(HAVE_ELF),y)
|
||||||
|
ifeq ($(HAVE_LIBBPF),y)
|
||||||
|
UTILOBJ += bpf_libbpf.o
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
NLOBJ=libgenl.o libnetlink.o
|
NLOBJ=libgenl.o libnetlink.o
|
||||||
|
ifeq ($(HAVE_MNL),y)
|
||||||
|
NLOBJ += mnl_utils.o
|
||||||
|
endif
|
||||||
|
|
||||||
all: libnetlink.a libutil.a
|
all: libnetlink.a libutil.a
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
|
/*
|
||||||
|
* bpf_glue.c: BPF code to call both legacy and libbpf code
|
||||||
|
* Authors: Hangbin Liu <haliu@redhat.com>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include "bpf_util.h"
|
||||||
|
#ifdef HAVE_LIBBPF
|
||||||
|
#include <bpf/bpf.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int bpf_program_load(enum bpf_prog_type type, const struct bpf_insn *insns,
|
||||||
|
size_t size_insns, const char *license, char *log,
|
||||||
|
size_t size_log)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_LIBBPF
|
||||||
|
return bpf_load_program(type, insns, size_insns, license, 0, log, size_log);
|
||||||
|
#else
|
||||||
|
return bpf_prog_load_dev(type, insns, size_insns, license, 0, log, size_log);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int bpf_program_attach(int prog_fd, int target_fd, enum bpf_attach_type type)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_LIBBPF
|
||||||
|
return bpf_prog_attach(prog_fd, target_fd, type, 0);
|
||||||
|
#else
|
||||||
|
return bpf_prog_attach_fd(prog_fd, target_fd, type);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_LIBBPF
|
||||||
|
static const char *_libbpf_compile_version = LIBBPF_VERSION;
|
||||||
|
static char _libbpf_version[10] = {};
|
||||||
|
|
||||||
|
const char *get_libbpf_version(void)
|
||||||
|
{
|
||||||
|
/* Start by copying compile-time version into buffer so we have a
|
||||||
|
* fallback value in case we are dynamically linked, or can't find a
|
||||||
|
* version in /proc/self/maps below.
|
||||||
|
*/
|
||||||
|
strncpy(_libbpf_version, _libbpf_compile_version,
|
||||||
|
sizeof(_libbpf_version)-1);
|
||||||
|
#ifdef LIBBPF_DYNAMIC
|
||||||
|
char buf[PATH_MAX], *s;
|
||||||
|
bool found = false;
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
/* When dynamically linking against libbpf, we can't be sure that the
|
||||||
|
* version we discovered at compile time is actually the one we are
|
||||||
|
* using at runtime. This can lead to hard-to-debug errors, so we try to
|
||||||
|
* discover the correct version at runtime.
|
||||||
|
*
|
||||||
|
* The simple solution to this would be if libbpf itself exported a
|
||||||
|
* version in its API. But since it doesn't, we work around this by
|
||||||
|
* parsing the mappings of the binary at runtime, looking for the full
|
||||||
|
* filename of libbpf.so and using that.
|
||||||
|
*/
|
||||||
|
fp = fopen("/proc/self/maps", "r");
|
||||||
|
if (fp == NULL)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
while ((s = fgets(buf, sizeof(buf), fp)) != NULL) {
|
||||||
|
if ((s = strstr(buf, "libbpf.so.")) != NULL) {
|
||||||
|
strncpy(_libbpf_version, s+10, sizeof(_libbpf_version)-1);
|
||||||
|
strtok(_libbpf_version, "\n");
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
out:
|
||||||
|
if (!found)
|
||||||
|
fprintf(stderr, "Couldn't find runtime libbpf version - falling back to compile-time value!\n");
|
||||||
|
#endif /* LIBBPF_DYNAMIC */
|
||||||
|
|
||||||
|
_libbpf_version[sizeof(_libbpf_version)-1] = '\0';
|
||||||
|
return _libbpf_version;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
const char *get_libbpf_version(void)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#endif /* HAVE_LIBBPF */
|
||||||
|
|
@ -940,6 +940,9 @@ static int bpf_do_parse(struct bpf_cfg_in *cfg, const bool *opt_tbl)
|
||||||
static int bpf_do_load(struct bpf_cfg_in *cfg)
|
static int bpf_do_load(struct bpf_cfg_in *cfg)
|
||||||
{
|
{
|
||||||
if (cfg->mode == EBPF_OBJECT) {
|
if (cfg->mode == EBPF_OBJECT) {
|
||||||
|
#ifdef HAVE_LIBBPF
|
||||||
|
return iproute2_load_libbpf(cfg);
|
||||||
|
#endif
|
||||||
cfg->prog_fd = bpf_obj_open(cfg->object, cfg->type,
|
cfg->prog_fd = bpf_obj_open(cfg->object, cfg->type,
|
||||||
cfg->section, cfg->ifindex,
|
cfg->section, cfg->ifindex,
|
||||||
cfg->verbose);
|
cfg->verbose);
|
||||||
|
|
@ -1087,10 +1090,9 @@ int bpf_prog_detach_fd(int target_fd, enum bpf_attach_type type)
|
||||||
return bpf(BPF_PROG_DETACH, &attr, sizeof(attr));
|
return bpf(BPF_PROG_DETACH, &attr, sizeof(attr));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bpf_prog_load_dev(enum bpf_prog_type type,
|
int bpf_prog_load_dev(enum bpf_prog_type type, const struct bpf_insn *insns,
|
||||||
const struct bpf_insn *insns, size_t size_insns,
|
size_t size_insns, const char *license, __u32 ifindex,
|
||||||
const char *license, __u32 ifindex,
|
char *log, size_t size_log)
|
||||||
char *log, size_t size_log)
|
|
||||||
{
|
{
|
||||||
union bpf_attr attr = {};
|
union bpf_attr attr = {};
|
||||||
|
|
||||||
|
|
@ -1109,14 +1111,6 @@ static int bpf_prog_load_dev(enum bpf_prog_type type,
|
||||||
return bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
|
return bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
|
||||||
}
|
}
|
||||||
|
|
||||||
int bpf_prog_load(enum bpf_prog_type type, const struct bpf_insn *insns,
|
|
||||||
size_t size_insns, const char *license, char *log,
|
|
||||||
size_t size_log)
|
|
||||||
{
|
|
||||||
return bpf_prog_load_dev(type, insns, size_insns, license, 0,
|
|
||||||
log, size_log);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef HAVE_ELF
|
#ifdef HAVE_ELF
|
||||||
struct bpf_elf_prog {
|
struct bpf_elf_prog {
|
||||||
enum bpf_prog_type type;
|
enum bpf_prog_type type;
|
||||||
|
|
@ -3164,4 +3158,179 @@ int bpf_recv_map_fds(const char *path, int *fds, struct bpf_map_aux *aux,
|
||||||
close(fd);
|
close(fd);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_LIBBPF
|
||||||
|
/* The following functions are wrapper functions for libbpf code to be
|
||||||
|
* compatible with the legacy format. So all the functions have prefix
|
||||||
|
* with iproute2_
|
||||||
|
*/
|
||||||
|
int iproute2_bpf_elf_ctx_init(struct bpf_cfg_in *cfg)
|
||||||
|
{
|
||||||
|
struct bpf_elf_ctx *ctx = &__ctx;
|
||||||
|
|
||||||
|
return bpf_elf_ctx_init(ctx, cfg->object, cfg->type, cfg->ifindex, cfg->verbose);
|
||||||
|
}
|
||||||
|
|
||||||
|
int iproute2_bpf_fetch_ancillary(void)
|
||||||
|
{
|
||||||
|
struct bpf_elf_ctx *ctx = &__ctx;
|
||||||
|
struct bpf_elf_sec_data data;
|
||||||
|
int i, ret = 0;
|
||||||
|
|
||||||
|
for (i = 1; i < ctx->elf_hdr.e_shnum; i++) {
|
||||||
|
ret = bpf_fill_section_data(ctx, i, &data);
|
||||||
|
if (ret < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (data.sec_hdr.sh_type == SHT_PROGBITS &&
|
||||||
|
!strcmp(data.sec_name, ELF_SECTION_MAPS))
|
||||||
|
ret = bpf_fetch_maps_begin(ctx, i, &data);
|
||||||
|
else if (data.sec_hdr.sh_type == SHT_SYMTAB &&
|
||||||
|
!strcmp(data.sec_name, ".symtab"))
|
||||||
|
ret = bpf_fetch_symtab(ctx, i, &data);
|
||||||
|
else if (data.sec_hdr.sh_type == SHT_STRTAB &&
|
||||||
|
!strcmp(data.sec_name, ".strtab"))
|
||||||
|
ret = bpf_fetch_strtab(ctx, i, &data);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "Error parsing section %d! Perhaps check with readelf -a?\n",
|
||||||
|
i);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bpf_has_map_data(ctx)) {
|
||||||
|
ret = bpf_fetch_maps_end(ctx);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "Error fixing up map structure, incompatible struct bpf_elf_map used?\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int iproute2_get_root_path(char *root_path, size_t len)
|
||||||
|
{
|
||||||
|
struct bpf_elf_ctx *ctx = &__ctx;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
snprintf(root_path, len, "%s/%s",
|
||||||
|
bpf_get_work_dir(ctx->type), BPF_DIR_GLOBALS);
|
||||||
|
|
||||||
|
ret = mkdir(root_path, S_IRWXU);
|
||||||
|
if (ret && errno != EEXIST) {
|
||||||
|
fprintf(stderr, "mkdir %s failed: %s\n", root_path, strerror(errno));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool iproute2_is_pin_map(const char *libbpf_map_name, char *pathname)
|
||||||
|
{
|
||||||
|
struct bpf_elf_ctx *ctx = &__ctx;
|
||||||
|
const char *map_name, *tmp;
|
||||||
|
unsigned int pinning;
|
||||||
|
int i, ret = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < ctx->map_num; i++) {
|
||||||
|
if (ctx->maps[i].pinning == PIN_OBJECT_NS &&
|
||||||
|
ctx->noafalg) {
|
||||||
|
fprintf(stderr, "Missing kernel AF_ALG support for PIN_OBJECT_NS!\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
map_name = bpf_map_fetch_name(ctx, i);
|
||||||
|
if (!map_name) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(libbpf_map_name, map_name))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
pinning = ctx->maps[i].pinning;
|
||||||
|
|
||||||
|
if (bpf_no_pinning(ctx, pinning) || !bpf_get_work_dir(ctx->type))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (pinning == PIN_OBJECT_NS)
|
||||||
|
ret = bpf_make_obj_path(ctx);
|
||||||
|
else if ((tmp = bpf_custom_pinning(ctx, pinning)))
|
||||||
|
ret = bpf_make_custom_path(ctx, tmp);
|
||||||
|
if (ret < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
bpf_make_pathname(pathname, PATH_MAX, map_name, ctx, pinning);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool iproute2_is_map_in_map(const char *libbpf_map_name, struct bpf_elf_map *imap,
|
||||||
|
struct bpf_elf_map *omap, char *omap_name)
|
||||||
|
{
|
||||||
|
struct bpf_elf_ctx *ctx = &__ctx;
|
||||||
|
const char *inner_map_name, *outer_map_name;
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
for (i = 0; i < ctx->map_num; i++) {
|
||||||
|
inner_map_name = bpf_map_fetch_name(ctx, i);
|
||||||
|
if (!inner_map_name) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(libbpf_map_name, inner_map_name))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!ctx->maps[i].id ||
|
||||||
|
ctx->maps[i].inner_id ||
|
||||||
|
ctx->maps[i].inner_idx == -1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
*imap = ctx->maps[i];
|
||||||
|
|
||||||
|
for (j = 0; j < ctx->map_num; j++) {
|
||||||
|
if (!bpf_is_map_in_map_type(&ctx->maps[j]))
|
||||||
|
continue;
|
||||||
|
if (ctx->maps[j].inner_id != ctx->maps[i].id)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
*omap = ctx->maps[j];
|
||||||
|
outer_map_name = bpf_map_fetch_name(ctx, j);
|
||||||
|
memcpy(omap_name, outer_map_name, strlen(outer_map_name) + 1);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int iproute2_find_map_name_by_id(unsigned int map_id, char *name)
|
||||||
|
{
|
||||||
|
struct bpf_elf_ctx *ctx = &__ctx;
|
||||||
|
const char *map_name;
|
||||||
|
int i, idx = -1;
|
||||||
|
|
||||||
|
for (i = 0; i < ctx->map_num; i++) {
|
||||||
|
if (ctx->maps[i].id == map_id &&
|
||||||
|
ctx->maps[i].type == BPF_MAP_TYPE_PROG_ARRAY) {
|
||||||
|
idx = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (idx < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
map_name = bpf_map_fetch_name(ctx, idx);
|
||||||
|
if (!map_name)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
memcpy(name, map_name, strlen(map_name) + 1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif /* HAVE_LIBBPF */
|
||||||
#endif /* HAVE_ELF */
|
#endif /* HAVE_ELF */
|
||||||
|
|
@ -0,0 +1,348 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
|
/*
|
||||||
|
* bpf_libbpf.c BPF code relay on libbpf
|
||||||
|
* Authors: Hangbin Liu <haliu@redhat.com>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include <libelf.h>
|
||||||
|
#include <gelf.h>
|
||||||
|
|
||||||
|
#include <bpf/libbpf.h>
|
||||||
|
#include <bpf/bpf.h>
|
||||||
|
|
||||||
|
#include "bpf_util.h"
|
||||||
|
|
||||||
|
static int verbose_print(enum libbpf_print_level level, const char *format, va_list args)
|
||||||
|
{
|
||||||
|
return vfprintf(stderr, format, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int silent_print(enum libbpf_print_level level, const char *format, va_list args)
|
||||||
|
{
|
||||||
|
if (level > LIBBPF_WARN)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Skip warning from bpf_object__init_user_maps() for legacy maps */
|
||||||
|
if (strstr(format, "has unrecognized, non-zero options"))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return vfprintf(stderr, format, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *get_bpf_program__section_name(const struct bpf_program *prog)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_LIBBPF_SECTION_NAME
|
||||||
|
return bpf_program__section_name(prog);
|
||||||
|
#else
|
||||||
|
return bpf_program__title(prog, false);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static int create_map(const char *name, struct bpf_elf_map *map,
|
||||||
|
__u32 ifindex, int inner_fd)
|
||||||
|
{
|
||||||
|
struct bpf_create_map_attr map_attr = {};
|
||||||
|
|
||||||
|
map_attr.name = name;
|
||||||
|
map_attr.map_type = map->type;
|
||||||
|
map_attr.map_flags = map->flags;
|
||||||
|
map_attr.key_size = map->size_key;
|
||||||
|
map_attr.value_size = map->size_value;
|
||||||
|
map_attr.max_entries = map->max_elem;
|
||||||
|
map_attr.map_ifindex = ifindex;
|
||||||
|
map_attr.inner_map_fd = inner_fd;
|
||||||
|
|
||||||
|
return bpf_create_map_xattr(&map_attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int create_map_in_map(struct bpf_object *obj, struct bpf_map *map,
|
||||||
|
struct bpf_elf_map *elf_map, int inner_fd,
|
||||||
|
bool *reuse_pin_map)
|
||||||
|
{
|
||||||
|
char pathname[PATH_MAX];
|
||||||
|
const char *map_name;
|
||||||
|
bool pin_map = false;
|
||||||
|
int map_fd, ret = 0;
|
||||||
|
|
||||||
|
map_name = bpf_map__name(map);
|
||||||
|
|
||||||
|
if (iproute2_is_pin_map(map_name, pathname)) {
|
||||||
|
pin_map = true;
|
||||||
|
|
||||||
|
/* Check if there already has a pinned map */
|
||||||
|
map_fd = bpf_obj_get(pathname);
|
||||||
|
if (map_fd > 0) {
|
||||||
|
if (reuse_pin_map)
|
||||||
|
*reuse_pin_map = true;
|
||||||
|
close(map_fd);
|
||||||
|
return bpf_map__set_pin_path(map, pathname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
map_fd = create_map(map_name, elf_map, bpf_map__ifindex(map), inner_fd);
|
||||||
|
if (map_fd < 0) {
|
||||||
|
fprintf(stderr, "create map %s failed\n", map_name);
|
||||||
|
return map_fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = bpf_map__reuse_fd(map, map_fd);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "map %s reuse fd failed\n", map_name);
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pin_map) {
|
||||||
|
ret = bpf_map__set_pin_path(map, pathname);
|
||||||
|
if (ret < 0)
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
err_out:
|
||||||
|
close(map_fd);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
handle_legacy_map_in_map(struct bpf_object *obj, struct bpf_map *inner_map,
|
||||||
|
const char *inner_map_name)
|
||||||
|
{
|
||||||
|
int inner_fd, outer_fd, inner_idx, ret = 0;
|
||||||
|
struct bpf_elf_map imap, omap;
|
||||||
|
struct bpf_map *outer_map;
|
||||||
|
/* What's the size limit of map name? */
|
||||||
|
char outer_map_name[128];
|
||||||
|
bool reuse_pin_map = false;
|
||||||
|
|
||||||
|
/* Deal with map-in-map */
|
||||||
|
if (iproute2_is_map_in_map(inner_map_name, &imap, &omap, outer_map_name)) {
|
||||||
|
ret = create_map_in_map(obj, inner_map, &imap, -1, NULL);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
inner_fd = bpf_map__fd(inner_map);
|
||||||
|
outer_map = bpf_object__find_map_by_name(obj, outer_map_name);
|
||||||
|
ret = create_map_in_map(obj, outer_map, &omap, inner_fd, &reuse_pin_map);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (!reuse_pin_map) {
|
||||||
|
inner_idx = imap.inner_idx;
|
||||||
|
outer_fd = bpf_map__fd(outer_map);
|
||||||
|
ret = bpf_map_update_elem(outer_fd, &inner_idx, &inner_fd, 0);
|
||||||
|
if (ret < 0)
|
||||||
|
fprintf(stderr, "Cannot update inner_idx into outer_map\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int find_legacy_tail_calls(struct bpf_program *prog, struct bpf_object *obj)
|
||||||
|
{
|
||||||
|
unsigned int map_id, key_id;
|
||||||
|
const char *sec_name;
|
||||||
|
struct bpf_map *map;
|
||||||
|
char map_name[128];
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Handle iproute2 tail call */
|
||||||
|
sec_name = get_bpf_program__section_name(prog);
|
||||||
|
ret = sscanf(sec_name, "%i/%i", &map_id, &key_id);
|
||||||
|
if (ret != 2)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
ret = iproute2_find_map_name_by_id(map_id, map_name);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "unable to find map id %u for tail call\n", map_id);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
map = bpf_object__find_map_by_name(obj, map_name);
|
||||||
|
if (!map)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* Save the map here for later updating */
|
||||||
|
bpf_program__set_priv(prog, map, NULL);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int update_legacy_tail_call_maps(struct bpf_object *obj)
|
||||||
|
{
|
||||||
|
int prog_fd, map_fd, ret = 0;
|
||||||
|
unsigned int map_id, key_id;
|
||||||
|
struct bpf_program *prog;
|
||||||
|
const char *sec_name;
|
||||||
|
struct bpf_map *map;
|
||||||
|
|
||||||
|
bpf_object__for_each_program(prog, obj) {
|
||||||
|
map = bpf_program__priv(prog);
|
||||||
|
if (!map)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
prog_fd = bpf_program__fd(prog);
|
||||||
|
if (prog_fd < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
sec_name = get_bpf_program__section_name(prog);
|
||||||
|
ret = sscanf(sec_name, "%i/%i", &map_id, &key_id);
|
||||||
|
if (ret != 2)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
map_fd = bpf_map__fd(map);
|
||||||
|
ret = bpf_map_update_elem(map_fd, &key_id, &prog_fd, 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "Cannot update map key for tail call!\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int handle_legacy_maps(struct bpf_object *obj)
|
||||||
|
{
|
||||||
|
char pathname[PATH_MAX];
|
||||||
|
struct bpf_map *map;
|
||||||
|
const char *map_name;
|
||||||
|
int map_fd, ret = 0;
|
||||||
|
|
||||||
|
bpf_object__for_each_map(map, obj) {
|
||||||
|
map_name = bpf_map__name(map);
|
||||||
|
|
||||||
|
ret = handle_legacy_map_in_map(obj, map, map_name);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* If it is a iproute2 legacy pin maps, just set pin path
|
||||||
|
* and let bpf_object__load() to deal with the map creation.
|
||||||
|
* We need to ignore map-in-maps which have pinned maps manually
|
||||||
|
*/
|
||||||
|
map_fd = bpf_map__fd(map);
|
||||||
|
if (map_fd < 0 && iproute2_is_pin_map(map_name, pathname)) {
|
||||||
|
ret = bpf_map__set_pin_path(map, pathname);
|
||||||
|
if (ret) {
|
||||||
|
fprintf(stderr, "map '%s': couldn't set pin path.\n", map_name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int load_bpf_object(struct bpf_cfg_in *cfg)
|
||||||
|
{
|
||||||
|
struct bpf_program *p, *prog = NULL;
|
||||||
|
struct bpf_object *obj;
|
||||||
|
char root_path[PATH_MAX];
|
||||||
|
struct bpf_map *map;
|
||||||
|
int prog_fd, ret = 0;
|
||||||
|
|
||||||
|
ret = iproute2_get_root_path(root_path, PATH_MAX);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
DECLARE_LIBBPF_OPTS(bpf_object_open_opts, open_opts,
|
||||||
|
.relaxed_maps = true,
|
||||||
|
.pin_root_path = root_path,
|
||||||
|
);
|
||||||
|
|
||||||
|
obj = bpf_object__open_file(cfg->object, &open_opts);
|
||||||
|
if (libbpf_get_error(obj)) {
|
||||||
|
fprintf(stderr, "ERROR: opening BPF object file failed\n");
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
bpf_object__for_each_program(p, obj) {
|
||||||
|
/* Only load the programs that will either be subsequently
|
||||||
|
* attached or inserted into a tail call map */
|
||||||
|
if (find_legacy_tail_calls(p, obj) < 0 && cfg->section &&
|
||||||
|
strcmp(get_bpf_program__section_name(p), cfg->section)) {
|
||||||
|
ret = bpf_program__set_autoload(p, false);
|
||||||
|
if (ret)
|
||||||
|
return -EINVAL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bpf_program__set_type(p, cfg->type);
|
||||||
|
bpf_program__set_ifindex(p, cfg->ifindex);
|
||||||
|
if (!prog)
|
||||||
|
prog = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
bpf_object__for_each_map(map, obj) {
|
||||||
|
if (!bpf_map__is_offload_neutral(map))
|
||||||
|
bpf_map__set_ifindex(map, cfg->ifindex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!prog) {
|
||||||
|
fprintf(stderr, "object file doesn't contain sec %s\n", cfg->section);
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle iproute2 legacy pin maps and map-in-maps */
|
||||||
|
ret = handle_legacy_maps(obj);
|
||||||
|
if (ret)
|
||||||
|
goto unload_obj;
|
||||||
|
|
||||||
|
ret = bpf_object__load(obj);
|
||||||
|
if (ret)
|
||||||
|
goto unload_obj;
|
||||||
|
|
||||||
|
ret = update_legacy_tail_call_maps(obj);
|
||||||
|
if (ret)
|
||||||
|
goto unload_obj;
|
||||||
|
|
||||||
|
prog_fd = fcntl(bpf_program__fd(prog), F_DUPFD_CLOEXEC, 1);
|
||||||
|
if (prog_fd < 0)
|
||||||
|
ret = -errno;
|
||||||
|
else
|
||||||
|
cfg->prog_fd = prog_fd;
|
||||||
|
|
||||||
|
unload_obj:
|
||||||
|
/* Close obj as we don't need it */
|
||||||
|
bpf_object__close(obj);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Load ebpf and return prog fd */
|
||||||
|
int iproute2_load_libbpf(struct bpf_cfg_in *cfg)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (cfg->verbose)
|
||||||
|
libbpf_set_print(verbose_print);
|
||||||
|
else
|
||||||
|
libbpf_set_print(silent_print);
|
||||||
|
|
||||||
|
ret = iproute2_bpf_elf_ctx_init(cfg);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "Cannot initialize ELF context!\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = iproute2_bpf_fetch_ancillary();
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "Error fetching ELF ancillary data!\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = load_bpf_object(cfg);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return cfg->prog_fd;
|
||||||
|
}
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "json_print.h"
|
#include "json_print.h"
|
||||||
|
|
@ -191,11 +192,12 @@ int print_color_string(enum output_type type,
|
||||||
* a value to it, you will need to use "is_json_context()" to have different
|
* a value to it, you will need to use "is_json_context()" to have different
|
||||||
* branch for json and regular output. grep -r "print_bool" for example
|
* branch for json and regular output. grep -r "print_bool" for example
|
||||||
*/
|
*/
|
||||||
int print_color_bool(enum output_type type,
|
static int __print_color_bool(enum output_type type,
|
||||||
enum color_attr color,
|
enum color_attr color,
|
||||||
const char *key,
|
const char *key,
|
||||||
const char *fmt,
|
const char *fmt,
|
||||||
bool value)
|
bool value,
|
||||||
|
const char *str)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
|
|
@ -205,13 +207,32 @@ int print_color_bool(enum output_type type,
|
||||||
else
|
else
|
||||||
jsonw_bool(_jw, value);
|
jsonw_bool(_jw, value);
|
||||||
} else if (_IS_FP_CONTEXT(type)) {
|
} else if (_IS_FP_CONTEXT(type)) {
|
||||||
ret = color_fprintf(stdout, color, fmt,
|
ret = color_fprintf(stdout, color, fmt, str);
|
||||||
value ? "true" : "false");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int print_color_bool(enum output_type type,
|
||||||
|
enum color_attr color,
|
||||||
|
const char *key,
|
||||||
|
const char *fmt,
|
||||||
|
bool value)
|
||||||
|
{
|
||||||
|
return __print_color_bool(type, color, key, fmt, value,
|
||||||
|
value ? "true" : "false");
|
||||||
|
}
|
||||||
|
|
||||||
|
int print_color_on_off(enum output_type type,
|
||||||
|
enum color_attr color,
|
||||||
|
const char *key,
|
||||||
|
const char *fmt,
|
||||||
|
bool value)
|
||||||
|
{
|
||||||
|
return __print_color_bool(type, color, key, fmt, value,
|
||||||
|
value ? "on" : "off");
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* In JSON context uses hardcode %#x format: 42 -> 0x2a
|
* In JSON context uses hardcode %#x format: 42 -> 0x2a
|
||||||
*/
|
*/
|
||||||
|
|
@ -288,3 +309,65 @@ void print_nl(void)
|
||||||
if (!_jw)
|
if (!_jw)
|
||||||
printf("%s", _SL_);
|
printf("%s", _SL_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int print_color_rate(bool use_iec, enum output_type type, enum color_attr color,
|
||||||
|
const char *key, const char *fmt, unsigned long long rate)
|
||||||
|
{
|
||||||
|
unsigned long kilo = use_iec ? 1024 : 1000;
|
||||||
|
const char *str = use_iec ? "i" : "";
|
||||||
|
static char *units[5] = {"", "K", "M", "G", "T"};
|
||||||
|
char *buf;
|
||||||
|
int rc;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (_IS_JSON_CONTEXT(type))
|
||||||
|
return print_color_lluint(type, color, key, "%llu", rate);
|
||||||
|
|
||||||
|
rate <<= 3; /* bytes/sec -> bits/sec */
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(units) - 1; i++) {
|
||||||
|
if (rate < kilo)
|
||||||
|
break;
|
||||||
|
if (((rate % kilo) != 0) && rate < 1000*kilo)
|
||||||
|
break;
|
||||||
|
rate /= kilo;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = asprintf(&buf, "%.0f%s%sbit", (double)rate, units[i],
|
||||||
|
i > 0 ? str : "");
|
||||||
|
if (rc < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
rc = print_color_string(type, color, key, fmt, buf);
|
||||||
|
free(buf);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *sprint_size(__u32 sz, char *buf)
|
||||||
|
{
|
||||||
|
long kilo = 1024;
|
||||||
|
long mega = kilo * kilo;
|
||||||
|
size_t len = SPRINT_BSIZE - 1;
|
||||||
|
double tmp = sz;
|
||||||
|
|
||||||
|
if (sz >= mega && fabs(mega * rint(tmp / mega) - sz) < 1024)
|
||||||
|
snprintf(buf, len, "%gMb", rint(tmp / mega));
|
||||||
|
else if (sz >= kilo && fabs(kilo * rint(tmp / kilo) - sz) < 16)
|
||||||
|
snprintf(buf, len, "%gKb", rint(tmp / kilo));
|
||||||
|
else
|
||||||
|
snprintf(buf, len, "%ub", sz);
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
int print_color_size(enum output_type type, enum color_attr color,
|
||||||
|
const char *key, const char *fmt, __u32 sz)
|
||||||
|
{
|
||||||
|
SPRINT_BUF(buf);
|
||||||
|
|
||||||
|
if (_IS_JSON_CONTEXT(type))
|
||||||
|
return print_color_uint(type, color, key, "%u", sz);
|
||||||
|
|
||||||
|
sprint_size(sz, buf);
|
||||||
|
return print_color_string(type, color, key, fmt, buf);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,110 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
|
/*
|
||||||
|
* mnl_utils.c Helpers for working with libmnl.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <libmnl/libmnl.h>
|
||||||
|
|
||||||
|
#include "libnetlink.h"
|
||||||
|
#include "mnl_utils.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
struct mnl_socket *mnlu_socket_open(int bus)
|
||||||
|
{
|
||||||
|
struct mnl_socket *nl;
|
||||||
|
int one = 1;
|
||||||
|
|
||||||
|
nl = mnl_socket_open(bus);
|
||||||
|
if (nl == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
mnl_socket_setsockopt(nl, NETLINK_CAP_ACK, &one, sizeof(one));
|
||||||
|
mnl_socket_setsockopt(nl, NETLINK_EXT_ACK, &one, sizeof(one));
|
||||||
|
|
||||||
|
if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0)
|
||||||
|
goto err_bind;
|
||||||
|
|
||||||
|
return nl;
|
||||||
|
|
||||||
|
err_bind:
|
||||||
|
mnl_socket_close(nl);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct nlmsghdr *mnlu_msg_prepare(void *buf, uint32_t nlmsg_type, uint16_t flags,
|
||||||
|
void *extra_header, size_t extra_header_size)
|
||||||
|
{
|
||||||
|
struct nlmsghdr *nlh;
|
||||||
|
void *eh;
|
||||||
|
|
||||||
|
nlh = mnl_nlmsg_put_header(buf);
|
||||||
|
nlh->nlmsg_type = nlmsg_type;
|
||||||
|
nlh->nlmsg_flags = flags;
|
||||||
|
nlh->nlmsg_seq = time(NULL);
|
||||||
|
|
||||||
|
eh = mnl_nlmsg_put_extra_header(nlh, extra_header_size);
|
||||||
|
memcpy(eh, extra_header, extra_header_size);
|
||||||
|
|
||||||
|
return nlh;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mnlu_cb_noop(const struct nlmsghdr *nlh, void *data)
|
||||||
|
{
|
||||||
|
return MNL_CB_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mnlu_cb_error(const struct nlmsghdr *nlh, void *data)
|
||||||
|
{
|
||||||
|
const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);
|
||||||
|
|
||||||
|
/* Netlink subsystems returns the errno value with different signess */
|
||||||
|
if (err->error < 0)
|
||||||
|
errno = -err->error;
|
||||||
|
else
|
||||||
|
errno = err->error;
|
||||||
|
|
||||||
|
if (nl_dump_ext_ack(nlh, NULL))
|
||||||
|
return MNL_CB_ERROR;
|
||||||
|
|
||||||
|
return err->error == 0 ? MNL_CB_STOP : MNL_CB_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mnlu_cb_stop(const struct nlmsghdr *nlh, void *data)
|
||||||
|
{
|
||||||
|
int len = *(int *)NLMSG_DATA(nlh);
|
||||||
|
|
||||||
|
if (len < 0) {
|
||||||
|
errno = -len;
|
||||||
|
nl_dump_ext_ack_done(nlh, len);
|
||||||
|
return MNL_CB_ERROR;
|
||||||
|
}
|
||||||
|
return MNL_CB_STOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
static mnl_cb_t mnlu_cb_array[NLMSG_MIN_TYPE] = {
|
||||||
|
[NLMSG_NOOP] = mnlu_cb_noop,
|
||||||
|
[NLMSG_ERROR] = mnlu_cb_error,
|
||||||
|
[NLMSG_DONE] = mnlu_cb_stop,
|
||||||
|
[NLMSG_OVERRUN] = mnlu_cb_noop,
|
||||||
|
};
|
||||||
|
|
||||||
|
int mnlu_socket_recv_run(struct mnl_socket *nl, unsigned int seq, void *buf, size_t buf_size,
|
||||||
|
mnl_cb_t cb, void *data)
|
||||||
|
{
|
||||||
|
unsigned int portid = mnl_socket_get_portid(nl);
|
||||||
|
int err;
|
||||||
|
|
||||||
|
do {
|
||||||
|
err = mnl_socket_recvfrom(nl, buf, buf_size);
|
||||||
|
if (err <= 0)
|
||||||
|
break;
|
||||||
|
err = mnl_cb_run2(buf, err, seq, portid,
|
||||||
|
cb, data, mnlu_cb_array,
|
||||||
|
ARRAY_SIZE(mnlu_cb_array));
|
||||||
|
} while (err > 0);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
217
lib/utils.c
217
lib/utils.c
|
|
@ -513,6 +513,120 @@ int get_addr64(__u64 *ap, const char *cp)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* See http://physics.nist.gov/cuu/Units/binary.html */
|
||||||
|
static const struct rate_suffix {
|
||||||
|
const char *name;
|
||||||
|
double scale;
|
||||||
|
} suffixes[] = {
|
||||||
|
{ "bit", 1. },
|
||||||
|
{ "Kibit", 1024. },
|
||||||
|
{ "kbit", 1000. },
|
||||||
|
{ "mibit", 1024.*1024. },
|
||||||
|
{ "mbit", 1000000. },
|
||||||
|
{ "gibit", 1024.*1024.*1024. },
|
||||||
|
{ "gbit", 1000000000. },
|
||||||
|
{ "tibit", 1024.*1024.*1024.*1024. },
|
||||||
|
{ "tbit", 1000000000000. },
|
||||||
|
{ "Bps", 8. },
|
||||||
|
{ "KiBps", 8.*1024. },
|
||||||
|
{ "KBps", 8000. },
|
||||||
|
{ "MiBps", 8.*1024*1024. },
|
||||||
|
{ "MBps", 8000000. },
|
||||||
|
{ "GiBps", 8.*1024.*1024.*1024. },
|
||||||
|
{ "GBps", 8000000000. },
|
||||||
|
{ "TiBps", 8.*1024.*1024.*1024.*1024. },
|
||||||
|
{ "TBps", 8000000000000. },
|
||||||
|
{ NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
int get_rate(unsigned int *rate, const char *str)
|
||||||
|
{
|
||||||
|
char *p;
|
||||||
|
double bps = strtod(str, &p);
|
||||||
|
const struct rate_suffix *s;
|
||||||
|
|
||||||
|
if (p == str)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
for (s = suffixes; s->name; ++s) {
|
||||||
|
if (strcasecmp(s->name, p) == 0) {
|
||||||
|
bps *= s->scale;
|
||||||
|
p += strlen(p);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*p)
|
||||||
|
return -1; /* unknown suffix */
|
||||||
|
|
||||||
|
bps /= 8; /* -> bytes per second */
|
||||||
|
*rate = bps;
|
||||||
|
/* detect if an overflow happened */
|
||||||
|
if (*rate != floor(bps))
|
||||||
|
return -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_rate64(__u64 *rate, const char *str)
|
||||||
|
{
|
||||||
|
char *p;
|
||||||
|
double bps = strtod(str, &p);
|
||||||
|
const struct rate_suffix *s;
|
||||||
|
|
||||||
|
if (p == str)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
for (s = suffixes; s->name; ++s) {
|
||||||
|
if (strcasecmp(s->name, p) == 0) {
|
||||||
|
bps *= s->scale;
|
||||||
|
p += strlen(p);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*p)
|
||||||
|
return -1; /* unknown suffix */
|
||||||
|
|
||||||
|
bps /= 8; /* -> bytes per second */
|
||||||
|
*rate = bps;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_size(unsigned int *size, const char *str)
|
||||||
|
{
|
||||||
|
double sz;
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
sz = strtod(str, &p);
|
||||||
|
if (p == str)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (*p) {
|
||||||
|
if (strcasecmp(p, "kb") == 0 || strcasecmp(p, "k") == 0)
|
||||||
|
sz *= 1024;
|
||||||
|
else if (strcasecmp(p, "gb") == 0 || strcasecmp(p, "g") == 0)
|
||||||
|
sz *= 1024*1024*1024;
|
||||||
|
else if (strcasecmp(p, "gbit") == 0)
|
||||||
|
sz *= 1024*1024*1024/8;
|
||||||
|
else if (strcasecmp(p, "mb") == 0 || strcasecmp(p, "m") == 0)
|
||||||
|
sz *= 1024*1024;
|
||||||
|
else if (strcasecmp(p, "mbit") == 0)
|
||||||
|
sz *= 1024*1024/8;
|
||||||
|
else if (strcasecmp(p, "kbit") == 0)
|
||||||
|
sz *= 1024/8;
|
||||||
|
else if (strcasecmp(p, "b") != 0)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*size = sz;
|
||||||
|
|
||||||
|
/* detect if an overflow happened */
|
||||||
|
if (*size != floor(sz))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void set_address_type(inet_prefix *addr)
|
static void set_address_type(inet_prefix *addr)
|
||||||
{
|
{
|
||||||
switch (addr->family) {
|
switch (addr->family) {
|
||||||
|
|
@ -1695,3 +1809,106 @@ char *sprint_time64(__s64 time, char *buf)
|
||||||
print_time64(buf, SPRINT_BSIZE-1, time);
|
print_time64(buf, SPRINT_BSIZE-1, time);
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int do_batch(const char *name, bool force,
|
||||||
|
int (*cmd)(int argc, char *argv[], void *data), void *data)
|
||||||
|
{
|
||||||
|
char *line = NULL;
|
||||||
|
size_t len = 0;
|
||||||
|
int ret = EXIT_SUCCESS;
|
||||||
|
|
||||||
|
if (name && strcmp(name, "-") != 0) {
|
||||||
|
if (freopen(name, "r", stdin) == NULL) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"Cannot open file \"%s\" for reading: %s\n",
|
||||||
|
name, strerror(errno));
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdlineno = 0;
|
||||||
|
while (getcmdline(&line, &len, stdin) != -1) {
|
||||||
|
char *largv[100];
|
||||||
|
int largc;
|
||||||
|
|
||||||
|
largc = makeargs(line, largv, 100);
|
||||||
|
if (!largc)
|
||||||
|
continue; /* blank line */
|
||||||
|
|
||||||
|
if (cmd(largc, largv, data)) {
|
||||||
|
fprintf(stderr, "Command failed %s:%d\n",
|
||||||
|
name, cmdlineno);
|
||||||
|
ret = EXIT_FAILURE;
|
||||||
|
if (!force)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line)
|
||||||
|
free(line);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int parse_one_of(const char *msg, const char *realval, const char * const *list,
|
||||||
|
size_t len, int *p_err)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
if (list[i] && matches(realval, list[i]) == 0) {
|
||||||
|
*p_err = 0;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "Error: argument of \"%s\" must be one of ", msg);
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
if (list[i])
|
||||||
|
fprintf(stderr, "\"%s\", ", list[i]);
|
||||||
|
fprintf(stderr, "not \"%s\"\n", realval);
|
||||||
|
*p_err = -EINVAL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool parse_on_off(const char *msg, const char *realval, int *p_err)
|
||||||
|
{
|
||||||
|
static const char * const values_on_off[] = { "off", "on" };
|
||||||
|
|
||||||
|
return parse_one_of(msg, realval, values_on_off, ARRAY_SIZE(values_on_off), p_err);
|
||||||
|
}
|
||||||
|
|
||||||
|
int parse_mapping(int *argcp, char ***argvp, bool allow_all,
|
||||||
|
int (*mapping_cb)(__u32 key, char *value, void *data),
|
||||||
|
void *mapping_cb_data)
|
||||||
|
{
|
||||||
|
int argc = *argcp;
|
||||||
|
char **argv = *argvp;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
while (argc > 0) {
|
||||||
|
char *colon = strchr(*argv, ':');
|
||||||
|
__u32 key;
|
||||||
|
|
||||||
|
if (!colon)
|
||||||
|
break;
|
||||||
|
*colon = '\0';
|
||||||
|
|
||||||
|
if (allow_all && matches(*argv, "all") == 0) {
|
||||||
|
key = (__u32) -1;
|
||||||
|
} else if (get_u32(&key, *argv, 0)) {
|
||||||
|
ret = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (mapping_cb(key, colon + 1, mapping_cb_data)) {
|
||||||
|
ret = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
argc--, argv++;
|
||||||
|
}
|
||||||
|
|
||||||
|
*argcp = argc;
|
||||||
|
*argvp = argv;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -665,7 +665,7 @@ the bridge to which this address is associated.
|
||||||
.SH bridge mdb - multicast group database management
|
.SH bridge mdb - multicast group database management
|
||||||
|
|
||||||
.B mdb
|
.B mdb
|
||||||
objects contain known IP multicast group addresses on a link.
|
objects contain known IP or L2 multicast group addresses on a link.
|
||||||
|
|
||||||
.P
|
.P
|
||||||
The corresponding commands display mdb entries, add new entries,
|
The corresponding commands display mdb entries, add new entries,
|
||||||
|
|
@ -685,11 +685,11 @@ the port whose link is known to have members of this multicast group.
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BI grp " GROUP"
|
.BI grp " GROUP"
|
||||||
the IP multicast group address whose members reside on the link connected to
|
the multicast group address (IPv4, IPv6 or L2 multicast) whose members reside
|
||||||
the port.
|
on the link connected to the port.
|
||||||
|
|
||||||
.B permanent
|
.B permanent
|
||||||
- the mdb entry is permanent
|
- the mdb entry is permanent. Optional for IPv4 and IPv6, mandatory for L2.
|
||||||
.sp
|
.sp
|
||||||
|
|
||||||
.B temp
|
.B temp
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,126 @@
|
||||||
|
.TH DCB-BUFFER 8 "12 November 2020" "iproute2" "Linux"
|
||||||
|
.SH NAME
|
||||||
|
dcb-buffer \- show / manipulate port buffer settings of
|
||||||
|
the DCB (Data Center Bridging) subsystem
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.sp
|
||||||
|
.ad l
|
||||||
|
.in +8
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.B dcb
|
||||||
|
.RI "[ " OPTIONS " ] "
|
||||||
|
.B buffer
|
||||||
|
.RI "{ " COMMAND " | " help " }"
|
||||||
|
.sp
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.B dcb buffer show dev
|
||||||
|
.RI DEV
|
||||||
|
.RB "[ " prio-buffer " ]"
|
||||||
|
.RB "[ " buffer-size " ]"
|
||||||
|
.RB "[ " total-size " ]"
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.B dcb buffer set dev
|
||||||
|
.RI DEV
|
||||||
|
.RB "[ " prio-buffer " " \fIPRIO-MAP " ]"
|
||||||
|
.RB "[ " buffer-size " " \fISIZE-MAP " ]"
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.IR PRIO-MAP " := [ " PRIO-MAP " ] " PRIO-MAPPING
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.IR PRIO-MAPPING " := { " PRIO " | " \fBall " }" \fB:\fIBUFFER\fR
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.IR SIZE-MAP " := [ " SIZE-MAP " ] " SIZE-MAPPING
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.IR SIZE-MAPPING " := { " BUFFER " | " \fBall " }" \fB:\fISIZE\fR
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.IR PRIO " := { " \fB0\fR " .. " \fB7\fR " }"
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.IR BUFFER " := { " \fB0\fR " .. " \fB7\fR " }"
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.IR SIZE " := { " INTEGER " | " INTEGER\fBK\fR " | " INTEGER\fBM\fR " | " ... " }"
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
|
||||||
|
.B dcb buffer
|
||||||
|
is used to configure assignment of traffic to port buffers based on traffic
|
||||||
|
priority, and sizes of those buffers. It can be also used to inspect the current
|
||||||
|
configuration, as well as total device memory that the port buffers take.
|
||||||
|
|
||||||
|
.SH PARAMETERS
|
||||||
|
|
||||||
|
For read-write parameters, the following describes only the write direction,
|
||||||
|
i.e. as used with the \fBset\fR command. For the \fBshow\fR command, the
|
||||||
|
parameter name is to be used as a simple keyword without further arguments. This
|
||||||
|
instructs the tool to show the value of a given parameter. When no parameters
|
||||||
|
are given, the tool shows the complete buffer configuration.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B total-size
|
||||||
|
A read-only property that shows the total device memory taken up by port
|
||||||
|
buffers. This might be more than a simple sum of individual buffer sizes if
|
||||||
|
there are any hidden or internal buffers.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B prio-buffer \fIPRIO-MAP
|
||||||
|
\fIPRIO-MAP\fR uses the array parameter syntax, see
|
||||||
|
.BR dcb (8)
|
||||||
|
for details. Keys are priorities, values are buffer indices. For each priority
|
||||||
|
sets a buffer where traffic with that priority is directed to.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B buffer-size \fISIZE-MAP
|
||||||
|
\fISIZE-MAP\fR uses the array parameter syntax, see
|
||||||
|
.BR dcb (8)
|
||||||
|
for details. Keys are buffer indices, values are sizes of that buffer in bytes.
|
||||||
|
The sizes can use the notation documented in section PARAMETERS at
|
||||||
|
.BR tc (8).
|
||||||
|
Note that the size requested by the tool can be rounded or capped by the driver
|
||||||
|
to satisfy the requirements of the device.
|
||||||
|
|
||||||
|
.SH EXAMPLE & USAGE
|
||||||
|
|
||||||
|
Configure the priomap in a one-to-one fashion:
|
||||||
|
|
||||||
|
.P
|
||||||
|
# dcb buffer set dev eth0 prio-buffer 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7
|
||||||
|
|
||||||
|
Set sizes of all buffers to 10KB, except for buffer 6, which will have the size
|
||||||
|
1MB:
|
||||||
|
|
||||||
|
.P
|
||||||
|
# dcb buffer set dev eth0 buffer-size all:10K 6:1M
|
||||||
|
|
||||||
|
Show what was set:
|
||||||
|
|
||||||
|
.P
|
||||||
|
# dcb buffer show dev eth0
|
||||||
|
.br
|
||||||
|
prio-buffer 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7
|
||||||
|
.br
|
||||||
|
buffer-size 0:10Kb 1:10Kb 2:10Kb 3:10Kb 4:10Kb 5:10Kb 6:1Mb 7:10Kb
|
||||||
|
.br
|
||||||
|
total-size 1222Kb
|
||||||
|
|
||||||
|
.SH EXIT STATUS
|
||||||
|
Exit status is 0 if command was successful or a positive integer upon failure.
|
||||||
|
|
||||||
|
.SH SEE ALSO
|
||||||
|
.BR dcb (8)
|
||||||
|
|
||||||
|
.SH REPORTING BUGS
|
||||||
|
Report any bugs to the Network Developers mailing list
|
||||||
|
.B <netdev@vger.kernel.org>
|
||||||
|
where the development and maintenance is primarily done.
|
||||||
|
You do not have to be subscribed to the list to send a message there.
|
||||||
|
|
||||||
|
.SH AUTHOR
|
||||||
|
Petr Machata <me@pmachata.org>
|
||||||
|
|
@ -0,0 +1,194 @@
|
||||||
|
.TH DCB-ETS 8 "19 October 2020" "iproute2" "Linux"
|
||||||
|
.SH NAME
|
||||||
|
dcb-ets \- show / manipulate ETS (Enhanced Transmission Selection) settings of
|
||||||
|
the DCB (Data Center Bridging) subsystem
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.sp
|
||||||
|
.ad l
|
||||||
|
.in +8
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.B dcb
|
||||||
|
.RI "[ " OPTIONS " ] "
|
||||||
|
.B ets
|
||||||
|
.RI "{ " COMMAND " | " help " }"
|
||||||
|
.sp
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.B dcb ets show dev
|
||||||
|
.RI DEV
|
||||||
|
.RB "[ " willing " ]"
|
||||||
|
.RB "[ " ets-cap " ]"
|
||||||
|
.RB "[ " cbs " ]"
|
||||||
|
.RB "[ " tc-tsa " ]"
|
||||||
|
.RB "[ " reco-tc-tsa " ]"
|
||||||
|
.RB "[ " pg-bw " ]"
|
||||||
|
.RB "[ " tc-bw " ]"
|
||||||
|
.RB "[ " reco-tc-bw " ]"
|
||||||
|
.RB "[ " prio-tc " ]"
|
||||||
|
.RB "[ " reco-prio-tc " ]"
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.B dcb ets set dev
|
||||||
|
.RI DEV
|
||||||
|
.RB "[ " willing " { " on " | " off " } ]"
|
||||||
|
.RB "[ { " tc-tsa " | " reco-tc-tsa " } " \fITSA-MAP\fB " ]"
|
||||||
|
.RB "[ { " pg-bw " | " tc-bw " | " reco-tc-bw " } " \fIBW-MAP\fB " ]"
|
||||||
|
.RB "[ { " prio-tc " | " reco-prio-tc " } " \fIPRIO-MAP\fB " ]"
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.IR TSA-MAP " := [ " TSA-MAP " ] " TSA-MAPPING
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.IR TSA-MAPPING " := { " TC " | " \fBall " }" \fB: "{ " \fBstrict\fR " | "
|
||||||
|
.IR \fBcbs\fR " | " \fBets\fR " | " \fBvendor\fR " }"
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.IR BW-MAP " := [ " BW-MAP " ] " BW-MAPPING
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.IR BW-MAPPING " := { " TC " | " \fBall " }" \fB:\fIINTEGER\fR
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.IR PRIO-MAP " := [ " PRIO-MAP " ] " PRIO-MAPPING
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.IR PRIO-MAPPING " := { " PRIO " | " \fBall " }" \fB:\fITC\fR
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.IR TC " := { " \fB0\fR " .. " \fB7\fR " }"
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.IR PRIO " := { " \fB0\fR " .. " \fB7\fR " }"
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
|
||||||
|
.B dcb ets
|
||||||
|
is used to configure Enhanced Transmission Selection attributes through Linux
|
||||||
|
DCB (Data Center Bridging) interface. ETS permits configuration of mapping of
|
||||||
|
priorities to traffic classes, traffic selection algorithm to use per traffic
|
||||||
|
class, bandwidth allocation, etc.
|
||||||
|
|
||||||
|
Two DCB TLVs are related to the ETS feature: a configuration and recommendation
|
||||||
|
values. Recommendation values are named with a prefix
|
||||||
|
.B reco-,
|
||||||
|
while the configuration ones have plain names.
|
||||||
|
|
||||||
|
.SH PARAMETERS
|
||||||
|
|
||||||
|
For read-write parameters, the following describes only the write direction,
|
||||||
|
i.e. as used with the \fBset\fR command. For the \fBshow\fR command, the
|
||||||
|
parameter name is to be used as a simple keyword without further arguments. This
|
||||||
|
instructs the tool to show the value of a given parameter. When no parameters
|
||||||
|
are given, the tool shows the complete ETS configuration.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B ets-cap
|
||||||
|
A read-only property that shows the number of supported ETS traffic classes.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B cbs
|
||||||
|
A read-only property that is enabled if the driver and the hardware support the
|
||||||
|
CBS Transmission Selection Algorithm.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B willing \fR{ \fBon\fR | \fBoff\fR }
|
||||||
|
Whether local host should accept configuration from peer TLVs.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B prio-tc \fIPRIO-MAP
|
||||||
|
.TQ
|
||||||
|
.B reco-prio-tc \fIPRIO-MAP
|
||||||
|
\fIPRIO-MAP\fR uses the array parameter syntax, see
|
||||||
|
.BR dcb (8)
|
||||||
|
for details. Keys are priorities, values are traffic classes. For each priority
|
||||||
|
sets a TC where traffic with that priority is directed to.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B tc-tsa \fITSA-MAP
|
||||||
|
.TQ
|
||||||
|
.B reco-tc-tsa \fITSA-MAP
|
||||||
|
\fITSA-MAP\fR uses the array parameter syntax, see
|
||||||
|
.BR dcb (8)
|
||||||
|
for details. Keys are TCs, values are Transmission Selection Algorithm (TSA)
|
||||||
|
keywords described below. For each TC sets an algorithm used for deciding how
|
||||||
|
traffic queued up at this TC is scheduled for transmission. Supported TSAs are:
|
||||||
|
|
||||||
|
.B strict
|
||||||
|
- for strict priority, where traffic in higher-numbered TCs always takes
|
||||||
|
precedence over traffic in lower-numbered TCs.
|
||||||
|
.br
|
||||||
|
.B ets
|
||||||
|
- for Enhanced Traffic Selection, where available bandwidth is distributed among
|
||||||
|
the ETS-enabled TCs according to the weights set by
|
||||||
|
.B tc-bw
|
||||||
|
and
|
||||||
|
.B reco-tc-bw\fR,
|
||||||
|
respectively.
|
||||||
|
.br
|
||||||
|
.B cbs
|
||||||
|
- for Credit Based Shaper, where traffic is scheduled in a strict manner up to
|
||||||
|
the limit set by a shaper.
|
||||||
|
.br
|
||||||
|
.B vendor
|
||||||
|
- for vendor-specific traffic selection algorithm.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B tc-bw \fIBW-MAP
|
||||||
|
.TQ
|
||||||
|
.B reco-tc-bw \fIBW-MAP
|
||||||
|
\fIBW-MAP\fR uses the array parameter syntax, see
|
||||||
|
.BR dcb (8)
|
||||||
|
for details. Keys are TCs, values are integers representing percent of available
|
||||||
|
bandwidth given to the traffic class in question. The value should be 0 for TCs
|
||||||
|
whose TSA is not \fBets\fR, and the sum of all values shall be 100. As an
|
||||||
|
exception to the standard wording, a configuration with no \fBets\fR TCs is
|
||||||
|
permitted to sum up to 0 instead.
|
||||||
|
.br
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B pg-bw \fIBW-MAP
|
||||||
|
The precise meaning of \fBpg-bw\fR is not standardized, but the assumption seems
|
||||||
|
to be that the same scheduling process as on the transmit side is applicable on
|
||||||
|
receive side as well, and configures receive bandwidth allocation for \fBets\fR
|
||||||
|
ingress traffic classes (priority groups).
|
||||||
|
|
||||||
|
.SH EXAMPLE & USAGE
|
||||||
|
|
||||||
|
Configure ETS priomap in a one-to-one fashion:
|
||||||
|
|
||||||
|
.P
|
||||||
|
# dcb ets set dev eth0 prio-tc 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7
|
||||||
|
|
||||||
|
Set TSA and transmit bandwidth configuration:
|
||||||
|
|
||||||
|
.P
|
||||||
|
# dcb ets set dev eth0 tc-tsa all:strict 0:ets 1:ets 2:ets \\
|
||||||
|
.br
|
||||||
|
tc-bw all:0 0:33 1:33 2:34
|
||||||
|
|
||||||
|
Show what was set:
|
||||||
|
|
||||||
|
.P
|
||||||
|
# dcb ets show dev eth0 prio-tc tc-tsa tc-bw
|
||||||
|
.br
|
||||||
|
prio-tc 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7
|
||||||
|
.br
|
||||||
|
tc-tsa 0:ets 1:ets 2:ets 3:strict 4:strict 5:strict 6:strict 7:strict
|
||||||
|
.br
|
||||||
|
tc-bw 0:33 1:33 2:34 3:0 4:0 5:0 6:0 7:0
|
||||||
|
|
||||||
|
.SH EXIT STATUS
|
||||||
|
Exit status is 0 if command was successful or a positive integer upon failure.
|
||||||
|
|
||||||
|
.SH SEE ALSO
|
||||||
|
.BR dcb (8)
|
||||||
|
|
||||||
|
.SH REPORTING BUGS
|
||||||
|
Report any bugs to the Network Developers mailing list
|
||||||
|
.B <netdev@vger.kernel.org>
|
||||||
|
where the development and maintenance is primarily done.
|
||||||
|
You do not have to be subscribed to the list to send a message there.
|
||||||
|
|
||||||
|
.SH AUTHOR
|
||||||
|
Petr Machata <me@pmachata.org>
|
||||||
|
|
@ -0,0 +1,94 @@
|
||||||
|
.TH DCB-MAXRATE 8 "22 November 2020" "iproute2" "Linux"
|
||||||
|
.SH NAME
|
||||||
|
dcb-maxrate \- show / manipulate port maxrate settings of
|
||||||
|
the DCB (Data Center Bridging) subsystem
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.sp
|
||||||
|
.ad l
|
||||||
|
.in +8
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.B dcb
|
||||||
|
.RI "[ " OPTIONS " ] "
|
||||||
|
.B maxrate
|
||||||
|
.RI "{ " COMMAND " | " help " }"
|
||||||
|
.sp
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.B dcb maxrate show dev
|
||||||
|
.RI DEV
|
||||||
|
.RB "[ " tc-maxrate " ]"
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.B dcb maxrate set dev
|
||||||
|
.RI DEV
|
||||||
|
.RB "[ " tc-maxrate " " \fIRATE-MAP " ]"
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.IR RATE-MAP " := [ " RATE-MAP " ] " RATE-MAPPING
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.IR RATE-MAPPING " := { " TC " | " \fBall " }" \fB:\fIRATE\fR
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.IR TC " := { " \fB0\fR " .. " \fB7\fR " }"
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.IR RATE " := { " INTEGER "[" \fBbit\fR "] | " INTEGER\fBKbit\fR " | "
|
||||||
|
.IR INTEGER\fBMib\fR " | " ... " }"
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
|
||||||
|
.B dcb maxrate
|
||||||
|
is used to configure and inspect maximum rate at which traffic is allowed to
|
||||||
|
egress from a given traffic class.
|
||||||
|
|
||||||
|
.SH PARAMETERS
|
||||||
|
|
||||||
|
The following describes only the write direction, i.e. as used with the
|
||||||
|
\fBset\fR command. For the \fBshow\fR command, the parameter name is to be used
|
||||||
|
as a simple keyword without further arguments. This instructs the tool to show
|
||||||
|
the value of a given parameter. When no parameters are given, the tool shows the
|
||||||
|
complete maxrate configuration.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B tc-maxrate \fIRATE-MAP
|
||||||
|
\fIRATE-MAP\fR uses the array parameter syntax, see
|
||||||
|
.BR dcb (8)
|
||||||
|
for details. Keys are TC indices, values are traffic rates in bits per second.
|
||||||
|
The rates can use the notation documented in section PARAMETERS at
|
||||||
|
.BR tc (8).
|
||||||
|
Note that under that notation, "bit" stands for bits per second whereas "b"
|
||||||
|
stands for bytes per second. When showing, the command line option
|
||||||
|
.B -i
|
||||||
|
toggles between using decadic and ISO/IEC prefixes.
|
||||||
|
|
||||||
|
.SH EXAMPLE & USAGE
|
||||||
|
|
||||||
|
Set rates of all traffic classes to 25Gbps, except for TC 6, which will
|
||||||
|
have the rate of 100Gbps:
|
||||||
|
|
||||||
|
.P
|
||||||
|
# dcb maxrate set dev eth0 tc-maxrate all:25Gbit 6:100Gbit
|
||||||
|
|
||||||
|
Show what was set:
|
||||||
|
|
||||||
|
.P
|
||||||
|
# dcb maxrate show dev eth0
|
||||||
|
.br
|
||||||
|
tc-maxrate 0:25Gbit 1:25Gbit 2:25Gbit 3:25Gbit 4:25Gbit 5:25Gbit 6:100Gbit 7:25Gbit
|
||||||
|
|
||||||
|
.SH EXIT STATUS
|
||||||
|
Exit status is 0 if command was successful or a positive integer upon failure.
|
||||||
|
|
||||||
|
.SH SEE ALSO
|
||||||
|
.BR dcb (8)
|
||||||
|
|
||||||
|
.SH REPORTING BUGS
|
||||||
|
Report any bugs to the Network Developers mailing list
|
||||||
|
.B <netdev@vger.kernel.org>
|
||||||
|
where the development and maintenance is primarily done.
|
||||||
|
You do not have to be subscribed to the list to send a message there.
|
||||||
|
|
||||||
|
.SH AUTHOR
|
||||||
|
Petr Machata <me@pmachata.org>
|
||||||
|
|
@ -0,0 +1,127 @@
|
||||||
|
.TH DCB-PFC 8 "31 October 2020" "iproute2" "Linux"
|
||||||
|
.SH NAME
|
||||||
|
dcb-pfc \- show / manipulate PFC (Priority-based Flow Control) settings of
|
||||||
|
the DCB (Data Center Bridging) subsystem
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.sp
|
||||||
|
.ad l
|
||||||
|
.in +8
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.B dcb
|
||||||
|
.RI "[ " OPTIONS " ] "
|
||||||
|
.B pfc
|
||||||
|
.RI "{ " COMMAND " | " help " }"
|
||||||
|
.sp
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.B dcb pfc show dev
|
||||||
|
.RI DEV
|
||||||
|
.RB "[ " pfc-cap " ]"
|
||||||
|
.RB "[ " prio-pfc " ]"
|
||||||
|
.RB "[ " macsec-bypass " ]"
|
||||||
|
.RB "[ " delay " ]"
|
||||||
|
.RB "[ " requests " ]"
|
||||||
|
.RB "[ " indications " ]"
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.B dcb pfc set dev
|
||||||
|
.RI DEV
|
||||||
|
.RB "[ " prio-pfc " " \fIPFC-MAP " ]"
|
||||||
|
.RB "[ " macsec-bypass " { " on " | " off " } ]"
|
||||||
|
.RB "[ " delay " " \fIINTEGER\fR " ]"
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.IR PFC-MAP " := [ " PFC-MAP " ] " PFC-MAPPING
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.IR PFC-MAPPING " := { " PRIO " | " \fBall " }" \fB:\fR "{ "
|
||||||
|
.IR \fBon\fR " | " \fBoff\fR " }"
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.IR PRIO " := { " \fB0\fR " .. " \fB7\fR " }"
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
|
||||||
|
.B dcb pfc
|
||||||
|
is used to configure Priority-based Flow Control attributes through Linux
|
||||||
|
DCB (Data Center Bridging) interface. PFC permits marking flows with a
|
||||||
|
certain priority as lossless, and holds related configuration, as well as
|
||||||
|
PFC counters.
|
||||||
|
|
||||||
|
.SH PARAMETERS
|
||||||
|
|
||||||
|
For read-write parameters, the following describes only the write direction,
|
||||||
|
i.e. as used with the \fBset\fR command. For the \fBshow\fR command, the
|
||||||
|
parameter name is to be used as a simple keyword without further arguments. This
|
||||||
|
instructs the tool to show the value of a given parameter. When no parameters
|
||||||
|
are given, the tool shows the complete PFC configuration.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B pfc-cap
|
||||||
|
A read-only property that shows the number of traffic classes that may
|
||||||
|
simultaneously support PFC.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B requests
|
||||||
|
A read-only count of the sent PFC frames per traffic class. Only shown when
|
||||||
|
-s is given, or when requested explicitly.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B indications
|
||||||
|
A read-only count of the received PFC frames per traffic class. Only shown
|
||||||
|
when -s is given, or when requested explicitly.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B macsec-bypass \fR{ \fBon\fR | \fBoff\fR }
|
||||||
|
Whether the sending station is capable of bypassing MACsec processing when
|
||||||
|
MACsec is disabled.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B prio-pfc \fIPFC-MAP
|
||||||
|
\fIPFC-MAP\fR uses the array parameter syntax, see
|
||||||
|
.BR dcb (8)
|
||||||
|
for details. Keys are priorities, values are on / off indicators of whether
|
||||||
|
PFC is enabled for a given priority.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B delay \fIINTEGER
|
||||||
|
The allowance made for round-trip propagation delay of the link in bits.
|
||||||
|
The value shall be 0..65535.
|
||||||
|
|
||||||
|
.SH EXAMPLE & USAGE
|
||||||
|
|
||||||
|
Enable PFC on priorities 6 and 7, leaving the rest intact:
|
||||||
|
|
||||||
|
.P
|
||||||
|
# dcb pfc set dev eth0 prio-pfc 6:on 7:on
|
||||||
|
|
||||||
|
Disable PFC of all priorities except 6 and 7, and configure delay to 4096
|
||||||
|
bits:
|
||||||
|
|
||||||
|
.P
|
||||||
|
# dcb pfc set dev eth0 prio-pfc all:off 6:on 7:on delay 0x1000
|
||||||
|
|
||||||
|
Show what was set:
|
||||||
|
|
||||||
|
.P
|
||||||
|
# dcb pfc show dev eth0
|
||||||
|
.br
|
||||||
|
pfc-cap 8 macsec-bypass off delay 4096
|
||||||
|
.br
|
||||||
|
prio-pfc 0:off 1:off 2:off 3:off 4:off 5:off 6:on 7:on
|
||||||
|
|
||||||
|
.SH EXIT STATUS
|
||||||
|
Exit status is 0 if command was successful or a positive integer upon failure.
|
||||||
|
|
||||||
|
.SH SEE ALSO
|
||||||
|
.BR dcb (8)
|
||||||
|
|
||||||
|
.SH REPORTING BUGS
|
||||||
|
Report any bugs to the Network Developers mailing list
|
||||||
|
.B <netdev@vger.kernel.org>
|
||||||
|
where the development and maintenance is primarily done.
|
||||||
|
You do not have to be subscribed to the list to send a message there.
|
||||||
|
|
||||||
|
.SH AUTHOR
|
||||||
|
Petr Machata <me@pmachata.org>
|
||||||
|
|
@ -0,0 +1,139 @@
|
||||||
|
.TH DCB 8 "19 October 2020" "iproute2" "Linux"
|
||||||
|
.SH NAME
|
||||||
|
dcb \- show / manipulate DCB (Data Center Bridging) settings
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.sp
|
||||||
|
.ad l
|
||||||
|
.in +8
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.B dcb
|
||||||
|
.RI "[ " OPTIONS " ] "
|
||||||
|
.RB "{ " buffer " | " ets " | " maxrate " | " pfc " }"
|
||||||
|
.RI "{ " COMMAND " | " help " }"
|
||||||
|
.sp
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.B dcb
|
||||||
|
.RB "[ " -force " ] "
|
||||||
|
.BI "-batch " filename
|
||||||
|
.sp
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.B dcb
|
||||||
|
.RI "[ " OPTIONS " ] "
|
||||||
|
.B help
|
||||||
|
.sp
|
||||||
|
|
||||||
|
.SH OPTIONS
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR "\-V" , " --Version"
|
||||||
|
Print the version of the
|
||||||
|
.B dcb
|
||||||
|
utility and exit.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR "\-b", " --batch " <FILENAME>
|
||||||
|
Read commands from provided file or standard input and invoke them. First
|
||||||
|
failure will cause termination of dcb.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR "\-f", " --force"
|
||||||
|
Don't terminate dcb on errors in batch mode. If there were any errors during
|
||||||
|
execution of the commands, the application return code will be non zero.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR "\-i" , " --iec"
|
||||||
|
When showing rates, use ISO/IEC 1024-based prefixes (Ki, Mi, Bi) instead of
|
||||||
|
the 1000-based ones (K, M, B).
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR "\-j" , " --json"
|
||||||
|
Generate JSON output.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR "\-p" , " --pretty"
|
||||||
|
When combined with -j generate a pretty JSON output.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR "\-s" , " --statistics"
|
||||||
|
If the object in question contains any statistical counters, shown them as
|
||||||
|
part of the "show" output.
|
||||||
|
|
||||||
|
.SH OBJECTS
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B buffer
|
||||||
|
- Configuration of port buffers
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B ets
|
||||||
|
- Configuration of ETS (Enhanced Transmission Selection)
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B maxrate
|
||||||
|
- Configuration of per-TC maximum transmit rate
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B pfc
|
||||||
|
- Configuration of PFC (Priority-based Flow Control)
|
||||||
|
|
||||||
|
.SH COMMANDS
|
||||||
|
|
||||||
|
A \fICOMMAND\fR specifies the action to perform on the object. The set of
|
||||||
|
possible actions depends on the object type. As a rule, it is possible to
|
||||||
|
.B show
|
||||||
|
objects and to invoke topical
|
||||||
|
.B help,
|
||||||
|
which prints a list of available commands and argument syntax conventions.
|
||||||
|
|
||||||
|
.SH ARRAY PARAMETERS
|
||||||
|
|
||||||
|
Like commands, specification of parameters is in the domain of individual
|
||||||
|
objects (and their commands) as well. However, much of the DCB interface
|
||||||
|
revolves around arrays of fixed size that specify one value per some key, such
|
||||||
|
as per traffic class or per priority. There is therefore a single syntax for
|
||||||
|
adjusting elements of these arrays. It consists of a series of
|
||||||
|
\fIKEY\fB:\fIVALUE\fR pairs, where the meaning of the individual keys and values
|
||||||
|
depends on the parameter.
|
||||||
|
|
||||||
|
The elements are evaluated in order from left to right, and the latter ones
|
||||||
|
override the earlier ones. The elements that are not specified on the command
|
||||||
|
line are queried from the kernel and their current value is retained.
|
||||||
|
|
||||||
|
As an example, take a made-up parameter tc-juju, which can be set to charm
|
||||||
|
traffic in a given TC with either good luck or bad luck. \fIKEY\fR can therefore
|
||||||
|
be 0..7 (as is usual for TC numbers in DCB), and \fIVALUE\fR either of
|
||||||
|
\fBnone\fR, \fBgood\fR, and \fBbad\fR. An example of changing a juju value of
|
||||||
|
TCs 0 and 7, while leaving all other intact, would then be:
|
||||||
|
|
||||||
|
.P
|
||||||
|
# dcb foo set dev eth0 tc-juju 0:good 7:bad
|
||||||
|
|
||||||
|
A special key, \fBall\fR, is recognized which sets the same value to all array
|
||||||
|
elements. This can be combined with the usual single-element syntax. E.g. in the
|
||||||
|
following, the juju of all keys is set to \fBnone\fR, except 0 and 7, which have
|
||||||
|
other values:
|
||||||
|
|
||||||
|
.P
|
||||||
|
# dcb foo set dev eth0 tc-juju all:none 0:good 7:bad
|
||||||
|
|
||||||
|
.SH EXIT STATUS
|
||||||
|
Exit status is 0 if command was successful or a positive integer upon failure.
|
||||||
|
|
||||||
|
.SH SEE ALSO
|
||||||
|
.BR dcb-buffer (8),
|
||||||
|
.BR dcb-ets (8),
|
||||||
|
.BR dcb-maxrate (8),
|
||||||
|
.BR dcb-pfc (8)
|
||||||
|
.br
|
||||||
|
|
||||||
|
.SH REPORTING BUGS
|
||||||
|
Report any bugs to the Network Developers mailing list
|
||||||
|
.B <netdev@vger.kernel.org>
|
||||||
|
where the development and maintenance is primarily done.
|
||||||
|
You do not have to be subscribed to the list to send a message there.
|
||||||
|
|
||||||
|
.SH AUTHOR
|
||||||
|
Petr Machata <me@pmachata.org>
|
||||||
|
|
@ -63,6 +63,10 @@ devlink-dev \- devlink device configuration
|
||||||
[
|
[
|
||||||
.B netns
|
.B netns
|
||||||
.RI "{ " PID " | " NAME " | " ID " }"
|
.RI "{ " PID " | " NAME " | " ID " }"
|
||||||
|
] [
|
||||||
|
.BR action " { " driver_reinit " | " fw_activate " }"
|
||||||
|
] [
|
||||||
|
.B limit no_reset
|
||||||
]
|
]
|
||||||
|
|
||||||
.ti -8
|
.ti -8
|
||||||
|
|
@ -173,6 +177,36 @@ If this argument is omitted all parameters supported by devlink devices are list
|
||||||
.RI { " PID " | " NAME " | " ID " }
|
.RI { " PID " | " NAME " | " ID " }
|
||||||
- Specifies the network namespace to reload into, either by pid, name or id.
|
- Specifies the network namespace to reload into, either by pid, name or id.
|
||||||
|
|
||||||
|
.BR action " { " driver_reinit " | " fw_activate " }"
|
||||||
|
- Specifies the reload action required.
|
||||||
|
If this argument is omitted
|
||||||
|
.I driver_reinit
|
||||||
|
action will be used.
|
||||||
|
Note that even though user asks for a specific action, the driver implementation
|
||||||
|
might require to perform another action alongside with it. For example, some
|
||||||
|
driver do not support driver reinitialization being performed without fw
|
||||||
|
activation. Therefore, the devlink reload command returns the list of actions
|
||||||
|
which were actrually performed.
|
||||||
|
|
||||||
|
.I driver_reinit
|
||||||
|
- Driver entities re-initialization, applying devlink-param and
|
||||||
|
devlink-resource values.
|
||||||
|
|
||||||
|
.I fw_activate
|
||||||
|
- Activates new firmware if such image is stored and pending activation. If no
|
||||||
|
limitation specified this action may involve firmware reset. If no new image
|
||||||
|
pending this action will reload current firmware image.
|
||||||
|
|
||||||
|
.B limit no_reset
|
||||||
|
- Specifies limitation on reload action.
|
||||||
|
If this argument is omitted limit is unspecificed and the reload action is not
|
||||||
|
limited. In such case driver implementation may include reset or downtime as
|
||||||
|
needed to perform the actions.
|
||||||
|
|
||||||
|
.I no_reset
|
||||||
|
- No reset allowed, no down time allowed, no link flap and no configuration is
|
||||||
|
lost.
|
||||||
|
|
||||||
.SS devlink dev info - display device information.
|
.SS devlink dev info - display device information.
|
||||||
Display device information provided by the driver. This command can be used
|
Display device information provided by the driver. This command can be used
|
||||||
to query versions of the hardware components or device components which
|
to query versions of the hardware components or device components which
|
||||||
|
|
|
||||||
|
|
@ -1352,6 +1352,7 @@ the following additional arguments are supported:
|
||||||
.BR type " { " macvlan " | " macvtap " } "
|
.BR type " { " macvlan " | " macvtap " } "
|
||||||
.BR mode " { " private " | " vepa " | " bridge " | " passthru
|
.BR mode " { " private " | " vepa " | " bridge " | " passthru
|
||||||
.RB " [ " nopromisc " ] | " source " } "
|
.RB " [ " nopromisc " ] | " source " } "
|
||||||
|
.RB " [ " bcqueuelen " { " LENGTH " } ] "
|
||||||
|
|
||||||
.in +8
|
.in +8
|
||||||
.sp
|
.sp
|
||||||
|
|
@ -1395,6 +1396,18 @@ against source mac address from received frames on underlying interface. This
|
||||||
allows creating mac based VLAN associations, instead of standard port or tag
|
allows creating mac based VLAN associations, instead of standard port or tag
|
||||||
based. The feature is useful to deploy 802.1x mac based behavior,
|
based. The feature is useful to deploy 802.1x mac based behavior,
|
||||||
where drivers of underlying interfaces doesn't allows that.
|
where drivers of underlying interfaces doesn't allows that.
|
||||||
|
|
||||||
|
.BR bcqueuelen " { " LENGTH " } "
|
||||||
|
- Set the length of the RX queue used to process broadcast and multicast packets.
|
||||||
|
.BR LENGTH " must be a positive integer in the range [0-4294967295]."
|
||||||
|
Setting a length of 0 will effectively drop all broadcast/multicast traffic.
|
||||||
|
If not specified the macvlan driver default (1000) is used.
|
||||||
|
Note that all macvlans that share the same underlying device are using the same
|
||||||
|
.RB "queue. The parameter here is a " request ", the actual queue length used"
|
||||||
|
will be the maximum length that any macvlan interface has requested.
|
||||||
|
When listing device parameters both the bcqueuelen parameter
|
||||||
|
as well as the actual used bcqueuelen are listed to better help
|
||||||
|
the user understand the setting.
|
||||||
.in -8
|
.in -8
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
|
|
@ -2451,6 +2464,26 @@ Commands:
|
||||||
.sp
|
.sp
|
||||||
.in -8
|
.in -8
|
||||||
|
|
||||||
|
Update the broadcast/multicast queue length.
|
||||||
|
|
||||||
|
.B "ip link set type { macvlan | macvap } "
|
||||||
|
[
|
||||||
|
.BI bcqueuelen " LENGTH "
|
||||||
|
]
|
||||||
|
|
||||||
|
.in +8
|
||||||
|
.BI bcqueuelen " LENGTH "
|
||||||
|
- Set the length of the RX queue used to process broadcast and multicast packets.
|
||||||
|
.IR LENGTH " must be a positive integer in the range [0-4294967295]."
|
||||||
|
Setting a length of 0 will effectively drop all broadcast/multicast traffic.
|
||||||
|
If not specified the macvlan driver default (1000) is used.
|
||||||
|
Note that all macvlans that share the same underlying device are using the same
|
||||||
|
.RB "queue. The parameter here is a " request ", the actual queue length used"
|
||||||
|
will be the maximum length that any macvlan interface has requested.
|
||||||
|
When listing device parameters both the bcqueuelen parameter
|
||||||
|
as well as the actual used bcqueuelen are listed to better help
|
||||||
|
the user understand the setting.
|
||||||
|
.in -8
|
||||||
|
|
||||||
.SS ip link show - display device attributes
|
.SS ip link show - display device attributes
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -854,6 +854,12 @@ option for creating
|
||||||
alias.
|
alias.
|
||||||
.RE
|
.RE
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR "\-br" , " \-brief"
|
||||||
|
Print only essential data needed to identify the filter and action (handle,
|
||||||
|
cookie, etc.) and stats. This option is currently only supported by
|
||||||
|
.BR "tc filter show " and " tc actions ls " commands.
|
||||||
|
|
||||||
.SH "EXAMPLES"
|
.SH "EXAMPLES"
|
||||||
.PP
|
.PP
|
||||||
tc -g class show dev eth0
|
tc -g class show dev eth0
|
||||||
|
|
|
||||||
|
|
@ -159,7 +159,7 @@ static void dev_print_dim_setting(struct rd *rd, struct nlattr **tb)
|
||||||
if (dim_setting > 1)
|
if (dim_setting > 1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
print_on_off(rd, "adaptive-moderation", dim_setting);
|
print_on_off(PRINT_ANY, "adaptive-moderation", "adaptive-moderation %s ", dim_setting);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
40
rdma/rdma.c
40
rdma/rdma.c
|
|
@ -41,40 +41,16 @@ static int rd_cmd(struct rd *rd, int argc, char **argv)
|
||||||
return rd_exec_cmd(rd, cmds, "object");
|
return rd_exec_cmd(rd, cmds, "object");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int rd_batch_cmd(int argc, char *argv[], void *data)
|
||||||
|
{
|
||||||
|
struct rd *rd = data;
|
||||||
|
|
||||||
|
return rd_cmd(rd, argc, argv);
|
||||||
|
}
|
||||||
|
|
||||||
static int rd_batch(struct rd *rd, const char *name, bool force)
|
static int rd_batch(struct rd *rd, const char *name, bool force)
|
||||||
{
|
{
|
||||||
char *line = NULL;
|
return do_batch(name, force, rd_batch_cmd, rd);
|
||||||
size_t len = 0;
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
if (name && strcmp(name, "-") != 0) {
|
|
||||||
if (!freopen(name, "r", stdin)) {
|
|
||||||
pr_err("Cannot open file \"%s\" for reading: %s\n",
|
|
||||||
name, strerror(errno));
|
|
||||||
return errno;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cmdlineno = 0;
|
|
||||||
while (getcmdline(&line, &len, stdin) != -1) {
|
|
||||||
char *largv[512];
|
|
||||||
int largc;
|
|
||||||
|
|
||||||
largc = makeargs(line, largv, ARRAY_SIZE(largv));
|
|
||||||
if (!largc)
|
|
||||||
continue; /* blank line */
|
|
||||||
|
|
||||||
ret = rd_cmd(rd, largc, largv);
|
|
||||||
if (ret) {
|
|
||||||
pr_err("Command failed %s:%d\n", name, cmdlineno);
|
|
||||||
if (!force)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
free(line);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rd_init(struct rd *rd, char *filename)
|
static int rd_init(struct rd *rd, char *filename)
|
||||||
|
|
|
||||||
|
|
@ -138,7 +138,6 @@ void print_driver_table(struct rd *rd, struct nlattr *tb);
|
||||||
void print_raw_data(struct rd *rd, struct nlattr **nla_line);
|
void print_raw_data(struct rd *rd, struct nlattr **nla_line);
|
||||||
void newline(struct rd *rd);
|
void newline(struct rd *rd);
|
||||||
void newline_indent(struct rd *rd);
|
void newline_indent(struct rd *rd);
|
||||||
void print_on_off(struct rd *rd, const char *key_str, bool on);
|
|
||||||
void print_raw_data(struct rd *rd, struct nlattr **nla_line);
|
void print_raw_data(struct rd *rd, struct nlattr **nla_line);
|
||||||
#define MAX_LINE_LENGTH 80
|
#define MAX_LINE_LENGTH 80
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ static void print_cq_dim_setting(struct rd *rd, struct nlattr *attr)
|
||||||
if (dim_setting > 1)
|
if (dim_setting > 1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
print_on_off(rd, "adaptive-moderation", dim_setting);
|
print_on_off(PRINT_ANY, "adaptive-moderation", "adaptive-moderation %s ", dim_setting);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int res_cq_line_raw(struct rd *rd, const char *name, int idx,
|
static int res_cq_line_raw(struct rd *rd, const char *name, int idx,
|
||||||
|
|
|
||||||
|
|
@ -781,11 +781,6 @@ static int print_driver_string(struct rd *rd, const char *key_str,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_on_off(struct rd *rd, const char *key_str, bool on)
|
|
||||||
{
|
|
||||||
print_driver_string(rd, key_str, (on) ? "on":"off");
|
|
||||||
}
|
|
||||||
|
|
||||||
static int print_driver_s32(struct rd *rd, const char *key_str, int32_t val,
|
static int print_driver_s32(struct rd *rd, const char *key_str, int32_t val,
|
||||||
enum rdma_nldev_print_type print_type)
|
enum rdma_nldev_print_type print_type)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1324,9 +1324,9 @@ static int flower_parse_opt(struct filter_util *qu, char *handle,
|
||||||
bool mpls_format_old = false;
|
bool mpls_format_old = false;
|
||||||
bool mpls_format_new = false;
|
bool mpls_format_new = false;
|
||||||
struct rtattr *tail;
|
struct rtattr *tail;
|
||||||
__be16 eth_type = TC_H_MIN(t->tcm_info);
|
__be16 tc_proto = TC_H_MIN(t->tcm_info);
|
||||||
|
__be16 eth_type = tc_proto;
|
||||||
__be16 vlan_ethtype = 0;
|
__be16 vlan_ethtype = 0;
|
||||||
__be16 cvlan_ethtype = 0;
|
|
||||||
__u8 ip_proto = 0xff;
|
__u8 ip_proto = 0xff;
|
||||||
__u32 flags = 0;
|
__u32 flags = 0;
|
||||||
__u32 mtf = 0;
|
__u32 mtf = 0;
|
||||||
|
|
@ -1432,7 +1432,7 @@ static int flower_parse_opt(struct filter_util *qu, char *handle,
|
||||||
__u16 vid;
|
__u16 vid;
|
||||||
|
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
if (!eth_type_vlan(eth_type)) {
|
if (!eth_type_vlan(tc_proto)) {
|
||||||
fprintf(stderr, "Can't set \"vlan_id\" if ethertype isn't 802.1Q or 802.1AD\n");
|
fprintf(stderr, "Can't set \"vlan_id\" if ethertype isn't 802.1Q or 802.1AD\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -1446,7 +1446,7 @@ static int flower_parse_opt(struct filter_util *qu, char *handle,
|
||||||
__u8 vlan_prio;
|
__u8 vlan_prio;
|
||||||
|
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
if (!eth_type_vlan(eth_type)) {
|
if (!eth_type_vlan(tc_proto)) {
|
||||||
fprintf(stderr, "Can't set \"vlan_prio\" if ethertype isn't 802.1Q or 802.1AD\n");
|
fprintf(stderr, "Can't set \"vlan_prio\" if ethertype isn't 802.1Q or 802.1AD\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -1464,6 +1464,8 @@ static int flower_parse_opt(struct filter_util *qu, char *handle,
|
||||||
&vlan_ethtype, n);
|
&vlan_ethtype, n);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
/* get new ethtype for later parsing */
|
||||||
|
eth_type = vlan_ethtype;
|
||||||
} else if (matches(*argv, "cvlan_id") == 0) {
|
} else if (matches(*argv, "cvlan_id") == 0) {
|
||||||
__u16 vid;
|
__u16 vid;
|
||||||
|
|
||||||
|
|
@ -1495,9 +1497,10 @@ static int flower_parse_opt(struct filter_util *qu, char *handle,
|
||||||
TCA_FLOWER_KEY_CVLAN_PRIO, cvlan_prio);
|
TCA_FLOWER_KEY_CVLAN_PRIO, cvlan_prio);
|
||||||
} else if (matches(*argv, "cvlan_ethtype") == 0) {
|
} else if (matches(*argv, "cvlan_ethtype") == 0) {
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
|
/* get new ethtype for later parsing */
|
||||||
ret = flower_parse_vlan_eth_type(*argv, vlan_ethtype,
|
ret = flower_parse_vlan_eth_type(*argv, vlan_ethtype,
|
||||||
TCA_FLOWER_KEY_CVLAN_ETH_TYPE,
|
TCA_FLOWER_KEY_CVLAN_ETH_TYPE,
|
||||||
&cvlan_ethtype, n);
|
ð_type, n);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return -1;
|
return -1;
|
||||||
} else if (matches(*argv, "mpls") == 0) {
|
} else if (matches(*argv, "mpls") == 0) {
|
||||||
|
|
@ -1627,9 +1630,7 @@ static int flower_parse_opt(struct filter_util *qu, char *handle,
|
||||||
}
|
}
|
||||||
} else if (matches(*argv, "ip_proto") == 0) {
|
} else if (matches(*argv, "ip_proto") == 0) {
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
ret = flower_parse_ip_proto(*argv, cvlan_ethtype ?
|
ret = flower_parse_ip_proto(*argv, eth_type,
|
||||||
cvlan_ethtype : vlan_ethtype ?
|
|
||||||
vlan_ethtype : eth_type,
|
|
||||||
TCA_FLOWER_KEY_IP_PROTO,
|
TCA_FLOWER_KEY_IP_PROTO,
|
||||||
&ip_proto, n);
|
&ip_proto, n);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
|
@ -1658,9 +1659,7 @@ static int flower_parse_opt(struct filter_util *qu, char *handle,
|
||||||
}
|
}
|
||||||
} else if (matches(*argv, "dst_ip") == 0) {
|
} else if (matches(*argv, "dst_ip") == 0) {
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
ret = flower_parse_ip_addr(*argv, cvlan_ethtype ?
|
ret = flower_parse_ip_addr(*argv, eth_type,
|
||||||
cvlan_ethtype : vlan_ethtype ?
|
|
||||||
vlan_ethtype : eth_type,
|
|
||||||
TCA_FLOWER_KEY_IPV4_DST,
|
TCA_FLOWER_KEY_IPV4_DST,
|
||||||
TCA_FLOWER_KEY_IPV4_DST_MASK,
|
TCA_FLOWER_KEY_IPV4_DST_MASK,
|
||||||
TCA_FLOWER_KEY_IPV6_DST,
|
TCA_FLOWER_KEY_IPV6_DST,
|
||||||
|
|
@ -1672,9 +1671,7 @@ static int flower_parse_opt(struct filter_util *qu, char *handle,
|
||||||
}
|
}
|
||||||
} else if (matches(*argv, "src_ip") == 0) {
|
} else if (matches(*argv, "src_ip") == 0) {
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
ret = flower_parse_ip_addr(*argv, cvlan_ethtype ?
|
ret = flower_parse_ip_addr(*argv, eth_type,
|
||||||
cvlan_ethtype : vlan_ethtype ?
|
|
||||||
vlan_ethtype : eth_type,
|
|
||||||
TCA_FLOWER_KEY_IPV4_SRC,
|
TCA_FLOWER_KEY_IPV4_SRC,
|
||||||
TCA_FLOWER_KEY_IPV4_SRC_MASK,
|
TCA_FLOWER_KEY_IPV4_SRC_MASK,
|
||||||
TCA_FLOWER_KEY_IPV6_SRC,
|
TCA_FLOWER_KEY_IPV6_SRC,
|
||||||
|
|
@ -1728,33 +1725,30 @@ static int flower_parse_opt(struct filter_util *qu, char *handle,
|
||||||
}
|
}
|
||||||
} else if (matches(*argv, "arp_tip") == 0) {
|
} else if (matches(*argv, "arp_tip") == 0) {
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
ret = flower_parse_arp_ip_addr(*argv, vlan_ethtype ?
|
ret = flower_parse_arp_ip_addr(*argv, eth_type,
|
||||||
vlan_ethtype : eth_type,
|
TCA_FLOWER_KEY_ARP_TIP,
|
||||||
TCA_FLOWER_KEY_ARP_TIP,
|
TCA_FLOWER_KEY_ARP_TIP_MASK,
|
||||||
TCA_FLOWER_KEY_ARP_TIP_MASK,
|
n);
|
||||||
n);
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
fprintf(stderr, "Illegal \"arp_tip\"\n");
|
fprintf(stderr, "Illegal \"arp_tip\"\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
} else if (matches(*argv, "arp_sip") == 0) {
|
} else if (matches(*argv, "arp_sip") == 0) {
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
ret = flower_parse_arp_ip_addr(*argv, vlan_ethtype ?
|
ret = flower_parse_arp_ip_addr(*argv, eth_type,
|
||||||
vlan_ethtype : eth_type,
|
TCA_FLOWER_KEY_ARP_SIP,
|
||||||
TCA_FLOWER_KEY_ARP_SIP,
|
TCA_FLOWER_KEY_ARP_SIP_MASK,
|
||||||
TCA_FLOWER_KEY_ARP_SIP_MASK,
|
n);
|
||||||
n);
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
fprintf(stderr, "Illegal \"arp_sip\"\n");
|
fprintf(stderr, "Illegal \"arp_sip\"\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
} else if (matches(*argv, "arp_op") == 0) {
|
} else if (matches(*argv, "arp_op") == 0) {
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
ret = flower_parse_arp_op(*argv, vlan_ethtype ?
|
ret = flower_parse_arp_op(*argv, eth_type,
|
||||||
vlan_ethtype : eth_type,
|
TCA_FLOWER_KEY_ARP_OP,
|
||||||
TCA_FLOWER_KEY_ARP_OP,
|
TCA_FLOWER_KEY_ARP_OP_MASK,
|
||||||
TCA_FLOWER_KEY_ARP_OP_MASK,
|
n);
|
||||||
n);
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
fprintf(stderr, "Illegal \"arp_op\"\n");
|
fprintf(stderr, "Illegal \"arp_op\"\n");
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -1894,8 +1888,8 @@ parse_done:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (eth_type != htons(ETH_P_ALL)) {
|
if (tc_proto != htons(ETH_P_ALL)) {
|
||||||
ret = addattr16(n, MAX_MSG, TCA_FLOWER_KEY_ETH_TYPE, eth_type);
|
ret = addattr16(n, MAX_MSG, TCA_FLOWER_KEY_ETH_TYPE, tc_proto);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -374,6 +374,11 @@ static int tc_print_one_action(FILE *f, struct rtattr *arg)
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
if (brief && tb[TCA_ACT_INDEX]) {
|
||||||
|
print_uint(PRINT_ANY, "index", "\t index %u",
|
||||||
|
rta_getattr_u32(tb[TCA_ACT_INDEX]));
|
||||||
|
print_nl();
|
||||||
|
}
|
||||||
if (show_stats && tb[TCA_ACT_STATS]) {
|
if (show_stats && tb[TCA_ACT_STATS]) {
|
||||||
print_string(PRINT_FP, NULL, "\tAction statistics:", NULL);
|
print_string(PRINT_FP, NULL, "\tAction statistics:", NULL);
|
||||||
print_nl();
|
print_nl();
|
||||||
|
|
@ -735,8 +740,12 @@ static int tc_act_list_or_flush(int *argc_p, char ***argv_p, int event)
|
||||||
addattr_nest_end(&req.n, tail);
|
addattr_nest_end(&req.n, tail);
|
||||||
|
|
||||||
tail3 = NLMSG_TAIL(&req.n);
|
tail3 = NLMSG_TAIL(&req.n);
|
||||||
flag_select.value |= TCA_FLAG_LARGE_DUMP_ON;
|
flag_select.value |= TCA_ACT_FLAG_LARGE_DUMP_ON;
|
||||||
flag_select.selector |= TCA_FLAG_LARGE_DUMP_ON;
|
flag_select.selector |= TCA_ACT_FLAG_LARGE_DUMP_ON;
|
||||||
|
if (brief) {
|
||||||
|
flag_select.value |= TCA_ACT_FLAG_TERSE_DUMP;
|
||||||
|
flag_select.selector |= TCA_ACT_FLAG_TERSE_DUMP;
|
||||||
|
}
|
||||||
addattr_l(&req.n, MAX_MSG, TCA_ROOT_FLAGS, &flag_select,
|
addattr_l(&req.n, MAX_MSG, TCA_ROOT_FLAGS, &flag_select,
|
||||||
sizeof(struct nla_bitfield32));
|
sizeof(struct nla_bitfield32));
|
||||||
tail3->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail3;
|
tail3->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail3;
|
||||||
|
|
|
||||||
|
|
@ -161,8 +161,9 @@ static int bpf_print_opt(struct action_util *au, FILE *f, struct rtattr *arg)
|
||||||
struct tc_act_bpf *parm;
|
struct tc_act_bpf *parm;
|
||||||
int d_ok = 0;
|
int d_ok = 0;
|
||||||
|
|
||||||
|
print_string(PRINT_ANY, "kind", "%s ", "bpf");
|
||||||
if (arg == NULL)
|
if (arg == NULL)
|
||||||
return -1;
|
return 0;
|
||||||
|
|
||||||
parse_rtattr_nested(tb, TCA_ACT_BPF_MAX, arg);
|
parse_rtattr_nested(tb, TCA_ACT_BPF_MAX, arg);
|
||||||
|
|
||||||
|
|
@ -172,7 +173,6 @@ static int bpf_print_opt(struct action_util *au, FILE *f, struct rtattr *arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
parm = RTA_DATA(tb[TCA_ACT_BPF_PARMS]);
|
parm = RTA_DATA(tb[TCA_ACT_BPF_PARMS]);
|
||||||
print_string(PRINT_ANY, "kind", "%s ", "bpf");
|
|
||||||
|
|
||||||
if (tb[TCA_ACT_BPF_NAME])
|
if (tb[TCA_ACT_BPF_NAME])
|
||||||
print_string(PRINT_ANY, "bpf_name", "%s ",
|
print_string(PRINT_ANY, "bpf_name", "%s ",
|
||||||
|
|
|
||||||
|
|
@ -110,8 +110,9 @@ static int print_connmark(struct action_util *au, FILE *f, struct rtattr *arg)
|
||||||
struct rtattr *tb[TCA_CONNMARK_MAX + 1];
|
struct rtattr *tb[TCA_CONNMARK_MAX + 1];
|
||||||
struct tc_connmark *ci;
|
struct tc_connmark *ci;
|
||||||
|
|
||||||
|
print_string(PRINT_ANY, "kind", "%s ", "connmark");
|
||||||
if (arg == NULL)
|
if (arg == NULL)
|
||||||
return -1;
|
return 0;
|
||||||
|
|
||||||
parse_rtattr_nested(tb, TCA_CONNMARK_MAX, arg);
|
parse_rtattr_nested(tb, TCA_CONNMARK_MAX, arg);
|
||||||
if (tb[TCA_CONNMARK_PARMS] == NULL) {
|
if (tb[TCA_CONNMARK_PARMS] == NULL) {
|
||||||
|
|
@ -121,7 +122,6 @@ static int print_connmark(struct action_util *au, FILE *f, struct rtattr *arg)
|
||||||
|
|
||||||
ci = RTA_DATA(tb[TCA_CONNMARK_PARMS]);
|
ci = RTA_DATA(tb[TCA_CONNMARK_PARMS]);
|
||||||
|
|
||||||
print_string(PRINT_ANY, "kind", "%s ", "connmark");
|
|
||||||
print_uint(PRINT_ANY, "zone", "zone %u", ci->zone);
|
print_uint(PRINT_ANY, "zone", "zone %u", ci->zone);
|
||||||
print_action_control(f, " ", ci->action, "");
|
print_action_control(f, " ", ci->action, "");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -166,8 +166,9 @@ print_csum(struct action_util *au, FILE *f, struct rtattr *arg)
|
||||||
|
|
||||||
int uflag_count = 0;
|
int uflag_count = 0;
|
||||||
|
|
||||||
|
print_string(PRINT_ANY, "kind", "%s ", "csum");
|
||||||
if (arg == NULL)
|
if (arg == NULL)
|
||||||
return -1;
|
return 0;
|
||||||
|
|
||||||
parse_rtattr_nested(tb, TCA_CSUM_MAX, arg);
|
parse_rtattr_nested(tb, TCA_CSUM_MAX, arg);
|
||||||
|
|
||||||
|
|
@ -199,7 +200,6 @@ print_csum(struct action_util *au, FILE *f, struct rtattr *arg)
|
||||||
uflag_1 = "?empty";
|
uflag_1 = "?empty";
|
||||||
}
|
}
|
||||||
|
|
||||||
print_string(PRINT_ANY, "kind", "%s ", "csum");
|
|
||||||
snprintf(buf, sizeof(buf), "%s%s%s%s%s%s%s",
|
snprintf(buf, sizeof(buf), "%s%s%s%s%s%s%s",
|
||||||
uflag_1, uflag_2, uflag_3,
|
uflag_1, uflag_2, uflag_3,
|
||||||
uflag_4, uflag_5, uflag_6, uflag_7);
|
uflag_4, uflag_5, uflag_6, uflag_7);
|
||||||
|
|
|
||||||
|
|
@ -443,8 +443,9 @@ static int print_ct(struct action_util *au, FILE *f, struct rtattr *arg)
|
||||||
struct tc_ct *p;
|
struct tc_ct *p;
|
||||||
int ct_action = 0;
|
int ct_action = 0;
|
||||||
|
|
||||||
|
print_string(PRINT_ANY, "kind", "%s", "ct");
|
||||||
if (arg == NULL)
|
if (arg == NULL)
|
||||||
return -1;
|
return 0;
|
||||||
|
|
||||||
parse_rtattr_nested(tb, TCA_CT_MAX, arg);
|
parse_rtattr_nested(tb, TCA_CT_MAX, arg);
|
||||||
if (tb[TCA_CT_PARMS] == NULL) {
|
if (tb[TCA_CT_PARMS] == NULL) {
|
||||||
|
|
@ -454,8 +455,6 @@ static int print_ct(struct action_util *au, FILE *f, struct rtattr *arg)
|
||||||
|
|
||||||
p = RTA_DATA(tb[TCA_CT_PARMS]);
|
p = RTA_DATA(tb[TCA_CT_PARMS]);
|
||||||
|
|
||||||
print_string(PRINT_ANY, "kind", "%s", "ct");
|
|
||||||
|
|
||||||
if (tb[TCA_CT_ACTION])
|
if (tb[TCA_CT_ACTION])
|
||||||
ct_action = rta_getattr_u16(tb[TCA_CT_ACTION]);
|
ct_action = rta_getattr_u16(tb[TCA_CT_ACTION]);
|
||||||
if (ct_action & TCA_CT_ACT_COMMIT) {
|
if (ct_action & TCA_CT_ACT_COMMIT) {
|
||||||
|
|
|
||||||
|
|
@ -188,8 +188,9 @@ static int print_ctinfo(struct action_util *au, FILE *f, struct rtattr *arg)
|
||||||
unsigned short zone = 0;
|
unsigned short zone = 0;
|
||||||
struct tc_ctinfo *ci;
|
struct tc_ctinfo *ci;
|
||||||
|
|
||||||
|
print_string(PRINT_ANY, "kind", "%s ", "ctinfo");
|
||||||
if (arg == NULL)
|
if (arg == NULL)
|
||||||
return -1;
|
return 0;
|
||||||
|
|
||||||
parse_rtattr_nested(tb, TCA_CTINFO_MAX, arg);
|
parse_rtattr_nested(tb, TCA_CTINFO_MAX, arg);
|
||||||
if (!tb[TCA_CTINFO_ACT]) {
|
if (!tb[TCA_CTINFO_ACT]) {
|
||||||
|
|
@ -234,7 +235,6 @@ static int print_ctinfo(struct action_util *au, FILE *f, struct rtattr *arg)
|
||||||
sizeof(__u16))
|
sizeof(__u16))
|
||||||
zone = rta_getattr_u16(tb[TCA_CTINFO_ZONE]);
|
zone = rta_getattr_u16(tb[TCA_CTINFO_ZONE]);
|
||||||
|
|
||||||
print_string(PRINT_ANY, "kind", "%s ", "ctinfo");
|
|
||||||
print_hu(PRINT_ANY, "zone", "zone %u", zone);
|
print_hu(PRINT_ANY, "zone", "zone %u", zone);
|
||||||
print_action_control(f, " ", ci->action, "");
|
print_action_control(f, " ", ci->action, "");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -171,8 +171,9 @@ print_gact(struct action_util *au, FILE *f, struct rtattr *arg)
|
||||||
struct tc_gact *p = NULL;
|
struct tc_gact *p = NULL;
|
||||||
struct rtattr *tb[TCA_GACT_MAX + 1];
|
struct rtattr *tb[TCA_GACT_MAX + 1];
|
||||||
|
|
||||||
|
print_string(PRINT_ANY, "kind", "%s ", "gact");
|
||||||
if (arg == NULL)
|
if (arg == NULL)
|
||||||
return -1;
|
return 0;
|
||||||
|
|
||||||
parse_rtattr_nested(tb, TCA_GACT_MAX, arg);
|
parse_rtattr_nested(tb, TCA_GACT_MAX, arg);
|
||||||
|
|
||||||
|
|
@ -182,7 +183,6 @@ print_gact(struct action_util *au, FILE *f, struct rtattr *arg)
|
||||||
}
|
}
|
||||||
p = RTA_DATA(tb[TCA_GACT_PARMS]);
|
p = RTA_DATA(tb[TCA_GACT_PARMS]);
|
||||||
|
|
||||||
print_string(PRINT_ANY, "kind", "%s ", "gact");
|
|
||||||
print_action_control(f, "action ", p->action, "");
|
print_action_control(f, "action ", p->action, "");
|
||||||
#ifdef CONFIG_GACT_PROB
|
#ifdef CONFIG_GACT_PROB
|
||||||
if (tb[TCA_GACT_PROB] != NULL) {
|
if (tb[TCA_GACT_PROB] != NULL) {
|
||||||
|
|
|
||||||
|
|
@ -465,10 +465,8 @@ static int print_gate_list(struct rtattr *list)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (maxoctets != -1) {
|
if (maxoctets != -1) {
|
||||||
memset(buf, 0, sizeof(buf));
|
print_size(PRINT_ANY, "max_octets", "\t max-octets %s",
|
||||||
print_uint(PRINT_JSON, "max_octets", NULL, maxoctets);
|
maxoctets);
|
||||||
print_string(PRINT_FP, NULL, "\t max-octets %s",
|
|
||||||
sprint_size(maxoctets, buf));
|
|
||||||
} else {
|
} else {
|
||||||
print_string(PRINT_FP, NULL,
|
print_string(PRINT_FP, NULL,
|
||||||
"\t max-octets %s", "wildcard");
|
"\t max-octets %s", "wildcard");
|
||||||
|
|
|
||||||
|
|
@ -227,8 +227,9 @@ static int print_ife(struct action_util *au, FILE *f, struct rtattr *arg)
|
||||||
int has_optional = 0;
|
int has_optional = 0;
|
||||||
SPRINT_BUF(b2);
|
SPRINT_BUF(b2);
|
||||||
|
|
||||||
|
print_string(PRINT_ANY, "kind", "%s ", "ife");
|
||||||
if (arg == NULL)
|
if (arg == NULL)
|
||||||
return -1;
|
return 0;
|
||||||
|
|
||||||
parse_rtattr_nested(tb, TCA_IFE_MAX, arg);
|
parse_rtattr_nested(tb, TCA_IFE_MAX, arg);
|
||||||
|
|
||||||
|
|
@ -238,7 +239,6 @@ static int print_ife(struct action_util *au, FILE *f, struct rtattr *arg)
|
||||||
}
|
}
|
||||||
p = RTA_DATA(tb[TCA_IFE_PARMS]);
|
p = RTA_DATA(tb[TCA_IFE_PARMS]);
|
||||||
|
|
||||||
print_string(PRINT_ANY, "kind", "%s ", "ife");
|
|
||||||
print_string(PRINT_ANY, "mode", "%s ",
|
print_string(PRINT_ANY, "mode", "%s ",
|
||||||
p->flags & IFE_ENCODE ? "encode" : "decode");
|
p->flags & IFE_ENCODE ? "encode" : "decode");
|
||||||
print_action_control(f, "action ", p->action, " ");
|
print_action_control(f, "action ", p->action, " ");
|
||||||
|
|
|
||||||
|
|
@ -433,7 +433,7 @@ print_ipt(struct action_util *au, FILE * f, struct rtattr *arg)
|
||||||
__u32 hook;
|
__u32 hook;
|
||||||
|
|
||||||
if (arg == NULL)
|
if (arg == NULL)
|
||||||
return -1;
|
return 0;
|
||||||
|
|
||||||
lib_dir = getenv("IPTABLES_LIB_DIR");
|
lib_dir = getenv("IPTABLES_LIB_DIR");
|
||||||
if (!lib_dir)
|
if (!lib_dir)
|
||||||
|
|
|
||||||
|
|
@ -281,8 +281,9 @@ print_mirred(struct action_util *au, FILE *f, struct rtattr *arg)
|
||||||
struct rtattr *tb[TCA_MIRRED_MAX + 1];
|
struct rtattr *tb[TCA_MIRRED_MAX + 1];
|
||||||
const char *dev;
|
const char *dev;
|
||||||
|
|
||||||
|
print_string(PRINT_ANY, "kind", "%s ", "mirred");
|
||||||
if (arg == NULL)
|
if (arg == NULL)
|
||||||
return -1;
|
return 0;
|
||||||
|
|
||||||
parse_rtattr_nested(tb, TCA_MIRRED_MAX, arg);
|
parse_rtattr_nested(tb, TCA_MIRRED_MAX, arg);
|
||||||
|
|
||||||
|
|
@ -298,7 +299,6 @@ print_mirred(struct action_util *au, FILE *f, struct rtattr *arg)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
print_string(PRINT_ANY, "kind", "%s ", "mirred");
|
|
||||||
print_string(PRINT_FP, NULL, "(%s", mirred_n2a(p->eaction));
|
print_string(PRINT_FP, NULL, "(%s", mirred_n2a(p->eaction));
|
||||||
print_string(PRINT_JSON, "mirred_action", NULL,
|
print_string(PRINT_JSON, "mirred_action", NULL,
|
||||||
mirred_action(p->eaction));
|
mirred_action(p->eaction));
|
||||||
|
|
|
||||||
|
|
@ -214,8 +214,9 @@ static int print_mpls(struct action_util *au, FILE *f, struct rtattr *arg)
|
||||||
SPRINT_BUF(b1);
|
SPRINT_BUF(b1);
|
||||||
__u32 val;
|
__u32 val;
|
||||||
|
|
||||||
|
print_string(PRINT_ANY, "kind", "%s ", "mpls");
|
||||||
if (!arg)
|
if (!arg)
|
||||||
return -1;
|
return 0;
|
||||||
|
|
||||||
parse_rtattr_nested(tb, TCA_MPLS_MAX, arg);
|
parse_rtattr_nested(tb, TCA_MPLS_MAX, arg);
|
||||||
|
|
||||||
|
|
@ -225,7 +226,6 @@ static int print_mpls(struct action_util *au, FILE *f, struct rtattr *arg)
|
||||||
}
|
}
|
||||||
parm = RTA_DATA(tb[TCA_MPLS_PARMS]);
|
parm = RTA_DATA(tb[TCA_MPLS_PARMS]);
|
||||||
|
|
||||||
print_string(PRINT_ANY, "kind", "%s ", "mpls");
|
|
||||||
print_string(PRINT_ANY, "mpls_action", " %s",
|
print_string(PRINT_ANY, "mpls_action", " %s",
|
||||||
action_names[parm->m_action]);
|
action_names[parm->m_action]);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -146,8 +146,9 @@ print_nat(struct action_util *au, FILE * f, struct rtattr *arg)
|
||||||
SPRINT_BUF(buf2);
|
SPRINT_BUF(buf2);
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
|
print_string(PRINT_ANY, "type", " %s ", "nat");
|
||||||
if (arg == NULL)
|
if (arg == NULL)
|
||||||
return -1;
|
return 0;
|
||||||
|
|
||||||
parse_rtattr_nested(tb, TCA_NAT_MAX, arg);
|
parse_rtattr_nested(tb, TCA_NAT_MAX, arg);
|
||||||
|
|
||||||
|
|
@ -160,7 +161,6 @@ print_nat(struct action_util *au, FILE * f, struct rtattr *arg)
|
||||||
len = ffs(sel->mask);
|
len = ffs(sel->mask);
|
||||||
len = len ? 33 - len : 0;
|
len = len ? 33 - len : 0;
|
||||||
|
|
||||||
print_string(PRINT_ANY, "type", " %s ", "nat");
|
|
||||||
print_string(PRINT_ANY, "direction", "%s",
|
print_string(PRINT_ANY, "direction", "%s",
|
||||||
sel->flags & TCA_NAT_FLAG_EGRESS ? "egress" : "ingress");
|
sel->flags & TCA_NAT_FLAG_EGRESS ? "egress" : "ingress");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -745,8 +745,9 @@ static int print_pedit(struct action_util *au, FILE *f, struct rtattr *arg)
|
||||||
struct m_pedit_key_ex *keys_ex = NULL;
|
struct m_pedit_key_ex *keys_ex = NULL;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
print_string(PRINT_ANY, "kind", " %s ", "pedit");
|
||||||
if (arg == NULL)
|
if (arg == NULL)
|
||||||
return -1;
|
return 0;
|
||||||
|
|
||||||
parse_rtattr_nested(tb, TCA_PEDIT_MAX, arg);
|
parse_rtattr_nested(tb, TCA_PEDIT_MAX, arg);
|
||||||
|
|
||||||
|
|
@ -783,7 +784,6 @@ static int print_pedit(struct action_util *au, FILE *f, struct rtattr *arg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
print_string(PRINT_ANY, "kind", " %s ", "pedit");
|
|
||||||
print_action_control(f, "action ", sel->action, " ");
|
print_action_control(f, "action ", sel->action, " ");
|
||||||
print_uint(PRINT_ANY, "nkeys", "keys %d\n", sel->nkeys);
|
print_uint(PRINT_ANY, "nkeys", "keys %d\n", sel->nkeys);
|
||||||
print_uint(PRINT_ANY, "index", " \t index %u", sel->index);
|
print_uint(PRINT_ANY, "index", " \t index %u", sel->index);
|
||||||
|
|
|
||||||
|
|
@ -238,7 +238,6 @@ int parse_police(int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n)
|
||||||
|
|
||||||
static int print_police(struct action_util *a, FILE *f, struct rtattr *arg)
|
static int print_police(struct action_util *a, FILE *f, struct rtattr *arg)
|
||||||
{
|
{
|
||||||
SPRINT_BUF(b1);
|
|
||||||
SPRINT_BUF(b2);
|
SPRINT_BUF(b2);
|
||||||
struct tc_police *p;
|
struct tc_police *p;
|
||||||
struct rtattr *tb[TCA_POLICE_MAX+1];
|
struct rtattr *tb[TCA_POLICE_MAX+1];
|
||||||
|
|
@ -269,10 +268,10 @@ static int print_police(struct action_util *a, FILE *f, struct rtattr *arg)
|
||||||
rate64 = rta_getattr_u64(tb[TCA_POLICE_RATE64]);
|
rate64 = rta_getattr_u64(tb[TCA_POLICE_RATE64]);
|
||||||
|
|
||||||
fprintf(f, " police 0x%x ", p->index);
|
fprintf(f, " police 0x%x ", p->index);
|
||||||
fprintf(f, "rate %s ", sprint_rate(rate64, b1));
|
tc_print_rate(PRINT_FP, NULL, "rate %s ", rate64);
|
||||||
buffer = tc_calc_xmitsize(rate64, p->burst);
|
buffer = tc_calc_xmitsize(rate64, p->burst);
|
||||||
fprintf(f, "burst %s ", sprint_size(buffer, b1));
|
print_size(PRINT_FP, NULL, "burst %s ", buffer);
|
||||||
fprintf(f, "mtu %s ", sprint_size(p->mtu, b1));
|
print_size(PRINT_FP, NULL, "mtu %s ", p->mtu);
|
||||||
if (show_raw)
|
if (show_raw)
|
||||||
fprintf(f, "[%08x] ", p->burst);
|
fprintf(f, "[%08x] ", p->burst);
|
||||||
|
|
||||||
|
|
@ -282,12 +281,11 @@ static int print_police(struct action_util *a, FILE *f, struct rtattr *arg)
|
||||||
prate64 = rta_getattr_u64(tb[TCA_POLICE_PEAKRATE64]);
|
prate64 = rta_getattr_u64(tb[TCA_POLICE_PEAKRATE64]);
|
||||||
|
|
||||||
if (prate64)
|
if (prate64)
|
||||||
fprintf(f, "peakrate %s ", sprint_rate(prate64, b1));
|
tc_print_rate(PRINT_FP, NULL, "peakrate %s ", prate64);
|
||||||
|
|
||||||
if (tb[TCA_POLICE_AVRATE])
|
if (tb[TCA_POLICE_AVRATE])
|
||||||
fprintf(f, "avrate %s ",
|
tc_print_rate(PRINT_FP, NULL, "avrate %s ",
|
||||||
sprint_rate(rta_getattr_u32(tb[TCA_POLICE_AVRATE]),
|
rta_getattr_u32(tb[TCA_POLICE_AVRATE]));
|
||||||
b1));
|
|
||||||
|
|
||||||
print_action_control(f, "action ", p->action, "");
|
print_action_control(f, "action ", p->action, "");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -143,8 +143,9 @@ static int print_sample(struct action_util *au, FILE *f, struct rtattr *arg)
|
||||||
struct rtattr *tb[TCA_SAMPLE_MAX + 1];
|
struct rtattr *tb[TCA_SAMPLE_MAX + 1];
|
||||||
struct tc_sample *p;
|
struct tc_sample *p;
|
||||||
|
|
||||||
|
print_string(PRINT_ANY, "kind", "%s ", "sample");
|
||||||
if (arg == NULL)
|
if (arg == NULL)
|
||||||
return -1;
|
return 0;
|
||||||
|
|
||||||
parse_rtattr_nested(tb, TCA_SAMPLE_MAX, arg);
|
parse_rtattr_nested(tb, TCA_SAMPLE_MAX, arg);
|
||||||
|
|
||||||
|
|
@ -155,7 +156,6 @@ static int print_sample(struct action_util *au, FILE *f, struct rtattr *arg)
|
||||||
}
|
}
|
||||||
p = RTA_DATA(tb[TCA_SAMPLE_PARMS]);
|
p = RTA_DATA(tb[TCA_SAMPLE_PARMS]);
|
||||||
|
|
||||||
print_string(PRINT_ANY, "kind", "%s ", "sample");
|
|
||||||
print_uint(PRINT_ANY, "rate", "rate 1/%u ",
|
print_uint(PRINT_ANY, "rate", "rate 1/%u ",
|
||||||
rta_getattr_u32(tb[TCA_SAMPLE_RATE]));
|
rta_getattr_u32(tb[TCA_SAMPLE_RATE]));
|
||||||
print_uint(PRINT_ANY, "group", "group %u",
|
print_uint(PRINT_ANY, "group", "group %u",
|
||||||
|
|
|
||||||
|
|
@ -166,7 +166,7 @@ static int print_simple(struct action_util *au, FILE *f, struct rtattr *arg)
|
||||||
char *simpdata;
|
char *simpdata;
|
||||||
|
|
||||||
if (arg == NULL)
|
if (arg == NULL)
|
||||||
return -1;
|
return 0;
|
||||||
|
|
||||||
parse_rtattr_nested(tb, TCA_DEF_MAX, arg);
|
parse_rtattr_nested(tb, TCA_DEF_MAX, arg);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -198,8 +198,9 @@ static int print_skbedit(struct action_util *au, FILE *f, struct rtattr *arg)
|
||||||
__u16 ptype;
|
__u16 ptype;
|
||||||
struct tc_skbedit *p;
|
struct tc_skbedit *p;
|
||||||
|
|
||||||
|
print_string(PRINT_ANY, "kind", "%s ", "skbedit");
|
||||||
if (arg == NULL)
|
if (arg == NULL)
|
||||||
return -1;
|
return 0;
|
||||||
|
|
||||||
parse_rtattr_nested(tb, TCA_SKBEDIT_MAX, arg);
|
parse_rtattr_nested(tb, TCA_SKBEDIT_MAX, arg);
|
||||||
|
|
||||||
|
|
@ -209,8 +210,6 @@ static int print_skbedit(struct action_util *au, FILE *f, struct rtattr *arg)
|
||||||
}
|
}
|
||||||
p = RTA_DATA(tb[TCA_SKBEDIT_PARMS]);
|
p = RTA_DATA(tb[TCA_SKBEDIT_PARMS]);
|
||||||
|
|
||||||
print_string(PRINT_ANY, "kind", "%s ", "skbedit");
|
|
||||||
|
|
||||||
if (tb[TCA_SKBEDIT_QUEUE_MAPPING] != NULL) {
|
if (tb[TCA_SKBEDIT_QUEUE_MAPPING] != NULL) {
|
||||||
print_uint(PRINT_ANY, "queue_mapping", "queue_mapping %u",
|
print_uint(PRINT_ANY, "queue_mapping", "queue_mapping %u",
|
||||||
rta_getattr_u16(tb[TCA_SKBEDIT_QUEUE_MAPPING]));
|
rta_getattr_u16(tb[TCA_SKBEDIT_QUEUE_MAPPING]));
|
||||||
|
|
|
||||||
|
|
@ -169,7 +169,7 @@ static int print_skbmod(struct action_util *au, FILE *f, struct rtattr *arg)
|
||||||
SPRINT_BUF(b2);
|
SPRINT_BUF(b2);
|
||||||
|
|
||||||
if (arg == NULL)
|
if (arg == NULL)
|
||||||
return -1;
|
return 0;
|
||||||
|
|
||||||
parse_rtattr_nested(tb, TCA_SKBMOD_MAX, arg);
|
parse_rtattr_nested(tb, TCA_SKBMOD_MAX, arg);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -670,8 +670,9 @@ static int print_tunnel_key(struct action_util *au, FILE *f, struct rtattr *arg)
|
||||||
struct rtattr *tb[TCA_TUNNEL_KEY_MAX + 1];
|
struct rtattr *tb[TCA_TUNNEL_KEY_MAX + 1];
|
||||||
struct tc_tunnel_key *parm;
|
struct tc_tunnel_key *parm;
|
||||||
|
|
||||||
|
print_string(PRINT_ANY, "kind", "%s ", "tunnel_key");
|
||||||
if (!arg)
|
if (!arg)
|
||||||
return -1;
|
return 0;
|
||||||
|
|
||||||
parse_rtattr_nested(tb, TCA_TUNNEL_KEY_MAX, arg);
|
parse_rtattr_nested(tb, TCA_TUNNEL_KEY_MAX, arg);
|
||||||
|
|
||||||
|
|
@ -681,8 +682,6 @@ static int print_tunnel_key(struct action_util *au, FILE *f, struct rtattr *arg)
|
||||||
}
|
}
|
||||||
parm = RTA_DATA(tb[TCA_TUNNEL_KEY_PARMS]);
|
parm = RTA_DATA(tb[TCA_TUNNEL_KEY_PARMS]);
|
||||||
|
|
||||||
print_string(PRINT_ANY, "kind", "%s ", "tunnel_key");
|
|
||||||
|
|
||||||
switch (parm->t_action) {
|
switch (parm->t_action) {
|
||||||
case TCA_TUNNEL_KEY_ACT_RELEASE:
|
case TCA_TUNNEL_KEY_ACT_RELEASE:
|
||||||
print_string(PRINT_ANY, "mode", " %s", "unset");
|
print_string(PRINT_ANY, "mode", " %s", "unset");
|
||||||
|
|
|
||||||
|
|
@ -238,8 +238,9 @@ static int print_vlan(struct action_util *au, FILE *f, struct rtattr *arg)
|
||||||
__u16 val;
|
__u16 val;
|
||||||
struct tc_vlan *parm;
|
struct tc_vlan *parm;
|
||||||
|
|
||||||
|
print_string(PRINT_ANY, "kind", "%s ", "vlan");
|
||||||
if (arg == NULL)
|
if (arg == NULL)
|
||||||
return -1;
|
return 0;
|
||||||
|
|
||||||
parse_rtattr_nested(tb, TCA_VLAN_MAX, arg);
|
parse_rtattr_nested(tb, TCA_VLAN_MAX, arg);
|
||||||
|
|
||||||
|
|
@ -249,7 +250,6 @@ static int print_vlan(struct action_util *au, FILE *f, struct rtattr *arg)
|
||||||
}
|
}
|
||||||
parm = RTA_DATA(tb[TCA_VLAN_PARMS]);
|
parm = RTA_DATA(tb[TCA_VLAN_PARMS]);
|
||||||
|
|
||||||
print_string(PRINT_ANY, "kind", "%s ", "vlan");
|
|
||||||
print_string(PRINT_ANY, "vlan_action", " %s",
|
print_string(PRINT_ANY, "vlan_action", " %s",
|
||||||
action_names[parm->v_action]);
|
action_names[parm->v_action]);
|
||||||
|
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue