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

This commit is contained in:
Stephen Hemminger 2020-12-24 19:29:15 -08:00
commit 2639bdc176
124 changed files with 5076 additions and 1053 deletions

View File

@ -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)

View File

@ -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;

View File

@ -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);

View File

@ -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));

113
configure vendored
View File

@ -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

1
dcb/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
dcb

24
dcb/Makefile Normal file
View File

@ -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)

468
dcb/dcb.c Normal file
View File

@ -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;
}

61
dcb/dcb.h Normal file
View File

@ -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__ */

235
dcb/dcb_buffer.c Normal file
View File

@ -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;
}
}

435
dcb/dcb_ets.c Normal file
View File

@ -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;
}
}

182
dcb/dcb_maxrate.c Normal file
View File

@ -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;
}
}

286
dcb/dcb_pfc.c Normal file
View File

@ -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;
}
}

View File

@ -12,7 +12,7 @@ endif
all: $(TARGETS) $(LIBS)
devlink: $(DEVLINKOBJ)
devlink: $(DEVLINKOBJ) $(LIBNETLINK)
$(QUIET_LINK)$(CC) $^ $(LDFLAGS) $(LDLIBS) -o $@
install: all

View File

@ -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)

View File

@ -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);

View File

@ -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.

View File

@ -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)

View File

@ -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")

View File

@ -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)

View File

@ -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,

View File

@ -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");

View File

@ -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");

View File

@ -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");

View File

@ -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

View File

@ -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

View File

@ -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__ */

View File

@ -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_ */

11
include/mnl_utils.h Normal file
View File

@ -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__ */

View File

@ -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 {

View File

@ -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)));
};
/*

View File

@ -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

View File

@ -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;

View File

@ -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 */

View File

@ -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,
};

View File

@ -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

View File

@ -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,
};

View File

@ -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 */

View File

@ -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,

View File

@ -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)

View File

@ -159,6 +159,7 @@ enum
UDP_MIB_SNDBUFERRORS, /* SndbufErrors */
UDP_MIB_CSUMERRORS, /* InCsumErrors */
UDP_MIB_IGNOREDMULTI, /* IgnoredMulti */
UDP_MIB_MEMERRORS, /* MemErrors */
__UDP_MIB_MAX
};

View File

@ -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 */

View File

@ -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,

View File

@ -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
View File

@ -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;

View File

@ -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);

View File

@ -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) {

View File

@ -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);
}

View File

@ -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)

View File

@ -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;
}

View File

@ -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,

View File

@ -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);

View File

@ -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);

View File

@ -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)

View File

@ -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++)

View File

@ -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]));

View File

@ -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;

View File

@ -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

86
lib/bpf_glue.c Normal file
View File

@ -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 */

View File

@ -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 */

348
lib/bpf_libbpf.c Normal file
View File

@ -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;
}

View File

@ -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);
}

110
lib/mnl_utils.c Normal file
View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

126
man/man8/dcb-buffer.8 Normal file
View File

@ -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>

194
man/man8/dcb-ets.8 Normal file
View File

@ -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>

94
man/man8/dcb-maxrate.8 Normal file
View File

@ -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>

127
man/man8/dcb-pfc.8 Normal file
View File

@ -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>

139
man/man8/dcb.8 Normal file
View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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)

View File

@ -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

View File

@ -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,

View File

@ -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)
{

View File

@ -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);
&eth_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;
}

View File

@ -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;

View File

@ -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 ",

View File

@ -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, "");

View File

@ -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);

View File

@ -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) {

View File

@ -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, "");

View File

@ -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) {

View File

@ -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");

View File

@ -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, " ");

View File

@ -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)

View File

@ -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));

View File

@ -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]);

View File

@ -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");

View File

@ -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);

View File

@ -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, "");

View File

@ -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",

View File

@ -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);

View File

@ -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]));

View File

@ -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);

View File

@ -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");

View File

@ -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