* [PATCH bpf-next v2 1/3] bpf: add bpf_ksock_sendto() kfunc
2026-10-20 15:58 [PATCH bpf-next v2 0/3] bpf: Add bpf_ksock_sendto() kfunc Mahe Tardy
@ 2026-10-20 15:58 ` Mahe Tardy
2026-10-20 15:58 ` [PATCH bpf-next v2 2/3] selftests/bpf: Test ksock sendto() Mahe Tardy
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Mahe Tardy @ 2026-10-20 15:58 UTC (permalink / raw)
To: bpf
Cc: andrii, ast, daniel, davem, willemb, eddyz87, edumazet,
john.fastabend, kuba, horms, liamwisehart, martin.lau, pabeni,
song, netdev, sdf.kernel, yonghong.song, jolsa, kuniyu, memxor,
emil, almagui, Mahe Tardy
The motivation is to plumb more of the socket API into ksock, thus
supporting sendto() in addition to send() which makes a lot of sense in
the context of UDP sockets which are the only type currently supported.
This is useful when ksock is used to reply to specific addresses or send
to multiple addresses.
Signed-off-by: Mahe Tardy <mahe.tardy@gmail.com>
---
net/core/bpf_ksock.c | 96 +++++++++++++++++++++++++++++++-------------
1 file changed, 69 insertions(+), 27 deletions(-)
diff --git a/net/core/bpf_ksock.c b/net/core/bpf_ksock.c
index 48a2e2327ace..37168f43c16c 100644
--- a/net/core/bpf_ksock.c
+++ b/net/core/bpf_ksock.c
@@ -37,6 +37,49 @@ static void ksock_release_work_fn(struct work_struct *work)
kfree(ks);
}
+static int bpf_ksock_addr_to_sockaddr(const union bpf_ksock_addr *addr,
+ u32 addr_sz, struct sockaddr_storage *sa)
+{
+ if (!addr || addr_sz != sizeof(*addr))
+ return -EINVAL;
+
+ /* Kfunc memory arguments may be unaligned. */
+ memcpy(sa, addr, sizeof(*addr));
+
+ switch (sa->ss_family) {
+ case AF_INET:
+ return sizeof(struct sockaddr_in);
+ case AF_INET6:
+ return sizeof(struct sockaddr_in6);
+ default:
+ return -EAFNOSUPPORT;
+ }
+}
+
+static int ksock_sendto(struct bpf_ksock *ks, const void *data, u32 data_sz,
+ const union bpf_ksock_addr *addr, u32 addr_sz)
+{
+ struct sockaddr_storage sa;
+ struct msghdr msg = {
+ .msg_flags = MSG_DONTWAIT,
+ };
+ struct kvec iov = {
+ .iov_base = (void *)data,
+ .iov_len = data_sz,
+ };
+ int addrlen;
+
+ if (addr) {
+ addrlen = bpf_ksock_addr_to_sockaddr(addr, addr_sz, &sa);
+ if (addrlen < 0)
+ return addrlen;
+ msg.msg_name = &sa;
+ msg.msg_namelen = addrlen;
+ }
+
+ return kernel_sendmsg(ks->sock, &msg, &iov, 1, data_sz);
+}
+
__bpf_kfunc_start_defs();
/**
@@ -155,22 +198,9 @@ __bpf_kfunc int bpf_ksock_connect(struct bpf_ksock *ks,
struct sockaddr_storage sa;
int addrlen;
- if (!addr || addr__sz != sizeof(*addr))
- return -EINVAL;
-
- /* Kfunc memory arguments may be unaligned. */
- memcpy(&sa, addr, sizeof(*addr));
-
- switch (sa.ss_family) {
- case AF_INET:
- addrlen = sizeof(struct sockaddr_in);
- break;
- case AF_INET6:
- addrlen = sizeof(struct sockaddr_in6);
- break;
- default:
- return -EAFNOSUPPORT;
- }
+ addrlen = bpf_ksock_addr_to_sockaddr(addr, addr__sz, &sa);
+ if (addrlen < 0)
+ return addrlen;
return connect_socket(ks->sock, &sa, addrlen, 0);
}
@@ -225,18 +255,29 @@ CFI_NOSEAL(bpf_ksock_release_dtor);
__bpf_kfunc int bpf_ksock_send(struct bpf_ksock *ks, const void *data,
u32 data__sz)
{
- struct msghdr msg = {
- .msg_flags = MSG_DONTWAIT,
- };
- struct kvec iov = {
- .iov_base = (void *)data,
- .iov_len = data__sz,
- };
- int ret;
-
- ret = kernel_sendmsg(ks->sock, &msg, &iov, 1, data__sz);
+ return ksock_sendto(ks, data, data__sz, NULL, 0);
+}
- return ret;
+/**
+ * bpf_ksock_sendto() - Send data through a BPF kernel socket to a remote address.
+ * @ks: The BPF kernel socket context. Must be an acquired reference.
+ * @data: Pointer to the data to send.
+ * @data__sz: Size of the data to send.
+ * @addr__nullable: Pointer to an IPv4 or IPv6 socket address, or NULL to use
+ * the connected destination.
+ * @addr__sz: Size of the address union, ignored when the address is NULL.
+ *
+ * Sends data to the specified address, or to the connected destination when
+ * the address is NULL, best-effort and nonblocking. This may sleep
+ * (kernel_sendmsg), so it can only be called from sleepable BPF programs.
+ *
+ * Return: Number of bytes sent on success, negative errno on error.
+ */
+__bpf_kfunc int bpf_ksock_sendto(struct bpf_ksock *ks, const void *data, u32 data__sz,
+ const union bpf_ksock_addr *addr__nullable,
+ u32 addr__sz)
+{
+ return ksock_sendto(ks, data, data__sz, addr__nullable, addr__sz);
}
__bpf_kfunc_end_defs();
@@ -255,6 +296,7 @@ BTF_KFUNCS_START(ksock_kfunc_btf_ids)
BTF_ID_FLAGS(func, bpf_ksock_release, KF_RELEASE)
BTF_ID_FLAGS(func, bpf_ksock_acquire, KF_ACQUIRE | KF_RCU | KF_RET_NULL)
BTF_ID_FLAGS(func, bpf_ksock_send, KF_SLEEPABLE)
+BTF_ID_FLAGS(func, bpf_ksock_sendto, KF_SLEEPABLE)
BTF_KFUNCS_END(ksock_kfunc_btf_ids)
#ifdef CONFIG_BPF_LSM
--
2.43.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH bpf-next v2 2/3] selftests/bpf: Test ksock sendto()
2026-10-20 15:58 [PATCH bpf-next v2 0/3] bpf: Add bpf_ksock_sendto() kfunc Mahe Tardy
2026-10-20 15:58 ` [PATCH bpf-next v2 1/3] bpf: add " Mahe Tardy
@ 2026-10-20 15:58 ` Mahe Tardy
2026-10-20 15:58 ` [PATCH bpf-next v2 3/3] selftests/bpf: Test forbidden bpf_ksock_sendto() LSM attach Mahe Tardy
2026-10-20 21:00 ` [PATCH bpf-next v2 0/3] bpf: Add bpf_ksock_sendto() kfunc patchwork-bot+netdevbpf
3 siblings, 0 replies; 5+ messages in thread
From: Mahe Tardy @ 2026-10-20 15:58 UTC (permalink / raw)
To: bpf
Cc: andrii, ast, daniel, davem, willemb, eddyz87, edumazet,
john.fastabend, kuba, horms, liamwisehart, martin.lau, pabeni,
song, netdev, sdf.kernel, yonghong.song, jolsa, kuniyu, memxor,
emil, almagui, Mahe Tardy
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
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH bpf-next v2 3/3] selftests/bpf: Test forbidden bpf_ksock_sendto() LSM attach
2026-10-20 15:58 [PATCH bpf-next v2 0/3] bpf: Add bpf_ksock_sendto() kfunc Mahe Tardy
2026-10-20 15:58 ` [PATCH bpf-next v2 1/3] bpf: add " Mahe Tardy
2026-10-20 15:58 ` [PATCH bpf-next v2 2/3] selftests/bpf: Test ksock sendto() Mahe Tardy
@ 2026-10-20 15:58 ` Mahe Tardy
2026-10-20 21:00 ` [PATCH bpf-next v2 0/3] bpf: Add bpf_ksock_sendto() kfunc patchwork-bot+netdevbpf
3 siblings, 0 replies; 5+ messages in thread
From: Mahe Tardy @ 2026-10-20 15:58 UTC (permalink / raw)
To: bpf
Cc: andrii, ast, daniel, davem, willemb, eddyz87, edumazet,
john.fastabend, kuba, horms, liamwisehart, martin.lau, pabeni,
song, netdev, sdf.kernel, yonghong.song, jolsa, kuniyu, memxor,
emil, almagui, Mahe Tardy
Similarly to '7b0dfbf5776b ("selftests/bpf: Test forbidden
bpf_ksock_send() LSM attach")', tests that the verifier filter prevents
bpf_ksock_sendto() to be called in a LSM program attached to
security_socket_sendmsg() as it would create a direct recursion.
Signed-off-by: Mahe Tardy <mahe.tardy@gmail.com>
---
.../selftests/bpf/progs/ksock_lsm_verifier.c | 25 ++++++++++++++++++-
1 file changed, 24 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/bpf/progs/ksock_lsm_verifier.c b/tools/testing/selftests/bpf/progs/ksock_lsm_verifier.c
index fd2ccfdb5802..9533570eb340 100644
--- a/tools/testing/selftests/bpf/progs/ksock_lsm_verifier.c
+++ b/tools/testing/selftests/bpf/progs/ksock_lsm_verifier.c
@@ -12,7 +12,7 @@ char send_data[11] = "dummy data";
SEC("lsm.s/socket_sendmsg")
__description("bpf_ksock_send is rejected from socket_sendmsg LSM hook")
__failure __msg("calling kernel function bpf_ksock_send is not allowed")
-int BPF_PROG(ksock_socket_sendmsg, struct socket *sock, struct msghdr *msg,
+int BPF_PROG(socket_sendmsg_ksock_send, struct socket *sock, struct msghdr *msg,
int size, int ret)
{
struct __ksock_ctx_value *v;
@@ -32,4 +32,27 @@ int BPF_PROG(ksock_socket_sendmsg, struct socket *sock, struct msghdr *msg,
return ret;
}
+SEC("lsm.s/socket_sendmsg")
+__description("bpf_ksock_sendto is rejected from socket_sendmsg LSM hook")
+__failure __msg("calling kernel function bpf_ksock_sendto is not allowed")
+int BPF_PROG(socket_sendmsg_ksock_sendto, struct socket *sock,
+ struct msghdr *msg, int size, int ret)
+{
+ struct __ksock_ctx_value *v;
+ struct bpf_ksock *ks;
+
+ v = ksock_ctx_value_lookup();
+ if (!v)
+ return ret;
+
+ ks = bpf_kptr_xchg(&v->ctx, NULL);
+ if (!ks)
+ return ret;
+
+ bpf_ksock_sendto(ks, send_data, sizeof(send_data), NULL, 0);
+ bpf_ksock_release(ks);
+
+ return ret;
+}
+
char __license[] SEC("license") = "GPL";
--
2.43.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* Re: [PATCH bpf-next v2 0/3] bpf: Add bpf_ksock_sendto() kfunc
2026-10-20 15:58 [PATCH bpf-next v2 0/3] bpf: Add bpf_ksock_sendto() kfunc Mahe Tardy
` (2 preceding siblings ...)
2026-10-20 15:58 ` [PATCH bpf-next v2 3/3] selftests/bpf: Test forbidden bpf_ksock_sendto() LSM attach Mahe Tardy
@ 2026-10-20 21:00 ` patchwork-bot+netdevbpf
3 siblings, 0 replies; 5+ messages in thread
From: patchwork-bot+netdevbpf @ 2026-10-20 21:00 UTC (permalink / raw)
To: Mahe Tardy
Cc: bpf, andrii, ast, daniel, davem, willemb, eddyz87, edumazet,
john.fastabend, kuba, horms, liamwisehart, martin.lau, pabeni,
song, netdev, sdf.kernel, yonghong.song, jolsa, kuniyu, memxor,
emil, almagui
Hello:
This series was applied to bpf/bpf-next.git (master)
by Alexei Starovoitov <ast@kernel.org>:
On Tue, 20 Oct 2026 15:58:45 +0000 you wrote:
> This patch series improves the bpf_ksock infrastructure in line with the
> work proposed during Prague Plumbers 2026 presentation[^1] by adding
> the bpf_ksock_sendto() kfunc to complement the bpf_ksock_send().
>
> This is useful when using ksock to reply to differents peers while
> reusing the same UDP socket and generally works toward the goal of ksock
> to support the basic socket operations.
>
> [...]
Here is the summary with links:
- [bpf-next,v2,1/3] bpf: add bpf_ksock_sendto() kfunc
https://git.kernel.org/bpf/bpf-next/c/74be619845bd
- [bpf-next,v2,2/3] selftests/bpf: Test ksock sendto()
https://git.kernel.org/bpf/bpf-next/c/7d563ae3ed74
- [bpf-next,v2,3/3] selftests/bpf: Test forbidden bpf_ksock_sendto() LSM attach
https://git.kernel.org/bpf/bpf-next/c/d90912846f26
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply [flat|nested] 5+ messages in thread