diff options
Diffstat (limited to 'tools/testing')
-rw-r--r-- | tools/testing/selftests/bpf/.gitignore | 2 | ||||
-rw-r--r-- | tools/testing/selftests/bpf/Makefile | 9 | ||||
-rw-r--r-- | tools/testing/selftests/bpf/bpf_helpers.h | 7 | ||||
-rw-r--r-- | tools/testing/selftests/bpf/cgroup_helpers.c | 57 | ||||
-rw-r--r-- | tools/testing/selftests/bpf/cgroup_helpers.h | 1 | ||||
-rw-r--r-- | tools/testing/selftests/bpf/get_cgroup_id_kern.c | 28 | ||||
-rw-r--r-- | tools/testing/selftests/bpf/get_cgroup_id_user.c | 141 | ||||
-rw-r--r-- | tools/testing/selftests/bpf/sendmsg4_prog.c | 49 | ||||
-rw-r--r-- | tools/testing/selftests/bpf/sendmsg6_prog.c | 60 | ||||
-rw-r--r-- | tools/testing/selftests/bpf/test_btf.c | 45 | ||||
-rwxr-xr-x | tools/testing/selftests/bpf/test_lirc_mode2.sh | 28 | ||||
-rw-r--r-- | tools/testing/selftests/bpf/test_lirc_mode2_kern.c | 23 | ||||
-rw-r--r-- | tools/testing/selftests/bpf/test_lirc_mode2_user.c | 149 | ||||
-rw-r--r-- | tools/testing/selftests/bpf/test_sock_addr.c | 1155 | ||||
-rw-r--r-- | tools/testing/selftests/bpf/test_sockmap.c | 87 | ||||
-rw-r--r-- | tools/testing/selftests/bpf/test_verifier.c | 185 |
16 files changed, 1790 insertions, 236 deletions
diff --git a/tools/testing/selftests/bpf/.gitignore b/tools/testing/selftests/bpf/.gitignore index adc8e5474b66..49938d72cf63 100644 --- a/tools/testing/selftests/bpf/.gitignore +++ b/tools/testing/selftests/bpf/.gitignore @@ -17,3 +17,5 @@ test_sock_addr urandom_read test_btf test_sockmap +test_lirc_mode2_user +get_cgroup_id_user diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index 85044448bbc7..607ed8729c06 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -24,7 +24,7 @@ urandom_read: urandom_read.c # Order correspond to 'make run_tests' order TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs \ test_align test_verifier_log test_dev_cgroup test_tcpbpf_user \ - test_sock test_btf test_sockmap + test_sock test_btf test_sockmap test_lirc_mode2_user get_cgroup_id_user TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test_obj_id.o \ test_pkt_md_access.o test_xdp_redirect.o test_xdp_meta.o sockmap_parse_prog.o \ @@ -34,7 +34,8 @@ TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test sockmap_tcp_msg_prog.o connect4_prog.o connect6_prog.o test_adjust_tail.o \ test_btf_haskv.o test_btf_nokv.o test_sockmap_kern.o test_tunnel_kern.o \ test_get_stack_rawtp.o test_sockmap_kern.o test_sockhash_kern.o \ - test_lwt_seg6local.o + test_lwt_seg6local.o sendmsg4_prog.o sendmsg6_prog.o test_lirc_mode2_kern.o \ + get_cgroup_id_kern.o # Order correspond to 'make run_tests' order TEST_PROGS := test_kmod.sh \ @@ -44,7 +45,8 @@ TEST_PROGS := test_kmod.sh \ test_offload.py \ test_sock_addr.sh \ test_tunnel.sh \ - test_lwt_seg6local.sh + test_lwt_seg6local.sh \ + test_lirc_mode2.sh # Compile but not part of 'make run_tests' TEST_GEN_PROGS_EXTENDED = test_libbpf_open test_sock_addr @@ -62,6 +64,7 @@ $(OUTPUT)/test_sock: cgroup_helpers.c $(OUTPUT)/test_sock_addr: cgroup_helpers.c $(OUTPUT)/test_sockmap: cgroup_helpers.c $(OUTPUT)/test_progs: trace_helpers.c +$(OUTPUT)/get_cgroup_id_user: cgroup_helpers.c .PHONY: force diff --git a/tools/testing/selftests/bpf/bpf_helpers.h b/tools/testing/selftests/bpf/bpf_helpers.h index 334d3e8c5e89..f2f28b6c8915 100644 --- a/tools/testing/selftests/bpf/bpf_helpers.h +++ b/tools/testing/selftests/bpf/bpf_helpers.h @@ -126,6 +126,13 @@ static int (*bpf_lwt_seg6_action)(void *ctx, unsigned int action, void *param, static int (*bpf_lwt_seg6_adjust_srh)(void *ctx, unsigned int offset, unsigned int len) = (void *) BPF_FUNC_lwt_seg6_adjust_srh; +static int (*bpf_rc_repeat)(void *ctx) = + (void *) BPF_FUNC_rc_repeat; +static int (*bpf_rc_keydown)(void *ctx, unsigned int protocol, + unsigned long long scancode, unsigned int toggle) = + (void *) BPF_FUNC_rc_keydown; +static unsigned long long (*bpf_get_current_cgroup_id)(void) = + (void *) BPF_FUNC_get_current_cgroup_id; /* llvm builtin functions that eBPF C program may use to * emit BPF_LD_ABS and BPF_LD_IND instructions diff --git a/tools/testing/selftests/bpf/cgroup_helpers.c b/tools/testing/selftests/bpf/cgroup_helpers.c index f3bca3ade0f3..c87b4e052ce9 100644 --- a/tools/testing/selftests/bpf/cgroup_helpers.c +++ b/tools/testing/selftests/bpf/cgroup_helpers.c @@ -6,6 +6,7 @@ #include <sys/types.h> #include <linux/limits.h> #include <stdio.h> +#include <stdlib.h> #include <linux/sched.h> #include <fcntl.h> #include <unistd.h> @@ -176,3 +177,59 @@ int create_and_get_cgroup(char *path) return fd; } + +/** + * get_cgroup_id() - Get cgroup id for a particular cgroup path + * @path: The cgroup path, relative to the workdir, to join + * + * On success, it returns the cgroup id. On failure it returns 0, + * which is an invalid cgroup id. + * If there is a failure, it prints the error to stderr. + */ +unsigned long long get_cgroup_id(char *path) +{ + int dirfd, err, flags, mount_id, fhsize; + union { + unsigned long long cgid; + unsigned char raw_bytes[8]; + } id; + char cgroup_workdir[PATH_MAX + 1]; + struct file_handle *fhp, *fhp2; + unsigned long long ret = 0; + + format_cgroup_path(cgroup_workdir, path); + + dirfd = AT_FDCWD; + flags = 0; + fhsize = sizeof(*fhp); + fhp = calloc(1, fhsize); + if (!fhp) { + log_err("calloc"); + return 0; + } + err = name_to_handle_at(dirfd, cgroup_workdir, fhp, &mount_id, flags); + if (err >= 0 || fhp->handle_bytes != 8) { + log_err("name_to_handle_at"); + goto free_mem; + } + + fhsize = sizeof(struct file_handle) + fhp->handle_bytes; + fhp2 = realloc(fhp, fhsize); + if (!fhp2) { + log_err("realloc"); + goto free_mem; + } + err = name_to_handle_at(dirfd, cgroup_workdir, fhp2, &mount_id, flags); + fhp = fhp2; + if (err < 0) { + log_err("name_to_handle_at"); + goto free_mem; + } + + memcpy(id.raw_bytes, fhp->f_handle, 8); + ret = id.cgid; + +free_mem: + free(fhp); + return ret; +} diff --git a/tools/testing/selftests/bpf/cgroup_helpers.h b/tools/testing/selftests/bpf/cgroup_helpers.h index 06485e0002b3..20a4a5dcd469 100644 --- a/tools/testing/selftests/bpf/cgroup_helpers.h +++ b/tools/testing/selftests/bpf/cgroup_helpers.h @@ -13,5 +13,6 @@ int create_and_get_cgroup(char *path); int join_cgroup(char *path); int setup_cgroup_environment(void); void cleanup_cgroup_environment(void); +unsigned long long get_cgroup_id(char *path); #endif diff --git a/tools/testing/selftests/bpf/get_cgroup_id_kern.c b/tools/testing/selftests/bpf/get_cgroup_id_kern.c new file mode 100644 index 000000000000..2cf8cb23f209 --- /dev/null +++ b/tools/testing/selftests/bpf/get_cgroup_id_kern.c @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018 Facebook + +#include <linux/bpf.h> +#include "bpf_helpers.h" + +struct bpf_map_def SEC("maps") cg_ids = { + .type = BPF_MAP_TYPE_ARRAY, + .key_size = sizeof(__u32), + .value_size = sizeof(__u64), + .max_entries = 1, +}; + +SEC("tracepoint/syscalls/sys_enter_nanosleep") +int trace(void *ctx) +{ + __u32 key = 0; + __u64 *val; + + val = bpf_map_lookup_elem(&cg_ids, &key); + if (val) + *val = bpf_get_current_cgroup_id(); + + return 0; +} + +char _license[] SEC("license") = "GPL"; +__u32 _version SEC("version") = 1; /* ignored by tracepoints, required by libbpf.a */ diff --git a/tools/testing/selftests/bpf/get_cgroup_id_user.c b/tools/testing/selftests/bpf/get_cgroup_id_user.c new file mode 100644 index 000000000000..ea19a42e5894 --- /dev/null +++ b/tools/testing/selftests/bpf/get_cgroup_id_user.c @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018 Facebook + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <fcntl.h> +#include <syscall.h> +#include <unistd.h> +#include <linux/perf_event.h> +#include <sys/ioctl.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <linux/bpf.h> +#include <bpf/bpf.h> +#include <bpf/libbpf.h> + +#include "cgroup_helpers.h" +#include "bpf_rlimit.h" + +#define CHECK(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; \ +}) + +static int bpf_find_map(const char *test, struct bpf_object *obj, + const char *name) +{ + struct bpf_map *map; + + map = bpf_object__find_map_by_name(obj, name); + if (!map) + return -1; + return bpf_map__fd(map); +} + +#define TEST_CGROUP "/test-bpf-get-cgroup-id/" + +int main(int argc, char **argv) +{ + const char *probe_name = "syscalls/sys_enter_nanosleep"; + const char *file = "get_cgroup_id_kern.o"; + int err, bytes, efd, prog_fd, pmu_fd; + struct perf_event_attr attr = {}; + int cgroup_fd, cgidmap_fd; + struct bpf_object *obj; + __u64 kcgid = 0, ucgid; + int exit_code = 1; + char buf[256]; + __u32 key = 0; + + err = setup_cgroup_environment(); + if (CHECK(err, "setup_cgroup_environment", "err %d errno %d\n", err, + errno)) + return 1; + + cgroup_fd = create_and_get_cgroup(TEST_CGROUP); + if (CHECK(cgroup_fd < 0, "create_and_get_cgroup", "err %d errno %d\n", + cgroup_fd, errno)) + goto cleanup_cgroup_env; + + err = join_cgroup(TEST_CGROUP); + if (CHECK(err, "join_cgroup", "err %d errno %d\n", err, errno)) + goto cleanup_cgroup_env; + + err = bpf_prog_load(file, BPF_PROG_TYPE_TRACEPOINT, &obj, &prog_fd); + if (CHECK(err, "bpf_prog_load", "err %d errno %d\n", err, errno)) + goto cleanup_cgroup_env; + + cgidmap_fd = bpf_find_map(__func__, obj, "cg_ids"); + if (CHECK(cgidmap_fd < 0, "bpf_find_map", "err %d errno %d\n", + cgidmap_fd, errno)) + goto close_prog; + + snprintf(buf, sizeof(buf), + "/sys/kernel/debug/tracing/events/%s/id", probe_name); + efd = open(buf, O_RDONLY, 0); + if (CHECK(efd < 0, "open", "err %d errno %d\n", efd, errno)) + goto close_prog; + bytes = read(efd, buf, sizeof(buf)); + close(efd); + if (CHECK(bytes <= 0 || bytes >= sizeof(buf), "read", + "bytes %d errno %d\n", bytes, errno)) + goto close_prog; + + attr.config = strtol(buf, NULL, 0); + attr.type = PERF_TYPE_TRACEPOINT; + attr.sample_type = PERF_SAMPLE_RAW; + attr.sample_period = 1; + attr.wakeup_events = 1; + + /* attach to this pid so the all bpf invocations will be in the + * cgroup associated with this pid. + */ + pmu_fd = syscall(__NR_perf_event_open, &attr, getpid(), -1, -1, 0); + if (CHECK(pmu_fd < 0, "perf_event_open", "err %d errno %d\n", pmu_fd, + errno)) + goto close_prog; + + err = ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0); + if (CHECK(err, "perf_event_ioc_enable", "err %d errno %d\n", err, + errno)) + goto close_pmu; + + err = ioctl(pmu_fd, PERF_EVENT_IOC_SET_BPF, prog_fd); + if (CHECK(err, "perf_event_ioc_set_bpf", "err %d errno %d\n", err, + errno)) + goto close_pmu; + + /* trigger some syscalls */ + sleep(1); + + err = bpf_map_lookup_elem(cgidmap_fd, &key, &kcgid); + if (CHECK(err, "bpf_map_lookup_elem", "err %d errno %d\n", err, errno)) + goto close_pmu; + + ucgid = get_cgroup_id(TEST_CGROUP); + if (CHECK(kcgid != ucgid, "compare_cgroup_id", + "kern cgid %llx user cgid %llx", kcgid, ucgid)) + goto close_pmu; + + exit_code = 0; + printf("%s:PASS\n", argv[0]); + +close_pmu: + close(pmu_fd); +close_prog: + bpf_object__close(obj); +cleanup_cgroup_env: + cleanup_cgroup_environment(); + return exit_code; +} diff --git a/tools/testing/selftests/bpf/sendmsg4_prog.c b/tools/testing/selftests/bpf/sendmsg4_prog.c new file mode 100644 index 000000000000..a91536b1c47e --- /dev/null +++ b/tools/testing/selftests/bpf/sendmsg4_prog.c @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018 Facebook + +#include <linux/stddef.h> +#include <linux/bpf.h> +#include <sys/socket.h> + +#include "bpf_helpers.h" +#include "bpf_endian.h" + +#define SRC1_IP4 0xAC100001U /* 172.16.0.1 */ +#define SRC2_IP4 0x00000000U +#define SRC_REWRITE_IP4 0x7f000004U +#define DST_IP4 0xC0A801FEU /* 192.168.1.254 */ +#define DST_REWRITE_IP4 0x7f000001U +#define DST_PORT 4040 +#define DST_REWRITE_PORT4 4444 + +int _version SEC("version") = 1; + +SEC("cgroup/sendmsg4") +int sendmsg_v4_prog(struct bpf_sock_addr *ctx) +{ + if (ctx->type != SOCK_DGRAM) + return 0; + + /* Rewrite source. */ + if (ctx->msg_src_ip4 == bpf_htonl(SRC1_IP4) || + ctx->msg_src_ip4 == bpf_htonl(SRC2_IP4)) { + ctx->msg_src_ip4 = bpf_htonl(SRC_REWRITE_IP4); + } else { + /* Unexpected source. Reject sendmsg. */ + return 0; + } + + /* Rewrite destination. */ + if ((ctx->user_ip4 >> 24) == (bpf_htonl(DST_IP4) >> 24) && + ctx->user_port == bpf_htons(DST_PORT)) { + ctx->user_ip4 = bpf_htonl(DST_REWRITE_IP4); + ctx->user_port = bpf_htons(DST_REWRITE_PORT4); + } else { + /* Unexpected source. Reject sendmsg. */ + return 0; + } + + return 1; +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/sendmsg6_prog.c b/tools/testing/selftests/bpf/sendmsg6_prog.c new file mode 100644 index 000000000000..5aeaa284fc47 --- /dev/null +++ b/tools/testing/selftests/bpf/sendmsg6_prog.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018 Facebook + +#include <linux/stddef.h> +#include <linux/bpf.h> +#include <sys/socket.h> + +#include "bpf_helpers.h" +#include "bpf_endian.h" + +#define SRC_REWRITE_IP6_0 0 +#define SRC_REWRITE_IP6_1 0 +#define SRC_REWRITE_IP6_2 0 +#define SRC_REWRITE_IP6_3 6 + +#define DST_REWRITE_IP6_0 0 +#define DST_REWRITE_IP6_1 0 +#define DST_REWRITE_IP6_2 0 +#define DST_REWRITE_IP6_3 1 + +#define DST_REWRITE_PORT6 6666 + +int _version SEC("version") = 1; + +SEC("cgroup/sendmsg6") +int sendmsg_v6_prog(struct bpf_sock_addr *ctx) +{ + if (ctx->type != SOCK_DGRAM) + return 0; + + /* Rewrite source. */ + if (ctx->msg_src_ip6[3] == bpf_htonl(1) || + ctx->msg_src_ip6[3] == bpf_htonl(0)) { + ctx->msg_src_ip6[0] = bpf_htonl(SRC_REWRITE_IP6_0); + ctx->msg_src_ip6[1] = bpf_htonl(SRC_REWRITE_IP6_1); + ctx->msg_src_ip6[2] = bpf_htonl(SRC_REWRITE_IP6_2); + ctx->msg_src_ip6[3] = bpf_htonl(SRC_REWRITE_IP6_3); + } else { + /* Unexpected source. Reject sendmsg. */ + return 0; + } + + /* Rewrite destination. */ + if ((ctx->user_ip6[0] & 0xFFFF) == bpf_htons(0xFACE) && + ctx->user_ip6[0] >> 16 == bpf_htons(0xB00C)) { + ctx->user_ip6[0] = bpf_htonl(DST_REWRITE_IP6_0); + ctx->user_ip6[1] = bpf_htonl(DST_REWRITE_IP6_1); + ctx->user_ip6[2] = bpf_htonl(DST_REWRITE_IP6_2); + ctx->user_ip6[3] = bpf_htonl(DST_REWRITE_IP6_3); + + ctx->user_port = bpf_htons(DST_REWRITE_PORT6); + } else { + /* Unexpected destination. Reject sendmsg. */ + return 0; + } + + return 1; +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/test_btf.c b/tools/testing/selftests/bpf/test_btf.c index 35064df688c1..3619f3023088 100644 --- a/tools/testing/selftests/bpf/test_btf.c +++ b/tools/testing/selftests/bpf/test_btf.c @@ -1179,6 +1179,29 @@ static struct btf_raw_test raw_tests[] = { }, { + .descr = "array test. t->size != 0\"", + .raw_types = { + /* int */ /* [1] */ + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), + /* int[16] */ /* [2] */ + BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_ARRAY, 0, 0), 1), + BTF_ARRAY_ENC(1, 1, 16), + BTF_END_RAW, + }, + .str_sec = "", + .str_sec_size = sizeof(""), + .map_type = BPF_MAP_TYPE_ARRAY, + .map_name = "array_test_map", + .key_size = sizeof(int), + .value_size = sizeof(int), + .key_type_id = 1, + .value_type_id = 1, + .max_entries = 4, + .btf_load_err = true, + .err_str = "size != 0", +}, + +{ .descr = "int test. invalid int_data", .raw_types = { BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_INT, 0, 0), 4), @@ -1219,6 +1242,28 @@ static struct btf_raw_test raw_tests[] = { .err_str = "Invalid btf_info", }, +{ + .descr = "fwd test. t->type != 0\"", + .raw_types = { + /* int */ /* [1] */ + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), + /* fwd type */ /* [2] */ + BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_FWD, 0, 0), 1), + BTF_END_RAW, + }, + .str_sec = "", + .str_sec_size = sizeof(""), + .map_type = BPF_MAP_TYPE_ARRAY, + .map_name = "fwd_test_map", + .key_size = sizeof(int), + .value_size = sizeof(int), + .key_type_id = 1, + .value_type_id = 1, + .max_entries = 4, + .btf_load_err = true, + .err_str = "type != 0", +}, + }; /* struct btf_raw_test raw_tests[] */ static const char *get_next_str(const char *start, const char *end) diff --git a/tools/testing/selftests/bpf/test_lirc_mode2.sh b/tools/testing/selftests/bpf/test_lirc_mode2.sh new file mode 100755 index 000000000000..ce2e15e4f976 --- /dev/null +++ b/tools/testing/selftests/bpf/test_lirc_mode2.sh @@ -0,0 +1,28 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +GREEN='\033[0;92m' +RED='\033[0;31m' +NC='\033[0m' # No Color + +modprobe rc-loopback + +for i in /sys/class/rc/rc* +do + if grep -q DRV_NAME=rc-loopback $i/uevent + then + LIRCDEV=$(grep DEVNAME= $i/lirc*/uevent | sed sQDEVNAME=Q/dev/Q) + fi +done + +if [ -n $LIRCDEV ]; +then + TYPE=lirc_mode2 + ./test_lirc_mode2_user $LIRCDEV + ret=$? + if [ $ret -ne 0 ]; then + echo -e ${RED}"FAIL: $TYPE"${NC} + else + echo -e ${GREEN}"PASS: $TYPE"${NC} + fi +fi diff --git a/tools/testing/selftests/bpf/test_lirc_mode2_kern.c b/tools/testing/selftests/bpf/test_lirc_mode2_kern.c new file mode 100644 index 000000000000..ba26855563a5 --- /dev/null +++ b/tools/testing/selftests/bpf/test_lirc_mode2_kern.c @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0 +// test ir decoder +// +// Copyright (C) 2018 Sean Young <sean@mess.org> + +#include <linux/bpf.h> +#include <linux/lirc.h> +#include "bpf_helpers.h" + +SEC("lirc_mode2") +int bpf_decoder(unsigned int *sample) +{ + if (LIRC_IS_PULSE(*sample)) { + unsigned int duration = LIRC_VALUE(*sample); + + if (duration & 0x10000) + bpf_rc_keydown(sample, 0x40, duration & 0xffff, 0); + } + + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/test_lirc_mode2_user.c b/tools/testing/selftests/bpf/test_lirc_mode2_user.c new file mode 100644 index 000000000000..d470d63c33db --- /dev/null +++ b/tools/testing/selftests/bpf/test_lirc_mode2_user.c @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-2.0 +// test ir decoder +// +// Copyright (C) 2018 Sean Young <sean@mess.org> + +// A lirc chardev is a device representing a consumer IR (cir) device which +// can receive infrared signals from remote control and/or transmit IR. +// +// IR is sent as a series of pulses and space somewhat like morse code. The +// BPF program can decode this into scancodes so that rc-core can translate +// this into input key codes using the rc keymap. +// +// This test works by sending IR over rc-loopback, so the IR is processed by +// BPF and then decoded into scancodes. The lirc chardev must be the one +// associated with rc-loopback, see the output of ir-keytable(1). +// +// The following CONFIG options must be enabled for the test to succeed: +// CONFIG_RC_CORE=y +// CONFIG_BPF_RAWIR_EVENT=y +// CONFIG_RC_LOOPBACK=y + +// Steps: +// 1. Open the /dev/lircN device for rc-loopback (given on command line) +// 2. Attach bpf_lirc_mode2 program which decodes some IR. +// 3. Send some IR to the same IR device; since it is loopback, this will +// end up in the bpf program +// 4. bpf program should decode IR and report keycode +// 5. We can read keycode from same /dev/lirc device + +#include <linux/bpf.h> +#include <linux/lirc.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <poll.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "bpf_util.h" +#include <bpf/bpf.h> +#include <bpf/libbpf.h> + +int main(int argc, char **argv) +{ + struct bpf_object *obj; + int ret, lircfd, progfd, mode; + int testir = 0x1dead; + u32 prog_ids[10], prog_flags[10], prog_cnt; + + if (argc != 2) { + printf("Usage: %s /dev/lircN\n", argv[0]); + return 2; + } + + ret = bpf_prog_load("test_lirc_mode2_kern.o", + BPF_PROG_TYPE_LIRC_MODE2, &obj, &progfd); + if (ret) { + printf("Failed to load bpf program\n"); + return 1; + } + + lircfd = open(argv[1], O_RDWR | O_NONBLOCK); + if (lircfd == -1) { + printf("failed to open lirc device %s: %m\n", argv[1]); + return 1; + } + + /* Let's try detach it before it was ever attached */ + ret = bpf_prog_detach2(progfd, lircfd, BPF_LIRC_MODE2); + if (ret != -1 || errno != ENOENT) { + printf("bpf_prog_detach2 not attached should fail: %m\n"); + return 1; + } + + mode = LIRC_MODE_SCANCODE; + if (ioctl(lircfd, LIRC_SET_REC_MODE, &mode)) { + printf("failed to set rec mode: %m\n"); + return 1; + } + + prog_cnt = 10; + ret = bpf_prog_query(lircfd, BPF_LIRC_MODE2, 0, prog_flags, prog_ids, + &prog_cnt); + if (ret) { + printf("Failed to query bpf programs on lirc device: %m\n"); + return 1; + } + + if (prog_cnt != 0) { + printf("Expected nothing to be attached\n"); + return 1; + } + + ret = bpf_prog_attach(progfd, lircfd, BPF_LIRC_MODE2, 0); + if (ret) { + printf("Failed to attach bpf to lirc device: %m\n"); + return 1; + } + + /* Write raw IR */ + ret = write(lircfd, &testir, sizeof(testir)); + if (ret != sizeof(testir)) { + printf("Failed to send test IR message: %m\n"); + return 1; + } + + struct pollfd pfd = { .fd = lircfd, .events = POLLIN }; + struct lirc_scancode lsc; + + poll(&pfd, 1, 100); + + /* Read decoded IR */ + ret = read(lircfd, &lsc, sizeof(lsc)); + if (ret != sizeof(lsc)) { + printf("Failed to read decoded IR: %m\n"); + return 1; + } + + if (lsc.scancode != 0xdead || lsc.rc_proto != 64) { + printf("Incorrect scancode decoded\n"); + return 1; + } + + prog_cnt = 10; + ret = bpf_prog_query(lircfd, BPF_LIRC_MODE2, 0, prog_flags, prog_ids, + &prog_cnt); + if (ret) { + printf("Failed to query bpf programs on lirc device: %m\n"); + return 1; + } + + if (prog_cnt != 1) { + printf("Expected one program to be attached\n"); + return 1; + } + + /* Let's try detaching it now it is actually attached */ + ret = bpf_prog_detach2(progfd, lircfd, BPF_LIRC_MODE2); + if (ret) { + printf("bpf_prog_detach2: returned %m\n"); + return 1; + } + + return 0; +} diff --git a/tools/testing/selftests/bpf/test_sock_addr.c b/tools/testing/selftests/bpf/test_sock_addr.c index 2950f80ba7fb..a5e76b9219b9 100644 --- a/tools/testing/selftests/bpf/test_sock_addr.c +++ b/tools/testing/selftests/bpf/test_sock_addr.c @@ -1,12 +1,16 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2018 Facebook +#define _GNU_SOURCE + #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <arpa/inet.h> +#include <netinet/in.h> #include <sys/types.h> +#include <sys/select.h> #include <sys/socket.h> #include <linux/filter.h> @@ -17,34 +21,465 @@ #include "cgroup_helpers.h" #include "bpf_rlimit.h" +#ifndef ENOTSUPP +# define ENOTSUPP 524 +#endif + +#ifndef ARRAY_SIZE +# define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#endif + #define CG_PATH "/foo" #define CONNECT4_PROG_PATH "./connect4_prog.o" #define CONNECT6_PROG_PATH "./connect6_prog.o" +#define SENDMSG4_PROG_PATH "./sendmsg4_prog.o" +#define SENDMSG6_PROG_PATH "./sendmsg6_prog.o" #define SERV4_IP "192.168.1.254" #define SERV4_REWRITE_IP "127.0.0.1" +#define SRC4_IP "172.16.0.1" +#define SRC4_REWRITE_IP "127.0.0.4" #define SERV4_PORT 4040 #define SERV4_REWRITE_PORT 4444 #define SERV6_IP "face:b00c:1234:5678::abcd" #define SERV6_REWRITE_IP "::1" +#define SERV6_V4MAPPED_IP "::ffff:192.168.0.4" +#define SRC6_IP "::1" +#define SRC6_REWRITE_IP "::6" #define SERV6_PORT 6060 #define SERV6_REWRITE_PORT 6666 #define INET_NTOP_BUF 40 -typedef int (*load_fn)(enum bpf_attach_type, const char *comment); +struct sock_addr_test; + +typedef int (*load_fn)(const struct sock_addr_test *test); typedef int (*info_fn)(int, struct sockaddr *, socklen_t *); -struct program { - enum bpf_attach_type type; - load_fn loadfn; - int fd; - const char *name; - enum bpf_attach_type invalid_type; +char bpf_log_buf[BPF_LOG_BUF_SIZE]; + +struct sock_addr_test { + const char *descr; + /* BPF prog properties */ + load_fn loadfn; + enum bpf_attach_type expected_attach_type; + enum bpf_attach_type attach_type; + /* Socket properties */ + int domain; + int type; + /* IP:port pairs for BPF prog to override */ + const char *requested_ip; + unsigned short requested_port; + const char *expected_ip; + unsigned short expected_port; + const char *expected_src_ip; + /* Expected test result */ + enum { + LOAD_REJECT, + ATTACH_REJECT, + SYSCALL_EPERM, + SYSCALL_ENOTSUPP, + SUCCESS, + } expected_result; }; -char bpf_log_buf[BPF_LOG_BUF_SIZE]; +static int bind4_prog_load(const struct sock_addr_test *test); +static int bind6_prog_load(const struct sock_addr_test *test); +static int connect4_prog_load(const struct sock_addr_test *test); +static int connect6_prog_load(const struct sock_addr_test *test); +static int sendmsg_deny_prog_load(const struct sock_addr_test *test); +static int sendmsg4_rw_asm_prog_load(const struct sock_addr_test *test); +static int sendmsg4_rw_c_prog_load(const struct sock_addr_test *test); +static int sendmsg6_rw_asm_prog_load(const struct sock_addr_test *test); +static int sendmsg6_rw_c_prog_load(const struct sock_addr_test *test); +static int sendmsg6_rw_v4mapped_prog_load(const struct sock_addr_test *test); + +static struct sock_addr_test tests[] = { + /* bind */ + { + "bind4: load prog with wrong expected attach type", + bind4_prog_load, + BPF_CGROUP_INET6_BIND, + BPF_CGROUP_INET4_BIND, + AF_INET, + SOCK_STREAM, + NULL, + 0, + NULL, + 0, + NULL, + LOAD_REJECT, + }, + { + "bind4: attach prog with wrong attach type", + bind4_prog_load, + BPF_CGROUP_INET4_BIND, + BPF_CGROUP_INET6_BIND, + AF_INET, + SOCK_STREAM, + NULL, + 0, + NULL, + 0, + NULL, + ATTACH_REJECT, + }, + { + "bind4: rewrite IP & TCP port in", + bind4_prog_load, + BPF_CGROUP_INET4_BIND, + BPF_CGROUP_INET4_BIND, + AF_INET, + SOCK_STREAM, + SERV4_IP, + SERV4_PORT, + SERV4_REWRITE_IP, + SERV4_REWRITE_PORT, + NULL, + SUCCESS, + }, + { + "bind4: rewrite IP & UDP port in", + bind4_prog_load, + BPF_CGROUP_INET4_BIND, + BPF_CGROUP_INET4_BIND, + AF_INET, + SOCK_DGRAM, + SERV4_IP, + SERV4_PORT, + SERV4_REWRITE_IP, + SERV4_REWRITE_PORT, + NULL, + SUCCESS, + }, + { + "bind6: load prog with wrong expected attach type", + bind6_prog_load, + BPF_CGROUP_INET4_BIND, + BPF_CGROUP_INET6_BIND, + AF_INET6, + SOCK_STREAM, + NULL, + 0, + NULL, + 0, + NULL, + LOAD_REJECT, + }, + { + "bind6: attach prog with wrong attach type", + bind6_prog_load, + BPF_CGROUP_INET6_BIND, + BPF_CGROUP_INET4_BIND, + AF_INET, + SOCK_STREAM, + NULL, + 0, + NULL, + 0, + NULL, + ATTACH_REJECT, + }, + { + "bind6: rewrite IP & TCP port in", + bind6_prog_load, + BPF_CGROUP_INET6_BIND, + BPF_CGROUP_INET6_BIND, + AF_INET6, + SOCK_STREAM, + SERV6_IP, + SERV6_PORT, + SERV6_REWRITE_IP, + SERV6_REWRITE_PORT, + NULL, + SUCCESS, + }, + { + "bind6: rewrite IP & UDP port in", + bind6_prog_load, + BPF_CGROUP_INET6_BIND, + BPF_CGROUP_INET6_BIND, + AF_INET6, + SOCK_DGRAM, + SERV6_IP, + SERV6_PORT, + SERV6_REWRITE_IP, + SERV6_REWRITE_PORT, + NULL, + SUCCESS, + }, + + /* connect */ + { + "connect4: load prog with wrong expected attach type", + connect4_prog_load, + BPF_CGROUP_INET6_CONNECT, + BPF_CGROUP_INET4_CONNECT, + AF_INET, + SOCK_STREAM, + NULL, + 0, + NULL, + 0, + NULL, + LOAD_REJECT, + }, + { + "connect4: attach prog with wrong attach type", + connect4_prog_load, + BPF_CGROUP_INET4_CONNECT, + BPF_CGROUP_INET6_CONNECT, + AF_INET, + SOCK_STREAM, + NULL, + 0, + NULL, + 0, + NULL, + ATTACH_REJECT, + }, + { + "connect4: rewrite IP & TCP port", + connect4_prog_load, + BPF_CGROUP_INET4_CONNECT, + BPF_CGROUP_INET4_CONNECT, + AF_INET, + SOCK_STREAM, + SERV4_IP, + SERV4_PORT, + SERV4_REWRITE_IP, + SERV4_REWRITE_PORT, + SRC4_REWRITE_IP, + SUCCESS, + }, + { + "connect4: rewrite IP & UDP port", + connect4_prog_load, + BPF_CGROUP_INET4_CONNECT, + BPF_CGROUP_INET4_CONNECT, + AF_INET, + SOCK_DGRAM, + SERV4_IP, + SERV4_PORT, + SERV4_REWRITE_IP, + SERV4_REWRITE_PORT, + SRC4_REWRITE_IP, + SUCCESS, + }, + { + "connect6: load prog with wrong expected attach type", + connect6_prog_load, + BPF_CGROUP_INET4_CONNECT, + BPF_CGROUP_INET6_CONNECT, + AF_INET6, + SOCK_STREAM, + NULL, + 0, + NULL, + 0, + NULL, + LOAD_REJECT, + }, + { + "connect6: attach prog with wrong attach type", + connect6_prog_load, + BPF_CGROUP_INET6_CONNECT, + BPF_CGROUP_INET4_CONNECT, + AF_INET, + SOCK_STREAM, + NULL, + 0, + NULL, + 0, + NULL, + ATTACH_REJECT, + }, + { + "connect6: rewrite IP & TCP port", + connect6_prog_load, + BPF_CGROUP_INET6_CONNECT, + BPF_CGROUP_INET6_CONNECT, + AF_INET6, + SOCK_STREAM, + SERV6_IP, + SERV6_PORT, + SERV6_REWRITE_IP, + SERV6_REWRITE_PORT, + SRC6_REWRITE_IP, + SUCCESS, + }, + { + "connect6: rewrite IP & UDP port", + connect6_prog_load, + BPF_CGROUP_INET6_CONNECT, + BPF_CGROUP_INET6_CONNECT, + AF_INET6, + SOCK_DGRAM, + SERV6_IP, + SERV6_PORT, + SERV6_REWRITE_IP, + SERV6_REWRITE_PORT, + SRC6_REWRITE_IP, + SUCCESS, + }, + + /* sendmsg */ + { + "sendmsg4: load prog with wrong expected attach type", + sendmsg4_rw_asm_prog_load, + BPF_CGROUP_UDP6_SENDMSG, + BPF_CGROUP_UDP4_SENDMSG, + AF_INET, + SOCK_DGRAM, + NULL, + 0, + NULL, + 0, + NULL, + LOAD_REJECT, + }, + { + "sendmsg4: attach prog with wrong attach type", + sendmsg4_rw_asm_prog_load, + BPF_CGROUP_UDP4_SENDMSG, + BPF_CGROUP_UDP6_SENDMSG, + AF_INET, + SOCK_DGRAM, + NULL, + 0, + NULL, + 0, + NULL, + ATTACH_REJECT, + }, + { + "sendmsg4: rewrite IP & port (asm)", + sendmsg4_rw_asm_prog_load, + BPF_CGROUP_UDP4_SENDMSG, + BPF_CGROUP_UDP4_SENDMSG, + AF_INET, + SOCK_DGRAM, + SERV4_IP, + SERV4_PORT, + SERV4_REWRITE_IP, + SERV4_REWRITE_PORT, + SRC4_REWRITE_IP, + SUCCESS, + }, + { + "sendmsg4: rewrite IP & port (C)", + sendmsg4_rw_c_prog_load, + BPF_CGROUP_UDP4_SENDMSG, + BPF_CGROUP_UDP4_SENDMSG, + AF_INET, + SOCK_DGRAM, + SERV4_IP, + SERV4_PORT, + SERV4_REWRITE_IP, + SERV4_REWRITE_PORT, + SRC4_REWRITE_IP, + SUCCESS, + }, + { + "sendmsg4: deny call", + sendmsg_deny_prog_load, + BPF_CGROUP_UDP4_SENDMSG, + BPF_CGROUP_UDP4_SENDMSG, + AF_INET, + SOCK_DGRAM, + SERV4_IP, + SERV4_PORT, + SERV4_REWRITE_IP, + SERV4_REWRITE_PORT, + SRC4_REWRITE_IP, + SYSCALL_EPERM, + }, + { + "sendmsg6: load prog with wrong expected attach type", + sendmsg6_rw_asm_prog_load, + BPF_CGROUP_UDP4_SENDMSG, + BPF_CGROUP_UDP6_SENDMSG, + AF_INET6, + SOCK_DGRAM, + NULL, + 0, + NULL, + 0, + NULL, + LOAD_REJECT, + }, + { + "sendmsg6: attach prog with wrong attach type", + sendmsg6_rw_asm_prog_load, + BPF_CGROUP_UDP6_SENDMSG, + BPF_CGROUP_UDP4_SENDMSG, + AF_INET6, + SOCK_DGRAM, + NULL, + 0, + NULL, + 0, + NULL, + ATTACH_REJECT, + }, + { + "sendmsg6: rewrite IP & port (asm)", + sendmsg6_rw_asm_prog_load, + BPF_CGROUP_UDP6_SENDMSG, + BPF_CGROUP_UDP6_SENDMSG, + AF_INET6, + SOCK_DGRAM, + SERV6_IP, + SERV6_PORT, + SERV6_REWRITE_IP, + SERV6_REWRITE_PORT, + SRC6_REWRITE_IP, + SUCCESS, + }, + { + "sendmsg6: rewrite IP & port (C)", + sendmsg6_rw_c_prog_load, + BPF_CGROUP_UDP6_SENDMSG, + BPF_CGROUP_UDP6_SENDMSG, + AF_INET6, + SOCK_DGRAM, + SERV6_IP, + SERV6_PORT, + SERV6_REWRITE_IP, + SERV6_REWRITE_PORT, + SRC6_REWRITE_IP, + SUCCESS, + }, + { + "sendmsg6: IPv4-mapped IPv6", + sendmsg6_rw_v4mapped_prog_load, + BPF_CGROUP_UDP6_SENDMSG, + BPF_CGROUP_UDP6_SENDMSG, + AF_INET6, + SOCK_DGRAM, + SERV6_IP, + SERV6_PORT, + SERV6_REWRITE_IP, + SERV6_REWRITE_PORT, + SRC6_REWRITE_IP, + SYSCALL_ENOTSUPP, + }, + { + "sendmsg6: deny call", + sendmsg_deny_prog_load, + BPF_CGROUP_UDP6_SENDMSG, + BPF_CGROUP_UDP6_SENDMSG, + AF_INET6, + SOCK_DGRAM, + SERV6_IP, + SERV6_PORT, + SERV6_REWRITE_IP, + SERV6_REWRITE_PORT, + SRC6_REWRITE_IP, + SYSCALL_EPERM, + }, +}; static int mk_sockaddr(int domain, const char *ip, unsigned short port, struct sockaddr *addr, socklen_t addr_len) @@ -84,25 +519,23 @@ static int mk_sockaddr(int domain, const char *ip, unsigned short port, return 0; } -static int load_insns(enum bpf_attach_type attach_type, - const struct bpf_insn *insns, size_t insns_cnt, - const char *comment) +static int load_insns(const struct sock_addr_test *test, + const struct bpf_insn *insns, size_t insns_cnt) { struct bpf_load_program_attr load_attr; int ret; memset(&load_attr, 0, sizeof(struct bpf_load_program_attr)); load_attr.prog_type = BPF_PROG_TYPE_CGROUP_SOCK_ADDR; - load_attr.expected_attach_type = attach_type; + load_attr.expected_attach_type = test->expected_attach_type; load_attr.insns = insns; load_attr.insns_cnt = insns_cnt; load_attr.license = "GPL"; ret = bpf_load_program_xattr(&load_attr, bpf_log_buf, BPF_LOG_BUF_SIZE); - if (ret < 0 && comment) { - log_err(">>> Loading %s program error.\n" - ">>> Output from verifier:\n%s\n-------\n", - comment, bpf_log_buf); + if (ret < 0 && test->expected_result != LOAD_REJECT) { + log_err(">>> Loading program error.\n" + ">>> Verifier output:\n%s\n-------\n", bpf_log_buf); } return ret; @@ -119,8 +552,7 @@ static int load_insns(enum bpf_attach_type attach_type, * to count jumps properly. */ -static int bind4_prog_load(enum bpf_attach_type attach_type, - const char *comment) +static int bind4_prog_load(const struct sock_addr_test *test) { union { uint8_t u4_addr8[4]; @@ -186,12 +618,10 @@ static int bind4_prog_load(enum bpf_attach_type attach_type, BPF_EXIT_INSN(), }; - return load_insns(attach_type, insns, - sizeof(insns) / sizeof(struct bpf_insn), comment); + return load_insns(test, insns, sizeof(insns) / sizeof(struct bpf_insn)); } -static int bind6_prog_load(enum bpf_attach_type attach_type, - const char *comment) +static int bind6_prog_load(const struct sock_addr_test *test) { struct sockaddr_in6 addr6_rw; struct in6_addr ip6; @@ -254,13 +684,10 @@ static int bind6_prog_load(enum bpf_attach_type attach_type, BPF_EXIT_INSN(), }; - return load_insns(attach_type, insns, - sizeof(insns) / sizeof(struct bpf_insn), comment); + return load_insns(test, insns, sizeof(insns) / sizeof(struct bpf_insn)); } -static int connect_prog_load_path(const char *path, - enum bpf_attach_type attach_type, - const char *comment) +static int load_path(const struct sock_addr_test *test, const char *path) { struct bpf_prog_load_attr attr; struct bpf_object *obj; @@ -269,75 +696,218 @@ static int connect_prog_load_path(const char *path, memset(&attr, 0, sizeof(struct bpf_prog_load_attr)); attr.file = path; attr.prog_type = BPF_PROG_TYPE_CGROUP_SOCK_ADDR; - attr.expected_attach_type = attach_type; + attr.expected_attach_type = test->expected_attach_type; if (bpf_prog_load_xattr(&attr, &obj, &prog_fd)) { - if (comment) - log_err(">>> Loading %s program at %s error.\n", - comment, path); + if (test->expected_result != LOAD_REJECT) + log_err(">>> Loading program (%s) error.\n", path); return -1; } return prog_fd; } -static int connect4_prog_load(enum bpf_attach_type attach_type, - const char *comment) +static int connect4_prog_load(const struct sock_addr_test *test) { - return connect_prog_load_path(CONNECT4_PROG_PATH, attach_type, comment); + return load_path(test, CONNECT4_PROG_PATH); } -static int connect6_prog_load(enum bpf_attach_type attach_type, - const char *comment) +static int connect6_prog_load(const struct sock_addr_test *test) { - return connect_prog_load_path(CONNECT6_PROG_PATH, attach_type, comment); + return load_path(test, CONNECT6_PROG_PATH); } -static void print_ip_port(int sockfd, info_fn fn, const char *fmt) +static int sendmsg_deny_prog_load(const struct sock_addr_test *test) { - char addr_buf[INET_NTOP_BUF]; - struct sockaddr_storage addr; - struct sockaddr_in6 *addr6; - struct sockaddr_in *addr4; - socklen_t addr_len; - unsigned short port; - void *nip; - - addr_len = sizeof(struct sockaddr_storage); - memset(&addr, 0, addr_len); - - if (fn(sockfd, (struct sockaddr *)&addr, (socklen_t *)&addr_len) == 0) { - if (addr.ss_family == AF_INET) { - addr4 = (struct sockaddr_in *)&addr; - nip = (void *)&addr4->sin_addr; - port = ntohs(addr4->sin_port); - } else if (addr.ss_family == AF_INET6) { - addr6 = (struct sockaddr_in6 *)&addr; - nip = (void *)&addr6->sin6_addr; - port = ntohs(addr6->sin6_port); - } else { - return; - } - const char *addr_str = - inet_ntop(addr.ss_family, nip, addr_buf, INET_NTOP_BUF); - printf(fmt, addr_str ? addr_str : "??", port); + struct bpf_insn insns[] = { + /* return 0 */ + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }; + return load_insns(test, insns, sizeof(insns) / sizeof(struct bpf_insn)); +} + +static int sendmsg4_rw_asm_prog_load(const struct sock_addr_test *test) +{ + struct sockaddr_in dst4_rw_addr; + struct in_addr src4_rw_ip; + + if (inet_pton(AF_INET, SRC4_REWRITE_IP, (void *)&src4_rw_ip) != 1) { + log_err("Invalid IPv4: %s", SRC4_REWRITE_IP); + return -1; + } + + if (mk_sockaddr(AF_INET, SERV4_REWRITE_IP, SERV4_REWRITE_PORT, + (struct sockaddr *)&dst4_rw_addr, + sizeof(dst4_rw_addr)) == -1) + return -1; + + struct bpf_insn insns[] = { + BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), + + /* if (sk.family == AF_INET && */ + BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6, + offsetof(struct bpf_sock_addr, family)), + BPF_JMP_IMM(BPF_JNE, BPF_REG_7, AF_INET, 8), + + /* sk.type == SOCK_DGRAM) { */ + BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6, + offsetof(struct bpf_sock_addr, type)), + BPF_JMP_IMM(BPF_JNE, BPF_REG_7, SOCK_DGRAM, 6), + + /* msg_src_ip4 = src4_rw_ip */ + BPF_MOV32_IMM(BPF_REG_7, src4_rw_ip.s_addr), + BPF_STX_MEM(BPF_W, BPF_REG_6, BPF_REG_7, + offsetof(struct bpf_sock_addr, msg_src_ip4)), + + /* user_ip4 = dst4_rw_addr.sin_addr */ + BPF_MOV32_IMM(BPF_REG_7, dst4_rw_addr.sin_addr.s_addr), + BPF_STX_MEM(BPF_W, BPF_REG_6, BPF_REG_7, + offsetof(struct bpf_sock_addr, user_ip4)), + + /* user_port = dst4_rw_addr.sin_port */ + BPF_MOV32_IMM(BPF_REG_7, dst4_rw_addr.sin_port), + BPF_STX_MEM(BPF_W, BPF_REG_6, BPF_REG_7, + offsetof(struct bpf_sock_addr, user_port)), + /* } */ + + /* return 1 */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + }; + + return load_insns(test, insns, sizeof(insns) / sizeof(struct bpf_insn)); +} + +static int sendmsg4_rw_c_prog_load(const struct sock_addr_test *test) +{ + return load_path(test, SENDMSG4_PROG_PATH); +} + +static int sendmsg6_rw_dst_asm_prog_load(const struct sock_addr_test *test, + const char *rw_dst_ip) +{ + struct sockaddr_in6 dst6_rw_addr; + struct in6_addr src6_rw_ip; + + if (inet_pton(AF_INET6, SRC6_REWRITE_IP, (void *)&src6_rw_ip) != 1) { + log_err("Invalid IPv6: %s", SRC6_REWRITE_IP); + return -1; + } + + if (mk_sockaddr(AF_INET6, rw_dst_ip, SERV6_REWRITE_PORT, + (struct sockaddr *)&dst6_rw_addr, + sizeof(dst6_rw_addr)) == -1) + return -1; + + struct bpf_insn insns[] = { + BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), + + /* if (sk.family == AF_INET6) { */ + BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6, + offsetof(struct bpf_sock_addr, family)), + BPF_JMP_IMM(BPF_JNE, BPF_REG_7, AF_INET6, 18), + +#define STORE_IPV6_WORD_N(DST, SRC, N) \ + BPF_MOV32_IMM(BPF_REG_7, SRC[N]), \ + BPF_STX_MEM(BPF_W, BPF_REG_6, BPF_REG_7, \ + offsetof(struct bpf_sock_addr, DST[N])) + +#define STORE_IPV6(DST, SRC) \ + STORE_IPV6_WORD_N(DST, SRC, 0), \ + STORE_IPV6_WORD_N(DST, SRC, 1), \ + STORE_IPV6_WORD_N(DST, SRC, 2), \ + STORE_IPV6_WORD_N(DST, SRC, 3) + + STORE_IPV6(msg_src_ip6, src6_rw_ip.s6_addr32), + STORE_IPV6(user_ip6, dst6_rw_addr.sin6_addr.s6_addr32), + + /* user_port = dst6_rw_addr.sin6_port */ + BPF_MOV32_IMM(BPF_REG_7, dst6_rw_addr.sin6_port), + BPF_STX_MEM(BPF_W, BPF_REG_6, BPF_REG_7, + offsetof(struct bpf_sock_addr, user_port)), + + /* } */ + + /* return 1 */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + }; + + return load_insns(test, insns, sizeof(insns) / sizeof(struct bpf_insn)); +} + +static int sendmsg6_rw_asm_prog_load(const struct sock_addr_test *test) +{ + return sendmsg6_rw_dst_asm_prog_load(test, SERV6_REWRITE_IP); +} + +static int sendmsg6_rw_v4mapped_prog_load(const struct sock_addr_test *test) +{ + return sendmsg6_rw_dst_asm_prog_load(test, SERV6_V4MAPPED_IP); +} + +static int sendmsg6_rw_c_prog_load(const struct sock_addr_test *test) +{ + return load_path(test, SENDMSG6_PROG_PATH); +} + +static int cmp_addr(const struct sockaddr_storage *addr1, + const struct sockaddr_storage *addr2, int cmp_port) +{ + const struct sockaddr_in *four1, *four2; + const struct sockaddr_in6 *six1, *six2; + + if (addr1->ss_family != addr2->ss_family) + return -1; + + if (addr1->ss_family == AF_INET) { + four1 = (const struct sockaddr_in *)addr1; + four2 = (const struct sockaddr_in *)addr2; + return !((four1->sin_port == four2->sin_port || !cmp_port) && + four1->sin_addr.s_addr == four2->sin_addr.s_addr); + } else if (addr1->ss_family == AF_INET6) { + six1 = (const struct sockaddr_in6 *)addr1; + six2 = (const struct sockaddr_in6 *)addr2; + return !((six1->sin6_port == six2->sin6_port || !cmp_port) && + !memcmp(&six1->sin6_addr, &six2->sin6_addr, + sizeof(struct in6_addr))); } + + return -1; } -static void print_local_ip_port(int sockfd, const char *fmt) +static int cmp_sock_addr(info_fn fn, int sock1, + const struct sockaddr_storage *addr2, int cmp_port) { - print_ip_port(sockfd, getsockname, fmt); + struct sockaddr_storage addr1; + socklen_t len1 = sizeof(addr1); + + memset(&addr1, 0, len1); + if (fn(sock1, (struct sockaddr *)&addr1, (socklen_t *)&len1) != 0) + return -1; + + return cmp_addr(&addr1, addr2, cmp_port); +} + +static int cmp_local_ip(int sock1, const struct sockaddr_storage *addr2) +{ + return cmp_sock_addr(getsockname, sock1, addr2, /*cmp_port*/ 0); } -static void print_remote_ip_port(int sockfd, const char *fmt) +static int cmp_local_addr(int sock1, const struct sockaddr_storage *addr2) { - print_ip_port(sockfd, getpeername, fmt); + return cmp_sock_addr(getsockname, sock1, addr2, /*cmp_port*/ 1); +} + +static int cmp_peer_addr(int sock1, const struct sockaddr_storage *addr2) +{ + return cmp_sock_addr(getpeername, sock1, addr2, /*cmp_port*/ 1); } static int start_server(int type, const struct sockaddr_storage *addr, socklen_t addr_len) { - int fd; fd = socket(addr->ss_family, type, 0); @@ -358,8 +928,6 @@ static int start_server(int type, const struct sockaddr_storage *addr, } } - print_local_ip_port(fd, "\t Actual: bind(%s, %d)\n"); - goto out; close_out: close(fd); @@ -372,19 +940,19 @@ static int connect_to_server(int type, const struct sockaddr_storage *addr, socklen_t addr_len) { int domain; - int fd; + int fd = -1; domain = addr->ss_family; if (domain != AF_INET && domain != AF_INET6) { log_err("Unsupported address family"); - return -1; + goto err; } fd = socket(domain, type, 0); if (fd == -1) { - log_err("Failed to creating client socket"); - return -1; + log_err("Failed to create client socket"); + goto err; } if (connect(fd, (const struct sockaddr *)addr, addr_len) == -1) { @@ -392,162 +960,394 @@ static int connect_to_server(int type, const struct sockaddr_storage *addr, goto err; } - print_remote_ip_port(fd, "\t Actual: connect(%s, %d)"); - print_local_ip_port(fd, " from (%s, %d)\n"); + goto out; +err: + close(fd); + fd = -1; +out: + return fd; +} + +int init_pktinfo(int domain, struct cmsghdr *cmsg) +{ + struct in6_pktinfo *pktinfo6; + struct in_pktinfo *pktinfo4; + + if (domain == AF_INET) { + cmsg->cmsg_level = SOL_IP; + cmsg->cmsg_type = IP_PKTINFO; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); + pktinfo4 = (struct in_pktinfo *)CMSG_DATA(cmsg); + memset(pktinfo4, 0, sizeof(struct in_pktinfo)); + if (inet_pton(domain, SRC4_IP, + (void *)&pktinfo4->ipi_spec_dst) != 1) + return -1; + } else if (domain == AF_INET6) { + cmsg->cmsg_level = SOL_IPV6; + cmsg->cmsg_type = IPV6_PKTINFO; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); + pktinfo6 = (struct in6_pktinfo *)CMSG_DATA(cmsg); + memset(pktinfo6, 0, sizeof(struct in6_pktinfo)); + if (inet_pton(domain, SRC6_IP, + (void *)&pktinfo6->ipi6_addr) != 1) + return -1; + } else { + return -1; + } return 0; +} + +static int sendmsg_to_server(const struct sockaddr_storage *addr, + socklen_t addr_len, int set_cmsg, int *syscall_err) +{ + union { + char buf[CMSG_SPACE(sizeof(struct in6_pktinfo))]; + struct cmsghdr align; + } control6; + union { + char buf[CMSG_SPACE(sizeof(struct in_pktinfo))]; + struct cmsghdr align; + } control4; + struct msghdr hdr; + struct iovec iov; + char data = 'a'; + int domain; + int fd = -1; + + domain = addr->ss_family; + + if (domain != AF_INET && domain != AF_INET6) { + log_err("Unsupported address family"); + goto err; + } + + fd = socket(domain, SOCK_DGRAM, 0); + if (fd == -1) { + log_err("Failed to create client socket"); + goto err; + } + + memset(&iov, 0, sizeof(iov)); + iov.iov_base = &data; + iov.iov_len = sizeof(data); + + memset(&hdr, 0, sizeof(hdr)); + hdr.msg_name = (void *)addr; + hdr.msg_namelen = addr_len; + hdr.msg_iov = &iov; + hdr.msg_iovlen = 1; + + if (set_cmsg) { + if (domain == AF_INET) { + hdr.msg_control = &control4; + hdr.msg_controllen = sizeof(control4.buf); + } else if (domain == AF_INET6) { + hdr.msg_control = &control6; + hdr.msg_controllen = sizeof(control6.buf); + } + if (init_pktinfo(domain, CMSG_FIRSTHDR(&hdr))) { + log_err("Fail to init pktinfo"); + goto err; + } + } + + if (sendmsg(fd, &hdr, 0) != sizeof(data)) { + log_err("Fail to send message to server"); + *syscall_err = errno; + goto err; + } + + goto out; err: close(fd); - return -1; + fd = -1; +out: + return fd; } -static void print_test_case_num(int domain, int type) +static int recvmsg_from_client(int sockfd, struct sockaddr_storage *src_addr) { - static int test_num; - - printf("Test case #%d (%s/%s):\n", ++test_num, - (domain == AF_INET ? "IPv4" : - domain == AF_INET6 ? "IPv6" : - "unknown_domain"), - (type == SOCK_STREAM ? "TCP" : - type == SOCK_DGRAM ? "UDP" : - "unknown_type")); + struct timeval tv; + struct msghdr hdr; + struct iovec iov; + char data[64]; + fd_set rfds; + + FD_ZERO(&rfds); + FD_SET(sockfd, &rfds); + + tv.tv_sec = 2; + tv.tv_usec = 0; + + if (select(sockfd + 1, &rfds, NULL, NULL, &tv) <= 0 || + !FD_ISSET(sockfd, &rfds)) + return -1; + + memset(&iov, 0, sizeof(iov)); + iov.iov_base = data; + iov.iov_len = sizeof(data); + + memset(&hdr, 0, sizeof(hdr)); + hdr.msg_name = src_addr; + hdr.msg_namelen = sizeof(struct sockaddr_storage); + hdr.msg_iov = &iov; + hdr.msg_iovlen = 1; + + return recvmsg(sockfd, &hdr, 0); } -static int run_test_case(int domain, int type, const char *ip, - unsigned short port) +static int init_addrs(const struct sock_addr_test *test, + struct sockaddr_storage *requested_addr, + struct sockaddr_storage *expected_addr, + struct sockaddr_storage *expected_src_addr) { - struct sockaddr_storage addr; - socklen_t addr_len = sizeof(addr); + socklen_t addr_len = sizeof(struct sockaddr_storage); + + if (mk_sockaddr(test->domain, test->expected_ip, test->expected_port, + (struct sockaddr *)expected_addr, addr_len) == -1) + goto err; + + if (mk_sockaddr(test->domain, test->requested_ip, test->requested_port, + (struct sockaddr *)requested_addr, addr_len) == -1) + goto err; + + if (test->expected_src_ip && + mk_sockaddr(test->domain, test->expected_src_ip, 0, + (struct sockaddr *)expected_src_addr, addr_len) == -1) + goto err; + + return 0; +err: + return -1; +} + +static int run_bind_test_case(const struct sock_addr_test *test) +{ + socklen_t addr_len = sizeof(struct sockaddr_storage); + struct sockaddr_storage requested_addr; + struct sockaddr_storage expected_addr; + int clientfd = -1; int servfd = -1; int err = 0; - print_test_case_num(domain, type); - - if (mk_sockaddr(domain, ip, port, (struct sockaddr *)&addr, - addr_len) == -1) - return -1; + if (init_addrs(test, &requested_addr, &expected_addr, NULL)) + goto err; - printf("\tRequested: bind(%s, %d) ..\n", ip, port); - servfd = start_server(type, &addr, addr_len); + servfd = start_server(test->type, &requested_addr, addr_len); if (servfd == -1) goto err; - printf("\tRequested: connect(%s, %d) from (*, *) ..\n", ip, port); - if (connect_to_server(type, &addr, addr_len)) + if (cmp_local_addr(servfd, &expected_addr)) + goto err; + + /* Try to connect to server just in case */ + clientfd = connect_to_server(test->type, &expected_addr, addr_len); + if (clientfd == -1) goto err; goto out; err: err = -1; out: + close(clientfd); close(servfd); return err; } -static void close_progs_fds(struct program *progs, size_t prog_cnt) +static int run_connect_test_case(const struct sock_addr_test *test) { - size_t i; + socklen_t addr_len = sizeof(struct sockaddr_storage); + struct sockaddr_storage expected_src_addr; + struct sockaddr_storage requested_addr; + struct sockaddr_storage expected_addr; + int clientfd = -1; + int servfd = -1; + int err = 0; - for (i = 0; i < prog_cnt; ++i) { - close(progs[i].fd); - progs[i].fd = -1; - } + if (init_addrs(test, &requested_addr, &expected_addr, + &expected_src_addr)) + goto err; + + /* Prepare server to connect to */ + servfd = start_server(test->type, &expected_addr, addr_len); + if (servfd == -1) + goto err; + + clientfd = connect_to_server(test->type, &requested_addr, addr_len); + if (clientfd == -1) + goto err; + + /* Make sure src and dst addrs were overridden properly */ + if (cmp_peer_addr(clientfd, &expected_addr)) + goto err; + + if (cmp_local_ip(clientfd, &expected_src_addr)) + goto err; + + goto out; +err: + err = -1; +out: + close(clientfd); + close(servfd); + return err; } -static int load_and_attach_progs(int cgfd, struct program *progs, - size_t prog_cnt) +static int run_sendmsg_test_case(const struct sock_addr_test *test) { - size_t i; - - for (i = 0; i < prog_cnt; ++i) { - printf("Load %s with invalid type (can pollute stderr) ", - progs[i].name); - fflush(stdout); - progs[i].fd = progs[i].loadfn(progs[i].invalid_type, NULL); - if (progs[i].fd != -1) { - log_err("Load with invalid type accepted for %s", - progs[i].name); - goto err; - } - printf("... REJECTED\n"); + socklen_t addr_len = sizeof(struct sockaddr_storage); + struct sockaddr_storage expected_src_addr; + struct sockaddr_storage requested_addr; + struct sockaddr_storage expected_addr; + struct sockaddr_storage real_src_addr; + int clientfd = -1; + int servfd = -1; + int set_cmsg; + int err = 0; + + if (test->type != SOCK_DGRAM) + goto err; - printf("Load %s with valid type", progs[i].name); - progs[i].fd = progs[i].loadfn(progs[i].type, progs[i].name); - if (progs[i].fd == -1) { - log_err("Failed to load program %s", progs[i].name); + if (init_addrs(test, &requested_addr, &expected_addr, + &expected_src_addr)) + goto err; + + /* Prepare server to sendmsg to */ + servfd = start_server(test->type, &expected_addr, addr_len); + if (servfd == -1) + goto err; + + for (set_cmsg = 0; set_cmsg <= 1; ++set_cmsg) { + if (clientfd >= 0) + close(clientfd); + + clientfd = sendmsg_to_server(&requested_addr, addr_len, + set_cmsg, &err); + if (err) + goto out; + else if (clientfd == -1) goto err; - } - printf(" ... OK\n"); - printf("Attach %s with invalid type", progs[i].name); - if (bpf_prog_attach(progs[i].fd, cgfd, progs[i].invalid_type, - BPF_F_ALLOW_OVERRIDE) != -1) { - log_err("Attach with invalid type accepted for %s", - progs[i].name); + /* Try to receive message on server instead of using + * getpeername(2) on client socket, to check that client's + * destination address was rewritten properly, since + * getpeername(2) doesn't work with unconnected datagram + * sockets. + * + * Get source address from recvmsg(2) as well to make sure + * source was rewritten properly: getsockname(2) can't be used + * since socket is unconnected and source defined for one + * specific packet may differ from the one used by default and + * returned by getsockname(2). + */ + if (recvmsg_from_client(servfd, &real_src_addr) == -1) goto err; - } - printf(" ... REJECTED\n"); - printf("Attach %s with valid type", progs[i].name); - if (bpf_prog_attach(progs[i].fd, cgfd, progs[i].type, - BPF_F_ALLOW_OVERRIDE) == -1) { - log_err("Failed to attach program %s", progs[i].name); + if (cmp_addr(&real_src_addr, &expected_src_addr, /*cmp_port*/0)) goto err; - } - printf(" ... OK\n"); } - return 0; + goto out; err: - close_progs_fds(progs, prog_cnt); - return -1; + err = -1; +out: + close(clientfd); + close(servfd); + return err; } -static int run_domain_test(int domain, int cgfd, struct program *progs, - size_t prog_cnt, const char *ip, unsigned short port) +static int run_test_case(int cgfd, const struct sock_addr_test *test) { + int progfd = -1; int err = 0; - if (load_and_attach_progs(cgfd, progs, prog_cnt) == -1) + printf("Test case: %s .. ", test->descr); + + progfd = test->loadfn(test); + if (test->expected_result == LOAD_REJECT && progfd < 0) + goto out; + else if (test->expected_result == LOAD_REJECT || progfd < 0) + goto err; + + err = bpf_prog_attach(progfd, cgfd, test->attach_type, + BPF_F_ALLOW_OVERRIDE); + if (test->expected_result == ATTACH_REJECT && err) { + err = 0; /* error was expected, reset it */ + goto out; + } else if (test->expected_result == ATTACH_REJECT || err) { goto err; + } - if (run_test_case(domain, SOCK_STREAM, ip, port) == -1) + switch (test->attach_type) { + case BPF_CGROUP_INET4_BIND: + case BPF_CGROUP_INET6_BIND: + err = run_bind_test_case(test); + break; + case BPF_CGROUP_INET4_CONNECT: + case BPF_CGROUP_INET6_CONNECT: + err = run_connect_test_case(test); + break; + case BPF_CGROUP_UDP4_SENDMSG: + case BPF_CGROUP_UDP6_SENDMSG: + err = run_sendmsg_test_case(test); + break; + default: goto err; + } + + if (test->expected_result == SYSCALL_EPERM && err == EPERM) { + err = 0; /* error was expected, reset it */ + goto out; + } + + if (test->expected_result == SYSCALL_ENOTSUPP && err == ENOTSUPP) { + err = 0; /* error was expected, reset it */ + goto out; + } - if (run_test_case(domain, SOCK_DGRAM, ip, port) == -1) + if (err || test->expected_result != SUCCESS) goto err; goto out; err: err = -1; out: - close_progs_fds(progs, prog_cnt); + /* Detaching w/o checking return code: best effort attempt. */ + if (progfd != -1) + bpf_prog_detach(cgfd, test->attach_type); + close(progfd); + printf("[%s]\n", err ? "FAIL" : "PASS"); return err; } -static int run_test(void) +static int run_tests(int cgfd) +{ + int passes = 0; + int fails = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(tests); ++i) { + if (run_test_case(cgfd, &tests[i])) + ++fails; + else + ++passes; + } + printf("Summary: %d PASSED, %d FAILED\n", passes, fails); + return fails ? -1 : 0; +} + +int main(int argc, char **argv) { - size_t inet6_prog_cnt; - size_t inet_prog_cnt; int cgfd = -1; int err = 0; - struct program inet6_progs[] = { - {BPF_CGROUP_INET6_BIND, bind6_prog_load, -1, "bind6", - BPF_CGROUP_INET4_BIND}, - {BPF_CGROUP_INET6_CONNECT, connect6_prog_load, -1, "connect6", - BPF_CGROUP_INET4_CONNECT}, - }; - inet6_prog_cnt = sizeof(inet6_progs) / sizeof(struct program); - - struct program inet_progs[] = { - {BPF_CGROUP_INET4_BIND, bind4_prog_load, -1, "bind4", - BPF_CGROUP_INET6_BIND}, - {BPF_CGROUP_INET4_CONNECT, connect4_prog_load, -1, "connect4", - BPF_CGROUP_INET6_CONNECT}, - }; - inet_prog_cnt = sizeof(inet_progs) / sizeof(struct program); + if (argc < 2) { + fprintf(stderr, + "%s has to be run via %s.sh. Skip direct run.\n", + argv[0], argv[0]); + exit(err); + } if (setup_cgroup_environment()) goto err; @@ -559,12 +1359,7 @@ static int run_test(void) if (join_cgroup(CG_PATH)) goto err; - if (run_domain_test(AF_INET, cgfd, inet_progs, inet_prog_cnt, SERV4_IP, - SERV4_PORT) == -1) - goto err; - - if (run_domain_test(AF_INET6, cgfd, inet6_progs, inet6_prog_cnt, - SERV6_IP, SERV6_PORT) == -1) + if (run_tests(cgfd)) goto err; goto out; @@ -573,17 +1368,5 @@ err: out: close(cgfd); cleanup_cgroup_environment(); - printf(err ? "### FAIL\n" : "### SUCCESS\n"); return err; } - -int main(int argc, char **argv) -{ - if (argc < 2) { - fprintf(stderr, - "%s has to be run via %s.sh. Skip direct run.\n", - argv[0], argv[0]); - exit(0); - } - return run_test(); -} diff --git a/tools/testing/selftests/bpf/test_sockmap.c b/tools/testing/selftests/bpf/test_sockmap.c index eb17fae458e6..05c8cb71724a 100644 --- a/tools/testing/selftests/bpf/test_sockmap.c +++ b/tools/testing/selftests/bpf/test_sockmap.c @@ -337,16 +337,28 @@ static int msg_loop(int fd, int iov_count, int iov_length, int cnt, int fd_flags = O_NONBLOCK; struct timeval timeout; float total_bytes; + int bytes_cnt = 0; + int chunk_sz; fd_set w; + if (opt->sendpage) + chunk_sz = iov_length * cnt; + else + chunk_sz = iov_length * iov_count; + fcntl(fd, fd_flags); total_bytes = (float)iov_count * (float)iov_length * (float)cnt; err = clock_gettime(CLOCK_MONOTONIC, &s->start); if (err < 0) perror("recv start time: "); while (s->bytes_recvd < total_bytes) { - timeout.tv_sec = 0; - timeout.tv_usec = 10; + if (txmsg_cork) { + timeout.tv_sec = 0; + timeout.tv_usec = 1000; + } else { + timeout.tv_sec = 1; + timeout.tv_usec = 0; + } /* FD sets */ FD_ZERO(&w); @@ -388,9 +400,14 @@ static int msg_loop(int fd, int iov_count, int iov_length, int cnt, errno = -EIO; fprintf(stderr, "detected data corruption @iov[%i]:%i %02x != %02x, %02x ?= %02x\n", - i, j, d[j], k - 1, d[j+1], k + 1); + i, j, d[j], k - 1, d[j+1], k); goto out_errno; } + bytes_cnt++; + if (bytes_cnt == chunk_sz) { + k = 0; + bytes_cnt = 0; + } recv--; } } @@ -429,8 +446,8 @@ static int sendmsg_test(struct sockmap_options *opt) struct msg_stats s = {0}; int iov_count = opt->iov_count; int iov_buf = opt->iov_length; + int rx_status, tx_status; int cnt = opt->rate; - int status; errno = 0; @@ -442,7 +459,7 @@ static int sendmsg_test(struct sockmap_options *opt) rxpid = fork(); if (rxpid == 0) { if (opt->drop_expected) - exit(1); + exit(0); if (opt->sendpage) iov_count = 1; @@ -463,7 +480,9 @@ static int sendmsg_test(struct sockmap_options *opt) "rx_sendmsg: TX: %zuB %fB/s %fGB/s RX: %zuB %fB/s %fGB/s\n", s.bytes_sent, sent_Bps, sent_Bps/giga, s.bytes_recvd, recvd_Bps, recvd_Bps/giga); - exit(1); + if (err && txmsg_cork) + err = 0; + exit(err ? 1 : 0); } else if (rxpid == -1) { perror("msg_loop_rx: "); return errno; @@ -491,14 +510,27 @@ static int sendmsg_test(struct sockmap_options *opt) "tx_sendmsg: TX: %zuB %fB/s %f GB/s RX: %zuB %fB/s %fGB/s\n", s.bytes_sent, sent_Bps, sent_Bps/giga, s.bytes_recvd, recvd_Bps, recvd_Bps/giga); - exit(1); + exit(err ? 1 : 0); } else if (txpid == -1) { perror("msg_loop_tx: "); return errno; } - assert(waitpid(rxpid, &status, 0) == rxpid); - assert(waitpid(txpid, &status, 0) == txpid); + assert(waitpid(rxpid, &rx_status, 0) == rxpid); + assert(waitpid(txpid, &tx_status, 0) == txpid); + if (WIFEXITED(rx_status)) { + err = WEXITSTATUS(rx_status); + if (err) { + fprintf(stderr, "rx thread exited with err %d. ", err); + goto out; + } + } + if (WIFEXITED(tx_status)) { + err = WEXITSTATUS(tx_status); + if (err) + fprintf(stderr, "tx thread exited with err %d. ", err); + } +out: return err; } @@ -844,6 +876,8 @@ static char *test_to_str(int test) #define OPTSTRING 60 static void test_options(char *options) { + char tstr[OPTSTRING]; + memset(options, 0, OPTSTRING); if (txmsg_pass) @@ -856,14 +890,22 @@ static void test_options(char *options) strncat(options, "redir_noisy,", OPTSTRING); if (txmsg_drop) strncat(options, "drop,", OPTSTRING); - if (txmsg_apply) - strncat(options, "apply,", OPTSTRING); - if (txmsg_cork) - strncat(options, "cork,", OPTSTRING); - if (txmsg_start) - strncat(options, "start,", OPTSTRING); - if (txmsg_end) - strncat(options, "end,", OPTSTRING); + if (txmsg_apply) { + snprintf(tstr, OPTSTRING, "apply %d,", txmsg_apply); + strncat(options, tstr, OPTSTRING); + } + if (txmsg_cork) { + snprintf(tstr, OPTSTRING, "cork %d,", txmsg_cork); + strncat(options, tstr, OPTSTRING); + } + if (txmsg_start) { + snprintf(tstr, OPTSTRING, "start %d,", txmsg_start); + strncat(options, tstr, OPTSTRING); + } + if (txmsg_end) { + snprintf(tstr, OPTSTRING, "end %d,", txmsg_end); + strncat(options, tstr, OPTSTRING); + } if (txmsg_ingress) strncat(options, "ingress,", OPTSTRING); if (txmsg_skb) @@ -872,7 +914,7 @@ static void test_options(char *options) static int __test_exec(int cgrp, int test, struct sockmap_options *opt) { - char *options = calloc(60, sizeof(char)); + char *options = calloc(OPTSTRING, sizeof(char)); int err; if (test == SENDPAGE) @@ -1010,14 +1052,14 @@ static int test_send(struct sockmap_options *opt, int cgrp) opt->iov_length = 1; opt->iov_count = 1; - opt->rate = 1024; + opt->rate = 512; err = test_exec(cgrp, opt); if (err) goto out; opt->iov_length = 256; opt->iov_count = 1024; - opt->rate = 10; + opt->rate = 2; err = test_exec(cgrp, opt); if (err) goto out; @@ -1329,6 +1371,11 @@ static int __test_suite(char *bpf_file) return cg_fd; } + if (join_cgroup(CG_PATH)) { + fprintf(stderr, "ERROR: failed to join cgroup\n"); + return -EINVAL; + } + /* Tests basic commands and APIs with range of iov values */ txmsg_start = txmsg_end = 0; err = test_txmsg(cg_fd); diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c index 4b4f015be217..7cb1d74057ce 100644 --- a/tools/testing/selftests/bpf/test_verifier.c +++ b/tools/testing/selftests/bpf/test_verifier.c @@ -50,7 +50,7 @@ #define MAX_INSNS BPF_MAXINSNS #define MAX_FIXUPS 8 -#define MAX_NR_MAPS 4 +#define MAX_NR_MAPS 7 #define POINTER_VALUE 0xcafe4all #define TEST_DATA_LEN 64 @@ -66,7 +66,9 @@ struct bpf_test { int fixup_map1[MAX_FIXUPS]; int fixup_map2[MAX_FIXUPS]; int fixup_map3[MAX_FIXUPS]; - int fixup_prog[MAX_FIXUPS]; + int fixup_map4[MAX_FIXUPS]; + int fixup_prog1[MAX_FIXUPS]; + int fixup_prog2[MAX_FIXUPS]; int fixup_map_in_map[MAX_FIXUPS]; const char *errstr; const char *errstr_unpriv; @@ -2769,7 +2771,7 @@ static struct bpf_test tests[] = { BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .fixup_prog = { 1 }, + .fixup_prog1 = { 1 }, .errstr_unpriv = "R3 leaks addr into helper", .result_unpriv = REJECT, .result = ACCEPT, @@ -2856,7 +2858,7 @@ static struct bpf_test tests[] = { BPF_MOV64_IMM(BPF_REG_0, 1), BPF_EXIT_INSN(), }, - .fixup_prog = { 1 }, + .fixup_prog1 = { 1 }, .result = ACCEPT, .retval = 42, }, @@ -2870,7 +2872,7 @@ static struct bpf_test tests[] = { BPF_MOV64_IMM(BPF_REG_0, 1), BPF_EXIT_INSN(), }, - .fixup_prog = { 1 }, + .fixup_prog1 = { 1 }, .result = ACCEPT, .retval = 41, }, @@ -2884,7 +2886,7 @@ static struct bpf_test tests[] = { BPF_MOV64_IMM(BPF_REG_0, 1), BPF_EXIT_INSN(), }, - .fixup_prog = { 1 }, + .fixup_prog1 = { 1 }, .result = ACCEPT, .retval = 1, }, @@ -2898,7 +2900,7 @@ static struct bpf_test tests[] = { BPF_MOV64_IMM(BPF_REG_0, 2), BPF_EXIT_INSN(), }, - .fixup_prog = { 1 }, + .fixup_prog1 = { 1 }, .result = ACCEPT, .retval = 2, }, @@ -2912,7 +2914,7 @@ static struct bpf_test tests[] = { BPF_MOV64_IMM(BPF_REG_0, 2), BPF_EXIT_INSN(), }, - .fixup_prog = { 1 }, + .fixup_prog1 = { 1 }, .result = ACCEPT, .retval = 2, }, @@ -2926,7 +2928,7 @@ static struct bpf_test tests[] = { BPF_MOV64_IMM(BPF_REG_0, 2), BPF_EXIT_INSN(), }, - .fixup_prog = { 2 }, + .fixup_prog1 = { 2 }, .result = ACCEPT, .retval = 42, }, @@ -11682,6 +11684,112 @@ static struct bpf_test tests[] = { .prog_type = BPF_PROG_TYPE_XDP, }, { + "calls: two calls returning different map pointers for lookup (hash, array)", + .insns = { + /* main prog */ + BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 2), + BPF_CALL_REL(11), + BPF_JMP_IMM(BPF_JA, 0, 0, 1), + BPF_CALL_REL(12), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2), + BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, + offsetof(struct test_val, foo)), + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + /* subprog 1 */ + BPF_LD_MAP_FD(BPF_REG_0, 0), + BPF_EXIT_INSN(), + /* subprog 2 */ + BPF_LD_MAP_FD(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + .fixup_map2 = { 13 }, + .fixup_map4 = { 16 }, + .result = ACCEPT, + .retval = 1, + }, + { + "calls: two calls returning different map pointers for lookup (hash, map in map)", + .insns = { + /* main prog */ + BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 2), + BPF_CALL_REL(11), + BPF_JMP_IMM(BPF_JA, 0, 0, 1), + BPF_CALL_REL(12), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2), + BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, + offsetof(struct test_val, foo)), + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + /* subprog 1 */ + BPF_LD_MAP_FD(BPF_REG_0, 0), + BPF_EXIT_INSN(), + /* subprog 2 */ + BPF_LD_MAP_FD(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + .fixup_map_in_map = { 16 }, + .fixup_map4 = { 13 }, + .result = REJECT, + .errstr = "R0 invalid mem access 'map_ptr'", + }, + { + "cond: two branches returning different map pointers for lookup (tail, tail)", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, + offsetof(struct __sk_buff, mark)), + BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 0, 3), + BPF_LD_MAP_FD(BPF_REG_2, 0), + BPF_JMP_IMM(BPF_JA, 0, 0, 2), + BPF_LD_MAP_FD(BPF_REG_2, 0), + BPF_MOV64_IMM(BPF_REG_3, 7), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_tail_call), + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .fixup_prog1 = { 5 }, + .fixup_prog2 = { 2 }, + .result_unpriv = REJECT, + .errstr_unpriv = "tail_call abusing map_ptr", + .result = ACCEPT, + .retval = 42, + }, + { + "cond: two branches returning same map pointers for lookup (tail, tail)", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, + offsetof(struct __sk_buff, mark)), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_6, 0, 3), + BPF_LD_MAP_FD(BPF_REG_2, 0), + BPF_JMP_IMM(BPF_JA, 0, 0, 2), + BPF_LD_MAP_FD(BPF_REG_2, 0), + BPF_MOV64_IMM(BPF_REG_3, 7), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_tail_call), + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .fixup_prog2 = { 2, 5 }, + .result_unpriv = ACCEPT, + .result = ACCEPT, + .retval = 42, + }, + { "search pruning: all branches should be verified (nop operation)", .insns = { BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), @@ -12162,12 +12270,13 @@ static int probe_filter_length(const struct bpf_insn *fp) return len + 1; } -static int create_map(uint32_t size_value, uint32_t max_elem) +static int create_map(uint32_t type, uint32_t size_key, + uint32_t size_value, uint32_t max_elem) { int fd; - fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(long long), - size_value, max_elem, BPF_F_NO_PREALLOC); + fd = bpf_create_map(type, size_key, size_value, max_elem, + type == BPF_MAP_TYPE_HASH ? BPF_F_NO_PREALLOC : 0); if (fd < 0) printf("Failed to create hash map '%s'!\n", strerror(errno)); @@ -12200,13 +12309,13 @@ static int create_prog_dummy2(int mfd, int idx) ARRAY_SIZE(prog), "GPL", 0, NULL, 0); } -static int create_prog_array(void) +static int create_prog_array(uint32_t max_elem, int p1key) { - int p1key = 0, p2key = 1; + int p2key = 1; int mfd, p1fd, p2fd; mfd = bpf_create_map(BPF_MAP_TYPE_PROG_ARRAY, sizeof(int), - sizeof(int), 4, 0); + sizeof(int), max_elem, 0); if (mfd < 0) { printf("Failed to create prog array '%s'!\n", strerror(errno)); return -1; @@ -12261,7 +12370,9 @@ static void do_test_fixup(struct bpf_test *test, struct bpf_insn *prog, int *fixup_map1 = test->fixup_map1; int *fixup_map2 = test->fixup_map2; int *fixup_map3 = test->fixup_map3; - int *fixup_prog = test->fixup_prog; + int *fixup_map4 = test->fixup_map4; + int *fixup_prog1 = test->fixup_prog1; + int *fixup_prog2 = test->fixup_prog2; int *fixup_map_in_map = test->fixup_map_in_map; if (test->fill_helper) @@ -12272,7 +12383,8 @@ static void do_test_fixup(struct bpf_test *test, struct bpf_insn *prog, * that really matters is value size in this case. */ if (*fixup_map1) { - map_fds[0] = create_map(sizeof(long long), 1); + map_fds[0] = create_map(BPF_MAP_TYPE_HASH, sizeof(long long), + sizeof(long long), 1); do { prog[*fixup_map1].imm = map_fds[0]; fixup_map1++; @@ -12280,7 +12392,8 @@ static void do_test_fixup(struct bpf_test *test, struct bpf_insn *prog, } if (*fixup_map2) { - map_fds[1] = create_map(sizeof(struct test_val), 1); + map_fds[1] = create_map(BPF_MAP_TYPE_HASH, sizeof(long long), + sizeof(struct test_val), 1); do { prog[*fixup_map2].imm = map_fds[1]; fixup_map2++; @@ -12288,25 +12401,43 @@ static void do_test_fixup(struct bpf_test *test, struct bpf_insn *prog, } if (*fixup_map3) { - map_fds[1] = create_map(sizeof(struct other_val), 1); + map_fds[2] = create_map(BPF_MAP_TYPE_HASH, sizeof(long long), + sizeof(struct other_val), 1); do { - prog[*fixup_map3].imm = map_fds[1]; + prog[*fixup_map3].imm = map_fds[2]; fixup_map3++; } while (*fixup_map3); } - if (*fixup_prog) { - map_fds[2] = create_prog_array(); + if (*fixup_map4) { + map_fds[3] = create_map(BPF_MAP_TYPE_ARRAY, sizeof(int), + sizeof(struct test_val), 1); + do { + prog[*fixup_map4].imm = map_fds[3]; + fixup_map4++; + } while (*fixup_map4); + } + + if (*fixup_prog1) { + map_fds[4] = create_prog_array(4, 0); + do { + prog[*fixup_prog1].imm = map_fds[4]; + fixup_prog1++; + } while (*fixup_prog1); + } + + if (*fixup_prog2) { + map_fds[5] = create_prog_array(8, 7); do { - prog[*fixup_prog].imm = map_fds[2]; - fixup_prog++; - } while (*fixup_prog); + prog[*fixup_prog2].imm = map_fds[5]; + fixup_prog2++; + } while (*fixup_prog2); } if (*fixup_map_in_map) { - map_fds[3] = create_map_in_map(); + map_fds[6] = create_map_in_map(); do { - prog[*fixup_map_in_map].imm = map_fds[3]; + prog[*fixup_map_in_map].imm = map_fds[6]; fixup_map_in_map++; } while (*fixup_map_in_map); } |