mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
selftests/bpf: Test ksock sendto()
These tests are covering that: 1. sendto() on a unconnected UDP socket works (and that the socket stays unconnected, i.e. send() on it fails with -EDESTADDRREQ). 2. sendto() on a connected UDP socket with a NULL address is equivalent to send(). 3. sendto() on a connected socket with an explicit address doesn't change the peer of the socket (send() after sendto() still sends to the original connected address). Signed-off-by: Mahe Tardy <mahe.tardy@gmail.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://patch.msgid.link/20261020155848.75711-3-mahe.tardy@gmail.com
This commit is contained in:
parent
74be619845
commit
7d563ae3ed
108
tools/testing/selftests/bpf/prog_tests/ksock_sendto.c
Normal file
108
tools/testing/selftests/bpf/prog_tests/ksock_sendto.c
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2026 Isovalent */
|
||||
|
||||
#include "test_progs.h"
|
||||
#include "network_helpers.h"
|
||||
#include "ksock_sendto.skel.h"
|
||||
|
||||
struct sendto_test_case {
|
||||
const char *name;
|
||||
bool connected;
|
||||
bool use_sendto_addr;
|
||||
};
|
||||
|
||||
static bool recv_payload(int fd, const void *expected, size_t len)
|
||||
{
|
||||
char buf[64];
|
||||
ssize_t n;
|
||||
|
||||
n = recv(fd, buf, sizeof(buf), MSG_TRUNC);
|
||||
if (!ASSERT_EQ(n, len, "recv length"))
|
||||
return false;
|
||||
return ASSERT_MEMEQ(buf, expected, len, "recv payload");
|
||||
}
|
||||
|
||||
static void test_sendto_case(const struct sendto_test_case *test)
|
||||
{
|
||||
LIBBPF_OPTS(bpf_test_run_opts, opts);
|
||||
struct ksock_sendto *skel;
|
||||
int receivers[2] = { -1, -1 };
|
||||
int err, i, port, sendto_fd;
|
||||
char buf[64];
|
||||
ssize_t n;
|
||||
|
||||
skel = ksock_sendto__open_and_load();
|
||||
if (!ASSERT_OK_PTR(skel, "ksock_sendto open and load"))
|
||||
return;
|
||||
|
||||
/* Use separate receivers for the default peer and explicit destination. */
|
||||
for (i = 0; i < ARRAY_SIZE(receivers); i++) {
|
||||
receivers[i] = start_server(AF_INET, SOCK_DGRAM, "127.0.0.1", 0, 1000);
|
||||
if (!ASSERT_OK_FD(receivers[i], "receiver socket"))
|
||||
goto out;
|
||||
}
|
||||
|
||||
port = get_socket_local_port(receivers[0]);
|
||||
if (!ASSERT_GE(port, 0, "connected port"))
|
||||
goto out;
|
||||
skel->bss->connected_port = port;
|
||||
port = get_socket_local_port(receivers[1]);
|
||||
if (!ASSERT_GE(port, 0, "sendto port"))
|
||||
goto out;
|
||||
skel->bss->sendto_port = port;
|
||||
skel->bss->connected = test->connected;
|
||||
skel->bss->use_sendto_addr = test->use_sendto_addr;
|
||||
|
||||
err = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.test_sendto), &opts);
|
||||
if (!ASSERT_OK(err, "run test_sendto") || !ASSERT_OK(opts.retval, "test_sendto retval"))
|
||||
goto out;
|
||||
|
||||
/* Check sendto() outcome */
|
||||
if (!ASSERT_EQ(skel->data->sendto_ret, sizeof(skel->data->sendto_data), "sendto bytes"))
|
||||
goto out;
|
||||
sendto_fd = receivers[test->use_sendto_addr ? 1 : 0];
|
||||
if (!recv_payload(sendto_fd, skel->data->sendto_data, sizeof(skel->data->sendto_data)))
|
||||
goto out;
|
||||
|
||||
/* Check send() outcome */
|
||||
if (test->connected) {
|
||||
if (!ASSERT_EQ(skel->data->send_ret, sizeof(skel->data->send_data), "send bytes"))
|
||||
goto out;
|
||||
if (!recv_payload(receivers[0], skel->data->send_data,
|
||||
sizeof(skel->data->send_data)))
|
||||
goto out;
|
||||
} else if (!ASSERT_EQ(skel->data->send_ret, -EDESTADDRREQ,
|
||||
"send after sendto remains unconnected")) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Catch unexpected deliveries to either destination. */
|
||||
for (i = 0; i < ARRAY_SIZE(receivers); i++) {
|
||||
n = recv(receivers[i], buf, sizeof(buf), MSG_DONTWAIT);
|
||||
if (!ASSERT_EQ(n, -1, "no extra datagram") ||
|
||||
!ASSERT_EQ(errno, EAGAIN, "recv errno"))
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
for (i = 0; i < ARRAY_SIZE(receivers); i++) {
|
||||
if (receivers[i] >= 0)
|
||||
close(receivers[i]);
|
||||
}
|
||||
ksock_sendto__destroy(skel);
|
||||
}
|
||||
|
||||
void test_ksock_sendto(void)
|
||||
{
|
||||
static const struct sendto_test_case tests[] = {
|
||||
{ .name = "unconnected", .use_sendto_addr = true },
|
||||
{ .name = "connected_null_addr", .connected = true },
|
||||
{ .name = "connected_explicit_addr", .connected = true, .use_sendto_addr = true },
|
||||
};
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tests); i++) {
|
||||
if (test__start_subtest(tests[i].name))
|
||||
test_sendto_case(&tests[i]);
|
||||
}
|
||||
}
|
||||
|
|
@ -16,6 +16,8 @@ int bpf_ksock_connect(struct bpf_ksock *ks, const union bpf_ksock_addr *addr,
|
|||
struct bpf_ksock *bpf_ksock_acquire(struct bpf_ksock *ks) __ksym;
|
||||
void bpf_ksock_release(struct bpf_ksock *ks) __ksym;
|
||||
int bpf_ksock_send(struct bpf_ksock *ks, const void *data, u32 data__sz) __ksym;
|
||||
int bpf_ksock_sendto(struct bpf_ksock *ks, const void *data, u32 data__sz,
|
||||
const union bpf_ksock_addr *addr__nullable, u32 addr__sz) __ksym;
|
||||
void bpf_rcu_read_lock(void) __ksym;
|
||||
void bpf_rcu_read_unlock(void) __ksym;
|
||||
|
||||
|
|
|
|||
59
tools/testing/selftests/bpf/progs/ksock_sendto.c
Normal file
59
tools/testing/selftests/bpf/progs/ksock_sendto.c
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2026 Isovalent */
|
||||
|
||||
#include "vmlinux.h"
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_endian.h>
|
||||
#include "bpf_tracing_net.h"
|
||||
#include "ksock_common.h"
|
||||
|
||||
char sendto_data[] = "hello from bpf sendto";
|
||||
char send_data[] = "hello from bpf send";
|
||||
|
||||
__be16 connected_port;
|
||||
__be16 sendto_port;
|
||||
bool connected;
|
||||
bool use_sendto_addr;
|
||||
int sendto_ret = -1;
|
||||
int send_ret = -1;
|
||||
|
||||
SEC("syscall")
|
||||
int test_sendto(void *ctx)
|
||||
{
|
||||
struct bpf_ksock_create_opts opts = {
|
||||
.family = AF_INET,
|
||||
.type = SOCK_DGRAM,
|
||||
.protocol = IPPROTO_UDP,
|
||||
};
|
||||
union bpf_ksock_addr addr = {};
|
||||
struct bpf_ksock *ks;
|
||||
int err = 0;
|
||||
|
||||
ks = bpf_ksock_create(&opts, sizeof(opts), &err);
|
||||
if (!ks)
|
||||
return err;
|
||||
|
||||
addr.sin.sin_family = AF_INET;
|
||||
addr.sin.sin_addr.s_addr = bpf_htonl(0x7f000001);
|
||||
if (connected) {
|
||||
addr.sin.sin_port = connected_port;
|
||||
err = bpf_ksock_connect(ks, &addr, sizeof(addr));
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
addr.sin.sin_port = sendto_port;
|
||||
if (use_sendto_addr)
|
||||
sendto_ret = bpf_ksock_sendto(ks, sendto_data, sizeof(sendto_data),
|
||||
&addr, sizeof(addr));
|
||||
else
|
||||
sendto_ret = bpf_ksock_sendto(ks, sendto_data, sizeof(sendto_data), NULL, 0);
|
||||
|
||||
/* sendto() must neither connect the socket nor change its default peer. */
|
||||
send_ret = bpf_ksock_send(ks, send_data, sizeof(send_data));
|
||||
out:
|
||||
bpf_ksock_release(ks);
|
||||
return err;
|
||||
}
|
||||
|
||||
char __license[] SEC("license") = "GPL";
|
||||
Loading…
Reference in New Issue
Block a user