Message ID | 20200821102948.21918-7-lmb@cloudflare.com (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | None | expand |
On 8/21/20 3:29 AM, Lorenz Bauer wrote: > Add a test which copies a socket from a sockmap into another sockmap > or sockhash. This excercises bpf_map_update_elem support from BPF > context. Compare the socket cookies from source and destination to > ensure that the copy succeeded. > > Also check that the verifier rejects map_update from unsafe contexts. > > Signed-off-by: Lorenz Bauer <lmb@cloudflare.com> A few nits below. Acked-by: Yonghong Song <yhs@fb.com> > --- > .../selftests/bpf/prog_tests/sockmap_basic.c | 78 +++++++++++++++++++ > .../bpf/progs/test_sockmap_invalid_update.c | 23 ++++++ > .../selftests/bpf/progs/test_sockmap_update.c | 48 ++++++++++++ > 3 files changed, 149 insertions(+) > create mode 100644 tools/testing/selftests/bpf/progs/test_sockmap_invalid_update.c > create mode 100644 tools/testing/selftests/bpf/progs/test_sockmap_update.c > > diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c > index 96e7b7f84c65..65ce7c289534 100644 > --- a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c > +++ b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c > @@ -4,6 +4,8 @@ > > #include "test_progs.h" > #include "test_skmsg_load_helpers.skel.h" > +#include "test_sockmap_update.skel.h" > +#include "test_sockmap_invalid_update.skel.h" > > #define TCP_REPAIR 19 /* TCP sock is under repair right now */ > > @@ -101,6 +103,76 @@ static void test_skmsg_helpers(enum bpf_map_type map_type) > test_skmsg_load_helpers__destroy(skel); > } > > +static void test_sockmap_update(enum bpf_map_type map_type) > +{ > + struct bpf_prog_test_run_attr tattr; > + int err, prog, src, dst, duration = 0; > + struct test_sockmap_update *skel; > + __u64 src_cookie, dst_cookie; > + const __u32 zero = 0; > + char dummy[14] = {0}; > + __s64 sk; > + > + sk = connected_socket_v4(); > + if (CHECK(sk == -1, "connected_socket_v4", "cannot connect\n")) > + return; > + > + skel = test_sockmap_update__open_and_load(); > + if (CHECK(!skel, "open_and_load", "cannot load skeleton\n")) { > + close(sk); > + return; > + } > + > + prog = bpf_program__fd(skel->progs.copy_sock_map); > + src = bpf_map__fd(skel->maps.src); > + if (map_type == BPF_MAP_TYPE_SOCKMAP) > + dst = bpf_map__fd(skel->maps.dst_sock_map); > + else > + dst = bpf_map__fd(skel->maps.dst_sock_hash); > + > + err = bpf_map_update_elem(src, &zero, &sk, BPF_NOEXIST); > + if (CHECK(err, "update_elem(src)", "errno=%u\n", errno)) > + goto out; > + > + err = bpf_map_lookup_elem(src, &zero, &src_cookie); > + if (CHECK(err, "lookup_elem(src, cookie)", "errno=%u\n", errno)) > + goto out; > + > + tattr = (struct bpf_prog_test_run_attr){ > + .prog_fd = prog, > + .repeat = 1, > + .data_in = dummy, > + .data_size_in = sizeof(dummy), > + }; > + > + err = bpf_prog_test_run_xattr(&tattr); > + if (CHECK_ATTR(err || !tattr.retval, "bpf_prog_test_run", > + "errno=%u retval=%u\n", errno, tattr.retval)) > + goto out; > + > + err = bpf_map_lookup_elem(dst, &zero, &dst_cookie); > + if (CHECK(err, "lookup_elem(dst, cookie)", "errno=%u\n", errno)) > + goto out; > + > + CHECK(dst_cookie != src_cookie, "cookie mismatch", "%llu != %llu\n", > + dst_cookie, src_cookie); > + > +out: > + close(sk); > + test_sockmap_update__destroy(skel); nit. In the beginning of the function, 'sk' is available and then skel is opened and loaded. You can have out: test_sockmap_update__destroy(skel); close_sk: close(sk); this probably more conforms to linux coding style. Then you can have if (CHECK(!skel, "open_and_load", "cannot load skeleton\n")) goto close_sk; > +} > + > +static void test_sockmap_invalid_update(void) > +{ > + struct test_sockmap_invalid_update *skel; > + int duration = 0; > + > + skel = test_sockmap_invalid_update__open_and_load(); > + CHECK(skel, "open_and_load", "verifier accepted map_update\n"); > + if (skel) > + test_sockmap_invalid_update__destroy(skel); nit, you can just have if (CHECK(skel, "open_and_load", "verifier accepted map_update\n")) test_sockmap_invalid_update__destroy(skel); > +} > + > void test_sockmap_basic(void) > { > if (test__start_subtest("sockmap create_update_free")) > @@ -111,4 +183,10 @@ void test_sockmap_basic(void) > test_skmsg_helpers(BPF_MAP_TYPE_SOCKMAP); > if (test__start_subtest("sockhash sk_msg load helpers")) > test_skmsg_helpers(BPF_MAP_TYPE_SOCKHASH); > + if (test__start_subtest("sockmap update")) > + test_sockmap_update(BPF_MAP_TYPE_SOCKMAP); > + if (test__start_subtest("sockhash update")) > + test_sockmap_update(BPF_MAP_TYPE_SOCKHASH); > + if (test__start_subtest("sockmap update in unsafe context")) > + test_sockmap_invalid_update(); > } > diff --git a/tools/testing/selftests/bpf/progs/test_sockmap_invalid_update.c b/tools/testing/selftests/bpf/progs/test_sockmap_invalid_update.c > new file mode 100644 > index 000000000000..02a59e220cbc > --- /dev/null > +++ b/tools/testing/selftests/bpf/progs/test_sockmap_invalid_update.c > @@ -0,0 +1,23 @@ > +// SPDX-License-Identifier: GPL-2.0 > +// Copyright (c) 2020 Cloudflare > +#include "vmlinux.h" > +#include <bpf/bpf_helpers.h> > + > +struct { > + __uint(type, BPF_MAP_TYPE_SOCKMAP); > + __uint(max_entries, 1); > + __type(key, __u32); > + __type(value, __u64); > +} map SEC(".maps"); > + > +SEC("sockops") > +int bpf_sockmap(struct bpf_sock_ops *skops) > +{ > + __u32 key = 0; > + > + if (skops->sk) > + bpf_map_update_elem(&map, &key, skops->sk, 0); > + return 0; > +} > + > +char _license[] SEC("license") = "GPL"; > diff --git a/tools/testing/selftests/bpf/progs/test_sockmap_update.c b/tools/testing/selftests/bpf/progs/test_sockmap_update.c > new file mode 100644 > index 000000000000..9d0c9f28cab2 > --- /dev/null > +++ b/tools/testing/selftests/bpf/progs/test_sockmap_update.c > @@ -0,0 +1,48 @@ > +// SPDX-License-Identifier: GPL-2.0 > +// Copyright (c) 2020 Cloudflare > +#include "vmlinux.h" > +#include <bpf/bpf_helpers.h> > + > +struct { > + __uint(type, BPF_MAP_TYPE_SOCKMAP); > + __uint(max_entries, 1); > + __type(key, __u32); > + __type(value, __u64); > +} src SEC(".maps"); > + > +struct { > + __uint(type, BPF_MAP_TYPE_SOCKMAP); > + __uint(max_entries, 1); > + __type(key, __u32); > + __type(value, __u64); > +} dst_sock_map SEC(".maps"); > + > +struct { > + __uint(type, BPF_MAP_TYPE_SOCKHASH); > + __uint(max_entries, 1); > + __type(key, __u32); > + __type(value, __u64); > +} dst_sock_hash SEC(".maps"); > + > +SEC("classifier/copy_sock_map") > +int copy_sock_map(void *ctx) > +{ > + struct bpf_sock *sk; > + bool failed = false; > + __u32 key = 0; > + > + sk = bpf_map_lookup_elem(&src, &key); > + if (!sk) > + return SK_DROP; > + > + if (bpf_map_update_elem(&dst_sock_map, &key, sk, 0)) > + failed = true; > + > + if (bpf_map_update_elem(&dst_sock_hash, &key, sk, 0)) > + failed = true; > + > + bpf_sk_release(sk); > + return failed ? SK_DROP : SK_PASS; > +} > + > +char _license[] SEC("license") = "GPL"; >
diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c index 96e7b7f84c65..65ce7c289534 100644 --- a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c +++ b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c @@ -4,6 +4,8 @@ #include "test_progs.h" #include "test_skmsg_load_helpers.skel.h" +#include "test_sockmap_update.skel.h" +#include "test_sockmap_invalid_update.skel.h" #define TCP_REPAIR 19 /* TCP sock is under repair right now */ @@ -101,6 +103,76 @@ static void test_skmsg_helpers(enum bpf_map_type map_type) test_skmsg_load_helpers__destroy(skel); } +static void test_sockmap_update(enum bpf_map_type map_type) +{ + struct bpf_prog_test_run_attr tattr; + int err, prog, src, dst, duration = 0; + struct test_sockmap_update *skel; + __u64 src_cookie, dst_cookie; + const __u32 zero = 0; + char dummy[14] = {0}; + __s64 sk; + + sk = connected_socket_v4(); + if (CHECK(sk == -1, "connected_socket_v4", "cannot connect\n")) + return; + + skel = test_sockmap_update__open_and_load(); + if (CHECK(!skel, "open_and_load", "cannot load skeleton\n")) { + close(sk); + return; + } + + prog = bpf_program__fd(skel->progs.copy_sock_map); + src = bpf_map__fd(skel->maps.src); + if (map_type == BPF_MAP_TYPE_SOCKMAP) + dst = bpf_map__fd(skel->maps.dst_sock_map); + else + dst = bpf_map__fd(skel->maps.dst_sock_hash); + + err = bpf_map_update_elem(src, &zero, &sk, BPF_NOEXIST); + if (CHECK(err, "update_elem(src)", "errno=%u\n", errno)) + goto out; + + err = bpf_map_lookup_elem(src, &zero, &src_cookie); + if (CHECK(err, "lookup_elem(src, cookie)", "errno=%u\n", errno)) + goto out; + + tattr = (struct bpf_prog_test_run_attr){ + .prog_fd = prog, + .repeat = 1, + .data_in = dummy, + .data_size_in = sizeof(dummy), + }; + + err = bpf_prog_test_run_xattr(&tattr); + if (CHECK_ATTR(err || !tattr.retval, "bpf_prog_test_run", + "errno=%u retval=%u\n", errno, tattr.retval)) + goto out; + + err = bpf_map_lookup_elem(dst, &zero, &dst_cookie); + if (CHECK(err, "lookup_elem(dst, cookie)", "errno=%u\n", errno)) + goto out; + + CHECK(dst_cookie != src_cookie, "cookie mismatch", "%llu != %llu\n", + dst_cookie, src_cookie); + +out: + close(sk); + test_sockmap_update__destroy(skel); +} + +static void test_sockmap_invalid_update(void) +{ + struct test_sockmap_invalid_update *skel; + int duration = 0; + + skel = test_sockmap_invalid_update__open_and_load(); + CHECK(skel, "open_and_load", "verifier accepted map_update\n"); + if (skel) + test_sockmap_invalid_update__destroy(skel); +} + void test_sockmap_basic(void) { if (test__start_subtest("sockmap create_update_free")) @@ -111,4 +183,10 @@ void test_sockmap_basic(void) test_skmsg_helpers(BPF_MAP_TYPE_SOCKMAP); if (test__start_subtest("sockhash sk_msg load helpers")) test_skmsg_helpers(BPF_MAP_TYPE_SOCKHASH); + if (test__start_subtest("sockmap update")) + test_sockmap_update(BPF_MAP_TYPE_SOCKMAP); + if (test__start_subtest("sockhash update")) + test_sockmap_update(BPF_MAP_TYPE_SOCKHASH); + if (test__start_subtest("sockmap update in unsafe context")) + test_sockmap_invalid_update(); } diff --git a/tools/testing/selftests/bpf/progs/test_sockmap_invalid_update.c b/tools/testing/selftests/bpf/progs/test_sockmap_invalid_update.c new file mode 100644 index 000000000000..02a59e220cbc --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_sockmap_invalid_update.c @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2020 Cloudflare +#include "vmlinux.h" +#include <bpf/bpf_helpers.h> + +struct { + __uint(type, BPF_MAP_TYPE_SOCKMAP); + __uint(max_entries, 1); + __type(key, __u32); + __type(value, __u64); +} map SEC(".maps"); + +SEC("sockops") +int bpf_sockmap(struct bpf_sock_ops *skops) +{ + __u32 key = 0; + + if (skops->sk) + bpf_map_update_elem(&map, &key, skops->sk, 0); + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/test_sockmap_update.c b/tools/testing/selftests/bpf/progs/test_sockmap_update.c new file mode 100644 index 000000000000..9d0c9f28cab2 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_sockmap_update.c @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2020 Cloudflare +#include "vmlinux.h" +#include <bpf/bpf_helpers.h> + +struct { + __uint(type, BPF_MAP_TYPE_SOCKMAP); + __uint(max_entries, 1); + __type(key, __u32); + __type(value, __u64); +} src SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_SOCKMAP); + __uint(max_entries, 1); + __type(key, __u32); + __type(value, __u64); +} dst_sock_map SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_SOCKHASH); + __uint(max_entries, 1); + __type(key, __u32); + __type(value, __u64); +} dst_sock_hash SEC(".maps"); + +SEC("classifier/copy_sock_map") +int copy_sock_map(void *ctx) +{ + struct bpf_sock *sk; + bool failed = false; + __u32 key = 0; + + sk = bpf_map_lookup_elem(&src, &key); + if (!sk) + return SK_DROP; + + if (bpf_map_update_elem(&dst_sock_map, &key, sk, 0)) + failed = true; + + if (bpf_map_update_elem(&dst_sock_hash, &key, sk, 0)) + failed = true; + + bpf_sk_release(sk); + return failed ? SK_DROP : SK_PASS; +} + +char _license[] SEC("license") = "GPL";
Add a test which copies a socket from a sockmap into another sockmap or sockhash. This excercises bpf_map_update_elem support from BPF context. Compare the socket cookies from source and destination to ensure that the copy succeeded. Also check that the verifier rejects map_update from unsafe contexts. Signed-off-by: Lorenz Bauer <lmb@cloudflare.com> --- .../selftests/bpf/prog_tests/sockmap_basic.c | 78 +++++++++++++++++++ .../bpf/progs/test_sockmap_invalid_update.c | 23 ++++++ .../selftests/bpf/progs/test_sockmap_update.c | 48 ++++++++++++ 3 files changed, 149 insertions(+) create mode 100644 tools/testing/selftests/bpf/progs/test_sockmap_invalid_update.c create mode 100644 tools/testing/selftests/bpf/progs/test_sockmap_update.c