sockmap_redir was introduced to comprehensively test the BPF redirection. This series strives to increase the tested sockmap/sockhash code coverage; adds support for skipping the actual redirect part, allowing to simply SK_DROP or SK_PASS the packet.
BPF_MAP_TYPE_SOCKMAP BPF_MAP_TYPE_SOCKHASH x sk_msg-to-egress sk_msg-to-ingress sk_skb-to-egress sk_skb-to-ingress x AF_INET, SOCK_STREAM AF_INET6, SOCK_STREAM AF_INET, SOCK_DGRAM AF_INET6, SOCK_DGRAM AF_UNIX, SOCK_STREAM AF_UNIX, SOCK_DGRAM AF_VSOCK, SOCK_STREAM AF_VSOCK, SOCK_SEQPACKET x SK_REDIRECT SK_DROP SK_PASS
Patch 5 ("Support no-redirect SK_DROP/SK_PASS") implements the feature. Patches 3 ("Rename functions") and 4 ("Let test specify skel's redirect_type") make preparatory changes.
I also took the opportunity to clean up (Patch 1, "Simplify try_recv()") and improve a bit (Patch 2, "Fix OOB handling").
$ cd tools/testing/selftests/bpf $ make $ sudo ./test_progs -t sockmap_redir ... Summary: 1/720 PASSED, 0 SKIPPED, 0 FAILED
Signed-off-by: Michal Luczaj mhal@rbox.co --- Michal Luczaj (5): selftests/bpf: sockmap_redir: Simplify try_recv() selftests/bpf: sockmap_redir: Fix OOB handling selftests/bpf: sockmap_redir: Rename functions selftests/bpf: sockmap_redir: Let test specify skel's redirect_type selftests/bpf: sockmap_redir: Support no-redirect SK_DROP/SK_PASS
.../selftests/bpf/prog_tests/sockmap_redir.c | 143 +++++++++++++++------ 1 file changed, 105 insertions(+), 38 deletions(-) --- base-commit: e8a6a9d3e8cc539d281e77b9df2439f223ec8153 change-id: 20250523-redir-test-pass-drop-2f2a5edca6e1
Best regards,
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 mhal@rbox.co --- .../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; }
On Fri, Sep 05, 2025 at 01:11 PM +02, Michal Luczaj wrote:
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 mhal@rbox.co
.../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);
}
This bit, which you highlighted in the description, I don't get.
If we're expecting to receive exactly one byte, why treat a short read as a succcess? Why not make it a strict "n != 1" check?
[...]
On Tue, Sep 09, 2025 at 11:51 AM +02, Jakub Sitnicki wrote:
On Fri, Sep 05, 2025 at 01:11 PM +02, Michal Luczaj wrote:
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 mhal@rbox.co
.../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);
}
This bit, which you highlighted in the description, I don't get.
If we're expecting to receive exactly one byte, why treat a short read as a succcess? Why not make it a strict "n != 1" check?
[...]
Nevermind. It makes sense now. We do want to report a failure for 0-len msg recv as well. You're effectively checking if the rcv queue is empty.
I'd add MSG_PEEK, to signal that we're _just checking_ if the socket is readable, and turn the check into the below to succeed only when queue is empty:
(n != -1 || (errno != EAGAIN && errno != EWOULDBLOCK))
It's a minor thing. Leaving it up to you. Either way:
Reviewed-by: Jakub Sitnicki jakub@cloudflare.com
On 9/9/25 12:15, Jakub Sitnicki wrote:
On Tue, Sep 09, 2025 at 11:51 AM +02, Jakub Sitnicki wrote:
On Fri, Sep 05, 2025 at 01:11 PM +02, Michal Luczaj wrote:
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 mhal@rbox.co
.../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);
}
This bit, which you highlighted in the description, I don't get.
If we're expecting to receive exactly one byte, why treat a short read as a succcess? Why not make it a strict "n != 1" check?
[...]
Nevermind. It makes sense now. We do want to report a failure for 0-len msg recv as well. You're effectively checking if the rcv queue is empty.
I'd add MSG_PEEK, to signal that we're _just checking_ if the socket is readable, and turn the check into the below to succeed only when queue is empty:
(n != -1 || (errno != EAGAIN && errno != EWOULDBLOCK))
Well, looks like adding MSG_PEEK exposed a bug in the test. I'll fix that.
Thanks, Michal
On Tue, Sep 09, 2025 at 11:25 PM +02, Michal Luczaj wrote:
On 9/9/25 12:15, Jakub Sitnicki wrote:
On Tue, Sep 09, 2025 at 11:51 AM +02, Jakub Sitnicki wrote:
On Fri, Sep 05, 2025 at 01:11 PM +02, Michal Luczaj wrote:
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 mhal@rbox.co
.../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);
}
This bit, which you highlighted in the description, I don't get.
If we're expecting to receive exactly one byte, why treat a short read as a succcess? Why not make it a strict "n != 1" check?
[...]
Nevermind. It makes sense now. We do want to report a failure for 0-len msg recv as well. You're effectively checking if the rcv queue is empty.
I'd add MSG_PEEK, to signal that we're _just checking_ if the socket is readable, and turn the check into the below to succeed only when queue is empty:
(n != -1 || (errno != EAGAIN && errno != EWOULDBLOCK))
Well, looks like adding MSG_PEEK exposed a bug in the test. I'll fix that.
The gift that keeps on giving xD
Other alternatives that should also work, but who knows:
- select/poll/epoll readability check - ioctl(SIOCINQ) but no way to tell if 0-len msg is pending
In some test cases, OOB packets might have been left unread. Flush them out and introduce additional checks.
Signed-off-by: Michal Luczaj mhal@rbox.co --- 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,
On 9/5/25 13:11, Michal Luczaj wrote:
In some test cases, OOB packets might have been left unread. Flush them out and introduce additional checks.
Signed-off-by: Michal Luczaj mhal@rbox.co
Sorry, this should also have:
Fixes: f0709263a07e ("selftests/bpf: Add selftest for sockmap/hashmap redirection")
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 mhal@rbox.co --- .../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;
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 mhal@rbox.co --- 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;
Add tests that make the BPF programs skip the actual redirect and immediately return SK_DROP/SK_PASS.
Suggested-by: Jiayuan Chen mrpre@163.com Signed-off-by: Michal Luczaj mhal@rbox.co --- .../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 <errno.h> @@ -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);
linux-kselftest-mirror@lists.linaro.org