From: Henrique Carvalho <henrique.carvalho@suse.com>
To: linux-cifs@vger.kernel.org
Cc: linkinjeon@kernel.org, sfrench@samba.org, metze@samba.org,
senozhatsky@chromium.org, tom@talpey.com, ematsumiya@suse.de,
Henrique Carvalho <henrique.carvalho@suse.com>
Subject: [PATCH v2 08/11] smb: server: add QUIC transport support
Date: Tue, 28 Apr 2026 12:57:57 -0300 [thread overview]
Message-ID: <20260428155759.226368-4-henrique.carvalho@suse.com> (raw)
In-Reply-To: <20260428155759.226368-1-henrique.carvalho@suse.com>
Add a QUIC listener and connection transport for ksmbd on top of the
shared server transport helpers. This wires in the QUIC transport file,
adds the per-interface QUIC socket/kthread state, and enables QUIC
listener startup and teardown alongside the existing TCP path.
Also add the server-side QUIC port configuration and pass the client
peer address through the existing tree connect management path for QUIC
connections.
Signed-off-by: Henrique Carvalho <henrique.carvalho@suse.com>
---
fs/smb/server/Makefile | 4 +-
fs/smb/server/connection.c | 1 +
fs/smb/server/interface.c | 30 ++-
fs/smb/server/interface.h | 2 +
fs/smb/server/server.h | 1 +
fs/smb/server/smb2pdu.c | 1 +
fs/smb/server/transport_ipc.c | 1 +
fs/smb/server/transport_quic.c | 327 +++++++++++++++++++++++++++++++++
fs/smb/server/transport_quic.h | 20 ++
9 files changed, 380 insertions(+), 7 deletions(-)
create mode 100644 fs/smb/server/transport_quic.c
create mode 100644 fs/smb/server/transport_quic.h
diff --git a/fs/smb/server/Makefile b/fs/smb/server/Makefile
index 89a9955f607d..569be14b32d4 100644
--- a/fs/smb/server/Makefile
+++ b/fs/smb/server/Makefile
@@ -8,8 +8,8 @@ ksmbd-y := unicode.o auth.o vfs.o vfs_cache.o server.o ndr.o \
misc.o oplock.o connection.o ksmbd_work.o crypto_ctx.o \
mgmt/ksmbd_ida.o mgmt/user_config.o mgmt/share_config.o \
mgmt/tree_connect.o mgmt/user_session.o smb_common.o \
- transport.o transport_tcp.o transport_ipc.o interface.o \
- smbacl.o smb2pdu.o \
+ transport.o transport_tcp.o transport_ipc.o transport_quic.o \
+ interface.o smbacl.o smb2pdu.o \
smb2ops.o smb2misc.o ksmbd_spnego_negtokeninit.asn1.o \
ksmbd_spnego_negtokentarg.asn1.o asn1.o
diff --git a/fs/smb/server/connection.c b/fs/smb/server/connection.c
index 07bbf4eb7b05..1db8e0a8b47d 100644
--- a/fs/smb/server/connection.c
+++ b/fs/smb/server/connection.c
@@ -15,6 +15,7 @@
#include "interface.h"
#include "transport.h"
#include "transport_tcp.h"
+#include "transport_quic.h"
#include "transport_rdma.h"
#include "misc.h"
diff --git a/fs/smb/server/interface.c b/fs/smb/server/interface.c
index a5e59a420639..25b8e7f44059 100644
--- a/fs/smb/server/interface.c
+++ b/fs/smb/server/interface.c
@@ -11,6 +11,7 @@
#include "auth.h"
#include "connection.h"
#include "transport_tcp.h"
+#include "transport_quic.h"
#include "interface.h"
static LIST_HEAD(iface_list);
@@ -36,12 +37,14 @@ static struct interface *alloc_iface(char *ifname)
}
/**
- * ksmbd_interface_run_kthread() - start a per-interface forker thread
- * @iface: pointer to struct interface
- * @kthread_fn: thread routine to run
- * @suf: suffix to append to the thread name (e.g. "tcp")
+ * ksmbd_tcp_run_kthread() - start forker thread
+ * @iface: pointer to struct interface
*
- * Return: the started task_struct, or ERR_PTR on failure.
+ * start forker thread(ksmbd/0) at module init time to listen
+ * on port 445 for new SMB connection requests. It creates per connection
+ * server threads(ksmbd/x)
+ *
+ * Return: 0 on success or error number
*/
struct task_struct *ksmbd_interface_run_kthread(struct interface *iface,
int (*kthread_fn)(void *), const char *suf)
@@ -87,6 +90,10 @@ static int ksmbd_netdev_event(struct notifier_block *nb, unsigned long event,
ret = ksmbd_tcp_create_socket(iface);
if (ret)
break;
+
+ ret = ksmbd_quic_create_socket(iface);
+ if (ret)
+ break;
}
if (!iface && bind_additional_ifaces) {
iface = alloc_iface(kstrdup(netdev->name, KSMBD_DEFAULT_GFP));
@@ -101,6 +108,10 @@ static int ksmbd_netdev_event(struct notifier_block *nb, unsigned long event,
ret = ksmbd_tcp_create_socket(iface);
if (ret)
break;
+
+ ret = ksmbd_quic_create_socket(iface);
+ if (ret)
+ break;
}
break;
case NETDEV_DOWN:
@@ -111,15 +122,23 @@ static int ksmbd_netdev_event(struct notifier_block *nb, unsigned long event,
if (iface->ksmbd_tcp_socket)
kernel_sock_shutdown(iface->ksmbd_tcp_socket, SHUT_RDWR);
+ if (iface->ksmbd_quic_socket)
+ kernel_sock_shutdown(iface->ksmbd_quic_socket, SHUT_RDWR);
if (iface->ksmbd_tcp_kthread)
ksmbd_interface_stop_kthread(iface->ksmbd_tcp_kthread);
+ if (iface->ksmbd_quic_kthread)
+ ksmbd_interface_stop_kthread(iface->ksmbd_quic_kthread);
if (iface->ksmbd_tcp_socket)
sock_release(iface->ksmbd_tcp_socket);
+ if (iface->ksmbd_quic_socket)
+ sock_release(iface->ksmbd_quic_socket);
iface->ksmbd_tcp_kthread = NULL;
+ iface->ksmbd_quic_kthread = NULL;
iface->ksmbd_tcp_socket = NULL;
+ iface->ksmbd_quic_socket = NULL;
put_net(iface->net);
iface->net = NULL;
@@ -194,3 +213,4 @@ int ksmbd_set_interfaces(char *ifc_list, int ifc_list_sz)
return 0;
}
+
diff --git a/fs/smb/server/interface.h b/fs/smb/server/interface.h
index 7e35645076a9..94ffb73fd635 100644
--- a/fs/smb/server/interface.h
+++ b/fs/smb/server/interface.h
@@ -16,6 +16,8 @@
struct interface {
struct task_struct *ksmbd_tcp_kthread;
struct socket *ksmbd_tcp_socket;
+ struct task_struct *ksmbd_quic_kthread;
+ struct socket *ksmbd_quic_socket;
struct list_head entry;
char *name;
int state;
diff --git a/fs/smb/server/server.h b/fs/smb/server/server.h
index b8a7317be86b..197d99264071 100644
--- a/fs/smb/server/server.h
+++ b/fs/smb/server/server.h
@@ -35,6 +35,7 @@ struct ksmbd_server_config {
short min_protocol;
short max_protocol;
unsigned short tcp_port;
+ unsigned short quic_port;
unsigned short ipc_timeout;
unsigned long ipc_last_active;
unsigned long deadtime;
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index 1f579ef0e9d6..6f82f6bd378a 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -31,6 +31,7 @@
#include "server.h"
#include "smb_common.h"
+#include "interface.h"
#include "../common/smb2status.h"
#include "ksmbd_work.h"
#include "mgmt/user_config.h"
diff --git a/fs/smb/server/transport_ipc.c b/fs/smb/server/transport_ipc.c
index 6ccfb1cf8ed7..d14f282f3337 100644
--- a/fs/smb/server/transport_ipc.c
+++ b/fs/smb/server/transport_ipc.c
@@ -315,6 +315,7 @@ static int ipc_server_config_on_startup(struct ksmbd_startup_request *req)
server_conf.flags = req->flags;
server_conf.signing = req->signing;
server_conf.tcp_port = req->tcp_port;
+ server_conf.quic_port = 443; /* MS-SMB2 1.9 */
server_conf.ipc_timeout = req->ipc_timeout * HZ;
if (check_mul_overflow(req->deadtime, SMB_ECHO_INTERVAL,
&server_conf.deadtime)) {
diff --git a/fs/smb/server/transport_quic.c b/fs/smb/server/transport_quic.c
new file mode 100644
index 000000000000..84067225c1d3
--- /dev/null
+++ b/fs/smb/server/transport_quic.c
@@ -0,0 +1,327 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026, SUSE LLC
+ *
+ * Author: Henrique Carvalho <henrique.carvalho@suse.com>
+ */
+
+#include <linux/freezer.h>
+#include <linux/socket.h>
+#include <linux/quic.h>
+#include <linux/key.h>
+#include <net/handshake.h>
+#include <keys/asymmetric-type.h>
+
+#include "smb_common.h"
+#include "server.h"
+#include "auth.h"
+#include "connection.h"
+#include "transport.h"
+#include "glob.h"
+#include "transport_quic.h"
+#include "interface.h"
+
+struct quic_transport {
+ struct ksmbd_transport transport;
+ struct socket *sock;
+};
+
+static const struct ksmbd_transport_ops ksmbd_quic_transport_ops;
+
+#define QUIC_TRANS(t) ((struct quic_transport *)container_of(t, struct quic_transport, transport))
+
+static inline void ksmbd_quic_reuseaddr(struct socket *sock)
+{
+ sock_set_reuseaddr(sock->sk);
+}
+
+static void ksmbd_quic_disconnect(struct ksmbd_transport *kt)
+{
+ struct quic_transport *t = QUIC_TRANS(kt);
+
+ kernel_sock_shutdown(t->sock, SHUT_RDWR);
+ ksmbd_conn_free(KSMBD_TRANS(t)->conn);
+ if (server_conf.max_connections)
+ atomic_dec(&active_num_conn);
+}
+
+static void ksmbd_quic_close_client_sk(struct socket *client_sk)
+{
+ if (client_sk->file)
+ fput(client_sk->file);
+ else
+ sock_release(client_sk);
+}
+
+static struct quic_transport *ksmbd_quic_alloc(void)
+{
+ struct quic_transport *t;
+
+ t = kzalloc_obj(*t, KSMBD_DEFAULT_GFP);
+ if (!t)
+ return NULL;
+
+ KSMBD_TRANS(t)->ops = &ksmbd_quic_transport_ops;
+ return t;
+}
+
+static void ksmbd_quic_free(struct ksmbd_transport *kt)
+{
+ struct quic_transport *t = QUIC_TRANS(kt);
+
+ ksmbd_quic_close_client_sk(t->sock);
+ kfree(kt->io.iov); /* TODO (transport.c): move to ksmbd_conn_free */
+ kfree(t);
+}
+
+/**
+ * ksmbd_quic_new_connection() - create a new quic session on mount
+ * @client_sk: socket associated with new connection
+ *
+ * whenever a new connection is requested, create a conn thread
+ * (session thread) to handle new incoming smb requests from the connection
+ *
+ * Return: 0 on success, otherwise error
+ */
+static int ksmbd_quic_new_connection(struct socket *client_sk)
+{
+ int rc;
+ struct quic_transport *t;
+
+ t = ksmbd_quic_alloc();
+ if (!t) {
+ ksmbd_quic_close_client_sk(client_sk);
+ return -ENOMEM;
+ }
+
+ t->sock = client_sk;
+
+ rc = ksmbd_transport_alloc_conn(KSMBD_TRANS(t), client_sk);
+ if (rc) {
+ if (KSMBD_TRANS(t)->conn) {
+ /*
+ * conn was allocated and hashed but the handler
+ * thread failed; tear down via the conn refcount
+ * path which also calls ->free_transport().
+ */
+ ksmbd_quic_disconnect(KSMBD_TRANS(t));
+ } else {
+ ksmbd_quic_close_client_sk(client_sk);
+ kfree(t);
+ }
+ }
+
+ return rc;
+}
+
+static void ksmbd_tls_done(void *data, int status, key_serial_t peerid)
+{
+ struct smb_tls *kstls = (struct smb_tls *)data;
+
+ kstls->status = status;
+ kstls->peerid = peerid;
+
+ complete(&kstls->handshake_done);
+}
+
+static struct smb_tls *ksmbd_tls_init(void)
+{
+ struct smb_tls *kstls;
+ const unsigned int timeout_ms = 5000;
+
+ kstls = kzalloc_obj(*kstls, KSMBD_DEFAULT_GFP);
+ if (!kstls)
+ return ERR_PTR(-ENOMEM);
+
+ kstls->status = 0;
+
+ kstls->args.ta_done = ksmbd_tls_done;
+ kstls->args.ta_data = kstls;
+ kstls->args.ta_timeout_ms = timeout_ms;
+
+ init_completion(&kstls->handshake_done);
+
+ return kstls;
+}
+
+static void ksmbd_tls_reinit(struct smb_tls *kstls, struct socket *client_sk)
+{
+ kstls->status = 0;
+ kstls->peerid = TLS_NO_PEERID;
+ kstls->args.ta_sock = client_sk;
+ reinit_completion(&kstls->handshake_done);
+}
+
+static void ksmbd_tls_free(struct smb_tls **kstls)
+{
+ kfree_sensitive(*kstls);
+ *kstls = NULL;
+}
+
+/**
+ * ksmbd_quic_kthread_fn() - listen to new SMB QUIC connections and callback server
+ * @p: arguments to forker thread
+ *
+ * Return: 0 on success, error number otherwise
+ */
+static int ksmbd_quic_kthread_fn(void *p)
+{
+ struct socket *client_sk = NULL;
+ struct interface *iface = (struct interface *)p;
+ struct smb_tls *ksmbd_tls;
+ int ret;
+
+ ksmbd_tls = ksmbd_tls_init();
+ if (IS_ERR(ksmbd_tls))
+ return PTR_ERR(ksmbd_tls);
+
+ while (!kthread_should_stop()) {
+ struct file *filp;
+
+ if (!iface->ksmbd_quic_socket)
+ break;
+
+ ret = kernel_accept(iface->ksmbd_quic_socket, &client_sk, 0);
+ if (ret == -EINVAL)
+ break;
+ if (ret)
+ continue;
+
+ ret = quic_do_setsockopt(client_sk->sk,
+ QUIC_SOCKOPT_ALPN,
+ KERNEL_SOCKPTR("smb"),
+ 3);
+ if (ret < 0) {
+ ksmbd_debug(CONN, "Failed to set QUIC_SOCKOPT_ALPN: %d\n", ret);
+ ksmbd_quic_close_client_sk(client_sk);
+ continue;
+ }
+
+ filp = sock_alloc_file(client_sk, 0, NULL);
+ if (IS_ERR(filp)) {
+ ksmbd_debug(CONN, "Socket file allocation failed: %ld", PTR_ERR(filp));
+ ksmbd_quic_close_client_sk(client_sk);
+ continue;
+ }
+
+ ksmbd_tls_reinit(ksmbd_tls, client_sk);
+
+ ret = tls_server_hello_x509(&ksmbd_tls->args, KSMBD_DEFAULT_GFP);
+ if (ret < 0) {
+ ksmbd_debug(CONN, "Handshake failed: %d", ret);
+ ksmbd_quic_close_client_sk(client_sk);
+ continue;
+ }
+
+ ret = wait_for_completion_interruptible(&ksmbd_tls->handshake_done);
+ if (ret < 0) {
+ tls_handshake_cancel(client_sk->sk);
+ ksmbd_quic_close_client_sk(client_sk);
+ ksmbd_debug(CONN, "Handshake failed: %d", ret);
+ continue;
+ }
+ if (ksmbd_tls->status) {
+ ksmbd_quic_close_client_sk(client_sk);
+ ksmbd_debug(CONN, "Handshake failed: %d", ksmbd_tls->status);
+ continue;
+ }
+ ksmbd_debug(CONN, "Handshake successful");
+
+ if (ksmbd_check_max_ip_conns(client_sk)) {
+ ksmbd_quic_close_client_sk(client_sk);
+ continue;
+ }
+
+ if (ksmbd_check_max_conns()) {
+ ksmbd_quic_close_client_sk(client_sk);
+ continue;
+ }
+
+ ksmbd_debug(CONN, "connect success: accepted new connection\n");
+ client_sk->sk->sk_rcvtimeo = KSMBD_QUIC_RECV_TIMEOUT;
+ client_sk->sk->sk_sndtimeo = KSMBD_QUIC_SEND_TIMEOUT;
+
+ ksmbd_quic_new_connection(client_sk);
+ }
+
+ ksmbd_tls_free(&ksmbd_tls);
+
+ ksmbd_debug(CONN, "releasing socket\n");
+ return 0;
+}
+
+/**
+ * ksmbd_quic_read() - read data from socket in given buffer
+ * @t: transport instance
+ * @buf: buffer to store read data from socket
+ * @to_read: number of bytes to read from socket
+ * @max_retries: number of retries if reading from socket fails
+ *
+ * Return: on success return number of bytes read from socket,
+ * otherwise return error number
+ */
+static int ksmbd_quic_readv(struct ksmbd_transport *t, char *buf,
+ unsigned int to_read, int max_retries)
+{
+ struct kvec iov;
+
+ iov.iov_base = buf;
+ iov.iov_len = to_read;
+
+ return ksmbd_readv(t, QUIC_TRANS(t)->sock, &iov, 1, to_read, max_retries);
+}
+
+static int ksmbd_quic_writev(struct ksmbd_transport *t, struct kvec *iov,
+ int nvecs, int size, bool need_invalidate,
+ unsigned int remote_key)
+
+{
+ int err = -EOPNOTSUPP;
+#if IS_ENABLED(CONFIG_IP_QUIC)
+ struct msghdr ksmbd_msg = {.msg_flags = MSG_NOSIGNAL};
+
+ err = kernel_sendmsg(QUIC_TRANS(t)->sock, &ksmbd_msg, iov, nvecs, size);
+#endif
+ return err;
+}
+
+void ksmbd_quic_destroy_socket(struct socket *ksmbd_socket)
+{
+ ksmbd_destroy_socket(ksmbd_socket);
+}
+
+int ksmbd_quic_create_socket(struct interface *iface)
+{
+#if IS_ENABLED(CONFIG_IP_QUIC)
+ struct task_struct *kthread;
+ struct socket *ksmbd_socket;
+ int ret;
+
+ ksmbd_socket = ksmbd_create_socket(iface, SOCK_DGRAM, IPPROTO_QUIC,
+ server_conf.quic_port);
+ if (IS_ERR(ksmbd_socket))
+ return PTR_ERR(ksmbd_socket);
+
+ ksmbd_quic_reuseaddr(ksmbd_socket);
+
+ kthread = ksmbd_interface_run_kthread(iface, ksmbd_quic_kthread_fn, "quic");
+ if (IS_ERR(kthread)) {
+ ret = PTR_ERR(kthread);
+ pr_err("Can't start ksmbd main kthread for quic: %d\n", ret);
+ ksmbd_destroy_socket(ksmbd_socket);
+ return ret;
+ }
+
+ iface->ksmbd_quic_socket = ksmbd_socket;
+ iface->ksmbd_quic_kthread = kthread;
+ iface->state = IFACE_STATE_CONFIGURED;
+#endif /* CONFIG_IP_QUIC */
+ return 0;
+}
+
+static const struct ksmbd_transport_ops ksmbd_quic_transport_ops = {
+ .read = ksmbd_quic_readv,
+ .writev = ksmbd_quic_writev,
+ .disconnect = ksmbd_quic_disconnect,
+ .free_transport = ksmbd_quic_free,
+};
diff --git a/fs/smb/server/transport_quic.h b/fs/smb/server/transport_quic.h
new file mode 100644
index 000000000000..5c62c45affc4
--- /dev/null
+++ b/fs/smb/server/transport_quic.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2026, SUSE LLC.
+ */
+
+#ifndef __KSMBD_TRANSPORT_QUIC_H__
+#define __KSMBD_TRANSPORT_QUIC_H__
+
+#include "interface.h"
+
+#define KSMBD_QUIC_RECV_TIMEOUT (7 * HZ)
+#define KSMBD_QUIC_SEND_TIMEOUT (5 * HZ)
+
+int ksmbd_quic_init(void);
+void ksmbd_quic_destroy(void);
+int ksmbd_quic_create_socket(struct interface *iface);
+void ksmbd_quic_destroy_socket(struct socket *ksmbd_socket);
+void ksmbd_quic_stop_kthread(struct task_struct *kthread);
+
+#endif /* __KSMBD_TRANSPORT_QUIC_H__ */
--
2.53.0
next prev parent reply other threads:[~2026-04-28 15:58 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-28 15:57 [PATCH v2 05/11] smb: server: split interface management from TCP in preparation for QUIC Henrique Carvalho
2026-04-28 15:57 ` [PATCH v2 06/11] smb: server: add shared transport helpers " Henrique Carvalho
2026-04-29 22:24 ` Namjae Jeon
2026-04-28 15:57 ` [PATCH v2 07/11] smb: server: reuse common transport helpers in TCP transport Henrique Carvalho
2026-04-28 15:57 ` Henrique Carvalho [this message]
2026-04-28 15:57 ` [PATCH v2 09/11] smb: server: refactor TCP transport definitions Henrique Carvalho
2026-04-28 15:57 ` [PATCH v2 10/11] smb: server: track TCP and QUIC listener state independently Henrique Carvalho
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=20260428155759.226368-4-henrique.carvalho@suse.com \
--to=henrique.carvalho@suse.com \
--cc=ematsumiya@suse.de \
--cc=linkinjeon@kernel.org \
--cc=linux-cifs@vger.kernel.org \
--cc=metze@samba.org \
--cc=senozhatsky@chromium.org \
--cc=sfrench@samba.org \
--cc=tom@talpey.com \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox