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:
Linus Torvalds
2020-03-31 17:29:33 -07:00
1772 changed files with 113891 additions and 26545 deletions

View File

@@ -3,4 +3,7 @@ gpiogpio-hammer
gpioinclude/
gpiolsgpio
tpm2/SpaceTest.log
tpm2/*.pyc
# Python bytecode and cache
__pycache__/
*.py[cod]

View File

@@ -36,7 +36,6 @@ TARGETS += net
TARGETS += net/forwarding
TARGETS += net/mptcp
TARGETS += netfilter
TARGETS += networking/timestamping
TARGETS += nsfs
TARGETS += pidfd
TARGETS += powerpc

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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();
}

View File

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

View File

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

View File

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

View File

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

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

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

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

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

View 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();
}

View File

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

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

View File

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

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

File diff suppressed because it is too large Load Diff

View File

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

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

View File

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

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

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

View File

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

View File

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

View File

@@ -13,7 +13,7 @@ enum e1 {
enum e2 {
C = 100,
D = -100,
D = 4294967295,
E = 0,
};

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

View File

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

View 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";

View 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";

View File

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

View 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";

View 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";

View File

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

View 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";

View File

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

View File

@@ -7,7 +7,6 @@
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include "bpf_trace_helpers.h"
static struct sockaddr_in old;

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

View File

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

View 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";

View File

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

View 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";

View File

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

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

View 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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View File

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

View File

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

View 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

View File

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

View File

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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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"
}

View 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

View File

@@ -0,0 +1,5 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
QDISC=prio
source sch_red_ets.sh

View 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

View 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

View 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()

View File

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

View File

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

View 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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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