From: Mahe Tardy <mahe.tardy@gmail.com>
To: bpf@vger.kernel.org
Cc: andrii@kernel.org, ast@kernel.org, daniel@iogearbox.net,
davem@davemloft.net, willemb@google.com, eddyz87@gmail.com,
edumazet@google.com, john.fastabend@gmail.com, kuba@kernel.org,
horms@kernel.org, liamwisehart@meta.com, martin.lau@linux.dev,
pabeni@redhat.com, song@kernel.org, netdev@vger.kernel.org,
sdf.kernel@gmail.com, yonghong.song@linux.dev, jolsa@kernel.org,
kuniyu@google.com, memxor@gmail.com, emil@etsalapatis.com,
almagui@kernel.org, Mahe Tardy <mahe.tardy@gmail.com>
Subject: [PATCH bpf-next 8/8] selftests/bpf: Test ksock sendto()
Date: Tue, 20 Oct 2026 10:25:12 +0000 [thread overview]
Message-ID: <20261020102512.518479-9-mahe.tardy@gmail.com> (raw)
In-Reply-To: <20261020102512.518479-1-mahe.tardy@gmail.com>
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>
---
.../selftests/bpf/prog_tests/ksock_sendto.c | 108 ++++++++++++++++++
.../selftests/bpf/progs/ksock_common.h | 2 +
.../selftests/bpf/progs/ksock_sendto.c | 59 ++++++++++
3 files changed, 169 insertions(+)
create mode 100644 tools/testing/selftests/bpf/prog_tests/ksock_sendto.c
create mode 100644 tools/testing/selftests/bpf/progs/ksock_sendto.c
diff --git a/tools/testing/selftests/bpf/prog_tests/ksock_sendto.c b/tools/testing/selftests/bpf/prog_tests/ksock_sendto.c
new file mode 100644
index 000000000000..498f0e1be6ee
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/ksock_sendto.c
@@ -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]);
+ }
+}
diff --git a/tools/testing/selftests/bpf/progs/ksock_common.h b/tools/testing/selftests/bpf/progs/ksock_common.h
index 01edaeb9fdd4..2a32cff2fbe9 100644
--- a/tools/testing/selftests/bpf/progs/ksock_common.h
+++ b/tools/testing/selftests/bpf/progs/ksock_common.h
@@ -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;
diff --git a/tools/testing/selftests/bpf/progs/ksock_sendto.c b/tools/testing/selftests/bpf/progs/ksock_sendto.c
new file mode 100644
index 000000000000..b6eacf277fcb
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/ksock_sendto.c
@@ -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";
--
2.43.0
next prev parent reply other threads:[~2026-10-20 10:25 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-20 10:25 [PATCH bpf-next 0/8] bpf: Expand ksock send-path support Mahe Tardy
2026-10-20 10:25 ` [PATCH bpf-next 1/8] bpf: improve ksock_kfunc_set LSM recursion filter Mahe Tardy
2026-10-20 10:25 ` [PATCH bpf-next 2/8] selftests/bpf: Test allowed bpf_ksock operations at send LSM attach Mahe Tardy
2026-10-20 10:52 ` bot+bpf-ci
2026-10-20 11:06 ` Mahe Tardy
2026-10-20 10:25 ` [PATCH bpf-next 3/8] bpf: move ksock_connect() out of init kfunc set Mahe Tardy
2026-10-20 10:25 ` [PATCH bpf-next 4/8] selftests/bpf: Test verifier filter for ksock connect on LSM attach Mahe Tardy
2026-10-20 10:25 ` [PATCH bpf-next 5/8] selftests/bpf: Test ksock connect/send combinations Mahe Tardy
2026-10-20 10:25 ` [PATCH bpf-next 6/8] bpf: add bpf_ksock_sendto() kfunc Mahe Tardy
2026-10-20 10:25 ` [PATCH bpf-next 7/8] selftests/bpf: Test forbidden bpf_ksock_sendto() LSM attach Mahe Tardy
2026-10-20 10:52 ` bot+bpf-ci
2026-10-20 11:08 ` Mahe Tardy
2026-10-20 10:25 ` Mahe Tardy [this message]
2026-10-20 15:07 ` [PATCH bpf-next 0/8] bpf: Expand ksock send-path support Alexei Starovoitov
2026-10-20 15:47 ` Mahe Tardy
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261020102512.518479-9-mahe.tardy@gmail.com \
--to=mahe.tardy@gmail.com \
--cc=almagui@kernel.org \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=davem@davemloft.net \
--cc=eddyz87@gmail.com \
--cc=edumazet@google.com \
--cc=emil@etsalapatis.com \
--cc=horms@kernel.org \
--cc=john.fastabend@gmail.com \
--cc=jolsa@kernel.org \
--cc=kuba@kernel.org \
--cc=kuniyu@google.com \
--cc=liamwisehart@meta.com \
--cc=martin.lau@linux.dev \
--cc=memxor@gmail.com \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=sdf.kernel@gmail.com \
--cc=song@kernel.org \
--cc=willemb@google.com \
--cc=yonghong.song@linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.