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)
|
||||
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
|
||||
LDLIBS += $(LIBNETLINK)
|
||||
|
|
|
|||
|
|
@ -77,20 +77,14 @@ static int do_cmd(const char *argv0, int argc, char **argv)
|
|||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
int ret;
|
||||
|
||||
if (rtnl_open(&rth, 0) < 0) {
|
||||
fprintf(stderr, "Cannot open rtnetlink\n");
|
||||
|
|
@ -99,25 +93,7 @@ static int batch(const char *name)
|
|||
|
||||
rtnl_set_strict_dump(&rth);
|
||||
|
||||
cmdlineno = 0;
|
||||
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);
|
||||
ret = do_batch(name, force, br_batch_cmd, NULL);
|
||||
|
||||
rtnl_close(&rth);
|
||||
return ret;
|
||||
|
|
|
|||
135
bridge/link.c
135
bridge/link.c
|
|
@ -78,14 +78,6 @@ static void print_portstate(__u8 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)
|
||||
{
|
||||
if (mode >= ARRAY_SIZE(hw_mode))
|
||||
|
|
@ -123,38 +115,38 @@ static void print_protinfo(FILE *fp, struct rtattr *attr)
|
|||
fprintf(fp, "%s ", _SL_);
|
||||
|
||||
if (prtb[IFLA_BRPORT_MODE])
|
||||
print_onoff(fp, "hairpin",
|
||||
rta_getattr_u8(prtb[IFLA_BRPORT_MODE]));
|
||||
print_on_off(PRINT_ANY, "hairpin", "hairpin %s ",
|
||||
rta_getattr_u8(prtb[IFLA_BRPORT_MODE]));
|
||||
if (prtb[IFLA_BRPORT_GUARD])
|
||||
print_onoff(fp, "guard",
|
||||
rta_getattr_u8(prtb[IFLA_BRPORT_GUARD]));
|
||||
print_on_off(PRINT_ANY, "guard", "guard %s ",
|
||||
rta_getattr_u8(prtb[IFLA_BRPORT_GUARD]));
|
||||
if (prtb[IFLA_BRPORT_PROTECT])
|
||||
print_onoff(fp, "root_block",
|
||||
rta_getattr_u8(prtb[IFLA_BRPORT_PROTECT]));
|
||||
print_on_off(PRINT_ANY, "root_block", "root_block %s ",
|
||||
rta_getattr_u8(prtb[IFLA_BRPORT_PROTECT]));
|
||||
if (prtb[IFLA_BRPORT_FAST_LEAVE])
|
||||
print_onoff(fp, "fastleave",
|
||||
rta_getattr_u8(prtb[IFLA_BRPORT_FAST_LEAVE]));
|
||||
print_on_off(PRINT_ANY, "fastleave", "fastleave %s ",
|
||||
rta_getattr_u8(prtb[IFLA_BRPORT_FAST_LEAVE]));
|
||||
if (prtb[IFLA_BRPORT_LEARNING])
|
||||
print_onoff(fp, "learning",
|
||||
rta_getattr_u8(prtb[IFLA_BRPORT_LEARNING]));
|
||||
print_on_off(PRINT_ANY, "learning", "learning %s ",
|
||||
rta_getattr_u8(prtb[IFLA_BRPORT_LEARNING]));
|
||||
if (prtb[IFLA_BRPORT_LEARNING_SYNC])
|
||||
print_onoff(fp, "learning_sync",
|
||||
rta_getattr_u8(prtb[IFLA_BRPORT_LEARNING_SYNC]));
|
||||
print_on_off(PRINT_ANY, "learning_sync", "learning_sync %s ",
|
||||
rta_getattr_u8(prtb[IFLA_BRPORT_LEARNING_SYNC]));
|
||||
if (prtb[IFLA_BRPORT_UNICAST_FLOOD])
|
||||
print_onoff(fp, "flood",
|
||||
rta_getattr_u8(prtb[IFLA_BRPORT_UNICAST_FLOOD]));
|
||||
print_on_off(PRINT_ANY, "flood", "flood %s ",
|
||||
rta_getattr_u8(prtb[IFLA_BRPORT_UNICAST_FLOOD]));
|
||||
if (prtb[IFLA_BRPORT_MCAST_FLOOD])
|
||||
print_onoff(fp, "mcast_flood",
|
||||
rta_getattr_u8(prtb[IFLA_BRPORT_MCAST_FLOOD]));
|
||||
print_on_off(PRINT_ANY, "mcast_flood", "mcast_flood %s ",
|
||||
rta_getattr_u8(prtb[IFLA_BRPORT_MCAST_FLOOD]));
|
||||
if (prtb[IFLA_BRPORT_MCAST_TO_UCAST])
|
||||
print_onoff(fp, "mcast_to_unicast",
|
||||
rta_getattr_u8(prtb[IFLA_BRPORT_MCAST_TO_UCAST]));
|
||||
print_on_off(PRINT_ANY, "mcast_to_unicast", "mcast_to_unicast %s ",
|
||||
rta_getattr_u8(prtb[IFLA_BRPORT_MCAST_TO_UCAST]));
|
||||
if (prtb[IFLA_BRPORT_NEIGH_SUPPRESS])
|
||||
print_onoff(fp, "neigh_suppress",
|
||||
rta_getattr_u8(prtb[IFLA_BRPORT_NEIGH_SUPPRESS]));
|
||||
print_on_off(PRINT_ANY, "neigh_suppress", "neigh_suppress %s ",
|
||||
rta_getattr_u8(prtb[IFLA_BRPORT_NEIGH_SUPPRESS]));
|
||||
if (prtb[IFLA_BRPORT_VLAN_TUNNEL])
|
||||
print_onoff(fp, "vlan_tunnel",
|
||||
rta_getattr_u8(prtb[IFLA_BRPORT_VLAN_TUNNEL]));
|
||||
print_on_off(PRINT_ANY, "vlan_tunnel", "vlan_tunnel %s ",
|
||||
rta_getattr_u8(prtb[IFLA_BRPORT_VLAN_TUNNEL]));
|
||||
|
||||
if (prtb[IFLA_BRPORT_BACKUP_PORT]) {
|
||||
int ifidx;
|
||||
|
|
@ -166,8 +158,8 @@ static void print_protinfo(FILE *fp, struct rtattr *attr)
|
|||
}
|
||||
|
||||
if (prtb[IFLA_BRPORT_ISOLATED])
|
||||
print_onoff(fp, "isolated",
|
||||
rta_getattr_u8(prtb[IFLA_BRPORT_ISOLATED]));
|
||||
print_on_off(PRINT_ANY, "isolated", "isolated %s ",
|
||||
rta_getattr_u8(prtb[IFLA_BRPORT_ISOLATED]));
|
||||
} else
|
||||
print_portstate(rta_getattr_u8(attr));
|
||||
}
|
||||
|
|
@ -275,22 +267,6 @@ static void usage(void)
|
|||
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)
|
||||
{
|
||||
struct {
|
||||
|
|
@ -323,6 +299,7 @@ static int brlink_modify(int argc, char **argv)
|
|||
__s16 mode = -1;
|
||||
__u16 flags = 0;
|
||||
struct rtattr *nest;
|
||||
int ret;
|
||||
|
||||
while (argc > 0) {
|
||||
if (strcmp(*argv, "dev") == 0) {
|
||||
|
|
@ -330,40 +307,49 @@ static int brlink_modify(int argc, char **argv)
|
|||
d = *argv;
|
||||
} else if (strcmp(*argv, "guard") == 0) {
|
||||
NEXT_ARG();
|
||||
if (!on_off("guard", &bpdu_guard, *argv))
|
||||
return -1;
|
||||
bpdu_guard = parse_on_off("guard", *argv, &ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else if (strcmp(*argv, "hairpin") == 0) {
|
||||
NEXT_ARG();
|
||||
if (!on_off("hairpin", &hairpin, *argv))
|
||||
return -1;
|
||||
hairpin = parse_on_off("hairpin", *argv, &ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else if (strcmp(*argv, "fastleave") == 0) {
|
||||
NEXT_ARG();
|
||||
if (!on_off("fastleave", &fast_leave, *argv))
|
||||
return -1;
|
||||
fast_leave = parse_on_off("fastleave", *argv, &ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else if (strcmp(*argv, "root_block") == 0) {
|
||||
NEXT_ARG();
|
||||
if (!on_off("root_block", &root_block, *argv))
|
||||
return -1;
|
||||
root_block = parse_on_off("root_block", *argv, &ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else if (strcmp(*argv, "learning") == 0) {
|
||||
NEXT_ARG();
|
||||
if (!on_off("learning", &learning, *argv))
|
||||
return -1;
|
||||
learning = parse_on_off("learning", *argv, &ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else if (strcmp(*argv, "learning_sync") == 0) {
|
||||
NEXT_ARG();
|
||||
if (!on_off("learning_sync", &learning_sync, *argv))
|
||||
return -1;
|
||||
learning_sync = parse_on_off("learning_sync", *argv, &ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else if (strcmp(*argv, "flood") == 0) {
|
||||
NEXT_ARG();
|
||||
if (!on_off("flood", &flood, *argv))
|
||||
return -1;
|
||||
flood = parse_on_off("flood", *argv, &ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else if (strcmp(*argv, "mcast_flood") == 0) {
|
||||
NEXT_ARG();
|
||||
if (!on_off("mcast_flood", &mcast_flood, *argv))
|
||||
return -1;
|
||||
mcast_flood = parse_on_off("mcast_flood", *argv, &ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else if (strcmp(*argv, "mcast_to_unicast") == 0) {
|
||||
NEXT_ARG();
|
||||
if (!on_off("mcast_to_unicast", &mcast_to_unicast, *argv))
|
||||
return -1;
|
||||
mcast_to_unicast = parse_on_off("mcast_to_unicast", *argv, &ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else if (strcmp(*argv, "cost") == 0) {
|
||||
NEXT_ARG();
|
||||
cost = atoi(*argv);
|
||||
|
|
@ -404,18 +390,19 @@ static int brlink_modify(int argc, char **argv)
|
|||
flags |= BRIDGE_FLAGS_MASTER;
|
||||
} else if (strcmp(*argv, "neigh_suppress") == 0) {
|
||||
NEXT_ARG();
|
||||
if (!on_off("neigh_suppress", &neigh_suppress,
|
||||
*argv))
|
||||
return -1;
|
||||
neigh_suppress = parse_on_off("neigh_suppress", *argv, &ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else if (strcmp(*argv, "vlan_tunnel") == 0) {
|
||||
NEXT_ARG();
|
||||
if (!on_off("vlan_tunnel", &vlan_tunnel,
|
||||
*argv))
|
||||
return -1;
|
||||
vlan_tunnel = parse_on_off("vlan_tunnel", *argv, &ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else if (strcmp(*argv, "isolated") == 0) {
|
||||
NEXT_ARG();
|
||||
if (!on_off("isolated", &isolated, *argv))
|
||||
return -1;
|
||||
isolated = parse_on_off("isolated", *argv, &ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else if (strcmp(*argv, "backup_port") == 0) {
|
||||
NEXT_ARG();
|
||||
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)
|
||||
{
|
||||
const void *grp, *src;
|
||||
const char *addr;
|
||||
SPRINT_BUF(abuf);
|
||||
const char *dev;
|
||||
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)
|
||||
return;
|
||||
|
||||
af = e->addr.proto == htons(ETH_P_IP) ? AF_INET : AF_INET6;
|
||||
grp = af == AF_INET ? (const void *)&e->addr.u.ip4 :
|
||||
(const void *)&e->addr.u.ip6;
|
||||
if (!e->addr.proto) {
|
||||
af = AF_PACKET;
|
||||
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);
|
||||
|
||||
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",
|
||||
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),
|
||||
"grp", " grp %s",
|
||||
inet_ntop(af, grp, abuf, sizeof(abuf)));
|
||||
"grp", " grp %s", addr);
|
||||
|
||||
if (tb && tb[MDBA_MDB_EATTR_SOURCE]) {
|
||||
src = (const void *)RTA_DATA(tb[MDBA_MDB_EATTR_SOURCE]);
|
||||
print_color_string(PRINT_ANY, ifa_family_color(af),
|
||||
|
|
@ -440,6 +453,25 @@ static int mdb_show(int argc, char **argv)
|
|||
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)
|
||||
{
|
||||
struct {
|
||||
|
|
@ -497,14 +529,10 @@ static int mdb_modify(int cmd, int flags, int argc, char **argv)
|
|||
if (!entry.ifindex)
|
||||
return nodev(p);
|
||||
|
||||
if (!inet_pton(AF_INET, grp, &entry.addr.u.ip4)) {
|
||||
if (!inet_pton(AF_INET6, grp, &entry.addr.u.ip6)) {
|
||||
fprintf(stderr, "Invalid address \"%s\"\n", grp);
|
||||
return -1;
|
||||
} else
|
||||
entry.addr.proto = htons(ETH_P_IPV6);
|
||||
} else
|
||||
entry.addr.proto = htons(ETH_P_IP);
|
||||
if (mdb_parse_grp(grp, &entry)) {
|
||||
fprintf(stderr, "Invalid address \"%s\"\n", grp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
entry.vid = vid;
|
||||
addattr_l(&req.n, sizeof(req), MDBA_SET_ENTRY, &entry, sizeof(entry));
|
||||
|
|
|
|||
|
|
@ -2,6 +2,11 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
# 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"}
|
||||
|
||||
# Output file which is input to Makefile
|
||||
|
|
@ -240,6 +245,111 @@ check_elf()
|
|||
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()
|
||||
# SELinux is a compile time option in the ss utility
|
||||
{
|
||||
|
|
@ -385,6 +495,9 @@ check_setns
|
|||
echo -n "SELinux support: "
|
||||
check_selinux
|
||||
|
||||
echo -n "libbpf support: "
|
||||
check_libbpf
|
||||
|
||||
echo -n "ELF support: "
|
||||
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)
|
||||
|
||||
devlink: $(DEVLINKOBJ)
|
||||
devlink: $(DEVLINKOBJ) $(LIBNETLINK)
|
||||
$(QUIET_LINK)$(CC) $^ $(LDFLAGS) $(LDLIBS) -o $@
|
||||
|
||||
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_PORT_FUNCTION_HW_ADDR BIT(38)
|
||||
#define DL_OPT_FLASH_OVERWRITE BIT(39)
|
||||
#define DL_OPT_RELOAD_ACTION BIT(40)
|
||||
#define DL_OPT_RELOAD_LIMIT BIT(41)
|
||||
|
||||
struct dl_opts {
|
||||
uint64_t present; /* flags of present items */
|
||||
|
|
@ -352,6 +354,8 @@ struct dl_opts {
|
|||
char port_function_hw_addr[MAX_ADDR_LEN];
|
||||
uint32_t port_function_hw_addr_len;
|
||||
uint32_t overwrite_mask;
|
||||
enum devlink_reload_action reload_action;
|
||||
enum devlink_reload_limit reload_limit;
|
||||
};
|
||||
|
||||
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_GROUP_NAME] = MNL_TYPE_STRING,
|
||||
[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_RATE] = 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;
|
||||
}
|
||||
|
||||
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 {
|
||||
uint64_t o_flag;
|
||||
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;
|
||||
}
|
||||
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") &&
|
||||
(o_all & DL_OPT_TRAP_POLICER_ID)) {
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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 :
|
||||
DEVLINK_ATTR_NETNS_FD,
|
||||
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)
|
||||
mnl_attr_put_u32(nlh, DEVLINK_ATTR_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 show [DEV name PARAMETER]\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 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)
|
||||
{
|
||||
switch (mode) {
|
||||
|
|
@ -2893,29 +2996,119 @@ static int cmd_dev_param(struct dl *dl)
|
|||
pr_err("Command \"%s\" not found\n", dl_argv(dl));
|
||||
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);
|
||||
if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME])
|
||||
return MNL_CB_ERROR;
|
||||
static void pr_out_action_stats(struct dl *dl, struct nlattr *action_stats)
|
||||
{
|
||||
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])
|
||||
reload_failed = mnl_attr_get_u8(tb[DEVLINK_ATTR_RELOAD_FAILED]);
|
||||
|
||||
if (reload_failed) {
|
||||
__pr_out_handle_start(dl, tb, true, false);
|
||||
check_indent_newline(dl);
|
||||
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);
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -2942,6 +3135,57 @@ static int cmd_dev_show(struct dl *dl)
|
|||
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)
|
||||
{
|
||||
struct nlmsghdr *nlh;
|
||||
|
|
@ -2955,11 +3199,13 @@ static int cmd_dev_reload(struct dl *dl)
|
|||
nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_RELOAD,
|
||||
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)
|
||||
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,
|
||||
|
|
@ -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])
|
||||
return MNL_CB_ERROR;
|
||||
pr_out_mon_header(genl->cmd);
|
||||
pr_out_handle(dl, tb);
|
||||
dl->stats = true;
|
||||
pr_out_dev(dl, tb);
|
||||
pr_out_mon_footer();
|
||||
break;
|
||||
case DEVLINK_CMD_PORT_GET: /* fall through */
|
||||
|
|
@ -7915,43 +8162,16 @@ static void dl_free(struct dl *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)
|
||||
{
|
||||
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 (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;
|
||||
return do_batch(name, force, dl_batch_cmd, dl);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
|
|
|
|||
|
|
@ -14,11 +14,11 @@
|
|||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <libmnl/libmnl.h>
|
||||
#include <linux/genetlink.h>
|
||||
|
||||
#include "libnetlink.h"
|
||||
#include "mnl_utils.h"
|
||||
#include "utils.h"
|
||||
#include "mnlg.h"
|
||||
|
||||
|
|
@ -28,26 +28,20 @@ struct mnlg_socket {
|
|||
uint32_t id;
|
||||
uint8_t version;
|
||||
unsigned int seq;
|
||||
unsigned int portid;
|
||||
};
|
||||
|
||||
static struct nlmsghdr *__mnlg_msg_prepare(struct mnlg_socket *nlg, uint8_t cmd,
|
||||
uint16_t flags, uint32_t id,
|
||||
uint8_t version)
|
||||
{
|
||||
struct genlmsghdr genl = {
|
||||
.cmd = cmd,
|
||||
.version = version,
|
||||
};
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
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 err;
|
||||
|
||||
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;
|
||||
return mnlu_socket_recv_run(nlg->nl, nlg->seq, nlg->buf, MNL_SOCKET_BUFFER_SIZE,
|
||||
data_cb, data);
|
||||
}
|
||||
|
||||
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 nlmsghdr *nlh;
|
||||
int one = 1;
|
||||
int err;
|
||||
|
||||
nlg = malloc(sizeof(*nlg));
|
||||
|
|
@ -274,19 +216,9 @@ struct mnlg_socket *mnlg_socket_open(const char *family_name, uint8_t version)
|
|||
if (!nlg->buf)
|
||||
goto err_buf_alloc;
|
||||
|
||||
nlg->nl = mnl_socket_open(NETLINK_GENERIC);
|
||||
nlg->nl = mnlu_socket_open(NETLINK_GENERIC);
|
||||
if (!nlg->nl)
|
||||
goto err_mnl_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);
|
||||
goto err_socket_open;
|
||||
|
||||
nlh = __mnlg_msg_prepare(nlg, CTRL_CMD_GETFAMILY,
|
||||
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_send:
|
||||
err_mnl_socket_bind:
|
||||
mnl_socket_close(nlg->nl);
|
||||
err_mnl_socket_open:
|
||||
err_socket_open:
|
||||
free(nlg->buf);
|
||||
err_buf_alloc:
|
||||
free(nlg);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,18 @@
|
|||
eBPF toy code examples (running in kernel) to familiarize yourself
|
||||
with syntax and features:
|
||||
|
||||
- bpf_shared.c -> Ingress/egress map sharing example
|
||||
- bpf_tailcall.c -> Using tail call chains
|
||||
- bpf_cyclic.c -> Simple cycle as tail calls
|
||||
- BTF defined map examples
|
||||
- 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 = {
|
||||
.type = BPF_MAP_TYPE_PROG_ARRAY,
|
||||
.size_key = sizeof(uint32_t),
|
||||
.size_value = sizeof(uint32_t),
|
||||
.pinning = PIN_GLOBAL_NS,
|
||||
.max_elem = 1,
|
||||
};
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
|
||||
__uint(key_size, sizeof(uint32_t));
|
||||
__uint(value_size, sizeof(uint32_t));
|
||||
__uint(max_entries, 1);
|
||||
__uint(pinning, LIBBPF_PIN_BY_NAME);
|
||||
} jmp_tc __section(".maps");
|
||||
|
||||
__section("aaa")
|
||||
int cls_aaa(struct __sk_buff *skb)
|
||||
|
|
|
|||
|
|
@ -1,24 +1,23 @@
|
|||
#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 = {
|
||||
.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,
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
|
||||
__uint(key_size, sizeof(uint32_t));
|
||||
__uint(value_size, sizeof(uint32_t));
|
||||
__uint(max_entries, 1);
|
||||
__uint(pinning, LIBBPF_PIN_BY_NAME);
|
||||
__array(values, struct inner_map);
|
||||
} map_outer __section(".maps") = {
|
||||
.values = {
|
||||
[0] = &map_inner,
|
||||
},
|
||||
};
|
||||
|
||||
__section("egress")
|
||||
|
|
|
|||
|
|
@ -18,13 +18,13 @@
|
|||
* 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,
|
||||
};
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||
__uint(key_size, sizeof(uint32_t));
|
||||
__uint(value_size, sizeof(uint32_t));
|
||||
__uint(max_entries, 1);
|
||||
__uint(pinning, LIBBPF_PIN_BY_NAME); /* or LIBBPF_PIN_NONE */
|
||||
} map_sh __section(".maps");
|
||||
|
||||
__section("egress")
|
||||
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
|
||||
* 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 */
|
||||
#include "../../include/bpf_api.h"
|
||||
#include "../../../include/bpf_api.h"
|
||||
|
||||
#define ENTRY_INIT 3
|
||||
#define ENTRY_0 0
|
||||
|
|
@ -19,6 +19,19 @@
|
|||
|
||||
#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. */
|
||||
|
||||
#ifndef __stringify
|
||||
|
|
|
|||
|
|
@ -274,12 +274,16 @@ int bpf_trace_pipe(void);
|
|||
|
||||
void bpf_print_ops(struct rtattr *bpf_ops, __u16 len);
|
||||
|
||||
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);
|
||||
int bpf_prog_load_dev(enum bpf_prog_type type, const struct bpf_insn *insns,
|
||||
size_t size_insns, const char *license, __u32 ifindex,
|
||||
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_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);
|
||||
|
||||
|
|
@ -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_recv_map_fds(const char *path, int *fds, struct bpf_map_aux *aux,
|
||||
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
|
||||
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;
|
||||
}
|
||||
#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 */
|
||||
|
||||
const char *get_libbpf_version(void);
|
||||
|
||||
#endif /* __BPF_UTIL__ */
|
||||
|
|
|
|||
|
|
@ -65,9 +65,11 @@ void print_nl(void);
|
|||
_PRINT_FUNC(int, int)
|
||||
_PRINT_FUNC(s64, int64_t)
|
||||
_PRINT_FUNC(bool, bool)
|
||||
_PRINT_FUNC(on_off, bool)
|
||||
_PRINT_FUNC(null, const char*)
|
||||
_PRINT_FUNC(string, const char*)
|
||||
_PRINT_FUNC(uint, unsigned int)
|
||||
_PRINT_FUNC(size, __u32)
|
||||
_PRINT_FUNC(u64, uint64_t)
|
||||
_PRINT_FUNC(hhu, unsigned char)
|
||||
_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);
|
||||
#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_ */
|
||||
|
|
|
|||
|
|
@ -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_RINGBUF,
|
||||
BPF_MAP_TYPE_INODE_STORAGE,
|
||||
BPF_MAP_TYPE_TASK_STORAGE,
|
||||
};
|
||||
|
||||
/* Note that tracing related programs such as
|
||||
|
|
@ -556,7 +557,12 @@ union bpf_attr {
|
|||
__aligned_u64 line_info; /* line info */
|
||||
__u32 line_info_cnt; /* number of bpf_line_info records */
|
||||
__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 */
|
||||
|
|
@ -3742,6 +3748,80 @@ union bpf_attr {
|
|||
* Return
|
||||
* The helper returns **TC_ACT_REDIRECT** on success or
|
||||
* **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) \
|
||||
FN(unspec), \
|
||||
|
|
@ -3900,6 +3980,12 @@ union bpf_attr {
|
|||
FN(per_cpu_ptr), \
|
||||
FN(this_cpu_ptr), \
|
||||
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
|
||||
|
|
@ -4071,6 +4157,11 @@ enum bpf_lwt_encap_mode {
|
|||
BPF_LWT_ENCAP_IP,
|
||||
};
|
||||
|
||||
/* Flags for bpf_bprm_opts_set helper */
|
||||
enum {
|
||||
BPF_F_BPRM_SECUREEXEC = (1ULL << 0),
|
||||
};
|
||||
|
||||
#define __bpf_md_ptr(type, name) \
|
||||
union { \
|
||||
type name; \
|
||||
|
|
@ -4418,6 +4509,9 @@ struct bpf_btf_info {
|
|||
__aligned_u64 btf;
|
||||
__u32 btf_size;
|
||||
__u32 id;
|
||||
__aligned_u64 name;
|
||||
__u32 name_len;
|
||||
__u32 kernel_btf;
|
||||
} __attribute__((aligned(8)));
|
||||
|
||||
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 */
|
||||
#define CAN_MAX_DLC 8
|
||||
#define CAN_MAX_RAW_DLC 15
|
||||
#define CAN_MAX_DLEN 8
|
||||
|
||||
/* 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
|
||||
|
||||
/**
|
||||
* struct can_frame - basic CAN frame structure
|
||||
* @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
|
||||
* N.B. the DLC field from ISO 11898-1 Chapter 8.4.2.3 has a 1:1
|
||||
* mapping of the 'data length code' to the real payload length
|
||||
* @__pad: padding
|
||||
* @__res0: reserved / padding
|
||||
* @__res1: reserved / padding
|
||||
* @data: CAN frame payload (up to 8 byte)
|
||||
* 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
|
||||
* @len: CAN frame payload length in byte (0 .. 8)
|
||||
* @can_dlc: deprecated name for CAN frame payload length in byte (0 .. 8)
|
||||
* @__pad: padding
|
||||
* @__res0: reserved / padding
|
||||
* @len8_dlc: optional DLC value (9 .. 15) at 8 byte payload length
|
||||
* len8_dlc contains values from 9 .. 15 when the payload length is
|
||||
* 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 {
|
||||
canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
|
||||
__u8 can_dlc; /* frame payload length in byte (0 .. CAN_MAX_DLEN) */
|
||||
__u8 __pad; /* padding */
|
||||
__u8 __res0; /* reserved / padding */
|
||||
__u8 __res1; /* reserved / padding */
|
||||
__u8 data[CAN_MAX_DLEN] __attribute__((aligned(8)));
|
||||
union {
|
||||
/* CAN frame payload length in byte (0 .. CAN_MAX_DLEN)
|
||||
* was previously named can_dlc so we need to carry that
|
||||
* name for legacy support
|
||||
*/
|
||||
__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_PRESUME_ACK 0x40 /* Ignore missing CAN ACKs */
|
||||
#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
|
||||
|
|
|
|||
|
|
@ -121,6 +121,7 @@ enum {
|
|||
IFLA_BRIDGE_VLAN_INFO,
|
||||
IFLA_BRIDGE_VLAN_TUNNEL_INFO,
|
||||
IFLA_BRIDGE_MRP,
|
||||
IFLA_BRIDGE_CFM,
|
||||
__IFLA_BRIDGE_MAX,
|
||||
};
|
||||
#define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1)
|
||||
|
|
@ -328,6 +329,130 @@ struct br_mrp_start_in_test {
|
|||
__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 {
|
||||
__u64 transition_blk;
|
||||
__u64 transition_fwd;
|
||||
|
|
@ -526,6 +651,7 @@ struct br_mdb_entry {
|
|||
union {
|
||||
__be32 ip4;
|
||||
struct in6_addr ip6;
|
||||
unsigned char mac_addr[ETH_ALEN];
|
||||
} u;
|
||||
__be16 proto;
|
||||
} addr;
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@
|
|||
#define ETH_P_1588 0x88F7 /* IEEE 1588 Timesync */
|
||||
#define ETH_P_NCSI 0x88F8 /* NCSI protocol */
|
||||
#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_IBOE 0x8915 /* Infiniband over Ethernet */
|
||||
#define ETH_P_TDLS 0x890D /* TDLS */
|
||||
|
|
|
|||
|
|
@ -586,6 +586,8 @@ enum {
|
|||
IFLA_MACVLAN_MACADDR,
|
||||
IFLA_MACVLAN_MACADDR_DATA,
|
||||
IFLA_MACVLAN_MACADDR_COUNT,
|
||||
IFLA_MACVLAN_BC_QUEUE_LEN,
|
||||
IFLA_MACVLAN_BC_QUEUE_LEN_USED,
|
||||
__IFLA_MACVLAN_MAX,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#ifndef __LINUX_IF_PACKET_H
|
||||
#define __LINUX_IF_PACKET_H
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
struct sockaddr_pkt {
|
||||
|
|
@ -296,6 +297,17 @@ struct packet_mreq {
|
|||
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_PROMISC 1
|
||||
#define PACKET_MR_ALLMULTI 2
|
||||
|
|
|
|||
|
|
@ -92,11 +92,11 @@ enum {
|
|||
/* Reserve empty slots */
|
||||
IPSET_ATTR_CADT_MAX = 16,
|
||||
/* Create-only specific attributes */
|
||||
IPSET_ATTR_GC,
|
||||
IPSET_ATTR_INITVAL, /* was unused IPSET_ATTR_GC */
|
||||
IPSET_ATTR_HASHSIZE,
|
||||
IPSET_ATTR_MAXELEM,
|
||||
IPSET_ATTR_NETMASK,
|
||||
IPSET_ATTR_PROBES,
|
||||
IPSET_ATTR_BUCKETSIZE, /* was unused IPSET_ATTR_PROBES */
|
||||
IPSET_ATTR_RESIZE,
|
||||
IPSET_ATTR_SIZE,
|
||||
/* Kernel-only */
|
||||
|
|
@ -214,6 +214,8 @@ enum ipset_cadt_flags {
|
|||
enum ipset_create_flags {
|
||||
IPSET_CREATE_FLAG_BIT_FORCEADD = 0,
|
||||
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,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -396,11 +396,13 @@ struct rtnexthop {
|
|||
#define RTNH_F_DEAD 1 /* Nexthop is dead (used by multipath) */
|
||||
#define RTNH_F_PERVASIVE 2 /* Do recursive gateway lookup */
|
||||
#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_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 */
|
||||
|
||||
|
|
@ -764,12 +766,18 @@ enum {
|
|||
#define TA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct tcamsg))
|
||||
/* tcamsg flags stored in attribute TCA_ROOT_FLAGS
|
||||
*
|
||||
* TCA_FLAG_LARGE_DUMP_ON user->kernel to request for larger than TCA_ACT_MAX_PRIO
|
||||
* actions in a dump. All dump responses will contain the number of actions
|
||||
* being dumped stored in for user app's consumption in TCA_ROOT_COUNT
|
||||
* TCA_ACT_FLAG_LARGE_DUMP_ON user->kernel to request for larger than
|
||||
* TCA_ACT_MAX_PRIO actions in a dump. All dump responses will contain the
|
||||
* 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_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 */
|
||||
#define RTEXT_FILTER_VF (1 << 0)
|
||||
|
|
@ -777,6 +785,8 @@ enum {
|
|||
#define RTEXT_FILTER_BRVLAN_COMPRESSED (1 << 2)
|
||||
#define RTEXT_FILTER_SKIP_STATS (1 << 3)
|
||||
#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 */
|
||||
|
||||
|
|
|
|||
|
|
@ -140,6 +140,7 @@ typedef __s32 sctp_assoc_t;
|
|||
#define SCTP_ECN_SUPPORTED 130
|
||||
#define SCTP_EXPOSE_POTENTIALLY_FAILED_STATE 131
|
||||
#define SCTP_EXPOSE_PF_STATE SCTP_EXPOSE_POTENTIALLY_FAILED_STATE
|
||||
#define SCTP_REMOTE_UDP_ENCAPS_PORT 132
|
||||
|
||||
/* PR-SCTP policies */
|
||||
#define SCTP_PR_SCTP_NONE 0x0000
|
||||
|
|
@ -1191,6 +1192,12 @@ struct sctp_event {
|
|||
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 */
|
||||
enum sctp_sched_type {
|
||||
SCTP_SS_FCFS,
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ enum {
|
|||
SEG6_LOCAL_IIF,
|
||||
SEG6_LOCAL_OIF,
|
||||
SEG6_LOCAL_BPF,
|
||||
SEG6_LOCAL_VRFTABLE,
|
||||
__SEG6_LOCAL_MAX,
|
||||
};
|
||||
#define SEG6_LOCAL_MAX (__SEG6_LOCAL_MAX - 1)
|
||||
|
|
|
|||
|
|
@ -159,6 +159,7 @@ enum
|
|||
UDP_MIB_SNDBUFERRORS, /* SndbufErrors */
|
||||
UDP_MIB_CSUMERRORS, /* InCsumErrors */
|
||||
UDP_MIB_IGNOREDMULTI, /* IgnoredMulti */
|
||||
UDP_MIB_MEMERRORS, /* MemErrors */
|
||||
__UDP_MIB_MAX
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -343,11 +343,15 @@ struct tcp_diag_md5sig {
|
|||
|
||||
/* setsockopt(fd, IPPROTO_TCP, TCP_ZEROCOPY_RECEIVE, ...) */
|
||||
|
||||
#define TCP_RECEIVE_ZEROCOPY_FLAG_TLB_CLEAN_HINT 0x1
|
||||
struct tcp_zerocopy_receive {
|
||||
__u64 address; /* in: address of mapping */
|
||||
__u32 length; /* in/out: number of bytes to map/mapped */
|
||||
__u32 recv_skip_hint; /* out: amount of bytes to skip */
|
||||
__u32 inq; /* out: amount of bytes in read queue */
|
||||
__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 */
|
||||
|
|
|
|||
|
|
@ -77,6 +77,13 @@
|
|||
#define TLS_CIPHER_AES_CCM_128_TAG_SIZE 16
|
||||
#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_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];
|
||||
};
|
||||
|
||||
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 {
|
||||
TLS_INFO_UNSPEC,
|
||||
TLS_INFO_VERSION,
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@
|
|||
|
||||
extern int preferred_family;
|
||||
extern int human_readable;
|
||||
extern int use_iec;
|
||||
extern int show_stats;
|
||||
extern int show_details;
|
||||
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_be16(__be16 *val, const char *arg, int base);
|
||||
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);
|
||||
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_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__ */
|
||||
|
|
|
|||
71
ip/ip.c
71
ip/ip.c
|
|
@ -24,6 +24,11 @@
|
|||
#include "namespace.h"
|
||||
#include "color.h"
|
||||
#include "rt_names.h"
|
||||
#include "bpf_util.h"
|
||||
|
||||
#ifndef LIBDIR
|
||||
#define LIBDIR "/usr/lib"
|
||||
#endif
|
||||
|
||||
int preferred_family = AF_UNSPEC;
|
||||
int human_readable;
|
||||
|
|
@ -41,6 +46,17 @@ bool do_all;
|
|||
|
||||
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)
|
||||
|
|
@ -121,60 +137,35 @@ static int do_cmd(const char *argv0, int argc, char **argv)
|
|||
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)
|
||||
{
|
||||
char *line = NULL;
|
||||
size_t len = 0;
|
||||
int ret = EXIT_SUCCESS;
|
||||
int orig_family = preferred_family;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
int ret;
|
||||
|
||||
if (rtnl_open(&rth, 0) < 0) {
|
||||
fprintf(stderr, "Cannot open rtnetlink\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
cmdlineno = 0;
|
||||
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);
|
||||
ret = do_batch(name, force, ip_batch_cmd, &orig_family);
|
||||
|
||||
rtnl_close(&rth);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
char *basename;
|
||||
const char *libbpf_version;
|
||||
char *batch_file = NULL;
|
||||
char *basename;
|
||||
int color = 0;
|
||||
|
||||
/* to run vrf exec without root, capabilities might be set, drop them
|
||||
|
|
@ -255,7 +246,11 @@ int main(int argc, char **argv)
|
|||
++timestamp;
|
||||
++timestamp_short;
|
||||
} 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);
|
||||
} else if (matches(opt, "-force") == 0) {
|
||||
++force;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
#include "json_print.h"
|
||||
|
||||
extern int use_iec;
|
||||
|
||||
struct link_filter {
|
||||
int ifindex;
|
||||
int family;
|
||||
|
|
@ -27,6 +29,8 @@ struct link_filter {
|
|||
int target_nsid;
|
||||
};
|
||||
|
||||
const char *get_ip_lib_dir(void);
|
||||
|
||||
int get_operstate(const char *name);
|
||||
int print_linkinfo(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"
|
||||
|
||||
#define IPLINK_IOCTL_COMPAT 1
|
||||
#ifndef LIBDIR
|
||||
#define LIBDIR "/usr/lib"
|
||||
#endif
|
||||
|
||||
#ifndef GSO_MAX_SIZE
|
||||
#define GSO_MAX_SIZE 65536
|
||||
|
|
@ -157,7 +154,7 @@ struct link_util *get_link_kind(const char *id)
|
|||
if (strcmp(l->id, id) == 0)
|
||||
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);
|
||||
if (dlh == NULL) {
|
||||
/* 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;
|
||||
char **argv = *argvp;
|
||||
struct rtattr *vfinfo;
|
||||
int ret;
|
||||
|
||||
tivt.min_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;
|
||||
|
||||
NEXT_ARG();
|
||||
if (matches(*argv, "on") == 0)
|
||||
ivs.setting = 1;
|
||||
else if (matches(*argv, "off") == 0)
|
||||
ivs.setting = 0;
|
||||
else
|
||||
return on_off("spoofchk", *argv);
|
||||
ivs.setting = parse_on_off("spoofchk", *argv, &ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
ivs.vf = vf;
|
||||
addattr_l(&req->n, sizeof(*req), IFLA_VF_SPOOFCHK,
|
||||
&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;
|
||||
|
||||
NEXT_ARG();
|
||||
if (matches(*argv, "on") == 0)
|
||||
ivs.setting = 1;
|
||||
else if (matches(*argv, "off") == 0)
|
||||
ivs.setting = 0;
|
||||
else
|
||||
return on_off("query_rss", *argv);
|
||||
ivs.setting = parse_on_off("query_rss", *argv, &ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
ivs.vf = vf;
|
||||
addattr_l(&req->n, sizeof(*req), IFLA_VF_RSS_QUERY_EN,
|
||||
&ivs, sizeof(ivs));
|
||||
|
|
@ -492,12 +484,9 @@ static int iplink_parse_vf(int vf, int *argcp, char ***argvp,
|
|||
struct ifla_vf_trust ivt;
|
||||
|
||||
NEXT_ARG();
|
||||
if (matches(*argv, "on") == 0)
|
||||
ivt.setting = 1;
|
||||
else if (matches(*argv, "off") == 0)
|
||||
ivt.setting = 0;
|
||||
else
|
||||
invarg("Invalid \"trust\" value\n", *argv);
|
||||
ivt.setting = parse_on_off("trust", *argv, &ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
ivt.vf = vf;
|
||||
addattr_l(&req->n, sizeof(*req), IFLA_VF_TRUST,
|
||||
&ivt, sizeof(ivt));
|
||||
|
|
@ -595,6 +584,7 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req, char **type)
|
|||
int index = 0;
|
||||
int group = -1;
|
||||
int addr_len = 0;
|
||||
int err;
|
||||
|
||||
ret = argc;
|
||||
|
||||
|
|
@ -738,12 +728,9 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req, char **type)
|
|||
int carrier;
|
||||
|
||||
NEXT_ARG();
|
||||
if (strcmp(*argv, "on") == 0)
|
||||
carrier = 1;
|
||||
else if (strcmp(*argv, "off") == 0)
|
||||
carrier = 0;
|
||||
else
|
||||
return on_off("carrier", *argv);
|
||||
carrier = parse_on_off("carrier", *argv, &err);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
addattr8(&req->n, sizeof(*req), IFLA_CARRIER, carrier);
|
||||
} 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;
|
||||
|
||||
NEXT_ARG();
|
||||
if (strcmp(*argv, "on") == 0)
|
||||
proto_down = 1;
|
||||
else if (strcmp(*argv, "off") == 0)
|
||||
proto_down = 0;
|
||||
else
|
||||
return on_off("protodown", *argv);
|
||||
proto_down = parse_on_off("protodown", *argv, &err);
|
||||
if (err)
|
||||
return err;
|
||||
addattr8(&req->n, sizeof(*req), IFLA_PROTO_DOWN,
|
||||
proto_down);
|
||||
} 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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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]));
|
||||
|
||||
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]));
|
||||
|
||||
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]));
|
||||
|
||||
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]));
|
||||
|
||||
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]));
|
||||
|
||||
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]));
|
||||
|
||||
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]));
|
||||
|
||||
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]));
|
||||
|
||||
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]));
|
||||
|
||||
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]));
|
||||
|
||||
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");
|
||||
|
||||
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]));
|
||||
|
||||
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]));
|
||||
|
||||
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]));
|
||||
|
||||
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])
|
||||
_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]));
|
||||
|
||||
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]));
|
||||
|
||||
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,
|
||||
struct nlmsghdr *n, int type)
|
||||
{
|
||||
__u8 val;
|
||||
|
||||
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);
|
||||
int ret;
|
||||
__u8 val = parse_on_off(arg_name, arg_val, &ret);
|
||||
|
||||
if (ret)
|
||||
exit(1);
|
||||
addattr8(n, 1024, type, val);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,12 +30,13 @@
|
|||
static void print_explain(struct link_util *lu, FILE *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"
|
||||
"MODE: private | vepa | bridge | passthru | source\n"
|
||||
"MODE_FLAG: null | nopromisc\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
|
||||
);
|
||||
}
|
||||
|
|
@ -62,6 +63,14 @@ static int flag_arg(const char *arg)
|
|||
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,
|
||||
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) {
|
||||
flags |= MACVLAN_FLAG_NOPROMISC;
|
||||
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) {
|
||||
explain(lu);
|
||||
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)
|
||||
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 */
|
||||
|
||||
if (mode != MACVLAN_MODE_SOURCE)
|
||||
|
|
|
|||
|
|
@ -49,36 +49,30 @@ static int on_off(const char *msg, const char *arg)
|
|||
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,
|
||||
int attrtype)
|
||||
{
|
||||
int argc = *argcp;
|
||||
char **argv = *argvp;
|
||||
struct ifla_vlan_qos_mapping m;
|
||||
struct rtattr *tail;
|
||||
|
||||
tail = addattr_nest(n, 1024, attrtype);
|
||||
|
||||
while (argc > 0) {
|
||||
char *colon = strchr(*argv, ':');
|
||||
|
||||
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));
|
||||
}
|
||||
if (parse_mapping(argcp, argvp, false, &parse_qos_mapping, n))
|
||||
return 1;
|
||||
|
||||
addattr_nest_end(n, tail);
|
||||
|
||||
*argcp = argc;
|
||||
*argvp = argv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,8 +23,6 @@
|
|||
#include "ll_map.h"
|
||||
#include "libgenl.h"
|
||||
|
||||
static const char * const values_on_off[] = { "off", "on" };
|
||||
|
||||
static const char * const validate_str[] = {
|
||||
[MACSEC_VALIDATE_DISABLED] = "disabled",
|
||||
[MACSEC_VALIDATE_CHECK] = "check",
|
||||
|
|
@ -108,25 +106,6 @@ static void ipmacsec_usage(void)
|
|||
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)
|
||||
{
|
||||
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)
|
||||
ipmacsec_usage();
|
||||
|
||||
ret = one_of("offload", *argv, offload_str, ARRAY_SIZE(offload_str),
|
||||
(int *)&offload);
|
||||
offload = parse_one_of("offload", *argv, offload_str, ARRAY_SIZE(offload_str), &ret);
|
||||
if (ret)
|
||||
ipmacsec_usage();
|
||||
|
||||
|
|
@ -1334,8 +1312,7 @@ static int macsec_parse_opt(struct link_util *lu, int argc, char **argv,
|
|||
NEXT_ARG();
|
||||
int i;
|
||||
|
||||
ret = one_of("encrypt", *argv, values_on_off,
|
||||
ARRAY_SIZE(values_on_off), &i);
|
||||
i = parse_on_off("encrypt", *argv, &ret);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
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();
|
||||
int i;
|
||||
|
||||
ret = one_of("send_sci", *argv, values_on_off,
|
||||
ARRAY_SIZE(values_on_off), &i);
|
||||
i = parse_on_off("send_sci", *argv, &ret);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
send_sci = i;
|
||||
|
|
@ -1354,8 +1330,7 @@ static int macsec_parse_opt(struct link_util *lu, int argc, char **argv,
|
|||
NEXT_ARG();
|
||||
int i;
|
||||
|
||||
ret = one_of("end_station", *argv, values_on_off,
|
||||
ARRAY_SIZE(values_on_off), &i);
|
||||
i = parse_on_off("end_station", *argv, &ret);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
es = i;
|
||||
|
|
@ -1364,8 +1339,7 @@ static int macsec_parse_opt(struct link_util *lu, int argc, char **argv,
|
|||
NEXT_ARG();
|
||||
int i;
|
||||
|
||||
ret = one_of("scb", *argv, values_on_off,
|
||||
ARRAY_SIZE(values_on_off), &i);
|
||||
i = parse_on_off("scb", *argv, &ret);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
scb = i;
|
||||
|
|
@ -1374,8 +1348,7 @@ static int macsec_parse_opt(struct link_util *lu, int argc, char **argv,
|
|||
NEXT_ARG();
|
||||
int i;
|
||||
|
||||
ret = one_of("protect", *argv, values_on_off,
|
||||
ARRAY_SIZE(values_on_off), &i);
|
||||
i = parse_on_off("protect", *argv, &ret);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
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();
|
||||
int i;
|
||||
|
||||
ret = one_of("replay", *argv, values_on_off,
|
||||
ARRAY_SIZE(values_on_off), &i);
|
||||
i = parse_on_off("replay", *argv, &ret);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
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);
|
||||
} else if (strcmp(*argv, "validate") == 0) {
|
||||
NEXT_ARG();
|
||||
ret = one_of("validate", *argv,
|
||||
validate_str, ARRAY_SIZE(validate_str),
|
||||
(int *)&validate);
|
||||
validate = parse_one_of("validate", *argv, validate_str,
|
||||
ARRAY_SIZE(validate_str), &ret);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
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);
|
||||
} else if (strcmp(*argv, "offload") == 0) {
|
||||
NEXT_ARG();
|
||||
ret = one_of("offload", *argv,
|
||||
offload_str, ARRAY_SIZE(offload_str),
|
||||
(int *)&offload);
|
||||
offload = parse_one_of("offload", *argv, offload_str,
|
||||
ARRAY_SIZE(offload_str), &ret);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
addattr8(n, MACSEC_BUFLEN,
|
||||
|
|
|
|||
|
|
@ -41,14 +41,6 @@ static void usage(void)
|
|||
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)
|
||||
{
|
||||
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])
|
||||
print_onoff(fp, "forwarding",
|
||||
rta_getattr_u32(tb[NETCONFA_FORWARDING]));
|
||||
print_on_off(PRINT_ANY, "forwarding", "forwarding %s ",
|
||||
rta_getattr_u32(tb[NETCONFA_FORWARDING]));
|
||||
|
||||
if (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])
|
||||
print_onoff(fp, "mc_forwarding",
|
||||
rta_getattr_u32(tb[NETCONFA_MC_FORWARDING]));
|
||||
print_on_off(PRINT_ANY, "mc_forwarding", "mc_forwarding %s ",
|
||||
rta_getattr_u32(tb[NETCONFA_MC_FORWARDING]));
|
||||
|
||||
if (tb[NETCONFA_PROXY_NEIGH])
|
||||
print_onoff(fp, "proxy_neigh",
|
||||
rta_getattr_u32(tb[NETCONFA_PROXY_NEIGH]));
|
||||
print_on_off(PRINT_ANY, "proxy_neigh", "proxy_neigh %s ",
|
||||
rta_getattr_u32(tb[NETCONFA_PROXY_NEIGH]));
|
||||
|
||||
if (tb[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN])
|
||||
print_onoff(fp, "ignore_routes_with_linkdown",
|
||||
rta_getattr_u32(tb[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]));
|
||||
print_on_off(PRINT_ANY, "ignore_routes_with_linkdown",
|
||||
"ignore_routes_with_linkdown %s ",
|
||||
rta_getattr_u32(tb[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]));
|
||||
|
||||
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();
|
||||
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)));
|
||||
}
|
||||
|
||||
if (tb[NHA_OIF])
|
||||
print_rt_flags(fp, nhm->nh_flags);
|
||||
print_rt_flags(fp, nhm->nh_flags);
|
||||
|
||||
if (tb[NHA_FDB])
|
||||
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");
|
||||
if (flags & RTNH_F_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)
|
||||
print_string(PRINT_ANY, NULL, "%s ", "notify");
|
||||
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]),
|
||||
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]) {
|
||||
print_string(PRINT_ANY, "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,
|
||||
char ***argvp)
|
||||
{
|
||||
int segs_ok = 0, hmac_ok = 0, table_ok = 0, nh4_ok = 0, nh6_ok = 0;
|
||||
int iif_ok = 0, oif_ok = 0, action_ok = 0, srh_ok = 0, bpf_ok = 0;
|
||||
__u32 action = 0, table, iif, oif;
|
||||
int segs_ok = 0, hmac_ok = 0, table_ok = 0, vrftable_ok = 0;
|
||||
int nh4_ok = 0, nh6_ok = 0, iif_ok = 0, oif_ok = 0;
|
||||
__u32 action = 0, table, vrftable, iif, oif;
|
||||
int action_ok = 0, srh_ok = 0, bpf_ok = 0;
|
||||
struct ipv6_sr_hdr *srh;
|
||||
char **argv = *argvp;
|
||||
int argc = *argcp;
|
||||
|
|
@ -887,6 +893,13 @@ static int parse_encap_seg6local(struct rtattr *rta, size_t len, int *argcp,
|
|||
duparg2("table", *argv);
|
||||
rtnl_rttable_a2n(&table, *argv);
|
||||
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) {
|
||||
NEXT_ARG();
|
||||
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)
|
||||
{
|
||||
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]));
|
||||
|
||||
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]) {
|
||||
print_onoff(f, "vnet_hdr",
|
||||
rta_getattr_u8(tb[IFLA_TUN_VNET_HDR]));
|
||||
print_on_off(PRINT_ANY, "vnet_hdr", "vnet_hdr %s ",
|
||||
rta_getattr_u8(tb[IFLA_TUN_VNET_HDR]));
|
||||
}
|
||||
|
||||
print_mq(f, tb);
|
||||
|
||||
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])
|
||||
print_owner(f, rta_getattr_u32(tb[IFLA_TUN_OWNER]));
|
||||
|
|
|
|||
|
|
@ -256,8 +256,8 @@ static int prog_load(int idx)
|
|||
BPF_EXIT_INSN(),
|
||||
};
|
||||
|
||||
return bpf_prog_load(BPF_PROG_TYPE_CGROUP_SOCK, prog, sizeof(prog),
|
||||
"GPL", bpf_log_buf, sizeof(bpf_log_buf));
|
||||
return bpf_program_load(BPF_PROG_TYPE_CGROUP_SOCK, prog, sizeof(prog),
|
||||
"GPL", bpf_log_buf, sizeof(bpf_log_buf));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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",
|
||||
strerror(errno));
|
||||
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 \
|
||||
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
|
||||
ifeq ($(HAVE_MNL),y)
|
||||
NLOBJ += mnl_utils.o
|
||||
endif
|
||||
|
||||
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)
|
||||
{
|
||||
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->section, cfg->ifindex,
|
||||
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));
|
||||
}
|
||||
|
||||
static int bpf_prog_load_dev(enum bpf_prog_type type,
|
||||
const struct bpf_insn *insns, size_t size_insns,
|
||||
const char *license, __u32 ifindex,
|
||||
char *log, size_t size_log)
|
||||
int bpf_prog_load_dev(enum bpf_prog_type type, const struct bpf_insn *insns,
|
||||
size_t size_insns, const char *license, __u32 ifindex,
|
||||
char *log, size_t size_log)
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
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
|
||||
struct bpf_elf_prog {
|
||||
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);
|
||||
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 */
|
||||
|
|
@ -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 <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "utils.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
|
||||
* branch for json and regular output. grep -r "print_bool" for example
|
||||
*/
|
||||
int print_color_bool(enum output_type type,
|
||||
enum color_attr color,
|
||||
const char *key,
|
||||
const char *fmt,
|
||||
bool value)
|
||||
static int __print_color_bool(enum output_type type,
|
||||
enum color_attr color,
|
||||
const char *key,
|
||||
const char *fmt,
|
||||
bool value,
|
||||
const char *str)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
|
|
@ -205,13 +207,32 @@ int print_color_bool(enum output_type type,
|
|||
else
|
||||
jsonw_bool(_jw, value);
|
||||
} else if (_IS_FP_CONTEXT(type)) {
|
||||
ret = color_fprintf(stdout, color, fmt,
|
||||
value ? "true" : "false");
|
||||
ret = color_fprintf(stdout, color, fmt, str);
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
|
|
@ -288,3 +309,65 @@ void print_nl(void)
|
|||
if (!_jw)
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
switch (addr->family) {
|
||||
|
|
@ -1695,3 +1809,106 @@ char *sprint_time64(__s64 time, char *buf)
|
|||
print_time64(buf, SPRINT_BSIZE-1, time);
|
||||
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
|
||||
|
||||
.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
|
||||
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
|
||||
.BI grp " GROUP"
|
||||
the IP multicast group address whose members reside on the link connected to
|
||||
the port.
|
||||
the multicast group address (IPv4, IPv6 or L2 multicast) whose members reside
|
||||
on the link connected to the port.
|
||||
|
||||
.B permanent
|
||||
- the mdb entry is permanent
|
||||
- the mdb entry is permanent. Optional for IPv4 and IPv6, mandatory for L2.
|
||||
.sp
|
||||
|
||||
.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
|
||||
.RI "{ " PID " | " NAME " | " ID " }"
|
||||
] [
|
||||
.BR action " { " driver_reinit " | " fw_activate " }"
|
||||
] [
|
||||
.B limit no_reset
|
||||
]
|
||||
|
||||
.ti -8
|
||||
|
|
@ -173,6 +177,36 @@ If this argument is omitted all parameters supported by devlink devices are list
|
|||
.RI { " PID " | " NAME " | " 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.
|
||||
Display device information provided by the driver. This command can be used
|
||||
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 mode " { " private " | " vepa " | " bridge " | " passthru
|
||||
.RB " [ " nopromisc " ] | " source " } "
|
||||
.RB " [ " bcqueuelen " { " LENGTH " } ] "
|
||||
|
||||
.in +8
|
||||
.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
|
||||
based. The feature is useful to deploy 802.1x mac based behavior,
|
||||
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
|
||||
|
||||
.TP
|
||||
|
|
@ -2451,6 +2464,26 @@ Commands:
|
|||
.sp
|
||||
.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
|
||||
|
||||
|
|
|
|||
|
|
@ -854,6 +854,12 @@ option for creating
|
|||
alias.
|
||||
.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"
|
||||
.PP
|
||||
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)
|
||||
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");
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
char *line = NULL;
|
||||
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;
|
||||
return do_batch(name, force, rd_batch_cmd, rd);
|
||||
}
|
||||
|
||||
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 newline(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);
|
||||
#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)
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -781,11 +781,6 @@ static int print_driver_string(struct rd *rd, const char *key_str,
|
|||
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,
|
||||
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_new = false;
|
||||
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 cvlan_ethtype = 0;
|
||||
__u8 ip_proto = 0xff;
|
||||
__u32 flags = 0;
|
||||
__u32 mtf = 0;
|
||||
|
|
@ -1432,7 +1432,7 @@ static int flower_parse_opt(struct filter_util *qu, char *handle,
|
|||
__u16 vid;
|
||||
|
||||
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");
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -1446,7 +1446,7 @@ static int flower_parse_opt(struct filter_util *qu, char *handle,
|
|||
__u8 vlan_prio;
|
||||
|
||||
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");
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -1464,6 +1464,8 @@ static int flower_parse_opt(struct filter_util *qu, char *handle,
|
|||
&vlan_ethtype, n);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
/* get new ethtype for later parsing */
|
||||
eth_type = vlan_ethtype;
|
||||
} else if (matches(*argv, "cvlan_id") == 0) {
|
||||
__u16 vid;
|
||||
|
||||
|
|
@ -1495,9 +1497,10 @@ static int flower_parse_opt(struct filter_util *qu, char *handle,
|
|||
TCA_FLOWER_KEY_CVLAN_PRIO, cvlan_prio);
|
||||
} else if (matches(*argv, "cvlan_ethtype") == 0) {
|
||||
NEXT_ARG();
|
||||
/* get new ethtype for later parsing */
|
||||
ret = flower_parse_vlan_eth_type(*argv, vlan_ethtype,
|
||||
TCA_FLOWER_KEY_CVLAN_ETH_TYPE,
|
||||
&cvlan_ethtype, n);
|
||||
ð_type, n);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
} 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) {
|
||||
NEXT_ARG();
|
||||
ret = flower_parse_ip_proto(*argv, cvlan_ethtype ?
|
||||
cvlan_ethtype : vlan_ethtype ?
|
||||
vlan_ethtype : eth_type,
|
||||
ret = flower_parse_ip_proto(*argv, eth_type,
|
||||
TCA_FLOWER_KEY_IP_PROTO,
|
||||
&ip_proto, n);
|
||||
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) {
|
||||
NEXT_ARG();
|
||||
ret = flower_parse_ip_addr(*argv, cvlan_ethtype ?
|
||||
cvlan_ethtype : vlan_ethtype ?
|
||||
vlan_ethtype : eth_type,
|
||||
ret = flower_parse_ip_addr(*argv, eth_type,
|
||||
TCA_FLOWER_KEY_IPV4_DST,
|
||||
TCA_FLOWER_KEY_IPV4_DST_MASK,
|
||||
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) {
|
||||
NEXT_ARG();
|
||||
ret = flower_parse_ip_addr(*argv, cvlan_ethtype ?
|
||||
cvlan_ethtype : vlan_ethtype ?
|
||||
vlan_ethtype : eth_type,
|
||||
ret = flower_parse_ip_addr(*argv, eth_type,
|
||||
TCA_FLOWER_KEY_IPV4_SRC,
|
||||
TCA_FLOWER_KEY_IPV4_SRC_MASK,
|
||||
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) {
|
||||
NEXT_ARG();
|
||||
ret = flower_parse_arp_ip_addr(*argv, vlan_ethtype ?
|
||||
vlan_ethtype : eth_type,
|
||||
TCA_FLOWER_KEY_ARP_TIP,
|
||||
TCA_FLOWER_KEY_ARP_TIP_MASK,
|
||||
n);
|
||||
ret = flower_parse_arp_ip_addr(*argv, eth_type,
|
||||
TCA_FLOWER_KEY_ARP_TIP,
|
||||
TCA_FLOWER_KEY_ARP_TIP_MASK,
|
||||
n);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Illegal \"arp_tip\"\n");
|
||||
return -1;
|
||||
}
|
||||
} else if (matches(*argv, "arp_sip") == 0) {
|
||||
NEXT_ARG();
|
||||
ret = flower_parse_arp_ip_addr(*argv, vlan_ethtype ?
|
||||
vlan_ethtype : eth_type,
|
||||
TCA_FLOWER_KEY_ARP_SIP,
|
||||
TCA_FLOWER_KEY_ARP_SIP_MASK,
|
||||
n);
|
||||
ret = flower_parse_arp_ip_addr(*argv, eth_type,
|
||||
TCA_FLOWER_KEY_ARP_SIP,
|
||||
TCA_FLOWER_KEY_ARP_SIP_MASK,
|
||||
n);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Illegal \"arp_sip\"\n");
|
||||
return -1;
|
||||
}
|
||||
} else if (matches(*argv, "arp_op") == 0) {
|
||||
NEXT_ARG();
|
||||
ret = flower_parse_arp_op(*argv, vlan_ethtype ?
|
||||
vlan_ethtype : eth_type,
|
||||
TCA_FLOWER_KEY_ARP_OP,
|
||||
TCA_FLOWER_KEY_ARP_OP_MASK,
|
||||
n);
|
||||
ret = flower_parse_arp_op(*argv, eth_type,
|
||||
TCA_FLOWER_KEY_ARP_OP,
|
||||
TCA_FLOWER_KEY_ARP_OP_MASK,
|
||||
n);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Illegal \"arp_op\"\n");
|
||||
return -1;
|
||||
|
|
@ -1894,8 +1888,8 @@ parse_done:
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (eth_type != htons(ETH_P_ALL)) {
|
||||
ret = addattr16(n, MAX_MSG, TCA_FLOWER_KEY_ETH_TYPE, eth_type);
|
||||
if (tc_proto != htons(ETH_P_ALL)) {
|
||||
ret = addattr16(n, MAX_MSG, TCA_FLOWER_KEY_ETH_TYPE, tc_proto);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -374,6 +374,11 @@ static int tc_print_one_action(FILE *f, struct rtattr *arg)
|
|||
if (err < 0)
|
||||
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]) {
|
||||
print_string(PRINT_FP, NULL, "\tAction statistics:", NULL);
|
||||
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);
|
||||
|
||||
tail3 = NLMSG_TAIL(&req.n);
|
||||
flag_select.value |= TCA_FLAG_LARGE_DUMP_ON;
|
||||
flag_select.selector |= TCA_FLAG_LARGE_DUMP_ON;
|
||||
flag_select.value |= TCA_ACT_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,
|
||||
sizeof(struct nla_bitfield32));
|
||||
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;
|
||||
int d_ok = 0;
|
||||
|
||||
print_string(PRINT_ANY, "kind", "%s ", "bpf");
|
||||
if (arg == NULL)
|
||||
return -1;
|
||||
return 0;
|
||||
|
||||
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]);
|
||||
print_string(PRINT_ANY, "kind", "%s ", "bpf");
|
||||
|
||||
if (tb[TCA_ACT_BPF_NAME])
|
||||
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 tc_connmark *ci;
|
||||
|
||||
print_string(PRINT_ANY, "kind", "%s ", "connmark");
|
||||
if (arg == NULL)
|
||||
return -1;
|
||||
return 0;
|
||||
|
||||
parse_rtattr_nested(tb, TCA_CONNMARK_MAX, arg);
|
||||
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]);
|
||||
|
||||
print_string(PRINT_ANY, "kind", "%s ", "connmark");
|
||||
print_uint(PRINT_ANY, "zone", "zone %u", ci->zone);
|
||||
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;
|
||||
|
||||
print_string(PRINT_ANY, "kind", "%s ", "csum");
|
||||
if (arg == NULL)
|
||||
return -1;
|
||||
return 0;
|
||||
|
||||
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";
|
||||
}
|
||||
|
||||
print_string(PRINT_ANY, "kind", "%s ", "csum");
|
||||
snprintf(buf, sizeof(buf), "%s%s%s%s%s%s%s",
|
||||
uflag_1, uflag_2, uflag_3,
|
||||
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;
|
||||
int ct_action = 0;
|
||||
|
||||
print_string(PRINT_ANY, "kind", "%s", "ct");
|
||||
if (arg == NULL)
|
||||
return -1;
|
||||
return 0;
|
||||
|
||||
parse_rtattr_nested(tb, TCA_CT_MAX, arg);
|
||||
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]);
|
||||
|
||||
print_string(PRINT_ANY, "kind", "%s", "ct");
|
||||
|
||||
if (tb[TCA_CT_ACTION])
|
||||
ct_action = rta_getattr_u16(tb[TCA_CT_ACTION]);
|
||||
if (ct_action & TCA_CT_ACT_COMMIT) {
|
||||
|
|
|
|||
|
|
@ -188,8 +188,9 @@ static int print_ctinfo(struct action_util *au, FILE *f, struct rtattr *arg)
|
|||
unsigned short zone = 0;
|
||||
struct tc_ctinfo *ci;
|
||||
|
||||
print_string(PRINT_ANY, "kind", "%s ", "ctinfo");
|
||||
if (arg == NULL)
|
||||
return -1;
|
||||
return 0;
|
||||
|
||||
parse_rtattr_nested(tb, TCA_CTINFO_MAX, arg);
|
||||
if (!tb[TCA_CTINFO_ACT]) {
|
||||
|
|
@ -234,7 +235,6 @@ static int print_ctinfo(struct action_util *au, FILE *f, struct rtattr *arg)
|
|||
sizeof(__u16))
|
||||
zone = rta_getattr_u16(tb[TCA_CTINFO_ZONE]);
|
||||
|
||||
print_string(PRINT_ANY, "kind", "%s ", "ctinfo");
|
||||
print_hu(PRINT_ANY, "zone", "zone %u", zone);
|
||||
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 rtattr *tb[TCA_GACT_MAX + 1];
|
||||
|
||||
print_string(PRINT_ANY, "kind", "%s ", "gact");
|
||||
if (arg == NULL)
|
||||
return -1;
|
||||
return 0;
|
||||
|
||||
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]);
|
||||
|
||||
print_string(PRINT_ANY, "kind", "%s ", "gact");
|
||||
print_action_control(f, "action ", p->action, "");
|
||||
#ifdef CONFIG_GACT_PROB
|
||||
if (tb[TCA_GACT_PROB] != NULL) {
|
||||
|
|
|
|||
|
|
@ -465,10 +465,8 @@ static int print_gate_list(struct rtattr *list)
|
|||
}
|
||||
|
||||
if (maxoctets != -1) {
|
||||
memset(buf, 0, sizeof(buf));
|
||||
print_uint(PRINT_JSON, "max_octets", NULL, maxoctets);
|
||||
print_string(PRINT_FP, NULL, "\t max-octets %s",
|
||||
sprint_size(maxoctets, buf));
|
||||
print_size(PRINT_ANY, "max_octets", "\t max-octets %s",
|
||||
maxoctets);
|
||||
} else {
|
||||
print_string(PRINT_FP, NULL,
|
||||
"\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;
|
||||
SPRINT_BUF(b2);
|
||||
|
||||
print_string(PRINT_ANY, "kind", "%s ", "ife");
|
||||
if (arg == NULL)
|
||||
return -1;
|
||||
return 0;
|
||||
|
||||
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]);
|
||||
|
||||
print_string(PRINT_ANY, "kind", "%s ", "ife");
|
||||
print_string(PRINT_ANY, "mode", "%s ",
|
||||
p->flags & IFE_ENCODE ? "encode" : "decode");
|
||||
print_action_control(f, "action ", p->action, " ");
|
||||
|
|
|
|||
|
|
@ -433,7 +433,7 @@ print_ipt(struct action_util *au, FILE * f, struct rtattr *arg)
|
|||
__u32 hook;
|
||||
|
||||
if (arg == NULL)
|
||||
return -1;
|
||||
return 0;
|
||||
|
||||
lib_dir = getenv("IPTABLES_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];
|
||||
const char *dev;
|
||||
|
||||
print_string(PRINT_ANY, "kind", "%s ", "mirred");
|
||||
if (arg == NULL)
|
||||
return -1;
|
||||
return 0;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
print_string(PRINT_ANY, "kind", "%s ", "mirred");
|
||||
print_string(PRINT_FP, NULL, "(%s", mirred_n2a(p->eaction));
|
||||
print_string(PRINT_JSON, "mirred_action", NULL,
|
||||
mirred_action(p->eaction));
|
||||
|
|
|
|||
|
|
@ -214,8 +214,9 @@ static int print_mpls(struct action_util *au, FILE *f, struct rtattr *arg)
|
|||
SPRINT_BUF(b1);
|
||||
__u32 val;
|
||||
|
||||
print_string(PRINT_ANY, "kind", "%s ", "mpls");
|
||||
if (!arg)
|
||||
return -1;
|
||||
return 0;
|
||||
|
||||
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]);
|
||||
|
||||
print_string(PRINT_ANY, "kind", "%s ", "mpls");
|
||||
print_string(PRINT_ANY, "mpls_action", " %s",
|
||||
action_names[parm->m_action]);
|
||||
|
||||
|
|
|
|||
|
|
@ -146,8 +146,9 @@ print_nat(struct action_util *au, FILE * f, struct rtattr *arg)
|
|||
SPRINT_BUF(buf2);
|
||||
int len;
|
||||
|
||||
print_string(PRINT_ANY, "type", " %s ", "nat");
|
||||
if (arg == NULL)
|
||||
return -1;
|
||||
return 0;
|
||||
|
||||
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 = len ? 33 - len : 0;
|
||||
|
||||
print_string(PRINT_ANY, "type", " %s ", "nat");
|
||||
print_string(PRINT_ANY, "direction", "%s",
|
||||
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;
|
||||
int err;
|
||||
|
||||
print_string(PRINT_ANY, "kind", " %s ", "pedit");
|
||||
if (arg == NULL)
|
||||
return -1;
|
||||
return 0;
|
||||
|
||||
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_uint(PRINT_ANY, "nkeys", "keys %d\n", sel->nkeys);
|
||||
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)
|
||||
{
|
||||
SPRINT_BUF(b1);
|
||||
SPRINT_BUF(b2);
|
||||
struct tc_police *p;
|
||||
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]);
|
||||
|
||||
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);
|
||||
fprintf(f, "burst %s ", sprint_size(buffer, b1));
|
||||
fprintf(f, "mtu %s ", sprint_size(p->mtu, b1));
|
||||
print_size(PRINT_FP, NULL, "burst %s ", buffer);
|
||||
print_size(PRINT_FP, NULL, "mtu %s ", p->mtu);
|
||||
if (show_raw)
|
||||
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]);
|
||||
|
||||
if (prate64)
|
||||
fprintf(f, "peakrate %s ", sprint_rate(prate64, b1));
|
||||
tc_print_rate(PRINT_FP, NULL, "peakrate %s ", prate64);
|
||||
|
||||
if (tb[TCA_POLICE_AVRATE])
|
||||
fprintf(f, "avrate %s ",
|
||||
sprint_rate(rta_getattr_u32(tb[TCA_POLICE_AVRATE]),
|
||||
b1));
|
||||
tc_print_rate(PRINT_FP, NULL, "avrate %s ",
|
||||
rta_getattr_u32(tb[TCA_POLICE_AVRATE]));
|
||||
|
||||
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 tc_sample *p;
|
||||
|
||||
print_string(PRINT_ANY, "kind", "%s ", "sample");
|
||||
if (arg == NULL)
|
||||
return -1;
|
||||
return 0;
|
||||
|
||||
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]);
|
||||
|
||||
print_string(PRINT_ANY, "kind", "%s ", "sample");
|
||||
print_uint(PRINT_ANY, "rate", "rate 1/%u ",
|
||||
rta_getattr_u32(tb[TCA_SAMPLE_RATE]));
|
||||
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;
|
||||
|
||||
if (arg == NULL)
|
||||
return -1;
|
||||
return 0;
|
||||
|
||||
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;
|
||||
struct tc_skbedit *p;
|
||||
|
||||
print_string(PRINT_ANY, "kind", "%s ", "skbedit");
|
||||
if (arg == NULL)
|
||||
return -1;
|
||||
return 0;
|
||||
|
||||
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]);
|
||||
|
||||
print_string(PRINT_ANY, "kind", "%s ", "skbedit");
|
||||
|
||||
if (tb[TCA_SKBEDIT_QUEUE_MAPPING] != NULL) {
|
||||
print_uint(PRINT_ANY, "queue_mapping", "queue_mapping %u",
|
||||
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);
|
||||
|
||||
if (arg == NULL)
|
||||
return -1;
|
||||
return 0;
|
||||
|
||||
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 tc_tunnel_key *parm;
|
||||
|
||||
print_string(PRINT_ANY, "kind", "%s ", "tunnel_key");
|
||||
if (!arg)
|
||||
return -1;
|
||||
return 0;
|
||||
|
||||
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]);
|
||||
|
||||
print_string(PRINT_ANY, "kind", "%s ", "tunnel_key");
|
||||
|
||||
switch (parm->t_action) {
|
||||
case TCA_TUNNEL_KEY_ACT_RELEASE:
|
||||
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;
|
||||
struct tc_vlan *parm;
|
||||
|
||||
print_string(PRINT_ANY, "kind", "%s ", "vlan");
|
||||
if (arg == NULL)
|
||||
return -1;
|
||||
return 0;
|
||||
|
||||
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]);
|
||||
|
||||
print_string(PRINT_ANY, "kind", "%s ", "vlan");
|
||||
print_string(PRINT_ANY, "vlan_action", " %s",
|
||||
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