try_recv() was meant to support both @expect_success cases, but all the callers use @expect_success=false anyway. Drop the unused logic and fold in MSG_DONTWAIT. Adapt callers. Subtle change here: recv() return value of 0 will also be considered (an unexpected) success. Signed-off-by: Michal Luczaj --- .../selftests/bpf/prog_tests/sockmap_redir.c | 25 +++++++++------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_redir.c b/tools/testing/selftests/bpf/prog_tests/sockmap_redir.c index 9c461d93113db20de65ac353f92dfdbe32ffbd3b..c1bf1076e8152b7d83c3e07e2dce746b5a39cf7e 100644 --- a/tools/testing/selftests/bpf/prog_tests/sockmap_redir.c +++ b/tools/testing/selftests/bpf/prog_tests/sockmap_redir.c @@ -144,17 +144,14 @@ static void get_redir_params(struct redir_spec *redir, *redirect_flags = 0; } -static void try_recv(const char *prefix, int fd, int flags, bool expect_success) +static void fail_recv(const char *prefix, int fd, int more_flags) { ssize_t n; char buf; - errno = 0; - n = recv(fd, &buf, 1, flags); - if (n < 0 && expect_success) - FAIL_ERRNO("%s: unexpected failure: retval=%zd", prefix, n); - if (!n && !expect_success) - FAIL("%s: expected failure: retval=%zd", prefix, n); + n = recv(fd, &buf, 1, MSG_DONTWAIT | more_flags); + if (n >= 0) + FAIL("%s: unexpected success: retval=%zd", prefix, n); } static void handle_unsupported(int sd_send, int sd_peer, int sd_in, int sd_out, @@ -188,13 +185,13 @@ static void handle_unsupported(int sd_send, int sd_peer, int sd_in, int sd_out, } /* Ensure queues are empty */ - try_recv("bpf.recv(sd_send)", sd_send, MSG_DONTWAIT, false); + fail_recv("bpf.recv(sd_send)", sd_send, 0); if (sd_in != sd_send) - try_recv("bpf.recv(sd_in)", sd_in, MSG_DONTWAIT, false); + fail_recv("bpf.recv(sd_in)", sd_in, 0); - try_recv("bpf.recv(sd_out)", sd_out, MSG_DONTWAIT, false); + fail_recv("bpf.recv(sd_out)", sd_out, 0); if (sd_recv != sd_out) - try_recv("bpf.recv(sd_recv)", sd_recv, MSG_DONTWAIT, false); + fail_recv("bpf.recv(sd_recv)", sd_recv, 0); } static void test_send_redir_recv(int sd_send, int send_flags, int sd_peer, @@ -257,15 +254,13 @@ static void test_send_redir_recv(int sd_send, int send_flags, int sd_peer, if (send_flags & MSG_OOB) { /* Fail reading OOB while in sockmap */ - try_recv("bpf.recv(sd_out, MSG_OOB)", sd_out, - MSG_OOB | MSG_DONTWAIT, false); + fail_recv("bpf.recv(sd_out, MSG_OOB)", sd_out, MSG_OOB); /* Remove sd_out from sockmap */ xbpf_map_delete_elem(maps->out, &u32(0)); /* Check that OOB was dropped on redirect */ - try_recv("recv(sd_out, MSG_OOB)", sd_out, - MSG_OOB | MSG_DONTWAIT, false); + fail_recv("recv(sd_out, MSG_OOB)", sd_out, MSG_OOB); goto del_in; } -- 2.50.1 In some test cases, OOB packets might have been left unread. Flush them out and introduce additional checks. Signed-off-by: Michal Luczaj --- tools/testing/selftests/bpf/prog_tests/sockmap_redir.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_redir.c b/tools/testing/selftests/bpf/prog_tests/sockmap_redir.c index c1bf1076e8152b7d83c3e07e2dce746b5a39cf7e..4997e72c14345b274367f3f2f4115c39d1ae48c9 100644 --- a/tools/testing/selftests/bpf/prog_tests/sockmap_redir.c +++ b/tools/testing/selftests/bpf/prog_tests/sockmap_redir.c @@ -184,6 +184,19 @@ static void handle_unsupported(int sd_send, int sd_peer, int sd_in, int sd_out, FAIL_ERRNO("unsupported: packet missing, retval=%zd", n); } + /* af_unix send("ab", MSG_OOB) spits out 2 packets, but only the latter + * ("b") is designated OOB. If the peer is in a sockmap, the OOB packet + * will be silently dropped. Otherwise OOB stays in the queue and should + * be taken care of. + */ + if ((send_flags & MSG_OOB) && !pass && !drop) { + errno = 0; + n = recv_timeout(sd_peer, &recv_buf, 1, MSG_OOB, IO_TIMEOUT_SEC); + /* Ignore unsupported sk_msg error */ + if (n != 1 && errno != EOPNOTSUPP) + FAIL_ERRNO("recv(OOB): retval=%zd", n); + } + /* Ensure queues are empty */ fail_recv("bpf.recv(sd_send)", sd_send, 0); if (sd_in != sd_send) @@ -192,6 +205,9 @@ static void handle_unsupported(int sd_send, int sd_peer, int sd_in, int sd_out, fail_recv("bpf.recv(sd_out)", sd_out, 0); if (sd_recv != sd_out) fail_recv("bpf.recv(sd_recv)", sd_recv, 0); + + fail_recv("recv(sd_peer, OOB)", sd_peer, MSG_OOB); + fail_recv("recv(sd_out, OOB)", sd_out, MSG_OOB); } static void test_send_redir_recv(int sd_send, int send_flags, int sd_peer, -- 2.50.1 Preparatory patch before adding SK_PASS/SK_DROP support: test_redir() => test_sockets() test_socket() => test_redir() test_send_redir_recv() => test_send_recv() After the change (and the following patch) the call stack will be: serial_test_sockmap_redir test_map test_sockets test_redir test_send_recv (test_verdict) (test_send_recv) Signed-off-by: Michal Luczaj --- .../selftests/bpf/prog_tests/sockmap_redir.c | 25 +++++++++++----------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_redir.c b/tools/testing/selftests/bpf/prog_tests/sockmap_redir.c index 4997e72c14345b274367f3f2f4115c39d1ae48c9..9fc5b4dd323d8554e6dc89d06be9054612e41020 100644 --- a/tools/testing/selftests/bpf/prog_tests/sockmap_redir.c +++ b/tools/testing/selftests/bpf/prog_tests/sockmap_redir.c @@ -210,9 +210,8 @@ static void handle_unsupported(int sd_send, int sd_peer, int sd_in, int sd_out, fail_recv("recv(sd_out, OOB)", sd_out, MSG_OOB); } -static void test_send_redir_recv(int sd_send, int send_flags, int sd_peer, - int sd_in, int sd_out, int sd_recv, - struct maps *maps, int status) +static void test_send_recv(int sd_send, int send_flags, int sd_peer, int sd_in, + int sd_out, int sd_recv, struct maps *maps, int status) { unsigned int drop, pass; char *send_buf = "ab"; @@ -335,9 +334,9 @@ static int get_support_status(enum prog_type type, const char *in, return status; } -static void test_socket(enum bpf_map_type type, struct redir_spec *redir, - struct maps *maps, struct socket_spec *s_in, - struct socket_spec *s_out) +static void test_redir(enum bpf_map_type type, struct redir_spec *redir, + struct maps *maps, struct socket_spec *s_in, + struct socket_spec *s_out) { int fd_in, fd_out, fd_send, fd_peer, fd_recv, flags, status; const char *in_str, *out_str; @@ -367,12 +366,12 @@ static void test_socket(enum bpf_map_type type, struct redir_spec *redir, if (!test__start_subtest(s)) return; - test_send_redir_recv(fd_send, flags, fd_peer, fd_in, fd_out, fd_recv, - maps, status); + test_send_recv(fd_send, flags, fd_peer, fd_in, fd_out, fd_recv, maps, + status); } -static void test_redir(enum bpf_map_type type, struct redir_spec *redir, - struct maps *maps) +static void test_sockets(enum bpf_map_type type, struct redir_spec *redir, + struct maps *maps) { struct socket_spec *s, sockets[] = { { AF_INET, SOCK_STREAM }, @@ -395,7 +394,7 @@ static void test_redir(enum bpf_map_type type, struct redir_spec *redir, /* Intra-proto */ for (s = sockets; s < sockets + ARRAY_SIZE(sockets); s++) - test_socket(type, redir, maps, s, s); + test_redir(type, redir, maps, s, s); /* Cross-proto */ for (int i = 0; i < ARRAY_SIZE(sockets); i++) { @@ -409,7 +408,7 @@ static void test_redir(enum bpf_map_type type, struct redir_spec *redir, in->sotype == out->sotype)) continue; - test_socket(type, redir, maps, in, out); + test_redir(type, redir, maps, in, out); } } out: @@ -460,7 +459,7 @@ static void test_map(enum bpf_map_type type) if (xbpf_prog_attach(prog_fd, maps.in, attach_type, 0)) return; - test_redir(type, r, &maps); + test_sockets(type, r, &maps); if (xbpf_prog_detach2(prog_fd, maps.in, attach_type)) return; -- 2.50.1 Preparatory patch before adding SK_PASS/SK_DROP support: allow to dynamically switch BPF program's redirect_type. This way, after setting up for a redirection, test can make the BPF program skip the actual bpf_{sk,msg}_redirect_{map,hash} part and return a specified verdict. Signed-off-by: Michal Luczaj --- tools/testing/selftests/bpf/prog_tests/sockmap_redir.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_redir.c b/tools/testing/selftests/bpf/prog_tests/sockmap_redir.c index 9fc5b4dd323d8554e6dc89d06be9054612e41020..f89df1ca1174800d801f221a819b099abf911450 100644 --- a/tools/testing/selftests/bpf/prog_tests/sockmap_redir.c +++ b/tools/testing/selftests/bpf/prog_tests/sockmap_redir.c @@ -371,7 +371,7 @@ static void test_redir(enum bpf_map_type type, struct redir_spec *redir, } static void test_sockets(enum bpf_map_type type, struct redir_spec *redir, - struct maps *maps) + struct maps *maps, int *skel_redir_type) { struct socket_spec *s, sockets[] = { { AF_INET, SOCK_STREAM }, @@ -392,6 +392,8 @@ static void test_sockets(enum bpf_map_type type, struct redir_spec *redir, if (socket_spec_pairs(s)) goto out; + *skel_redir_type = type; + /* Intra-proto */ for (s = sockets; s < sockets + ARRAY_SIZE(sockets); s++) test_redir(type, redir, maps, s, s); @@ -451,7 +453,6 @@ static void test_map(enum bpf_map_type type) return; } - skel->bss->redirect_type = type; maps.verd = bpf_map__fd(skel->maps.verdict_map); get_redir_params(r, skel, &prog_fd, &attach_type, &skel->bss->redirect_flags); @@ -459,7 +460,7 @@ static void test_map(enum bpf_map_type type) if (xbpf_prog_attach(prog_fd, maps.in, attach_type, 0)) return; - test_sockets(type, r, &maps); + test_sockets(type, r, &maps, &skel->bss->redirect_type); if (xbpf_prog_detach2(prog_fd, maps.in, attach_type)) return; -- 2.50.1 Add tests that make the BPF programs skip the actual redirect and immediately return SK_DROP/SK_PASS. Suggested-by: Jiayuan Chen Signed-off-by: Michal Luczaj --- .../selftests/bpf/prog_tests/sockmap_redir.c | 74 +++++++++++++++++++--- 1 file changed, 65 insertions(+), 9 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_redir.c b/tools/testing/selftests/bpf/prog_tests/sockmap_redir.c index f89df1ca1174800d801f221a819b099abf911450..4c752000bbd8c84684b2df81eb389be953d8ba02 100644 --- a/tools/testing/selftests/bpf/prog_tests/sockmap_redir.c +++ b/tools/testing/selftests/bpf/prog_tests/sockmap_redir.c @@ -18,6 +18,10 @@ * AF_UNIX, SOCK_DGRAM * AF_VSOCK, SOCK_STREAM * AF_VSOCK, SOCK_SEQPACKET + * x + * SK_REDIRECT + * SK_DROP + * SK_PASS */ #include @@ -65,6 +69,10 @@ */ #define UNSUPPORTED_RACY_VERD _BITUL(1) +/* Mark for an immediate SK_DROP/SK_PASS, i.e. BPF program will not redirect. + */ +#define NO_REDIRECT _BITUL(2) + enum prog_type { SK_MSG_EGRESS, SK_MSG_INGRESS, @@ -154,8 +162,9 @@ static void fail_recv(const char *prefix, int fd, int more_flags) FAIL("%s: unexpected success: retval=%zd", prefix, n); } -static void handle_unsupported(int sd_send, int sd_peer, int sd_in, int sd_out, - int sd_recv, int map_verd, int status) +static void handle_unsupported(int sd_send, int send_flags, int sd_peer, + int sd_in, int sd_out, int sd_recv, + int map_verd, int status) { unsigned int drop, pass; char recv_buf; @@ -171,7 +180,7 @@ static void handle_unsupported(int sd_send, int sd_peer, int sd_in, int sd_out, goto get_verdict; } - if (pass != 0) { + if (pass && !(status & NO_REDIRECT)) { FAIL("unsupported: wanted verdict pass 0, have %u", pass); return; } @@ -237,14 +246,14 @@ static void test_send_recv(int sd_send, int send_flags, int sd_peer, int sd_in, FAIL("incomplete send"); if (n < 0) { /* sk_msg redirect combo not supported? */ - if (status & SUPPORTED || errno != EACCES) + if (errno != EACCES) FAIL_ERRNO("send"); goto out; } - if (!(status & SUPPORTED)) { - handle_unsupported(sd_send, sd_peer, sd_in, sd_out, sd_recv, - maps->verd, status); + if (!(status & SUPPORTED) || (status & NO_REDIRECT)) { + handle_unsupported(sd_send, send_flags, sd_peer, sd_in, sd_out, + sd_recv, maps->verd, status); goto out; } @@ -326,9 +335,10 @@ static int is_redir_supported(enum prog_type type, const char *in, static int get_support_status(enum prog_type type, const char *in, const char *out) { - int status = is_redir_supported(type, in, out); + int status = in ? is_redir_supported(type, in, out) : 0; - if (type == SK_SKB_INGRESS && strstarts(out, "v_")) + if ((type == SK_SKB_INGRESS || type == SK_SKB_EGRESS) && + strstarts(out, "v_")) status |= UNSUPPORTED_RACY_VERD; return status; @@ -370,6 +380,41 @@ static void test_redir(enum bpf_map_type type, struct redir_spec *redir, status); } +static void test_verdict(enum bpf_map_type type, struct redir_spec *redir, + struct maps *maps, struct socket_spec *s_in, + enum sk_action action) +{ + int fd_in, fd_out, fd_send, fd_peer, fd_recv, flags, status; + char s[MAX_TEST_NAME]; + const char *s_str; + + fd_in = s_in->in[0]; + fd_out = s_in->in[1]; + fd_send = s_in->in[redir->idx_send]; + fd_peer = s_in->in[redir->idx_send ^ 1]; + fd_recv = s_in->in[redir->idx_send ^ 1]; + flags = s_in->send_flags; + + s_str = socket_kind_to_str(fd_in); + status = get_support_status(redir->prog_type, NULL, s_str); + status |= NO_REDIRECT; + + snprintf(s, sizeof(s), + "%-4s %-17s %-7s %-5s%6s", + /* hash sk_skb-to-ingress u_str pass (OOB) */ + type == BPF_MAP_TYPE_SOCKMAP ? "map" : "hash", + redir->name, + s_str, + action == SK_PASS ? "pass" : "drop", + flags & MSG_OOB ? "(OOB)" : ""); + + if (!test__start_subtest(s)) + return; + + test_send_recv(fd_send, flags, fd_peer, fd_in, fd_out, fd_recv, maps, + status); +} + static void test_sockets(enum bpf_map_type type, struct redir_spec *redir, struct maps *maps, int *skel_redir_type) { @@ -413,6 +458,17 @@ static void test_sockets(enum bpf_map_type type, struct redir_spec *redir, test_redir(type, redir, maps, in, out); } } + + /* No redirect: SK_DROP */ + *skel_redir_type = __MAX_BPF_MAP_TYPE + SK_DROP; + for (s = sockets; s < sockets + ARRAY_SIZE(sockets); s++) + test_verdict(type, redir, maps, s, SK_DROP); + + /* No redirect: SK_PASS */ + *skel_redir_type = __MAX_BPF_MAP_TYPE + SK_PASS; + for (s = sockets; s < sockets + ARRAY_SIZE(sockets); s++) + test_verdict(type, redir, maps, s, SK_PASS); + out: while (--s >= sockets) socket_spec_close(s); -- 2.50.1