Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 105 additions & 38 deletions tools/testing/selftests/bpf/prog_tests/sockmap_redir.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -144,21 +152,19 @@ 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,
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;
Expand All @@ -174,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;
}
Expand All @@ -187,19 +193,34 @@ 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 */
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);

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,
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";
Expand All @@ -225,14 +246,14 @@ static void test_send_redir_recv(int sd_send, int send_flags, int sd_peer,
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;
}

Expand All @@ -257,15 +278,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;
}
Expand Down Expand Up @@ -316,17 +335,18 @@ 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;
}

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;
Expand Down Expand Up @@ -356,12 +376,47 @@ 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_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)
{
struct socket_spec *s, sockets[] = {
{ AF_INET, SOCK_STREAM },
Expand All @@ -382,9 +437,11 @@ static void test_redir(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_socket(type, redir, maps, s, s);
test_redir(type, redir, maps, s, s);

/* Cross-proto */
for (int i = 0; i < ARRAY_SIZE(sockets); i++) {
Expand All @@ -398,9 +455,20 @@ 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);
}
}

/* 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);
Expand Down Expand Up @@ -441,15 +509,14 @@ 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);

if (xbpf_prog_attach(prog_fd, maps.in, attach_type, 0))
return;

test_redir(type, r, &maps);
test_sockets(type, r, &maps, &skel->bss->redirect_type);

if (xbpf_prog_detach2(prog_fd, maps.in, attach_type))
return;
Expand Down
Loading