Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next
Pull networking updates from David Miller: "Highlights: 1) Fix the iwlwifi regression, from Johannes Berg. 2) Support BSS coloring and 802.11 encapsulation offloading in hardware, from John Crispin. 3) Fix some potential Spectre issues in qtnfmac, from Sergey Matyukevich. 4) Add TTL decrement action to openvswitch, from Matteo Croce. 5) Allow paralleization through flow_action setup by not taking the RTNL mutex, from Vlad Buslov. 6) A lot of zero-length array to flexible-array conversions, from Gustavo A. R. Silva. 7) Align XDP statistics names across several drivers for consistency, from Lorenzo Bianconi. 8) Add various pieces of infrastructure for offloading conntrack, and make use of it in mlx5 driver, from Paul Blakey. 9) Allow using listening sockets in BPF sockmap, from Jakub Sitnicki. 10) Lots of parallelization improvements during configuration changes in mlxsw driver, from Ido Schimmel. 11) Add support to devlink for generic packet traps, which report packets dropped during ACL processing. And use them in mlxsw driver. From Jiri Pirko. 12) Support bcmgenet on ACPI, from Jeremy Linton. 13) Make BPF compatible with RT, from Thomas Gleixnet, Alexei Starovoitov, and your's truly. 14) Support XDP meta-data in virtio_net, from Yuya Kusakabe. 15) Fix sysfs permissions when network devices change namespaces, from Christian Brauner. 16) Add a flags element to ethtool_ops so that drivers can more simply indicate which coalescing parameters they actually support, and therefore the generic layer can validate the user's ethtool request. Use this in all drivers, from Jakub Kicinski. 17) Offload FIFO qdisc in mlxsw, from Petr Machata. 18) Support UDP sockets in sockmap, from Lorenz Bauer. 19) Fix stretch ACK bugs in several TCP congestion control modules, from Pengcheng Yang. 20) Support virtual functiosn in octeontx2 driver, from Tomasz Duszynski. 21) Add region operations for devlink and use it in ice driver to dump NVM contents, from Jacob Keller. 22) Add support for hw offload of MACSEC, from Antoine Tenart. 23) Add support for BPF programs that can be attached to LSM hooks, from KP Singh. 24) Support for multiple paths, path managers, and counters in MPTCP. From Peter Krystad, Paolo Abeni, Florian Westphal, Davide Caratti, and others. 25) More progress on adding the netlink interface to ethtool, from Michal Kubecek" * git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (2121 commits) net: ipv6: rpl_iptunnel: Fix potential memory leak in rpl_do_srh_inline cxgb4/chcr: nic-tls stats in ethtool net: dsa: fix oops while probing Marvell DSA switches net/bpfilter: remove superfluous testing message net: macb: Fix handling of fixed-link node net: dsa: ksz: Select KSZ protocol tag netdevsim: dev: Fix memory leak in nsim_dev_take_snapshot_write net: stmmac: add EHL 2.5Gbps PCI info and PCI ID net: stmmac: add EHL PSE0 & PSE1 1Gbps PCI info and PCI ID net: stmmac: create dwmac-intel.c to contain all Intel platform net: dsa: bcm_sf2: Support specifying VLAN tag egress rule net: dsa: bcm_sf2: Add support for matching VLAN TCI net: dsa: bcm_sf2: Move writing of CFP_DATA(5) into slicing functions net: dsa: bcm_sf2: Check earlier for FLOW_EXT and FLOW_MAC_EXT net: dsa: bcm_sf2: Disable learning for ASP port net: dsa: b53: Deny enslaving port 7 for 7278 into a bridge net: dsa: b53: Prevent tagged VLAN on port 7 for 7278 net: dsa: b53: Restore VLAN entries upon (re)configuration net: dsa: bcm_sf2: Fix overflow checks hv_netvsc: Remove unnecessary round_up for recv_completion_cnt ...
This commit is contained in:
5
tools/testing/selftests/.gitignore
vendored
5
tools/testing/selftests/.gitignore
vendored
@@ -3,4 +3,7 @@ gpiogpio-hammer
|
||||
gpioinclude/
|
||||
gpiolsgpio
|
||||
tpm2/SpaceTest.log
|
||||
tpm2/*.pyc
|
||||
|
||||
# Python bytecode and cache
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
|
@@ -36,7 +36,6 @@ TARGETS += net
|
||||
TARGETS += net/forwarding
|
||||
TARGETS += net/mptcp
|
||||
TARGETS += netfilter
|
||||
TARGETS += networking/timestamping
|
||||
TARGETS += nsfs
|
||||
TARGETS += pidfd
|
||||
TARGETS += powerpc
|
||||
|
1
tools/testing/selftests/bpf/.gitignore
vendored
1
tools/testing/selftests/bpf/.gitignore
vendored
@@ -31,6 +31,7 @@ test_tcp_check_syncookie_user
|
||||
test_sysctl
|
||||
test_hashmap
|
||||
test_btf_dump
|
||||
test_current_pid_tgid_new_ns
|
||||
xdping
|
||||
test_cpp
|
||||
*.skel.h
|
||||
|
@@ -20,8 +20,9 @@ CLANG ?= clang
|
||||
LLC ?= llc
|
||||
LLVM_OBJCOPY ?= llvm-objcopy
|
||||
BPF_GCC ?= $(shell command -v bpf-gcc;)
|
||||
CFLAGS += -g -Wall -O2 $(GENFLAGS) -I$(CURDIR) -I$(APIDIR) \
|
||||
CFLAGS += -g -rdynamic -Wall -O2 $(GENFLAGS) -I$(CURDIR) \
|
||||
-I$(INCLUDE_DIR) -I$(GENDIR) -I$(LIBDIR) -I$(TOOLSINCDIR) \
|
||||
-I$(APIDIR) \
|
||||
-Dbpf_prog_load=bpf_prog_test_load \
|
||||
-Dbpf_load_program=bpf_test_load_program
|
||||
LDLIBS += -lcap -lelf -lz -lrt -lpthread
|
||||
@@ -32,7 +33,8 @@ TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test
|
||||
test_sock test_btf test_sockmap get_cgroup_id_user test_socket_cookie \
|
||||
test_cgroup_storage \
|
||||
test_netcnt test_tcpnotify_user test_sock_fields test_sysctl test_hashmap \
|
||||
test_progs-no_alu32
|
||||
test_progs-no_alu32 \
|
||||
test_current_pid_tgid_new_ns
|
||||
|
||||
# Also test bpf-gcc, if present
|
||||
ifneq ($(BPF_GCC),)
|
||||
@@ -62,7 +64,8 @@ TEST_PROGS := test_kmod.sh \
|
||||
test_tc_tunnel.sh \
|
||||
test_tc_edt.sh \
|
||||
test_xdping.sh \
|
||||
test_bpftool_build.sh
|
||||
test_bpftool_build.sh \
|
||||
test_bpftool.sh
|
||||
|
||||
TEST_PROGS_EXTENDED := with_addr.sh \
|
||||
with_tunnels.sh \
|
||||
@@ -128,10 +131,13 @@ $(OUTPUT)/test_stub.o: test_stub.c $(BPFOBJ)
|
||||
$(call msg,CC,,$@)
|
||||
$(CC) -c $(CFLAGS) -o $@ $<
|
||||
|
||||
VMLINUX_BTF_PATHS := $(abspath ../../../../vmlinux) \
|
||||
/sys/kernel/btf/vmlinux \
|
||||
/boot/vmlinux-$(shell uname -r)
|
||||
VMLINUX_BTF:= $(firstword $(wildcard $(VMLINUX_BTF_PATHS)))
|
||||
VMLINUX_BTF_PATHS := $(if $(O),$(O)/vmlinux) \
|
||||
$(if $(KBUILD_OUTPUT),$(KBUILD_OUTPUT)/vmlinux) \
|
||||
../../../../vmlinux \
|
||||
/sys/kernel/btf/vmlinux \
|
||||
/boot/vmlinux-$(shell uname -r)
|
||||
VMLINUX_BTF := $(abspath $(firstword $(wildcard $(VMLINUX_BTF_PATHS))))
|
||||
|
||||
$(OUTPUT)/runqslower: $(BPFOBJ)
|
||||
$(Q)$(MAKE) $(submake_extras) -C $(TOOLSDIR)/bpf/runqslower \
|
||||
OUTPUT=$(SCRATCH_DIR)/ VMLINUX_BTF=$(VMLINUX_BTF) \
|
||||
@@ -171,6 +177,10 @@ $(BUILD_DIR)/libbpf $(BUILD_DIR)/bpftool $(INCLUDE_DIR):
|
||||
$(call msg,MKDIR,,$@)
|
||||
mkdir -p $@
|
||||
|
||||
$(INCLUDE_DIR)/vmlinux.h: $(VMLINUX_BTF) | $(BPFTOOL) $(INCLUDE_DIR)
|
||||
$(call msg,GEN,,$@)
|
||||
$(BPFTOOL) btf dump file $(VMLINUX_BTF) format c > $@
|
||||
|
||||
# Get Clang's default includes on this system, as opposed to those seen by
|
||||
# '-target bpf'. This fixes "missing" files on some architectures/distros,
|
||||
# such as asm/byteorder.h, asm/socket.h, asm/sockios.h, sys/cdefs.h etc.
|
||||
@@ -189,8 +199,8 @@ MENDIAN=$(if $(IS_LITTLE_ENDIAN),-mlittle-endian,-mbig-endian)
|
||||
|
||||
CLANG_SYS_INCLUDES = $(call get_sys_includes,$(CLANG))
|
||||
BPF_CFLAGS = -g -D__TARGET_ARCH_$(SRCARCH) $(MENDIAN) \
|
||||
-I$(INCLUDE_DIR) -I$(CURDIR) -I$(CURDIR)/include/uapi \
|
||||
-I$(APIDIR) -I$(abspath $(OUTPUT)/../usr/include)
|
||||
-I$(INCLUDE_DIR) -I$(CURDIR) -I$(APIDIR) \
|
||||
-I$(abspath $(OUTPUT)/../usr/include)
|
||||
|
||||
CLANG_CFLAGS = $(CLANG_SYS_INCLUDES) \
|
||||
-Wno-compare-distinct-pointer-types
|
||||
@@ -209,7 +219,7 @@ define CLANG_BPF_BUILD_RULE
|
||||
$(call msg,CLNG-LLC,$(TRUNNER_BINARY),$2)
|
||||
($(CLANG) $3 -O2 -target bpf -emit-llvm \
|
||||
-c $1 -o - || echo "BPF obj compilation failed") | \
|
||||
$(LLC) -mattr=dwarfris -march=bpf -mcpu=probe $4 -filetype=obj -o $2
|
||||
$(LLC) -mattr=dwarfris -march=bpf -mcpu=v3 $4 -filetype=obj -o $2
|
||||
endef
|
||||
# Similar to CLANG_BPF_BUILD_RULE, but with disabled alu32
|
||||
define CLANG_NOALU32_BPF_BUILD_RULE
|
||||
@@ -223,7 +233,7 @@ define CLANG_NATIVE_BPF_BUILD_RULE
|
||||
$(call msg,CLNG-BPF,$(TRUNNER_BINARY),$2)
|
||||
($(CLANG) $3 -O2 -emit-llvm \
|
||||
-c $1 -o - || echo "BPF obj compilation failed") | \
|
||||
$(LLC) -march=bpf -mcpu=probe $4 -filetype=obj -o $2
|
||||
$(LLC) -march=bpf -mcpu=v3 $4 -filetype=obj -o $2
|
||||
endef
|
||||
# Build BPF object using GCC
|
||||
define GCC_BPF_BUILD_RULE
|
||||
@@ -279,6 +289,7 @@ $(TRUNNER_BPF_PROGS_DIR)$(if $2,-)$2-bpfobjs := y
|
||||
$(TRUNNER_BPF_OBJS): $(TRUNNER_OUTPUT)/%.o: \
|
||||
$(TRUNNER_BPF_PROGS_DIR)/%.c \
|
||||
$(TRUNNER_BPF_PROGS_DIR)/*.h \
|
||||
$$(INCLUDE_DIR)/vmlinux.h \
|
||||
$$(BPFOBJ) | $(TRUNNER_OUTPUT)
|
||||
$$(call $(TRUNNER_BPF_BUILD_RULE),$$<,$$@, \
|
||||
$(TRUNNER_BPF_CFLAGS), \
|
||||
|
@@ -6,7 +6,7 @@
|
||||
#include <linux/types.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_core_read.h>
|
||||
#include "bpf_trace_helpers.h"
|
||||
#include <bpf/bpf_tracing.h>
|
||||
|
||||
#define BPF_STRUCT_OPS(name, args...) \
|
||||
SEC("struct_ops/"#name) \
|
||||
|
@@ -1,120 +0,0 @@
|
||||
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
|
||||
#ifndef __BPF_TRACE_HELPERS_H
|
||||
#define __BPF_TRACE_HELPERS_H
|
||||
|
||||
#include <bpf/bpf_helpers.h>
|
||||
|
||||
#define ___bpf_concat(a, b) a ## b
|
||||
#define ___bpf_apply(fn, n) ___bpf_concat(fn, n)
|
||||
#define ___bpf_nth(_, _1, _2, _3, _4, _5, _6, _7, _8, _9, _a, _b, _c, N, ...) N
|
||||
#define ___bpf_narg(...) \
|
||||
___bpf_nth(_, ##__VA_ARGS__, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
|
||||
#define ___bpf_empty(...) \
|
||||
___bpf_nth(_, ##__VA_ARGS__, N, N, N, N, N, N, N, N, N, N, 0)
|
||||
|
||||
#define ___bpf_ctx_cast0() ctx
|
||||
#define ___bpf_ctx_cast1(x) ___bpf_ctx_cast0(), (void *)ctx[0]
|
||||
#define ___bpf_ctx_cast2(x, args...) ___bpf_ctx_cast1(args), (void *)ctx[1]
|
||||
#define ___bpf_ctx_cast3(x, args...) ___bpf_ctx_cast2(args), (void *)ctx[2]
|
||||
#define ___bpf_ctx_cast4(x, args...) ___bpf_ctx_cast3(args), (void *)ctx[3]
|
||||
#define ___bpf_ctx_cast5(x, args...) ___bpf_ctx_cast4(args), (void *)ctx[4]
|
||||
#define ___bpf_ctx_cast6(x, args...) ___bpf_ctx_cast5(args), (void *)ctx[5]
|
||||
#define ___bpf_ctx_cast7(x, args...) ___bpf_ctx_cast6(args), (void *)ctx[6]
|
||||
#define ___bpf_ctx_cast8(x, args...) ___bpf_ctx_cast7(args), (void *)ctx[7]
|
||||
#define ___bpf_ctx_cast9(x, args...) ___bpf_ctx_cast8(args), (void *)ctx[8]
|
||||
#define ___bpf_ctx_cast10(x, args...) ___bpf_ctx_cast9(args), (void *)ctx[9]
|
||||
#define ___bpf_ctx_cast11(x, args...) ___bpf_ctx_cast10(args), (void *)ctx[10]
|
||||
#define ___bpf_ctx_cast12(x, args...) ___bpf_ctx_cast11(args), (void *)ctx[11]
|
||||
#define ___bpf_ctx_cast(args...) \
|
||||
___bpf_apply(___bpf_ctx_cast, ___bpf_narg(args))(args)
|
||||
|
||||
/*
|
||||
* BPF_PROG is a convenience wrapper for generic tp_btf/fentry/fexit and
|
||||
* similar kinds of BPF programs, that accept input arguments as a single
|
||||
* pointer to untyped u64 array, where each u64 can actually be a typed
|
||||
* pointer or integer of different size. Instead of requring user to write
|
||||
* manual casts and work with array elements by index, BPF_PROG macro
|
||||
* allows user to declare a list of named and typed input arguments in the
|
||||
* same syntax as for normal C function. All the casting is hidden and
|
||||
* performed transparently, while user code can just assume working with
|
||||
* function arguments of specified type and name.
|
||||
*
|
||||
* Original raw context argument is preserved as well as 'ctx' argument.
|
||||
* This is useful when using BPF helpers that expect original context
|
||||
* as one of the parameters (e.g., for bpf_perf_event_output()).
|
||||
*/
|
||||
#define BPF_PROG(name, args...) \
|
||||
name(unsigned long long *ctx); \
|
||||
static __always_inline typeof(name(0)) \
|
||||
____##name(unsigned long long *ctx, ##args); \
|
||||
typeof(name(0)) name(unsigned long long *ctx) \
|
||||
{ \
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \
|
||||
return ____##name(___bpf_ctx_cast(args)); \
|
||||
_Pragma("GCC diagnostic pop") \
|
||||
} \
|
||||
static __always_inline typeof(name(0)) \
|
||||
____##name(unsigned long long *ctx, ##args)
|
||||
|
||||
struct pt_regs;
|
||||
|
||||
#define ___bpf_kprobe_args0() ctx
|
||||
#define ___bpf_kprobe_args1(x) \
|
||||
___bpf_kprobe_args0(), (void *)PT_REGS_PARM1(ctx)
|
||||
#define ___bpf_kprobe_args2(x, args...) \
|
||||
___bpf_kprobe_args1(args), (void *)PT_REGS_PARM2(ctx)
|
||||
#define ___bpf_kprobe_args3(x, args...) \
|
||||
___bpf_kprobe_args2(args), (void *)PT_REGS_PARM3(ctx)
|
||||
#define ___bpf_kprobe_args4(x, args...) \
|
||||
___bpf_kprobe_args3(args), (void *)PT_REGS_PARM4(ctx)
|
||||
#define ___bpf_kprobe_args5(x, args...) \
|
||||
___bpf_kprobe_args4(args), (void *)PT_REGS_PARM5(ctx)
|
||||
#define ___bpf_kprobe_args(args...) \
|
||||
___bpf_apply(___bpf_kprobe_args, ___bpf_narg(args))(args)
|
||||
|
||||
/*
|
||||
* BPF_KPROBE serves the same purpose for kprobes as BPF_PROG for
|
||||
* tp_btf/fentry/fexit BPF programs. It hides the underlying platform-specific
|
||||
* low-level way of getting kprobe input arguments from struct pt_regs, and
|
||||
* provides a familiar typed and named function arguments syntax and
|
||||
* semantics of accessing kprobe input paremeters.
|
||||
*
|
||||
* Original struct pt_regs* context is preserved as 'ctx' argument. This might
|
||||
* be necessary when using BPF helpers like bpf_perf_event_output().
|
||||
*/
|
||||
#define BPF_KPROBE(name, args...) \
|
||||
name(struct pt_regs *ctx); \
|
||||
static __always_inline typeof(name(0)) ____##name(struct pt_regs *ctx, ##args);\
|
||||
typeof(name(0)) name(struct pt_regs *ctx) \
|
||||
{ \
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \
|
||||
return ____##name(___bpf_kprobe_args(args)); \
|
||||
_Pragma("GCC diagnostic pop") \
|
||||
} \
|
||||
static __always_inline typeof(name(0)) ____##name(struct pt_regs *ctx, ##args)
|
||||
|
||||
#define ___bpf_kretprobe_args0() ctx
|
||||
#define ___bpf_kretprobe_argsN(x, args...) \
|
||||
___bpf_kprobe_args(args), (void *)PT_REGS_RET(ctx)
|
||||
#define ___bpf_kretprobe_args(args...) \
|
||||
___bpf_apply(___bpf_kretprobe_args, ___bpf_empty(args))(args)
|
||||
|
||||
/*
|
||||
* BPF_KRETPROBE is similar to BPF_KPROBE, except, in addition to listing all
|
||||
* input kprobe arguments, one last extra argument has to be specified, which
|
||||
* captures kprobe return value.
|
||||
*/
|
||||
#define BPF_KRETPROBE(name, args...) \
|
||||
name(struct pt_regs *ctx); \
|
||||
static __always_inline typeof(name(0)) ____##name(struct pt_regs *ctx, ##args);\
|
||||
typeof(name(0)) name(struct pt_regs *ctx) \
|
||||
{ \
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \
|
||||
return ____##name(___bpf_kretprobe_args(args)); \
|
||||
_Pragma("GCC diagnostic pop") \
|
||||
} \
|
||||
static __always_inline typeof(name(0)) ____##name(struct pt_regs *ctx, ##args)
|
||||
#endif
|
@@ -35,3 +35,5 @@ CONFIG_MPLS_ROUTING=m
|
||||
CONFIG_MPLS_IPTUNNEL=m
|
||||
CONFIG_IPV6_SIT=m
|
||||
CONFIG_BPF_JIT=y
|
||||
CONFIG_BPF_LSM=y
|
||||
CONFIG_SECURITY=y
|
||||
|
@@ -1,23 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _UAPI_LINUX_TYPES_H
|
||||
#define _UAPI_LINUX_TYPES_H
|
||||
|
||||
#include <asm-generic/int-ll64.h>
|
||||
|
||||
/* copied from linux:include/uapi/linux/types.h */
|
||||
#define __bitwise
|
||||
typedef __u16 __bitwise __le16;
|
||||
typedef __u16 __bitwise __be16;
|
||||
typedef __u32 __bitwise __le32;
|
||||
typedef __u32 __bitwise __be32;
|
||||
typedef __u64 __bitwise __le64;
|
||||
typedef __u64 __bitwise __be64;
|
||||
|
||||
typedef __u16 __bitwise __sum16;
|
||||
typedef __u32 __bitwise __wsum;
|
||||
|
||||
#define __aligned_u64 __u64 __attribute__((aligned(8)))
|
||||
#define __aligned_be64 __be64 __attribute__((aligned(8)))
|
||||
#define __aligned_le64 __le64 __attribute__((aligned(8)))
|
||||
|
||||
#endif /* _UAPI_LINUX_TYPES_H */
|
@@ -11,6 +11,7 @@
|
||||
static const unsigned int total_bytes = 10 * 1024 * 1024;
|
||||
static const struct timeval timeo_sec = { .tv_sec = 10 };
|
||||
static const size_t timeo_optlen = sizeof(timeo_sec);
|
||||
static int expected_stg = 0xeB9F;
|
||||
static int stop, duration;
|
||||
|
||||
static int settimeo(int fd)
|
||||
@@ -88,7 +89,7 @@ done:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void do_test(const char *tcp_ca)
|
||||
static void do_test(const char *tcp_ca, const struct bpf_map *sk_stg_map)
|
||||
{
|
||||
struct sockaddr_in6 sa6 = {};
|
||||
ssize_t nr_recv = 0, bytes = 0;
|
||||
@@ -126,14 +127,34 @@ static void do_test(const char *tcp_ca)
|
||||
err = listen(lfd, 1);
|
||||
if (CHECK(err == -1, "listen", "errno:%d\n", errno))
|
||||
goto done;
|
||||
err = pthread_create(&srv_thread, NULL, server, (void *)(long)lfd);
|
||||
if (CHECK(err != 0, "pthread_create", "err:%d\n", err))
|
||||
goto done;
|
||||
|
||||
if (sk_stg_map) {
|
||||
err = bpf_map_update_elem(bpf_map__fd(sk_stg_map), &fd,
|
||||
&expected_stg, BPF_NOEXIST);
|
||||
if (CHECK(err, "bpf_map_update_elem(sk_stg_map)",
|
||||
"err:%d errno:%d\n", err, errno))
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* connect to server */
|
||||
err = connect(fd, (struct sockaddr *)&sa6, addrlen);
|
||||
if (CHECK(err == -1, "connect", "errno:%d\n", errno))
|
||||
goto wait_thread;
|
||||
goto done;
|
||||
|
||||
if (sk_stg_map) {
|
||||
int tmp_stg;
|
||||
|
||||
err = bpf_map_lookup_elem(bpf_map__fd(sk_stg_map), &fd,
|
||||
&tmp_stg);
|
||||
if (CHECK(!err || errno != ENOENT,
|
||||
"bpf_map_lookup_elem(sk_stg_map)",
|
||||
"err:%d errno:%d\n", err, errno))
|
||||
goto done;
|
||||
}
|
||||
|
||||
err = pthread_create(&srv_thread, NULL, server, (void *)(long)lfd);
|
||||
if (CHECK(err != 0, "pthread_create", "err:%d errno:%d\n", err, errno))
|
||||
goto done;
|
||||
|
||||
/* recv total_bytes */
|
||||
while (bytes < total_bytes && !READ_ONCE(stop)) {
|
||||
@@ -149,7 +170,6 @@ static void do_test(const char *tcp_ca)
|
||||
CHECK(bytes != total_bytes, "recv", "%zd != %u nr_recv:%zd errno:%d\n",
|
||||
bytes, total_bytes, nr_recv, errno);
|
||||
|
||||
wait_thread:
|
||||
WRITE_ONCE(stop, 1);
|
||||
pthread_join(srv_thread, &thread_ret);
|
||||
CHECK(IS_ERR(thread_ret), "pthread_join", "thread_ret:%ld",
|
||||
@@ -175,7 +195,7 @@ static void test_cubic(void)
|
||||
return;
|
||||
}
|
||||
|
||||
do_test("bpf_cubic");
|
||||
do_test("bpf_cubic", NULL);
|
||||
|
||||
bpf_link__destroy(link);
|
||||
bpf_cubic__destroy(cubic_skel);
|
||||
@@ -197,7 +217,10 @@ static void test_dctcp(void)
|
||||
return;
|
||||
}
|
||||
|
||||
do_test("bpf_dctcp");
|
||||
do_test("bpf_dctcp", dctcp_skel->maps.sk_stg_map);
|
||||
CHECK(dctcp_skel->bss->stg_result != expected_stg,
|
||||
"Unexpected stg_result", "stg_result (%x) != expected_stg (%x)\n",
|
||||
dctcp_skel->bss->stg_result, expected_stg);
|
||||
|
||||
bpf_link__destroy(link);
|
||||
bpf_dctcp__destroy(dctcp_skel);
|
||||
|
@@ -125,6 +125,6 @@ void test_btf_dump() {
|
||||
if (!test__start_subtest(t->name))
|
||||
continue;
|
||||
|
||||
test_btf_dump_case(i, &btf_dump_test_cases[i]);
|
||||
test_btf_dump_case(i, &btf_dump_test_cases[i]);
|
||||
}
|
||||
}
|
||||
|
@@ -6,7 +6,7 @@
|
||||
|
||||
#define PING_CMD "ping -q -c1 -w1 127.0.0.1 > /dev/null"
|
||||
|
||||
char bpf_log_buf[BPF_LOG_BUF_SIZE];
|
||||
static char bpf_log_buf[BPF_LOG_BUF_SIZE];
|
||||
|
||||
static int prog_load(void)
|
||||
{
|
||||
|
@@ -6,7 +6,7 @@
|
||||
|
||||
#define PING_CMD "ping -q -c1 -w1 127.0.0.1 > /dev/null"
|
||||
|
||||
char bpf_log_buf[BPF_LOG_BUF_SIZE];
|
||||
static char bpf_log_buf[BPF_LOG_BUF_SIZE];
|
||||
|
||||
static int map_fd = -1;
|
||||
|
||||
|
@@ -8,7 +8,7 @@
|
||||
#define BAR "/foo/bar/"
|
||||
#define PING_CMD "ping -q -c1 -w1 127.0.0.1 > /dev/null"
|
||||
|
||||
char bpf_log_buf[BPF_LOG_BUF_SIZE];
|
||||
static char bpf_log_buf[BPF_LOG_BUF_SIZE];
|
||||
|
||||
static int prog_load(int verdict)
|
||||
{
|
||||
|
244
tools/testing/selftests/bpf/prog_tests/cgroup_link.c
Normal file
244
tools/testing/selftests/bpf/prog_tests/cgroup_link.c
Normal file
@@ -0,0 +1,244 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <test_progs.h>
|
||||
#include "cgroup_helpers.h"
|
||||
#include "test_cgroup_link.skel.h"
|
||||
|
||||
static __u32 duration = 0;
|
||||
#define PING_CMD "ping -q -c1 -w1 127.0.0.1 > /dev/null"
|
||||
|
||||
static struct test_cgroup_link *skel = NULL;
|
||||
|
||||
int ping_and_check(int exp_calls, int exp_alt_calls)
|
||||
{
|
||||
skel->bss->calls = 0;
|
||||
skel->bss->alt_calls = 0;
|
||||
CHECK_FAIL(system(PING_CMD));
|
||||
if (CHECK(skel->bss->calls != exp_calls, "call_cnt",
|
||||
"exp %d, got %d\n", exp_calls, skel->bss->calls))
|
||||
return -EINVAL;
|
||||
if (CHECK(skel->bss->alt_calls != exp_alt_calls, "alt_call_cnt",
|
||||
"exp %d, got %d\n", exp_alt_calls, skel->bss->alt_calls))
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void test_cgroup_link(void)
|
||||
{
|
||||
struct {
|
||||
const char *path;
|
||||
int fd;
|
||||
} cgs[] = {
|
||||
{ "/cg1" },
|
||||
{ "/cg1/cg2" },
|
||||
{ "/cg1/cg2/cg3" },
|
||||
{ "/cg1/cg2/cg3/cg4" },
|
||||
};
|
||||
int last_cg = ARRAY_SIZE(cgs) - 1, cg_nr = ARRAY_SIZE(cgs);
|
||||
DECLARE_LIBBPF_OPTS(bpf_link_update_opts, link_upd_opts);
|
||||
struct bpf_link *links[ARRAY_SIZE(cgs)] = {}, *tmp_link;
|
||||
__u32 prog_ids[ARRAY_SIZE(cgs)], prog_cnt = 0, attach_flags;
|
||||
int i = 0, err, prog_fd;
|
||||
bool detach_legacy = false;
|
||||
|
||||
skel = test_cgroup_link__open_and_load();
|
||||
if (CHECK(!skel, "skel_open_load", "failed to open/load skeleton\n"))
|
||||
return;
|
||||
prog_fd = bpf_program__fd(skel->progs.egress);
|
||||
|
||||
err = setup_cgroup_environment();
|
||||
if (CHECK(err, "cg_init", "failed: %d\n", err))
|
||||
goto cleanup;
|
||||
|
||||
for (i = 0; i < cg_nr; i++) {
|
||||
cgs[i].fd = create_and_get_cgroup(cgs[i].path);
|
||||
if (CHECK(cgs[i].fd < 0, "cg_create", "fail: %d\n", cgs[i].fd))
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
err = join_cgroup(cgs[last_cg].path);
|
||||
if (CHECK(err, "cg_join", "fail: %d\n", err))
|
||||
goto cleanup;
|
||||
|
||||
for (i = 0; i < cg_nr; i++) {
|
||||
links[i] = bpf_program__attach_cgroup(skel->progs.egress,
|
||||
cgs[i].fd);
|
||||
if (CHECK(IS_ERR(links[i]), "cg_attach", "i: %d, err: %ld\n",
|
||||
i, PTR_ERR(links[i])))
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ping_and_check(cg_nr, 0);
|
||||
|
||||
/* query the number of effective progs and attach flags in root cg */
|
||||
err = bpf_prog_query(cgs[0].fd, BPF_CGROUP_INET_EGRESS,
|
||||
BPF_F_QUERY_EFFECTIVE, &attach_flags, NULL,
|
||||
&prog_cnt);
|
||||
CHECK_FAIL(err);
|
||||
CHECK_FAIL(attach_flags != BPF_F_ALLOW_MULTI);
|
||||
if (CHECK(prog_cnt != 1, "effect_cnt", "exp %d, got %d\n", 1, prog_cnt))
|
||||
goto cleanup;
|
||||
|
||||
/* query the number of effective progs in last cg */
|
||||
err = bpf_prog_query(cgs[last_cg].fd, BPF_CGROUP_INET_EGRESS,
|
||||
BPF_F_QUERY_EFFECTIVE, NULL, NULL,
|
||||
&prog_cnt);
|
||||
CHECK_FAIL(err);
|
||||
CHECK_FAIL(attach_flags != BPF_F_ALLOW_MULTI);
|
||||
if (CHECK(prog_cnt != cg_nr, "effect_cnt", "exp %d, got %d\n",
|
||||
cg_nr, prog_cnt))
|
||||
goto cleanup;
|
||||
|
||||
/* query the effective prog IDs in last cg */
|
||||
err = bpf_prog_query(cgs[last_cg].fd, BPF_CGROUP_INET_EGRESS,
|
||||
BPF_F_QUERY_EFFECTIVE, &attach_flags,
|
||||
prog_ids, &prog_cnt);
|
||||
CHECK_FAIL(err);
|
||||
CHECK_FAIL(attach_flags != BPF_F_ALLOW_MULTI);
|
||||
if (CHECK(prog_cnt != cg_nr, "effect_cnt", "exp %d, got %d\n",
|
||||
cg_nr, prog_cnt))
|
||||
goto cleanup;
|
||||
for (i = 1; i < prog_cnt; i++) {
|
||||
CHECK(prog_ids[i - 1] != prog_ids[i], "prog_id_check",
|
||||
"idx %d, prev id %d, cur id %d\n",
|
||||
i, prog_ids[i - 1], prog_ids[i]);
|
||||
}
|
||||
|
||||
/* detach bottom program and ping again */
|
||||
bpf_link__destroy(links[last_cg]);
|
||||
links[last_cg] = NULL;
|
||||
|
||||
ping_and_check(cg_nr - 1, 0);
|
||||
|
||||
/* mix in with non link-based multi-attachments */
|
||||
err = bpf_prog_attach(prog_fd, cgs[last_cg].fd,
|
||||
BPF_CGROUP_INET_EGRESS, BPF_F_ALLOW_MULTI);
|
||||
if (CHECK(err, "cg_attach_legacy", "errno=%d\n", errno))
|
||||
goto cleanup;
|
||||
detach_legacy = true;
|
||||
|
||||
links[last_cg] = bpf_program__attach_cgroup(skel->progs.egress,
|
||||
cgs[last_cg].fd);
|
||||
if (CHECK(IS_ERR(links[last_cg]), "cg_attach", "err: %ld\n",
|
||||
PTR_ERR(links[last_cg])))
|
||||
goto cleanup;
|
||||
|
||||
ping_and_check(cg_nr + 1, 0);
|
||||
|
||||
/* detach link */
|
||||
bpf_link__destroy(links[last_cg]);
|
||||
links[last_cg] = NULL;
|
||||
|
||||
/* detach legacy */
|
||||
err = bpf_prog_detach2(prog_fd, cgs[last_cg].fd, BPF_CGROUP_INET_EGRESS);
|
||||
if (CHECK(err, "cg_detach_legacy", "errno=%d\n", errno))
|
||||
goto cleanup;
|
||||
detach_legacy = false;
|
||||
|
||||
/* attach legacy exclusive prog attachment */
|
||||
err = bpf_prog_attach(prog_fd, cgs[last_cg].fd,
|
||||
BPF_CGROUP_INET_EGRESS, 0);
|
||||
if (CHECK(err, "cg_attach_exclusive", "errno=%d\n", errno))
|
||||
goto cleanup;
|
||||
detach_legacy = true;
|
||||
|
||||
/* attempt to mix in with multi-attach bpf_link */
|
||||
tmp_link = bpf_program__attach_cgroup(skel->progs.egress,
|
||||
cgs[last_cg].fd);
|
||||
if (CHECK(!IS_ERR(tmp_link), "cg_attach_fail", "unexpected success!\n")) {
|
||||
bpf_link__destroy(tmp_link);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ping_and_check(cg_nr, 0);
|
||||
|
||||
/* detach */
|
||||
err = bpf_prog_detach2(prog_fd, cgs[last_cg].fd, BPF_CGROUP_INET_EGRESS);
|
||||
if (CHECK(err, "cg_detach_legacy", "errno=%d\n", errno))
|
||||
goto cleanup;
|
||||
detach_legacy = false;
|
||||
|
||||
ping_and_check(cg_nr - 1, 0);
|
||||
|
||||
/* attach back link-based one */
|
||||
links[last_cg] = bpf_program__attach_cgroup(skel->progs.egress,
|
||||
cgs[last_cg].fd);
|
||||
if (CHECK(IS_ERR(links[last_cg]), "cg_attach", "err: %ld\n",
|
||||
PTR_ERR(links[last_cg])))
|
||||
goto cleanup;
|
||||
|
||||
ping_and_check(cg_nr, 0);
|
||||
|
||||
/* check legacy exclusive prog can't be attached */
|
||||
err = bpf_prog_attach(prog_fd, cgs[last_cg].fd,
|
||||
BPF_CGROUP_INET_EGRESS, 0);
|
||||
if (CHECK(!err, "cg_attach_exclusive", "unexpected success")) {
|
||||
bpf_prog_detach2(prog_fd, cgs[last_cg].fd, BPF_CGROUP_INET_EGRESS);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* replace BPF programs inside their links for all but first link */
|
||||
for (i = 1; i < cg_nr; i++) {
|
||||
err = bpf_link__update_program(links[i], skel->progs.egress_alt);
|
||||
if (CHECK(err, "prog_upd", "link #%d\n", i))
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ping_and_check(1, cg_nr - 1);
|
||||
|
||||
/* Attempt program update with wrong expected BPF program */
|
||||
link_upd_opts.old_prog_fd = bpf_program__fd(skel->progs.egress_alt);
|
||||
link_upd_opts.flags = BPF_F_REPLACE;
|
||||
err = bpf_link_update(bpf_link__fd(links[0]),
|
||||
bpf_program__fd(skel->progs.egress_alt),
|
||||
&link_upd_opts);
|
||||
if (CHECK(err == 0 || errno != EPERM, "prog_cmpxchg1",
|
||||
"unexpectedly succeeded, err %d, errno %d\n", err, -errno))
|
||||
goto cleanup;
|
||||
|
||||
/* Compare-exchange single link program from egress to egress_alt */
|
||||
link_upd_opts.old_prog_fd = bpf_program__fd(skel->progs.egress);
|
||||
link_upd_opts.flags = BPF_F_REPLACE;
|
||||
err = bpf_link_update(bpf_link__fd(links[0]),
|
||||
bpf_program__fd(skel->progs.egress_alt),
|
||||
&link_upd_opts);
|
||||
if (CHECK(err, "prog_cmpxchg2", "errno %d\n", -errno))
|
||||
goto cleanup;
|
||||
|
||||
/* ping */
|
||||
ping_and_check(0, cg_nr);
|
||||
|
||||
/* close cgroup FDs before detaching links */
|
||||
for (i = 0; i < cg_nr; i++) {
|
||||
if (cgs[i].fd > 0) {
|
||||
close(cgs[i].fd);
|
||||
cgs[i].fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* BPF programs should still get called */
|
||||
ping_and_check(0, cg_nr);
|
||||
|
||||
/* leave cgroup and remove them, don't detach programs */
|
||||
cleanup_cgroup_environment();
|
||||
|
||||
/* BPF programs should have been auto-detached */
|
||||
ping_and_check(0, 0);
|
||||
|
||||
cleanup:
|
||||
if (detach_legacy)
|
||||
bpf_prog_detach2(prog_fd, cgs[last_cg].fd,
|
||||
BPF_CGROUP_INET_EGRESS);
|
||||
|
||||
for (i = 0; i < cg_nr; i++) {
|
||||
if (!IS_ERR(links[i]))
|
||||
bpf_link__destroy(links[i]);
|
||||
}
|
||||
test_cgroup_link__destroy(skel);
|
||||
|
||||
for (i = 0; i < cg_nr; i++) {
|
||||
if (cgs[i].fd > 0)
|
||||
close(cgs[i].fd);
|
||||
}
|
||||
cleanup_cgroup_environment();
|
||||
}
|
@@ -1,22 +1,17 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2019 Facebook */
|
||||
#include <test_progs.h>
|
||||
#include "test_pkt_access.skel.h"
|
||||
#include "fentry_test.skel.h"
|
||||
#include "fexit_test.skel.h"
|
||||
|
||||
void test_fentry_fexit(void)
|
||||
{
|
||||
struct test_pkt_access *pkt_skel = NULL;
|
||||
struct fentry_test *fentry_skel = NULL;
|
||||
struct fexit_test *fexit_skel = NULL;
|
||||
__u64 *fentry_res, *fexit_res;
|
||||
__u32 duration = 0, retval;
|
||||
int err, pkt_fd, i;
|
||||
int err, prog_fd, i;
|
||||
|
||||
pkt_skel = test_pkt_access__open_and_load();
|
||||
if (CHECK(!pkt_skel, "pkt_skel_load", "pkt_access skeleton failed\n"))
|
||||
return;
|
||||
fentry_skel = fentry_test__open_and_load();
|
||||
if (CHECK(!fentry_skel, "fentry_skel_load", "fentry skeleton failed\n"))
|
||||
goto close_prog;
|
||||
@@ -31,8 +26,8 @@ void test_fentry_fexit(void)
|
||||
if (CHECK(err, "fexit_attach", "fexit attach failed: %d\n", err))
|
||||
goto close_prog;
|
||||
|
||||
pkt_fd = bpf_program__fd(pkt_skel->progs.test_pkt_access);
|
||||
err = bpf_prog_test_run(pkt_fd, 1, &pkt_v6, sizeof(pkt_v6),
|
||||
prog_fd = bpf_program__fd(fexit_skel->progs.test1);
|
||||
err = bpf_prog_test_run(prog_fd, 1, NULL, 0,
|
||||
NULL, NULL, &retval, &duration);
|
||||
CHECK(err || retval, "ipv6",
|
||||
"err %d errno %d retval %d duration %d\n",
|
||||
@@ -49,7 +44,6 @@ void test_fentry_fexit(void)
|
||||
}
|
||||
|
||||
close_prog:
|
||||
test_pkt_access__destroy(pkt_skel);
|
||||
fentry_test__destroy(fentry_skel);
|
||||
fexit_test__destroy(fexit_skel);
|
||||
}
|
||||
|
@@ -1,20 +1,15 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2019 Facebook */
|
||||
#include <test_progs.h>
|
||||
#include "test_pkt_access.skel.h"
|
||||
#include "fentry_test.skel.h"
|
||||
|
||||
void test_fentry_test(void)
|
||||
{
|
||||
struct test_pkt_access *pkt_skel = NULL;
|
||||
struct fentry_test *fentry_skel = NULL;
|
||||
int err, pkt_fd, i;
|
||||
int err, prog_fd, i;
|
||||
__u32 duration = 0, retval;
|
||||
__u64 *result;
|
||||
|
||||
pkt_skel = test_pkt_access__open_and_load();
|
||||
if (CHECK(!pkt_skel, "pkt_skel_load", "pkt_access skeleton failed\n"))
|
||||
return;
|
||||
fentry_skel = fentry_test__open_and_load();
|
||||
if (CHECK(!fentry_skel, "fentry_skel_load", "fentry skeleton failed\n"))
|
||||
goto cleanup;
|
||||
@@ -23,10 +18,10 @@ void test_fentry_test(void)
|
||||
if (CHECK(err, "fentry_attach", "fentry attach failed: %d\n", err))
|
||||
goto cleanup;
|
||||
|
||||
pkt_fd = bpf_program__fd(pkt_skel->progs.test_pkt_access);
|
||||
err = bpf_prog_test_run(pkt_fd, 1, &pkt_v6, sizeof(pkt_v6),
|
||||
prog_fd = bpf_program__fd(fentry_skel->progs.test1);
|
||||
err = bpf_prog_test_run(prog_fd, 1, NULL, 0,
|
||||
NULL, NULL, &retval, &duration);
|
||||
CHECK(err || retval, "ipv6",
|
||||
CHECK(err || retval, "test_run",
|
||||
"err %d errno %d retval %d duration %d\n",
|
||||
err, errno, retval, duration);
|
||||
|
||||
@@ -39,5 +34,4 @@ void test_fentry_test(void)
|
||||
|
||||
cleanup:
|
||||
fentry_test__destroy(fentry_skel);
|
||||
test_pkt_access__destroy(pkt_skel);
|
||||
}
|
||||
|
@@ -1,64 +1,37 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2019 Facebook */
|
||||
#include <test_progs.h>
|
||||
#include "fexit_test.skel.h"
|
||||
|
||||
void test_fexit_test(void)
|
||||
{
|
||||
struct bpf_prog_load_attr attr = {
|
||||
.file = "./fexit_test.o",
|
||||
};
|
||||
|
||||
char prog_name[] = "fexit/bpf_fentry_testX";
|
||||
struct bpf_object *obj = NULL, *pkt_obj;
|
||||
int err, pkt_fd, kfree_skb_fd, i;
|
||||
struct bpf_link *link[6] = {};
|
||||
struct bpf_program *prog[6];
|
||||
struct fexit_test *fexit_skel = NULL;
|
||||
int err, prog_fd, i;
|
||||
__u32 duration = 0, retval;
|
||||
struct bpf_map *data_map;
|
||||
const int zero = 0;
|
||||
u64 result[6];
|
||||
__u64 *result;
|
||||
|
||||
err = bpf_prog_load("./test_pkt_access.o", BPF_PROG_TYPE_SCHED_CLS,
|
||||
&pkt_obj, &pkt_fd);
|
||||
if (CHECK(err, "prog_load sched cls", "err %d errno %d\n", err, errno))
|
||||
return;
|
||||
err = bpf_prog_load_xattr(&attr, &obj, &kfree_skb_fd);
|
||||
if (CHECK(err, "prog_load fail", "err %d errno %d\n", err, errno))
|
||||
goto close_prog;
|
||||
fexit_skel = fexit_test__open_and_load();
|
||||
if (CHECK(!fexit_skel, "fexit_skel_load", "fexit skeleton failed\n"))
|
||||
goto cleanup;
|
||||
|
||||
for (i = 0; i < 6; i++) {
|
||||
prog_name[sizeof(prog_name) - 2] = '1' + i;
|
||||
prog[i] = bpf_object__find_program_by_title(obj, prog_name);
|
||||
if (CHECK(!prog[i], "find_prog", "prog %s not found\n", prog_name))
|
||||
goto close_prog;
|
||||
link[i] = bpf_program__attach_trace(prog[i]);
|
||||
if (CHECK(IS_ERR(link[i]), "attach_trace", "failed to link\n"))
|
||||
goto close_prog;
|
||||
}
|
||||
data_map = bpf_object__find_map_by_name(obj, "fexit_te.bss");
|
||||
if (CHECK(!data_map, "find_data_map", "data map not found\n"))
|
||||
goto close_prog;
|
||||
err = fexit_test__attach(fexit_skel);
|
||||
if (CHECK(err, "fexit_attach", "fexit attach failed: %d\n", err))
|
||||
goto cleanup;
|
||||
|
||||
err = bpf_prog_test_run(pkt_fd, 1, &pkt_v6, sizeof(pkt_v6),
|
||||
prog_fd = bpf_program__fd(fexit_skel->progs.test1);
|
||||
err = bpf_prog_test_run(prog_fd, 1, NULL, 0,
|
||||
NULL, NULL, &retval, &duration);
|
||||
CHECK(err || retval, "ipv6",
|
||||
CHECK(err || retval, "test_run",
|
||||
"err %d errno %d retval %d duration %d\n",
|
||||
err, errno, retval, duration);
|
||||
|
||||
err = bpf_map_lookup_elem(bpf_map__fd(data_map), &zero, &result);
|
||||
if (CHECK(err, "get_result",
|
||||
"failed to get output data: %d\n", err))
|
||||
goto close_prog;
|
||||
result = (__u64 *)fexit_skel->bss;
|
||||
for (i = 0; i < 6; i++) {
|
||||
if (CHECK(result[i] != 1, "result",
|
||||
"fexit_test%d failed err %lld\n", i + 1, result[i]))
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
for (i = 0; i < 6; i++)
|
||||
if (CHECK(result[i] != 1, "result", "bpf_fentry_test%d failed err %ld\n",
|
||||
i + 1, result[i]))
|
||||
goto close_prog;
|
||||
|
||||
close_prog:
|
||||
for (i = 0; i < 6; i++)
|
||||
if (!IS_ERR_OR_NULL(link[i]))
|
||||
bpf_link__destroy(link[i]);
|
||||
bpf_object__close(obj);
|
||||
bpf_object__close(pkt_obj);
|
||||
cleanup:
|
||||
fexit_test__destroy(fexit_skel);
|
||||
}
|
||||
|
@@ -82,6 +82,7 @@ static void get_stack_print_output(void *ctx, int cpu, void *data, __u32 size)
|
||||
void test_get_stack_raw_tp(void)
|
||||
{
|
||||
const char *file = "./test_get_stack_rawtp.o";
|
||||
const char *file_err = "./test_get_stack_rawtp_err.o";
|
||||
const char *prog_name = "raw_tracepoint/sys_enter";
|
||||
int i, err, prog_fd, exp_cnt = MAX_CNT_RAWTP;
|
||||
struct perf_buffer_opts pb_opts = {};
|
||||
@@ -93,6 +94,10 @@ void test_get_stack_raw_tp(void)
|
||||
struct bpf_map *map;
|
||||
cpu_set_t cpu_set;
|
||||
|
||||
err = bpf_prog_load(file_err, BPF_PROG_TYPE_RAW_TRACEPOINT, &obj, &prog_fd);
|
||||
if (CHECK(err >= 0, "prog_load raw tp", "err %d errno %d\n", err, errno))
|
||||
return;
|
||||
|
||||
err = bpf_prog_load(file, BPF_PROG_TYPE_RAW_TRACEPOINT, &obj, &prog_fd);
|
||||
if (CHECK(err, "prog_load raw tp", "err %d errno %d\n", err, errno))
|
||||
return;
|
||||
|
61
tools/testing/selftests/bpf/prog_tests/global_data_init.c
Normal file
61
tools/testing/selftests/bpf/prog_tests/global_data_init.c
Normal file
@@ -0,0 +1,61 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <test_progs.h>
|
||||
|
||||
void test_global_data_init(void)
|
||||
{
|
||||
const char *file = "./test_global_data.o";
|
||||
int err = -ENOMEM, map_fd, zero = 0;
|
||||
__u8 *buff = NULL, *newval = NULL;
|
||||
struct bpf_object *obj;
|
||||
struct bpf_map *map;
|
||||
__u32 duration = 0;
|
||||
size_t sz;
|
||||
|
||||
obj = bpf_object__open_file(file, NULL);
|
||||
if (CHECK_FAIL(!obj))
|
||||
return;
|
||||
|
||||
map = bpf_object__find_map_by_name(obj, "test_glo.rodata");
|
||||
if (CHECK_FAIL(!map || !bpf_map__is_internal(map)))
|
||||
goto out;
|
||||
|
||||
sz = bpf_map__def(map)->value_size;
|
||||
newval = malloc(sz);
|
||||
if (CHECK_FAIL(!newval))
|
||||
goto out;
|
||||
|
||||
memset(newval, 0, sz);
|
||||
/* wrong size, should fail */
|
||||
err = bpf_map__set_initial_value(map, newval, sz - 1);
|
||||
if (CHECK(!err, "reject set initial value wrong size", "err %d\n", err))
|
||||
goto out;
|
||||
|
||||
err = bpf_map__set_initial_value(map, newval, sz);
|
||||
if (CHECK(err, "set initial value", "err %d\n", err))
|
||||
goto out;
|
||||
|
||||
err = bpf_object__load(obj);
|
||||
if (CHECK_FAIL(err))
|
||||
goto out;
|
||||
|
||||
map_fd = bpf_map__fd(map);
|
||||
if (CHECK_FAIL(map_fd < 0))
|
||||
goto out;
|
||||
|
||||
buff = malloc(sz);
|
||||
if (buff)
|
||||
err = bpf_map_lookup_elem(map_fd, &zero, buff);
|
||||
if (CHECK(!buff || err || memcmp(buff, newval, sz),
|
||||
"compare .rodata map data override",
|
||||
"err %d errno %d\n", err, errno))
|
||||
goto out;
|
||||
|
||||
memset(newval, 1, sz);
|
||||
/* object loaded - should fail */
|
||||
err = bpf_map__set_initial_value(map, newval, sz);
|
||||
CHECK(!err, "reject set initial value after load", "err %d\n", err);
|
||||
out:
|
||||
free(buff);
|
||||
free(newval);
|
||||
bpf_object__close(obj);
|
||||
}
|
105
tools/testing/selftests/bpf/prog_tests/link_pinning.c
Normal file
105
tools/testing/selftests/bpf/prog_tests/link_pinning.c
Normal file
@@ -0,0 +1,105 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2020 Facebook */
|
||||
|
||||
#include <test_progs.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "test_link_pinning.skel.h"
|
||||
|
||||
static int duration = 0;
|
||||
|
||||
void test_link_pinning_subtest(struct bpf_program *prog,
|
||||
struct test_link_pinning__bss *bss)
|
||||
{
|
||||
const char *link_pin_path = "/sys/fs/bpf/pinned_link_test";
|
||||
struct stat statbuf = {};
|
||||
struct bpf_link *link;
|
||||
int err, i;
|
||||
|
||||
link = bpf_program__attach(prog);
|
||||
if (CHECK(IS_ERR(link), "link_attach", "err: %ld\n", PTR_ERR(link)))
|
||||
goto cleanup;
|
||||
|
||||
bss->in = 1;
|
||||
usleep(1);
|
||||
CHECK(bss->out != 1, "res_check1", "exp %d, got %d\n", 1, bss->out);
|
||||
|
||||
/* pin link */
|
||||
err = bpf_link__pin(link, link_pin_path);
|
||||
if (CHECK(err, "link_pin", "err: %d\n", err))
|
||||
goto cleanup;
|
||||
|
||||
CHECK(strcmp(link_pin_path, bpf_link__pin_path(link)), "pin_path1",
|
||||
"exp %s, got %s\n", link_pin_path, bpf_link__pin_path(link));
|
||||
|
||||
/* check that link was pinned */
|
||||
err = stat(link_pin_path, &statbuf);
|
||||
if (CHECK(err, "stat_link", "err %d errno %d\n", err, errno))
|
||||
goto cleanup;
|
||||
|
||||
bss->in = 2;
|
||||
usleep(1);
|
||||
CHECK(bss->out != 2, "res_check2", "exp %d, got %d\n", 2, bss->out);
|
||||
|
||||
/* destroy link, pinned link should keep program attached */
|
||||
bpf_link__destroy(link);
|
||||
link = NULL;
|
||||
|
||||
bss->in = 3;
|
||||
usleep(1);
|
||||
CHECK(bss->out != 3, "res_check3", "exp %d, got %d\n", 3, bss->out);
|
||||
|
||||
/* re-open link from BPFFS */
|
||||
link = bpf_link__open(link_pin_path);
|
||||
if (CHECK(IS_ERR(link), "link_open", "err: %ld\n", PTR_ERR(link)))
|
||||
goto cleanup;
|
||||
|
||||
CHECK(strcmp(link_pin_path, bpf_link__pin_path(link)), "pin_path2",
|
||||
"exp %s, got %s\n", link_pin_path, bpf_link__pin_path(link));
|
||||
|
||||
/* unpin link from BPFFS, program still attached */
|
||||
err = bpf_link__unpin(link);
|
||||
if (CHECK(err, "link_unpin", "err: %d\n", err))
|
||||
goto cleanup;
|
||||
|
||||
/* still active, as we have FD open now */
|
||||
bss->in = 4;
|
||||
usleep(1);
|
||||
CHECK(bss->out != 4, "res_check4", "exp %d, got %d\n", 4, bss->out);
|
||||
|
||||
bpf_link__destroy(link);
|
||||
link = NULL;
|
||||
|
||||
/* Validate it's finally detached.
|
||||
* Actual detachment might get delayed a bit, so there is no reliable
|
||||
* way to validate it immediately here, let's count up for long enough
|
||||
* and see if eventually output stops being updated
|
||||
*/
|
||||
for (i = 5; i < 10000; i++) {
|
||||
bss->in = i;
|
||||
usleep(1);
|
||||
if (bss->out == i - 1)
|
||||
break;
|
||||
}
|
||||
CHECK(i == 10000, "link_attached", "got to iteration #%d\n", i);
|
||||
|
||||
cleanup:
|
||||
if (!IS_ERR(link))
|
||||
bpf_link__destroy(link);
|
||||
}
|
||||
|
||||
void test_link_pinning(void)
|
||||
{
|
||||
struct test_link_pinning* skel;
|
||||
|
||||
skel = test_link_pinning__open_and_load();
|
||||
if (CHECK(!skel, "skel_open", "failed to open skeleton\n"))
|
||||
return;
|
||||
|
||||
if (test__start_subtest("pin_raw_tp"))
|
||||
test_link_pinning_subtest(skel->progs.raw_tp_prog, skel->bss);
|
||||
if (test__start_subtest("pin_tp_btf"))
|
||||
test_link_pinning_subtest(skel->progs.tp_btf_prog, skel->bss);
|
||||
|
||||
test_link_pinning__destroy(skel);
|
||||
}
|
65
tools/testing/selftests/bpf/prog_tests/modify_return.c
Normal file
65
tools/testing/selftests/bpf/prog_tests/modify_return.c
Normal file
@@ -0,0 +1,65 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
/*
|
||||
* Copyright 2020 Google LLC.
|
||||
*/
|
||||
|
||||
#include <test_progs.h>
|
||||
#include "modify_return.skel.h"
|
||||
|
||||
#define LOWER(x) ((x) & 0xffff)
|
||||
#define UPPER(x) ((x) >> 16)
|
||||
|
||||
|
||||
static void run_test(__u32 input_retval, __u16 want_side_effect, __s16 want_ret)
|
||||
{
|
||||
struct modify_return *skel = NULL;
|
||||
int err, prog_fd;
|
||||
__u32 duration = 0, retval;
|
||||
__u16 side_effect;
|
||||
__s16 ret;
|
||||
|
||||
skel = modify_return__open_and_load();
|
||||
if (CHECK(!skel, "skel_load", "modify_return skeleton failed\n"))
|
||||
goto cleanup;
|
||||
|
||||
err = modify_return__attach(skel);
|
||||
if (CHECK(err, "modify_return", "attach failed: %d\n", err))
|
||||
goto cleanup;
|
||||
|
||||
skel->bss->input_retval = input_retval;
|
||||
prog_fd = bpf_program__fd(skel->progs.fmod_ret_test);
|
||||
err = bpf_prog_test_run(prog_fd, 1, NULL, 0, NULL, 0,
|
||||
&retval, &duration);
|
||||
|
||||
CHECK(err, "test_run", "err %d errno %d\n", err, errno);
|
||||
|
||||
side_effect = UPPER(retval);
|
||||
ret = LOWER(retval);
|
||||
|
||||
CHECK(ret != want_ret, "test_run",
|
||||
"unexpected ret: %d, expected: %d\n", ret, want_ret);
|
||||
CHECK(side_effect != want_side_effect, "modify_return",
|
||||
"unexpected side_effect: %d\n", side_effect);
|
||||
|
||||
CHECK(skel->bss->fentry_result != 1, "modify_return",
|
||||
"fentry failed\n");
|
||||
CHECK(skel->bss->fexit_result != 1, "modify_return",
|
||||
"fexit failed\n");
|
||||
CHECK(skel->bss->fmod_ret_result != 1, "modify_return",
|
||||
"fmod_ret failed\n");
|
||||
|
||||
cleanup:
|
||||
modify_return__destroy(skel);
|
||||
}
|
||||
|
||||
void test_modify_return(void)
|
||||
{
|
||||
run_test(0 /* input_retval */,
|
||||
1 /* want_side_effect */,
|
||||
4 /* want_ret */);
|
||||
run_test(-EINVAL /* input_retval */,
|
||||
0 /* want_side_effect */,
|
||||
-EINVAL /* want_ret */);
|
||||
}
|
||||
|
88
tools/testing/selftests/bpf/prog_tests/ns_current_pid_tgid.c
Normal file
88
tools/testing/selftests/bpf/prog_tests/ns_current_pid_tgid.c
Normal file
@@ -0,0 +1,88 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2020 Carlos Neira cneirabustos@gmail.com */
|
||||
#include <test_progs.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
|
||||
struct bss {
|
||||
__u64 dev;
|
||||
__u64 ino;
|
||||
__u64 pid_tgid;
|
||||
__u64 user_pid_tgid;
|
||||
};
|
||||
|
||||
void test_ns_current_pid_tgid(void)
|
||||
{
|
||||
const char *probe_name = "raw_tracepoint/sys_enter";
|
||||
const char *file = "test_ns_current_pid_tgid.o";
|
||||
int err, key = 0, duration = 0;
|
||||
struct bpf_link *link = NULL;
|
||||
struct bpf_program *prog;
|
||||
struct bpf_map *bss_map;
|
||||
struct bpf_object *obj;
|
||||
struct bss bss;
|
||||
struct stat st;
|
||||
__u64 id;
|
||||
|
||||
obj = bpf_object__open_file(file, NULL);
|
||||
if (CHECK(IS_ERR(obj), "obj_open", "err %ld\n", PTR_ERR(obj)))
|
||||
return;
|
||||
|
||||
err = bpf_object__load(obj);
|
||||
if (CHECK(err, "obj_load", "err %d errno %d\n", err, errno))
|
||||
goto cleanup;
|
||||
|
||||
bss_map = bpf_object__find_map_by_name(obj, "test_ns_.bss");
|
||||
if (CHECK(!bss_map, "find_bss_map", "failed\n"))
|
||||
goto cleanup;
|
||||
|
||||
prog = bpf_object__find_program_by_title(obj, probe_name);
|
||||
if (CHECK(!prog, "find_prog", "prog '%s' not found\n",
|
||||
probe_name))
|
||||
goto cleanup;
|
||||
|
||||
memset(&bss, 0, sizeof(bss));
|
||||
pid_t tid = syscall(SYS_gettid);
|
||||
pid_t pid = getpid();
|
||||
|
||||
id = (__u64) tid << 32 | pid;
|
||||
bss.user_pid_tgid = id;
|
||||
|
||||
if (CHECK_FAIL(stat("/proc/self/ns/pid", &st))) {
|
||||
perror("Failed to stat /proc/self/ns/pid");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
bss.dev = st.st_dev;
|
||||
bss.ino = st.st_ino;
|
||||
|
||||
err = bpf_map_update_elem(bpf_map__fd(bss_map), &key, &bss, 0);
|
||||
if (CHECK(err, "setting_bss", "failed to set bss : %d\n", err))
|
||||
goto cleanup;
|
||||
|
||||
link = bpf_program__attach_raw_tracepoint(prog, "sys_enter");
|
||||
if (CHECK(IS_ERR(link), "attach_raw_tp", "err %ld\n",
|
||||
PTR_ERR(link))) {
|
||||
link = NULL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* trigger some syscalls */
|
||||
usleep(1);
|
||||
|
||||
err = bpf_map_lookup_elem(bpf_map__fd(bss_map), &key, &bss);
|
||||
if (CHECK(err, "set_bss", "failed to get bss : %d\n", err))
|
||||
goto cleanup;
|
||||
|
||||
if (CHECK(id != bss.pid_tgid, "Compare user pid/tgid vs. bpf pid/tgid",
|
||||
"User pid/tgid %llu BPF pid/tgid %llu\n", id, bss.pid_tgid))
|
||||
goto cleanup;
|
||||
cleanup:
|
||||
if (!link) {
|
||||
bpf_link__destroy(link);
|
||||
link = NULL;
|
||||
}
|
||||
bpf_object__close(obj);
|
||||
}
|
170
tools/testing/selftests/bpf/prog_tests/perf_branches.c
Normal file
170
tools/testing/selftests/bpf/prog_tests/perf_branches.c
Normal file
@@ -0,0 +1,170 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#define _GNU_SOURCE
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <sys/socket.h>
|
||||
#include <test_progs.h>
|
||||
#include "bpf/libbpf_internal.h"
|
||||
#include "test_perf_branches.skel.h"
|
||||
|
||||
static void check_good_sample(struct test_perf_branches *skel)
|
||||
{
|
||||
int written_global = skel->bss->written_global_out;
|
||||
int required_size = skel->bss->required_size_out;
|
||||
int written_stack = skel->bss->written_stack_out;
|
||||
int pbe_size = sizeof(struct perf_branch_entry);
|
||||
int duration = 0;
|
||||
|
||||
if (CHECK(!skel->bss->valid, "output not valid",
|
||||
"no valid sample from prog"))
|
||||
return;
|
||||
|
||||
/*
|
||||
* It's hard to validate the contents of the branch entries b/c it
|
||||
* would require some kind of disassembler and also encoding the
|
||||
* valid jump instructions for supported architectures. So just check
|
||||
* the easy stuff for now.
|
||||
*/
|
||||
CHECK(required_size <= 0, "read_branches_size", "err %d\n", required_size);
|
||||
CHECK(written_stack < 0, "read_branches_stack", "err %d\n", written_stack);
|
||||
CHECK(written_stack % pbe_size != 0, "read_branches_stack",
|
||||
"stack bytes written=%d not multiple of struct size=%d\n",
|
||||
written_stack, pbe_size);
|
||||
CHECK(written_global < 0, "read_branches_global", "err %d\n", written_global);
|
||||
CHECK(written_global % pbe_size != 0, "read_branches_global",
|
||||
"global bytes written=%d not multiple of struct size=%d\n",
|
||||
written_global, pbe_size);
|
||||
CHECK(written_global < written_stack, "read_branches_size",
|
||||
"written_global=%d < written_stack=%d\n", written_global, written_stack);
|
||||
}
|
||||
|
||||
static void check_bad_sample(struct test_perf_branches *skel)
|
||||
{
|
||||
int written_global = skel->bss->written_global_out;
|
||||
int required_size = skel->bss->required_size_out;
|
||||
int written_stack = skel->bss->written_stack_out;
|
||||
int duration = 0;
|
||||
|
||||
if (CHECK(!skel->bss->valid, "output not valid",
|
||||
"no valid sample from prog"))
|
||||
return;
|
||||
|
||||
CHECK((required_size != -EINVAL && required_size != -ENOENT),
|
||||
"read_branches_size", "err %d\n", required_size);
|
||||
CHECK((written_stack != -EINVAL && written_stack != -ENOENT),
|
||||
"read_branches_stack", "written %d\n", written_stack);
|
||||
CHECK((written_global != -EINVAL && written_global != -ENOENT),
|
||||
"read_branches_global", "written %d\n", written_global);
|
||||
}
|
||||
|
||||
static void test_perf_branches_common(int perf_fd,
|
||||
void (*cb)(struct test_perf_branches *))
|
||||
{
|
||||
struct test_perf_branches *skel;
|
||||
int err, i, duration = 0;
|
||||
bool detached = false;
|
||||
struct bpf_link *link;
|
||||
volatile int j = 0;
|
||||
cpu_set_t cpu_set;
|
||||
|
||||
skel = test_perf_branches__open_and_load();
|
||||
if (CHECK(!skel, "test_perf_branches_load",
|
||||
"perf_branches skeleton failed\n"))
|
||||
return;
|
||||
|
||||
/* attach perf_event */
|
||||
link = bpf_program__attach_perf_event(skel->progs.perf_branches, perf_fd);
|
||||
if (CHECK(IS_ERR(link), "attach_perf_event", "err %ld\n", PTR_ERR(link)))
|
||||
goto out_destroy_skel;
|
||||
|
||||
/* generate some branches on cpu 0 */
|
||||
CPU_ZERO(&cpu_set);
|
||||
CPU_SET(0, &cpu_set);
|
||||
err = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set), &cpu_set);
|
||||
if (CHECK(err, "set_affinity", "cpu #0, err %d\n", err))
|
||||
goto out_destroy;
|
||||
/* spin the loop for a while (random high number) */
|
||||
for (i = 0; i < 1000000; ++i)
|
||||
++j;
|
||||
|
||||
test_perf_branches__detach(skel);
|
||||
detached = true;
|
||||
|
||||
cb(skel);
|
||||
out_destroy:
|
||||
bpf_link__destroy(link);
|
||||
out_destroy_skel:
|
||||
if (!detached)
|
||||
test_perf_branches__detach(skel);
|
||||
test_perf_branches__destroy(skel);
|
||||
}
|
||||
|
||||
static void test_perf_branches_hw(void)
|
||||
{
|
||||
struct perf_event_attr attr = {0};
|
||||
int duration = 0;
|
||||
int pfd;
|
||||
|
||||
/* create perf event */
|
||||
attr.size = sizeof(attr);
|
||||
attr.type = PERF_TYPE_HARDWARE;
|
||||
attr.config = PERF_COUNT_HW_CPU_CYCLES;
|
||||
attr.freq = 1;
|
||||
attr.sample_freq = 4000;
|
||||
attr.sample_type = PERF_SAMPLE_BRANCH_STACK;
|
||||
attr.branch_sample_type = PERF_SAMPLE_BRANCH_USER | PERF_SAMPLE_BRANCH_ANY;
|
||||
pfd = syscall(__NR_perf_event_open, &attr, -1, 0, -1, PERF_FLAG_FD_CLOEXEC);
|
||||
|
||||
/*
|
||||
* Some setups don't support branch records (virtual machines, !x86),
|
||||
* so skip test in this case.
|
||||
*/
|
||||
if (pfd == -1) {
|
||||
if (errno == ENOENT || errno == EOPNOTSUPP) {
|
||||
printf("%s:SKIP:no PERF_SAMPLE_BRANCH_STACK\n",
|
||||
__func__);
|
||||
test__skip();
|
||||
return;
|
||||
}
|
||||
if (CHECK(pfd < 0, "perf_event_open", "err %d errno %d\n",
|
||||
pfd, errno))
|
||||
return;
|
||||
}
|
||||
|
||||
test_perf_branches_common(pfd, check_good_sample);
|
||||
|
||||
close(pfd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Tests negative case -- run bpf_read_branch_records() on improperly configured
|
||||
* perf event.
|
||||
*/
|
||||
static void test_perf_branches_no_hw(void)
|
||||
{
|
||||
struct perf_event_attr attr = {0};
|
||||
int duration = 0;
|
||||
int pfd;
|
||||
|
||||
/* create perf event */
|
||||
attr.size = sizeof(attr);
|
||||
attr.type = PERF_TYPE_SOFTWARE;
|
||||
attr.config = PERF_COUNT_SW_CPU_CLOCK;
|
||||
attr.freq = 1;
|
||||
attr.sample_freq = 4000;
|
||||
pfd = syscall(__NR_perf_event_open, &attr, -1, 0, -1, PERF_FLAG_FD_CLOEXEC);
|
||||
if (CHECK(pfd < 0, "perf_event_open", "err %d\n", pfd))
|
||||
return;
|
||||
|
||||
test_perf_branches_common(pfd, check_bad_sample);
|
||||
|
||||
close(pfd);
|
||||
}
|
||||
|
||||
void test_perf_branches(void)
|
||||
{
|
||||
if (test__start_subtest("perf_branches_hw"))
|
||||
test_perf_branches_hw();
|
||||
if (test__start_subtest("perf_branches_no_hw"))
|
||||
test_perf_branches_no_hw();
|
||||
}
|
@@ -36,6 +36,7 @@ static int result_map, tmp_index_ovr_map, linum_map, data_check_map;
|
||||
static __u32 expected_results[NR_RESULTS];
|
||||
static int sk_fds[REUSEPORT_ARRAY_SIZE];
|
||||
static int reuseport_array = -1, outer_map = -1;
|
||||
static enum bpf_map_type inner_map_type;
|
||||
static int select_by_skb_data_prog;
|
||||
static int saved_tcp_syncookie = -1;
|
||||
static struct bpf_object *obj;
|
||||
@@ -63,13 +64,15 @@ static union sa46 {
|
||||
} \
|
||||
})
|
||||
|
||||
static int create_maps(void)
|
||||
static int create_maps(enum bpf_map_type inner_type)
|
||||
{
|
||||
struct bpf_create_map_attr attr = {};
|
||||
|
||||
inner_map_type = inner_type;
|
||||
|
||||
/* Creating reuseport_array */
|
||||
attr.name = "reuseport_array";
|
||||
attr.map_type = BPF_MAP_TYPE_REUSEPORT_SOCKARRAY;
|
||||
attr.map_type = inner_type;
|
||||
attr.key_size = sizeof(__u32);
|
||||
attr.value_size = sizeof(__u32);
|
||||
attr.max_entries = REUSEPORT_ARRAY_SIZE;
|
||||
@@ -506,11 +509,6 @@ static void test_syncookie(int type, sa_family_t family)
|
||||
.pass_on_failure = 0,
|
||||
};
|
||||
|
||||
if (type != SOCK_STREAM) {
|
||||
test__skip();
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* +1 for TCP-SYN and
|
||||
* +1 for the TCP-ACK (ack the syncookie)
|
||||
@@ -728,12 +726,36 @@ static void cleanup_per_test(bool no_inner_map)
|
||||
|
||||
static void cleanup(void)
|
||||
{
|
||||
if (outer_map != -1)
|
||||
if (outer_map != -1) {
|
||||
close(outer_map);
|
||||
if (reuseport_array != -1)
|
||||
outer_map = -1;
|
||||
}
|
||||
|
||||
if (reuseport_array != -1) {
|
||||
close(reuseport_array);
|
||||
if (obj)
|
||||
reuseport_array = -1;
|
||||
}
|
||||
|
||||
if (obj) {
|
||||
bpf_object__close(obj);
|
||||
obj = NULL;
|
||||
}
|
||||
|
||||
memset(expected_results, 0, sizeof(expected_results));
|
||||
}
|
||||
|
||||
static const char *maptype_str(enum bpf_map_type type)
|
||||
{
|
||||
switch (type) {
|
||||
case BPF_MAP_TYPE_REUSEPORT_SOCKARRAY:
|
||||
return "reuseport_sockarray";
|
||||
case BPF_MAP_TYPE_SOCKMAP:
|
||||
return "sockmap";
|
||||
case BPF_MAP_TYPE_SOCKHASH:
|
||||
return "sockhash";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *family_str(sa_family_t family)
|
||||
@@ -760,7 +782,7 @@ static const char *sotype_str(int sotype)
|
||||
}
|
||||
}
|
||||
|
||||
#define TEST_INIT(fn, ...) { fn, #fn, __VA_ARGS__ }
|
||||
#define TEST_INIT(fn_, ...) { .fn = fn_, .name = #fn_, __VA_ARGS__ }
|
||||
|
||||
static void test_config(int sotype, sa_family_t family, bool inany)
|
||||
{
|
||||
@@ -768,12 +790,15 @@ static void test_config(int sotype, sa_family_t family, bool inany)
|
||||
void (*fn)(int sotype, sa_family_t family);
|
||||
const char *name;
|
||||
bool no_inner_map;
|
||||
int need_sotype;
|
||||
} tests[] = {
|
||||
TEST_INIT(test_err_inner_map, true /* no_inner_map */),
|
||||
TEST_INIT(test_err_inner_map,
|
||||
.no_inner_map = true),
|
||||
TEST_INIT(test_err_skb_data),
|
||||
TEST_INIT(test_err_sk_select_port),
|
||||
TEST_INIT(test_pass),
|
||||
TEST_INIT(test_syncookie),
|
||||
TEST_INIT(test_syncookie,
|
||||
.need_sotype = SOCK_STREAM),
|
||||
TEST_INIT(test_pass_on_err),
|
||||
TEST_INIT(test_detach_bpf),
|
||||
};
|
||||
@@ -781,7 +806,11 @@ static void test_config(int sotype, sa_family_t family, bool inany)
|
||||
const struct test *t;
|
||||
|
||||
for (t = tests; t < tests + ARRAY_SIZE(tests); t++) {
|
||||
snprintf(s, sizeof(s), "%s/%s %s %s",
|
||||
if (t->need_sotype && t->need_sotype != sotype)
|
||||
continue; /* test not compatible with socket type */
|
||||
|
||||
snprintf(s, sizeof(s), "%s %s/%s %s %s",
|
||||
maptype_str(inner_map_type),
|
||||
family_str(family), sotype_str(sotype),
|
||||
inany ? "INANY" : "LOOPBACK", t->name);
|
||||
|
||||
@@ -816,13 +845,20 @@ static void test_all(void)
|
||||
test_config(c->sotype, c->family, c->inany);
|
||||
}
|
||||
|
||||
void test_select_reuseport(void)
|
||||
void test_map_type(enum bpf_map_type mt)
|
||||
{
|
||||
if (create_maps())
|
||||
if (create_maps(mt))
|
||||
goto out;
|
||||
if (prepare_bpf_obj())
|
||||
goto out;
|
||||
|
||||
test_all();
|
||||
out:
|
||||
cleanup();
|
||||
}
|
||||
|
||||
void test_select_reuseport(void)
|
||||
{
|
||||
saved_tcp_fo = read_int_sysctl(TCP_FO_SYSCTL);
|
||||
if (saved_tcp_fo < 0)
|
||||
goto out;
|
||||
@@ -835,8 +871,9 @@ void test_select_reuseport(void)
|
||||
if (disable_syncookie())
|
||||
goto out;
|
||||
|
||||
test_all();
|
||||
test_map_type(BPF_MAP_TYPE_REUSEPORT_SOCKARRAY);
|
||||
test_map_type(BPF_MAP_TYPE_SOCKMAP);
|
||||
test_map_type(BPF_MAP_TYPE_SOCKHASH);
|
||||
out:
|
||||
cleanup();
|
||||
restore_sysctls();
|
||||
}
|
||||
|
309
tools/testing/selftests/bpf/prog_tests/sk_assign.c
Normal file
309
tools/testing/selftests/bpf/prog_tests/sk_assign.c
Normal file
@@ -0,0 +1,309 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2018 Facebook
|
||||
// Copyright (c) 2019 Cloudflare
|
||||
// Copyright (c) 2020 Isovalent, Inc.
|
||||
/*
|
||||
* Test that the socket assign program is able to redirect traffic towards a
|
||||
* socket, regardless of whether the port or address destination of the traffic
|
||||
* matches the port.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "test_progs.h"
|
||||
|
||||
#define BIND_PORT 1234
|
||||
#define CONNECT_PORT 4321
|
||||
#define TEST_DADDR (0xC0A80203)
|
||||
#define NS_SELF "/proc/self/ns/net"
|
||||
|
||||
static const struct timeval timeo_sec = { .tv_sec = 3 };
|
||||
static const size_t timeo_optlen = sizeof(timeo_sec);
|
||||
static int stop, duration;
|
||||
|
||||
static bool
|
||||
configure_stack(void)
|
||||
{
|
||||
char tc_cmd[BUFSIZ];
|
||||
|
||||
/* Move to a new networking namespace */
|
||||
if (CHECK_FAIL(unshare(CLONE_NEWNET)))
|
||||
return false;
|
||||
|
||||
/* Configure necessary links, routes */
|
||||
if (CHECK_FAIL(system("ip link set dev lo up")))
|
||||
return false;
|
||||
if (CHECK_FAIL(system("ip route add local default dev lo")))
|
||||
return false;
|
||||
if (CHECK_FAIL(system("ip -6 route add local default dev lo")))
|
||||
return false;
|
||||
|
||||
/* Load qdisc, BPF program */
|
||||
if (CHECK_FAIL(system("tc qdisc add dev lo clsact")))
|
||||
return false;
|
||||
sprintf(tc_cmd, "%s %s %s %s", "tc filter add dev lo ingress bpf",
|
||||
"direct-action object-file ./test_sk_assign.o",
|
||||
"section classifier/sk_assign_test",
|
||||
(env.verbosity < VERBOSE_VERY) ? " 2>/dev/null" : "");
|
||||
if (CHECK(system(tc_cmd), "BPF load failed;",
|
||||
"run with -vv for more info\n"))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int
|
||||
start_server(const struct sockaddr *addr, socklen_t len, int type)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = socket(addr->sa_family, type, 0);
|
||||
if (CHECK_FAIL(fd == -1))
|
||||
goto out;
|
||||
if (CHECK_FAIL(setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeo_sec,
|
||||
timeo_optlen)))
|
||||
goto close_out;
|
||||
if (CHECK_FAIL(bind(fd, addr, len) == -1))
|
||||
goto close_out;
|
||||
if (type == SOCK_STREAM && CHECK_FAIL(listen(fd, 128) == -1))
|
||||
goto close_out;
|
||||
|
||||
goto out;
|
||||
close_out:
|
||||
close(fd);
|
||||
fd = -1;
|
||||
out:
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int
|
||||
connect_to_server(const struct sockaddr *addr, socklen_t len, int type)
|
||||
{
|
||||
int fd = -1;
|
||||
|
||||
fd = socket(addr->sa_family, type, 0);
|
||||
if (CHECK_FAIL(fd == -1))
|
||||
goto out;
|
||||
if (CHECK_FAIL(setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeo_sec,
|
||||
timeo_optlen)))
|
||||
goto close_out;
|
||||
if (CHECK_FAIL(connect(fd, addr, len)))
|
||||
goto close_out;
|
||||
|
||||
goto out;
|
||||
close_out:
|
||||
close(fd);
|
||||
fd = -1;
|
||||
out:
|
||||
return fd;
|
||||
}
|
||||
|
||||
static in_port_t
|
||||
get_port(int fd)
|
||||
{
|
||||
struct sockaddr_storage ss;
|
||||
socklen_t slen = sizeof(ss);
|
||||
in_port_t port = 0;
|
||||
|
||||
if (CHECK_FAIL(getsockname(fd, (struct sockaddr *)&ss, &slen)))
|
||||
return port;
|
||||
|
||||
switch (ss.ss_family) {
|
||||
case AF_INET:
|
||||
port = ((struct sockaddr_in *)&ss)->sin_port;
|
||||
break;
|
||||
case AF_INET6:
|
||||
port = ((struct sockaddr_in6 *)&ss)->sin6_port;
|
||||
break;
|
||||
default:
|
||||
CHECK(1, "Invalid address family", "%d\n", ss.ss_family);
|
||||
}
|
||||
return port;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
rcv_msg(int srv_client, int type)
|
||||
{
|
||||
struct sockaddr_storage ss;
|
||||
char buf[BUFSIZ];
|
||||
socklen_t slen;
|
||||
|
||||
if (type == SOCK_STREAM)
|
||||
return read(srv_client, &buf, sizeof(buf));
|
||||
else
|
||||
return recvfrom(srv_client, &buf, sizeof(buf), 0,
|
||||
(struct sockaddr *)&ss, &slen);
|
||||
}
|
||||
|
||||
static int
|
||||
run_test(int server_fd, const struct sockaddr *addr, socklen_t len, int type)
|
||||
{
|
||||
int client = -1, srv_client = -1;
|
||||
char buf[] = "testing";
|
||||
in_port_t port;
|
||||
int ret = 1;
|
||||
|
||||
client = connect_to_server(addr, len, type);
|
||||
if (client == -1) {
|
||||
perror("Cannot connect to server");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (type == SOCK_STREAM) {
|
||||
srv_client = accept(server_fd, NULL, NULL);
|
||||
if (CHECK_FAIL(srv_client == -1)) {
|
||||
perror("Can't accept connection");
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
srv_client = server_fd;
|
||||
}
|
||||
if (CHECK_FAIL(write(client, buf, sizeof(buf)) != sizeof(buf))) {
|
||||
perror("Can't write on client");
|
||||
goto out;
|
||||
}
|
||||
if (CHECK_FAIL(rcv_msg(srv_client, type) != sizeof(buf))) {
|
||||
perror("Can't read on server");
|
||||
goto out;
|
||||
}
|
||||
|
||||
port = get_port(srv_client);
|
||||
if (CHECK_FAIL(!port))
|
||||
goto out;
|
||||
/* SOCK_STREAM is connected via accept(), so the server's local address
|
||||
* will be the CONNECT_PORT rather than the BIND port that corresponds
|
||||
* to the listen socket. SOCK_DGRAM on the other hand is connectionless
|
||||
* so we can't really do the same check there; the server doesn't ever
|
||||
* create a socket with CONNECT_PORT.
|
||||
*/
|
||||
if (type == SOCK_STREAM &&
|
||||
CHECK(port != htons(CONNECT_PORT), "Expected", "port %u but got %u",
|
||||
CONNECT_PORT, ntohs(port)))
|
||||
goto out;
|
||||
else if (type == SOCK_DGRAM &&
|
||||
CHECK(port != htons(BIND_PORT), "Expected",
|
||||
"port %u but got %u", BIND_PORT, ntohs(port)))
|
||||
goto out;
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
close(client);
|
||||
if (srv_client != server_fd)
|
||||
close(srv_client);
|
||||
if (ret)
|
||||
WRITE_ONCE(stop, 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
prepare_addr(struct sockaddr *addr, int family, __u16 port, bool rewrite_addr)
|
||||
{
|
||||
struct sockaddr_in *addr4;
|
||||
struct sockaddr_in6 *addr6;
|
||||
|
||||
switch (family) {
|
||||
case AF_INET:
|
||||
addr4 = (struct sockaddr_in *)addr;
|
||||
memset(addr4, 0, sizeof(*addr4));
|
||||
addr4->sin_family = family;
|
||||
addr4->sin_port = htons(port);
|
||||
if (rewrite_addr)
|
||||
addr4->sin_addr.s_addr = htonl(TEST_DADDR);
|
||||
else
|
||||
addr4->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
break;
|
||||
case AF_INET6:
|
||||
addr6 = (struct sockaddr_in6 *)addr;
|
||||
memset(addr6, 0, sizeof(*addr6));
|
||||
addr6->sin6_family = family;
|
||||
addr6->sin6_port = htons(port);
|
||||
addr6->sin6_addr = in6addr_loopback;
|
||||
if (rewrite_addr)
|
||||
addr6->sin6_addr.s6_addr32[3] = htonl(TEST_DADDR);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Invalid family %d", family);
|
||||
}
|
||||
}
|
||||
|
||||
struct test_sk_cfg {
|
||||
const char *name;
|
||||
int family;
|
||||
struct sockaddr *addr;
|
||||
socklen_t len;
|
||||
int type;
|
||||
bool rewrite_addr;
|
||||
};
|
||||
|
||||
#define TEST(NAME, FAMILY, TYPE, REWRITE) \
|
||||
{ \
|
||||
.name = NAME, \
|
||||
.family = FAMILY, \
|
||||
.addr = (FAMILY == AF_INET) ? (struct sockaddr *)&addr4 \
|
||||
: (struct sockaddr *)&addr6, \
|
||||
.len = (FAMILY == AF_INET) ? sizeof(addr4) : sizeof(addr6), \
|
||||
.type = TYPE, \
|
||||
.rewrite_addr = REWRITE, \
|
||||
}
|
||||
|
||||
void test_sk_assign(void)
|
||||
{
|
||||
struct sockaddr_in addr4;
|
||||
struct sockaddr_in6 addr6;
|
||||
struct test_sk_cfg tests[] = {
|
||||
TEST("ipv4 tcp port redir", AF_INET, SOCK_STREAM, false),
|
||||
TEST("ipv4 tcp addr redir", AF_INET, SOCK_STREAM, true),
|
||||
TEST("ipv6 tcp port redir", AF_INET6, SOCK_STREAM, false),
|
||||
TEST("ipv6 tcp addr redir", AF_INET6, SOCK_STREAM, true),
|
||||
TEST("ipv4 udp port redir", AF_INET, SOCK_DGRAM, false),
|
||||
TEST("ipv4 udp addr redir", AF_INET, SOCK_DGRAM, true),
|
||||
TEST("ipv6 udp port redir", AF_INET6, SOCK_DGRAM, false),
|
||||
TEST("ipv6 udp addr redir", AF_INET6, SOCK_DGRAM, true),
|
||||
};
|
||||
int server = -1;
|
||||
int self_net;
|
||||
|
||||
self_net = open(NS_SELF, O_RDONLY);
|
||||
if (CHECK_FAIL(self_net < 0)) {
|
||||
perror("Unable to open "NS_SELF);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!configure_stack()) {
|
||||
perror("configure_stack");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
for (int i = 0; i < ARRAY_SIZE(tests) && !READ_ONCE(stop); i++) {
|
||||
struct test_sk_cfg *test = &tests[i];
|
||||
const struct sockaddr *addr;
|
||||
|
||||
if (!test__start_subtest(test->name))
|
||||
continue;
|
||||
prepare_addr(test->addr, test->family, BIND_PORT, false);
|
||||
addr = (const struct sockaddr *)test->addr;
|
||||
server = start_server(addr, test->len, test->type);
|
||||
if (server == -1)
|
||||
goto cleanup;
|
||||
|
||||
/* connect to unbound ports */
|
||||
prepare_addr(test->addr, test->family, CONNECT_PORT,
|
||||
test->rewrite_addr);
|
||||
if (run_test(server, addr, test->len, test->type))
|
||||
goto close;
|
||||
|
||||
close(server);
|
||||
server = -1;
|
||||
}
|
||||
|
||||
close:
|
||||
close(server);
|
||||
cleanup:
|
||||
if (CHECK_FAIL(setns(self_net, CLONE_NEWNET)))
|
||||
perror("Failed to setns("NS_SELF")");
|
||||
close(self_net);
|
||||
}
|
@@ -14,6 +14,7 @@ void test_skb_ctx(void)
|
||||
.wire_len = 100,
|
||||
.gso_segs = 8,
|
||||
.mark = 9,
|
||||
.gso_size = 10,
|
||||
};
|
||||
struct bpf_prog_test_run_attr tattr = {
|
||||
.data_in = &pkt_v4,
|
||||
|
124
tools/testing/selftests/bpf/prog_tests/sockmap_ktls.c
Normal file
124
tools/testing/selftests/bpf/prog_tests/sockmap_ktls.c
Normal file
@@ -0,0 +1,124 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2020 Cloudflare
|
||||
/*
|
||||
* Tests for sockmap/sockhash holding kTLS sockets.
|
||||
*/
|
||||
|
||||
#include "test_progs.h"
|
||||
|
||||
#define MAX_TEST_NAME 80
|
||||
#define TCP_ULP 31
|
||||
|
||||
static int tcp_server(int family)
|
||||
{
|
||||
int err, s;
|
||||
|
||||
s = socket(family, SOCK_STREAM, 0);
|
||||
if (CHECK_FAIL(s == -1)) {
|
||||
perror("socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = listen(s, SOMAXCONN);
|
||||
if (CHECK_FAIL(err)) {
|
||||
perror("listen");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static int disconnect(int fd)
|
||||
{
|
||||
struct sockaddr unspec = { AF_UNSPEC };
|
||||
|
||||
return connect(fd, &unspec, sizeof(unspec));
|
||||
}
|
||||
|
||||
/* Disconnect (unhash) a kTLS socket after removing it from sockmap. */
|
||||
static void test_sockmap_ktls_disconnect_after_delete(int family, int map)
|
||||
{
|
||||
struct sockaddr_storage addr = {0};
|
||||
socklen_t len = sizeof(addr);
|
||||
int err, cli, srv, zero = 0;
|
||||
|
||||
srv = tcp_server(family);
|
||||
if (srv == -1)
|
||||
return;
|
||||
|
||||
err = getsockname(srv, (struct sockaddr *)&addr, &len);
|
||||
if (CHECK_FAIL(err)) {
|
||||
perror("getsockopt");
|
||||
goto close_srv;
|
||||
}
|
||||
|
||||
cli = socket(family, SOCK_STREAM, 0);
|
||||
if (CHECK_FAIL(cli == -1)) {
|
||||
perror("socket");
|
||||
goto close_srv;
|
||||
}
|
||||
|
||||
err = connect(cli, (struct sockaddr *)&addr, len);
|
||||
if (CHECK_FAIL(err)) {
|
||||
perror("connect");
|
||||
goto close_cli;
|
||||
}
|
||||
|
||||
err = bpf_map_update_elem(map, &zero, &cli, 0);
|
||||
if (CHECK_FAIL(err)) {
|
||||
perror("bpf_map_update_elem");
|
||||
goto close_cli;
|
||||
}
|
||||
|
||||
err = setsockopt(cli, IPPROTO_TCP, TCP_ULP, "tls", strlen("tls"));
|
||||
if (CHECK_FAIL(err)) {
|
||||
perror("setsockopt(TCP_ULP)");
|
||||
goto close_cli;
|
||||
}
|
||||
|
||||
err = bpf_map_delete_elem(map, &zero);
|
||||
if (CHECK_FAIL(err)) {
|
||||
perror("bpf_map_delete_elem");
|
||||
goto close_cli;
|
||||
}
|
||||
|
||||
err = disconnect(cli);
|
||||
if (CHECK_FAIL(err))
|
||||
perror("disconnect");
|
||||
|
||||
close_cli:
|
||||
close(cli);
|
||||
close_srv:
|
||||
close(srv);
|
||||
}
|
||||
|
||||
static void run_tests(int family, enum bpf_map_type map_type)
|
||||
{
|
||||
char test_name[MAX_TEST_NAME];
|
||||
int map;
|
||||
|
||||
map = bpf_create_map(map_type, sizeof(int), sizeof(int), 1, 0);
|
||||
if (CHECK_FAIL(map == -1)) {
|
||||
perror("bpf_map_create");
|
||||
return;
|
||||
}
|
||||
|
||||
snprintf(test_name, MAX_TEST_NAME,
|
||||
"sockmap_ktls disconnect_after_delete %s %s",
|
||||
family == AF_INET ? "IPv4" : "IPv6",
|
||||
map_type == BPF_MAP_TYPE_SOCKMAP ? "SOCKMAP" : "SOCKHASH");
|
||||
if (!test__start_subtest(test_name))
|
||||
return;
|
||||
|
||||
test_sockmap_ktls_disconnect_after_delete(family, map);
|
||||
|
||||
close(map);
|
||||
}
|
||||
|
||||
void test_sockmap_ktls(void)
|
||||
{
|
||||
run_tests(AF_INET, BPF_MAP_TYPE_SOCKMAP);
|
||||
run_tests(AF_INET, BPF_MAP_TYPE_SOCKHASH);
|
||||
run_tests(AF_INET6, BPF_MAP_TYPE_SOCKMAP);
|
||||
run_tests(AF_INET6, BPF_MAP_TYPE_SOCKHASH);
|
||||
}
|
1635
tools/testing/selftests/bpf/prog_tests/sockmap_listen.c
Normal file
1635
tools/testing/selftests/bpf/prog_tests/sockmap_listen.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -188,7 +188,7 @@ static int start_server(void)
|
||||
};
|
||||
int fd;
|
||||
|
||||
fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
|
||||
if (fd < 0) {
|
||||
log_err("Failed to create server socket");
|
||||
return -1;
|
||||
@@ -205,6 +205,7 @@ static int start_server(void)
|
||||
|
||||
static pthread_mutex_t server_started_mtx = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_cond_t server_started = PTHREAD_COND_INITIALIZER;
|
||||
static volatile bool server_done = false;
|
||||
|
||||
static void *server_thread(void *arg)
|
||||
{
|
||||
@@ -222,23 +223,24 @@ static void *server_thread(void *arg)
|
||||
|
||||
if (CHECK_FAIL(err < 0)) {
|
||||
perror("Failed to listed on socket");
|
||||
return NULL;
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
client_fd = accept(fd, (struct sockaddr *)&addr, &len);
|
||||
while (true) {
|
||||
client_fd = accept(fd, (struct sockaddr *)&addr, &len);
|
||||
if (client_fd == -1 && errno == EAGAIN) {
|
||||
usleep(50);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (CHECK_FAIL(client_fd < 0)) {
|
||||
perror("Failed to accept client");
|
||||
return NULL;
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
/* Wait for the next connection (that never arrives)
|
||||
* to keep this thread alive to prevent calling
|
||||
* close() on client_fd.
|
||||
*/
|
||||
if (CHECK_FAIL(accept(fd, (struct sockaddr *)&addr, &len) >= 0)) {
|
||||
perror("Unexpected success in second accept");
|
||||
return NULL;
|
||||
}
|
||||
while (!server_done)
|
||||
usleep(50);
|
||||
|
||||
close(client_fd);
|
||||
|
||||
@@ -249,6 +251,7 @@ void test_tcp_rtt(void)
|
||||
{
|
||||
int server_fd, cgroup_fd;
|
||||
pthread_t tid;
|
||||
void *server_res;
|
||||
|
||||
cgroup_fd = test__join_cgroup("/tcp_rtt");
|
||||
if (CHECK_FAIL(cgroup_fd < 0))
|
||||
@@ -267,6 +270,11 @@ void test_tcp_rtt(void)
|
||||
pthread_mutex_unlock(&server_started_mtx);
|
||||
|
||||
CHECK_FAIL(run_test(cgroup_fd, server_fd));
|
||||
|
||||
server_done = true;
|
||||
CHECK_FAIL(pthread_join(tid, &server_res));
|
||||
CHECK_FAIL(IS_ERR(server_res));
|
||||
|
||||
close_server_fd:
|
||||
close(server_fd);
|
||||
close_cgroup_fd:
|
||||
|
86
tools/testing/selftests/bpf/prog_tests/test_lsm.c
Normal file
86
tools/testing/selftests/bpf/prog_tests/test_lsm.c
Normal file
@@ -0,0 +1,86 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
/*
|
||||
* Copyright (C) 2020 Google LLC.
|
||||
*/
|
||||
|
||||
#include <test_progs.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <malloc.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "lsm.skel.h"
|
||||
|
||||
char *CMD_ARGS[] = {"true", NULL};
|
||||
|
||||
int heap_mprotect(void)
|
||||
{
|
||||
void *buf;
|
||||
long sz;
|
||||
int ret;
|
||||
|
||||
sz = sysconf(_SC_PAGESIZE);
|
||||
if (sz < 0)
|
||||
return sz;
|
||||
|
||||
buf = memalign(sz, 2 * sz);
|
||||
if (buf == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = mprotect(buf, sz, PROT_READ | PROT_WRITE | PROT_EXEC);
|
||||
free(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int exec_cmd(int *monitored_pid)
|
||||
{
|
||||
int child_pid, child_status;
|
||||
|
||||
child_pid = fork();
|
||||
if (child_pid == 0) {
|
||||
*monitored_pid = getpid();
|
||||
execvp(CMD_ARGS[0], CMD_ARGS);
|
||||
return -EINVAL;
|
||||
} else if (child_pid > 0) {
|
||||
waitpid(child_pid, &child_status, 0);
|
||||
return child_status;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
void test_test_lsm(void)
|
||||
{
|
||||
struct lsm *skel = NULL;
|
||||
int err, duration = 0;
|
||||
|
||||
skel = lsm__open_and_load();
|
||||
if (CHECK(!skel, "skel_load", "lsm skeleton failed\n"))
|
||||
goto close_prog;
|
||||
|
||||
err = lsm__attach(skel);
|
||||
if (CHECK(err, "attach", "lsm attach failed: %d\n", err))
|
||||
goto close_prog;
|
||||
|
||||
err = exec_cmd(&skel->bss->monitored_pid);
|
||||
if (CHECK(err < 0, "exec_cmd", "err %d errno %d\n", err, errno))
|
||||
goto close_prog;
|
||||
|
||||
CHECK(skel->bss->bprm_count != 1, "bprm_count", "bprm_count = %d\n",
|
||||
skel->bss->bprm_count);
|
||||
|
||||
skel->bss->monitored_pid = getpid();
|
||||
|
||||
err = heap_mprotect();
|
||||
if (CHECK(errno != EPERM, "heap_mprotect", "want errno=EPERM, got %d\n",
|
||||
errno))
|
||||
goto close_prog;
|
||||
|
||||
CHECK(skel->bss->mprotect_count != 1, "mprotect_count",
|
||||
"mprotect_count = %d\n", skel->bss->mprotect_count);
|
||||
|
||||
close_prog:
|
||||
lsm__destroy(skel);
|
||||
}
|
@@ -55,31 +55,40 @@ void test_trampoline_count(void)
|
||||
/* attach 'allowed' 40 trampoline programs */
|
||||
for (i = 0; i < MAX_TRAMP_PROGS; i++) {
|
||||
obj = bpf_object__open_file(object, NULL);
|
||||
if (CHECK(IS_ERR(obj), "obj_open_file", "err %ld\n", PTR_ERR(obj)))
|
||||
if (CHECK(IS_ERR(obj), "obj_open_file", "err %ld\n", PTR_ERR(obj))) {
|
||||
obj = NULL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
err = bpf_object__load(obj);
|
||||
if (CHECK(err, "obj_load", "err %d\n", err))
|
||||
goto cleanup;
|
||||
inst[i].obj = obj;
|
||||
obj = NULL;
|
||||
|
||||
if (rand() % 2) {
|
||||
link = load(obj, fentry_name);
|
||||
if (CHECK(IS_ERR(link), "attach prog", "err %ld\n", PTR_ERR(link)))
|
||||
link = load(inst[i].obj, fentry_name);
|
||||
if (CHECK(IS_ERR(link), "attach prog", "err %ld\n", PTR_ERR(link))) {
|
||||
link = NULL;
|
||||
goto cleanup;
|
||||
}
|
||||
inst[i].link_fentry = link;
|
||||
} else {
|
||||
link = load(obj, fexit_name);
|
||||
if (CHECK(IS_ERR(link), "attach prog", "err %ld\n", PTR_ERR(link)))
|
||||
link = load(inst[i].obj, fexit_name);
|
||||
if (CHECK(IS_ERR(link), "attach prog", "err %ld\n", PTR_ERR(link))) {
|
||||
link = NULL;
|
||||
goto cleanup;
|
||||
}
|
||||
inst[i].link_fexit = link;
|
||||
}
|
||||
}
|
||||
|
||||
/* and try 1 extra.. */
|
||||
obj = bpf_object__open_file(object, NULL);
|
||||
if (CHECK(IS_ERR(obj), "obj_open_file", "err %ld\n", PTR_ERR(obj)))
|
||||
if (CHECK(IS_ERR(obj), "obj_open_file", "err %ld\n", PTR_ERR(obj))) {
|
||||
obj = NULL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
err = bpf_object__load(obj);
|
||||
if (CHECK(err, "obj_load", "err %d\n", err))
|
||||
@@ -104,7 +113,9 @@ void test_trampoline_count(void)
|
||||
cleanup_extra:
|
||||
bpf_object__close(obj);
|
||||
cleanup:
|
||||
while (--i) {
|
||||
if (i >= MAX_TRAMP_PROGS)
|
||||
i = MAX_TRAMP_PROGS - 1;
|
||||
for (; i >= 0; i--) {
|
||||
bpf_link__destroy(inst[i].link_fentry);
|
||||
bpf_link__destroy(inst[i].link_fexit);
|
||||
bpf_object__close(inst[i].obj);
|
||||
|
43
tools/testing/selftests/bpf/prog_tests/vmlinux.c
Normal file
43
tools/testing/selftests/bpf/prog_tests/vmlinux.c
Normal file
@@ -0,0 +1,43 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2020 Facebook */
|
||||
|
||||
#include <test_progs.h>
|
||||
#include <time.h>
|
||||
#include "test_vmlinux.skel.h"
|
||||
|
||||
#define MY_TV_NSEC 1337
|
||||
|
||||
static void nsleep()
|
||||
{
|
||||
struct timespec ts = { .tv_nsec = MY_TV_NSEC };
|
||||
|
||||
(void)syscall(__NR_nanosleep, &ts, NULL);
|
||||
}
|
||||
|
||||
void test_vmlinux(void)
|
||||
{
|
||||
int duration = 0, err;
|
||||
struct test_vmlinux* skel;
|
||||
struct test_vmlinux__bss *bss;
|
||||
|
||||
skel = test_vmlinux__open_and_load();
|
||||
if (CHECK(!skel, "skel_open", "failed to open skeleton\n"))
|
||||
return;
|
||||
bss = skel->bss;
|
||||
|
||||
err = test_vmlinux__attach(skel);
|
||||
if (CHECK(err, "skel_attach", "skeleton attach failed: %d\n", err))
|
||||
goto cleanup;
|
||||
|
||||
/* trigger everything */
|
||||
nsleep();
|
||||
|
||||
CHECK(!bss->tp_called, "tp", "not called\n");
|
||||
CHECK(!bss->raw_tp_called, "raw_tp", "not called\n");
|
||||
CHECK(!bss->tp_btf_called, "tp_btf", "not called\n");
|
||||
CHECK(!bss->kprobe_called, "kprobe", "not called\n");
|
||||
CHECK(!bss->fentry_called, "fentry", "not called\n");
|
||||
|
||||
cleanup:
|
||||
test_vmlinux__destroy(skel);
|
||||
}
|
62
tools/testing/selftests/bpf/prog_tests/xdp_attach.c
Normal file
62
tools/testing/selftests/bpf/prog_tests/xdp_attach.c
Normal file
@@ -0,0 +1,62 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <test_progs.h>
|
||||
|
||||
#define IFINDEX_LO 1
|
||||
#define XDP_FLAGS_REPLACE (1U << 4)
|
||||
|
||||
void test_xdp_attach(void)
|
||||
{
|
||||
struct bpf_object *obj1, *obj2, *obj3;
|
||||
const char *file = "./test_xdp.o";
|
||||
int err, fd1, fd2, fd3;
|
||||
__u32 duration = 0;
|
||||
DECLARE_LIBBPF_OPTS(bpf_xdp_set_link_opts, opts,
|
||||
.old_fd = -1);
|
||||
|
||||
err = bpf_prog_load(file, BPF_PROG_TYPE_XDP, &obj1, &fd1);
|
||||
if (CHECK_FAIL(err))
|
||||
return;
|
||||
err = bpf_prog_load(file, BPF_PROG_TYPE_XDP, &obj2, &fd2);
|
||||
if (CHECK_FAIL(err))
|
||||
goto out_1;
|
||||
err = bpf_prog_load(file, BPF_PROG_TYPE_XDP, &obj3, &fd3);
|
||||
if (CHECK_FAIL(err))
|
||||
goto out_2;
|
||||
|
||||
err = bpf_set_link_xdp_fd_opts(IFINDEX_LO, fd1, XDP_FLAGS_REPLACE,
|
||||
&opts);
|
||||
if (CHECK(err, "load_ok", "initial load failed"))
|
||||
goto out_close;
|
||||
|
||||
err = bpf_set_link_xdp_fd_opts(IFINDEX_LO, fd2, XDP_FLAGS_REPLACE,
|
||||
&opts);
|
||||
if (CHECK(!err, "load_fail", "load with expected id didn't fail"))
|
||||
goto out;
|
||||
|
||||
opts.old_fd = fd1;
|
||||
err = bpf_set_link_xdp_fd_opts(IFINDEX_LO, fd2, 0, &opts);
|
||||
if (CHECK(err, "replace_ok", "replace valid old_fd failed"))
|
||||
goto out;
|
||||
|
||||
err = bpf_set_link_xdp_fd_opts(IFINDEX_LO, fd3, 0, &opts);
|
||||
if (CHECK(!err, "replace_fail", "replace invalid old_fd didn't fail"))
|
||||
goto out;
|
||||
|
||||
err = bpf_set_link_xdp_fd_opts(IFINDEX_LO, -1, 0, &opts);
|
||||
if (CHECK(!err, "remove_fail", "remove invalid old_fd didn't fail"))
|
||||
goto out;
|
||||
|
||||
opts.old_fd = fd2;
|
||||
err = bpf_set_link_xdp_fd_opts(IFINDEX_LO, -1, 0, &opts);
|
||||
if (CHECK(err, "remove_ok", "remove valid old_fd failed"))
|
||||
goto out;
|
||||
|
||||
out:
|
||||
bpf_set_link_xdp_fd(IFINDEX_LO, -1, 0);
|
||||
out_close:
|
||||
bpf_object__close(obj3);
|
||||
out_2:
|
||||
bpf_object__close(obj2);
|
||||
out_1:
|
||||
bpf_object__close(obj1);
|
||||
}
|
@@ -4,17 +4,51 @@
|
||||
#include "test_xdp.skel.h"
|
||||
#include "test_xdp_bpf2bpf.skel.h"
|
||||
|
||||
struct meta {
|
||||
int ifindex;
|
||||
int pkt_len;
|
||||
};
|
||||
|
||||
static void on_sample(void *ctx, int cpu, void *data, __u32 size)
|
||||
{
|
||||
int duration = 0;
|
||||
struct meta *meta = (struct meta *)data;
|
||||
struct ipv4_packet *trace_pkt_v4 = data + sizeof(*meta);
|
||||
|
||||
if (CHECK(size < sizeof(pkt_v4) + sizeof(*meta),
|
||||
"check_size", "size %u < %zu\n",
|
||||
size, sizeof(pkt_v4) + sizeof(*meta)))
|
||||
return;
|
||||
|
||||
if (CHECK(meta->ifindex != if_nametoindex("lo"), "check_meta_ifindex",
|
||||
"meta->ifindex = %d\n", meta->ifindex))
|
||||
return;
|
||||
|
||||
if (CHECK(meta->pkt_len != sizeof(pkt_v4), "check_meta_pkt_len",
|
||||
"meta->pkt_len = %zd\n", sizeof(pkt_v4)))
|
||||
return;
|
||||
|
||||
if (CHECK(memcmp(trace_pkt_v4, &pkt_v4, sizeof(pkt_v4)),
|
||||
"check_packet_content", "content not the same\n"))
|
||||
return;
|
||||
|
||||
*(bool *)ctx = true;
|
||||
}
|
||||
|
||||
void test_xdp_bpf2bpf(void)
|
||||
{
|
||||
__u32 duration = 0, retval, size;
|
||||
char buf[128];
|
||||
int err, pkt_fd, map_fd;
|
||||
bool passed = false;
|
||||
struct iphdr *iph = (void *)buf + sizeof(struct ethhdr);
|
||||
struct iptnl_info value4 = {.family = AF_INET};
|
||||
struct test_xdp *pkt_skel = NULL;
|
||||
struct test_xdp_bpf2bpf *ftrace_skel = NULL;
|
||||
struct vip key4 = {.protocol = 6, .family = AF_INET};
|
||||
DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts);
|
||||
struct bpf_program *prog;
|
||||
struct perf_buffer *pb = NULL;
|
||||
struct perf_buffer_opts pb_opts = {};
|
||||
|
||||
/* Load XDP program to introspect */
|
||||
pkt_skel = test_xdp__open_and_load();
|
||||
@@ -27,11 +61,21 @@ void test_xdp_bpf2bpf(void)
|
||||
bpf_map_update_elem(map_fd, &key4, &value4, 0);
|
||||
|
||||
/* Load trace program */
|
||||
opts.attach_prog_fd = pkt_fd,
|
||||
ftrace_skel = test_xdp_bpf2bpf__open_opts(&opts);
|
||||
ftrace_skel = test_xdp_bpf2bpf__open();
|
||||
if (CHECK(!ftrace_skel, "__open", "ftrace skeleton failed\n"))
|
||||
goto out;
|
||||
|
||||
/* Demonstrate the bpf_program__set_attach_target() API rather than
|
||||
* the load with options, i.e. opts.attach_prog_fd.
|
||||
*/
|
||||
prog = ftrace_skel->progs.trace_on_entry;
|
||||
bpf_program__set_expected_attach_type(prog, BPF_TRACE_FENTRY);
|
||||
bpf_program__set_attach_target(prog, pkt_fd, "_xdp_tx_iptunnel");
|
||||
|
||||
prog = ftrace_skel->progs.trace_on_exit;
|
||||
bpf_program__set_expected_attach_type(prog, BPF_TRACE_FEXIT);
|
||||
bpf_program__set_attach_target(prog, pkt_fd, "_xdp_tx_iptunnel");
|
||||
|
||||
err = test_xdp_bpf2bpf__load(ftrace_skel);
|
||||
if (CHECK(err, "__load", "ftrace skeleton failed\n"))
|
||||
goto out;
|
||||
@@ -40,6 +84,14 @@ void test_xdp_bpf2bpf(void)
|
||||
if (CHECK(err, "ftrace_attach", "ftrace attach failed: %d\n", err))
|
||||
goto out;
|
||||
|
||||
/* Set up perf buffer */
|
||||
pb_opts.sample_cb = on_sample;
|
||||
pb_opts.ctx = &passed;
|
||||
pb = perf_buffer__new(bpf_map__fd(ftrace_skel->maps.perf_buf_map),
|
||||
1, &pb_opts);
|
||||
if (CHECK(IS_ERR(pb), "perf_buf__new", "err %ld\n", PTR_ERR(pb)))
|
||||
goto out;
|
||||
|
||||
/* Run test program */
|
||||
err = bpf_prog_test_run(pkt_fd, 1, &pkt_v4, sizeof(pkt_v4),
|
||||
buf, &size, &retval, &duration);
|
||||
@@ -50,6 +102,15 @@ void test_xdp_bpf2bpf(void)
|
||||
err, errno, retval, size))
|
||||
goto out;
|
||||
|
||||
/* Make sure bpf_xdp_output() was triggered and it sent the expected
|
||||
* data to the perf ring buffer.
|
||||
*/
|
||||
err = perf_buffer__poll(pb, 100);
|
||||
if (CHECK(err < 0, "perf_buffer__poll", "err %d\n", err))
|
||||
goto out;
|
||||
|
||||
CHECK_FAIL(!passed);
|
||||
|
||||
/* Verify test results */
|
||||
if (CHECK(ftrace_skel->bss->test_result_fentry != if_nametoindex("lo"),
|
||||
"result", "fentry failed err %llu\n",
|
||||
@@ -60,6 +121,8 @@ void test_xdp_bpf2bpf(void)
|
||||
"fexit failed err %llu\n", ftrace_skel->bss->test_result_fexit);
|
||||
|
||||
out:
|
||||
if (pb)
|
||||
perf_buffer__free(pb);
|
||||
test_xdp__destroy(pkt_skel);
|
||||
test_xdp_bpf2bpf__destroy(ftrace_skel);
|
||||
}
|
||||
|
@@ -6,14 +6,24 @@
|
||||
* the kernel BPF logic.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/types.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include "bpf_trace_helpers.h"
|
||||
#include <bpf/bpf_tracing.h>
|
||||
#include "bpf_tcp_helpers.h"
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
int stg_result = 0;
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_SK_STORAGE);
|
||||
__uint(map_flags, BPF_F_NO_PREALLOC);
|
||||
__type(key, int);
|
||||
__type(value, int);
|
||||
} sk_stg_map SEC(".maps");
|
||||
|
||||
#define DCTCP_MAX_ALPHA 1024U
|
||||
|
||||
struct dctcp {
|
||||
@@ -43,12 +53,18 @@ void BPF_PROG(dctcp_init, struct sock *sk)
|
||||
{
|
||||
const struct tcp_sock *tp = tcp_sk(sk);
|
||||
struct dctcp *ca = inet_csk_ca(sk);
|
||||
int *stg;
|
||||
|
||||
ca->prior_rcv_nxt = tp->rcv_nxt;
|
||||
ca->dctcp_alpha = min(dctcp_alpha_on_init, DCTCP_MAX_ALPHA);
|
||||
ca->loss_cwnd = 0;
|
||||
ca->ce_state = 0;
|
||||
|
||||
stg = bpf_sk_storage_get(&sk_stg_map, (void *)tp, NULL, 0);
|
||||
if (stg) {
|
||||
stg_result = *stg;
|
||||
bpf_sk_storage_delete(&sk_stg_map, (void *)tp);
|
||||
}
|
||||
dctcp_reset(tp, ca);
|
||||
}
|
||||
|
||||
|
@@ -13,7 +13,7 @@ enum e1 {
|
||||
|
||||
enum e2 {
|
||||
C = 100,
|
||||
D = -100,
|
||||
D = 4294967295,
|
||||
E = 0,
|
||||
};
|
||||
|
||||
|
@@ -2,7 +2,7 @@
|
||||
/* Copyright (c) 2019 Facebook */
|
||||
#include <linux/bpf.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include "bpf_trace_helpers.h"
|
||||
#include <bpf/bpf_tracing.h>
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
|
@@ -5,7 +5,7 @@
|
||||
#include <linux/bpf.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_endian.h>
|
||||
#include "bpf_trace_helpers.h"
|
||||
#include <bpf/bpf_tracing.h>
|
||||
|
||||
struct sk_buff {
|
||||
unsigned int len;
|
||||
|
@@ -2,7 +2,7 @@
|
||||
/* Copyright (c) 2019 Facebook */
|
||||
#include <linux/bpf.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include "bpf_trace_helpers.h"
|
||||
#include <bpf/bpf_tracing.h>
|
||||
|
||||
struct sk_buff {
|
||||
unsigned int len;
|
||||
|
@@ -2,7 +2,7 @@
|
||||
/* Copyright (c) 2019 Facebook */
|
||||
#include <linux/bpf.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include "bpf_trace_helpers.h"
|
||||
#include <bpf/bpf_tracing.h>
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
|
@@ -4,7 +4,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_endian.h>
|
||||
#include "bpf_trace_helpers.h"
|
||||
#include <bpf/bpf_tracing.h>
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
struct {
|
||||
|
48
tools/testing/selftests/bpf/progs/lsm.c
Normal file
48
tools/testing/selftests/bpf/progs/lsm.c
Normal file
@@ -0,0 +1,48 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
/*
|
||||
* Copyright 2020 Google LLC.
|
||||
*/
|
||||
|
||||
#include "vmlinux.h"
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
#include <errno.h>
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
int monitored_pid = 0;
|
||||
int mprotect_count = 0;
|
||||
int bprm_count = 0;
|
||||
|
||||
SEC("lsm/file_mprotect")
|
||||
int BPF_PROG(test_int_hook, struct vm_area_struct *vma,
|
||||
unsigned long reqprot, unsigned long prot, int ret)
|
||||
{
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
__u32 pid = bpf_get_current_pid_tgid() >> 32;
|
||||
int is_heap = 0;
|
||||
|
||||
is_heap = (vma->vm_start >= vma->vm_mm->start_brk &&
|
||||
vma->vm_end <= vma->vm_mm->brk);
|
||||
|
||||
if (is_heap && monitored_pid == pid) {
|
||||
mprotect_count++;
|
||||
ret = -EPERM;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
SEC("lsm/bprm_committed_creds")
|
||||
int BPF_PROG(test_void_hook, struct linux_binprm *bprm)
|
||||
{
|
||||
__u32 pid = bpf_get_current_pid_tgid() >> 32;
|
||||
|
||||
if (monitored_pid == pid)
|
||||
bprm_count++;
|
||||
|
||||
return 0;
|
||||
}
|
49
tools/testing/selftests/bpf/progs/modify_return.c
Normal file
49
tools/testing/selftests/bpf/progs/modify_return.c
Normal file
@@ -0,0 +1,49 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
/*
|
||||
* Copyright 2020 Google LLC.
|
||||
*/
|
||||
|
||||
#include <linux/bpf.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
static int sequence = 0;
|
||||
__s32 input_retval = 0;
|
||||
|
||||
__u64 fentry_result = 0;
|
||||
SEC("fentry/bpf_modify_return_test")
|
||||
int BPF_PROG(fentry_test, int a, __u64 b)
|
||||
{
|
||||
sequence++;
|
||||
fentry_result = (sequence == 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
__u64 fmod_ret_result = 0;
|
||||
SEC("fmod_ret/bpf_modify_return_test")
|
||||
int BPF_PROG(fmod_ret_test, int a, int *b, int ret)
|
||||
{
|
||||
sequence++;
|
||||
/* This is the first fmod_ret program, the ret passed should be 0 */
|
||||
fmod_ret_result = (sequence == 2 && ret == 0);
|
||||
return input_retval;
|
||||
}
|
||||
|
||||
__u64 fexit_result = 0;
|
||||
SEC("fexit/bpf_modify_return_test")
|
||||
int BPF_PROG(fexit_test, int a, __u64 b, int ret)
|
||||
{
|
||||
sequence++;
|
||||
/* If the input_reval is non-zero a successful modification should have
|
||||
* occurred.
|
||||
*/
|
||||
if (input_retval)
|
||||
fexit_result = (sequence == 3 && ret == input_retval);
|
||||
else
|
||||
fexit_result = (sequence == 3 && ret == 4);
|
||||
|
||||
return 0;
|
||||
}
|
@@ -12,7 +12,6 @@ int bpf_prog1(struct __sk_buff *skb)
|
||||
__u32 lport = skb->local_port;
|
||||
__u32 rport = skb->remote_port;
|
||||
__u8 *d = data;
|
||||
__u32 len = (__u32) data_end - (__u32) data;
|
||||
int err;
|
||||
|
||||
if (data + 10 > data_end) {
|
||||
|
@@ -4,6 +4,7 @@
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
|
||||
int kprobe_res = 0;
|
||||
int kretprobe_res = 0;
|
||||
@@ -18,7 +19,7 @@ int handle_kprobe(struct pt_regs *ctx)
|
||||
}
|
||||
|
||||
SEC("kretprobe/sys_nanosleep")
|
||||
int handle_kretprobe(struct pt_regs *ctx)
|
||||
int BPF_KRETPROBE(handle_kretprobe)
|
||||
{
|
||||
kretprobe_res = 2;
|
||||
return 0;
|
||||
|
24
tools/testing/selftests/bpf/progs/test_cgroup_link.c
Normal file
24
tools/testing/selftests/bpf/progs/test_cgroup_link.c
Normal file
@@ -0,0 +1,24 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2020 Facebook
|
||||
#include <linux/bpf.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
|
||||
int calls = 0;
|
||||
int alt_calls = 0;
|
||||
|
||||
SEC("cgroup_skb/egress1")
|
||||
int egress(struct __sk_buff *skb)
|
||||
{
|
||||
__sync_fetch_and_add(&calls, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
SEC("cgroup_skb/egress2")
|
||||
int egress_alt(struct __sk_buff *skb)
|
||||
{
|
||||
__sync_fetch_and_add(&alt_calls, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
26
tools/testing/selftests/bpf/progs/test_get_stack_rawtp_err.c
Normal file
26
tools/testing/selftests/bpf/progs/test_get_stack_rawtp_err.c
Normal file
@@ -0,0 +1,26 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <linux/bpf.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
|
||||
#define MAX_STACK_RAWTP 10
|
||||
|
||||
SEC("raw_tracepoint/sys_enter")
|
||||
int bpf_prog2(void *ctx)
|
||||
{
|
||||
__u64 stack[MAX_STACK_RAWTP];
|
||||
int error;
|
||||
|
||||
/* set all the flags which should return -EINVAL */
|
||||
error = bpf_get_stack(ctx, stack, 0, -1);
|
||||
if (error < 0)
|
||||
goto loop;
|
||||
|
||||
return error;
|
||||
loop:
|
||||
while (1) {
|
||||
error++;
|
||||
}
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
@@ -68,7 +68,7 @@ static struct foo struct3 = {
|
||||
bpf_map_update_elem(&result_##map, &key, var, 0); \
|
||||
} while (0)
|
||||
|
||||
SEC("static_data_load")
|
||||
SEC("classifier/static_data_load")
|
||||
int load_static_data(struct __sk_buff *skb)
|
||||
{
|
||||
static const __u64 bar = ~0;
|
||||
|
25
tools/testing/selftests/bpf/progs/test_link_pinning.c
Normal file
25
tools/testing/selftests/bpf/progs/test_link_pinning.c
Normal file
@@ -0,0 +1,25 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2020 Facebook */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
|
||||
int in = 0;
|
||||
int out = 0;
|
||||
|
||||
SEC("raw_tp/sys_enter")
|
||||
int raw_tp_prog(const void *ctx)
|
||||
{
|
||||
out = in;
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("tp_btf/sys_enter")
|
||||
int tp_btf_prog(const void *ctx)
|
||||
{
|
||||
out = in;
|
||||
return 0;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
37
tools/testing/selftests/bpf/progs/test_ns_current_pid_tgid.c
Normal file
37
tools/testing/selftests/bpf/progs/test_ns_current_pid_tgid.c
Normal file
@@ -0,0 +1,37 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2019 Carlos Neira cneirabustos@gmail.com */
|
||||
|
||||
#include <linux/bpf.h>
|
||||
#include <stdint.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
|
||||
static volatile struct {
|
||||
__u64 dev;
|
||||
__u64 ino;
|
||||
__u64 pid_tgid;
|
||||
__u64 user_pid_tgid;
|
||||
} res;
|
||||
|
||||
SEC("raw_tracepoint/sys_enter")
|
||||
int trace(void *ctx)
|
||||
{
|
||||
__u64 ns_pid_tgid, expected_pid;
|
||||
struct bpf_pidns_info nsdata;
|
||||
__u32 key = 0;
|
||||
|
||||
if (bpf_get_ns_current_pid_tgid(res.dev, res.ino, &nsdata,
|
||||
sizeof(struct bpf_pidns_info)))
|
||||
return 0;
|
||||
|
||||
ns_pid_tgid = (__u64)nsdata.tgid << 32 | nsdata.pid;
|
||||
expected_pid = res.user_pid_tgid;
|
||||
|
||||
if (expected_pid != ns_pid_tgid)
|
||||
return 0;
|
||||
|
||||
res.pid_tgid = ns_pid_tgid;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
@@ -6,7 +6,6 @@
|
||||
#include <linux/ptrace.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
#include "bpf_trace_helpers.h"
|
||||
|
||||
struct task_struct;
|
||||
|
||||
@@ -17,11 +16,9 @@ int BPF_KPROBE(prog1, struct task_struct *tsk, const char *buf, bool exec)
|
||||
}
|
||||
|
||||
SEC("kretprobe/__set_task_comm")
|
||||
int BPF_KRETPROBE(prog2,
|
||||
struct task_struct *tsk, const char *buf, bool exec,
|
||||
int ret)
|
||||
int BPF_KRETPROBE(prog2, int ret)
|
||||
{
|
||||
return !PT_REGS_PARM1(ctx) && ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
SEC("raw_tp/task_rename")
|
||||
|
50
tools/testing/selftests/bpf/progs/test_perf_branches.c
Normal file
50
tools/testing/selftests/bpf/progs/test_perf_branches.c
Normal file
@@ -0,0 +1,50 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2019 Facebook
|
||||
|
||||
#include <stddef.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
|
||||
int valid = 0;
|
||||
int required_size_out = 0;
|
||||
int written_stack_out = 0;
|
||||
int written_global_out = 0;
|
||||
|
||||
struct {
|
||||
__u64 _a;
|
||||
__u64 _b;
|
||||
__u64 _c;
|
||||
} fpbe[30] = {0};
|
||||
|
||||
SEC("perf_event")
|
||||
int perf_branches(void *ctx)
|
||||
{
|
||||
__u64 entries[4 * 3] = {0};
|
||||
int required_size, written_stack, written_global;
|
||||
|
||||
/* write to stack */
|
||||
written_stack = bpf_read_branch_records(ctx, entries, sizeof(entries), 0);
|
||||
/* ignore spurious events */
|
||||
if (!written_stack)
|
||||
return 1;
|
||||
|
||||
/* get required size */
|
||||
required_size = bpf_read_branch_records(ctx, NULL, 0,
|
||||
BPF_F_GET_BRANCH_RECORDS_SIZE);
|
||||
|
||||
written_global = bpf_read_branch_records(ctx, fpbe, sizeof(fpbe), 0);
|
||||
/* ignore spurious events */
|
||||
if (!written_global)
|
||||
return 1;
|
||||
|
||||
required_size_out = required_size;
|
||||
written_stack_out = written_stack;
|
||||
written_global_out = written_global;
|
||||
valid = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
@@ -4,7 +4,7 @@
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include "bpf_trace_helpers.h"
|
||||
#include <bpf/bpf_tracing.h>
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
|
||||
|
@@ -7,7 +7,6 @@
|
||||
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
#include "bpf_trace_helpers.h"
|
||||
|
||||
static struct sockaddr_in old;
|
||||
|
||||
|
204
tools/testing/selftests/bpf/progs/test_sk_assign.c
Normal file
204
tools/testing/selftests/bpf/progs/test_sk_assign.c
Normal file
@@ -0,0 +1,204 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2019 Cloudflare Ltd.
|
||||
// Copyright (c) 2020 Isovalent, Inc.
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <linux/pkt_cls.h>
|
||||
#include <linux/tcp.h>
|
||||
#include <sys/socket.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_endian.h>
|
||||
|
||||
int _version SEC("version") = 1;
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
/* Fill 'tuple' with L3 info, and attempt to find L4. On fail, return NULL. */
|
||||
static inline struct bpf_sock_tuple *
|
||||
get_tuple(struct __sk_buff *skb, bool *ipv4, bool *tcp)
|
||||
{
|
||||
void *data_end = (void *)(long)skb->data_end;
|
||||
void *data = (void *)(long)skb->data;
|
||||
struct bpf_sock_tuple *result;
|
||||
struct ethhdr *eth;
|
||||
__u64 tuple_len;
|
||||
__u8 proto = 0;
|
||||
__u64 ihl_len;
|
||||
|
||||
eth = (struct ethhdr *)(data);
|
||||
if (eth + 1 > data_end)
|
||||
return NULL;
|
||||
|
||||
if (eth->h_proto == bpf_htons(ETH_P_IP)) {
|
||||
struct iphdr *iph = (struct iphdr *)(data + sizeof(*eth));
|
||||
|
||||
if (iph + 1 > data_end)
|
||||
return NULL;
|
||||
if (iph->ihl != 5)
|
||||
/* Options are not supported */
|
||||
return NULL;
|
||||
ihl_len = iph->ihl * 4;
|
||||
proto = iph->protocol;
|
||||
*ipv4 = true;
|
||||
result = (struct bpf_sock_tuple *)&iph->saddr;
|
||||
} else if (eth->h_proto == bpf_htons(ETH_P_IPV6)) {
|
||||
struct ipv6hdr *ip6h = (struct ipv6hdr *)(data + sizeof(*eth));
|
||||
|
||||
if (ip6h + 1 > data_end)
|
||||
return NULL;
|
||||
ihl_len = sizeof(*ip6h);
|
||||
proto = ip6h->nexthdr;
|
||||
*ipv4 = false;
|
||||
result = (struct bpf_sock_tuple *)&ip6h->saddr;
|
||||
} else {
|
||||
return (struct bpf_sock_tuple *)data;
|
||||
}
|
||||
|
||||
if (proto != IPPROTO_TCP && proto != IPPROTO_UDP)
|
||||
return NULL;
|
||||
|
||||
*tcp = (proto == IPPROTO_TCP);
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline int
|
||||
handle_udp(struct __sk_buff *skb, struct bpf_sock_tuple *tuple, bool ipv4)
|
||||
{
|
||||
struct bpf_sock_tuple ln = {0};
|
||||
struct bpf_sock *sk;
|
||||
size_t tuple_len;
|
||||
int ret;
|
||||
|
||||
tuple_len = ipv4 ? sizeof(tuple->ipv4) : sizeof(tuple->ipv6);
|
||||
if ((void *)tuple + tuple_len > (void *)(long)skb->data_end)
|
||||
return TC_ACT_SHOT;
|
||||
|
||||
sk = bpf_sk_lookup_udp(skb, tuple, tuple_len, BPF_F_CURRENT_NETNS, 0);
|
||||
if (sk)
|
||||
goto assign;
|
||||
|
||||
if (ipv4) {
|
||||
if (tuple->ipv4.dport != bpf_htons(4321))
|
||||
return TC_ACT_OK;
|
||||
|
||||
ln.ipv4.daddr = bpf_htonl(0x7f000001);
|
||||
ln.ipv4.dport = bpf_htons(1234);
|
||||
|
||||
sk = bpf_sk_lookup_udp(skb, &ln, sizeof(ln.ipv4),
|
||||
BPF_F_CURRENT_NETNS, 0);
|
||||
} else {
|
||||
if (tuple->ipv6.dport != bpf_htons(4321))
|
||||
return TC_ACT_OK;
|
||||
|
||||
/* Upper parts of daddr are already zero. */
|
||||
ln.ipv6.daddr[3] = bpf_htonl(0x1);
|
||||
ln.ipv6.dport = bpf_htons(1234);
|
||||
|
||||
sk = bpf_sk_lookup_udp(skb, &ln, sizeof(ln.ipv6),
|
||||
BPF_F_CURRENT_NETNS, 0);
|
||||
}
|
||||
|
||||
/* workaround: We can't do a single socket lookup here, because then
|
||||
* the compiler will likely spill tuple_len to the stack. This makes it
|
||||
* lose all bounds information in the verifier, which then rejects the
|
||||
* call as unsafe.
|
||||
*/
|
||||
if (!sk)
|
||||
return TC_ACT_SHOT;
|
||||
|
||||
assign:
|
||||
ret = bpf_sk_assign(skb, sk, 0);
|
||||
bpf_sk_release(sk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
handle_tcp(struct __sk_buff *skb, struct bpf_sock_tuple *tuple, bool ipv4)
|
||||
{
|
||||
struct bpf_sock_tuple ln = {0};
|
||||
struct bpf_sock *sk;
|
||||
size_t tuple_len;
|
||||
int ret;
|
||||
|
||||
tuple_len = ipv4 ? sizeof(tuple->ipv4) : sizeof(tuple->ipv6);
|
||||
if ((void *)tuple + tuple_len > (void *)(long)skb->data_end)
|
||||
return TC_ACT_SHOT;
|
||||
|
||||
sk = bpf_skc_lookup_tcp(skb, tuple, tuple_len, BPF_F_CURRENT_NETNS, 0);
|
||||
if (sk) {
|
||||
if (sk->state != BPF_TCP_LISTEN)
|
||||
goto assign;
|
||||
bpf_sk_release(sk);
|
||||
}
|
||||
|
||||
if (ipv4) {
|
||||
if (tuple->ipv4.dport != bpf_htons(4321))
|
||||
return TC_ACT_OK;
|
||||
|
||||
ln.ipv4.daddr = bpf_htonl(0x7f000001);
|
||||
ln.ipv4.dport = bpf_htons(1234);
|
||||
|
||||
sk = bpf_skc_lookup_tcp(skb, &ln, sizeof(ln.ipv4),
|
||||
BPF_F_CURRENT_NETNS, 0);
|
||||
} else {
|
||||
if (tuple->ipv6.dport != bpf_htons(4321))
|
||||
return TC_ACT_OK;
|
||||
|
||||
/* Upper parts of daddr are already zero. */
|
||||
ln.ipv6.daddr[3] = bpf_htonl(0x1);
|
||||
ln.ipv6.dport = bpf_htons(1234);
|
||||
|
||||
sk = bpf_skc_lookup_tcp(skb, &ln, sizeof(ln.ipv6),
|
||||
BPF_F_CURRENT_NETNS, 0);
|
||||
}
|
||||
|
||||
/* workaround: We can't do a single socket lookup here, because then
|
||||
* the compiler will likely spill tuple_len to the stack. This makes it
|
||||
* lose all bounds information in the verifier, which then rejects the
|
||||
* call as unsafe.
|
||||
*/
|
||||
if (!sk)
|
||||
return TC_ACT_SHOT;
|
||||
|
||||
if (sk->state != BPF_TCP_LISTEN) {
|
||||
bpf_sk_release(sk);
|
||||
return TC_ACT_SHOT;
|
||||
}
|
||||
|
||||
assign:
|
||||
ret = bpf_sk_assign(skb, sk, 0);
|
||||
bpf_sk_release(sk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
SEC("classifier/sk_assign_test")
|
||||
int bpf_sk_assign_test(struct __sk_buff *skb)
|
||||
{
|
||||
struct bpf_sock_tuple *tuple, ln = {0};
|
||||
bool ipv4 = false;
|
||||
bool tcp = false;
|
||||
int tuple_len;
|
||||
int ret = 0;
|
||||
|
||||
tuple = get_tuple(skb, &ipv4, &tcp);
|
||||
if (!tuple)
|
||||
return TC_ACT_SHOT;
|
||||
|
||||
/* Note that the verifier socket return type for bpf_skc_lookup_tcp()
|
||||
* differs from bpf_sk_lookup_udp(), so even though the C-level type is
|
||||
* the same here, if we try to share the implementations they will
|
||||
* fail to verify because we're crossing pointer types.
|
||||
*/
|
||||
if (tcp)
|
||||
ret = handle_tcp(skb, tuple, ipv4);
|
||||
else
|
||||
ret = handle_udp(skb, tuple, ipv4);
|
||||
|
||||
return ret == 0 ? TC_ACT_OK : TC_ACT_SHOT;
|
||||
}
|
@@ -23,6 +23,8 @@ int process(struct __sk_buff *skb)
|
||||
return 1;
|
||||
if (skb->gso_segs != 8)
|
||||
return 1;
|
||||
if (skb->gso_size != 10)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
98
tools/testing/selftests/bpf/progs/test_sockmap_listen.c
Normal file
98
tools/testing/selftests/bpf/progs/test_sockmap_listen.c
Normal file
@@ -0,0 +1,98 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2020 Cloudflare
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <linux/bpf.h>
|
||||
|
||||
#include <bpf/bpf_helpers.h>
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_SOCKMAP);
|
||||
__uint(max_entries, 2);
|
||||
__type(key, __u32);
|
||||
__type(value, __u64);
|
||||
} sock_map SEC(".maps");
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_SOCKHASH);
|
||||
__uint(max_entries, 2);
|
||||
__type(key, __u32);
|
||||
__type(value, __u64);
|
||||
} sock_hash SEC(".maps");
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||
__uint(max_entries, 2);
|
||||
__type(key, int);
|
||||
__type(value, unsigned int);
|
||||
} verdict_map SEC(".maps");
|
||||
|
||||
static volatile bool test_sockmap; /* toggled by user-space */
|
||||
|
||||
SEC("sk_skb/stream_parser")
|
||||
int prog_skb_parser(struct __sk_buff *skb)
|
||||
{
|
||||
return skb->len;
|
||||
}
|
||||
|
||||
SEC("sk_skb/stream_verdict")
|
||||
int prog_skb_verdict(struct __sk_buff *skb)
|
||||
{
|
||||
unsigned int *count;
|
||||
__u32 zero = 0;
|
||||
int verdict;
|
||||
|
||||
if (test_sockmap)
|
||||
verdict = bpf_sk_redirect_map(skb, &sock_map, zero, 0);
|
||||
else
|
||||
verdict = bpf_sk_redirect_hash(skb, &sock_hash, &zero, 0);
|
||||
|
||||
count = bpf_map_lookup_elem(&verdict_map, &verdict);
|
||||
if (count)
|
||||
(*count)++;
|
||||
|
||||
return verdict;
|
||||
}
|
||||
|
||||
SEC("sk_msg")
|
||||
int prog_msg_verdict(struct sk_msg_md *msg)
|
||||
{
|
||||
unsigned int *count;
|
||||
__u32 zero = 0;
|
||||
int verdict;
|
||||
|
||||
if (test_sockmap)
|
||||
verdict = bpf_msg_redirect_map(msg, &sock_map, zero, 0);
|
||||
else
|
||||
verdict = bpf_msg_redirect_hash(msg, &sock_hash, &zero, 0);
|
||||
|
||||
count = bpf_map_lookup_elem(&verdict_map, &verdict);
|
||||
if (count)
|
||||
(*count)++;
|
||||
|
||||
return verdict;
|
||||
}
|
||||
|
||||
SEC("sk_reuseport")
|
||||
int prog_reuseport(struct sk_reuseport_md *reuse)
|
||||
{
|
||||
unsigned int *count;
|
||||
int err, verdict;
|
||||
__u32 zero = 0;
|
||||
|
||||
if (test_sockmap)
|
||||
err = bpf_sk_select_reuseport(reuse, &sock_map, &zero, 0);
|
||||
else
|
||||
err = bpf_sk_select_reuseport(reuse, &sock_hash, &zero, 0);
|
||||
verdict = err ? SK_DROP : SK_PASS;
|
||||
|
||||
count = bpf_map_lookup_elem(&verdict_map, &verdict);
|
||||
if (count)
|
||||
(*count)++;
|
||||
|
||||
return verdict;
|
||||
}
|
||||
|
||||
int _version SEC("version") = 1;
|
||||
char _license[] SEC("license") = "GPL";
|
@@ -2,7 +2,8 @@
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <linux/bpf.h>
|
||||
#include "bpf_trace_helpers.h"
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
|
||||
struct task_struct;
|
||||
|
||||
|
84
tools/testing/selftests/bpf/progs/test_vmlinux.c
Normal file
84
tools/testing/selftests/bpf/progs/test_vmlinux.c
Normal file
@@ -0,0 +1,84 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2020 Facebook */
|
||||
|
||||
#include "vmlinux.h"
|
||||
#include <asm/unistd.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
#include <bpf/bpf_core_read.h>
|
||||
|
||||
#define MY_TV_NSEC 1337
|
||||
|
||||
bool tp_called = false;
|
||||
bool raw_tp_called = false;
|
||||
bool tp_btf_called = false;
|
||||
bool kprobe_called = false;
|
||||
bool fentry_called = false;
|
||||
|
||||
SEC("tp/syscalls/sys_enter_nanosleep")
|
||||
int handle__tp(struct trace_event_raw_sys_enter *args)
|
||||
{
|
||||
struct __kernel_timespec *ts;
|
||||
|
||||
if (args->id != __NR_nanosleep)
|
||||
return 0;
|
||||
|
||||
ts = (void *)args->args[0];
|
||||
if (BPF_CORE_READ(ts, tv_nsec) != MY_TV_NSEC)
|
||||
return 0;
|
||||
|
||||
tp_called = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("raw_tp/sys_enter")
|
||||
int BPF_PROG(handle__raw_tp, struct pt_regs *regs, long id)
|
||||
{
|
||||
struct __kernel_timespec *ts;
|
||||
|
||||
if (id != __NR_nanosleep)
|
||||
return 0;
|
||||
|
||||
ts = (void *)PT_REGS_PARM1_CORE(regs);
|
||||
if (BPF_CORE_READ(ts, tv_nsec) != MY_TV_NSEC)
|
||||
return 0;
|
||||
|
||||
raw_tp_called = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("tp_btf/sys_enter")
|
||||
int BPF_PROG(handle__tp_btf, struct pt_regs *regs, long id)
|
||||
{
|
||||
struct __kernel_timespec *ts;
|
||||
|
||||
if (id != __NR_nanosleep)
|
||||
return 0;
|
||||
|
||||
ts = (void *)PT_REGS_PARM1_CORE(regs);
|
||||
if (BPF_CORE_READ(ts, tv_nsec) != MY_TV_NSEC)
|
||||
return 0;
|
||||
|
||||
tp_btf_called = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("kprobe/hrtimer_nanosleep")
|
||||
int BPF_KPROBE(handle__kprobe,
|
||||
ktime_t rqtp, enum hrtimer_mode mode, clockid_t clockid)
|
||||
{
|
||||
if (rqtp == MY_TV_NSEC)
|
||||
kprobe_called = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("fentry/hrtimer_nanosleep")
|
||||
int BPF_PROG(handle__fentry,
|
||||
ktime_t rqtp, enum hrtimer_mode mode, clockid_t clockid)
|
||||
{
|
||||
if (rqtp == MY_TV_NSEC)
|
||||
fentry_called = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
@@ -1,7 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <linux/bpf.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include "bpf_trace_helpers.h"
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
struct net_device {
|
||||
/* Structure does not need to contain all entries,
|
||||
@@ -27,16 +29,38 @@ struct xdp_buff {
|
||||
struct xdp_rxq_info *rxq;
|
||||
} __attribute__((preserve_access_index));
|
||||
|
||||
struct meta {
|
||||
int ifindex;
|
||||
int pkt_len;
|
||||
};
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
|
||||
__uint(key_size, sizeof(int));
|
||||
__uint(value_size, sizeof(int));
|
||||
} perf_buf_map SEC(".maps");
|
||||
|
||||
__u64 test_result_fentry = 0;
|
||||
SEC("fentry/_xdp_tx_iptunnel")
|
||||
SEC("fentry/FUNC")
|
||||
int BPF_PROG(trace_on_entry, struct xdp_buff *xdp)
|
||||
{
|
||||
struct meta meta;
|
||||
void *data_end = (void *)(long)xdp->data_end;
|
||||
void *data = (void *)(long)xdp->data;
|
||||
|
||||
meta.ifindex = xdp->rxq->dev->ifindex;
|
||||
meta.pkt_len = data_end - data;
|
||||
bpf_xdp_output(xdp, &perf_buf_map,
|
||||
((__u64) meta.pkt_len << 32) |
|
||||
BPF_F_CURRENT_CPU,
|
||||
&meta, sizeof(meta));
|
||||
|
||||
test_result_fentry = xdp->rxq->dev->ifindex;
|
||||
return 0;
|
||||
}
|
||||
|
||||
__u64 test_result_fexit = 0;
|
||||
SEC("fexit/_xdp_tx_iptunnel")
|
||||
SEC("fexit/FUNC")
|
||||
int BPF_PROG(trace_on_exit, struct xdp_buff *xdp, int ret)
|
||||
{
|
||||
test_result_fexit = ret;
|
||||
|
178
tools/testing/selftests/bpf/test_bpftool.py
Normal file
178
tools/testing/selftests/bpf/test_bpftool.py
Normal file
@@ -0,0 +1,178 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# Copyright (c) 2020 SUSE LLC.
|
||||
|
||||
import collections
|
||||
import functools
|
||||
import json
|
||||
import os
|
||||
import socket
|
||||
import subprocess
|
||||
import unittest
|
||||
|
||||
|
||||
# Add the source tree of bpftool and /usr/local/sbin to PATH
|
||||
cur_dir = os.path.dirname(os.path.realpath(__file__))
|
||||
bpftool_dir = os.path.abspath(os.path.join(cur_dir, "..", "..", "..", "..",
|
||||
"tools", "bpf", "bpftool"))
|
||||
os.environ["PATH"] = bpftool_dir + ":/usr/local/sbin:" + os.environ["PATH"]
|
||||
|
||||
|
||||
class IfaceNotFoundError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class UnprivilegedUserError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def _bpftool(args, json=True):
|
||||
_args = ["bpftool"]
|
||||
if json:
|
||||
_args.append("-j")
|
||||
_args.extend(args)
|
||||
|
||||
return subprocess.check_output(_args)
|
||||
|
||||
|
||||
def bpftool(args):
|
||||
return _bpftool(args, json=False).decode("utf-8")
|
||||
|
||||
|
||||
def bpftool_json(args):
|
||||
res = _bpftool(args)
|
||||
return json.loads(res)
|
||||
|
||||
|
||||
def get_default_iface():
|
||||
for iface in socket.if_nameindex():
|
||||
if iface[1] != "lo":
|
||||
return iface[1]
|
||||
raise IfaceNotFoundError("Could not find any network interface to probe")
|
||||
|
||||
|
||||
def default_iface(f):
|
||||
@functools.wraps(f)
|
||||
def wrapper(*args, **kwargs):
|
||||
iface = get_default_iface()
|
||||
return f(*args, iface, **kwargs)
|
||||
return wrapper
|
||||
|
||||
|
||||
class TestBpftool(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
if os.getuid() != 0:
|
||||
raise UnprivilegedUserError(
|
||||
"This test suite needs root privileges")
|
||||
|
||||
@default_iface
|
||||
def test_feature_dev_json(self, iface):
|
||||
unexpected_helpers = [
|
||||
"bpf_probe_write_user",
|
||||
"bpf_trace_printk",
|
||||
]
|
||||
expected_keys = [
|
||||
"syscall_config",
|
||||
"program_types",
|
||||
"map_types",
|
||||
"helpers",
|
||||
"misc",
|
||||
]
|
||||
|
||||
res = bpftool_json(["feature", "probe", "dev", iface])
|
||||
# Check if the result has all expected keys.
|
||||
self.assertCountEqual(res.keys(), expected_keys)
|
||||
# Check if unexpected helpers are not included in helpers probes
|
||||
# result.
|
||||
for helpers in res["helpers"].values():
|
||||
for unexpected_helper in unexpected_helpers:
|
||||
self.assertNotIn(unexpected_helper, helpers)
|
||||
|
||||
def test_feature_kernel(self):
|
||||
test_cases = [
|
||||
bpftool_json(["feature", "probe", "kernel"]),
|
||||
bpftool_json(["feature", "probe"]),
|
||||
bpftool_json(["feature"]),
|
||||
]
|
||||
unexpected_helpers = [
|
||||
"bpf_probe_write_user",
|
||||
"bpf_trace_printk",
|
||||
]
|
||||
expected_keys = [
|
||||
"syscall_config",
|
||||
"system_config",
|
||||
"program_types",
|
||||
"map_types",
|
||||
"helpers",
|
||||
"misc",
|
||||
]
|
||||
|
||||
for tc in test_cases:
|
||||
# Check if the result has all expected keys.
|
||||
self.assertCountEqual(tc.keys(), expected_keys)
|
||||
# Check if unexpected helpers are not included in helpers probes
|
||||
# result.
|
||||
for helpers in tc["helpers"].values():
|
||||
for unexpected_helper in unexpected_helpers:
|
||||
self.assertNotIn(unexpected_helper, helpers)
|
||||
|
||||
def test_feature_kernel_full(self):
|
||||
test_cases = [
|
||||
bpftool_json(["feature", "probe", "kernel", "full"]),
|
||||
bpftool_json(["feature", "probe", "full"]),
|
||||
]
|
||||
expected_helpers = [
|
||||
"bpf_probe_write_user",
|
||||
"bpf_trace_printk",
|
||||
]
|
||||
|
||||
for tc in test_cases:
|
||||
# Check if expected helpers are included at least once in any
|
||||
# helpers list for any program type. Unfortunately we cannot assume
|
||||
# that they will be included in all program types or a specific
|
||||
# subset of programs. It depends on the kernel version and
|
||||
# configuration.
|
||||
found_helpers = False
|
||||
|
||||
for helpers in tc["helpers"].values():
|
||||
if all(expected_helper in helpers
|
||||
for expected_helper in expected_helpers):
|
||||
found_helpers = True
|
||||
break
|
||||
|
||||
self.assertTrue(found_helpers)
|
||||
|
||||
def test_feature_kernel_full_vs_not_full(self):
|
||||
full_res = bpftool_json(["feature", "probe", "full"])
|
||||
not_full_res = bpftool_json(["feature", "probe"])
|
||||
not_full_set = set()
|
||||
full_set = set()
|
||||
|
||||
for helpers in full_res["helpers"].values():
|
||||
for helper in helpers:
|
||||
full_set.add(helper)
|
||||
|
||||
for helpers in not_full_res["helpers"].values():
|
||||
for helper in helpers:
|
||||
not_full_set.add(helper)
|
||||
|
||||
self.assertCountEqual(full_set - not_full_set,
|
||||
{"bpf_probe_write_user", "bpf_trace_printk"})
|
||||
self.assertCountEqual(not_full_set - full_set, set())
|
||||
|
||||
def test_feature_macros(self):
|
||||
expected_patterns = [
|
||||
r"/\*\*\* System call availability \*\*\*/",
|
||||
r"#define HAVE_BPF_SYSCALL",
|
||||
r"/\*\*\* eBPF program types \*\*\*/",
|
||||
r"#define HAVE.*PROG_TYPE",
|
||||
r"/\*\*\* eBPF map types \*\*\*/",
|
||||
r"#define HAVE.*MAP_TYPE",
|
||||
r"/\*\*\* eBPF helper functions \*\*\*/",
|
||||
r"#define HAVE.*HELPER",
|
||||
r"/\*\*\* eBPF misc features \*\*\*/",
|
||||
]
|
||||
|
||||
res = bpftool(["feature", "probe", "macros"])
|
||||
for pattern in expected_patterns:
|
||||
self.assertRegex(res, pattern)
|
5
tools/testing/selftests/bpf/test_bpftool.sh
Executable file
5
tools/testing/selftests/bpf/test_bpftool.sh
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# Copyright (c) 2020 SUSE LLC.
|
||||
|
||||
python3 -m unittest -v test_bpftool.TestBpftool
|
159
tools/testing/selftests/bpf/test_current_pid_tgid_new_ns.c
Normal file
159
tools/testing/selftests/bpf/test_current_pid_tgid_new_ns.c
Normal file
@@ -0,0 +1,159 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2020 Carlos Neira cneirabustos@gmail.com */
|
||||
#define _GNU_SOURCE
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sched.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/mount.h>
|
||||
#include "test_progs.h"
|
||||
|
||||
#define CHECK_NEWNS(condition, tag, format...) ({ \
|
||||
int __ret = !!(condition); \
|
||||
if (__ret) { \
|
||||
printf("%s:FAIL:%s ", __func__, tag); \
|
||||
printf(format); \
|
||||
} else { \
|
||||
printf("%s:PASS:%s\n", __func__, tag); \
|
||||
} \
|
||||
__ret; \
|
||||
})
|
||||
|
||||
struct bss {
|
||||
__u64 dev;
|
||||
__u64 ino;
|
||||
__u64 pid_tgid;
|
||||
__u64 user_pid_tgid;
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
pid_t pid;
|
||||
int exit_code = 1;
|
||||
struct stat st;
|
||||
|
||||
printf("Testing bpf_get_ns_current_pid_tgid helper in new ns\n");
|
||||
|
||||
if (stat("/proc/self/ns/pid", &st)) {
|
||||
perror("stat failed on /proc/self/ns/pid ns\n");
|
||||
printf("%s:FAILED\n", argv[0]);
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
if (CHECK_NEWNS(unshare(CLONE_NEWPID | CLONE_NEWNS),
|
||||
"unshare CLONE_NEWPID | CLONE_NEWNS", "error errno=%d\n", errno))
|
||||
return exit_code;
|
||||
|
||||
pid = fork();
|
||||
if (pid == -1) {
|
||||
perror("Fork() failed\n");
|
||||
printf("%s:FAILED\n", argv[0]);
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
if (pid > 0) {
|
||||
int status;
|
||||
|
||||
usleep(5);
|
||||
waitpid(pid, &status, 0);
|
||||
return 0;
|
||||
} else {
|
||||
|
||||
pid = fork();
|
||||
if (pid == -1) {
|
||||
perror("Fork() failed\n");
|
||||
printf("%s:FAILED\n", argv[0]);
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
if (pid > 0) {
|
||||
int status;
|
||||
waitpid(pid, &status, 0);
|
||||
return 0;
|
||||
} else {
|
||||
if (CHECK_NEWNS(mount("none", "/proc", NULL, MS_PRIVATE|MS_REC, NULL),
|
||||
"Unmounting proc", "Cannot umount proc! errno=%d\n", errno))
|
||||
return exit_code;
|
||||
|
||||
if (CHECK_NEWNS(mount("proc", "/proc", "proc", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL),
|
||||
"Mounting proc", "Cannot mount proc! errno=%d\n", errno))
|
||||
return exit_code;
|
||||
|
||||
const char *probe_name = "raw_tracepoint/sys_enter";
|
||||
const char *file = "test_ns_current_pid_tgid.o";
|
||||
struct bpf_link *link = NULL;
|
||||
struct bpf_program *prog;
|
||||
struct bpf_map *bss_map;
|
||||
struct bpf_object *obj;
|
||||
int exit_code = 1;
|
||||
int err, key = 0;
|
||||
struct bss bss;
|
||||
struct stat st;
|
||||
__u64 id;
|
||||
|
||||
obj = bpf_object__open_file(file, NULL);
|
||||
if (CHECK_NEWNS(IS_ERR(obj), "obj_open", "err %ld\n", PTR_ERR(obj)))
|
||||
return exit_code;
|
||||
|
||||
err = bpf_object__load(obj);
|
||||
if (CHECK_NEWNS(err, "obj_load", "err %d errno %d\n", err, errno))
|
||||
goto cleanup;
|
||||
|
||||
bss_map = bpf_object__find_map_by_name(obj, "test_ns_.bss");
|
||||
if (CHECK_NEWNS(!bss_map, "find_bss_map", "failed\n"))
|
||||
goto cleanup;
|
||||
|
||||
prog = bpf_object__find_program_by_title(obj, probe_name);
|
||||
if (CHECK_NEWNS(!prog, "find_prog", "prog '%s' not found\n",
|
||||
probe_name))
|
||||
goto cleanup;
|
||||
|
||||
memset(&bss, 0, sizeof(bss));
|
||||
pid_t tid = syscall(SYS_gettid);
|
||||
pid_t pid = getpid();
|
||||
|
||||
id = (__u64) tid << 32 | pid;
|
||||
bss.user_pid_tgid = id;
|
||||
|
||||
if (CHECK_NEWNS(stat("/proc/self/ns/pid", &st),
|
||||
"stat new ns", "Failed to stat /proc/self/ns/pid errno=%d\n", errno))
|
||||
goto cleanup;
|
||||
|
||||
bss.dev = st.st_dev;
|
||||
bss.ino = st.st_ino;
|
||||
|
||||
err = bpf_map_update_elem(bpf_map__fd(bss_map), &key, &bss, 0);
|
||||
if (CHECK_NEWNS(err, "setting_bss", "failed to set bss : %d\n", err))
|
||||
goto cleanup;
|
||||
|
||||
link = bpf_program__attach_raw_tracepoint(prog, "sys_enter");
|
||||
if (CHECK_NEWNS(IS_ERR(link), "attach_raw_tp", "err %ld\n",
|
||||
PTR_ERR(link))) {
|
||||
link = NULL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* trigger some syscalls */
|
||||
usleep(1);
|
||||
|
||||
err = bpf_map_lookup_elem(bpf_map__fd(bss_map), &key, &bss);
|
||||
if (CHECK_NEWNS(err, "set_bss", "failed to get bss : %d\n", err))
|
||||
goto cleanup;
|
||||
|
||||
if (CHECK_NEWNS(id != bss.pid_tgid, "Compare user pid/tgid vs. bpf pid/tgid",
|
||||
"User pid/tgid %llu BPF pid/tgid %llu\n", id, bss.pid_tgid))
|
||||
goto cleanup;
|
||||
|
||||
exit_code = 0;
|
||||
printf("%s:PASS\n", argv[0]);
|
||||
cleanup:
|
||||
if (!link) {
|
||||
bpf_link__destroy(link);
|
||||
link = NULL;
|
||||
}
|
||||
bpf_object__close(obj);
|
||||
}
|
||||
}
|
||||
}
|
@@ -756,11 +756,7 @@ static void test_sockmap(unsigned int tasks, void *data)
|
||||
/* Test update without programs */
|
||||
for (i = 0; i < 6; i++) {
|
||||
err = bpf_map_update_elem(fd, &i, &sfd[i], BPF_ANY);
|
||||
if (i < 2 && !err) {
|
||||
printf("Allowed update sockmap '%i:%i' not in ESTABLISHED\n",
|
||||
i, sfd[i]);
|
||||
goto out_sockmap;
|
||||
} else if (i >= 2 && err) {
|
||||
if (err) {
|
||||
printf("Failed noprog update sockmap '%i:%i'\n",
|
||||
i, sfd[i]);
|
||||
goto out_sockmap;
|
||||
|
@@ -1,11 +1,16 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* Copyright (c) 2017 Facebook
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#include "test_progs.h"
|
||||
#include "cgroup_helpers.h"
|
||||
#include "bpf_rlimit.h"
|
||||
#include <argp.h>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <execinfo.h> /* backtrace */
|
||||
|
||||
/* defined in test_progs.h */
|
||||
struct test_env env = {};
|
||||
@@ -27,6 +32,20 @@ struct prog_test_def {
|
||||
int old_error_cnt;
|
||||
};
|
||||
|
||||
/* Override C runtime library's usleep() implementation to ensure nanosleep()
|
||||
* is always called. Usleep is frequently used in selftests as a way to
|
||||
* trigger kprobe and tracepoints.
|
||||
*/
|
||||
int usleep(useconds_t usec)
|
||||
{
|
||||
struct timespec ts = {
|
||||
.tv_sec = usec / 1000000,
|
||||
.tv_nsec = (usec % 1000000) * 1000,
|
||||
};
|
||||
|
||||
return syscall(__NR_nanosleep, &ts, NULL);
|
||||
}
|
||||
|
||||
static bool should_run(struct test_selector *sel, int num, const char *name)
|
||||
{
|
||||
int i;
|
||||
@@ -74,6 +93,34 @@ static void skip_account(void)
|
||||
}
|
||||
}
|
||||
|
||||
static void stdio_restore(void);
|
||||
|
||||
/* A bunch of tests set custom affinity per-thread and/or per-process. Reset
|
||||
* it after each test/sub-test.
|
||||
*/
|
||||
static void reset_affinity() {
|
||||
|
||||
cpu_set_t cpuset;
|
||||
int i, err;
|
||||
|
||||
CPU_ZERO(&cpuset);
|
||||
for (i = 0; i < env.nr_cpus; i++)
|
||||
CPU_SET(i, &cpuset);
|
||||
|
||||
err = sched_setaffinity(0, sizeof(cpuset), &cpuset);
|
||||
if (err < 0) {
|
||||
stdio_restore();
|
||||
fprintf(stderr, "Failed to reset process affinity: %d!\n", err);
|
||||
exit(-1);
|
||||
}
|
||||
err = pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset);
|
||||
if (err < 0) {
|
||||
stdio_restore();
|
||||
fprintf(stderr, "Failed to reset thread affinity: %d!\n", err);
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
void test__end_subtest()
|
||||
{
|
||||
struct prog_test_def *test = env.test;
|
||||
@@ -91,6 +138,8 @@ void test__end_subtest()
|
||||
test->test_num, test->subtest_num,
|
||||
test->subtest_name, sub_error_cnt ? "FAIL" : "OK");
|
||||
|
||||
reset_affinity();
|
||||
|
||||
free(test->subtest_name);
|
||||
test->subtest_name = NULL;
|
||||
}
|
||||
@@ -196,7 +245,7 @@ int bpf_find_map(const char *test, struct bpf_object *obj, const char *name)
|
||||
|
||||
map = bpf_object__find_map_by_name(obj, name);
|
||||
if (!map) {
|
||||
printf("%s:FAIL:map '%s' not found\n", test, name);
|
||||
fprintf(stdout, "%s:FAIL:map '%s' not found\n", test, name);
|
||||
test__fail();
|
||||
return -1;
|
||||
}
|
||||
@@ -367,7 +416,7 @@ static int libbpf_print_fn(enum libbpf_print_level level,
|
||||
{
|
||||
if (env.verbosity < VERBOSE_VERY && level == LIBBPF_DEBUG)
|
||||
return 0;
|
||||
vprintf(format, args);
|
||||
vfprintf(stdout, format, args);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -408,7 +457,7 @@ err:
|
||||
|
||||
int parse_num_list(const char *s, struct test_selector *sel)
|
||||
{
|
||||
int i, set_len = 0, num, start = 0, end = -1;
|
||||
int i, set_len = 0, new_len, num, start = 0, end = -1;
|
||||
bool *set = NULL, *tmp, parsing_end = false;
|
||||
char *next;
|
||||
|
||||
@@ -443,18 +492,19 @@ int parse_num_list(const char *s, struct test_selector *sel)
|
||||
return -EINVAL;
|
||||
|
||||
if (end + 1 > set_len) {
|
||||
set_len = end + 1;
|
||||
tmp = realloc(set, set_len);
|
||||
new_len = end + 1;
|
||||
tmp = realloc(set, new_len);
|
||||
if (!tmp) {
|
||||
free(set);
|
||||
return -ENOMEM;
|
||||
}
|
||||
for (i = set_len; i < start; i++)
|
||||
tmp[i] = false;
|
||||
set = tmp;
|
||||
set_len = new_len;
|
||||
}
|
||||
for (i = start; i <= end; i++) {
|
||||
for (i = start; i <= end; i++)
|
||||
set[i] = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!set)
|
||||
@@ -613,10 +663,27 @@ int cd_flavor_subdir(const char *exec_name)
|
||||
if (!flavor)
|
||||
return 0;
|
||||
flavor++;
|
||||
printf("Switching to flavor '%s' subdirectory...\n", flavor);
|
||||
fprintf(stdout, "Switching to flavor '%s' subdirectory...\n", flavor);
|
||||
return chdir(flavor);
|
||||
}
|
||||
|
||||
#define MAX_BACKTRACE_SZ 128
|
||||
void crash_handler(int signum)
|
||||
{
|
||||
void *bt[MAX_BACKTRACE_SZ];
|
||||
size_t sz;
|
||||
|
||||
sz = backtrace(bt, ARRAY_SIZE(bt));
|
||||
|
||||
if (env.test)
|
||||
dump_test_log(env.test, true);
|
||||
if (env.stdout)
|
||||
stdio_restore();
|
||||
|
||||
fprintf(stderr, "Caught signal #%d!\nStack trace:\n", signum);
|
||||
backtrace_symbols_fd(bt, sz, STDERR_FILENO);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
static const struct argp argp = {
|
||||
@@ -624,8 +691,14 @@ int main(int argc, char **argv)
|
||||
.parser = parse_arg,
|
||||
.doc = argp_program_doc,
|
||||
};
|
||||
struct sigaction sigact = {
|
||||
.sa_handler = crash_handler,
|
||||
.sa_flags = SA_RESETHAND,
|
||||
};
|
||||
int err, i;
|
||||
|
||||
sigaction(SIGSEGV, &sigact, NULL);
|
||||
|
||||
err = argp_parse(&argp, argc, argv, 0, NULL, &env);
|
||||
if (err)
|
||||
return err;
|
||||
@@ -639,6 +712,12 @@ int main(int argc, char **argv)
|
||||
srand(time(NULL));
|
||||
|
||||
env.jit_enabled = is_jit_enabled();
|
||||
env.nr_cpus = libbpf_num_possible_cpus();
|
||||
if (env.nr_cpus < 0) {
|
||||
fprintf(stderr, "Failed to get number of CPUs: %d!\n",
|
||||
env.nr_cpus);
|
||||
return -1;
|
||||
}
|
||||
|
||||
stdio_hijack();
|
||||
for (i = 0; i < prog_test_cnt; i++) {
|
||||
@@ -669,12 +748,13 @@ int main(int argc, char **argv)
|
||||
test->test_num, test->test_name,
|
||||
test->error_cnt ? "FAIL" : "OK");
|
||||
|
||||
reset_affinity();
|
||||
if (test->need_cgroup_cleanup)
|
||||
cleanup_cgroup_environment();
|
||||
}
|
||||
stdio_restore();
|
||||
printf("Summary: %d/%d PASSED, %d SKIPPED, %d FAILED\n",
|
||||
env.succ_cnt, env.sub_succ_cnt, env.skip_cnt, env.fail_cnt);
|
||||
fprintf(stdout, "Summary: %d/%d PASSED, %d SKIPPED, %d FAILED\n",
|
||||
env.succ_cnt, env.sub_succ_cnt, env.skip_cnt, env.fail_cnt);
|
||||
|
||||
free(env.test_selector.blacklist.strs);
|
||||
free(env.test_selector.whitelist.strs);
|
||||
|
@@ -71,6 +71,7 @@ struct test_env {
|
||||
FILE *stderr;
|
||||
char *log_buf;
|
||||
size_t log_cnt;
|
||||
int nr_cpus;
|
||||
|
||||
int succ_cnt; /* successful tests */
|
||||
int sub_succ_cnt; /* successful sub-tests */
|
||||
@@ -109,10 +110,10 @@ extern struct ipv6_packet pkt_v6;
|
||||
int __save_errno = errno; \
|
||||
if (__ret) { \
|
||||
test__fail(); \
|
||||
printf("%s:FAIL:%s ", __func__, tag); \
|
||||
printf(format); \
|
||||
fprintf(stdout, "%s:FAIL:%s ", __func__, tag); \
|
||||
fprintf(stdout, ##format); \
|
||||
} else { \
|
||||
printf("%s:PASS:%s %d nsec\n", \
|
||||
fprintf(stdout, "%s:PASS:%s %d nsec\n", \
|
||||
__func__, tag, duration); \
|
||||
} \
|
||||
errno = __save_errno; \
|
||||
@@ -124,7 +125,7 @@ extern struct ipv6_packet pkt_v6;
|
||||
int __save_errno = errno; \
|
||||
if (__ret) { \
|
||||
test__fail(); \
|
||||
printf("%s:FAIL:%d\n", __func__, __LINE__); \
|
||||
fprintf(stdout, "%s:FAIL:%d\n", __func__, __LINE__); \
|
||||
} \
|
||||
errno = __save_errno; \
|
||||
__ret; \
|
||||
|
@@ -4,12 +4,15 @@
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/perf_event.h>
|
||||
#include <sys/mman.h>
|
||||
#include "trace_helpers.h"
|
||||
|
||||
#define DEBUGFS "/sys/kernel/debug/tracing/"
|
||||
|
||||
#define MAX_SYMS 300000
|
||||
static struct ksym syms[MAX_SYMS];
|
||||
static int sym_cnt;
|
||||
@@ -86,3 +89,23 @@ long ksym_get_addr(const char *name)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void read_trace_pipe(void)
|
||||
{
|
||||
int trace_fd;
|
||||
|
||||
trace_fd = open(DEBUGFS "trace_pipe", O_RDONLY, 0);
|
||||
if (trace_fd < 0)
|
||||
return;
|
||||
|
||||
while (1) {
|
||||
static char buf[4096];
|
||||
ssize_t sz;
|
||||
|
||||
sz = read(trace_fd, buf, sizeof(buf) - 1);
|
||||
if (sz > 0) {
|
||||
buf[sz] = 0;
|
||||
puts(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -12,5 +12,6 @@ struct ksym {
|
||||
int load_kallsyms(void);
|
||||
struct ksym *ksym_search(long key);
|
||||
long ksym_get_addr(const char *name);
|
||||
void read_trace_pipe(void);
|
||||
|
||||
#endif
|
||||
|
@@ -257,17 +257,15 @@
|
||||
* [0x00ff'ffff'ff00'0000, 0x00ff'ffff'ffff'ffff]
|
||||
*/
|
||||
BPF_ALU64_IMM(BPF_RSH, BPF_REG_1, 8),
|
||||
/* no-op or OOB pointer computation */
|
||||
/* error on OOB pointer computation */
|
||||
BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1),
|
||||
/* potentially OOB access */
|
||||
BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_0, 0),
|
||||
/* exit */
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.fixup_map_hash_8b = { 3 },
|
||||
/* not actually fully unbounded, but the bound is very high */
|
||||
.errstr = "R0 unbounded memory access",
|
||||
.errstr = "value 72057594021150720 makes map_value pointer be out of bounds",
|
||||
.result = REJECT
|
||||
},
|
||||
{
|
||||
@@ -299,17 +297,15 @@
|
||||
* [0x00ff'ffff'ff00'0000, 0x00ff'ffff'ffff'ffff]
|
||||
*/
|
||||
BPF_ALU64_IMM(BPF_RSH, BPF_REG_1, 8),
|
||||
/* no-op or OOB pointer computation */
|
||||
/* error on OOB pointer computation */
|
||||
BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1),
|
||||
/* potentially OOB access */
|
||||
BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_0, 0),
|
||||
/* exit */
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.fixup_map_hash_8b = { 3 },
|
||||
/* not actually fully unbounded, but the bound is very high */
|
||||
.errstr = "R0 unbounded memory access",
|
||||
.errstr = "value 72057594021150720 makes map_value pointer be out of bounds",
|
||||
.result = REJECT
|
||||
},
|
||||
{
|
||||
@@ -411,16 +407,14 @@
|
||||
BPF_ALU32_IMM(BPF_RSH, BPF_REG_1, 31),
|
||||
/* r1 = 0xffff'fffe (NOT 0!) */
|
||||
BPF_ALU32_IMM(BPF_SUB, BPF_REG_1, 2),
|
||||
/* computes OOB pointer */
|
||||
/* error on computing OOB pointer */
|
||||
BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1),
|
||||
/* OOB access */
|
||||
BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_0, 0),
|
||||
/* exit */
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.fixup_map_hash_8b = { 3 },
|
||||
.errstr = "R0 invalid mem access",
|
||||
.errstr = "math between map_value pointer and 4294967294 is not allowed",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
@@ -506,3 +500,42 @@
|
||||
.errstr = "map_value pointer and 1000000000000",
|
||||
.result = REJECT
|
||||
},
|
||||
{
|
||||
"bounds check mixed 32bit and 64bit arithmatic. test1",
|
||||
.insns = {
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_MOV64_IMM(BPF_REG_1, -1),
|
||||
BPF_ALU64_IMM(BPF_LSH, BPF_REG_1, 32),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 1),
|
||||
/* r1 = 0xffffFFFF00000001 */
|
||||
BPF_JMP32_IMM(BPF_JGT, BPF_REG_1, 1, 3),
|
||||
/* check ALU64 op keeps 32bit bounds */
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 1),
|
||||
BPF_JMP32_IMM(BPF_JGT, BPF_REG_1, 2, 1),
|
||||
BPF_JMP_A(1),
|
||||
/* invalid ldx if bounds are lost above */
|
||||
BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, -1),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.result = ACCEPT
|
||||
},
|
||||
{
|
||||
"bounds check mixed 32bit and 64bit arithmatic. test2",
|
||||
.insns = {
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_MOV64_IMM(BPF_REG_1, -1),
|
||||
BPF_ALU64_IMM(BPF_LSH, BPF_REG_1, 32),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 1),
|
||||
/* r1 = 0xffffFFFF00000001 */
|
||||
BPF_MOV64_IMM(BPF_REG_2, 3),
|
||||
/* r1 = 0x2 */
|
||||
BPF_ALU32_IMM(BPF_ADD, BPF_REG_1, 1),
|
||||
/* check ALU32 op zero extends 64bit bounds */
|
||||
BPF_JMP_REG(BPF_JGT, BPF_REG_1, BPF_REG_2, 1),
|
||||
BPF_JMP_A(1),
|
||||
/* invalid ldx if bounds are lost above */
|
||||
BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, -1),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.result = ACCEPT
|
||||
},
|
||||
|
@@ -9,17 +9,17 @@
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 28),
|
||||
BPF_MOV64_REG(BPF_REG_7, BPF_REG_0),
|
||||
BPF_MOV64_IMM(BPF_REG_9, sizeof(struct test_val)),
|
||||
BPF_MOV64_IMM(BPF_REG_9, sizeof(struct test_val)/2),
|
||||
BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_7),
|
||||
BPF_MOV64_IMM(BPF_REG_3, sizeof(struct test_val)),
|
||||
BPF_MOV64_IMM(BPF_REG_3, sizeof(struct test_val)/2),
|
||||
BPF_MOV64_IMM(BPF_REG_4, 256),
|
||||
BPF_EMIT_CALL(BPF_FUNC_get_stack),
|
||||
BPF_MOV64_IMM(BPF_REG_1, 0),
|
||||
BPF_MOV64_REG(BPF_REG_8, BPF_REG_0),
|
||||
BPF_ALU64_IMM(BPF_LSH, BPF_REG_8, 32),
|
||||
BPF_ALU64_IMM(BPF_ARSH, BPF_REG_8, 32),
|
||||
BPF_JMP_REG(BPF_JSLT, BPF_REG_1, BPF_REG_8, 16),
|
||||
BPF_JMP_REG(BPF_JSGT, BPF_REG_1, BPF_REG_8, 16),
|
||||
BPF_ALU64_REG(BPF_SUB, BPF_REG_9, BPF_REG_8),
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_7),
|
||||
BPF_ALU64_REG(BPF_ADD, BPF_REG_2, BPF_REG_8),
|
||||
@@ -29,7 +29,7 @@
|
||||
BPF_MOV64_REG(BPF_REG_3, BPF_REG_2),
|
||||
BPF_ALU64_REG(BPF_ADD, BPF_REG_3, BPF_REG_1),
|
||||
BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),
|
||||
BPF_MOV64_IMM(BPF_REG_5, sizeof(struct test_val)),
|
||||
BPF_MOV64_IMM(BPF_REG_5, sizeof(struct test_val)/2),
|
||||
BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_5),
|
||||
BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_1, 4),
|
||||
BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
|
||||
|
@@ -91,3 +91,108 @@
|
||||
.result = REJECT,
|
||||
.errstr = "variable ctx access var_off=(0x0; 0x4)",
|
||||
},
|
||||
{
|
||||
"pass ctx or null check, 1: ctx",
|
||||
.insns = {
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
|
||||
BPF_FUNC_get_netns_cookie),
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.prog_type = BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
|
||||
.expected_attach_type = BPF_CGROUP_UDP6_SENDMSG,
|
||||
.result = ACCEPT,
|
||||
},
|
||||
{
|
||||
"pass ctx or null check, 2: null",
|
||||
.insns = {
|
||||
BPF_MOV64_IMM(BPF_REG_1, 0),
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
|
||||
BPF_FUNC_get_netns_cookie),
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.prog_type = BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
|
||||
.expected_attach_type = BPF_CGROUP_UDP6_SENDMSG,
|
||||
.result = ACCEPT,
|
||||
},
|
||||
{
|
||||
"pass ctx or null check, 3: 1",
|
||||
.insns = {
|
||||
BPF_MOV64_IMM(BPF_REG_1, 1),
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
|
||||
BPF_FUNC_get_netns_cookie),
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.prog_type = BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
|
||||
.expected_attach_type = BPF_CGROUP_UDP6_SENDMSG,
|
||||
.result = REJECT,
|
||||
.errstr = "R1 type=inv expected=ctx",
|
||||
},
|
||||
{
|
||||
"pass ctx or null check, 4: ctx - const",
|
||||
.insns = {
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -612),
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
|
||||
BPF_FUNC_get_netns_cookie),
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.prog_type = BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
|
||||
.expected_attach_type = BPF_CGROUP_UDP6_SENDMSG,
|
||||
.result = REJECT,
|
||||
.errstr = "dereference of modified ctx ptr",
|
||||
},
|
||||
{
|
||||
"pass ctx or null check, 5: null (connect)",
|
||||
.insns = {
|
||||
BPF_MOV64_IMM(BPF_REG_1, 0),
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
|
||||
BPF_FUNC_get_netns_cookie),
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.prog_type = BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
|
||||
.expected_attach_type = BPF_CGROUP_INET4_CONNECT,
|
||||
.result = ACCEPT,
|
||||
},
|
||||
{
|
||||
"pass ctx or null check, 6: null (bind)",
|
||||
.insns = {
|
||||
BPF_MOV64_IMM(BPF_REG_1, 0),
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
|
||||
BPF_FUNC_get_netns_cookie),
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.prog_type = BPF_PROG_TYPE_CGROUP_SOCK,
|
||||
.expected_attach_type = BPF_CGROUP_INET4_POST_BIND,
|
||||
.result = ACCEPT,
|
||||
},
|
||||
{
|
||||
"pass ctx or null check, 7: ctx (bind)",
|
||||
.insns = {
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
|
||||
BPF_FUNC_get_socket_cookie),
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.prog_type = BPF_PROG_TYPE_CGROUP_SOCK,
|
||||
.expected_attach_type = BPF_CGROUP_INET4_POST_BIND,
|
||||
.result = ACCEPT,
|
||||
},
|
||||
{
|
||||
"pass ctx or null check, 8: null (bind)",
|
||||
.insns = {
|
||||
BPF_MOV64_IMM(BPF_REG_1, 0),
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
|
||||
BPF_FUNC_get_socket_cookie),
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.prog_type = BPF_PROG_TYPE_CGROUP_SOCK,
|
||||
.expected_attach_type = BPF_CGROUP_INET4_POST_BIND,
|
||||
.result = REJECT,
|
||||
.errstr = "R1 type=inv expected=ctx",
|
||||
},
|
||||
|
@@ -1010,6 +1010,53 @@
|
||||
.result = ACCEPT,
|
||||
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
|
||||
},
|
||||
{
|
||||
"read gso_size from CGROUP_SKB",
|
||||
.insns = {
|
||||
BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1,
|
||||
offsetof(struct __sk_buff, gso_size)),
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.result = ACCEPT,
|
||||
.prog_type = BPF_PROG_TYPE_CGROUP_SKB,
|
||||
},
|
||||
{
|
||||
"read gso_size from CGROUP_SKB",
|
||||
.insns = {
|
||||
BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_1,
|
||||
offsetof(struct __sk_buff, gso_size)),
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.result = ACCEPT,
|
||||
.prog_type = BPF_PROG_TYPE_CGROUP_SKB,
|
||||
},
|
||||
{
|
||||
"write gso_size from CGROUP_SKB",
|
||||
.insns = {
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
|
||||
offsetof(struct __sk_buff, gso_size)),
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.result = REJECT,
|
||||
.result_unpriv = REJECT,
|
||||
.errstr = "invalid bpf_context access off=176 size=4",
|
||||
.prog_type = BPF_PROG_TYPE_CGROUP_SKB,
|
||||
},
|
||||
{
|
||||
"read gso_size from CLS",
|
||||
.insns = {
|
||||
BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1,
|
||||
offsetof(struct __sk_buff, gso_size)),
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.result = ACCEPT,
|
||||
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
|
||||
},
|
||||
{
|
||||
"check wire_len is not readable by sockets",
|
||||
.insns = {
|
||||
|
@@ -45,6 +45,7 @@ ALL_TESTS="
|
||||
blackhole_ipv6
|
||||
"
|
||||
NUM_NETIFS=4
|
||||
: ${TIMEOUT:=20000} # ms
|
||||
source $lib_dir/tc_common.sh
|
||||
source $lib_dir/lib.sh
|
||||
|
||||
@@ -123,7 +124,7 @@ blackhole_ipv4()
|
||||
skip_hw dst_ip 198.51.100.1 src_ip 192.0.2.1 ip_proto icmp \
|
||||
action pass
|
||||
|
||||
ip -4 route show 198.51.100.0/30 | grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload ip -4 route show 198.51.100.0/30
|
||||
check_err $? "route not marked as offloaded when should"
|
||||
|
||||
ping_do $h1 198.51.100.1
|
||||
@@ -147,7 +148,7 @@ blackhole_ipv6()
|
||||
skip_hw dst_ip 2001:db8:2::1 src_ip 2001:db8:1::1 \
|
||||
ip_proto icmpv6 action pass
|
||||
|
||||
ip -6 route show 2001:db8:2::/120 | grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload ip -6 route show 2001:db8:2::/120
|
||||
check_err $? "route not marked as offloaded when should"
|
||||
|
||||
ping6_do $h1 2001:db8:2::1
|
||||
|
151
tools/testing/selftests/drivers/net/mlxsw/devlink_trap_acl_drops.sh
Executable file
151
tools/testing/selftests/drivers/net/mlxsw/devlink_trap_acl_drops.sh
Executable file
@@ -0,0 +1,151 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Test devlink-trap ACL drops functionality over mlxsw.
|
||||
|
||||
lib_dir=$(dirname $0)/../../../net/forwarding
|
||||
|
||||
ALL_TESTS="
|
||||
ingress_flow_action_drop_test
|
||||
egress_flow_action_drop_test
|
||||
"
|
||||
NUM_NETIFS=4
|
||||
source $lib_dir/tc_common.sh
|
||||
source $lib_dir/lib.sh
|
||||
source $lib_dir/devlink_lib.sh
|
||||
|
||||
h1_create()
|
||||
{
|
||||
simple_if_init $h1
|
||||
}
|
||||
|
||||
h1_destroy()
|
||||
{
|
||||
simple_if_fini $h1
|
||||
}
|
||||
|
||||
h2_create()
|
||||
{
|
||||
simple_if_init $h2
|
||||
}
|
||||
|
||||
h2_destroy()
|
||||
{
|
||||
simple_if_fini $h2
|
||||
}
|
||||
|
||||
switch_create()
|
||||
{
|
||||
ip link add dev br0 type bridge vlan_filtering 1 mcast_snooping 0
|
||||
|
||||
ip link set dev $swp1 master br0
|
||||
ip link set dev $swp2 master br0
|
||||
|
||||
ip link set dev br0 up
|
||||
ip link set dev $swp1 up
|
||||
ip link set dev $swp2 up
|
||||
|
||||
tc qdisc add dev $swp1 clsact
|
||||
tc qdisc add dev $swp2 clsact
|
||||
}
|
||||
|
||||
switch_destroy()
|
||||
{
|
||||
tc qdisc del dev $swp2 clsact
|
||||
tc qdisc del dev $swp1 clsact
|
||||
|
||||
ip link set dev $swp2 down
|
||||
ip link set dev $swp1 down
|
||||
|
||||
ip link del dev br0
|
||||
}
|
||||
|
||||
setup_prepare()
|
||||
{
|
||||
h1=${NETIFS[p1]}
|
||||
swp1=${NETIFS[p2]}
|
||||
|
||||
swp2=${NETIFS[p3]}
|
||||
h2=${NETIFS[p4]}
|
||||
|
||||
h1mac=$(mac_get $h1)
|
||||
h2mac=$(mac_get $h2)
|
||||
|
||||
vrf_prepare
|
||||
|
||||
h1_create
|
||||
h2_create
|
||||
|
||||
switch_create
|
||||
}
|
||||
|
||||
cleanup()
|
||||
{
|
||||
pre_cleanup
|
||||
|
||||
switch_destroy
|
||||
|
||||
h2_destroy
|
||||
h1_destroy
|
||||
|
||||
vrf_cleanup
|
||||
}
|
||||
|
||||
ingress_flow_action_drop_test()
|
||||
{
|
||||
local mz_pid
|
||||
|
||||
tc filter add dev $swp2 egress protocol ip pref 1 handle 101 \
|
||||
flower src_mac $h1mac action pass
|
||||
|
||||
tc filter add dev $swp1 ingress protocol ip pref 1 handle 101 \
|
||||
flower dst_ip 192.0.2.2 action drop
|
||||
|
||||
$MZ $h1 -c 0 -p 100 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
|
||||
-t ip -d 1msec -q &
|
||||
mz_pid=$!
|
||||
|
||||
RET=0
|
||||
|
||||
devlink_trap_drop_test ingress_flow_action_drop acl_drops $swp2 101
|
||||
|
||||
log_test "ingress_flow_action_drop"
|
||||
|
||||
tc filter del dev $swp1 ingress protocol ip pref 1 handle 101 flower
|
||||
|
||||
devlink_trap_drop_cleanup $mz_pid $swp2 ip 1 101
|
||||
}
|
||||
|
||||
egress_flow_action_drop_test()
|
||||
{
|
||||
local mz_pid
|
||||
|
||||
tc filter add dev $swp2 egress protocol ip pref 2 handle 102 \
|
||||
flower src_mac $h1mac action pass
|
||||
|
||||
tc filter add dev $swp2 egress protocol ip pref 1 handle 101 \
|
||||
flower dst_ip 192.0.2.2 action drop
|
||||
|
||||
$MZ $h1 -c 0 -p 100 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
|
||||
-t ip -d 1msec -q &
|
||||
mz_pid=$!
|
||||
|
||||
RET=0
|
||||
|
||||
devlink_trap_drop_test egress_flow_action_drop acl_drops $swp2 102
|
||||
|
||||
log_test "egress_flow_action_drop"
|
||||
|
||||
tc filter del dev $swp2 egress protocol ip pref 1 handle 101 flower
|
||||
|
||||
devlink_trap_drop_cleanup $mz_pid $swp2 ip 2 102
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
setup_prepare
|
||||
setup_wait
|
||||
|
||||
tests_run
|
||||
|
||||
exit $EXIT_STATUS
|
@@ -107,11 +107,11 @@ source_mac_is_multicast_test()
|
||||
|
||||
RET=0
|
||||
|
||||
devlink_trap_drop_test $trap_name $group_name $swp2
|
||||
devlink_trap_drop_test $trap_name $group_name $swp2 101
|
||||
|
||||
log_test "Source MAC is multicast"
|
||||
|
||||
devlink_trap_drop_cleanup $mz_pid $swp2 ip
|
||||
devlink_trap_drop_cleanup $mz_pid $swp2 ip 1 101
|
||||
}
|
||||
|
||||
__vlan_tag_mismatch_test()
|
||||
@@ -132,7 +132,7 @@ __vlan_tag_mismatch_test()
|
||||
$MZ $h1 "$opt" -c 0 -p 100 -a own -b $dmac -t ip -d 1msec -q &
|
||||
mz_pid=$!
|
||||
|
||||
devlink_trap_drop_test $trap_name $group_name $swp2
|
||||
devlink_trap_drop_test $trap_name $group_name $swp2 101
|
||||
|
||||
# Add PVID and make sure packets are no longer dropped.
|
||||
bridge vlan add vid 1 dev $swp1 pvid untagged master
|
||||
@@ -148,7 +148,7 @@ __vlan_tag_mismatch_test()
|
||||
|
||||
devlink_trap_action_set $trap_name "drop"
|
||||
|
||||
devlink_trap_drop_cleanup $mz_pid $swp2 ip
|
||||
devlink_trap_drop_cleanup $mz_pid $swp2 ip 1 101
|
||||
}
|
||||
|
||||
vlan_tag_mismatch_untagged_test()
|
||||
@@ -193,7 +193,7 @@ ingress_vlan_filter_test()
|
||||
$MZ $h1 -Q $vid -c 0 -p 100 -a own -b $dmac -t ip -d 1msec -q &
|
||||
mz_pid=$!
|
||||
|
||||
devlink_trap_drop_test $trap_name $group_name $swp2
|
||||
devlink_trap_drop_test $trap_name $group_name $swp2 101
|
||||
|
||||
# Add the VLAN on the bridge port and make sure packets are no longer
|
||||
# dropped.
|
||||
@@ -212,7 +212,7 @@ ingress_vlan_filter_test()
|
||||
|
||||
log_test "Ingress VLAN filter"
|
||||
|
||||
devlink_trap_drop_cleanup $mz_pid $swp2 ip
|
||||
devlink_trap_drop_cleanup $mz_pid $swp2 ip 1 101
|
||||
|
||||
bridge vlan del vid $vid dev $swp1 master
|
||||
bridge vlan del vid $vid dev $swp2 master
|
||||
@@ -237,7 +237,7 @@ __ingress_stp_filter_test()
|
||||
$MZ $h1 -Q $vid -c 0 -p 100 -a own -b $dmac -t ip -d 1msec -q &
|
||||
mz_pid=$!
|
||||
|
||||
devlink_trap_drop_test $trap_name $group_name $swp2
|
||||
devlink_trap_drop_test $trap_name $group_name $swp2 101
|
||||
|
||||
# Change STP state to forwarding and make sure packets are no longer
|
||||
# dropped.
|
||||
@@ -254,7 +254,7 @@ __ingress_stp_filter_test()
|
||||
|
||||
devlink_trap_action_set $trap_name "drop"
|
||||
|
||||
devlink_trap_drop_cleanup $mz_pid $swp2 ip
|
||||
devlink_trap_drop_cleanup $mz_pid $swp2 ip 1 101
|
||||
|
||||
bridge vlan del vid $vid dev $swp1 master
|
||||
bridge vlan del vid $vid dev $swp2 master
|
||||
@@ -308,7 +308,7 @@ port_list_is_empty_uc_test()
|
||||
$MZ $h1 -c 0 -p 100 -a own -b $dmac -t ip -d 1msec -q &
|
||||
mz_pid=$!
|
||||
|
||||
devlink_trap_drop_test $trap_name $group_name $swp2
|
||||
devlink_trap_drop_test $trap_name $group_name $swp2 101
|
||||
|
||||
# Allow packets to be flooded to one port.
|
||||
ip link set dev $swp2 type bridge_slave flood on
|
||||
@@ -326,7 +326,7 @@ port_list_is_empty_uc_test()
|
||||
|
||||
log_test "Port list is empty - unicast"
|
||||
|
||||
devlink_trap_drop_cleanup $mz_pid $swp2 ip
|
||||
devlink_trap_drop_cleanup $mz_pid $swp2 ip 1 101
|
||||
|
||||
ip link set dev $swp1 type bridge_slave flood on
|
||||
}
|
||||
@@ -354,7 +354,7 @@ port_list_is_empty_mc_test()
|
||||
$MZ $h1 -c 0 -p 100 -a own -b $dmac -t ip -B $dip -d 1msec -q &
|
||||
mz_pid=$!
|
||||
|
||||
devlink_trap_drop_test $trap_name $group_name $swp2
|
||||
devlink_trap_drop_test $trap_name $group_name $swp2 101
|
||||
|
||||
# Allow packets to be flooded to one port.
|
||||
ip link set dev $swp2 type bridge_slave mcast_flood on
|
||||
@@ -372,7 +372,7 @@ port_list_is_empty_mc_test()
|
||||
|
||||
log_test "Port list is empty - multicast"
|
||||
|
||||
devlink_trap_drop_cleanup $mz_pid $swp2 ip
|
||||
devlink_trap_drop_cleanup $mz_pid $swp2 ip 1 101
|
||||
|
||||
ip link set dev $swp1 type bridge_slave mcast_flood on
|
||||
}
|
||||
@@ -401,7 +401,7 @@ port_loopback_filter_uc_test()
|
||||
$MZ $h1 -c 0 -p 100 -a own -b $dmac -t ip -d 1msec -q &
|
||||
mz_pid=$!
|
||||
|
||||
devlink_trap_drop_test $trap_name $group_name $swp2
|
||||
devlink_trap_drop_test $trap_name $group_name $swp2 101
|
||||
|
||||
# Allow packets to be flooded.
|
||||
ip link set dev $swp2 type bridge_slave flood on
|
||||
@@ -419,7 +419,7 @@ port_loopback_filter_uc_test()
|
||||
|
||||
log_test "Port loopback filter - unicast"
|
||||
|
||||
devlink_trap_drop_cleanup $mz_pid $swp2 ip
|
||||
devlink_trap_drop_cleanup $mz_pid $swp2 ip 1 101
|
||||
}
|
||||
|
||||
port_loopback_filter_test()
|
||||
|
@@ -176,11 +176,11 @@ non_ip_test()
|
||||
00:00 de:ad:be:ef" &
|
||||
mz_pid=$!
|
||||
|
||||
devlink_trap_drop_test $trap_name $group_name $rp2
|
||||
devlink_trap_drop_test $trap_name $group_name $rp2 101
|
||||
|
||||
log_test "Non IP"
|
||||
|
||||
devlink_trap_drop_cleanup $mz_pid $rp2 "ip"
|
||||
devlink_trap_drop_cleanup $mz_pid $rp2 "ip" 1 101
|
||||
}
|
||||
|
||||
__uc_dip_over_mc_dmac_test()
|
||||
@@ -206,11 +206,11 @@ __uc_dip_over_mc_dmac_test()
|
||||
-B $dip -d 1msec -q &
|
||||
mz_pid=$!
|
||||
|
||||
devlink_trap_drop_test $trap_name $group_name $rp2
|
||||
devlink_trap_drop_test $trap_name $group_name $rp2 101
|
||||
|
||||
log_test "Unicast destination IP over multicast destination MAC: $desc"
|
||||
|
||||
devlink_trap_drop_cleanup $mz_pid $rp2 $proto
|
||||
devlink_trap_drop_cleanup $mz_pid $rp2 $proto 1 101
|
||||
}
|
||||
|
||||
uc_dip_over_mc_dmac_test()
|
||||
@@ -242,11 +242,11 @@ __sip_is_loopback_test()
|
||||
-b $rp1mac -B $dip -d 1msec -q &
|
||||
mz_pid=$!
|
||||
|
||||
devlink_trap_drop_test $trap_name $group_name $rp2
|
||||
devlink_trap_drop_test $trap_name $group_name $rp2 101
|
||||
|
||||
log_test "Source IP is loopback address: $desc"
|
||||
|
||||
devlink_trap_drop_cleanup $mz_pid $rp2 $proto
|
||||
devlink_trap_drop_cleanup $mz_pid $rp2 $proto 1 101
|
||||
}
|
||||
|
||||
sip_is_loopback_test()
|
||||
@@ -277,11 +277,11 @@ __dip_is_loopback_test()
|
||||
-B $dip -d 1msec -q &
|
||||
mz_pid=$!
|
||||
|
||||
devlink_trap_drop_test $trap_name $group_name $rp2
|
||||
devlink_trap_drop_test $trap_name $group_name $rp2 101
|
||||
|
||||
log_test "Destination IP is loopback address: $desc"
|
||||
|
||||
devlink_trap_drop_cleanup $mz_pid $rp2 $proto
|
||||
devlink_trap_drop_cleanup $mz_pid $rp2 $proto 1 101
|
||||
}
|
||||
|
||||
dip_is_loopback_test()
|
||||
@@ -313,11 +313,11 @@ __sip_is_mc_test()
|
||||
-b $rp1mac -B $dip -d 1msec -q &
|
||||
mz_pid=$!
|
||||
|
||||
devlink_trap_drop_test $trap_name $group_name $rp2
|
||||
devlink_trap_drop_test $trap_name $group_name $rp2 101
|
||||
|
||||
log_test "Source IP is multicast: $desc"
|
||||
|
||||
devlink_trap_drop_cleanup $mz_pid $rp2 $proto
|
||||
devlink_trap_drop_cleanup $mz_pid $rp2 $proto 1 101
|
||||
}
|
||||
|
||||
sip_is_mc_test()
|
||||
@@ -345,11 +345,11 @@ ipv4_sip_is_limited_bc_test()
|
||||
-B $h2_ipv4 -d 1msec -q &
|
||||
mz_pid=$!
|
||||
|
||||
devlink_trap_drop_test $trap_name $group_name $rp2
|
||||
devlink_trap_drop_test $trap_name $group_name $rp2 101
|
||||
|
||||
log_test "IPv4 source IP is limited broadcast"
|
||||
|
||||
devlink_trap_drop_cleanup $mz_pid $rp2 "ip"
|
||||
devlink_trap_drop_cleanup $mz_pid $rp2 "ip" 1 101
|
||||
}
|
||||
|
||||
ipv4_payload_get()
|
||||
@@ -399,11 +399,11 @@ __ipv4_header_corrupted_test()
|
||||
$MZ $h1 -c 0 -d 1msec -a $h1mac -b $rp1mac -q p=$payload &
|
||||
mz_pid=$!
|
||||
|
||||
devlink_trap_drop_test $trap_name $group_name $rp2
|
||||
devlink_trap_drop_test $trap_name $group_name $rp2 101
|
||||
|
||||
log_test "IP header corrupted: $desc: IPv4"
|
||||
|
||||
devlink_trap_drop_cleanup $mz_pid $rp2 "ip"
|
||||
devlink_trap_drop_cleanup $mz_pid $rp2 "ip" 1 101
|
||||
}
|
||||
|
||||
ipv6_payload_get()
|
||||
@@ -446,11 +446,11 @@ __ipv6_header_corrupted_test()
|
||||
$MZ $h1 -c 0 -d 1msec -a $h1mac -b $rp1mac -q p=$payload &
|
||||
mz_pid=$!
|
||||
|
||||
devlink_trap_drop_test $trap_name $group_name $rp2
|
||||
devlink_trap_drop_test $trap_name $group_name $rp2 101
|
||||
|
||||
log_test "IP header corrupted: $desc: IPv6"
|
||||
|
||||
devlink_trap_drop_cleanup $mz_pid $rp2 "ip"
|
||||
devlink_trap_drop_cleanup $mz_pid $rp2 "ip" 1 101
|
||||
}
|
||||
|
||||
ip_header_corrupted_test()
|
||||
@@ -485,11 +485,11 @@ ipv6_mc_dip_reserved_scope_test()
|
||||
"33:33:00:00:00:00" -B $dip -d 1msec -q &
|
||||
mz_pid=$!
|
||||
|
||||
devlink_trap_drop_test $trap_name $group_name $rp2
|
||||
devlink_trap_drop_test $trap_name $group_name $rp2 101
|
||||
|
||||
log_test "IPv6 multicast destination IP reserved scope"
|
||||
|
||||
devlink_trap_drop_cleanup $mz_pid $rp2 "ipv6"
|
||||
devlink_trap_drop_cleanup $mz_pid $rp2 "ipv6" 1 101
|
||||
}
|
||||
|
||||
ipv6_mc_dip_interface_local_scope_test()
|
||||
@@ -511,11 +511,11 @@ ipv6_mc_dip_interface_local_scope_test()
|
||||
"33:33:00:00:00:00" -B $dip -d 1msec -q &
|
||||
mz_pid=$!
|
||||
|
||||
devlink_trap_drop_test $trap_name $group_name $rp2
|
||||
devlink_trap_drop_test $trap_name $group_name $rp2 101
|
||||
|
||||
log_test "IPv6 multicast destination IP interface-local scope"
|
||||
|
||||
devlink_trap_drop_cleanup $mz_pid $rp2 "ipv6"
|
||||
devlink_trap_drop_cleanup $mz_pid $rp2 "ipv6" 1 101
|
||||
}
|
||||
|
||||
__blackhole_route_test()
|
||||
@@ -542,10 +542,10 @@ __blackhole_route_test()
|
||||
-B $dip -d 1msec -q &
|
||||
mz_pid=$!
|
||||
|
||||
devlink_trap_drop_test $trap_name $group_name $rp2
|
||||
devlink_trap_drop_test $trap_name $group_name $rp2 101
|
||||
log_test "Blackhole route: IPv$flags"
|
||||
|
||||
devlink_trap_drop_cleanup $mz_pid $rp2 $proto
|
||||
devlink_trap_drop_cleanup $mz_pid $rp2 $proto 1 101
|
||||
ip -$flags route del blackhole $subnet
|
||||
}
|
||||
|
||||
@@ -641,13 +641,9 @@ erif_disabled_test()
|
||||
mz_pid=$!
|
||||
|
||||
sleep 5
|
||||
# In order to see this trap we need a route that points to disabled RIF.
|
||||
# When ipv6 address is flushed, there is a delay and the routes are
|
||||
# deleted before the RIF and we cannot get state that we have route
|
||||
# to disabled RIF.
|
||||
# Delete IPv6 address first and then check this trap with flushing IPv4.
|
||||
ip -6 add flush dev br0
|
||||
ip -4 add flush dev br0
|
||||
# Unlinking the port from the bridge will disable the RIF associated
|
||||
# with br0 as it is no longer an upper of any mlxsw port.
|
||||
ip link set dev $rp1 nomaster
|
||||
|
||||
t1_packets=$(devlink_trap_rx_packets_get $trap_name)
|
||||
t1_bytes=$(devlink_trap_rx_bytes_get $trap_name)
|
||||
@@ -659,7 +655,6 @@ erif_disabled_test()
|
||||
log_test "Egress RIF disabled"
|
||||
|
||||
kill $mz_pid && wait $mz_pid &> /dev/null
|
||||
ip link set dev $rp1 nomaster
|
||||
__addr_add_del $rp1 add 192.0.2.2/24 2001:db8:1::2/64
|
||||
ip link del dev br0 type bridge
|
||||
devlink_trap_action_set $trap_name "drop"
|
||||
|
384
tools/testing/selftests/drivers/net/mlxsw/devlink_trap_policer.sh
Executable file
384
tools/testing/selftests/drivers/net/mlxsw/devlink_trap_policer.sh
Executable file
@@ -0,0 +1,384 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Test devlink-trap policer functionality over mlxsw.
|
||||
|
||||
# +---------------------------------+
|
||||
# | H1 (vrf) |
|
||||
# | + $h1 |
|
||||
# | | 192.0.2.1/24 |
|
||||
# | | |
|
||||
# | | default via 192.0.2.2 |
|
||||
# +----|----------------------------+
|
||||
# |
|
||||
# +----|----------------------------------------------------------------------+
|
||||
# | SW | |
|
||||
# | + $rp1 |
|
||||
# | 192.0.2.2/24 |
|
||||
# | |
|
||||
# | 198.51.100.2/24 |
|
||||
# | + $rp2 |
|
||||
# | | |
|
||||
# +----|----------------------------------------------------------------------+
|
||||
# |
|
||||
# +----|----------------------------+
|
||||
# | | default via 198.51.100.2 |
|
||||
# | | |
|
||||
# | | 198.51.100.1/24 |
|
||||
# | + $h2 |
|
||||
# | H2 (vrf) |
|
||||
# +---------------------------------+
|
||||
|
||||
lib_dir=$(dirname $0)/../../../net/forwarding
|
||||
|
||||
ALL_TESTS="
|
||||
rate_limits_test
|
||||
burst_limits_test
|
||||
rate_test
|
||||
burst_test
|
||||
"
|
||||
NUM_NETIFS=4
|
||||
source $lib_dir/tc_common.sh
|
||||
source $lib_dir/lib.sh
|
||||
source $lib_dir/devlink_lib.sh
|
||||
|
||||
h1_create()
|
||||
{
|
||||
simple_if_init $h1 192.0.2.1/24
|
||||
mtu_set $h1 10000
|
||||
|
||||
ip -4 route add default vrf v$h1 nexthop via 192.0.2.2
|
||||
}
|
||||
|
||||
h1_destroy()
|
||||
{
|
||||
ip -4 route del default vrf v$h1 nexthop via 192.0.2.2
|
||||
|
||||
mtu_restore $h1
|
||||
simple_if_fini $h1 192.0.2.1/24
|
||||
}
|
||||
|
||||
h2_create()
|
||||
{
|
||||
simple_if_init $h2 198.51.100.1/24
|
||||
mtu_set $h2 10000
|
||||
|
||||
ip -4 route add default vrf v$h2 nexthop via 198.51.100.2
|
||||
}
|
||||
|
||||
h2_destroy()
|
||||
{
|
||||
ip -4 route del default vrf v$h2 nexthop via 198.51.100.2
|
||||
|
||||
mtu_restore $h2
|
||||
simple_if_fini $h2 198.51.100.1/24
|
||||
}
|
||||
|
||||
router_create()
|
||||
{
|
||||
ip link set dev $rp1 up
|
||||
ip link set dev $rp2 up
|
||||
|
||||
__addr_add_del $rp1 add 192.0.2.2/24
|
||||
__addr_add_del $rp2 add 198.51.100.2/24
|
||||
mtu_set $rp1 10000
|
||||
mtu_set $rp2 10000
|
||||
|
||||
ip -4 route add blackhole 198.51.100.100
|
||||
|
||||
devlink trap set $DEVLINK_DEV trap blackhole_route action trap
|
||||
}
|
||||
|
||||
router_destroy()
|
||||
{
|
||||
devlink trap set $DEVLINK_DEV trap blackhole_route action drop
|
||||
|
||||
ip -4 route del blackhole 198.51.100.100
|
||||
|
||||
mtu_restore $rp2
|
||||
mtu_restore $rp1
|
||||
__addr_add_del $rp2 del 198.51.100.2/24
|
||||
__addr_add_del $rp1 del 192.0.2.2/24
|
||||
|
||||
ip link set dev $rp2 down
|
||||
ip link set dev $rp1 down
|
||||
}
|
||||
|
||||
setup_prepare()
|
||||
{
|
||||
h1=${NETIFS[p1]}
|
||||
rp1=${NETIFS[p2]}
|
||||
|
||||
rp2=${NETIFS[p3]}
|
||||
h2=${NETIFS[p4]}
|
||||
|
||||
rp1_mac=$(mac_get $rp1)
|
||||
|
||||
vrf_prepare
|
||||
|
||||
h1_create
|
||||
h2_create
|
||||
|
||||
router_create
|
||||
}
|
||||
|
||||
cleanup()
|
||||
{
|
||||
pre_cleanup
|
||||
|
||||
router_destroy
|
||||
|
||||
h2_destroy
|
||||
h1_destroy
|
||||
|
||||
vrf_cleanup
|
||||
|
||||
# Reload to ensure devlink-trap settings are back to default.
|
||||
devlink_reload
|
||||
}
|
||||
|
||||
rate_limits_test()
|
||||
{
|
||||
RET=0
|
||||
|
||||
devlink trap policer set $DEVLINK_DEV policer 1 rate 0 &> /dev/null
|
||||
check_fail $? "Policer rate was changed to rate lower than limit"
|
||||
devlink trap policer set $DEVLINK_DEV policer 1 \
|
||||
rate 2000000001 &> /dev/null
|
||||
check_fail $? "Policer rate was changed to rate higher than limit"
|
||||
|
||||
devlink trap policer set $DEVLINK_DEV policer 1 rate 1
|
||||
check_err $? "Failed to set policer rate to minimum"
|
||||
devlink trap policer set $DEVLINK_DEV policer 1 rate 2000000000
|
||||
check_err $? "Failed to set policer rate to maximum"
|
||||
|
||||
log_test "Trap policer rate limits"
|
||||
}
|
||||
|
||||
burst_limits_test()
|
||||
{
|
||||
RET=0
|
||||
|
||||
devlink trap policer set $DEVLINK_DEV policer 1 burst 0 &> /dev/null
|
||||
check_fail $? "Policer burst size was changed to 0"
|
||||
devlink trap policer set $DEVLINK_DEV policer 1 burst 17 &> /dev/null
|
||||
check_fail $? "Policer burst size was changed to burst size that is not power of 2"
|
||||
devlink trap policer set $DEVLINK_DEV policer 1 burst 8 &> /dev/null
|
||||
check_fail $? "Policer burst size was changed to burst size lower than limit"
|
||||
devlink trap policer set $DEVLINK_DEV policer 1 \
|
||||
burst $((2**25)) &> /dev/null
|
||||
check_fail $? "Policer burst size was changed to burst size higher than limit"
|
||||
|
||||
devlink trap policer set $DEVLINK_DEV policer 1 burst 16
|
||||
check_err $? "Failed to set policer burst size to minimum"
|
||||
devlink trap policer set $DEVLINK_DEV policer 1 burst $((2**24))
|
||||
check_err $? "Failed to set policer burst size to maximum"
|
||||
|
||||
log_test "Trap policer burst size limits"
|
||||
}
|
||||
|
||||
trap_rate_get()
|
||||
{
|
||||
local t0 t1
|
||||
|
||||
t0=$(devlink_trap_rx_packets_get blackhole_route)
|
||||
sleep 10
|
||||
t1=$(devlink_trap_rx_packets_get blackhole_route)
|
||||
|
||||
echo $(((t1 - t0) / 10))
|
||||
}
|
||||
|
||||
policer_drop_rate_get()
|
||||
{
|
||||
local id=$1; shift
|
||||
local t0 t1
|
||||
|
||||
t0=$(devlink_trap_policer_rx_dropped_get $id)
|
||||
sleep 10
|
||||
t1=$(devlink_trap_policer_rx_dropped_get $id)
|
||||
|
||||
echo $(((t1 - t0) / 10))
|
||||
}
|
||||
|
||||
__rate_test()
|
||||
{
|
||||
local rate pct drop_rate
|
||||
local id=$1; shift
|
||||
|
||||
RET=0
|
||||
|
||||
devlink trap policer set $DEVLINK_DEV policer $id rate 1000 burst 16
|
||||
devlink trap group set $DEVLINK_DEV group l3_drops policer $id
|
||||
|
||||
# Send packets at highest possible rate and make sure they are dropped
|
||||
# by the policer. Make sure measured received rate is about 1000 pps
|
||||
log_info "=== Tx rate: Highest, Policer rate: 1000 pps ==="
|
||||
|
||||
start_traffic $h1 192.0.2.1 198.51.100.100 $rp1_mac
|
||||
|
||||
sleep 5 # Take measurements when rate is stable
|
||||
|
||||
rate=$(trap_rate_get)
|
||||
pct=$((100 * (rate - 1000) / 1000))
|
||||
((-5 <= pct && pct <= 5))
|
||||
check_err $? "Expected rate 1000 pps, got $rate pps, which is $pct% off. Required accuracy is +-5%"
|
||||
log_info "Expected rate 1000 pps, measured rate $rate pps"
|
||||
|
||||
drop_rate=$(policer_drop_rate_get $id)
|
||||
(( drop_rate > 0 ))
|
||||
check_err $? "Expected non-zero policer drop rate, got 0"
|
||||
log_info "Measured policer drop rate of $drop_rate pps"
|
||||
|
||||
stop_traffic
|
||||
|
||||
# Send packets at a rate of 1000 pps and make sure they are not dropped
|
||||
# by the policer
|
||||
log_info "=== Tx rate: 1000 pps, Policer rate: 1000 pps ==="
|
||||
|
||||
start_traffic $h1 192.0.2.1 198.51.100.100 $rp1_mac -d 1msec
|
||||
|
||||
sleep 5 # Take measurements when rate is stable
|
||||
|
||||
drop_rate=$(policer_drop_rate_get $id)
|
||||
(( drop_rate == 0 ))
|
||||
check_err $? "Expected zero policer drop rate, got a drop rate of $drop_rate pps"
|
||||
log_info "Measured policer drop rate of $drop_rate pps"
|
||||
|
||||
stop_traffic
|
||||
|
||||
# Unbind the policer and send packets at highest possible rate. Make
|
||||
# sure they are not dropped by the policer and that the measured
|
||||
# received rate is higher than 1000 pps
|
||||
log_info "=== Tx rate: Highest, Policer rate: No policer ==="
|
||||
|
||||
devlink trap group set $DEVLINK_DEV group l3_drops nopolicer
|
||||
|
||||
start_traffic $h1 192.0.2.1 198.51.100.100 $rp1_mac
|
||||
|
||||
rate=$(trap_rate_get)
|
||||
(( rate > 1000 ))
|
||||
check_err $? "Expected rate higher than 1000 pps, got $rate pps"
|
||||
log_info "Measured rate $rate pps"
|
||||
|
||||
drop_rate=$(policer_drop_rate_get $id)
|
||||
(( drop_rate == 0 ))
|
||||
check_err $? "Expected zero policer drop rate, got a drop rate of $drop_rate pps"
|
||||
log_info "Measured policer drop rate of $drop_rate pps"
|
||||
|
||||
stop_traffic
|
||||
|
||||
log_test "Trap policer rate"
|
||||
}
|
||||
|
||||
rate_test()
|
||||
{
|
||||
local id
|
||||
|
||||
for id in $(devlink_trap_policer_ids_get); do
|
||||
echo
|
||||
log_info "Running rate test for policer $id"
|
||||
__rate_test $id
|
||||
done
|
||||
}
|
||||
|
||||
__burst_test()
|
||||
{
|
||||
local t0_rx t0_drop t1_rx t1_drop rx drop
|
||||
local id=$1; shift
|
||||
|
||||
RET=0
|
||||
|
||||
devlink trap policer set $DEVLINK_DEV policer $id rate 1000 burst 32
|
||||
devlink trap group set $DEVLINK_DEV group l3_drops policer $id
|
||||
|
||||
# Send a burst of 64 packets and make sure that about 32 are received
|
||||
# and the rest are dropped by the policer
|
||||
log_info "=== Tx burst size: 64, Policer burst size: 32 pps ==="
|
||||
|
||||
t0_rx=$(devlink_trap_rx_packets_get blackhole_route)
|
||||
t0_drop=$(devlink_trap_policer_rx_dropped_get $id)
|
||||
|
||||
start_traffic $h1 192.0.2.1 198.51.100.100 $rp1_mac -c 64
|
||||
|
||||
t1_rx=$(devlink_trap_rx_packets_get blackhole_route)
|
||||
t1_drop=$(devlink_trap_policer_rx_dropped_get $id)
|
||||
|
||||
rx=$((t1_rx - t0_rx))
|
||||
pct=$((100 * (rx - 32) / 32))
|
||||
((-20 <= pct && pct <= 20))
|
||||
check_err $? "Expected burst size of 32 packets, got $rx packets, which is $pct% off. Required accuracy is +-20%"
|
||||
log_info "Expected burst size of 32 packets, measured burst size of $rx packets"
|
||||
|
||||
drop=$((t1_drop - t0_drop))
|
||||
(( drop > 0 ))
|
||||
check_err $? "Expected non-zero policer drops, got 0"
|
||||
log_info "Measured policer drops of $drop packets"
|
||||
|
||||
# Send a burst of 16 packets and make sure that 16 are received
|
||||
# and that none are dropped by the policer
|
||||
log_info "=== Tx burst size: 16, Policer burst size: 32 pps ==="
|
||||
|
||||
t0_rx=$(devlink_trap_rx_packets_get blackhole_route)
|
||||
t0_drop=$(devlink_trap_policer_rx_dropped_get $id)
|
||||
|
||||
start_traffic $h1 192.0.2.1 198.51.100.100 $rp1_mac -c 16
|
||||
|
||||
t1_rx=$(devlink_trap_rx_packets_get blackhole_route)
|
||||
t1_drop=$(devlink_trap_policer_rx_dropped_get $id)
|
||||
|
||||
rx=$((t1_rx - t0_rx))
|
||||
(( rx == 16 ))
|
||||
check_err $? "Expected burst size of 16 packets, got $rx packets"
|
||||
log_info "Expected burst size of 16 packets, measured burst size of $rx packets"
|
||||
|
||||
drop=$((t1_drop - t0_drop))
|
||||
(( drop == 0 ))
|
||||
check_err $? "Expected zero policer drops, got $drop"
|
||||
log_info "Measured policer drops of $drop packets"
|
||||
|
||||
# Unbind the policer and send a burst of 64 packets. Make sure that
|
||||
# 64 packets are received and that none are dropped by the policer
|
||||
log_info "=== Tx burst size: 64, Policer burst size: No policer ==="
|
||||
|
||||
devlink trap group set $DEVLINK_DEV group l3_drops nopolicer
|
||||
|
||||
t0_rx=$(devlink_trap_rx_packets_get blackhole_route)
|
||||
t0_drop=$(devlink_trap_policer_rx_dropped_get $id)
|
||||
|
||||
start_traffic $h1 192.0.2.1 198.51.100.100 $rp1_mac -c 64
|
||||
|
||||
t1_rx=$(devlink_trap_rx_packets_get blackhole_route)
|
||||
t1_drop=$(devlink_trap_policer_rx_dropped_get $id)
|
||||
|
||||
rx=$((t1_rx - t0_rx))
|
||||
(( rx == 64 ))
|
||||
check_err $? "Expected burst size of 64 packets, got $rx packets"
|
||||
log_info "Expected burst size of 64 packets, measured burst size of $rx packets"
|
||||
|
||||
drop=$((t1_drop - t0_drop))
|
||||
(( drop == 0 ))
|
||||
check_err $? "Expected zero policer drops, got $drop"
|
||||
log_info "Measured policer drops of $drop packets"
|
||||
|
||||
log_test "Trap policer burst size"
|
||||
}
|
||||
|
||||
burst_test()
|
||||
{
|
||||
local id
|
||||
|
||||
for id in $(devlink_trap_policer_ids_get); do
|
||||
echo
|
||||
log_info "Running burst size test for policer $id"
|
||||
__burst_test $id
|
||||
done
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
setup_prepare
|
||||
setup_wait
|
||||
|
||||
tests_run
|
||||
|
||||
exit $EXIT_STATUS
|
@@ -314,11 +314,11 @@ overlay_smac_is_mc_test()
|
||||
-B 192.0.2.17 -t udp sp=12345,dp=$VXPORT,p=$payload -q &
|
||||
mz_pid=$!
|
||||
|
||||
devlink_trap_drop_test $trap_name $group_name $swp1
|
||||
devlink_trap_drop_test $trap_name $group_name $swp1 101
|
||||
|
||||
log_test "Overlay source MAC is multicast"
|
||||
|
||||
devlink_trap_drop_cleanup $mz_pid $swp1 "ip"
|
||||
devlink_trap_drop_cleanup $mz_pid $swp1 "ip" 1 101
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
|
@@ -8,7 +8,8 @@ lib_dir=$(dirname $0)/../../../net/forwarding
|
||||
ALL_TESTS="
|
||||
netdev_pre_up_test
|
||||
vxlan_vlan_add_test
|
||||
port_vlan_add_test
|
||||
vxlan_bridge_create_test
|
||||
bridge_create_test
|
||||
"
|
||||
NUM_NETIFS=2
|
||||
source $lib_dir/lib.sh
|
||||
@@ -106,32 +107,56 @@ vxlan_vlan_add_test()
|
||||
ip link del dev br1
|
||||
}
|
||||
|
||||
port_vlan_add_test()
|
||||
vxlan_bridge_create_test()
|
||||
{
|
||||
RET=0
|
||||
|
||||
ip link add name br1 up type bridge vlan_filtering 1 mcast_snooping 0
|
||||
|
||||
# Unsupported configuration: mlxsw demands VXLAN with "noudpcsum".
|
||||
ip link add name vx1 up type vxlan id 1000 \
|
||||
local 192.0.2.17 remote 192.0.2.18 \
|
||||
dstport 4789 tos inherit ttl 100
|
||||
|
||||
ip link set dev $swp1 master br1
|
||||
check_err $?
|
||||
|
||||
bridge vlan del dev $swp1 vid 1
|
||||
# Test with VLAN-aware bridge.
|
||||
ip link add name br1 up type bridge vlan_filtering 1 mcast_snooping 0
|
||||
|
||||
ip link set dev vx1 master br1
|
||||
check_err $?
|
||||
|
||||
bridge vlan add dev $swp1 vid 1 pvid untagged 2>&1 >/dev/null \
|
||||
ip link set dev $swp1 master br1 2>&1 > /dev/null \
|
||||
| grep -q mlxsw_spectrum
|
||||
check_err $?
|
||||
|
||||
log_test "extack - map VLAN at port"
|
||||
# Test with VLAN-unaware bridge.
|
||||
ip link set dev br1 type bridge vlan_filtering 0
|
||||
|
||||
ip link set dev $swp1 master br1 2>&1 > /dev/null \
|
||||
| grep -q mlxsw_spectrum
|
||||
check_err $?
|
||||
|
||||
log_test "extack - bridge creation with VXLAN"
|
||||
|
||||
ip link del dev br1
|
||||
ip link del dev vx1
|
||||
}
|
||||
|
||||
bridge_create_test()
|
||||
{
|
||||
RET=0
|
||||
|
||||
ip link add name br1 up type bridge vlan_filtering 1
|
||||
ip link add name br2 up type bridge vlan_filtering 1
|
||||
|
||||
ip link set dev $swp1 master br1
|
||||
check_err $?
|
||||
|
||||
# Only one VLAN-aware bridge is supported, so this should fail with
|
||||
# an extack.
|
||||
ip link set dev $swp2 master br2 2>&1 > /dev/null \
|
||||
| grep -q mlxsw_spectrum
|
||||
check_err $?
|
||||
|
||||
log_test "extack - multiple VLAN-aware bridges creation"
|
||||
|
||||
ip link del dev br2
|
||||
ip link del dev br1
|
||||
}
|
||||
|
||||
|
13
tools/testing/selftests/drivers/net/mlxsw/mlxsw_lib.sh
Normal file
13
tools/testing/selftests/drivers/net/mlxsw/mlxsw_lib.sh
Normal file
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
##############################################################################
|
||||
# Defines
|
||||
|
||||
if [[ ! -v MLXSW_CHIP ]]; then
|
||||
MLXSW_CHIP=$(devlink -j dev info $DEVLINK_DEV | jq -r '.[][]["driver"]')
|
||||
if [ -z "$MLXSW_CHIP" ]; then
|
||||
echo "SKIP: Device $DEVLINK_DEV doesn't support devlink info command"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
@@ -114,23 +114,12 @@ ping_ipv4()
|
||||
ping_test $h1 192.0.2.2
|
||||
}
|
||||
|
||||
wait_for_packets()
|
||||
{
|
||||
local t0=$1; shift
|
||||
local prio_observe=$1; shift
|
||||
|
||||
local t1=$(ethtool_stats_get $swp1 rx_frames_prio_$prio_observe)
|
||||
local delta=$((t1 - t0))
|
||||
echo $delta
|
||||
((delta >= 10))
|
||||
}
|
||||
|
||||
__test_defprio()
|
||||
{
|
||||
local prio_install=$1; shift
|
||||
local prio_observe=$1; shift
|
||||
local delta
|
||||
local key
|
||||
local t1
|
||||
local i
|
||||
|
||||
RET=0
|
||||
@@ -139,9 +128,10 @@ __test_defprio()
|
||||
|
||||
local t0=$(ethtool_stats_get $swp1 rx_frames_prio_$prio_observe)
|
||||
mausezahn -q $h1 -d 100m -c 10 -t arp reply
|
||||
delta=$(busywait "$HIT_TIMEOUT" wait_for_packets $t0 $prio_observe)
|
||||
t1=$(busywait "$HIT_TIMEOUT" until_counter_is ">= $((t0 + 10))" \
|
||||
ethtool_stats_get $swp1 rx_frames_prio_$prio_observe)
|
||||
|
||||
check_err $? "Default priority $prio_install/$prio_observe: Expected to capture 10 packets, got $delta."
|
||||
check_err $? "Default priority $prio_install/$prio_observe: Expected to capture 10 packets, got $((t1 - t0))."
|
||||
log_test "Default priority $prio_install/$prio_observe"
|
||||
|
||||
defprio_uninstall $swp1 $prio_install
|
||||
|
@@ -31,6 +31,7 @@ ALL_TESTS="
|
||||
ping_ipv4
|
||||
test_update
|
||||
test_no_update
|
||||
test_pedit_norewrite
|
||||
test_dscp_leftover
|
||||
"
|
||||
|
||||
@@ -56,6 +57,11 @@ zero()
|
||||
echo 0
|
||||
}
|
||||
|
||||
three()
|
||||
{
|
||||
echo 3
|
||||
}
|
||||
|
||||
h1_create()
|
||||
{
|
||||
simple_if_init $h1 192.0.2.1/28
|
||||
@@ -103,6 +109,9 @@ switch_create()
|
||||
simple_if_init $swp1 192.0.2.2/28
|
||||
__simple_if_init $swp2 v$swp1 192.0.2.17/28
|
||||
|
||||
tc qdisc add dev $swp1 clsact
|
||||
tc qdisc add dev $swp2 clsact
|
||||
|
||||
lldptool -T -i $swp1 -V APP $(dscp_map 0) >/dev/null
|
||||
lldptool -T -i $swp2 -V APP $(dscp_map 0) >/dev/null
|
||||
lldpad_app_wait_set $swp1
|
||||
@@ -115,6 +124,9 @@ switch_destroy()
|
||||
lldptool -T -i $swp1 -V APP -d $(dscp_map 0) >/dev/null
|
||||
lldpad_app_wait_del
|
||||
|
||||
tc qdisc del dev $swp2 clsact
|
||||
tc qdisc del dev $swp1 clsact
|
||||
|
||||
__simple_if_fini $swp2 192.0.2.17/28
|
||||
simple_if_fini $swp1 192.0.2.2/28
|
||||
}
|
||||
@@ -223,18 +235,36 @@ __test_update()
|
||||
|
||||
test_update()
|
||||
{
|
||||
echo "Test net.ipv4.ip_forward_update_priority=1"
|
||||
__test_update 1 reprioritize
|
||||
}
|
||||
|
||||
test_no_update()
|
||||
{
|
||||
echo "Test net.ipv4.ip_forward_update_priority=0"
|
||||
__test_update 0 echo
|
||||
}
|
||||
|
||||
# Test that when DSCP is updated in pedit, the DSCP rewrite is turned off.
|
||||
test_pedit_norewrite()
|
||||
{
|
||||
echo "Test no DSCP rewrite after DSCP is updated by pedit"
|
||||
|
||||
tc filter add dev $swp1 ingress handle 101 pref 1 prot ip flower \
|
||||
action pedit ex munge ip dsfield set $((3 << 2)) retain 0xfc \
|
||||
action skbedit priority 3
|
||||
|
||||
__test_update 0 three
|
||||
|
||||
tc filter del dev $swp1 ingress pref 1
|
||||
}
|
||||
|
||||
# Test that when the last APP rule is removed, the prio->DSCP map is properly
|
||||
# set to zeroes, and that the last APP rule does not stay active in the ASIC.
|
||||
test_dscp_leftover()
|
||||
{
|
||||
echo "Test that last removed DSCP rule is deconfigured correctly"
|
||||
|
||||
lldptool -T -i $swp2 -V APP -d $(dscp_map 0) >/dev/null
|
||||
lldpad_app_wait_del
|
||||
|
||||
|
@@ -2,16 +2,15 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
ROUTER_NUM_NETIFS=4
|
||||
: ${TIMEOUT:=20000} # ms
|
||||
|
||||
router_h1_create()
|
||||
{
|
||||
simple_if_init $h1 192.0.1.1/24
|
||||
ip route add 193.0.0.0/8 via 192.0.1.2 dev $h1
|
||||
}
|
||||
|
||||
router_h1_destroy()
|
||||
{
|
||||
ip route del 193.0.0.0/8 via 192.0.1.2 dev $h1
|
||||
simple_if_fini $h1 192.0.1.1/24
|
||||
}
|
||||
|
||||
@@ -64,13 +63,15 @@ router_setup_prepare()
|
||||
router_create
|
||||
}
|
||||
|
||||
router_offload_validate()
|
||||
wait_for_routes()
|
||||
{
|
||||
local route_count=$1
|
||||
local offloaded_count
|
||||
local t0=$1; shift
|
||||
local route_count=$1; shift
|
||||
|
||||
offloaded_count=$(ip route | grep -o 'offload' | wc -l)
|
||||
[[ $offloaded_count -ge $route_count ]]
|
||||
local t1=$(ip route | grep -o 'offload' | wc -l)
|
||||
local delta=$((t1 - t0))
|
||||
echo $delta
|
||||
[[ $delta -ge $route_count ]]
|
||||
}
|
||||
|
||||
router_routes_create()
|
||||
@@ -90,8 +91,8 @@ router_routes_create()
|
||||
break 3
|
||||
fi
|
||||
|
||||
echo route add 193.${i}.${j}.${k}/32 via \
|
||||
192.0.2.1 dev $rp2 >> $ROUTE_FILE
|
||||
echo route add 193.${i}.${j}.${k}/32 dev $rp2 \
|
||||
>> $ROUTE_FILE
|
||||
((count++))
|
||||
done
|
||||
done
|
||||
@@ -111,45 +112,19 @@ router_test()
|
||||
{
|
||||
local route_count=$1
|
||||
local should_fail=$2
|
||||
local count=0
|
||||
local delta
|
||||
|
||||
RET=0
|
||||
|
||||
local t0=$(ip route | grep -o 'offload' | wc -l)
|
||||
router_routes_create $route_count
|
||||
delta=$(busywait "$TIMEOUT" wait_for_routes $t0 $route_count)
|
||||
|
||||
router_offload_validate $route_count
|
||||
check_err_fail $should_fail $? "Offload of $route_count routes"
|
||||
check_err_fail $should_fail $? "Offload routes: Expected $route_count, got $delta."
|
||||
if [[ $RET -ne 0 ]] || [[ $should_fail -eq 1 ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
tc filter add dev $h2 ingress protocol ip pref 1 flower \
|
||||
skip_sw dst_ip 193.0.0.0/8 action drop
|
||||
|
||||
for i in {0..255}
|
||||
do
|
||||
for j in {0..255}
|
||||
do
|
||||
for k in {0..255}
|
||||
do
|
||||
if [[ $count -eq $route_count ]]; then
|
||||
break 3
|
||||
fi
|
||||
|
||||
$MZ $h1 -c 1 -p 64 -a $h1mac -b $rp1mac \
|
||||
-A 192.0.1.1 -B 193.${i}.${j}.${k} \
|
||||
-t ip -q
|
||||
((count++))
|
||||
done
|
||||
done
|
||||
done
|
||||
|
||||
tc_check_packets "dev $h2 ingress" 1 $route_count
|
||||
check_err $? "Offload mismatch"
|
||||
|
||||
tc filter del dev $h2 ingress protocol ip pref 1 flower \
|
||||
skip_sw dst_ip 193.0.0.0/8 action drop
|
||||
|
||||
router_routes_destroy
|
||||
}
|
||||
|
||||
|
@@ -32,6 +32,7 @@ ALL_TESTS="
|
||||
devlink_reload_test
|
||||
"
|
||||
NUM_NETIFS=2
|
||||
: ${TIMEOUT:=20000} # ms
|
||||
source $lib_dir/lib.sh
|
||||
source $lib_dir/devlink_lib.sh
|
||||
|
||||
@@ -360,20 +361,24 @@ vlan_rif_refcount_test()
|
||||
ip link add link br0 name br0.10 up type vlan id 10
|
||||
ip -6 address add 2001:db8:1::1/64 dev br0.10
|
||||
|
||||
ip -6 route get fibmatch 2001:db8:1::2 dev br0.10 | grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload \
|
||||
ip -6 route get fibmatch 2001:db8:1::2 dev br0.10
|
||||
check_err $? "vlan rif was not created before adding port to vlan"
|
||||
|
||||
bridge vlan add vid 10 dev $swp1
|
||||
ip -6 route get fibmatch 2001:db8:1::2 dev br0.10 | grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload \
|
||||
ip -6 route get fibmatch 2001:db8:1::2 dev br0.10
|
||||
check_err $? "vlan rif was destroyed after adding port to vlan"
|
||||
|
||||
bridge vlan del vid 10 dev $swp1
|
||||
ip -6 route get fibmatch 2001:db8:1::2 dev br0.10 | grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload \
|
||||
ip -6 route get fibmatch 2001:db8:1::2 dev br0.10
|
||||
check_err $? "vlan rif was destroyed after removing port from vlan"
|
||||
|
||||
ip link set dev $swp1 nomaster
|
||||
ip -6 route get fibmatch 2001:db8:1::2 dev br0.10 | grep -q offload
|
||||
check_fail $? "vlan rif was not destroyed after unlinking port from bridge"
|
||||
busywait "$TIMEOUT" not wait_for_offload \
|
||||
ip -6 route get fibmatch 2001:db8:1::2 dev br0.10
|
||||
check_err $? "vlan rif was not destroyed after unlinking port from bridge"
|
||||
|
||||
log_test "vlan rif refcount"
|
||||
|
||||
@@ -401,22 +406,28 @@ subport_rif_refcount_test()
|
||||
ip -6 address add 2001:db8:1::1/64 dev bond1
|
||||
ip -6 address add 2001:db8:2::1/64 dev bond1.10
|
||||
|
||||
ip -6 route get fibmatch 2001:db8:1::2 dev bond1 | grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload \
|
||||
ip -6 route get fibmatch 2001:db8:1::2 dev bond1
|
||||
check_err $? "subport rif was not created on lag device"
|
||||
ip -6 route get fibmatch 2001:db8:2::2 dev bond1.10 | grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload \
|
||||
ip -6 route get fibmatch 2001:db8:2::2 dev bond1.10
|
||||
check_err $? "subport rif was not created on vlan device"
|
||||
|
||||
ip link set dev $swp1 nomaster
|
||||
ip -6 route get fibmatch 2001:db8:1::2 dev bond1 | grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload \
|
||||
ip -6 route get fibmatch 2001:db8:1::2 dev bond1
|
||||
check_err $? "subport rif of lag device was destroyed when should not"
|
||||
ip -6 route get fibmatch 2001:db8:2::2 dev bond1.10 | grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload \
|
||||
ip -6 route get fibmatch 2001:db8:2::2 dev bond1.10
|
||||
check_err $? "subport rif of vlan device was destroyed when should not"
|
||||
|
||||
ip link set dev $swp2 nomaster
|
||||
ip -6 route get fibmatch 2001:db8:1::2 dev bond1 | grep -q offload
|
||||
check_fail $? "subport rif of lag device was not destroyed when should"
|
||||
ip -6 route get fibmatch 2001:db8:2::2 dev bond1.10 | grep -q offload
|
||||
check_fail $? "subport rif of vlan device was not destroyed when should"
|
||||
busywait "$TIMEOUT" not wait_for_offload \
|
||||
ip -6 route get fibmatch 2001:db8:1::2 dev bond1
|
||||
check_err $? "subport rif of lag device was not destroyed when should"
|
||||
busywait "$TIMEOUT" not wait_for_offload \
|
||||
ip -6 route get fibmatch 2001:db8:2::2 dev bond1.10
|
||||
check_err $? "subport rif of vlan device was not destroyed when should"
|
||||
|
||||
log_test "subport rif refcount"
|
||||
|
||||
@@ -575,7 +586,8 @@ bridge_extern_learn_test()
|
||||
|
||||
bridge fdb add de:ad:be:ef:13:37 dev $swp1 master extern_learn
|
||||
|
||||
bridge fdb show brport $swp1 | grep de:ad:be:ef:13:37 | grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload \
|
||||
bridge fdb show brport $swp1 de:ad:be:ef:13:37
|
||||
check_err $? "fdb entry not marked as offloaded when should"
|
||||
|
||||
log_test "externally learned fdb entry"
|
||||
@@ -595,9 +607,11 @@ neigh_offload_test()
|
||||
ip -6 neigh add 2001:db8:1::2 lladdr de:ad:be:ef:13:37 nud perm \
|
||||
dev $swp1
|
||||
|
||||
ip -4 neigh show dev $swp1 | grep 192.0.2.2 | grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload \
|
||||
ip -4 neigh show dev $swp1 192.0.2.2
|
||||
check_err $? "ipv4 neigh entry not marked as offloaded when should"
|
||||
ip -6 neigh show dev $swp1 | grep 2001:db8:1::2 | grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload \
|
||||
ip -6 neigh show dev $swp1 2001:db8:1::2
|
||||
check_err $? "ipv6 neigh entry not marked as offloaded when should"
|
||||
|
||||
log_test "neighbour offload indication"
|
||||
@@ -623,25 +637,31 @@ nexthop_offload_test()
|
||||
ip -6 route add 2001:db8:2::/64 vrf v$swp1 \
|
||||
nexthop via 2001:db8:1::2 dev $swp1
|
||||
|
||||
ip -4 route show 198.51.100.0/24 vrf v$swp1 | grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload \
|
||||
ip -4 route show 198.51.100.0/24 vrf v$swp1
|
||||
check_err $? "ipv4 nexthop not marked as offloaded when should"
|
||||
ip -6 route show 2001:db8:2::/64 vrf v$swp1 | grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload \
|
||||
ip -6 route show 2001:db8:2::/64 vrf v$swp1
|
||||
check_err $? "ipv6 nexthop not marked as offloaded when should"
|
||||
|
||||
ip link set dev $swp2 down
|
||||
sleep 1
|
||||
|
||||
ip -4 route show 198.51.100.0/24 vrf v$swp1 | grep -q offload
|
||||
check_fail $? "ipv4 nexthop marked as offloaded when should not"
|
||||
ip -6 route show 2001:db8:2::/64 vrf v$swp1 | grep -q offload
|
||||
check_fail $? "ipv6 nexthop marked as offloaded when should not"
|
||||
busywait "$TIMEOUT" not wait_for_offload \
|
||||
ip -4 route show 198.51.100.0/24 vrf v$swp1
|
||||
check_err $? "ipv4 nexthop marked as offloaded when should not"
|
||||
busywait "$TIMEOUT" not wait_for_offload \
|
||||
ip -6 route show 2001:db8:2::/64 vrf v$swp1
|
||||
check_err $? "ipv6 nexthop marked as offloaded when should not"
|
||||
|
||||
ip link set dev $swp2 up
|
||||
setup_wait
|
||||
|
||||
ip -4 route show 198.51.100.0/24 vrf v$swp1 | grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload \
|
||||
ip -4 route show 198.51.100.0/24 vrf v$swp1
|
||||
check_err $? "ipv4 nexthop not marked as offloaded after neigh add"
|
||||
ip -6 route show 2001:db8:2::/64 vrf v$swp1 | grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload \
|
||||
ip -6 route show 2001:db8:2::/64 vrf v$swp1
|
||||
check_err $? "ipv6 nexthop not marked as offloaded after neigh add"
|
||||
|
||||
log_test "nexthop offload indication"
|
||||
|
@@ -56,11 +56,19 @@ switch_destroy()
|
||||
}
|
||||
|
||||
# Callback from sch_ets_tests.sh
|
||||
get_stats()
|
||||
collect_stats()
|
||||
{
|
||||
local band=$1; shift
|
||||
local -a streams=("$@")
|
||||
local stream
|
||||
|
||||
ethtool_stats_get "$h2" rx_octets_prio_$band
|
||||
# Wait for qdisc counter update so that we don't get it mid-way through.
|
||||
busywait_for_counter 1000 +1 \
|
||||
qdisc_parent_stats_get $swp2 10:$((${streams[0]} + 1)) .bytes \
|
||||
> /dev/null
|
||||
|
||||
for stream in ${streams[@]}; do
|
||||
qdisc_parent_stats_get $swp2 10:$((stream + 1)) .bytes
|
||||
done
|
||||
}
|
||||
|
||||
bail_on_lldpad
|
||||
|
533
tools/testing/selftests/drivers/net/mlxsw/sch_red_core.sh
Normal file
533
tools/testing/selftests/drivers/net/mlxsw/sch_red_core.sh
Normal file
@@ -0,0 +1,533 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
# This test sends a >1Gbps stream of traffic from H1, to the switch, which
|
||||
# forwards it to a 1Gbps port. This 1Gbps stream is then looped back to the
|
||||
# switch and forwarded to the port under test $swp3, which is also 1Gbps.
|
||||
#
|
||||
# This way, $swp3 should be 100% filled with traffic without any of it spilling
|
||||
# to the backlog. Any extra packets sent should almost 1:1 go to backlog. That
|
||||
# is what H2 is used for--it sends the extra traffic to create backlog.
|
||||
#
|
||||
# A RED Qdisc is installed on $swp3. The configuration is such that the minimum
|
||||
# and maximum size are 1 byte apart, so there is a very clear border under which
|
||||
# no marking or dropping takes place, and above which everything is marked or
|
||||
# dropped.
|
||||
#
|
||||
# The test uses the buffer build-up behavior to test the installed RED.
|
||||
#
|
||||
# In order to test WRED, $swp3 actually contains RED under PRIO, with two
|
||||
# different configurations. Traffic is prioritized using 802.1p and relies on
|
||||
# the implicit mlxsw configuration, where packet priority is taken 1:1 from the
|
||||
# 802.1p marking.
|
||||
#
|
||||
# +--------------------------+ +--------------------------+
|
||||
# | H1 | | H2 |
|
||||
# | + $h1.10 | | + $h2.10 |
|
||||
# | | 192.0.2.1/28 | | | 192.0.2.2/28 |
|
||||
# | | | | | |
|
||||
# | | $h1.11 + | | | $h2.11 + |
|
||||
# | | 192.0.2.17/28 | | | | 192.0.2.18/28 | |
|
||||
# | | | | | | | |
|
||||
# | \______ ______/ | | \______ ______/ |
|
||||
# | \ / | | \ / |
|
||||
# | + $h1 | | + $h2 |
|
||||
# +-------------|------------+ +-------------|------------+
|
||||
# | >1Gbps |
|
||||
# +-------------|------------------------------------------------|------------+
|
||||
# | SW + $swp1 + $swp2 |
|
||||
# | _______/ \___________ ___________/ \_______ |
|
||||
# | / \ / \ |
|
||||
# | +-|-----------------+ | +-|-----------------+ | |
|
||||
# | | + $swp1.10 | | | + $swp2.10 | | |
|
||||
# | | | | .-------------+ $swp5.10 | | |
|
||||
# | | BR1_10 | | | | | | |
|
||||
# | | | | | | BR2_10 | | |
|
||||
# | | + $swp2.10 | | | | | | |
|
||||
# | +-|-----------------+ | | | + $swp3.10 | | |
|
||||
# | | | | +-|-----------------+ | |
|
||||
# | | +-----------------|-+ | | +-----------------|-+ |
|
||||
# | | | $swp1.11 + | | | | $swp2.11 + | |
|
||||
# | | | | | .-----------------+ $swp5.11 | |
|
||||
# | | | BR1_11 | | | | | | |
|
||||
# | | | | | | | | BR2_11 | |
|
||||
# | | | $swp2.11 + | | | | | | |
|
||||
# | | +-----------------|-+ | | | | $swp3.11 + | |
|
||||
# | | | | | | +-----------------|-+ |
|
||||
# | \_______ ___________/ | | \___________ _______/ |
|
||||
# | \ / \ / \ / |
|
||||
# | + $swp4 + $swp5 + $swp3 |
|
||||
# +-------------|----------------------|-------------------------|------------+
|
||||
# | | | 1Gbps
|
||||
# \________1Gbps_________/ |
|
||||
# +----------------------------|------------+
|
||||
# | H3 + $h3 |
|
||||
# | _____________________/ \_______ |
|
||||
# | / \ |
|
||||
# | | | |
|
||||
# | + $h3.10 $h3.11 + |
|
||||
# | 192.0.2.3/28 192.0.2.19/28 |
|
||||
# +-----------------------------------------+
|
||||
|
||||
NUM_NETIFS=8
|
||||
CHECK_TC="yes"
|
||||
lib_dir=$(dirname $0)/../../../net/forwarding
|
||||
source $lib_dir/lib.sh
|
||||
source $lib_dir/devlink_lib.sh
|
||||
source qos_lib.sh
|
||||
|
||||
ipaddr()
|
||||
{
|
||||
local host=$1; shift
|
||||
local vlan=$1; shift
|
||||
|
||||
echo 192.0.2.$((16 * (vlan - 10) + host))
|
||||
}
|
||||
|
||||
host_create()
|
||||
{
|
||||
local dev=$1; shift
|
||||
local host=$1; shift
|
||||
|
||||
simple_if_init $dev
|
||||
mtu_set $dev 10000
|
||||
|
||||
vlan_create $dev 10 v$dev $(ipaddr $host 10)/28
|
||||
ip link set dev $dev.10 type vlan egress 0:0
|
||||
|
||||
vlan_create $dev 11 v$dev $(ipaddr $host 11)/28
|
||||
ip link set dev $dev.11 type vlan egress 0:1
|
||||
}
|
||||
|
||||
host_destroy()
|
||||
{
|
||||
local dev=$1; shift
|
||||
|
||||
vlan_destroy $dev 11
|
||||
vlan_destroy $dev 10
|
||||
mtu_restore $dev
|
||||
simple_if_fini $dev
|
||||
}
|
||||
|
||||
h1_create()
|
||||
{
|
||||
host_create $h1 1
|
||||
}
|
||||
|
||||
h1_destroy()
|
||||
{
|
||||
host_destroy $h1
|
||||
}
|
||||
|
||||
h2_create()
|
||||
{
|
||||
host_create $h2 2
|
||||
|
||||
# Some of the tests in this suite use multicast traffic. As this traffic
|
||||
# enters BR2_10 resp. BR2_11, it is flooded to all other ports. Thus
|
||||
# e.g. traffic ingressing through $swp2 is flooded to $swp3 (the
|
||||
# intended destination) and $swp5 (which is intended as ingress for
|
||||
# another stream of traffic).
|
||||
#
|
||||
# This is generally not a problem, but if the $swp5 throughput is lower
|
||||
# than $swp2 throughput, there will be a build-up at $swp5. That may
|
||||
# cause packets to fail to queue up at $swp3 due to shared buffer
|
||||
# quotas, and the test to spuriously fail.
|
||||
#
|
||||
# Prevent this by setting the speed of $h2 to 1Gbps.
|
||||
|
||||
ethtool -s $h2 speed 1000 autoneg off
|
||||
}
|
||||
|
||||
h2_destroy()
|
||||
{
|
||||
ethtool -s $h2 autoneg on
|
||||
host_destroy $h2
|
||||
}
|
||||
|
||||
h3_create()
|
||||
{
|
||||
host_create $h3 3
|
||||
ethtool -s $h3 speed 1000 autoneg off
|
||||
}
|
||||
|
||||
h3_destroy()
|
||||
{
|
||||
ethtool -s $h3 autoneg on
|
||||
host_destroy $h3
|
||||
}
|
||||
|
||||
switch_create()
|
||||
{
|
||||
local intf
|
||||
local vlan
|
||||
|
||||
ip link add dev br1_10 type bridge
|
||||
ip link add dev br1_11 type bridge
|
||||
|
||||
ip link add dev br2_10 type bridge
|
||||
ip link add dev br2_11 type bridge
|
||||
|
||||
for intf in $swp1 $swp2 $swp3 $swp4 $swp5; do
|
||||
ip link set dev $intf up
|
||||
mtu_set $intf 10000
|
||||
done
|
||||
|
||||
for intf in $swp1 $swp4; do
|
||||
for vlan in 10 11; do
|
||||
vlan_create $intf $vlan
|
||||
ip link set dev $intf.$vlan master br1_$vlan
|
||||
ip link set dev $intf.$vlan up
|
||||
done
|
||||
done
|
||||
|
||||
for intf in $swp2 $swp3 $swp5; do
|
||||
for vlan in 10 11; do
|
||||
vlan_create $intf $vlan
|
||||
ip link set dev $intf.$vlan master br2_$vlan
|
||||
ip link set dev $intf.$vlan up
|
||||
done
|
||||
done
|
||||
|
||||
ip link set dev $swp4.10 type vlan egress 0:0
|
||||
ip link set dev $swp4.11 type vlan egress 0:1
|
||||
for intf in $swp1 $swp2 $swp5; do
|
||||
for vlan in 10 11; do
|
||||
ip link set dev $intf.$vlan type vlan ingress 0:0 1:1
|
||||
done
|
||||
done
|
||||
|
||||
for intf in $swp2 $swp3 $swp4 $swp5; do
|
||||
ethtool -s $intf speed 1000 autoneg off
|
||||
done
|
||||
|
||||
ip link set dev br1_10 up
|
||||
ip link set dev br1_11 up
|
||||
ip link set dev br2_10 up
|
||||
ip link set dev br2_11 up
|
||||
|
||||
local size=$(devlink_pool_size_thtype 0 | cut -d' ' -f 1)
|
||||
devlink_port_pool_th_set $swp3 8 $size
|
||||
}
|
||||
|
||||
switch_destroy()
|
||||
{
|
||||
local intf
|
||||
local vlan
|
||||
|
||||
devlink_port_pool_th_restore $swp3 8
|
||||
|
||||
tc qdisc del dev $swp3 root 2>/dev/null
|
||||
|
||||
ip link set dev br2_11 down
|
||||
ip link set dev br2_10 down
|
||||
ip link set dev br1_11 down
|
||||
ip link set dev br1_10 down
|
||||
|
||||
for intf in $swp5 $swp4 $swp3 $swp2; do
|
||||
ethtool -s $intf autoneg on
|
||||
done
|
||||
|
||||
for intf in $swp5 $swp3 $swp2 $swp4 $swp1; do
|
||||
for vlan in 11 10; do
|
||||
ip link set dev $intf.$vlan down
|
||||
ip link set dev $intf.$vlan nomaster
|
||||
vlan_destroy $intf $vlan
|
||||
done
|
||||
|
||||
mtu_restore $intf
|
||||
ip link set dev $intf down
|
||||
done
|
||||
|
||||
ip link del dev br2_11
|
||||
ip link del dev br2_10
|
||||
ip link del dev br1_11
|
||||
ip link del dev br1_10
|
||||
}
|
||||
|
||||
setup_prepare()
|
||||
{
|
||||
h1=${NETIFS[p1]}
|
||||
swp1=${NETIFS[p2]}
|
||||
|
||||
swp2=${NETIFS[p3]}
|
||||
h2=${NETIFS[p4]}
|
||||
|
||||
swp3=${NETIFS[p5]}
|
||||
h3=${NETIFS[p6]}
|
||||
|
||||
swp4=${NETIFS[p7]}
|
||||
swp5=${NETIFS[p8]}
|
||||
|
||||
h3_mac=$(mac_get $h3)
|
||||
|
||||
vrf_prepare
|
||||
|
||||
h1_create
|
||||
h2_create
|
||||
h3_create
|
||||
switch_create
|
||||
}
|
||||
|
||||
cleanup()
|
||||
{
|
||||
pre_cleanup
|
||||
|
||||
switch_destroy
|
||||
h3_destroy
|
||||
h2_destroy
|
||||
h1_destroy
|
||||
|
||||
vrf_cleanup
|
||||
}
|
||||
|
||||
ping_ipv4()
|
||||
{
|
||||
ping_test $h1.10 $(ipaddr 3 10) " from host 1, vlan 10"
|
||||
ping_test $h1.11 $(ipaddr 3 11) " from host 1, vlan 11"
|
||||
ping_test $h2.10 $(ipaddr 3 10) " from host 2, vlan 10"
|
||||
ping_test $h2.11 $(ipaddr 3 11) " from host 2, vlan 11"
|
||||
}
|
||||
|
||||
get_tc()
|
||||
{
|
||||
local vlan=$1; shift
|
||||
|
||||
echo $((vlan - 10))
|
||||
}
|
||||
|
||||
get_qdisc_handle()
|
||||
{
|
||||
local vlan=$1; shift
|
||||
|
||||
local tc=$(get_tc $vlan)
|
||||
local band=$((8 - tc))
|
||||
|
||||
# Handle is 107: for TC1, 108: for TC0.
|
||||
echo "10$band:"
|
||||
}
|
||||
|
||||
get_qdisc_backlog()
|
||||
{
|
||||
local vlan=$1; shift
|
||||
|
||||
qdisc_stats_get $swp3 $(get_qdisc_handle $vlan) .backlog
|
||||
}
|
||||
|
||||
get_mc_transmit_queue()
|
||||
{
|
||||
local vlan=$1; shift
|
||||
|
||||
local tc=$(($(get_tc $vlan) + 8))
|
||||
ethtool_stats_get $swp3 tc_transmit_queue_tc_$tc
|
||||
}
|
||||
|
||||
get_nmarked()
|
||||
{
|
||||
local vlan=$1; shift
|
||||
|
||||
ethtool_stats_get $swp3 ecn_marked
|
||||
}
|
||||
|
||||
get_qdisc_npackets()
|
||||
{
|
||||
local vlan=$1; shift
|
||||
|
||||
busywait_for_counter 1100 +1 \
|
||||
qdisc_stats_get $swp3 $(get_qdisc_handle $vlan) .packets
|
||||
}
|
||||
|
||||
# This sends traffic in an attempt to build a backlog of $size. Returns 0 on
|
||||
# success. After 10 failed attempts it bails out and returns 1. It dumps the
|
||||
# backlog size to stdout.
|
||||
build_backlog()
|
||||
{
|
||||
local vlan=$1; shift
|
||||
local size=$1; shift
|
||||
local proto=$1; shift
|
||||
|
||||
local tc=$((vlan - 10))
|
||||
local band=$((8 - tc))
|
||||
local cur=-1
|
||||
local i=0
|
||||
|
||||
while :; do
|
||||
local cur=$(busywait 1100 until_counter_is "> $cur" \
|
||||
get_qdisc_backlog $vlan)
|
||||
local diff=$((size - cur))
|
||||
local pkts=$(((diff + 7999) / 8000))
|
||||
|
||||
if ((cur >= size)); then
|
||||
echo $cur
|
||||
return 0
|
||||
elif ((i++ > 10)); then
|
||||
echo $cur
|
||||
return 1
|
||||
fi
|
||||
|
||||
$MZ $h2.$vlan -p 8000 -a own -b $h3_mac \
|
||||
-A $(ipaddr 2 $vlan) -B $(ipaddr 3 $vlan) \
|
||||
-t $proto -q -c $pkts "$@"
|
||||
done
|
||||
}
|
||||
|
||||
check_marking()
|
||||
{
|
||||
local vlan=$1; shift
|
||||
local cond=$1; shift
|
||||
|
||||
local npackets_0=$(get_qdisc_npackets $vlan)
|
||||
local nmarked_0=$(get_nmarked $vlan)
|
||||
sleep 5
|
||||
local npackets_1=$(get_qdisc_npackets $vlan)
|
||||
local nmarked_1=$(get_nmarked $vlan)
|
||||
|
||||
local nmarked_d=$((nmarked_1 - nmarked_0))
|
||||
local npackets_d=$((npackets_1 - npackets_0))
|
||||
local pct=$((100 * nmarked_d / npackets_d))
|
||||
|
||||
echo $pct
|
||||
((pct $cond))
|
||||
}
|
||||
|
||||
ecn_test_common()
|
||||
{
|
||||
local name=$1; shift
|
||||
local vlan=$1; shift
|
||||
local limit=$1; shift
|
||||
local backlog
|
||||
local pct
|
||||
|
||||
# Build the below-the-limit backlog using UDP. We could use TCP just
|
||||
# fine, but this way we get a proof that UDP is accepted when queue
|
||||
# length is below the limit. The main stream is using TCP, and if the
|
||||
# limit is misconfigured, we would see this traffic being ECN marked.
|
||||
RET=0
|
||||
backlog=$(build_backlog $vlan $((2 * limit / 3)) udp)
|
||||
check_err $? "Could not build the requested backlog"
|
||||
pct=$(check_marking $vlan "== 0")
|
||||
check_err $? "backlog $backlog / $limit Got $pct% marked packets, expected == 0."
|
||||
log_test "TC $((vlan - 10)): $name backlog < limit"
|
||||
|
||||
# Now push TCP, because non-TCP traffic would be early-dropped after the
|
||||
# backlog crosses the limit, and we want to make sure that the backlog
|
||||
# is above the limit.
|
||||
RET=0
|
||||
backlog=$(build_backlog $vlan $((3 * limit / 2)) tcp tos=0x01)
|
||||
check_err $? "Could not build the requested backlog"
|
||||
pct=$(check_marking $vlan ">= 95")
|
||||
check_err $? "backlog $backlog / $limit Got $pct% marked packets, expected >= 95."
|
||||
log_test "TC $((vlan - 10)): $name backlog > limit"
|
||||
}
|
||||
|
||||
do_ecn_test()
|
||||
{
|
||||
local vlan=$1; shift
|
||||
local limit=$1; shift
|
||||
local name=ECN
|
||||
|
||||
start_tcp_traffic $h1.$vlan $(ipaddr 1 $vlan) $(ipaddr 3 $vlan) \
|
||||
$h3_mac tos=0x01
|
||||
sleep 1
|
||||
|
||||
ecn_test_common "$name" $vlan $limit
|
||||
|
||||
# Up there we saw that UDP gets accepted when backlog is below the
|
||||
# limit. Now that it is above, it should all get dropped, and backlog
|
||||
# building should fail.
|
||||
RET=0
|
||||
build_backlog $vlan $((2 * limit)) udp >/dev/null
|
||||
check_fail $? "UDP traffic went into backlog instead of being early-dropped"
|
||||
log_test "TC $((vlan - 10)): $name backlog > limit: UDP early-dropped"
|
||||
|
||||
stop_traffic
|
||||
sleep 1
|
||||
}
|
||||
|
||||
do_ecn_nodrop_test()
|
||||
{
|
||||
local vlan=$1; shift
|
||||
local limit=$1; shift
|
||||
local name="ECN nodrop"
|
||||
|
||||
start_tcp_traffic $h1.$vlan $(ipaddr 1 $vlan) $(ipaddr 3 $vlan) \
|
||||
$h3_mac tos=0x01
|
||||
sleep 1
|
||||
|
||||
ecn_test_common "$name" $vlan $limit
|
||||
|
||||
# Up there we saw that UDP gets accepted when backlog is below the
|
||||
# limit. Now that it is above, in nodrop mode, make sure it goes to
|
||||
# backlog as well.
|
||||
RET=0
|
||||
build_backlog $vlan $((2 * limit)) udp >/dev/null
|
||||
check_err $? "UDP traffic was early-dropped instead of getting into backlog"
|
||||
log_test "TC $((vlan - 10)): $name backlog > limit: UDP not dropped"
|
||||
|
||||
stop_traffic
|
||||
sleep 1
|
||||
}
|
||||
|
||||
do_red_test()
|
||||
{
|
||||
local vlan=$1; shift
|
||||
local limit=$1; shift
|
||||
local backlog
|
||||
local pct
|
||||
|
||||
# Use ECN-capable TCP to verify there's no marking even though the queue
|
||||
# is above limit.
|
||||
start_tcp_traffic $h1.$vlan $(ipaddr 1 $vlan) $(ipaddr 3 $vlan) \
|
||||
$h3_mac tos=0x01
|
||||
|
||||
# Pushing below the queue limit should work.
|
||||
RET=0
|
||||
backlog=$(build_backlog $vlan $((2 * limit / 3)) tcp tos=0x01)
|
||||
check_err $? "Could not build the requested backlog"
|
||||
pct=$(check_marking $vlan "== 0")
|
||||
check_err $? "backlog $backlog / $limit Got $pct% marked packets, expected == 0."
|
||||
log_test "TC $((vlan - 10)): RED backlog < limit"
|
||||
|
||||
# Pushing above should not.
|
||||
RET=0
|
||||
backlog=$(build_backlog $vlan $((3 * limit / 2)) tcp tos=0x01)
|
||||
check_fail $? "Traffic went into backlog instead of being early-dropped"
|
||||
pct=$(check_marking $vlan "== 0")
|
||||
check_err $? "backlog $backlog / $limit Got $pct% marked packets, expected == 0."
|
||||
local diff=$((limit - backlog))
|
||||
pct=$((100 * diff / limit))
|
||||
((0 <= pct && pct <= 5))
|
||||
check_err $? "backlog $backlog / $limit expected <= 5% distance"
|
||||
log_test "TC $((vlan - 10)): RED backlog > limit"
|
||||
|
||||
stop_traffic
|
||||
sleep 1
|
||||
}
|
||||
|
||||
do_mc_backlog_test()
|
||||
{
|
||||
local vlan=$1; shift
|
||||
local limit=$1; shift
|
||||
local backlog
|
||||
local pct
|
||||
|
||||
RET=0
|
||||
|
||||
start_tcp_traffic $h1.$vlan $(ipaddr 1 $vlan) $(ipaddr 3 $vlan) bc
|
||||
start_tcp_traffic $h2.$vlan $(ipaddr 2 $vlan) $(ipaddr 3 $vlan) bc
|
||||
|
||||
qbl=$(busywait 5000 until_counter_is ">= 500000" \
|
||||
get_qdisc_backlog $vlan)
|
||||
check_err $? "Could not build MC backlog"
|
||||
|
||||
# Verify that we actually see the backlog on BUM TC. Do a busywait as
|
||||
# well, performance blips might cause false fail.
|
||||
local ebl
|
||||
ebl=$(busywait 5000 until_counter_is ">= 500000" \
|
||||
get_mc_transmit_queue $vlan)
|
||||
check_err $? "MC backlog reported by qdisc not visible in ethtool"
|
||||
|
||||
stop_traffic
|
||||
stop_traffic
|
||||
|
||||
log_test "TC $((vlan - 10)): Qdisc reports MC backlog"
|
||||
}
|
94
tools/testing/selftests/drivers/net/mlxsw/sch_red_ets.sh
Executable file
94
tools/testing/selftests/drivers/net/mlxsw/sch_red_ets.sh
Executable file
@@ -0,0 +1,94 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
ALL_TESTS="
|
||||
ping_ipv4
|
||||
ecn_test
|
||||
ecn_nodrop_test
|
||||
red_test
|
||||
mc_backlog_test
|
||||
"
|
||||
: ${QDISC:=ets}
|
||||
source sch_red_core.sh
|
||||
|
||||
# do_ecn_test first build 2/3 of the requested backlog and expects no marking,
|
||||
# and then builds 3/2 of it and does expect marking. The values of $BACKLOG1 and
|
||||
# $BACKLOG2 are far enough not to overlap, so that we can assume that if we do
|
||||
# see (do not see) marking, it is actually due to the configuration of that one
|
||||
# TC, and not due to configuration of the other TC leaking over.
|
||||
BACKLOG1=200000
|
||||
BACKLOG2=500000
|
||||
|
||||
install_qdisc()
|
||||
{
|
||||
local -a args=("$@")
|
||||
|
||||
tc qdisc add dev $swp3 root handle 10: $QDISC \
|
||||
bands 8 priomap 7 6 5 4 3 2 1 0
|
||||
tc qdisc add dev $swp3 parent 10:8 handle 108: red \
|
||||
limit 1000000 min $BACKLOG1 max $((BACKLOG1 + 1)) \
|
||||
probability 1.0 avpkt 8000 burst 38 "${args[@]}"
|
||||
tc qdisc add dev $swp3 parent 10:7 handle 107: red \
|
||||
limit 1000000 min $BACKLOG2 max $((BACKLOG2 + 1)) \
|
||||
probability 1.0 avpkt 8000 burst 63 "${args[@]}"
|
||||
sleep 1
|
||||
}
|
||||
|
||||
uninstall_qdisc()
|
||||
{
|
||||
tc qdisc del dev $swp3 parent 10:7
|
||||
tc qdisc del dev $swp3 parent 10:8
|
||||
tc qdisc del dev $swp3 root
|
||||
}
|
||||
|
||||
ecn_test()
|
||||
{
|
||||
install_qdisc ecn
|
||||
|
||||
do_ecn_test 10 $BACKLOG1
|
||||
do_ecn_test 11 $BACKLOG2
|
||||
|
||||
uninstall_qdisc
|
||||
}
|
||||
|
||||
ecn_nodrop_test()
|
||||
{
|
||||
install_qdisc ecn nodrop
|
||||
|
||||
do_ecn_nodrop_test 10 $BACKLOG1
|
||||
do_ecn_nodrop_test 11 $BACKLOG2
|
||||
|
||||
uninstall_qdisc
|
||||
}
|
||||
|
||||
red_test()
|
||||
{
|
||||
install_qdisc
|
||||
|
||||
do_red_test 10 $BACKLOG1
|
||||
do_red_test 11 $BACKLOG2
|
||||
|
||||
uninstall_qdisc
|
||||
}
|
||||
|
||||
mc_backlog_test()
|
||||
{
|
||||
install_qdisc
|
||||
|
||||
# Note that the backlog numbers here do not correspond to RED
|
||||
# configuration, but are arbitrary.
|
||||
do_mc_backlog_test 10 $BACKLOG1
|
||||
do_mc_backlog_test 11 $BACKLOG2
|
||||
|
||||
uninstall_qdisc
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
setup_prepare
|
||||
setup_wait
|
||||
|
||||
bail_on_lldpad
|
||||
tests_run
|
||||
|
||||
exit $EXIT_STATUS
|
5
tools/testing/selftests/drivers/net/mlxsw/sch_red_prio.sh
Executable file
5
tools/testing/selftests/drivers/net/mlxsw/sch_red_prio.sh
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
QDISC=prio
|
||||
source sch_red_ets.sh
|
68
tools/testing/selftests/drivers/net/mlxsw/sch_red_root.sh
Executable file
68
tools/testing/selftests/drivers/net/mlxsw/sch_red_root.sh
Executable file
@@ -0,0 +1,68 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
ALL_TESTS="
|
||||
ping_ipv4
|
||||
ecn_test
|
||||
ecn_nodrop_test
|
||||
red_test
|
||||
mc_backlog_test
|
||||
"
|
||||
source sch_red_core.sh
|
||||
|
||||
BACKLOG=300000
|
||||
|
||||
install_qdisc()
|
||||
{
|
||||
local -a args=("$@")
|
||||
|
||||
tc qdisc add dev $swp3 root handle 108: red \
|
||||
limit 1000000 min $BACKLOG max $((BACKLOG + 1)) \
|
||||
probability 1.0 avpkt 8000 burst 38 "${args[@]}"
|
||||
sleep 1
|
||||
}
|
||||
|
||||
uninstall_qdisc()
|
||||
{
|
||||
tc qdisc del dev $swp3 root
|
||||
}
|
||||
|
||||
ecn_test()
|
||||
{
|
||||
install_qdisc ecn
|
||||
do_ecn_test 10 $BACKLOG
|
||||
uninstall_qdisc
|
||||
}
|
||||
|
||||
ecn_nodrop_test()
|
||||
{
|
||||
install_qdisc ecn nodrop
|
||||
do_ecn_nodrop_test 10 $BACKLOG
|
||||
uninstall_qdisc
|
||||
}
|
||||
|
||||
red_test()
|
||||
{
|
||||
install_qdisc
|
||||
do_red_test 10 $BACKLOG
|
||||
uninstall_qdisc
|
||||
}
|
||||
|
||||
mc_backlog_test()
|
||||
{
|
||||
install_qdisc
|
||||
# Note that the backlog value here does not correspond to RED
|
||||
# configuration, but is arbitrary.
|
||||
do_mc_backlog_test 10 $BACKLOG
|
||||
uninstall_qdisc
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
setup_prepare
|
||||
setup_wait
|
||||
|
||||
bail_on_lldpad
|
||||
tests_run
|
||||
|
||||
exit $EXIT_STATUS
|
222
tools/testing/selftests/drivers/net/mlxsw/sharedbuffer.sh
Executable file
222
tools/testing/selftests/drivers/net/mlxsw/sharedbuffer.sh
Executable file
@@ -0,0 +1,222 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
ALL_TESTS="
|
||||
port_pool_test
|
||||
port_tc_ip_test
|
||||
port_tc_arp_test
|
||||
"
|
||||
|
||||
NUM_NETIFS=2
|
||||
source ../../../net/forwarding/lib.sh
|
||||
source ../../../net/forwarding/devlink_lib.sh
|
||||
source mlxsw_lib.sh
|
||||
|
||||
SB_POOL_ING=0
|
||||
SB_POOL_EGR_CPU=10
|
||||
|
||||
SB_ITC_CPU_IP=3
|
||||
SB_ITC_CPU_ARP=2
|
||||
SB_ITC=0
|
||||
|
||||
h1_create()
|
||||
{
|
||||
simple_if_init $h1 192.0.1.1/24
|
||||
}
|
||||
|
||||
h1_destroy()
|
||||
{
|
||||
simple_if_fini $h1 192.0.1.1/24
|
||||
}
|
||||
|
||||
h2_create()
|
||||
{
|
||||
simple_if_init $h2 192.0.1.2/24
|
||||
}
|
||||
|
||||
h2_destroy()
|
||||
{
|
||||
simple_if_fini $h2 192.0.1.2/24
|
||||
}
|
||||
|
||||
sb_occ_pool_check()
|
||||
{
|
||||
local dl_port=$1; shift
|
||||
local pool=$1; shift
|
||||
local exp_max_occ=$1
|
||||
local max_occ
|
||||
local err=0
|
||||
|
||||
max_occ=$(devlink sb -j occupancy show $dl_port \
|
||||
| jq -e ".[][][\"pool\"][\"$pool\"][\"max\"]")
|
||||
|
||||
if [[ "$max_occ" -ne "$exp_max_occ" ]]; then
|
||||
err=1
|
||||
fi
|
||||
|
||||
echo $max_occ
|
||||
return $err
|
||||
}
|
||||
|
||||
sb_occ_itc_check()
|
||||
{
|
||||
local dl_port=$1; shift
|
||||
local itc=$1; shift
|
||||
local exp_max_occ=$1
|
||||
local max_occ
|
||||
local err=0
|
||||
|
||||
max_occ=$(devlink sb -j occupancy show $dl_port \
|
||||
| jq -e ".[][][\"itc\"][\"$itc\"][\"max\"]")
|
||||
|
||||
if [[ "$max_occ" -ne "$exp_max_occ" ]]; then
|
||||
err=1
|
||||
fi
|
||||
|
||||
echo $max_occ
|
||||
return $err
|
||||
}
|
||||
|
||||
sb_occ_etc_check()
|
||||
{
|
||||
local dl_port=$1; shift
|
||||
local etc=$1; shift
|
||||
local exp_max_occ=$1; shift
|
||||
local max_occ
|
||||
local err=0
|
||||
|
||||
max_occ=$(devlink sb -j occupancy show $dl_port \
|
||||
| jq -e ".[][][\"etc\"][\"$etc\"][\"max\"]")
|
||||
|
||||
if [[ "$max_occ" -ne "$exp_max_occ" ]]; then
|
||||
err=1
|
||||
fi
|
||||
|
||||
echo $max_occ
|
||||
return $err
|
||||
}
|
||||
|
||||
port_pool_test()
|
||||
{
|
||||
local exp_max_occ=288
|
||||
local max_occ
|
||||
|
||||
devlink sb occupancy clearmax $DEVLINK_DEV
|
||||
|
||||
$MZ $h1 -c 1 -p 160 -a $h1mac -b $h2mac -A 192.0.1.1 -B 192.0.1.2 \
|
||||
-t ip -q
|
||||
|
||||
devlink sb occupancy snapshot $DEVLINK_DEV
|
||||
|
||||
RET=0
|
||||
max_occ=$(sb_occ_pool_check $dl_port1 $SB_POOL_ING $exp_max_occ)
|
||||
check_err $? "Expected iPool($SB_POOL_ING) max occupancy to be $exp_max_occ, but got $max_occ"
|
||||
log_test "physical port's($h1) ingress pool"
|
||||
|
||||
RET=0
|
||||
max_occ=$(sb_occ_pool_check $dl_port2 $SB_POOL_ING $exp_max_occ)
|
||||
check_err $? "Expected iPool($SB_POOL_ING) max occupancy to be $exp_max_occ, but got $max_occ"
|
||||
log_test "physical port's($h2) ingress pool"
|
||||
|
||||
RET=0
|
||||
max_occ=$(sb_occ_pool_check $cpu_dl_port $SB_POOL_EGR_CPU $exp_max_occ)
|
||||
check_err $? "Expected ePool($SB_POOL_EGR_CPU) max occupancy to be $exp_max_occ, but got $max_occ"
|
||||
log_test "CPU port's egress pool"
|
||||
}
|
||||
|
||||
port_tc_ip_test()
|
||||
{
|
||||
local exp_max_occ=288
|
||||
local max_occ
|
||||
|
||||
devlink sb occupancy clearmax $DEVLINK_DEV
|
||||
|
||||
$MZ $h1 -c 1 -p 160 -a $h1mac -b $h2mac -A 192.0.1.1 -B 192.0.1.2 \
|
||||
-t ip -q
|
||||
|
||||
devlink sb occupancy snapshot $DEVLINK_DEV
|
||||
|
||||
RET=0
|
||||
max_occ=$(sb_occ_itc_check $dl_port2 $SB_ITC $exp_max_occ)
|
||||
check_err $? "Expected ingress TC($SB_ITC) max occupancy to be $exp_max_occ, but got $max_occ"
|
||||
log_test "physical port's($h1) ingress TC - IP packet"
|
||||
|
||||
RET=0
|
||||
max_occ=$(sb_occ_itc_check $dl_port2 $SB_ITC $exp_max_occ)
|
||||
check_err $? "Expected ingress TC($SB_ITC) max occupancy to be $exp_max_occ, but got $max_occ"
|
||||
log_test "physical port's($h2) ingress TC - IP packet"
|
||||
|
||||
RET=0
|
||||
max_occ=$(sb_occ_etc_check $cpu_dl_port $SB_ITC_CPU_IP $exp_max_occ)
|
||||
check_err $? "Expected egress TC($SB_ITC_CPU_IP) max occupancy to be $exp_max_occ, but got $max_occ"
|
||||
log_test "CPU port's egress TC - IP packet"
|
||||
}
|
||||
|
||||
port_tc_arp_test()
|
||||
{
|
||||
local exp_max_occ=96
|
||||
local max_occ
|
||||
|
||||
if [[ $MLXSW_CHIP != "mlxsw_spectrum" ]]; then
|
||||
exp_max_occ=144
|
||||
fi
|
||||
|
||||
devlink sb occupancy clearmax $DEVLINK_DEV
|
||||
|
||||
$MZ $h1 -c 1 -p 160 -a $h1mac -A 192.0.1.1 -t arp -q
|
||||
|
||||
devlink sb occupancy snapshot $DEVLINK_DEV
|
||||
|
||||
RET=0
|
||||
max_occ=$(sb_occ_itc_check $dl_port2 $SB_ITC $exp_max_occ)
|
||||
check_err $? "Expected ingress TC($SB_ITC) max occupancy to be $exp_max_occ, but got $max_occ"
|
||||
log_test "physical port's($h1) ingress TC - ARP packet"
|
||||
|
||||
RET=0
|
||||
max_occ=$(sb_occ_itc_check $dl_port2 $SB_ITC $exp_max_occ)
|
||||
check_err $? "Expected ingress TC($SB_ITC) max occupancy to be $exp_max_occ, but got $max_occ"
|
||||
log_test "physical port's($h2) ingress TC - ARP packet"
|
||||
|
||||
RET=0
|
||||
max_occ=$(sb_occ_etc_check $cpu_dl_port $SB_ITC_CPU_ARP $exp_max_occ)
|
||||
check_err $? "Expected egress TC($SB_ITC_IP2ME) max occupancy to be $exp_max_occ, but got $max_occ"
|
||||
log_test "CPU port's egress TC - ARP packet"
|
||||
}
|
||||
|
||||
setup_prepare()
|
||||
{
|
||||
h1=${NETIFS[p1]}
|
||||
h2=${NETIFS[p2]}
|
||||
|
||||
h1mac=$(mac_get $h1)
|
||||
h2mac=$(mac_get $h2)
|
||||
|
||||
dl_port1=$(devlink_port_by_netdev $h1)
|
||||
dl_port2=$(devlink_port_by_netdev $h2)
|
||||
|
||||
cpu_dl_port=$(devlink_cpu_port_get)
|
||||
|
||||
vrf_prepare
|
||||
|
||||
h1_create
|
||||
h2_create
|
||||
}
|
||||
|
||||
cleanup()
|
||||
{
|
||||
pre_cleanup
|
||||
|
||||
h2_destroy
|
||||
h1_destroy
|
||||
|
||||
vrf_cleanup
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
setup_prepare
|
||||
setup_wait
|
||||
|
||||
tests_run
|
||||
|
||||
exit $EXIT_STATUS
|
416
tools/testing/selftests/drivers/net/mlxsw/sharedbuffer_configuration.py
Executable file
416
tools/testing/selftests/drivers/net/mlxsw/sharedbuffer_configuration.py
Executable file
@@ -0,0 +1,416 @@
|
||||
#!/usr/bin/python
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
import subprocess
|
||||
import json as j
|
||||
import random
|
||||
|
||||
|
||||
class SkipTest(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class RandomValuePicker:
|
||||
"""
|
||||
Class for storing shared buffer configuration. Can handle 3 different
|
||||
objects, pool, tcbind and portpool. Provide an interface to get random
|
||||
values for a specific object type as the follow:
|
||||
1. Pool:
|
||||
- random size
|
||||
|
||||
2. TcBind:
|
||||
- random pool number
|
||||
- random threshold
|
||||
|
||||
3. PortPool:
|
||||
- random threshold
|
||||
"""
|
||||
def __init__(self, pools):
|
||||
self._pools = []
|
||||
for pool in pools:
|
||||
self._pools.append(pool)
|
||||
|
||||
def _cell_size(self):
|
||||
return self._pools[0]["cell_size"]
|
||||
|
||||
def _get_static_size(self, th):
|
||||
# For threshold of 16, this works out to be about 12MB on Spectrum-1,
|
||||
# and about 17MB on Spectrum-2.
|
||||
return th * 8000 * self._cell_size()
|
||||
|
||||
def _get_size(self):
|
||||
return self._get_static_size(16)
|
||||
|
||||
def _get_thtype(self):
|
||||
return "static"
|
||||
|
||||
def _get_th(self, pool):
|
||||
# Threshold value could be any integer between 3 to 16
|
||||
th = random.randint(3, 16)
|
||||
if pool["thtype"] == "dynamic":
|
||||
return th
|
||||
else:
|
||||
return self._get_static_size(th)
|
||||
|
||||
def _get_pool(self, direction):
|
||||
ing_pools = []
|
||||
egr_pools = []
|
||||
for pool in self._pools:
|
||||
if pool["type"] == "ingress":
|
||||
ing_pools.append(pool)
|
||||
else:
|
||||
egr_pools.append(pool)
|
||||
if direction == "ingress":
|
||||
arr = ing_pools
|
||||
else:
|
||||
arr = egr_pools
|
||||
return arr[random.randint(0, len(arr) - 1)]
|
||||
|
||||
def get_value(self, objid):
|
||||
if isinstance(objid, Pool):
|
||||
if objid["pool"] in [4, 8, 9, 10]:
|
||||
# The threshold type of pools 4, 8, 9 and 10 cannot be changed
|
||||
raise SkipTest()
|
||||
else:
|
||||
return (self._get_size(), self._get_thtype())
|
||||
if isinstance(objid, TcBind):
|
||||
if objid["tc"] >= 8:
|
||||
# Multicast TCs cannot be changed
|
||||
raise SkipTest()
|
||||
else:
|
||||
pool = self._get_pool(objid["type"])
|
||||
th = self._get_th(pool)
|
||||
pool_n = pool["pool"]
|
||||
return (pool_n, th)
|
||||
if isinstance(objid, PortPool):
|
||||
pool_n = objid["pool"]
|
||||
pool = self._pools[pool_n]
|
||||
assert pool["pool"] == pool_n
|
||||
th = self._get_th(pool)
|
||||
return (th,)
|
||||
|
||||
|
||||
class RecordValuePickerException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class RecordValuePicker:
|
||||
"""
|
||||
Class for storing shared buffer configuration. Can handle 2 different
|
||||
objects, pool and tcbind. Provide an interface to get the stored values per
|
||||
object type.
|
||||
"""
|
||||
def __init__(self, objlist):
|
||||
self._recs = []
|
||||
for item in objlist:
|
||||
self._recs.append({"objid": item, "value": item.var_tuple()})
|
||||
|
||||
def get_value(self, objid):
|
||||
if isinstance(objid, Pool) and objid["pool"] in [4, 8, 9, 10]:
|
||||
# The threshold type of pools 4, 8, 9 and 10 cannot be changed
|
||||
raise SkipTest()
|
||||
if isinstance(objid, TcBind) and objid["tc"] >= 8:
|
||||
# Multicast TCs cannot be changed
|
||||
raise SkipTest()
|
||||
for rec in self._recs:
|
||||
if rec["objid"].weak_eq(objid):
|
||||
return rec["value"]
|
||||
raise RecordValuePickerException()
|
||||
|
||||
|
||||
def run_cmd(cmd, json=False):
|
||||
out = subprocess.check_output(cmd, shell=True)
|
||||
if json:
|
||||
return j.loads(out)
|
||||
return out
|
||||
|
||||
|
||||
def run_json_cmd(cmd):
|
||||
return run_cmd(cmd, json=True)
|
||||
|
||||
|
||||
def log_test(test_name, err_msg=None):
|
||||
if err_msg:
|
||||
print("\t%s" % err_msg)
|
||||
print("TEST: %-80s [FAIL]" % test_name)
|
||||
else:
|
||||
print("TEST: %-80s [ OK ]" % test_name)
|
||||
|
||||
|
||||
class CommonItem(dict):
|
||||
varitems = []
|
||||
|
||||
def var_tuple(self):
|
||||
ret = []
|
||||
self.varitems.sort()
|
||||
for key in self.varitems:
|
||||
ret.append(self[key])
|
||||
return tuple(ret)
|
||||
|
||||
def weak_eq(self, other):
|
||||
for key in self:
|
||||
if key in self.varitems:
|
||||
continue
|
||||
if self[key] != other[key]:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class CommonList(list):
|
||||
def get_by(self, by_obj):
|
||||
for item in self:
|
||||
if item.weak_eq(by_obj):
|
||||
return item
|
||||
return None
|
||||
|
||||
def del_by(self, by_obj):
|
||||
for item in self:
|
||||
if item.weak_eq(by_obj):
|
||||
self.remove(item)
|
||||
|
||||
|
||||
class Pool(CommonItem):
|
||||
varitems = ["size", "thtype"]
|
||||
|
||||
def dl_set(self, dlname, size, thtype):
|
||||
run_cmd("devlink sb pool set {} sb {} pool {} size {} thtype {}".format(dlname, self["sb"],
|
||||
self["pool"],
|
||||
size, thtype))
|
||||
|
||||
|
||||
class PoolList(CommonList):
|
||||
pass
|
||||
|
||||
|
||||
def get_pools(dlname, direction=None):
|
||||
d = run_json_cmd("devlink sb pool show -j")
|
||||
pools = PoolList()
|
||||
for pooldict in d["pool"][dlname]:
|
||||
if not direction or direction == pooldict["type"]:
|
||||
pools.append(Pool(pooldict))
|
||||
return pools
|
||||
|
||||
|
||||
def do_check_pools(dlname, pools, vp):
|
||||
for pool in pools:
|
||||
pre_pools = get_pools(dlname)
|
||||
try:
|
||||
(size, thtype) = vp.get_value(pool)
|
||||
except SkipTest:
|
||||
continue
|
||||
pool.dl_set(dlname, size, thtype)
|
||||
post_pools = get_pools(dlname)
|
||||
pool = post_pools.get_by(pool)
|
||||
|
||||
err_msg = None
|
||||
if pool["size"] != size:
|
||||
err_msg = "Incorrect pool size (got {}, expected {})".format(pool["size"], size)
|
||||
if pool["thtype"] != thtype:
|
||||
err_msg = "Incorrect pool threshold type (got {}, expected {})".format(pool["thtype"], thtype)
|
||||
|
||||
pre_pools.del_by(pool)
|
||||
post_pools.del_by(pool)
|
||||
if pre_pools != post_pools:
|
||||
err_msg = "Other pool setup changed as well"
|
||||
log_test("pool {} of sb {} set verification".format(pool["pool"],
|
||||
pool["sb"]), err_msg)
|
||||
|
||||
|
||||
def check_pools(dlname, pools):
|
||||
# Save defaults
|
||||
record_vp = RecordValuePicker(pools)
|
||||
|
||||
# For each pool, set random size and static threshold type
|
||||
do_check_pools(dlname, pools, RandomValuePicker(pools))
|
||||
|
||||
# Restore defaults
|
||||
do_check_pools(dlname, pools, record_vp)
|
||||
|
||||
|
||||
class TcBind(CommonItem):
|
||||
varitems = ["pool", "threshold"]
|
||||
|
||||
def __init__(self, port, d):
|
||||
super(TcBind, self).__init__(d)
|
||||
self["dlportname"] = port.name
|
||||
|
||||
def dl_set(self, pool, th):
|
||||
run_cmd("devlink sb tc bind set {} sb {} tc {} type {} pool {} th {}".format(self["dlportname"],
|
||||
self["sb"],
|
||||
self["tc"],
|
||||
self["type"],
|
||||
pool, th))
|
||||
|
||||
|
||||
class TcBindList(CommonList):
|
||||
pass
|
||||
|
||||
|
||||
def get_tcbinds(ports, verify_existence=False):
|
||||
d = run_json_cmd("devlink sb tc bind show -j -n")
|
||||
tcbinds = TcBindList()
|
||||
for port in ports:
|
||||
err_msg = None
|
||||
if port.name not in d["tc_bind"] or len(d["tc_bind"][port.name]) == 0:
|
||||
err_msg = "No tc bind for port"
|
||||
else:
|
||||
for tcbinddict in d["tc_bind"][port.name]:
|
||||
tcbinds.append(TcBind(port, tcbinddict))
|
||||
if verify_existence:
|
||||
log_test("tc bind existence for port {} verification".format(port.name), err_msg)
|
||||
return tcbinds
|
||||
|
||||
|
||||
def do_check_tcbind(ports, tcbinds, vp):
|
||||
for tcbind in tcbinds:
|
||||
pre_tcbinds = get_tcbinds(ports)
|
||||
try:
|
||||
(pool, th) = vp.get_value(tcbind)
|
||||
except SkipTest:
|
||||
continue
|
||||
tcbind.dl_set(pool, th)
|
||||
post_tcbinds = get_tcbinds(ports)
|
||||
tcbind = post_tcbinds.get_by(tcbind)
|
||||
|
||||
err_msg = None
|
||||
if tcbind["pool"] != pool:
|
||||
err_msg = "Incorrect pool (got {}, expected {})".format(tcbind["pool"], pool)
|
||||
if tcbind["threshold"] != th:
|
||||
err_msg = "Incorrect threshold (got {}, expected {})".format(tcbind["threshold"], th)
|
||||
|
||||
pre_tcbinds.del_by(tcbind)
|
||||
post_tcbinds.del_by(tcbind)
|
||||
if pre_tcbinds != post_tcbinds:
|
||||
err_msg = "Other tc bind setup changed as well"
|
||||
log_test("tc bind {}-{} of sb {} set verification".format(tcbind["dlportname"],
|
||||
tcbind["tc"],
|
||||
tcbind["sb"]), err_msg)
|
||||
|
||||
|
||||
def check_tcbind(dlname, ports, pools):
|
||||
tcbinds = get_tcbinds(ports, verify_existence=True)
|
||||
|
||||
# Save defaults
|
||||
record_vp = RecordValuePicker(tcbinds)
|
||||
|
||||
# Bind each port and unicast TC (TCs < 8) to a random pool and a random
|
||||
# threshold
|
||||
do_check_tcbind(ports, tcbinds, RandomValuePicker(pools))
|
||||
|
||||
# Restore defaults
|
||||
do_check_tcbind(ports, tcbinds, record_vp)
|
||||
|
||||
|
||||
class PortPool(CommonItem):
|
||||
varitems = ["threshold"]
|
||||
|
||||
def __init__(self, port, d):
|
||||
super(PortPool, self).__init__(d)
|
||||
self["dlportname"] = port.name
|
||||
|
||||
def dl_set(self, th):
|
||||
run_cmd("devlink sb port pool set {} sb {} pool {} th {}".format(self["dlportname"],
|
||||
self["sb"],
|
||||
self["pool"], th))
|
||||
|
||||
|
||||
class PortPoolList(CommonList):
|
||||
pass
|
||||
|
||||
|
||||
def get_portpools(ports, verify_existence=False):
|
||||
d = run_json_cmd("devlink sb port pool -j -n")
|
||||
portpools = PortPoolList()
|
||||
for port in ports:
|
||||
err_msg = None
|
||||
if port.name not in d["port_pool"] or len(d["port_pool"][port.name]) == 0:
|
||||
err_msg = "No port pool for port"
|
||||
else:
|
||||
for portpooldict in d["port_pool"][port.name]:
|
||||
portpools.append(PortPool(port, portpooldict))
|
||||
if verify_existence:
|
||||
log_test("port pool existence for port {} verification".format(port.name), err_msg)
|
||||
return portpools
|
||||
|
||||
|
||||
def do_check_portpool(ports, portpools, vp):
|
||||
for portpool in portpools:
|
||||
pre_portpools = get_portpools(ports)
|
||||
(th,) = vp.get_value(portpool)
|
||||
portpool.dl_set(th)
|
||||
post_portpools = get_portpools(ports)
|
||||
portpool = post_portpools.get_by(portpool)
|
||||
|
||||
err_msg = None
|
||||
if portpool["threshold"] != th:
|
||||
err_msg = "Incorrect threshold (got {}, expected {})".format(portpool["threshold"], th)
|
||||
|
||||
pre_portpools.del_by(portpool)
|
||||
post_portpools.del_by(portpool)
|
||||
if pre_portpools != post_portpools:
|
||||
err_msg = "Other port pool setup changed as well"
|
||||
log_test("port pool {}-{} of sb {} set verification".format(portpool["dlportname"],
|
||||
portpool["pool"],
|
||||
portpool["sb"]), err_msg)
|
||||
|
||||
|
||||
def check_portpool(dlname, ports, pools):
|
||||
portpools = get_portpools(ports, verify_existence=True)
|
||||
|
||||
# Save defaults
|
||||
record_vp = RecordValuePicker(portpools)
|
||||
|
||||
# For each port pool, set a random threshold
|
||||
do_check_portpool(ports, portpools, RandomValuePicker(pools))
|
||||
|
||||
# Restore defaults
|
||||
do_check_portpool(ports, portpools, record_vp)
|
||||
|
||||
|
||||
class Port:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
|
||||
class PortList(list):
|
||||
pass
|
||||
|
||||
|
||||
def get_ports(dlname):
|
||||
d = run_json_cmd("devlink port show -j")
|
||||
ports = PortList()
|
||||
for name in d["port"]:
|
||||
if name.find(dlname) == 0 and d["port"][name]["flavour"] == "physical":
|
||||
ports.append(Port(name))
|
||||
return ports
|
||||
|
||||
|
||||
def get_device():
|
||||
devices_info = run_json_cmd("devlink -j dev info")["info"]
|
||||
for d in devices_info:
|
||||
if "mlxsw_spectrum" in devices_info[d]["driver"]:
|
||||
return d
|
||||
return None
|
||||
|
||||
|
||||
class UnavailableDevlinkNameException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def test_sb_configuration():
|
||||
# Use static seed
|
||||
random.seed(0)
|
||||
|
||||
dlname = get_device()
|
||||
if not dlname:
|
||||
raise UnavailableDevlinkNameException()
|
||||
|
||||
ports = get_ports(dlname)
|
||||
pools = get_pools(dlname)
|
||||
|
||||
check_pools(dlname, pools)
|
||||
check_tcbind(dlname, ports, pools)
|
||||
check_portpool(dlname, ports, pools)
|
||||
|
||||
|
||||
test_sb_configuration()
|
@@ -8,8 +8,9 @@ source $lib_dir/lib.sh
|
||||
source $lib_dir/tc_common.sh
|
||||
source $lib_dir/devlink_lib.sh
|
||||
|
||||
if [ "$DEVLINK_VIDDID" != "15b3:cf6c" ]; then
|
||||
echo "SKIP: test is tailored for Mellanox Spectrum-2"
|
||||
if [[ "$DEVLINK_VIDDID" != "15b3:cf6c" && \
|
||||
"$DEVLINK_VIDDID" != "15b3:cf70" ]]; then
|
||||
echo "SKIP: test is tailored for Mellanox Spectrum-2 and Spectrum-3"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
@@ -8,9 +8,9 @@ tc_flower_get_target()
|
||||
# The driver associates a counter with each tc filter, which means the
|
||||
# number of supported filters is bounded by the number of available
|
||||
# counters.
|
||||
# Currently, the driver supports 12K (12,288) flow counters and six of
|
||||
# Currently, the driver supports 30K (30,720) flow counters and six of
|
||||
# these are used for multicast routing.
|
||||
local target=12282
|
||||
local target=30714
|
||||
|
||||
if ((! should_fail)); then
|
||||
echo $target
|
||||
|
130
tools/testing/selftests/drivers/net/mlxsw/tc_action_hw_stats.sh
Executable file
130
tools/testing/selftests/drivers/net/mlxsw/tc_action_hw_stats.sh
Executable file
@@ -0,0 +1,130 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
lib_dir=$(dirname $0)/../../../net/forwarding
|
||||
|
||||
ALL_TESTS="
|
||||
default_hw_stats_test
|
||||
immediate_hw_stats_test
|
||||
delayed_hw_stats_test
|
||||
disabled_hw_stats_test
|
||||
"
|
||||
NUM_NETIFS=2
|
||||
|
||||
source $lib_dir/tc_common.sh
|
||||
source $lib_dir/lib.sh
|
||||
source $lib_dir/devlink_lib.sh
|
||||
|
||||
h1_create()
|
||||
{
|
||||
simple_if_init $h1 192.0.2.1/24
|
||||
}
|
||||
|
||||
h1_destroy()
|
||||
{
|
||||
simple_if_fini $h1 192.0.2.1/24
|
||||
}
|
||||
|
||||
switch_create()
|
||||
{
|
||||
simple_if_init $swp1 192.0.2.2/24
|
||||
tc qdisc add dev $swp1 clsact
|
||||
}
|
||||
|
||||
switch_destroy()
|
||||
{
|
||||
tc qdisc del dev $swp1 clsact
|
||||
simple_if_fini $swp1 192.0.2.2/24
|
||||
}
|
||||
|
||||
hw_stats_test()
|
||||
{
|
||||
RET=0
|
||||
|
||||
local name=$1
|
||||
local action_hw_stats=$2
|
||||
local occ_delta=$3
|
||||
local expected_packet_count=$4
|
||||
|
||||
local orig_occ=$(devlink_resource_get "counters" "flow" | jq '.["occ"]')
|
||||
|
||||
tc filter add dev $swp1 ingress protocol ip pref 1 handle 101 flower \
|
||||
skip_sw dst_ip 192.0.2.2 action drop $action_hw_stats
|
||||
check_err $? "Failed to add rule with $name hw_stats"
|
||||
|
||||
local new_occ=$(devlink_resource_get "counters" "flow" | jq '.["occ"]')
|
||||
local expected_occ=$((orig_occ + occ_delta))
|
||||
[ "$new_occ" == "$expected_occ" ]
|
||||
check_err $? "Expected occupancy of $expected_occ, got $new_occ"
|
||||
|
||||
$MZ $h1 -c 1 -p 64 -a $h1mac -b $swp1mac -A 192.0.2.1 -B 192.0.2.2 \
|
||||
-t ip -q
|
||||
|
||||
tc_check_packets "dev $swp1 ingress" 101 $expected_packet_count
|
||||
check_err $? "Did not match incoming packet"
|
||||
|
||||
tc filter del dev $swp1 ingress protocol ip pref 1 handle 101 flower
|
||||
|
||||
log_test "$name hw_stats"
|
||||
}
|
||||
|
||||
default_hw_stats_test()
|
||||
{
|
||||
hw_stats_test "default" "" 2 1
|
||||
}
|
||||
|
||||
immediate_hw_stats_test()
|
||||
{
|
||||
hw_stats_test "immediate" "hw_stats immediate" 2 1
|
||||
}
|
||||
|
||||
delayed_hw_stats_test()
|
||||
{
|
||||
RET=0
|
||||
|
||||
tc filter add dev $swp1 ingress protocol ip pref 1 handle 101 flower \
|
||||
skip_sw dst_ip 192.0.2.2 action drop hw_stats delayed
|
||||
check_fail $? "Unexpected success in adding rule with delayed hw_stats"
|
||||
|
||||
log_test "delayed hw_stats"
|
||||
}
|
||||
|
||||
disabled_hw_stats_test()
|
||||
{
|
||||
hw_stats_test "disabled" "hw_stats disabled" 0 0
|
||||
}
|
||||
|
||||
setup_prepare()
|
||||
{
|
||||
h1=${NETIFS[p1]}
|
||||
swp1=${NETIFS[p2]}
|
||||
|
||||
h1mac=$(mac_get $h1)
|
||||
swp1mac=$(mac_get $swp1)
|
||||
|
||||
vrf_prepare
|
||||
|
||||
h1_create
|
||||
switch_create
|
||||
}
|
||||
|
||||
cleanup()
|
||||
{
|
||||
pre_cleanup
|
||||
|
||||
switch_destroy
|
||||
h1_destroy
|
||||
|
||||
vrf_cleanup
|
||||
}
|
||||
|
||||
check_tc_action_hw_stats_support
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
setup_prepare
|
||||
setup_wait
|
||||
|
||||
tests_run
|
||||
|
||||
exit $EXIT_STATUS
|
186
tools/testing/selftests/drivers/net/mlxsw/tc_flower_restrictions.sh
Executable file
186
tools/testing/selftests/drivers/net/mlxsw/tc_flower_restrictions.sh
Executable file
@@ -0,0 +1,186 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
lib_dir=$(dirname $0)/../../../net/forwarding
|
||||
|
||||
ALL_TESTS="
|
||||
shared_block_drop_test
|
||||
egress_redirect_test
|
||||
multi_mirror_test
|
||||
"
|
||||
NUM_NETIFS=2
|
||||
|
||||
source $lib_dir/tc_common.sh
|
||||
source $lib_dir/lib.sh
|
||||
|
||||
switch_create()
|
||||
{
|
||||
simple_if_init $swp1 192.0.2.1/24
|
||||
simple_if_init $swp2 192.0.2.2/24
|
||||
}
|
||||
|
||||
switch_destroy()
|
||||
{
|
||||
simple_if_fini $swp2 192.0.2.2/24
|
||||
simple_if_fini $swp1 192.0.2.1/24
|
||||
}
|
||||
|
||||
shared_block_drop_test()
|
||||
{
|
||||
RET=0
|
||||
|
||||
# It is forbidden in mlxsw driver to have mixed-bound
|
||||
# shared block with a drop rule.
|
||||
|
||||
tc qdisc add dev $swp1 ingress_block 22 clsact
|
||||
check_err $? "Failed to create clsact with ingress block"
|
||||
|
||||
tc filter add block 22 protocol ip pref 1 handle 101 flower \
|
||||
skip_sw dst_ip 192.0.2.2 action drop
|
||||
check_err $? "Failed to add drop rule to ingress bound block"
|
||||
|
||||
tc qdisc add dev $swp2 ingress_block 22 clsact
|
||||
check_err $? "Failed to create another clsact with ingress shared block"
|
||||
|
||||
tc qdisc del dev $swp2 clsact
|
||||
|
||||
tc qdisc add dev $swp2 egress_block 22 clsact
|
||||
check_fail $? "Incorrect success to create another clsact with egress shared block"
|
||||
|
||||
tc filter del block 22 protocol ip pref 1 handle 101 flower
|
||||
|
||||
tc qdisc add dev $swp2 egress_block 22 clsact
|
||||
check_err $? "Failed to create another clsact with egress shared block after blocker drop rule removed"
|
||||
|
||||
tc filter add block 22 protocol ip pref 1 handle 101 flower \
|
||||
skip_sw dst_ip 192.0.2.2 action drop
|
||||
check_fail $? "Incorrect success to add drop rule to mixed bound block"
|
||||
|
||||
tc qdisc del dev $swp1 clsact
|
||||
|
||||
tc qdisc add dev $swp1 egress_block 22 clsact
|
||||
check_err $? "Failed to create another clsact with egress shared block"
|
||||
|
||||
tc filter add block 22 protocol ip pref 1 handle 101 flower \
|
||||
skip_sw dst_ip 192.0.2.2 action drop
|
||||
check_err $? "Failed to add drop rule to egress bound shared block"
|
||||
|
||||
tc filter del block 22 protocol ip pref 1 handle 101 flower
|
||||
|
||||
tc qdisc del dev $swp2 clsact
|
||||
tc qdisc del dev $swp1 clsact
|
||||
|
||||
log_test "shared block drop"
|
||||
}
|
||||
|
||||
egress_redirect_test()
|
||||
{
|
||||
RET=0
|
||||
|
||||
# It is forbidden in mlxsw driver to have mirred redirect on
|
||||
# egress-bound block.
|
||||
|
||||
tc qdisc add dev $swp1 ingress_block 22 clsact
|
||||
check_err $? "Failed to create clsact with ingress block"
|
||||
|
||||
tc filter add block 22 protocol ip pref 1 handle 101 flower \
|
||||
skip_sw dst_ip 192.0.2.2 \
|
||||
action mirred egress redirect dev $swp2
|
||||
check_err $? "Failed to add redirect rule to ingress bound block"
|
||||
|
||||
tc qdisc add dev $swp2 ingress_block 22 clsact
|
||||
check_err $? "Failed to create another clsact with ingress shared block"
|
||||
|
||||
tc qdisc del dev $swp2 clsact
|
||||
|
||||
tc qdisc add dev $swp2 egress_block 22 clsact
|
||||
check_fail $? "Incorrect success to create another clsact with egress shared block"
|
||||
|
||||
tc filter del block 22 protocol ip pref 1 handle 101 flower
|
||||
|
||||
tc qdisc add dev $swp2 egress_block 22 clsact
|
||||
check_err $? "Failed to create another clsact with egress shared block after blocker redirect rule removed"
|
||||
|
||||
tc filter add block 22 protocol ip pref 1 handle 101 flower \
|
||||
skip_sw dst_ip 192.0.2.2 \
|
||||
action mirred egress redirect dev $swp2
|
||||
check_fail $? "Incorrect success to add redirect rule to mixed bound block"
|
||||
|
||||
tc qdisc del dev $swp1 clsact
|
||||
|
||||
tc qdisc add dev $swp1 egress_block 22 clsact
|
||||
check_err $? "Failed to create another clsact with egress shared block"
|
||||
|
||||
tc filter add block 22 protocol ip pref 1 handle 101 flower \
|
||||
skip_sw dst_ip 192.0.2.2 \
|
||||
action mirred egress redirect dev $swp2
|
||||
check_fail $? "Incorrect success to add redirect rule to egress bound shared block"
|
||||
|
||||
tc qdisc del dev $swp2 clsact
|
||||
|
||||
tc filter add block 22 protocol ip pref 1 handle 101 flower \
|
||||
skip_sw dst_ip 192.0.2.2 \
|
||||
action mirred egress redirect dev $swp2
|
||||
check_fail $? "Incorrect success to add redirect rule to egress bound block"
|
||||
|
||||
tc qdisc del dev $swp1 clsact
|
||||
|
||||
log_test "shared block drop"
|
||||
}
|
||||
|
||||
multi_mirror_test()
|
||||
{
|
||||
RET=0
|
||||
|
||||
# It is forbidden in mlxsw driver to have multiple mirror
|
||||
# actions in a single rule.
|
||||
|
||||
tc qdisc add dev $swp1 clsact
|
||||
|
||||
tc filter add dev $swp1 ingress protocol ip pref 1 handle 101 flower \
|
||||
skip_sw dst_ip 192.0.2.2 \
|
||||
action mirred egress mirror dev $swp2
|
||||
check_err $? "Failed to add rule with single mirror action"
|
||||
|
||||
tc filter del dev $swp1 ingress protocol ip pref 1 handle 101 flower
|
||||
|
||||
tc filter add dev $swp1 ingress protocol ip pref 1 handle 101 flower \
|
||||
skip_sw dst_ip 192.0.2.2 \
|
||||
action mirred egress mirror dev $swp2 \
|
||||
action mirred egress mirror dev $swp1
|
||||
check_fail $? "Incorrect success to add rule with two mirror actions"
|
||||
|
||||
tc qdisc del dev $swp1 clsact
|
||||
|
||||
log_test "multi mirror"
|
||||
}
|
||||
|
||||
setup_prepare()
|
||||
{
|
||||
swp1=${NETIFS[p1]}
|
||||
swp2=${NETIFS[p2]}
|
||||
|
||||
vrf_prepare
|
||||
|
||||
switch_create
|
||||
}
|
||||
|
||||
cleanup()
|
||||
{
|
||||
pre_cleanup
|
||||
|
||||
switch_destroy
|
||||
|
||||
vrf_cleanup
|
||||
}
|
||||
|
||||
check_tc_shblock_support
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
setup_prepare
|
||||
setup_wait
|
||||
|
||||
tests_run
|
||||
|
||||
exit $EXIT_STATUS
|
@@ -2,9 +2,9 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
# Test for resource limit of offloaded flower rules. The test adds a given
|
||||
# number of flower matches for different IPv6 addresses, then generates traffic,
|
||||
# and ensures each was hit exactly once. This file contains functions to set up
|
||||
# a testing topology and run the test, and is meant to be sourced from a test
|
||||
# number of flower matches for different IPv6 addresses, then check the offload
|
||||
# indication for all of the tc flower rules. This file contains functions to set
|
||||
# up a testing topology and run the test, and is meant to be sourced from a test
|
||||
# script that calls the testing routine with a given number of rules.
|
||||
|
||||
TC_FLOWER_NUM_NETIFS=2
|
||||
@@ -94,22 +94,15 @@ __tc_flower_test()
|
||||
|
||||
tc_flower_rules_create $count $should_fail
|
||||
|
||||
for ((i = 0; i < count; ++i)); do
|
||||
$MZ $h1 -q -c 1 -t ip -p 20 -b bc -6 \
|
||||
-A 2001:db8:2::1 \
|
||||
-B $(tc_flower_addr $i)
|
||||
done
|
||||
|
||||
MISMATCHES=$(
|
||||
tc -j -s filter show dev $h2 ingress |
|
||||
jq -r '[ .[] | select(.kind == "flower") | .options |
|
||||
values as $rule | .actions[].stats.packets |
|
||||
select(. != 1) | "\(.) on \($rule.keys.dst_ip)" ] |
|
||||
join(", ")'
|
||||
)
|
||||
|
||||
test -z "$MISMATCHES"
|
||||
check_err $? "Expected to capture 1 packet for each IP, but got $MISMATCHES"
|
||||
offload_count=$(tc -j -s filter show dev $h2 ingress |
|
||||
jq -r '[ .[] | select(.kind == "flower") |
|
||||
.options | .in_hw ]' | jq .[] | wc -l)
|
||||
[[ $((offload_count - 1)) -eq $count ]]
|
||||
if [[ $should_fail -eq 0 ]]; then
|
||||
check_err $? "Offload mismatch"
|
||||
else
|
||||
check_err_fail $should_fail $? "Offload more than expacted"
|
||||
fi
|
||||
}
|
||||
|
||||
tc_flower_test()
|
||||
|
@@ -9,6 +9,7 @@ lib_dir=$(dirname $0)/../../../net/forwarding
|
||||
ALL_TESTS="sanitization_test offload_indication_test \
|
||||
sanitization_vlan_aware_test offload_indication_vlan_aware_test"
|
||||
NUM_NETIFS=2
|
||||
: ${TIMEOUT:=20000} # ms
|
||||
source $lib_dir/lib.sh
|
||||
|
||||
setup_prepare()
|
||||
@@ -470,8 +471,8 @@ offload_indication_fdb_flood_test()
|
||||
|
||||
bridge fdb append 00:00:00:00:00:00 dev vxlan0 self dst 198.51.100.2
|
||||
|
||||
bridge fdb show brport vxlan0 | grep 00:00:00:00:00:00 \
|
||||
| grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload grep_bridge_fdb 00:00:00:00:00:00 \
|
||||
bridge fdb show brport vxlan0
|
||||
check_err $?
|
||||
|
||||
bridge fdb del 00:00:00:00:00:00 dev vxlan0 self
|
||||
@@ -486,11 +487,11 @@ offload_indication_fdb_bridge_test()
|
||||
bridge fdb add de:ad:be:ef:13:37 dev vxlan0 self master static \
|
||||
dst 198.51.100.2
|
||||
|
||||
bridge fdb show brport vxlan0 | grep de:ad:be:ef:13:37 | grep self \
|
||||
| grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload grep_bridge_fdb \
|
||||
de:ad:be:ef:13:37 self bridge fdb show brport vxlan0
|
||||
check_err $?
|
||||
bridge fdb show brport vxlan0 | grep de:ad:be:ef:13:37 | grep -v self \
|
||||
| grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload grep_bridge_fdb \
|
||||
de:ad:be:ef:13:37 self -v bridge fdb show brport vxlan0
|
||||
check_err $?
|
||||
|
||||
log_test "vxlan entry offload indication - initial state"
|
||||
@@ -500,9 +501,9 @@ offload_indication_fdb_bridge_test()
|
||||
RET=0
|
||||
|
||||
bridge fdb del de:ad:be:ef:13:37 dev vxlan0 master
|
||||
bridge fdb show brport vxlan0 | grep de:ad:be:ef:13:37 | grep self \
|
||||
| grep -q offload
|
||||
check_fail $?
|
||||
busywait "$TIMEOUT" not wait_for_offload grep_bridge_fdb \
|
||||
de:ad:be:ef:13:37 self bridge fdb show brport vxlan0
|
||||
check_err $?
|
||||
|
||||
log_test "vxlan entry offload indication - after removal from bridge"
|
||||
|
||||
@@ -511,11 +512,11 @@ offload_indication_fdb_bridge_test()
|
||||
RET=0
|
||||
|
||||
bridge fdb add de:ad:be:ef:13:37 dev vxlan0 master static
|
||||
bridge fdb show brport vxlan0 | grep de:ad:be:ef:13:37 | grep self \
|
||||
| grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload grep_bridge_fdb \
|
||||
de:ad:be:ef:13:37 self bridge fdb show brport vxlan0
|
||||
check_err $?
|
||||
bridge fdb show brport vxlan0 | grep de:ad:be:ef:13:37 | grep -v self \
|
||||
| grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload grep_bridge_fdb \
|
||||
de:ad:be:ef:13:37 self -v bridge fdb show brport vxlan0
|
||||
check_err $?
|
||||
|
||||
log_test "vxlan entry offload indication - after re-add to bridge"
|
||||
@@ -525,9 +526,9 @@ offload_indication_fdb_bridge_test()
|
||||
RET=0
|
||||
|
||||
bridge fdb del de:ad:be:ef:13:37 dev vxlan0 self
|
||||
bridge fdb show brport vxlan0 | grep de:ad:be:ef:13:37 | grep -v self \
|
||||
| grep -q offload
|
||||
check_fail $?
|
||||
busywait "$TIMEOUT" not wait_for_offload grep_bridge_fdb \
|
||||
de:ad:be:ef:13:37 self -v bridge fdb show brport vxlan0
|
||||
check_err $?
|
||||
|
||||
log_test "vxlan entry offload indication - after removal from vxlan"
|
||||
|
||||
@@ -536,11 +537,11 @@ offload_indication_fdb_bridge_test()
|
||||
RET=0
|
||||
|
||||
bridge fdb add de:ad:be:ef:13:37 dev vxlan0 self dst 198.51.100.2
|
||||
bridge fdb show brport vxlan0 | grep de:ad:be:ef:13:37 | grep self \
|
||||
| grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload grep_bridge_fdb \
|
||||
de:ad:be:ef:13:37 self bridge fdb show brport vxlan0
|
||||
check_err $?
|
||||
bridge fdb show brport vxlan0 | grep de:ad:be:ef:13:37 | grep -v self \
|
||||
| grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload grep_bridge_fdb \
|
||||
de:ad:be:ef:13:37 self -v bridge fdb show brport vxlan0
|
||||
check_err $?
|
||||
|
||||
log_test "vxlan entry offload indication - after re-add to vxlan"
|
||||
@@ -558,27 +559,32 @@ offload_indication_decap_route_test()
|
||||
{
|
||||
RET=0
|
||||
|
||||
ip route show table local | grep 198.51.100.1 | grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload \
|
||||
ip route show table local 198.51.100.1
|
||||
check_err $?
|
||||
|
||||
ip link set dev vxlan0 down
|
||||
ip route show table local | grep 198.51.100.1 | grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload \
|
||||
ip route show table local 198.51.100.1
|
||||
check_err $?
|
||||
|
||||
ip link set dev vxlan1 down
|
||||
ip route show table local | grep 198.51.100.1 | grep -q offload
|
||||
check_fail $?
|
||||
busywait "$TIMEOUT" not wait_for_offload \
|
||||
ip route show table local 198.51.100.1
|
||||
check_err $?
|
||||
|
||||
log_test "vxlan decap route - vxlan device down"
|
||||
|
||||
RET=0
|
||||
|
||||
ip link set dev vxlan1 up
|
||||
ip route show table local | grep 198.51.100.1 | grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload \
|
||||
ip route show table local 198.51.100.1
|
||||
check_err $?
|
||||
|
||||
ip link set dev vxlan0 up
|
||||
ip route show table local | grep 198.51.100.1 | grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload \
|
||||
ip route show table local 198.51.100.1
|
||||
check_err $?
|
||||
|
||||
log_test "vxlan decap route - vxlan device up"
|
||||
@@ -586,11 +592,13 @@ offload_indication_decap_route_test()
|
||||
RET=0
|
||||
|
||||
ip address delete 198.51.100.1/32 dev lo
|
||||
ip route show table local | grep 198.51.100.1 | grep -q offload
|
||||
check_fail $?
|
||||
busywait "$TIMEOUT" not wait_for_offload \
|
||||
ip route show table local 198.51.100.1
|
||||
check_err $?
|
||||
|
||||
ip address add 198.51.100.1/32 dev lo
|
||||
ip route show table local | grep 198.51.100.1 | grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload \
|
||||
ip route show table local 198.51.100.1
|
||||
check_err $?
|
||||
|
||||
log_test "vxlan decap route - add local route"
|
||||
@@ -598,16 +606,19 @@ offload_indication_decap_route_test()
|
||||
RET=0
|
||||
|
||||
ip link set dev $swp1 nomaster
|
||||
ip route show table local | grep 198.51.100.1 | grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload \
|
||||
ip route show table local 198.51.100.1
|
||||
check_err $?
|
||||
|
||||
ip link set dev $swp2 nomaster
|
||||
ip route show table local | grep 198.51.100.1 | grep -q offload
|
||||
check_fail $?
|
||||
busywait "$TIMEOUT" not wait_for_offload \
|
||||
ip route show table local 198.51.100.1
|
||||
check_err $?
|
||||
|
||||
ip link set dev $swp1 master br0
|
||||
ip link set dev $swp2 master br1
|
||||
ip route show table local | grep 198.51.100.1 | grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload \
|
||||
ip route show table local 198.51.100.1
|
||||
check_err $?
|
||||
|
||||
log_test "vxlan decap route - local ports enslavement"
|
||||
@@ -615,12 +626,14 @@ offload_indication_decap_route_test()
|
||||
RET=0
|
||||
|
||||
ip link del dev br0
|
||||
ip route show table local | grep 198.51.100.1 | grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload \
|
||||
ip route show table local 198.51.100.1
|
||||
check_err $?
|
||||
|
||||
ip link del dev br1
|
||||
ip route show table local | grep 198.51.100.1 | grep -q offload
|
||||
check_fail $?
|
||||
busywait "$TIMEOUT" not wait_for_offload \
|
||||
ip route show table local 198.51.100.1
|
||||
check_err $?
|
||||
|
||||
log_test "vxlan decap route - bridge device deletion"
|
||||
|
||||
@@ -632,16 +645,19 @@ offload_indication_decap_route_test()
|
||||
ip link set dev $swp2 master br1
|
||||
ip link set dev vxlan0 master br0
|
||||
ip link set dev vxlan1 master br1
|
||||
ip route show table local | grep 198.51.100.1 | grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload \
|
||||
ip route show table local 198.51.100.1
|
||||
check_err $?
|
||||
|
||||
ip link del dev vxlan0
|
||||
ip route show table local | grep 198.51.100.1 | grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload \
|
||||
ip route show table local 198.51.100.1
|
||||
check_err $?
|
||||
|
||||
ip link del dev vxlan1
|
||||
ip route show table local | grep 198.51.100.1 | grep -q offload
|
||||
check_fail $?
|
||||
busywait "$TIMEOUT" not wait_for_offload \
|
||||
ip route show table local 198.51.100.1
|
||||
check_err $?
|
||||
|
||||
log_test "vxlan decap route - vxlan device deletion"
|
||||
|
||||
@@ -656,12 +672,15 @@ check_fdb_offloaded()
|
||||
local mac=00:11:22:33:44:55
|
||||
local zmac=00:00:00:00:00:00
|
||||
|
||||
bridge fdb show dev vxlan0 | grep $mac | grep self | grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload grep_bridge_fdb $mac self \
|
||||
bridge fdb show dev vxlan0
|
||||
check_err $?
|
||||
bridge fdb show dev vxlan0 | grep $mac | grep master | grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload grep_bridge_fdb $mac master \
|
||||
bridge fdb show dev vxlan0
|
||||
check_err $?
|
||||
|
||||
bridge fdb show dev vxlan0 | grep $zmac | grep self | grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload grep_bridge_fdb $zmac self \
|
||||
bridge fdb show dev vxlan0
|
||||
check_err $?
|
||||
}
|
||||
|
||||
@@ -672,13 +691,15 @@ check_vxlan_fdb_not_offloaded()
|
||||
|
||||
bridge fdb show dev vxlan0 | grep $mac | grep -q self
|
||||
check_err $?
|
||||
bridge fdb show dev vxlan0 | grep $mac | grep self | grep -q offload
|
||||
check_fail $?
|
||||
busywait "$TIMEOUT" not wait_for_offload grep_bridge_fdb $mac self \
|
||||
bridge fdb show dev vxlan0
|
||||
check_err $?
|
||||
|
||||
bridge fdb show dev vxlan0 | grep $zmac | grep -q self
|
||||
check_err $?
|
||||
bridge fdb show dev vxlan0 | grep $zmac | grep self | grep -q offload
|
||||
check_fail $?
|
||||
busywait "$TIMEOUT" not wait_for_offload grep_bridge_fdb $zmac self \
|
||||
bridge fdb show dev vxlan0
|
||||
check_err $?
|
||||
}
|
||||
|
||||
check_bridge_fdb_not_offloaded()
|
||||
@@ -688,8 +709,9 @@ check_bridge_fdb_not_offloaded()
|
||||
|
||||
bridge fdb show dev vxlan0 | grep $mac | grep -q master
|
||||
check_err $?
|
||||
bridge fdb show dev vxlan0 | grep $mac | grep master | grep -q offload
|
||||
check_fail $?
|
||||
busywait "$TIMEOUT" not wait_for_offload grep_bridge_fdb $mac master \
|
||||
bridge fdb show dev vxlan0
|
||||
check_err $?
|
||||
}
|
||||
|
||||
__offload_indication_join_vxlan_first()
|
||||
@@ -771,12 +793,14 @@ __offload_indication_join_vxlan_last()
|
||||
|
||||
ip link set dev $swp1 master br0
|
||||
|
||||
bridge fdb show dev vxlan0 | grep $zmac | grep self | grep -q offload
|
||||
check_fail $?
|
||||
busywait "$TIMEOUT" not wait_for_offload grep_bridge_fdb $zmac self \
|
||||
bridge fdb show dev vxlan0
|
||||
check_err $?
|
||||
|
||||
ip link set dev vxlan0 master br0
|
||||
|
||||
bridge fdb show dev vxlan0 | grep $zmac | grep self | grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload grep_bridge_fdb $zmac self \
|
||||
bridge fdb show dev vxlan0
|
||||
check_err $?
|
||||
|
||||
log_test "offload indication - attach vxlan last"
|
||||
@@ -854,20 +878,26 @@ sanitization_vlan_aware_test()
|
||||
bridge vlan del vid 10 dev vxlan20
|
||||
bridge vlan add vid 20 dev vxlan20 pvid untagged
|
||||
|
||||
# Test that offloading of an unsupported tunnel fails when it is
|
||||
# triggered by addition of VLAN to a local port
|
||||
RET=0
|
||||
# Test that when two VXLAN tunnels with conflicting configurations
|
||||
# (i.e., different TTL) are enslaved to the same VLAN-aware bridge,
|
||||
# then the enslavement of a port to the bridge is denied.
|
||||
|
||||
# TOS must be set to inherit
|
||||
ip link set dev vxlan10 type vxlan tos 42
|
||||
# Use the offload indication of the local route to ensure the VXLAN
|
||||
# configuration was correctly rollbacked.
|
||||
ip address add 198.51.100.1/32 dev lo
|
||||
|
||||
ip link set dev $swp1 master br0
|
||||
bridge vlan add vid 10 dev $swp1 &> /dev/null
|
||||
ip link set dev vxlan10 type vxlan ttl 10
|
||||
ip link set dev $swp1 master br0 &> /dev/null
|
||||
check_fail $?
|
||||
|
||||
log_test "vlan-aware - failed vlan addition to a local port"
|
||||
busywait "$TIMEOUT" not wait_for_offload \
|
||||
ip route show table local 198.51.100.1
|
||||
check_err $?
|
||||
|
||||
ip link set dev vxlan10 type vxlan tos inherit
|
||||
log_test "vlan-aware - failed enslavement to bridge due to conflict"
|
||||
|
||||
ip link set dev vxlan10 type vxlan ttl 20
|
||||
ip address del 198.51.100.1/32 dev lo
|
||||
|
||||
ip link del dev vxlan20
|
||||
ip link del dev vxlan10
|
||||
@@ -924,11 +954,11 @@ offload_indication_vlan_aware_fdb_test()
|
||||
bridge fdb add de:ad:be:ef:13:37 dev vxlan10 self master static \
|
||||
dst 198.51.100.2 vlan 10
|
||||
|
||||
bridge fdb show brport vxlan10 | grep de:ad:be:ef:13:37 | grep self \
|
||||
| grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload grep_bridge_fdb \
|
||||
de:ad:be:ef:13:37 self bridge fdb show brport vxlan10
|
||||
check_err $?
|
||||
bridge fdb show brport vxlan10 | grep de:ad:be:ef:13:37 | grep -v self \
|
||||
| grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload grep_bridge_fdb \
|
||||
de:ad:be:ef:13:37 self -v bridge fdb show brport vxlan10
|
||||
check_err $?
|
||||
|
||||
log_test "vxlan entry offload indication - initial state"
|
||||
@@ -938,9 +968,9 @@ offload_indication_vlan_aware_fdb_test()
|
||||
RET=0
|
||||
|
||||
bridge fdb del de:ad:be:ef:13:37 dev vxlan10 master vlan 10
|
||||
bridge fdb show brport vxlan10 | grep de:ad:be:ef:13:37 | grep self \
|
||||
| grep -q offload
|
||||
check_fail $?
|
||||
busywait "$TIMEOUT" not wait_for_offload grep_bridge_fdb \
|
||||
de:ad:be:ef:13:37 self bridge fdb show brport vxlan10
|
||||
check_err $?
|
||||
|
||||
log_test "vxlan entry offload indication - after removal from bridge"
|
||||
|
||||
@@ -949,11 +979,11 @@ offload_indication_vlan_aware_fdb_test()
|
||||
RET=0
|
||||
|
||||
bridge fdb add de:ad:be:ef:13:37 dev vxlan10 master static vlan 10
|
||||
bridge fdb show brport vxlan10 | grep de:ad:be:ef:13:37 | grep self \
|
||||
| grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload grep_bridge_fdb \
|
||||
de:ad:be:ef:13:37 self bridge fdb show brport vxlan10
|
||||
check_err $?
|
||||
bridge fdb show brport vxlan10 | grep de:ad:be:ef:13:37 | grep -v self \
|
||||
| grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload grep_bridge_fdb \
|
||||
de:ad:be:ef:13:37 self -v bridge fdb show brport vxlan10
|
||||
check_err $?
|
||||
|
||||
log_test "vxlan entry offload indication - after re-add to bridge"
|
||||
@@ -963,9 +993,9 @@ offload_indication_vlan_aware_fdb_test()
|
||||
RET=0
|
||||
|
||||
bridge fdb del de:ad:be:ef:13:37 dev vxlan10 self
|
||||
bridge fdb show brport vxlan10 | grep de:ad:be:ef:13:37 | grep -v self \
|
||||
| grep -q offload
|
||||
check_fail $?
|
||||
busywait "$TIMEOUT" not wait_for_offload grep_bridge_fdb \
|
||||
de:ad:be:ef:13:37 self -v bridge fdb show brport vxlan10
|
||||
check_err $?
|
||||
|
||||
log_test "vxlan entry offload indication - after removal from vxlan"
|
||||
|
||||
@@ -974,11 +1004,11 @@ offload_indication_vlan_aware_fdb_test()
|
||||
RET=0
|
||||
|
||||
bridge fdb add de:ad:be:ef:13:37 dev vxlan10 self dst 198.51.100.2
|
||||
bridge fdb show brport vxlan10 | grep de:ad:be:ef:13:37 | grep self \
|
||||
| grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload grep_bridge_fdb \
|
||||
de:ad:be:ef:13:37 self bridge fdb show brport vxlan10
|
||||
check_err $?
|
||||
bridge fdb show brport vxlan10 | grep de:ad:be:ef:13:37 | grep -v self \
|
||||
| grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload grep_bridge_fdb \
|
||||
de:ad:be:ef:13:37 self -v bridge fdb show brport vxlan10
|
||||
check_err $?
|
||||
|
||||
log_test "vxlan entry offload indication - after re-add to vxlan"
|
||||
@@ -990,28 +1020,31 @@ offload_indication_vlan_aware_decap_route_test()
|
||||
{
|
||||
RET=0
|
||||
|
||||
ip route show table local | grep 198.51.100.1 | grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload \
|
||||
ip route show table local 198.51.100.1
|
||||
check_err $?
|
||||
|
||||
# Toggle PVID flag on one VxLAN device and make sure route is still
|
||||
# marked as offloaded
|
||||
bridge vlan add vid 10 dev vxlan10 untagged
|
||||
|
||||
ip route show table local | grep 198.51.100.1 | grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload \
|
||||
ip route show table local 198.51.100.1
|
||||
check_err $?
|
||||
|
||||
# Toggle PVID flag on second VxLAN device and make sure route is no
|
||||
# longer marked as offloaded
|
||||
bridge vlan add vid 20 dev vxlan20 untagged
|
||||
|
||||
ip route show table local | grep 198.51.100.1 | grep -q offload
|
||||
check_fail $?
|
||||
busywait "$TIMEOUT" not wait_for_offload \
|
||||
ip route show table local 198.51.100.1
|
||||
check_err $?
|
||||
|
||||
# Toggle PVID flag back and make sure route is marked as offloaded
|
||||
bridge vlan add vid 10 dev vxlan10 pvid untagged
|
||||
bridge vlan add vid 20 dev vxlan20 pvid untagged
|
||||
|
||||
ip route show table local | grep 198.51.100.1 | grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload ip route show table local 198.51.100.1
|
||||
check_err $?
|
||||
|
||||
log_test "vxlan decap route - vni map/unmap"
|
||||
@@ -1064,35 +1097,33 @@ offload_indication_vlan_aware_l3vni_test()
|
||||
ip link set dev vxlan0 master br0
|
||||
bridge vlan add dev vxlan0 vid 10 pvid untagged
|
||||
|
||||
# No local port or router port is member in the VLAN, so tunnel should
|
||||
# not be offloaded
|
||||
bridge fdb show brport vxlan0 | grep $zmac | grep self \
|
||||
| grep -q offload
|
||||
check_fail $? "vxlan tunnel offloaded when should not"
|
||||
busywait "$TIMEOUT" wait_for_offload grep_bridge_fdb $zmac self \
|
||||
bridge fdb show brport vxlan0
|
||||
check_err $? "vxlan tunnel not offloaded when should"
|
||||
|
||||
# Configure a VLAN interface and make sure tunnel is offloaded
|
||||
ip link add link br0 name br10 up type vlan id 10
|
||||
sysctl_set net.ipv6.conf.br10.disable_ipv6 0
|
||||
ip -6 address add 2001:db8:1::1/64 dev br10
|
||||
bridge fdb show brport vxlan0 | grep $zmac | grep self \
|
||||
| grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload grep_bridge_fdb $zmac self \
|
||||
bridge fdb show brport vxlan0
|
||||
check_err $? "vxlan tunnel not offloaded when should"
|
||||
|
||||
# Unlink the VXLAN device, make sure tunnel is no longer offloaded,
|
||||
# then add it back to the bridge and make sure it is offloaded
|
||||
ip link set dev vxlan0 nomaster
|
||||
bridge fdb show brport vxlan0 | grep $zmac | grep self \
|
||||
| grep -q offload
|
||||
check_fail $? "vxlan tunnel offloaded after unlinked from bridge"
|
||||
busywait "$TIMEOUT" not wait_for_offload grep_bridge_fdb $zmac self \
|
||||
bridge fdb show brport vxlan0
|
||||
check_err $? "vxlan tunnel offloaded after unlinked from bridge"
|
||||
|
||||
ip link set dev vxlan0 master br0
|
||||
bridge fdb show brport vxlan0 | grep $zmac | grep self \
|
||||
| grep -q offload
|
||||
check_fail $? "vxlan tunnel offloaded despite no matching vid"
|
||||
busywait "$TIMEOUT" not wait_for_offload grep_bridge_fdb $zmac self \
|
||||
bridge fdb show brport vxlan0
|
||||
check_err $? "vxlan tunnel offloaded despite no matching vid"
|
||||
|
||||
bridge vlan add dev vxlan0 vid 10 pvid untagged
|
||||
bridge fdb show brport vxlan0 | grep $zmac | grep self \
|
||||
| grep -q offload
|
||||
busywait "$TIMEOUT" wait_for_offload grep_bridge_fdb $zmac self \
|
||||
bridge fdb show brport vxlan0
|
||||
check_err $? "vxlan tunnel not offloaded after adding vid"
|
||||
|
||||
log_test "vxlan - l3 vni"
|
||||
|
@@ -141,6 +141,16 @@ regions_test()
|
||||
|
||||
check_region_snapshot_count dummy post-first-delete 2
|
||||
|
||||
devlink region new $DL_HANDLE/dummy snapshot 25
|
||||
check_err $? "Failed to create a new snapshot with id 25"
|
||||
|
||||
check_region_snapshot_count dummy post-first-request 3
|
||||
|
||||
devlink region del $DL_HANDLE/dummy snapshot 25
|
||||
check_err $? "Failed to delete snapshot with id 25"
|
||||
|
||||
check_region_snapshot_count dummy post-second-delete 2
|
||||
|
||||
log_test "regions test"
|
||||
}
|
||||
|
||||
@@ -367,6 +377,11 @@ dummy_reporter_test()
|
||||
{
|
||||
RET=0
|
||||
|
||||
check_reporter_info dummy healthy 0 0 0 true
|
||||
|
||||
devlink health set $DL_HANDLE reporter dummy auto_recover false
|
||||
check_err $? "Failed to dummy reporter auto_recover option"
|
||||
|
||||
check_reporter_info dummy healthy 0 0 0 false
|
||||
|
||||
local BREAK_MSG="foo bar"
|
||||
|
@@ -16,6 +16,8 @@ ALL_TESTS="
|
||||
trap_group_action_test
|
||||
bad_trap_group_test
|
||||
trap_group_stats_test
|
||||
trap_policer_test
|
||||
trap_policer_bind_test
|
||||
port_del_test
|
||||
dev_del_test
|
||||
"
|
||||
@@ -23,6 +25,7 @@ NETDEVSIM_PATH=/sys/bus/netdevsim/
|
||||
DEV_ADDR=1337
|
||||
DEV=netdevsim${DEV_ADDR}
|
||||
DEVLINK_DEV=netdevsim/${DEV}
|
||||
DEBUGFS_DIR=/sys/kernel/debug/netdevsim/$DEV/
|
||||
SLEEP_TIME=1
|
||||
NETDEV=""
|
||||
NUM_NETIFS=0
|
||||
@@ -103,6 +106,11 @@ trap_metadata_test()
|
||||
for trap_name in $(devlink_traps_get); do
|
||||
devlink_trap_metadata_test $trap_name "input_port"
|
||||
check_err $? "Input port not reported as metadata of trap $trap_name"
|
||||
if [ $trap_name == "ingress_flow_action_drop" ] ||
|
||||
[ $trap_name == "egress_flow_action_drop" ]; then
|
||||
devlink_trap_metadata_test $trap_name "flow_action_cookie"
|
||||
check_err $? "Flow action cookie not reported as metadata of trap $trap_name"
|
||||
fi
|
||||
done
|
||||
|
||||
log_test "Trap metadata"
|
||||
@@ -251,6 +259,119 @@ trap_group_stats_test()
|
||||
log_test "Trap group statistics"
|
||||
}
|
||||
|
||||
trap_policer_test()
|
||||
{
|
||||
local packets_t0
|
||||
local packets_t1
|
||||
|
||||
if [ $(devlink_trap_policers_num_get) -eq 0 ]; then
|
||||
check_err 1 "Failed to dump policers"
|
||||
fi
|
||||
|
||||
devlink trap policer set $DEVLINK_DEV policer 1337 &> /dev/null
|
||||
check_fail $? "Did not get an error for setting a non-existing policer"
|
||||
devlink trap policer show $DEVLINK_DEV policer 1337 &> /dev/null
|
||||
check_fail $? "Did not get an error for getting a non-existing policer"
|
||||
|
||||
devlink trap policer set $DEVLINK_DEV policer 1 rate 2000 burst 16
|
||||
check_err $? "Failed to set valid parameters for a valid policer"
|
||||
if [ $(devlink_trap_policer_rate_get 1) -ne 2000 ]; then
|
||||
check_err 1 "Policer rate was not changed"
|
||||
fi
|
||||
if [ $(devlink_trap_policer_burst_get 1) -ne 16 ]; then
|
||||
check_err 1 "Policer burst size was not changed"
|
||||
fi
|
||||
|
||||
devlink trap policer set $DEVLINK_DEV policer 1 rate 0 &> /dev/null
|
||||
check_fail $? "Policer rate was changed to rate lower than limit"
|
||||
devlink trap policer set $DEVLINK_DEV policer 1 rate 9000 &> /dev/null
|
||||
check_fail $? "Policer rate was changed to rate higher than limit"
|
||||
devlink trap policer set $DEVLINK_DEV policer 1 burst 2 &> /dev/null
|
||||
check_fail $? "Policer burst size was changed to burst size lower than limit"
|
||||
devlink trap policer set $DEVLINK_DEV policer 1 rate 65537 &> /dev/null
|
||||
check_fail $? "Policer burst size was changed to burst size higher than limit"
|
||||
echo "y" > $DEBUGFS_DIR/fail_trap_policer_set
|
||||
devlink trap policer set $DEVLINK_DEV policer 1 rate 3000 &> /dev/null
|
||||
check_fail $? "Managed to set policer rate when should not"
|
||||
echo "n" > $DEBUGFS_DIR/fail_trap_policer_set
|
||||
if [ $(devlink_trap_policer_rate_get 1) -ne 2000 ]; then
|
||||
check_err 1 "Policer rate was changed to an invalid value"
|
||||
fi
|
||||
if [ $(devlink_trap_policer_burst_get 1) -ne 16 ]; then
|
||||
check_err 1 "Policer burst size was changed to an invalid value"
|
||||
fi
|
||||
|
||||
packets_t0=$(devlink_trap_policer_rx_dropped_get 1)
|
||||
sleep .5
|
||||
packets_t1=$(devlink_trap_policer_rx_dropped_get 1)
|
||||
if [ ! $packets_t1 -gt $packets_t0 ]; then
|
||||
check_err 1 "Policer drop counter was not incremented"
|
||||
fi
|
||||
|
||||
echo "y"> $DEBUGFS_DIR/fail_trap_policer_counter_get
|
||||
devlink -s trap policer show $DEVLINK_DEV policer 1 &> /dev/null
|
||||
check_fail $? "Managed to read policer drop counter when should not"
|
||||
echo "n"> $DEBUGFS_DIR/fail_trap_policer_counter_get
|
||||
devlink -s trap policer show $DEVLINK_DEV policer 1 &> /dev/null
|
||||
check_err $? "Did not manage to read policer drop counter when should"
|
||||
|
||||
log_test "Trap policer"
|
||||
}
|
||||
|
||||
trap_group_check_policer()
|
||||
{
|
||||
local group_name=$1; shift
|
||||
|
||||
devlink -j -p trap group show $DEVLINK_DEV group $group_name \
|
||||
| jq -e '.[][][]["policer"]' &> /dev/null
|
||||
}
|
||||
|
||||
trap_policer_bind_test()
|
||||
{
|
||||
devlink trap group set $DEVLINK_DEV group l2_drops policer 1
|
||||
check_err $? "Failed to bind a valid policer"
|
||||
if [ $(devlink_trap_group_policer_get "l2_drops") -ne 1 ]; then
|
||||
check_err 1 "Bound policer was not changed"
|
||||
fi
|
||||
|
||||
devlink trap group set $DEVLINK_DEV group l2_drops policer 1337 \
|
||||
&> /dev/null
|
||||
check_fail $? "Did not get an error for binding a non-existing policer"
|
||||
if [ $(devlink_trap_group_policer_get "l2_drops") -ne 1 ]; then
|
||||
check_err 1 "Bound policer was changed when should not"
|
||||
fi
|
||||
|
||||
devlink trap group set $DEVLINK_DEV group l2_drops policer 0
|
||||
check_err $? "Failed to unbind a policer when using ID 0"
|
||||
trap_group_check_policer "l2_drops"
|
||||
check_fail $? "Trap group has a policer after unbinding with ID 0"
|
||||
|
||||
devlink trap group set $DEVLINK_DEV group l2_drops policer 1
|
||||
check_err $? "Failed to bind a valid policer"
|
||||
|
||||
devlink trap group set $DEVLINK_DEV group l2_drops nopolicer
|
||||
check_err $? "Failed to unbind a policer when using 'nopolicer' keyword"
|
||||
trap_group_check_policer "l2_drops"
|
||||
check_fail $? "Trap group has a policer after unbinding with 'nopolicer' keyword"
|
||||
|
||||
devlink trap group set $DEVLINK_DEV group l2_drops policer 1
|
||||
check_err $? "Failed to bind a valid policer"
|
||||
|
||||
echo "y"> $DEBUGFS_DIR/fail_trap_group_set
|
||||
devlink trap group set $DEVLINK_DEV group l2_drops policer 2 \
|
||||
&> /dev/null
|
||||
check_fail $? "Managed to bind a policer when should not"
|
||||
echo "n"> $DEBUGFS_DIR/fail_trap_group_set
|
||||
devlink trap group set $DEVLINK_DEV group l2_drops policer 2
|
||||
check_err $? "Did not manage to bind a policer when should"
|
||||
|
||||
devlink trap group set $DEVLINK_DEV group l2_drops action drop \
|
||||
policer 1337 &> /dev/null
|
||||
check_fail $? "Did not get an error for partially modified trap group"
|
||||
|
||||
log_test "Trap policer binding"
|
||||
}
|
||||
|
||||
port_del_test()
|
||||
{
|
||||
local group_name
|
||||
|
5
tools/testing/selftests/net/.gitignore
vendored
5
tools/testing/selftests/net/.gitignore
vendored
@@ -23,3 +23,8 @@ so_txtime
|
||||
tcp_fastopen_backup_key
|
||||
nettest
|
||||
fin_ack_lat
|
||||
reuseaddr_ports_exhausted
|
||||
hwtstamp_config
|
||||
rxtimestamp
|
||||
timestamping
|
||||
txtimestamp
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user