Merge git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
static const unsigned int total_bytes = 10 * 1024 * 1024;
|
||||
static const struct timeval timeo_sec = { .tv_sec = 10 };
|
||||
static const size_t timeo_optlen = sizeof(timeo_sec);
|
||||
static int expected_stg = 0xeB9F;
|
||||
static int stop, duration;
|
||||
|
||||
static int settimeo(int fd)
|
||||
@@ -88,7 +89,7 @@ done:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void do_test(const char *tcp_ca)
|
||||
static void do_test(const char *tcp_ca, const struct bpf_map *sk_stg_map)
|
||||
{
|
||||
struct sockaddr_in6 sa6 = {};
|
||||
ssize_t nr_recv = 0, bytes = 0;
|
||||
@@ -126,14 +127,34 @@ static void do_test(const char *tcp_ca)
|
||||
err = listen(lfd, 1);
|
||||
if (CHECK(err == -1, "listen", "errno:%d\n", errno))
|
||||
goto done;
|
||||
err = pthread_create(&srv_thread, NULL, server, (void *)(long)lfd);
|
||||
if (CHECK(err != 0, "pthread_create", "err:%d\n", err))
|
||||
goto done;
|
||||
|
||||
if (sk_stg_map) {
|
||||
err = bpf_map_update_elem(bpf_map__fd(sk_stg_map), &fd,
|
||||
&expected_stg, BPF_NOEXIST);
|
||||
if (CHECK(err, "bpf_map_update_elem(sk_stg_map)",
|
||||
"err:%d errno:%d\n", err, errno))
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* connect to server */
|
||||
err = connect(fd, (struct sockaddr *)&sa6, addrlen);
|
||||
if (CHECK(err == -1, "connect", "errno:%d\n", errno))
|
||||
goto wait_thread;
|
||||
goto done;
|
||||
|
||||
if (sk_stg_map) {
|
||||
int tmp_stg;
|
||||
|
||||
err = bpf_map_lookup_elem(bpf_map__fd(sk_stg_map), &fd,
|
||||
&tmp_stg);
|
||||
if (CHECK(!err || errno != ENOENT,
|
||||
"bpf_map_lookup_elem(sk_stg_map)",
|
||||
"err:%d errno:%d\n", err, errno))
|
||||
goto done;
|
||||
}
|
||||
|
||||
err = pthread_create(&srv_thread, NULL, server, (void *)(long)lfd);
|
||||
if (CHECK(err != 0, "pthread_create", "err:%d errno:%d\n", err, errno))
|
||||
goto done;
|
||||
|
||||
/* recv total_bytes */
|
||||
while (bytes < total_bytes && !READ_ONCE(stop)) {
|
||||
@@ -149,7 +170,6 @@ static void do_test(const char *tcp_ca)
|
||||
CHECK(bytes != total_bytes, "recv", "%zd != %u nr_recv:%zd errno:%d\n",
|
||||
bytes, total_bytes, nr_recv, errno);
|
||||
|
||||
wait_thread:
|
||||
WRITE_ONCE(stop, 1);
|
||||
pthread_join(srv_thread, &thread_ret);
|
||||
CHECK(IS_ERR(thread_ret), "pthread_join", "thread_ret:%ld",
|
||||
@@ -175,7 +195,7 @@ static void test_cubic(void)
|
||||
return;
|
||||
}
|
||||
|
||||
do_test("bpf_cubic");
|
||||
do_test("bpf_cubic", NULL);
|
||||
|
||||
bpf_link__destroy(link);
|
||||
bpf_cubic__destroy(cubic_skel);
|
||||
@@ -197,7 +217,10 @@ static void test_dctcp(void)
|
||||
return;
|
||||
}
|
||||
|
||||
do_test("bpf_dctcp");
|
||||
do_test("bpf_dctcp", dctcp_skel->maps.sk_stg_map);
|
||||
CHECK(dctcp_skel->bss->stg_result != expected_stg,
|
||||
"Unexpected stg_result", "stg_result (%x) != expected_stg (%x)\n",
|
||||
dctcp_skel->bss->stg_result, expected_stg);
|
||||
|
||||
bpf_link__destroy(link);
|
||||
bpf_dctcp__destroy(dctcp_skel);
|
||||
|
@@ -125,6 +125,6 @@ void test_btf_dump() {
|
||||
if (!test__start_subtest(t->name))
|
||||
continue;
|
||||
|
||||
test_btf_dump_case(i, &btf_dump_test_cases[i]);
|
||||
test_btf_dump_case(i, &btf_dump_test_cases[i]);
|
||||
}
|
||||
}
|
||||
|
244
tools/testing/selftests/bpf/prog_tests/cgroup_link.c
Normal file
244
tools/testing/selftests/bpf/prog_tests/cgroup_link.c
Normal file
@@ -0,0 +1,244 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <test_progs.h>
|
||||
#include "cgroup_helpers.h"
|
||||
#include "test_cgroup_link.skel.h"
|
||||
|
||||
static __u32 duration = 0;
|
||||
#define PING_CMD "ping -q -c1 -w1 127.0.0.1 > /dev/null"
|
||||
|
||||
static struct test_cgroup_link *skel = NULL;
|
||||
|
||||
int ping_and_check(int exp_calls, int exp_alt_calls)
|
||||
{
|
||||
skel->bss->calls = 0;
|
||||
skel->bss->alt_calls = 0;
|
||||
CHECK_FAIL(system(PING_CMD));
|
||||
if (CHECK(skel->bss->calls != exp_calls, "call_cnt",
|
||||
"exp %d, got %d\n", exp_calls, skel->bss->calls))
|
||||
return -EINVAL;
|
||||
if (CHECK(skel->bss->alt_calls != exp_alt_calls, "alt_call_cnt",
|
||||
"exp %d, got %d\n", exp_alt_calls, skel->bss->alt_calls))
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void test_cgroup_link(void)
|
||||
{
|
||||
struct {
|
||||
const char *path;
|
||||
int fd;
|
||||
} cgs[] = {
|
||||
{ "/cg1" },
|
||||
{ "/cg1/cg2" },
|
||||
{ "/cg1/cg2/cg3" },
|
||||
{ "/cg1/cg2/cg3/cg4" },
|
||||
};
|
||||
int last_cg = ARRAY_SIZE(cgs) - 1, cg_nr = ARRAY_SIZE(cgs);
|
||||
DECLARE_LIBBPF_OPTS(bpf_link_update_opts, link_upd_opts);
|
||||
struct bpf_link *links[ARRAY_SIZE(cgs)] = {}, *tmp_link;
|
||||
__u32 prog_ids[ARRAY_SIZE(cgs)], prog_cnt = 0, attach_flags;
|
||||
int i = 0, err, prog_fd;
|
||||
bool detach_legacy = false;
|
||||
|
||||
skel = test_cgroup_link__open_and_load();
|
||||
if (CHECK(!skel, "skel_open_load", "failed to open/load skeleton\n"))
|
||||
return;
|
||||
prog_fd = bpf_program__fd(skel->progs.egress);
|
||||
|
||||
err = setup_cgroup_environment();
|
||||
if (CHECK(err, "cg_init", "failed: %d\n", err))
|
||||
goto cleanup;
|
||||
|
||||
for (i = 0; i < cg_nr; i++) {
|
||||
cgs[i].fd = create_and_get_cgroup(cgs[i].path);
|
||||
if (CHECK(cgs[i].fd < 0, "cg_create", "fail: %d\n", cgs[i].fd))
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
err = join_cgroup(cgs[last_cg].path);
|
||||
if (CHECK(err, "cg_join", "fail: %d\n", err))
|
||||
goto cleanup;
|
||||
|
||||
for (i = 0; i < cg_nr; i++) {
|
||||
links[i] = bpf_program__attach_cgroup(skel->progs.egress,
|
||||
cgs[i].fd);
|
||||
if (CHECK(IS_ERR(links[i]), "cg_attach", "i: %d, err: %ld\n",
|
||||
i, PTR_ERR(links[i])))
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ping_and_check(cg_nr, 0);
|
||||
|
||||
/* query the number of effective progs and attach flags in root cg */
|
||||
err = bpf_prog_query(cgs[0].fd, BPF_CGROUP_INET_EGRESS,
|
||||
BPF_F_QUERY_EFFECTIVE, &attach_flags, NULL,
|
||||
&prog_cnt);
|
||||
CHECK_FAIL(err);
|
||||
CHECK_FAIL(attach_flags != BPF_F_ALLOW_MULTI);
|
||||
if (CHECK(prog_cnt != 1, "effect_cnt", "exp %d, got %d\n", 1, prog_cnt))
|
||||
goto cleanup;
|
||||
|
||||
/* query the number of effective progs in last cg */
|
||||
err = bpf_prog_query(cgs[last_cg].fd, BPF_CGROUP_INET_EGRESS,
|
||||
BPF_F_QUERY_EFFECTIVE, NULL, NULL,
|
||||
&prog_cnt);
|
||||
CHECK_FAIL(err);
|
||||
CHECK_FAIL(attach_flags != BPF_F_ALLOW_MULTI);
|
||||
if (CHECK(prog_cnt != cg_nr, "effect_cnt", "exp %d, got %d\n",
|
||||
cg_nr, prog_cnt))
|
||||
goto cleanup;
|
||||
|
||||
/* query the effective prog IDs in last cg */
|
||||
err = bpf_prog_query(cgs[last_cg].fd, BPF_CGROUP_INET_EGRESS,
|
||||
BPF_F_QUERY_EFFECTIVE, &attach_flags,
|
||||
prog_ids, &prog_cnt);
|
||||
CHECK_FAIL(err);
|
||||
CHECK_FAIL(attach_flags != BPF_F_ALLOW_MULTI);
|
||||
if (CHECK(prog_cnt != cg_nr, "effect_cnt", "exp %d, got %d\n",
|
||||
cg_nr, prog_cnt))
|
||||
goto cleanup;
|
||||
for (i = 1; i < prog_cnt; i++) {
|
||||
CHECK(prog_ids[i - 1] != prog_ids[i], "prog_id_check",
|
||||
"idx %d, prev id %d, cur id %d\n",
|
||||
i, prog_ids[i - 1], prog_ids[i]);
|
||||
}
|
||||
|
||||
/* detach bottom program and ping again */
|
||||
bpf_link__destroy(links[last_cg]);
|
||||
links[last_cg] = NULL;
|
||||
|
||||
ping_and_check(cg_nr - 1, 0);
|
||||
|
||||
/* mix in with non link-based multi-attachments */
|
||||
err = bpf_prog_attach(prog_fd, cgs[last_cg].fd,
|
||||
BPF_CGROUP_INET_EGRESS, BPF_F_ALLOW_MULTI);
|
||||
if (CHECK(err, "cg_attach_legacy", "errno=%d\n", errno))
|
||||
goto cleanup;
|
||||
detach_legacy = true;
|
||||
|
||||
links[last_cg] = bpf_program__attach_cgroup(skel->progs.egress,
|
||||
cgs[last_cg].fd);
|
||||
if (CHECK(IS_ERR(links[last_cg]), "cg_attach", "err: %ld\n",
|
||||
PTR_ERR(links[last_cg])))
|
||||
goto cleanup;
|
||||
|
||||
ping_and_check(cg_nr + 1, 0);
|
||||
|
||||
/* detach link */
|
||||
bpf_link__destroy(links[last_cg]);
|
||||
links[last_cg] = NULL;
|
||||
|
||||
/* detach legacy */
|
||||
err = bpf_prog_detach2(prog_fd, cgs[last_cg].fd, BPF_CGROUP_INET_EGRESS);
|
||||
if (CHECK(err, "cg_detach_legacy", "errno=%d\n", errno))
|
||||
goto cleanup;
|
||||
detach_legacy = false;
|
||||
|
||||
/* attach legacy exclusive prog attachment */
|
||||
err = bpf_prog_attach(prog_fd, cgs[last_cg].fd,
|
||||
BPF_CGROUP_INET_EGRESS, 0);
|
||||
if (CHECK(err, "cg_attach_exclusive", "errno=%d\n", errno))
|
||||
goto cleanup;
|
||||
detach_legacy = true;
|
||||
|
||||
/* attempt to mix in with multi-attach bpf_link */
|
||||
tmp_link = bpf_program__attach_cgroup(skel->progs.egress,
|
||||
cgs[last_cg].fd);
|
||||
if (CHECK(!IS_ERR(tmp_link), "cg_attach_fail", "unexpected success!\n")) {
|
||||
bpf_link__destroy(tmp_link);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ping_and_check(cg_nr, 0);
|
||||
|
||||
/* detach */
|
||||
err = bpf_prog_detach2(prog_fd, cgs[last_cg].fd, BPF_CGROUP_INET_EGRESS);
|
||||
if (CHECK(err, "cg_detach_legacy", "errno=%d\n", errno))
|
||||
goto cleanup;
|
||||
detach_legacy = false;
|
||||
|
||||
ping_and_check(cg_nr - 1, 0);
|
||||
|
||||
/* attach back link-based one */
|
||||
links[last_cg] = bpf_program__attach_cgroup(skel->progs.egress,
|
||||
cgs[last_cg].fd);
|
||||
if (CHECK(IS_ERR(links[last_cg]), "cg_attach", "err: %ld\n",
|
||||
PTR_ERR(links[last_cg])))
|
||||
goto cleanup;
|
||||
|
||||
ping_and_check(cg_nr, 0);
|
||||
|
||||
/* check legacy exclusive prog can't be attached */
|
||||
err = bpf_prog_attach(prog_fd, cgs[last_cg].fd,
|
||||
BPF_CGROUP_INET_EGRESS, 0);
|
||||
if (CHECK(!err, "cg_attach_exclusive", "unexpected success")) {
|
||||
bpf_prog_detach2(prog_fd, cgs[last_cg].fd, BPF_CGROUP_INET_EGRESS);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* replace BPF programs inside their links for all but first link */
|
||||
for (i = 1; i < cg_nr; i++) {
|
||||
err = bpf_link__update_program(links[i], skel->progs.egress_alt);
|
||||
if (CHECK(err, "prog_upd", "link #%d\n", i))
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ping_and_check(1, cg_nr - 1);
|
||||
|
||||
/* Attempt program update with wrong expected BPF program */
|
||||
link_upd_opts.old_prog_fd = bpf_program__fd(skel->progs.egress_alt);
|
||||
link_upd_opts.flags = BPF_F_REPLACE;
|
||||
err = bpf_link_update(bpf_link__fd(links[0]),
|
||||
bpf_program__fd(skel->progs.egress_alt),
|
||||
&link_upd_opts);
|
||||
if (CHECK(err == 0 || errno != EPERM, "prog_cmpxchg1",
|
||||
"unexpectedly succeeded, err %d, errno %d\n", err, -errno))
|
||||
goto cleanup;
|
||||
|
||||
/* Compare-exchange single link program from egress to egress_alt */
|
||||
link_upd_opts.old_prog_fd = bpf_program__fd(skel->progs.egress);
|
||||
link_upd_opts.flags = BPF_F_REPLACE;
|
||||
err = bpf_link_update(bpf_link__fd(links[0]),
|
||||
bpf_program__fd(skel->progs.egress_alt),
|
||||
&link_upd_opts);
|
||||
if (CHECK(err, "prog_cmpxchg2", "errno %d\n", -errno))
|
||||
goto cleanup;
|
||||
|
||||
/* ping */
|
||||
ping_and_check(0, cg_nr);
|
||||
|
||||
/* close cgroup FDs before detaching links */
|
||||
for (i = 0; i < cg_nr; i++) {
|
||||
if (cgs[i].fd > 0) {
|
||||
close(cgs[i].fd);
|
||||
cgs[i].fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* BPF programs should still get called */
|
||||
ping_and_check(0, cg_nr);
|
||||
|
||||
/* leave cgroup and remove them, don't detach programs */
|
||||
cleanup_cgroup_environment();
|
||||
|
||||
/* BPF programs should have been auto-detached */
|
||||
ping_and_check(0, 0);
|
||||
|
||||
cleanup:
|
||||
if (detach_legacy)
|
||||
bpf_prog_detach2(prog_fd, cgs[last_cg].fd,
|
||||
BPF_CGROUP_INET_EGRESS);
|
||||
|
||||
for (i = 0; i < cg_nr; i++) {
|
||||
if (!IS_ERR(links[i]))
|
||||
bpf_link__destroy(links[i]);
|
||||
}
|
||||
test_cgroup_link__destroy(skel);
|
||||
|
||||
for (i = 0; i < cg_nr; i++) {
|
||||
if (cgs[i].fd > 0)
|
||||
close(cgs[i].fd);
|
||||
}
|
||||
cleanup_cgroup_environment();
|
||||
}
|
@@ -82,6 +82,7 @@ static void get_stack_print_output(void *ctx, int cpu, void *data, __u32 size)
|
||||
void test_get_stack_raw_tp(void)
|
||||
{
|
||||
const char *file = "./test_get_stack_rawtp.o";
|
||||
const char *file_err = "./test_get_stack_rawtp_err.o";
|
||||
const char *prog_name = "raw_tracepoint/sys_enter";
|
||||
int i, err, prog_fd, exp_cnt = MAX_CNT_RAWTP;
|
||||
struct perf_buffer_opts pb_opts = {};
|
||||
@@ -93,6 +94,10 @@ void test_get_stack_raw_tp(void)
|
||||
struct bpf_map *map;
|
||||
cpu_set_t cpu_set;
|
||||
|
||||
err = bpf_prog_load(file_err, BPF_PROG_TYPE_RAW_TRACEPOINT, &obj, &prog_fd);
|
||||
if (CHECK(err >= 0, "prog_load raw tp", "err %d errno %d\n", err, errno))
|
||||
return;
|
||||
|
||||
err = bpf_prog_load(file, BPF_PROG_TYPE_RAW_TRACEPOINT, &obj, &prog_fd);
|
||||
if (CHECK(err, "prog_load raw tp", "err %d errno %d\n", err, errno))
|
||||
return;
|
||||
|
61
tools/testing/selftests/bpf/prog_tests/global_data_init.c
Normal file
61
tools/testing/selftests/bpf/prog_tests/global_data_init.c
Normal file
@@ -0,0 +1,61 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <test_progs.h>
|
||||
|
||||
void test_global_data_init(void)
|
||||
{
|
||||
const char *file = "./test_global_data.o";
|
||||
int err = -ENOMEM, map_fd, zero = 0;
|
||||
__u8 *buff = NULL, *newval = NULL;
|
||||
struct bpf_object *obj;
|
||||
struct bpf_map *map;
|
||||
__u32 duration = 0;
|
||||
size_t sz;
|
||||
|
||||
obj = bpf_object__open_file(file, NULL);
|
||||
if (CHECK_FAIL(!obj))
|
||||
return;
|
||||
|
||||
map = bpf_object__find_map_by_name(obj, "test_glo.rodata");
|
||||
if (CHECK_FAIL(!map || !bpf_map__is_internal(map)))
|
||||
goto out;
|
||||
|
||||
sz = bpf_map__def(map)->value_size;
|
||||
newval = malloc(sz);
|
||||
if (CHECK_FAIL(!newval))
|
||||
goto out;
|
||||
|
||||
memset(newval, 0, sz);
|
||||
/* wrong size, should fail */
|
||||
err = bpf_map__set_initial_value(map, newval, sz - 1);
|
||||
if (CHECK(!err, "reject set initial value wrong size", "err %d\n", err))
|
||||
goto out;
|
||||
|
||||
err = bpf_map__set_initial_value(map, newval, sz);
|
||||
if (CHECK(err, "set initial value", "err %d\n", err))
|
||||
goto out;
|
||||
|
||||
err = bpf_object__load(obj);
|
||||
if (CHECK_FAIL(err))
|
||||
goto out;
|
||||
|
||||
map_fd = bpf_map__fd(map);
|
||||
if (CHECK_FAIL(map_fd < 0))
|
||||
goto out;
|
||||
|
||||
buff = malloc(sz);
|
||||
if (buff)
|
||||
err = bpf_map_lookup_elem(map_fd, &zero, buff);
|
||||
if (CHECK(!buff || err || memcmp(buff, newval, sz),
|
||||
"compare .rodata map data override",
|
||||
"err %d errno %d\n", err, errno))
|
||||
goto out;
|
||||
|
||||
memset(newval, 1, sz);
|
||||
/* object loaded - should fail */
|
||||
err = bpf_map__set_initial_value(map, newval, sz);
|
||||
CHECK(!err, "reject set initial value after load", "err %d\n", err);
|
||||
out:
|
||||
free(buff);
|
||||
free(newval);
|
||||
bpf_object__close(obj);
|
||||
}
|
309
tools/testing/selftests/bpf/prog_tests/sk_assign.c
Normal file
309
tools/testing/selftests/bpf/prog_tests/sk_assign.c
Normal file
@@ -0,0 +1,309 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2018 Facebook
|
||||
// Copyright (c) 2019 Cloudflare
|
||||
// Copyright (c) 2020 Isovalent, Inc.
|
||||
/*
|
||||
* Test that the socket assign program is able to redirect traffic towards a
|
||||
* socket, regardless of whether the port or address destination of the traffic
|
||||
* matches the port.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "test_progs.h"
|
||||
|
||||
#define BIND_PORT 1234
|
||||
#define CONNECT_PORT 4321
|
||||
#define TEST_DADDR (0xC0A80203)
|
||||
#define NS_SELF "/proc/self/ns/net"
|
||||
|
||||
static const struct timeval timeo_sec = { .tv_sec = 3 };
|
||||
static const size_t timeo_optlen = sizeof(timeo_sec);
|
||||
static int stop, duration;
|
||||
|
||||
static bool
|
||||
configure_stack(void)
|
||||
{
|
||||
char tc_cmd[BUFSIZ];
|
||||
|
||||
/* Move to a new networking namespace */
|
||||
if (CHECK_FAIL(unshare(CLONE_NEWNET)))
|
||||
return false;
|
||||
|
||||
/* Configure necessary links, routes */
|
||||
if (CHECK_FAIL(system("ip link set dev lo up")))
|
||||
return false;
|
||||
if (CHECK_FAIL(system("ip route add local default dev lo")))
|
||||
return false;
|
||||
if (CHECK_FAIL(system("ip -6 route add local default dev lo")))
|
||||
return false;
|
||||
|
||||
/* Load qdisc, BPF program */
|
||||
if (CHECK_FAIL(system("tc qdisc add dev lo clsact")))
|
||||
return false;
|
||||
sprintf(tc_cmd, "%s %s %s %s", "tc filter add dev lo ingress bpf",
|
||||
"direct-action object-file ./test_sk_assign.o",
|
||||
"section classifier/sk_assign_test",
|
||||
(env.verbosity < VERBOSE_VERY) ? " 2>/dev/null" : "");
|
||||
if (CHECK(system(tc_cmd), "BPF load failed;",
|
||||
"run with -vv for more info\n"))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int
|
||||
start_server(const struct sockaddr *addr, socklen_t len, int type)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = socket(addr->sa_family, type, 0);
|
||||
if (CHECK_FAIL(fd == -1))
|
||||
goto out;
|
||||
if (CHECK_FAIL(setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeo_sec,
|
||||
timeo_optlen)))
|
||||
goto close_out;
|
||||
if (CHECK_FAIL(bind(fd, addr, len) == -1))
|
||||
goto close_out;
|
||||
if (type == SOCK_STREAM && CHECK_FAIL(listen(fd, 128) == -1))
|
||||
goto close_out;
|
||||
|
||||
goto out;
|
||||
close_out:
|
||||
close(fd);
|
||||
fd = -1;
|
||||
out:
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int
|
||||
connect_to_server(const struct sockaddr *addr, socklen_t len, int type)
|
||||
{
|
||||
int fd = -1;
|
||||
|
||||
fd = socket(addr->sa_family, type, 0);
|
||||
if (CHECK_FAIL(fd == -1))
|
||||
goto out;
|
||||
if (CHECK_FAIL(setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeo_sec,
|
||||
timeo_optlen)))
|
||||
goto close_out;
|
||||
if (CHECK_FAIL(connect(fd, addr, len)))
|
||||
goto close_out;
|
||||
|
||||
goto out;
|
||||
close_out:
|
||||
close(fd);
|
||||
fd = -1;
|
||||
out:
|
||||
return fd;
|
||||
}
|
||||
|
||||
static in_port_t
|
||||
get_port(int fd)
|
||||
{
|
||||
struct sockaddr_storage ss;
|
||||
socklen_t slen = sizeof(ss);
|
||||
in_port_t port = 0;
|
||||
|
||||
if (CHECK_FAIL(getsockname(fd, (struct sockaddr *)&ss, &slen)))
|
||||
return port;
|
||||
|
||||
switch (ss.ss_family) {
|
||||
case AF_INET:
|
||||
port = ((struct sockaddr_in *)&ss)->sin_port;
|
||||
break;
|
||||
case AF_INET6:
|
||||
port = ((struct sockaddr_in6 *)&ss)->sin6_port;
|
||||
break;
|
||||
default:
|
||||
CHECK(1, "Invalid address family", "%d\n", ss.ss_family);
|
||||
}
|
||||
return port;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
rcv_msg(int srv_client, int type)
|
||||
{
|
||||
struct sockaddr_storage ss;
|
||||
char buf[BUFSIZ];
|
||||
socklen_t slen;
|
||||
|
||||
if (type == SOCK_STREAM)
|
||||
return read(srv_client, &buf, sizeof(buf));
|
||||
else
|
||||
return recvfrom(srv_client, &buf, sizeof(buf), 0,
|
||||
(struct sockaddr *)&ss, &slen);
|
||||
}
|
||||
|
||||
static int
|
||||
run_test(int server_fd, const struct sockaddr *addr, socklen_t len, int type)
|
||||
{
|
||||
int client = -1, srv_client = -1;
|
||||
char buf[] = "testing";
|
||||
in_port_t port;
|
||||
int ret = 1;
|
||||
|
||||
client = connect_to_server(addr, len, type);
|
||||
if (client == -1) {
|
||||
perror("Cannot connect to server");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (type == SOCK_STREAM) {
|
||||
srv_client = accept(server_fd, NULL, NULL);
|
||||
if (CHECK_FAIL(srv_client == -1)) {
|
||||
perror("Can't accept connection");
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
srv_client = server_fd;
|
||||
}
|
||||
if (CHECK_FAIL(write(client, buf, sizeof(buf)) != sizeof(buf))) {
|
||||
perror("Can't write on client");
|
||||
goto out;
|
||||
}
|
||||
if (CHECK_FAIL(rcv_msg(srv_client, type) != sizeof(buf))) {
|
||||
perror("Can't read on server");
|
||||
goto out;
|
||||
}
|
||||
|
||||
port = get_port(srv_client);
|
||||
if (CHECK_FAIL(!port))
|
||||
goto out;
|
||||
/* SOCK_STREAM is connected via accept(), so the server's local address
|
||||
* will be the CONNECT_PORT rather than the BIND port that corresponds
|
||||
* to the listen socket. SOCK_DGRAM on the other hand is connectionless
|
||||
* so we can't really do the same check there; the server doesn't ever
|
||||
* create a socket with CONNECT_PORT.
|
||||
*/
|
||||
if (type == SOCK_STREAM &&
|
||||
CHECK(port != htons(CONNECT_PORT), "Expected", "port %u but got %u",
|
||||
CONNECT_PORT, ntohs(port)))
|
||||
goto out;
|
||||
else if (type == SOCK_DGRAM &&
|
||||
CHECK(port != htons(BIND_PORT), "Expected",
|
||||
"port %u but got %u", BIND_PORT, ntohs(port)))
|
||||
goto out;
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
close(client);
|
||||
if (srv_client != server_fd)
|
||||
close(srv_client);
|
||||
if (ret)
|
||||
WRITE_ONCE(stop, 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
prepare_addr(struct sockaddr *addr, int family, __u16 port, bool rewrite_addr)
|
||||
{
|
||||
struct sockaddr_in *addr4;
|
||||
struct sockaddr_in6 *addr6;
|
||||
|
||||
switch (family) {
|
||||
case AF_INET:
|
||||
addr4 = (struct sockaddr_in *)addr;
|
||||
memset(addr4, 0, sizeof(*addr4));
|
||||
addr4->sin_family = family;
|
||||
addr4->sin_port = htons(port);
|
||||
if (rewrite_addr)
|
||||
addr4->sin_addr.s_addr = htonl(TEST_DADDR);
|
||||
else
|
||||
addr4->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
break;
|
||||
case AF_INET6:
|
||||
addr6 = (struct sockaddr_in6 *)addr;
|
||||
memset(addr6, 0, sizeof(*addr6));
|
||||
addr6->sin6_family = family;
|
||||
addr6->sin6_port = htons(port);
|
||||
addr6->sin6_addr = in6addr_loopback;
|
||||
if (rewrite_addr)
|
||||
addr6->sin6_addr.s6_addr32[3] = htonl(TEST_DADDR);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Invalid family %d", family);
|
||||
}
|
||||
}
|
||||
|
||||
struct test_sk_cfg {
|
||||
const char *name;
|
||||
int family;
|
||||
struct sockaddr *addr;
|
||||
socklen_t len;
|
||||
int type;
|
||||
bool rewrite_addr;
|
||||
};
|
||||
|
||||
#define TEST(NAME, FAMILY, TYPE, REWRITE) \
|
||||
{ \
|
||||
.name = NAME, \
|
||||
.family = FAMILY, \
|
||||
.addr = (FAMILY == AF_INET) ? (struct sockaddr *)&addr4 \
|
||||
: (struct sockaddr *)&addr6, \
|
||||
.len = (FAMILY == AF_INET) ? sizeof(addr4) : sizeof(addr6), \
|
||||
.type = TYPE, \
|
||||
.rewrite_addr = REWRITE, \
|
||||
}
|
||||
|
||||
void test_sk_assign(void)
|
||||
{
|
||||
struct sockaddr_in addr4;
|
||||
struct sockaddr_in6 addr6;
|
||||
struct test_sk_cfg tests[] = {
|
||||
TEST("ipv4 tcp port redir", AF_INET, SOCK_STREAM, false),
|
||||
TEST("ipv4 tcp addr redir", AF_INET, SOCK_STREAM, true),
|
||||
TEST("ipv6 tcp port redir", AF_INET6, SOCK_STREAM, false),
|
||||
TEST("ipv6 tcp addr redir", AF_INET6, SOCK_STREAM, true),
|
||||
TEST("ipv4 udp port redir", AF_INET, SOCK_DGRAM, false),
|
||||
TEST("ipv4 udp addr redir", AF_INET, SOCK_DGRAM, true),
|
||||
TEST("ipv6 udp port redir", AF_INET6, SOCK_DGRAM, false),
|
||||
TEST("ipv6 udp addr redir", AF_INET6, SOCK_DGRAM, true),
|
||||
};
|
||||
int server = -1;
|
||||
int self_net;
|
||||
|
||||
self_net = open(NS_SELF, O_RDONLY);
|
||||
if (CHECK_FAIL(self_net < 0)) {
|
||||
perror("Unable to open "NS_SELF);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!configure_stack()) {
|
||||
perror("configure_stack");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
for (int i = 0; i < ARRAY_SIZE(tests) && !READ_ONCE(stop); i++) {
|
||||
struct test_sk_cfg *test = &tests[i];
|
||||
const struct sockaddr *addr;
|
||||
|
||||
if (!test__start_subtest(test->name))
|
||||
continue;
|
||||
prepare_addr(test->addr, test->family, BIND_PORT, false);
|
||||
addr = (const struct sockaddr *)test->addr;
|
||||
server = start_server(addr, test->len, test->type);
|
||||
if (server == -1)
|
||||
goto cleanup;
|
||||
|
||||
/* connect to unbound ports */
|
||||
prepare_addr(test->addr, test->family, CONNECT_PORT,
|
||||
test->rewrite_addr);
|
||||
if (run_test(server, addr, test->len, test->type))
|
||||
goto close;
|
||||
|
||||
close(server);
|
||||
server = -1;
|
||||
}
|
||||
|
||||
close:
|
||||
close(server);
|
||||
cleanup:
|
||||
if (CHECK_FAIL(setns(self_net, CLONE_NEWNET)))
|
||||
perror("Failed to setns("NS_SELF")");
|
||||
close(self_net);
|
||||
}
|
@@ -226,7 +226,7 @@ static void *server_thread(void *arg)
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
while (!server_done) {
|
||||
while (true) {
|
||||
client_fd = accept(fd, (struct sockaddr *)&addr, &len);
|
||||
if (client_fd == -1 && errno == EAGAIN) {
|
||||
usleep(50);
|
||||
@@ -272,7 +272,7 @@ void test_tcp_rtt(void)
|
||||
CHECK_FAIL(run_test(cgroup_fd, server_fd));
|
||||
|
||||
server_done = true;
|
||||
pthread_join(tid, &server_res);
|
||||
CHECK_FAIL(pthread_join(tid, &server_res));
|
||||
CHECK_FAIL(IS_ERR(server_res));
|
||||
|
||||
close_server_fd:
|
||||
|
86
tools/testing/selftests/bpf/prog_tests/test_lsm.c
Normal file
86
tools/testing/selftests/bpf/prog_tests/test_lsm.c
Normal file
@@ -0,0 +1,86 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
/*
|
||||
* Copyright (C) 2020 Google LLC.
|
||||
*/
|
||||
|
||||
#include <test_progs.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <malloc.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "lsm.skel.h"
|
||||
|
||||
char *CMD_ARGS[] = {"true", NULL};
|
||||
|
||||
int heap_mprotect(void)
|
||||
{
|
||||
void *buf;
|
||||
long sz;
|
||||
int ret;
|
||||
|
||||
sz = sysconf(_SC_PAGESIZE);
|
||||
if (sz < 0)
|
||||
return sz;
|
||||
|
||||
buf = memalign(sz, 2 * sz);
|
||||
if (buf == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = mprotect(buf, sz, PROT_READ | PROT_WRITE | PROT_EXEC);
|
||||
free(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int exec_cmd(int *monitored_pid)
|
||||
{
|
||||
int child_pid, child_status;
|
||||
|
||||
child_pid = fork();
|
||||
if (child_pid == 0) {
|
||||
*monitored_pid = getpid();
|
||||
execvp(CMD_ARGS[0], CMD_ARGS);
|
||||
return -EINVAL;
|
||||
} else if (child_pid > 0) {
|
||||
waitpid(child_pid, &child_status, 0);
|
||||
return child_status;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
void test_test_lsm(void)
|
||||
{
|
||||
struct lsm *skel = NULL;
|
||||
int err, duration = 0;
|
||||
|
||||
skel = lsm__open_and_load();
|
||||
if (CHECK(!skel, "skel_load", "lsm skeleton failed\n"))
|
||||
goto close_prog;
|
||||
|
||||
err = lsm__attach(skel);
|
||||
if (CHECK(err, "attach", "lsm attach failed: %d\n", err))
|
||||
goto close_prog;
|
||||
|
||||
err = exec_cmd(&skel->bss->monitored_pid);
|
||||
if (CHECK(err < 0, "exec_cmd", "err %d errno %d\n", err, errno))
|
||||
goto close_prog;
|
||||
|
||||
CHECK(skel->bss->bprm_count != 1, "bprm_count", "bprm_count = %d\n",
|
||||
skel->bss->bprm_count);
|
||||
|
||||
skel->bss->monitored_pid = getpid();
|
||||
|
||||
err = heap_mprotect();
|
||||
if (CHECK(errno != EPERM, "heap_mprotect", "want errno=EPERM, got %d\n",
|
||||
errno))
|
||||
goto close_prog;
|
||||
|
||||
CHECK(skel->bss->mprotect_count != 1, "mprotect_count",
|
||||
"mprotect_count = %d\n", skel->bss->mprotect_count);
|
||||
|
||||
close_prog:
|
||||
lsm__destroy(skel);
|
||||
}
|
@@ -11,7 +11,7 @@ static void nsleep()
|
||||
{
|
||||
struct timespec ts = { .tv_nsec = MY_TV_NSEC };
|
||||
|
||||
(void)nanosleep(&ts, NULL);
|
||||
(void)syscall(__NR_nanosleep, &ts, NULL);
|
||||
}
|
||||
|
||||
void test_vmlinux(void)
|
||||
|
62
tools/testing/selftests/bpf/prog_tests/xdp_attach.c
Normal file
62
tools/testing/selftests/bpf/prog_tests/xdp_attach.c
Normal file
@@ -0,0 +1,62 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <test_progs.h>
|
||||
|
||||
#define IFINDEX_LO 1
|
||||
#define XDP_FLAGS_REPLACE (1U << 4)
|
||||
|
||||
void test_xdp_attach(void)
|
||||
{
|
||||
struct bpf_object *obj1, *obj2, *obj3;
|
||||
const char *file = "./test_xdp.o";
|
||||
int err, fd1, fd2, fd3;
|
||||
__u32 duration = 0;
|
||||
DECLARE_LIBBPF_OPTS(bpf_xdp_set_link_opts, opts,
|
||||
.old_fd = -1);
|
||||
|
||||
err = bpf_prog_load(file, BPF_PROG_TYPE_XDP, &obj1, &fd1);
|
||||
if (CHECK_FAIL(err))
|
||||
return;
|
||||
err = bpf_prog_load(file, BPF_PROG_TYPE_XDP, &obj2, &fd2);
|
||||
if (CHECK_FAIL(err))
|
||||
goto out_1;
|
||||
err = bpf_prog_load(file, BPF_PROG_TYPE_XDP, &obj3, &fd3);
|
||||
if (CHECK_FAIL(err))
|
||||
goto out_2;
|
||||
|
||||
err = bpf_set_link_xdp_fd_opts(IFINDEX_LO, fd1, XDP_FLAGS_REPLACE,
|
||||
&opts);
|
||||
if (CHECK(err, "load_ok", "initial load failed"))
|
||||
goto out_close;
|
||||
|
||||
err = bpf_set_link_xdp_fd_opts(IFINDEX_LO, fd2, XDP_FLAGS_REPLACE,
|
||||
&opts);
|
||||
if (CHECK(!err, "load_fail", "load with expected id didn't fail"))
|
||||
goto out;
|
||||
|
||||
opts.old_fd = fd1;
|
||||
err = bpf_set_link_xdp_fd_opts(IFINDEX_LO, fd2, 0, &opts);
|
||||
if (CHECK(err, "replace_ok", "replace valid old_fd failed"))
|
||||
goto out;
|
||||
|
||||
err = bpf_set_link_xdp_fd_opts(IFINDEX_LO, fd3, 0, &opts);
|
||||
if (CHECK(!err, "replace_fail", "replace invalid old_fd didn't fail"))
|
||||
goto out;
|
||||
|
||||
err = bpf_set_link_xdp_fd_opts(IFINDEX_LO, -1, 0, &opts);
|
||||
if (CHECK(!err, "remove_fail", "remove invalid old_fd didn't fail"))
|
||||
goto out;
|
||||
|
||||
opts.old_fd = fd2;
|
||||
err = bpf_set_link_xdp_fd_opts(IFINDEX_LO, -1, 0, &opts);
|
||||
if (CHECK(err, "remove_ok", "remove valid old_fd failed"))
|
||||
goto out;
|
||||
|
||||
out:
|
||||
bpf_set_link_xdp_fd(IFINDEX_LO, -1, 0);
|
||||
out_close:
|
||||
bpf_object__close(obj3);
|
||||
out_2:
|
||||
bpf_object__close(obj2);
|
||||
out_1:
|
||||
bpf_object__close(obj1);
|
||||
}
|
Reference in New Issue
Block a user