* [RFC mptcp-next v9 01/10] tls: introduce struct tls_prot_ops
2026-03-13 1:42 [RFC mptcp-next v9 00/10] MPTCP KTLS support Geliang Tang
@ 2026-03-13 1:42 ` Geliang Tang
2026-03-13 1:42 ` [RFC mptcp-next v9 02/10] tls: add ops in tls_context Geliang Tang
` (9 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Geliang Tang @ 2026-03-13 1:42 UTC (permalink / raw)
To: mptcp; +Cc: Geliang Tang, Gang Yan
From: Geliang Tang <tanggeliang@kylinos.cn>
To extend MPTCP support based on TCP TLS, a tls_prot_ops structure has
been introduced for TLS, encapsulating TCP-specific helpers within this
structure.
Add registering, validating and finding functions for this structure to
add, validate and find a tls_prot_ops on the global list tls_prot_ops_list.
Register TCP-specific structure tls_tcp_ops in tls_init().
Co-developed-by: Gang Yan <yangang@kylinos.cn>
Signed-off-by: Gang Yan <yangang@kylinos.cn>
Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn>
---
include/net/tls.h | 19 ++++++++++
net/tls/tls_main.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 107 insertions(+)
diff --git a/include/net/tls.h b/include/net/tls.h
index ebd2550280ae..40001110bccb 100644
--- a/include/net/tls.h
+++ b/include/net/tls.h
@@ -220,6 +220,25 @@ struct tls_prot_info {
u16 tail_size;
};
+struct tls_prot_ops {
+ int protocol;
+ struct module *owner;
+ struct list_head list;
+
+ int (*inq)(struct sock *sk);
+ int (*sendmsg_locked)(struct sock *sk, struct msghdr *msg, size_t size);
+ struct sk_buff *(*recv_skb)(struct sock *sk, u32 *off);
+ int (*read_sock)(struct sock *sk, read_descriptor_t *desc,
+ sk_read_actor_t recv_actor);
+ void (*read_done)(struct sock *sk, size_t len);
+ u32 (*get_skb_off)(struct sk_buff *skb);
+ u32 (*get_skb_seq)(struct sk_buff *skb);
+ __poll_t (*poll)(struct file *file, struct socket *sock,
+ struct poll_table_struct *wait);
+ bool (*epollin_ready)(const struct sock *sk);
+ void (*check_app_limited)(struct sock *sk);
+};
+
struct tls_context {
/* read-only cache line */
struct tls_prot_info prot_info;
diff --git a/net/tls/tls_main.c b/net/tls/tls_main.c
index fd39acf41a61..b123e35a153d 100644
--- a/net/tls/tls_main.c
+++ b/net/tls/tls_main.c
@@ -128,6 +128,24 @@ static struct proto_ops tls_proto_ops[TLS_NUM_PROTS][TLS_NUM_CONFIG][TLS_NUM_CON
static void build_protos(struct proto prot[TLS_NUM_CONFIG][TLS_NUM_CONFIG],
const struct proto *base);
+static DEFINE_SPINLOCK(tls_prot_ops_lock);
+static LIST_HEAD(tls_prot_ops_list);
+
+/* Must be called with rcu read lock held */
+static struct tls_prot_ops *tls_prot_ops_find(int protocol)
+{
+ struct tls_prot_ops *ops, *ret = NULL;
+
+ list_for_each_entry_rcu(ops, &tls_prot_ops_list, list) {
+ if (ops->protocol == protocol) {
+ ret = ops;
+ break;
+ }
+ }
+
+ return ret;
+}
+
void update_sk_prot(struct sock *sk, struct tls_context *ctx)
{
int ip_ver = sk->sk_family == AF_INET6 ? TLSV6 : TLSV4;
@@ -1236,6 +1254,74 @@ static struct tcp_ulp_ops tcp_tls_ulp_ops __read_mostly = {
.get_info_size = tls_get_info_size,
};
+static int tls_validate_prot_ops(const struct tls_prot_ops *ops)
+{
+ if (!ops->inq || !ops->sendmsg_locked ||
+ !ops->recv_skb || !ops->read_sock || !ops->read_done ||
+ !ops->get_skb_off || !ops->get_skb_seq ||
+ !ops->poll || !ops->epollin_ready ||
+ !ops->check_app_limited) {
+ pr_err("%d does not implement required ops\n", ops->protocol);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int tls_register_prot_ops(struct tls_prot_ops *ops)
+{
+ int ret;
+
+ ret = tls_validate_prot_ops(ops);
+ if (ret)
+ return ret;
+
+ spin_lock(&tls_prot_ops_lock);
+ if (tls_prot_ops_find(ops->protocol)) {
+ spin_unlock(&tls_prot_ops_lock);
+ return -EEXIST;
+ }
+ list_add_tail_rcu(&ops->list, &tls_prot_ops_list);
+ spin_unlock(&tls_prot_ops_lock);
+
+ pr_debug("tls_prot_ops %d registered\n", ops->protocol);
+ return 0;
+}
+
+static struct sk_buff *tls_tcp_recv_skb(struct sock *sk, u32 *off)
+{
+ return tcp_recv_skb(sk, tcp_sk(sk)->copied_seq, off);
+}
+
+static u32 tls_tcp_get_skb_off(struct sk_buff *skb)
+{
+ return 0;
+}
+
+static u32 tls_tcp_get_skb_seq(struct sk_buff *skb)
+{
+ return TCP_SKB_CB(skb)->seq;
+}
+
+static bool tls_tcp_epollin_ready(const struct sock *sk)
+{
+ return tcp_epollin_ready(sk, INT_MAX);
+}
+
+static struct tls_prot_ops tls_tcp_ops = {
+ .protocol = IPPROTO_TCP,
+ .inq = tcp_inq,
+ .sendmsg_locked = tcp_sendmsg_locked,
+ .recv_skb = tls_tcp_recv_skb,
+ .read_sock = tcp_read_sock,
+ .read_done = tcp_read_done,
+ .get_skb_off = tls_tcp_get_skb_off,
+ .get_skb_seq = tls_tcp_get_skb_seq,
+ .poll = tcp_poll,
+ .epollin_ready = tls_tcp_epollin_ready,
+ .check_app_limited = tcp_rate_check_app_limited,
+};
+
static int __init tls_register(void)
{
int err;
@@ -1254,6 +1340,8 @@ static int __init tls_register(void)
tcp_register_ulp(&tcp_tls_ulp_ops);
+ tls_register_prot_ops(&tls_tcp_ops);
+
return 0;
err_strp:
tls_strp_dev_exit();
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [RFC mptcp-next v9 02/10] tls: add ops in tls_context
2026-03-13 1:42 [RFC mptcp-next v9 00/10] MPTCP KTLS support Geliang Tang
2026-03-13 1:42 ` [RFC mptcp-next v9 01/10] tls: introduce struct tls_prot_ops Geliang Tang
@ 2026-03-13 1:42 ` Geliang Tang
2026-03-13 1:42 ` [RFC mptcp-next v9 03/10] tls: add MPTCP SKB offset check in strp queue walk Geliang Tang
` (8 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Geliang Tang @ 2026-03-13 1:42 UTC (permalink / raw)
To: mptcp; +Cc: Geliang Tang, Gang Yan
From: Geliang Tang <tanggeliang@kylinos.cn>
A pointer to struct tls_prot_ops, named 'ops', has been added to struct
tls_context. The places originally calling TLS-specific helpers have now
been modified to indirectly invoke them via 'ops' pointer in tls_context.
In do_tls_setsockopt_conf(), ctx->ops is assigned either 'tls_mptcp_ops'
or 'tls_tcp_ops' based on the socket protocol.
Co-developed-by: Gang Yan <yangang@kylinos.cn>
Signed-off-by: Gang Yan <yangang@kylinos.cn>
Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn>
---
include/net/tls.h | 1 +
net/tls/tls_main.c | 13 +++++++++----
net/tls/tls_strp.c | 29 +++++++++++++++++++----------
net/tls/tls_sw.c | 7 +++++--
4 files changed, 34 insertions(+), 16 deletions(-)
diff --git a/include/net/tls.h b/include/net/tls.h
index 40001110bccb..3c67c45f13be 100644
--- a/include/net/tls.h
+++ b/include/net/tls.h
@@ -277,6 +277,7 @@ struct tls_context {
struct sock *sk;
void (*sk_destruct)(struct sock *sk);
+ const struct tls_prot_ops *ops;
union tls_crypto_context crypto_send;
union tls_crypto_context crypto_recv;
diff --git a/net/tls/tls_main.c b/net/tls/tls_main.c
index b123e35a153d..7c537b7fbabb 100644
--- a/net/tls/tls_main.c
+++ b/net/tls/tls_main.c
@@ -206,13 +206,13 @@ int tls_push_sg(struct sock *sk,
ctx->splicing_pages = true;
while (1) {
/* is sending application-limited? */
- tcp_rate_check_app_limited(sk);
+ ctx->ops->check_app_limited(sk);
p = sg_page(sg);
retry:
bvec_set_page(&bvec, p, size, offset);
iov_iter_bvec(&msg.msg_iter, ITER_SOURCE, &bvec, 1, size);
- ret = tcp_sendmsg_locked(sk, &msg, size);
+ ret = ctx->ops->sendmsg_locked(sk, &msg, size);
if (ret != size) {
if (ret > 0) {
@@ -427,14 +427,14 @@ static __poll_t tls_sk_poll(struct file *file, struct socket *sock,
u8 shutdown;
int state;
- mask = tcp_poll(file, sock, wait);
+ tls_ctx = tls_get_ctx(sk);
+ mask = tls_ctx->ops->poll(file, sock, wait);
state = inet_sk_state_load(sk);
shutdown = READ_ONCE(sk->sk_shutdown);
if (unlikely(state != TCP_ESTABLISHED || shutdown & RCV_SHUTDOWN))
return mask;
- tls_ctx = tls_get_ctx(sk);
ctx = tls_sw_ctx_rx(tls_ctx);
psock = sk_psock_get(sk);
@@ -1094,6 +1094,11 @@ static int tls_init(struct sock *sk)
ctx->tx_conf = TLS_BASE;
ctx->rx_conf = TLS_BASE;
ctx->tx_max_payload_len = TLS_MAX_PAYLOAD_SIZE;
+ ctx->ops = tls_prot_ops_find(sk->sk_protocol);
+ if (!ctx->ops) {
+ rc = -EINVAL;
+ goto out;
+ }
update_sk_prot(sk, ctx);
out:
write_unlock_bh(&sk->sk_callback_lock);
diff --git a/net/tls/tls_strp.c b/net/tls/tls_strp.c
index 98e12f0ff57e..ef1a8659ee18 100644
--- a/net/tls/tls_strp.c
+++ b/net/tls/tls_strp.c
@@ -120,6 +120,7 @@ struct sk_buff *tls_strp_msg_detach(struct tls_sw_context_rx *ctx)
int tls_strp_msg_cow(struct tls_sw_context_rx *ctx)
{
struct tls_strparser *strp = &ctx->strp;
+ struct tls_context *tls_ctx;
struct sk_buff *skb;
if (strp->copy_mode)
@@ -132,7 +133,8 @@ int tls_strp_msg_cow(struct tls_sw_context_rx *ctx)
tls_strp_anchor_free(strp);
strp->anchor = skb;
- tcp_read_done(strp->sk, strp->stm.full_len);
+ tls_ctx = tls_get_ctx(strp->sk);
+ tls_ctx->ops->read_done(strp->sk, strp->stm.full_len);
strp->copy_mode = 1;
return 0;
@@ -376,6 +378,7 @@ static int tls_strp_copyin(read_descriptor_t *desc, struct sk_buff *in_skb,
static int tls_strp_read_copyin(struct tls_strparser *strp)
{
+ struct tls_context *ctx = tls_get_ctx(strp->sk);
read_descriptor_t desc;
desc.arg.data = strp;
@@ -383,13 +386,14 @@ static int tls_strp_read_copyin(struct tls_strparser *strp)
desc.count = 1; /* give more than one skb per call */
/* sk should be locked here, so okay to do read_sock */
- tcp_read_sock(strp->sk, &desc, tls_strp_copyin);
+ ctx->ops->read_sock(strp->sk, &desc, tls_strp_copyin);
return desc.error;
}
static int tls_strp_read_copy(struct tls_strparser *strp, bool qshort)
{
+ struct tls_context *ctx = tls_get_ctx(strp->sk);
struct skb_shared_info *shinfo;
struct page *page;
int need_spc, len;
@@ -398,7 +402,7 @@ static int tls_strp_read_copy(struct tls_strparser *strp, bool qshort)
* to read the data out. Otherwise the connection will stall.
* Without pressure threshold of INT_MAX will never be ready.
*/
- if (likely(qshort && !tcp_epollin_ready(strp->sk, INT_MAX)))
+ if (likely(qshort && !ctx->ops->epollin_ready(strp->sk)))
return 0;
shinfo = skb_shinfo(strp->anchor);
@@ -434,12 +438,13 @@ static int tls_strp_read_copy(struct tls_strparser *strp, bool qshort)
static bool tls_strp_check_queue_ok(struct tls_strparser *strp)
{
unsigned int len = strp->stm.offset + strp->stm.full_len;
+ struct tls_context *ctx = tls_get_ctx(strp->sk);
struct sk_buff *first, *skb;
u32 seq;
first = skb_shinfo(strp->anchor)->frag_list;
skb = first;
- seq = TCP_SKB_CB(first)->seq;
+ seq = ctx->ops->get_skb_seq(first);
/* Make sure there's no duplicate data in the queue,
* and the decrypted status matches.
@@ -449,7 +454,7 @@ static bool tls_strp_check_queue_ok(struct tls_strparser *strp)
len -= skb->len;
skb = skb->next;
- if (TCP_SKB_CB(skb)->seq != seq)
+ if (ctx->ops->get_skb_seq(skb) != seq)
return false;
if (skb_cmp_decrypted(first, skb))
return false;
@@ -460,11 +465,11 @@ static bool tls_strp_check_queue_ok(struct tls_strparser *strp)
static void tls_strp_load_anchor_with_queue(struct tls_strparser *strp, int len)
{
- struct tcp_sock *tp = tcp_sk(strp->sk);
+ struct tls_context *ctx = tls_get_ctx(strp->sk);
struct sk_buff *first;
u32 offset;
- first = tcp_recv_skb(strp->sk, tp->copied_seq, &offset);
+ first = ctx->ops->recv_skb(strp->sk, &offset);
if (WARN_ON_ONCE(!first))
return;
@@ -483,6 +488,7 @@ static void tls_strp_load_anchor_with_queue(struct tls_strparser *strp, int len)
bool tls_strp_msg_load(struct tls_strparser *strp, bool force_refresh)
{
+ struct tls_context *ctx = tls_get_ctx(strp->sk);
struct strp_msg *rxm;
struct tls_msg *tlm;
@@ -490,7 +496,7 @@ bool tls_strp_msg_load(struct tls_strparser *strp, bool force_refresh)
DEBUG_NET_WARN_ON_ONCE(!strp->stm.full_len);
if (!strp->copy_mode && force_refresh) {
- if (unlikely(tcp_inq(strp->sk) < strp->stm.full_len)) {
+ if (unlikely(ctx->ops->inq(strp->sk) < strp->stm.full_len)) {
WRITE_ONCE(strp->msg_ready, 0);
memset(&strp->stm, 0, sizeof(strp->stm));
return false;
@@ -511,9 +517,10 @@ bool tls_strp_msg_load(struct tls_strparser *strp, bool force_refresh)
/* Called with lock held on lower socket */
static int tls_strp_read_sock(struct tls_strparser *strp)
{
+ struct tls_context *ctx = tls_get_ctx(strp->sk);
int sz, inq;
- inq = tcp_inq(strp->sk);
+ inq = ctx->ops->inq(strp->sk);
if (inq < 1)
return 0;
@@ -583,10 +590,12 @@ static void tls_strp_work(struct work_struct *w)
void tls_strp_msg_done(struct tls_strparser *strp)
{
+ struct tls_context *ctx = tls_get_ctx(strp->sk);
+
WARN_ON(!strp->stm.full_len);
if (likely(!strp->copy_mode))
- tcp_read_done(strp->sk, strp->stm.full_len);
+ ctx->ops->read_done(strp->sk, strp->stm.full_len);
else
tls_strp_flush_anchor_copy(strp);
diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c
index a656ce235758..b6959ebcded8 100644
--- a/net/tls/tls_sw.c
+++ b/net/tls/tls_sw.c
@@ -1952,13 +1952,14 @@ tls_read_flush_backlog(struct sock *sk, struct tls_prot_info *prot,
size_t len_left, size_t decrypted, ssize_t done,
size_t *flushed_at)
{
+ struct tls_context *tls_ctx = tls_get_ctx(sk);
size_t max_rec;
if (len_left <= decrypted)
return false;
max_rec = prot->overhead_size - prot->tail_size + TLS_MAX_PAYLOAD_SIZE;
- if (done - *flushed_at < SZ_128K && tcp_inq(sk) > max_rec)
+ if (done - *flushed_at < SZ_128K && tls_ctx->ops->inq(sk) > max_rec)
return false;
*flushed_at = done;
@@ -2445,6 +2446,7 @@ int tls_rx_msg_size(struct tls_strparser *strp, struct sk_buff *skb)
size_t cipher_overhead;
size_t data_len = 0;
int ret;
+ u32 seq;
/* Verify that we have a full TLS header, or wait for more data */
if (strp->stm.offset + prot->prepend_size > skb->len)
@@ -2487,8 +2489,9 @@ int tls_rx_msg_size(struct tls_strparser *strp, struct sk_buff *skb)
goto read_failure;
}
+ seq = tls_ctx->ops->get_skb_seq(skb);
tls_device_rx_resync_new_rec(strp->sk, data_len + TLS_HEADER_SIZE,
- TCP_SKB_CB(skb)->seq + strp->stm.offset);
+ seq + strp->stm.offset);
return data_len + TLS_HEADER_SIZE;
read_failure:
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [RFC mptcp-next v9 03/10] tls: add MPTCP SKB offset check in strp queue walk
2026-03-13 1:42 [RFC mptcp-next v9 00/10] MPTCP KTLS support Geliang Tang
2026-03-13 1:42 ` [RFC mptcp-next v9 01/10] tls: introduce struct tls_prot_ops Geliang Tang
2026-03-13 1:42 ` [RFC mptcp-next v9 02/10] tls: add ops in tls_context Geliang Tang
@ 2026-03-13 1:42 ` Geliang Tang
2026-03-13 1:42 ` [RFC mptcp-next v9 04/10] mptcp: update mptcp_check_readable Geliang Tang
` (7 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Geliang Tang @ 2026-03-13 1:42 UTC (permalink / raw)
To: mptcp; +Cc: Gang Yan, Geliang Tang
From: Gang Yan <yangang@kylinos.cn>
In MPTCP scenarios, subflow SKBs may have non-zero offsets due to
out-of-order packet handling or partial data delivery. When walking
the TLS strp queue to verify sequence numbers and decryption status,
we must also validate each SKB's offset using get_skb_off() to ensure
the queue state is consistent. This check is specific to MPTCP; TCP
does not require offset validation as its SKBs always start at offset 0.
If any SKB reports an invalid offset, return false to indicate the
queue is not in a consistent state and trigger a resynchronization.
Co-developed-by: Geliang Tang <geliang@kernel.org>
Signed-off-by: Geliang Tang <geliang@kernel.org>
Signed-off-by: Gang Yan <yangang@kylinos.cn>
---
net/tls/tls_strp.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/net/tls/tls_strp.c b/net/tls/tls_strp.c
index ef1a8659ee18..4cf0dfbd7fb4 100644
--- a/net/tls/tls_strp.c
+++ b/net/tls/tls_strp.c
@@ -454,6 +454,8 @@ static bool tls_strp_check_queue_ok(struct tls_strparser *strp)
len -= skb->len;
skb = skb->next;
+ if (ctx->ops->get_skb_off(skb))
+ return false;
if (ctx->ops->get_skb_seq(skb) != seq)
return false;
if (skb_cmp_decrypted(first, skb))
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [RFC mptcp-next v9 04/10] mptcp: update mptcp_check_readable
2026-03-13 1:42 [RFC mptcp-next v9 00/10] MPTCP KTLS support Geliang Tang
` (2 preceding siblings ...)
2026-03-13 1:42 ` [RFC mptcp-next v9 03/10] tls: add MPTCP SKB offset check in strp queue walk Geliang Tang
@ 2026-03-13 1:42 ` Geliang Tang
2026-03-13 1:42 ` [RFC mptcp-next v9 05/10] mptcp: avoid deadlocks in read_sock path Geliang Tang
` (6 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Geliang Tang @ 2026-03-13 1:42 UTC (permalink / raw)
To: mptcp; +Cc: Gang Yan, Geliang Tang
From: Gang Yan <yangang@kylinos.cn>
This patch makes mptcp_check_readable() aligned with TCP, and renames it to
mptcp_stream_is_readable(). It will be used in the case of KTLS, because
'prot' will be modified, tls_sw_sock_is_readable() is expected to be called
from prot->sock_is_readable().
Co-developed-by: Geliang Tang <geliang@kernel.org>
Signed-off-by: Geliang Tang <geliang@kernel.org>
Signed-off-by: Gang Yan <yangang@kylinos.cn>
---
net/mptcp/protocol.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index b5676b37f8f4..c0109338648a 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -3242,9 +3242,11 @@ void __mptcp_unaccepted_force_close(struct sock *sk)
__mptcp_destroy_sock(sk);
}
-static __poll_t mptcp_check_readable(struct sock *sk)
+static bool mptcp_stream_is_readable(struct sock *sk)
{
- return mptcp_epollin_ready(sk) ? EPOLLIN | EPOLLRDNORM : 0;
+ if (mptcp_epollin_ready(sk))
+ return true;
+ return sk_is_readable(sk);
}
static void mptcp_check_listen_stop(struct sock *sk)
@@ -4306,7 +4308,8 @@ static __poll_t mptcp_poll(struct file *file, struct socket *sock,
mask |= EPOLLIN | EPOLLRDNORM | EPOLLRDHUP;
if (state != TCP_SYN_SENT && state != TCP_SYN_RECV) {
- mask |= mptcp_check_readable(sk);
+ if (mptcp_stream_is_readable(sk))
+ mask |= EPOLLIN | EPOLLRDNORM;
if (shutdown & SEND_SHUTDOWN)
mask |= EPOLLOUT | EPOLLWRNORM;
else
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [RFC mptcp-next v9 05/10] mptcp: avoid deadlocks in read_sock path
2026-03-13 1:42 [RFC mptcp-next v9 00/10] MPTCP KTLS support Geliang Tang
` (3 preceding siblings ...)
2026-03-13 1:42 ` [RFC mptcp-next v9 04/10] mptcp: update mptcp_check_readable Geliang Tang
@ 2026-03-13 1:42 ` Geliang Tang
2026-03-13 1:42 ` [RFC mptcp-next v9 06/10] mptcp: implement tls_mptcp_ops Geliang Tang
` (5 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Geliang Tang @ 2026-03-13 1:42 UTC (permalink / raw)
To: mptcp; +Cc: Geliang Tang, Gang Yan
From: Geliang Tang <tanggeliang@kylinos.cn>
When invoking mptcp_read_sock() from a softirq context (e.g., through
the TLS read_sock interface), calling lock_sock_fast() in
mptcp_rcv_space_adjust() or mptcp_cleanup_rbuf() can lead to deadlocks,
since the socket lock may already be held.
Replace lock_sock_fast() with spin_trylock_bh() in these functions to
make the locking attempt non-blocking. If the lock cannot be acquired,
skip the operation to avoid deadlock.
Also introduce mptcp_data_trylock() and use it in mptcp_move_skbs() to
make the data locking non-blocking in the read_sock path.
Co-developed-by: Gang Yan <yangang@kylinos.cn>
Signed-off-by: Gang Yan <yangang@kylinos.cn>
Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn>
---
net/mptcp/protocol.c | 16 ++++++++--------
net/mptcp/protocol.h | 1 +
2 files changed, 9 insertions(+), 8 deletions(-)
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index c0109338648a..fdfe6145f6da 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -557,12 +557,11 @@ static void mptcp_send_ack(struct mptcp_sock *msk)
static void mptcp_subflow_cleanup_rbuf(struct sock *ssk, int copied)
{
- bool slow;
-
- slow = lock_sock_fast(ssk);
+ if (!spin_trylock_bh(&ssk->sk_lock.slock))
+ return;
if (tcp_can_send_ack(ssk))
tcp_cleanup_rbuf(ssk, copied);
- unlock_sock_fast(ssk, slow);
+ spin_unlock_bh(&ssk->sk_lock.slock);
}
static bool mptcp_subflow_could_cleanup(const struct sock *ssk, bool rx_empty)
@@ -2152,14 +2151,14 @@ static void mptcp_rcv_space_adjust(struct mptcp_sock *msk, int copied)
*/
mptcp_for_each_subflow(msk, subflow) {
struct sock *ssk;
- bool slow;
ssk = mptcp_subflow_tcp_sock(subflow);
- slow = lock_sock_fast(ssk);
+ if (!spin_trylock_bh(&ssk->sk_lock.slock))
+ continue;
/* subflows can be added before tcp_init_transfer() */
if (tcp_sk(ssk)->rcvq_space.space)
tcp_rcvbuf_grow(ssk, copied);
- unlock_sock_fast(ssk, slow);
+ spin_unlock_bh(&ssk->sk_lock.slock);
}
}
@@ -2232,7 +2231,8 @@ static bool mptcp_move_skbs(struct sock *sk)
bool enqueued = false;
u32 moved;
- mptcp_data_lock(sk);
+ if (!mptcp_data_trylock(sk))
+ return false;
while (mptcp_can_spool_backlog(sk, &skbs)) {
mptcp_data_unlock(sk);
enqueued |= __mptcp_move_skbs(sk, &skbs, &moved);
diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
index f5d4d7d030f2..3146e26687b4 100644
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -378,6 +378,7 @@ struct mptcp_sock {
};
#define mptcp_data_lock(sk) spin_lock_bh(&(sk)->sk_lock.slock)
+#define mptcp_data_trylock(sk) spin_trylock_bh(&(sk)->sk_lock.slock)
#define mptcp_data_unlock(sk) spin_unlock_bh(&(sk)->sk_lock.slock)
#define mptcp_for_each_subflow(__msk, __subflow) \
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [RFC mptcp-next v9 06/10] mptcp: implement tls_mptcp_ops
2026-03-13 1:42 [RFC mptcp-next v9 00/10] MPTCP KTLS support Geliang Tang
` (4 preceding siblings ...)
2026-03-13 1:42 ` [RFC mptcp-next v9 05/10] mptcp: avoid deadlocks in read_sock path Geliang Tang
@ 2026-03-13 1:42 ` Geliang Tang
2026-03-13 1:42 ` [RFC mptcp-next v9 07/10] mptcp: update ULP getsockopt Geliang Tang
` (4 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Geliang Tang @ 2026-03-13 1:42 UTC (permalink / raw)
To: mptcp; +Cc: Geliang Tang, Gang Yan
From: Geliang Tang <tanggeliang@kylinos.cn>
This patch implements the MPTCP-specific struct tls_prot_ops, named
'tls_mptcp_ops'.
Note that there is a slight difference between mptcp_inq() and
mptcp_inq_hint(), it does not return 1 when the socket is closed or
shut down; instead, it returns 0. Otherwise, it would break the
condition "inq < 1" in tls_strp_read_sock().
Passing an MPTCP socket to tcp_sock_rate_check_app_limited() can
trigger a crash. Here, an MPTCP version of check_app_limited() is
implemented, which calls tcp_sock_rate_check_app_limited() for each
subflow.
MPTCP TLS_HW mode is not yet implemented, returning EOPNOTSUPP here.
Co-developed-by: Gang Yan <yangang@kylinos.cn>
Signed-off-by: Gang Yan <yangang@kylinos.cn>
Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn>
---
include/net/mptcp.h | 2 +
include/net/tcp.h | 1 +
net/ipv4/tcp.c | 9 +++-
net/mptcp/protocol.c | 106 ++++++++++++++++++++++++++++++++++++++++---
net/tls/tls_main.c | 6 +++
5 files changed, 116 insertions(+), 8 deletions(-)
diff --git a/include/net/mptcp.h b/include/net/mptcp.h
index 4cf59e83c1c5..02564eceeb7e 100644
--- a/include/net/mptcp.h
+++ b/include/net/mptcp.h
@@ -132,6 +132,8 @@ struct mptcp_pm_ops {
void (*release)(struct mptcp_sock *msk);
} ____cacheline_aligned_in_smp;
+extern struct tls_prot_ops tls_mptcp_ops;
+
#ifdef CONFIG_MPTCP
void mptcp_init(void);
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 9f0aee9e5d76..071a25e77c03 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -837,6 +837,7 @@ static inline int tcp_bound_to_half_wnd(struct tcp_sock *tp, int pktsize)
/* tcp.c */
void tcp_get_info(struct sock *, struct tcp_info *);
+void tcp_sock_rate_check_app_limited(struct tcp_sock *tp);
void tcp_rate_check_app_limited(struct sock *sk);
/* Read 'sendfile()'-style from a TCP socket */
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index fe23550d5c51..400f84f193a3 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -1110,9 +1110,9 @@ int tcp_sendmsg_fastopen(struct sock *sk, struct msghdr *msg, int *copied,
}
/* If a gap is detected between sends, mark the socket application-limited. */
-void tcp_rate_check_app_limited(struct sock *sk)
+void tcp_sock_rate_check_app_limited(struct tcp_sock *tp)
{
- struct tcp_sock *tp = tcp_sk(sk);
+ struct sock *sk = (struct sock *)tp;
if (/* We have less than one packet to send. */
tp->write_seq - tp->snd_nxt < tp->mss_cache &&
@@ -1125,6 +1125,11 @@ void tcp_rate_check_app_limited(struct sock *sk)
tp->app_limited =
(tp->delivered + tcp_packets_in_flight(tp)) ? : 1;
}
+
+void tcp_rate_check_app_limited(struct sock *sk)
+{
+ tcp_sock_rate_check_app_limited(tcp_sk(sk));
+}
EXPORT_SYMBOL_GPL(tcp_rate_check_app_limited);
int tcp_sendmsg_locked(struct sock *sk, struct msghdr *msg, size_t size)
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index fdfe6145f6da..f6351b663a01 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -24,11 +24,12 @@
#include <net/mptcp.h>
#include <net/hotdata.h>
#include <net/xfrm.h>
+#include <net/tls.h>
#include <asm/ioctls.h>
#include "protocol.h"
#include "mib.h"
-static unsigned int mptcp_inq_hint(const struct sock *sk);
+static unsigned int mptcp_inq_hint(struct sock *sk);
#define CREATE_TRACE_POINTS
#include <trace/events/mptcp.h>
@@ -1885,7 +1886,7 @@ static void mptcp_rps_record_subflows(const struct mptcp_sock *msk)
}
}
-static int mptcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
+static int mptcp_sendmsg_locked(struct sock *sk, struct msghdr *msg, size_t len)
{
struct mptcp_sock *msk = mptcp_sk(sk);
struct page_frag *pfrag;
@@ -1896,8 +1897,6 @@ static int mptcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
/* silently ignore everything else */
msg->msg_flags &= MSG_MORE | MSG_DONTWAIT | MSG_NOSIGNAL | MSG_FASTOPEN;
- lock_sock(sk);
-
mptcp_rps_record_subflows(msk);
if (unlikely(inet_test_bit(DEFER_CONNECT, sk) ||
@@ -2005,7 +2004,6 @@ static int mptcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
__mptcp_push_pending(sk, msg->msg_flags);
out:
- release_sock(sk);
return copied;
do_error:
@@ -2016,6 +2014,17 @@ static int mptcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
goto out;
}
+static int mptcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
+{
+ int ret;
+
+ lock_sock(sk);
+ ret = mptcp_sendmsg_locked(sk, msg, len);
+ release_sock(sk);
+
+ return ret;
+}
+
static void mptcp_rcv_space_adjust(struct mptcp_sock *msk, int copied);
static void mptcp_eat_recv_skb(struct sock *sk, struct sk_buff *skb)
@@ -2244,7 +2253,7 @@ static bool mptcp_move_skbs(struct sock *sk)
return enqueued;
}
-static unsigned int mptcp_inq_hint(const struct sock *sk)
+static int mptcp_inq(struct sock *sk)
{
const struct mptcp_sock *msk = mptcp_sk(sk);
const struct sk_buff *skb;
@@ -2259,6 +2268,16 @@ static unsigned int mptcp_inq_hint(const struct sock *sk)
return (unsigned int)hint_val;
}
+ return 0;
+}
+
+static unsigned int mptcp_inq_hint(struct sock *sk)
+{
+ unsigned int inq = mptcp_inq(sk);
+
+ if (inq)
+ return inq;
+
if (sk->sk_state == TCP_CLOSE || (sk->sk_shutdown & RCV_SHUTDOWN))
return 1;
@@ -4684,3 +4703,78 @@ int __init mptcp_proto_v6_init(void)
return err;
}
#endif
+
+static void mptcp_read_done(struct sock *sk, size_t len)
+{
+ struct mptcp_sock *msk = mptcp_sk(sk);
+ struct sk_buff *skb;
+ size_t left;
+ u32 offset;
+
+ msk_owned_by_me(msk);
+
+ if (sk->sk_state == TCP_LISTEN)
+ return;
+
+ left = len;
+ while (left && (skb = mptcp_recv_skb(sk, &offset)) != NULL) {
+ int used;
+
+ used = min_t(size_t, skb->len - offset, left);
+ msk->bytes_consumed += used;
+ MPTCP_SKB_CB(skb)->offset += used;
+ MPTCP_SKB_CB(skb)->map_seq += used;
+ left -= used;
+
+ if (skb->len > offset + used)
+ break;
+
+ mptcp_eat_recv_skb(sk, skb);
+ }
+
+ mptcp_rcv_space_adjust(msk, len - left);
+
+ /* Clean up data we have read: This will do ACK frames. */
+ if (left != len)
+ mptcp_cleanup_rbuf(msk, len - left);
+}
+
+static u32 mptcp_get_skb_off(struct sk_buff *skb)
+{
+ return MPTCP_SKB_CB(skb)->offset;
+}
+
+static u32 mptcp_get_skb_seq(struct sk_buff *skb)
+{
+ return MPTCP_SKB_CB(skb)->map_seq;
+}
+
+static void mptcp_check_app_limited(struct sock *sk)
+{
+ struct mptcp_sock *msk = mptcp_sk(sk);
+ struct mptcp_subflow_context *subflow;
+
+ mptcp_for_each_subflow(msk, subflow) {
+ struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
+ bool slow;
+
+ slow = lock_sock_fast(ssk);
+ tcp_sock_rate_check_app_limited(tcp_sk(ssk));
+ unlock_sock_fast(ssk, slow);
+ }
+}
+
+struct tls_prot_ops tls_mptcp_ops = {
+ .protocol = IPPROTO_MPTCP,
+ .inq = mptcp_inq,
+ .sendmsg_locked = mptcp_sendmsg_locked,
+ .recv_skb = mptcp_recv_skb,
+ .read_sock = mptcp_read_sock,
+ .read_done = mptcp_read_done,
+ .get_skb_off = mptcp_get_skb_off,
+ .get_skb_seq = mptcp_get_skb_seq,
+ .poll = mptcp_poll,
+ .epollin_ready = mptcp_epollin_ready,
+ .check_app_limited = mptcp_check_app_limited,
+};
+EXPORT_SYMBOL(tls_mptcp_ops);
diff --git a/net/tls/tls_main.c b/net/tls/tls_main.c
index 7c537b7fbabb..79c7f2efcda8 100644
--- a/net/tls/tls_main.c
+++ b/net/tls/tls_main.c
@@ -772,6 +772,9 @@ static int do_tls_setsockopt_conf(struct sock *sk, sockptr_t optval,
tls_sw_strparser_arm(sk, ctx);
}
+ if (conf == TLS_HW && sk->sk_protocol == IPPROTO_MPTCP)
+ return -EOPNOTSUPP;
+
if (tx)
ctx->tx_conf = conf;
else
@@ -1346,6 +1349,9 @@ static int __init tls_register(void)
tcp_register_ulp(&tcp_tls_ulp_ops);
tls_register_prot_ops(&tls_tcp_ops);
+#ifdef CONFIG_MPTCP
+ tls_register_prot_ops(&tls_mptcp_ops);
+#endif
return 0;
err_strp:
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [RFC mptcp-next v9 07/10] mptcp: update ULP getsockopt
2026-03-13 1:42 [RFC mptcp-next v9 00/10] MPTCP KTLS support Geliang Tang
` (5 preceding siblings ...)
2026-03-13 1:42 ` [RFC mptcp-next v9 06/10] mptcp: implement tls_mptcp_ops Geliang Tang
@ 2026-03-13 1:42 ` Geliang Tang
2026-03-13 1:42 ` [RFC mptcp-next v9 08/10] mptcp: enable TLS setsockopt Geliang Tang
` (3 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Geliang Tang @ 2026-03-13 1:42 UTC (permalink / raw)
To: mptcp; +Cc: Geliang Tang, Gang Yan
From: Geliang Tang <tanggeliang@kylinos.cn>
This patch extracts TCP_ULP getsockopt operation into a tcp_sock_get_ulp()
helper so that it can also be used in MPTCP.
TCP_ULP was obtained by calling mptcp_getsockopt_first_sf_only() to get
ULP of the first subflow. Now that the mechanism has changed, a new helper
mptcp_getsockopt_tcp_ulp() is added to get ULP of msk.
Co-developed-by: Gang Yan <yangang@kylinos.cn>
Signed-off-by: Gang Yan <yangang@kylinos.cn>
Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn>
---
include/linux/tcp.h | 1 +
net/ipv4/tcp.c | 36 ++++++++++++++++++++++--------------
net/mptcp/sockopt.c | 12 ++++++++++++
3 files changed, 35 insertions(+), 14 deletions(-)
diff --git a/include/linux/tcp.h b/include/linux/tcp.h
index c44cf9ae8d16..2e673f7b10ef 100644
--- a/include/linux/tcp.h
+++ b/include/linux/tcp.h
@@ -643,6 +643,7 @@ void tcp_sock_set_quickack(struct sock *sk, int val);
int tcp_sock_set_syncnt(struct sock *sk, int val);
int tcp_sock_set_user_timeout(struct sock *sk, int val);
int tcp_sock_set_maxseg(struct sock *sk, int val);
+int tcp_sock_get_ulp(struct sock *sk, sockptr_t optval, sockptr_t optlen);
static inline bool dst_tcp_usec_ts(const struct dst_entry *dst)
{
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 400f84f193a3..63b576eebae6 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -4494,6 +4494,27 @@ struct sk_buff *tcp_get_timestamping_opt_stats(const struct sock *sk,
return stats;
}
+int tcp_sock_get_ulp(struct sock *sk, sockptr_t optval, sockptr_t optlen)
+{
+ struct inet_connection_sock *icsk = inet_csk(sk);
+ int len;
+
+ if (copy_from_sockptr(&len, optlen, sizeof(int)))
+ return -EFAULT;
+ len = min_t(unsigned int, len, TCP_ULP_NAME_MAX);
+ if (!icsk->icsk_ulp_ops) {
+ len = 0;
+ if (copy_to_sockptr(optlen, &len, sizeof(int)))
+ return -EFAULT;
+ return 0;
+ }
+ if (copy_to_sockptr(optlen, &len, sizeof(int)))
+ return -EFAULT;
+ if (copy_to_sockptr(optval, icsk->icsk_ulp_ops->name, len))
+ return -EFAULT;
+ return 0;
+}
+
int do_tcp_getsockopt(struct sock *sk, int level,
int optname, sockptr_t optval, sockptr_t optlen)
{
@@ -4603,20 +4624,7 @@ int do_tcp_getsockopt(struct sock *sk, int level,
return 0;
case TCP_ULP:
- if (copy_from_sockptr(&len, optlen, sizeof(int)))
- return -EFAULT;
- len = min_t(unsigned int, len, TCP_ULP_NAME_MAX);
- if (!icsk->icsk_ulp_ops) {
- len = 0;
- if (copy_to_sockptr(optlen, &len, sizeof(int)))
- return -EFAULT;
- return 0;
- }
- if (copy_to_sockptr(optlen, &len, sizeof(int)))
- return -EFAULT;
- if (copy_to_sockptr(optval, icsk->icsk_ulp_ops->name, len))
- return -EFAULT;
- return 0;
+ return tcp_sock_get_ulp(sk, optval, optlen);
case TCP_FASTOPEN_KEY: {
u64 key[TCP_FASTOPEN_KEY_BUF_LENGTH / sizeof(u64)];
diff --git a/net/mptcp/sockopt.c b/net/mptcp/sockopt.c
index de90a2897d2d..a6230f7910fd 100644
--- a/net/mptcp/sockopt.c
+++ b/net/mptcp/sockopt.c
@@ -1393,6 +1393,17 @@ static int mptcp_put_int_option(struct mptcp_sock *msk, char __user *optval,
return 0;
}
+static int mptcp_getsockopt_tcp_ulp(struct sock *sk, char __user *optval,
+ int __user *optlen)
+{
+ int ret;
+
+ lock_sock(sk);
+ ret = tcp_sock_get_ulp(sk, USER_SOCKPTR(optval), USER_SOCKPTR(optlen));
+ release_sock(sk);
+ return ret;
+}
+
static int mptcp_getsockopt_sol_tcp(struct mptcp_sock *msk, int optname,
char __user *optval, int __user *optlen)
{
@@ -1400,6 +1411,7 @@ static int mptcp_getsockopt_sol_tcp(struct mptcp_sock *msk, int optname,
switch (optname) {
case TCP_ULP:
+ return mptcp_getsockopt_tcp_ulp(sk, optval, optlen);
case TCP_CONGESTION:
case TCP_INFO:
case TCP_CC_INFO:
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [RFC mptcp-next v9 08/10] mptcp: enable TLS setsockopt
2026-03-13 1:42 [RFC mptcp-next v9 00/10] MPTCP KTLS support Geliang Tang
` (6 preceding siblings ...)
2026-03-13 1:42 ` [RFC mptcp-next v9 07/10] mptcp: update ULP getsockopt Geliang Tang
@ 2026-03-13 1:42 ` Geliang Tang
2026-03-13 1:42 ` [RFC mptcp-next v9 09/10] selftests: mptcp: connect: set smc instead of tls Geliang Tang
` (2 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Geliang Tang @ 2026-03-13 1:42 UTC (permalink / raw)
To: mptcp; +Cc: Geliang Tang, Gang Yan
From: Geliang Tang <tanggeliang@kylinos.cn>
This patch extracts TCP_ULP setsockopt operation into a tcp_sock_set_ulp()
helper so that it can also be used in MPTCP.
Add MPTCP TLS setsockopt support in mptcp_setsockopt_sol_tcp(). It allows
setting the TCP_ULP option to 'tls' exclusively, and enables configuration
of the TLS_TX and TLS_RX options at the SOL_TLS level.
This option cannot be set when the socket is in CLOSE or LISTEN state.
Co-developed-by: Gang Yan <yangang@kylinos.cn>
Signed-off-by: Gang Yan <yangang@kylinos.cn>
Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn>
---
include/linux/tcp.h | 1 +
net/ipv4/tcp.c | 42 ++++++++++++++++++++++++------------------
net/mptcp/sockopt.c | 25 ++++++++++++++++++++++++-
3 files changed, 49 insertions(+), 19 deletions(-)
diff --git a/include/linux/tcp.h b/include/linux/tcp.h
index 2e673f7b10ef..79bf6356b98f 100644
--- a/include/linux/tcp.h
+++ b/include/linux/tcp.h
@@ -644,6 +644,7 @@ int tcp_sock_set_syncnt(struct sock *sk, int val);
int tcp_sock_set_user_timeout(struct sock *sk, int val);
int tcp_sock_set_maxseg(struct sock *sk, int val);
int tcp_sock_get_ulp(struct sock *sk, sockptr_t optval, sockptr_t optlen);
+int tcp_sock_set_ulp(struct sock *sk, sockptr_t optval, unsigned int optlen);
static inline bool dst_tcp_usec_ts(const struct dst_entry *dst)
{
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 63b576eebae6..6cfb2e3faf73 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -3857,6 +3857,28 @@ int tcp_sock_set_maxseg(struct sock *sk, int val)
return 0;
}
+int tcp_sock_set_ulp(struct sock *sk, sockptr_t optval, unsigned int optlen)
+{
+ char name[TCP_ULP_NAME_MAX];
+ int err = 0;
+ size_t len;
+ int val;
+
+ if (optlen < 1)
+ return -EINVAL;
+
+ len = min_t(long, TCP_ULP_NAME_MAX - 1, optlen);
+ val = strncpy_from_sockptr(name, optval, len);
+ if (val < 0)
+ return -EFAULT;
+ name[val] = 0;
+
+ sockopt_lock_sock(sk);
+ err = tcp_set_ulp(sk, name);
+ sockopt_release_sock(sk);
+ return err;
+}
+
/*
* Socket option code for TCP.
*/
@@ -3890,24 +3912,8 @@ int do_tcp_setsockopt(struct sock *sk, int level, int optname,
sockopt_release_sock(sk);
return err;
}
- case TCP_ULP: {
- char name[TCP_ULP_NAME_MAX];
-
- if (optlen < 1)
- return -EINVAL;
-
- val = strncpy_from_sockptr(name, optval,
- min_t(long, TCP_ULP_NAME_MAX - 1,
- optlen));
- if (val < 0)
- return -EFAULT;
- name[val] = 0;
-
- sockopt_lock_sock(sk);
- err = tcp_set_ulp(sk, name);
- sockopt_release_sock(sk);
- return err;
- }
+ case TCP_ULP:
+ return tcp_sock_set_ulp(sk, optval, optlen);
case TCP_FASTOPEN_KEY: {
__u8 key[TCP_FASTOPEN_KEY_BUF_LENGTH];
__u8 *backup_key = NULL;
diff --git a/net/mptcp/sockopt.c b/net/mptcp/sockopt.c
index a6230f7910fd..aafc627b3da9 100644
--- a/net/mptcp/sockopt.c
+++ b/net/mptcp/sockopt.c
@@ -12,6 +12,7 @@
#include <net/protocol.h>
#include <net/tcp.h>
#include <net/mptcp.h>
+#include <net/tls.h>
#include "protocol.h"
#define MIN_INFO_OPTLEN_SIZE 16
@@ -567,6 +568,7 @@ static bool mptcp_supported_sockopt(int level, int optname)
case TCP_FASTOPEN_CONNECT:
case TCP_FASTOPEN_KEY:
case TCP_FASTOPEN_NO_COOKIE:
+ case TCP_ULP:
return true;
}
@@ -576,6 +578,13 @@ static bool mptcp_supported_sockopt(int level, int optname)
* TCP_REPAIR_WINDOW are not supported, better avoid this mess
*/
}
+ if (level == SOL_TLS) {
+ switch (optname) {
+ case TLS_TX:
+ case TLS_RX:
+ return true;
+ }
+ }
return false;
}
@@ -815,6 +824,20 @@ static int mptcp_setsockopt_all_sf(struct mptcp_sock *msk, int level,
return ret;
}
+static int mptcp_setsockopt_tcp_ulp(struct sock *sk, sockptr_t optval,
+ unsigned int optlen)
+{
+ char ulp[4] = "";
+
+ if (copy_from_user(ulp, optval.user, 4))
+ return -EFAULT;
+ if (strcmp(ulp, "tls\0"))
+ return -EOPNOTSUPP;
+ if ((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_LISTEN))
+ return -ENOTCONN;
+ return tcp_sock_set_ulp(sk, optval, optlen);
+}
+
static int mptcp_setsockopt_sol_tcp(struct mptcp_sock *msk, int optname,
sockptr_t optval, unsigned int optlen)
{
@@ -823,7 +846,7 @@ static int mptcp_setsockopt_sol_tcp(struct mptcp_sock *msk, int optname,
switch (optname) {
case TCP_ULP:
- return -EOPNOTSUPP;
+ return mptcp_setsockopt_tcp_ulp(sk, optval, optlen);
case TCP_CONGESTION:
return mptcp_setsockopt_sol_tcp_congestion(msk, optval, optlen);
case TCP_DEFER_ACCEPT:
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* [RFC mptcp-next v9 09/10] selftests: mptcp: connect: set smc instead of tls
2026-03-13 1:42 [RFC mptcp-next v9 00/10] MPTCP KTLS support Geliang Tang
` (7 preceding siblings ...)
2026-03-13 1:42 ` [RFC mptcp-next v9 08/10] mptcp: enable TLS setsockopt Geliang Tang
@ 2026-03-13 1:42 ` Geliang Tang
2026-03-13 6:26 ` Dust Li
2026-03-13 1:42 ` [RFC mptcp-next v9 10/10] selftests: mptcp: connect: add TLS tests Geliang Tang
2026-03-13 4:27 ` [RFC mptcp-next v9 00/10] MPTCP KTLS support MPTCP CI
10 siblings, 1 reply; 15+ messages in thread
From: Geliang Tang @ 2026-03-13 1:42 UTC (permalink / raw)
To: mptcp; +Cc: Geliang Tang, Dust Li, Gang Yan
From: Geliang Tang <tanggeliang@kylinos.cn>
With KTLS being implemented, "tls" should no longer be used in
sock_test_tcpulp(), it breaks mptcp_connect.sh tests. Another ULP
name, "smc", is set instead in this patch.
Cc: Dust Li <dust.li@linux.alibaba.com>
Co-developed-by: Gang Yan <yangang@kylinos.cn>
Signed-off-by: Gang Yan <yangang@kylinos.cn>
Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn>
---
tools/testing/selftests/net/mptcp/config | 1 +
tools/testing/selftests/net/mptcp/mptcp_connect.c | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/net/mptcp/config b/tools/testing/selftests/net/mptcp/config
index 59051ee2a986..18bd29ac5b24 100644
--- a/tools/testing/selftests/net/mptcp/config
+++ b/tools/testing/selftests/net/mptcp/config
@@ -34,3 +34,4 @@ CONFIG_NFT_SOCKET=m
CONFIG_NFT_TPROXY=m
CONFIG_SYN_COOKIES=y
CONFIG_VETH=y
+CONFIG_TLS=y
diff --git a/tools/testing/selftests/net/mptcp/mptcp_connect.c b/tools/testing/selftests/net/mptcp/mptcp_connect.c
index cbe573c4ab3a..64c8a4bfe749 100644
--- a/tools/testing/selftests/net/mptcp/mptcp_connect.c
+++ b/tools/testing/selftests/net/mptcp/mptcp_connect.c
@@ -289,7 +289,7 @@ static void sock_test_tcpulp(int sock, int proto, unsigned int line)
if (ret == 0)
X("setsockopt");
} else if (proto == IPPROTO_MPTCP) {
- ret = do_ulp_so(sock, "tls");
+ ret = do_ulp_so(sock, "smc");
if (ret != -1)
X("setsockopt");
}
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* Re: [RFC mptcp-next v9 09/10] selftests: mptcp: connect: set smc instead of tls
2026-03-13 1:42 ` [RFC mptcp-next v9 09/10] selftests: mptcp: connect: set smc instead of tls Geliang Tang
@ 2026-03-13 6:26 ` Dust Li
2026-03-13 8:04 ` Geliang Tang
0 siblings, 1 reply; 15+ messages in thread
From: Dust Li @ 2026-03-13 6:26 UTC (permalink / raw)
To: Geliang Tang, mptcp; +Cc: Geliang Tang, Gang Yan
On 2026-03-13 09:42:51, Geliang Tang wrote:
>From: Geliang Tang <tanggeliang@kylinos.cn>
Hi Geliang,
Thanks for the effort !
But it's a pitty that SMC ULP support was removed in
df31a6b0a305(Revert "net/smc: Introduce TCP ULP support") due to
it violates some VFS assuptions :(
Best regards,
Dust
>
>With KTLS being implemented, "tls" should no longer be used in
>sock_test_tcpulp(), it breaks mptcp_connect.sh tests. Another ULP
>name, "smc", is set instead in this patch.
>
>Cc: Dust Li <dust.li@linux.alibaba.com>
>Co-developed-by: Gang Yan <yangang@kylinos.cn>
>Signed-off-by: Gang Yan <yangang@kylinos.cn>
>Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn>
>---
> tools/testing/selftests/net/mptcp/config | 1 +
> tools/testing/selftests/net/mptcp/mptcp_connect.c | 2 +-
> 2 files changed, 2 insertions(+), 1 deletion(-)
>
>diff --git a/tools/testing/selftests/net/mptcp/config b/tools/testing/selftests/net/mptcp/config
>index 59051ee2a986..18bd29ac5b24 100644
>--- a/tools/testing/selftests/net/mptcp/config
>+++ b/tools/testing/selftests/net/mptcp/config
>@@ -34,3 +34,4 @@ CONFIG_NFT_SOCKET=m
> CONFIG_NFT_TPROXY=m
> CONFIG_SYN_COOKIES=y
> CONFIG_VETH=y
>+CONFIG_TLS=y
>diff --git a/tools/testing/selftests/net/mptcp/mptcp_connect.c b/tools/testing/selftests/net/mptcp/mptcp_connect.c
>index cbe573c4ab3a..64c8a4bfe749 100644
>--- a/tools/testing/selftests/net/mptcp/mptcp_connect.c
>+++ b/tools/testing/selftests/net/mptcp/mptcp_connect.c
>@@ -289,7 +289,7 @@ static void sock_test_tcpulp(int sock, int proto, unsigned int line)
> if (ret == 0)
> X("setsockopt");
> } else if (proto == IPPROTO_MPTCP) {
>- ret = do_ulp_so(sock, "tls");
>+ ret = do_ulp_so(sock, "smc");
> if (ret != -1)
> X("setsockopt");
> }
>--
>2.53.0
^ permalink raw reply [flat|nested] 15+ messages in thread* Re: [RFC mptcp-next v9 09/10] selftests: mptcp: connect: set smc instead of tls
2026-03-13 6:26 ` Dust Li
@ 2026-03-13 8:04 ` Geliang Tang
0 siblings, 0 replies; 15+ messages in thread
From: Geliang Tang @ 2026-03-13 8:04 UTC (permalink / raw)
To: dust.li, mptcp; +Cc: Geliang Tang, Gang Yan
Hi Dust,
On Fri, 2026-03-13 at 14:26 +0800, Dust Li wrote:
> On 2026-03-13 09:42:51, Geliang Tang wrote:
> > From: Geliang Tang <tanggeliang@kylinos.cn>
>
> Hi Geliang,
>
> Thanks for the effort !
> But it's a pitty that SMC ULP support was removed in
> df31a6b0a305(Revert "net/smc: Introduce TCP ULP support") due to
> it violates some VFS assuptions :(
Thanks for the reminder. I will consider using another ULP instead.
-Geliang
>
> Best regards,
> Dust
>
> >
> > With KTLS being implemented, "tls" should no longer be used in
> > sock_test_tcpulp(), it breaks mptcp_connect.sh tests. Another ULP
> > name, "smc", is set instead in this patch.
> >
> > Cc: Dust Li <dust.li@linux.alibaba.com>
> > Co-developed-by: Gang Yan <yangang@kylinos.cn>
> > Signed-off-by: Gang Yan <yangang@kylinos.cn>
> > Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn>
> > ---
> > tools/testing/selftests/net/mptcp/config | 1 +
> > tools/testing/selftests/net/mptcp/mptcp_connect.c | 2 +-
> > 2 files changed, 2 insertions(+), 1 deletion(-)
> >
> > diff --git a/tools/testing/selftests/net/mptcp/config
> > b/tools/testing/selftests/net/mptcp/config
> > index 59051ee2a986..18bd29ac5b24 100644
> > --- a/tools/testing/selftests/net/mptcp/config
> > +++ b/tools/testing/selftests/net/mptcp/config
> > @@ -34,3 +34,4 @@ CONFIG_NFT_SOCKET=m
> > CONFIG_NFT_TPROXY=m
> > CONFIG_SYN_COOKIES=y
> > CONFIG_VETH=y
> > +CONFIG_TLS=y
> > diff --git a/tools/testing/selftests/net/mptcp/mptcp_connect.c
> > b/tools/testing/selftests/net/mptcp/mptcp_connect.c
> > index cbe573c4ab3a..64c8a4bfe749 100644
> > --- a/tools/testing/selftests/net/mptcp/mptcp_connect.c
> > +++ b/tools/testing/selftests/net/mptcp/mptcp_connect.c
> > @@ -289,7 +289,7 @@ static void sock_test_tcpulp(int sock, int
> > proto, unsigned int line)
> > if (ret == 0)
> > X("setsockopt");
> > } else if (proto == IPPROTO_MPTCP) {
> > - ret = do_ulp_so(sock, "tls");
> > + ret = do_ulp_so(sock, "smc");
> > if (ret != -1)
> > X("setsockopt");
> > }
> > --
> > 2.53.0
^ permalink raw reply [flat|nested] 15+ messages in thread
* [RFC mptcp-next v9 10/10] selftests: mptcp: connect: add TLS tests
2026-03-13 1:42 [RFC mptcp-next v9 00/10] MPTCP KTLS support Geliang Tang
` (8 preceding siblings ...)
2026-03-13 1:42 ` [RFC mptcp-next v9 09/10] selftests: mptcp: connect: set smc instead of tls Geliang Tang
@ 2026-03-13 1:42 ` Geliang Tang
2026-03-13 4:27 ` [RFC mptcp-next v9 00/10] MPTCP KTLS support MPTCP CI
10 siblings, 0 replies; 15+ messages in thread
From: Geliang Tang @ 2026-03-13 1:42 UTC (permalink / raw)
To: mptcp; +Cc: Geliang Tang, Gang Yan
From: Geliang Tang <tanggeliang@kylinos.cn>
This patch adds MPTCP TLS tests for mptcp_connect.c/mptcp_connect.sh.
A new TLS type has been added to cfg_sockopt_types, enabled via the
parameter "-o TLS". do_setsockopt_tls() has been implemented to set
TLS parameters for both the server and client.
After adding TLS configuration, sock_test_tcpulp() needs to be updated
as getsockopt ULP may now return not only "mptcp" but also "tls".
These tests report "read: Resource temporarily unavailable" errors
occasionally, which is fixed by adding handling for EAGAIN in
copyfd_io_poll().
Co-developed-by: Gang Yan <yangang@kylinos.cn>
Signed-off-by: Gang Yan <yangang@kylinos.cn>
Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn>
---
.../selftests/net/mptcp/mptcp_connect.c | 47 ++++++++++++++++++-
.../selftests/net/mptcp/mptcp_connect.sh | 33 +++++++++++++
2 files changed, 78 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/net/mptcp/mptcp_connect.c b/tools/testing/selftests/net/mptcp/mptcp_connect.c
index 64c8a4bfe749..0b4428215236 100644
--- a/tools/testing/selftests/net/mptcp/mptcp_connect.c
+++ b/tools/testing/selftests/net/mptcp/mptcp_connect.c
@@ -34,6 +34,7 @@
#include <linux/time_types.h>
#include <linux/sockios.h>
#include <linux/compiler.h>
+#include <linux/tls.h>
extern int optind;
@@ -89,6 +90,7 @@ struct cfg_cmsg_types {
struct cfg_sockopt_types {
unsigned int transparent:1;
unsigned int mptfo:1;
+ unsigned int tls:1;
};
struct tcp_inq_state {
@@ -272,6 +274,35 @@ static int do_ulp_so(int sock, const char *name)
return setsockopt(sock, IPPROTO_TCP, TCP_ULP, name, strlen(name));
}
+static void do_setsockopt_tls(int fd)
+{
+ struct tls12_crypto_info_aes_gcm_128 tls_tx = {
+ .info = {
+ .version = TLS_1_2_VERSION,
+ .cipher_type = TLS_CIPHER_AES_GCM_128,
+ },
+ };
+ struct tls12_crypto_info_aes_gcm_128 tls_rx = {
+ .info = {
+ .version = TLS_1_2_VERSION,
+ .cipher_type = TLS_CIPHER_AES_GCM_128,
+ },
+ };
+ int err;
+
+ err = do_ulp_so(fd, "tls");
+ if (err)
+ xerror("setsockopt TCP_ULP");
+
+ err = setsockopt(fd, SOL_TLS, TLS_TX, (void *)&tls_tx, sizeof(tls_tx));
+ if (err)
+ xerror("setsockopt TLS_TX");
+
+ err = setsockopt(fd, SOL_TLS, TLS_RX, (void *)&tls_rx, sizeof(tls_rx));
+ if (err)
+ xerror("setsockopt TLS_RX");
+}
+
#define X(m) xerror("%s:%u: %s: failed for proto %d at line %u", __FILE__, __LINE__, (m), proto, line)
static void sock_test_tcpulp(int sock, int proto, unsigned int line)
{
@@ -283,7 +314,7 @@ static void sock_test_tcpulp(int sock, int proto, unsigned int line)
X("getsockopt");
if (buflen > 0) {
- if (strcmp(buf, "mptcp") != 0)
+ if (strcmp(buf, "mptcp") != 0 && strcmp(buf, "tls") != 0)
xerror("unexpected ULP '%s' for proto %d at line %u", buf, proto, line);
ret = do_ulp_so(sock, "tls");
if (ret == 0)
@@ -422,8 +453,11 @@ static int sock_connect_mptcp(const char * const remoteaddr,
}
freeaddrinfo(addr);
- if (sock != -1)
+ if (sock != -1) {
SOCK_TEST_TCPULP(sock, proto);
+ if (cfg_sockopt_types.tls)
+ do_setsockopt_tls(sock);
+ }
return sock;
}
@@ -684,6 +718,8 @@ static int copyfd_io_poll(int infd, int peerfd, int outfd,
/* Else, still have data to transmit */
} else if (len < 0) {
+ if (errno == EAGAIN)
+ continue;
if (cfg_rcv_trunc)
return 0;
perror("read");
@@ -1212,6 +1248,8 @@ int main_loop_s(int listensock)
}
SOCK_TEST_TCPULP(remotesock, 0);
+ if (cfg_sockopt_types.tls)
+ do_setsockopt_tls(remotesock);
memset(&winfo, 0, sizeof(winfo));
err = copyfd_io(fd, remotesock, 1, true, &winfo);
@@ -1312,6 +1350,11 @@ static void parse_setsock_options(const char *name)
return;
}
+ if (strncmp(name, "TLS", len) == 0) {
+ cfg_sockopt_types.tls = 1;
+ return;
+ }
+
fprintf(stderr, "Unrecognized setsockopt option %s\n", name);
exit(1);
}
diff --git a/tools/testing/selftests/net/mptcp/mptcp_connect.sh b/tools/testing/selftests/net/mptcp/mptcp_connect.sh
index a6447f7a31fe..ef8d6ee22b00 100755
--- a/tools/testing/selftests/net/mptcp/mptcp_connect.sh
+++ b/tools/testing/selftests/net/mptcp/mptcp_connect.sh
@@ -815,6 +815,36 @@ run_tests_disconnect()
connect_per_transfer=1
}
+run_tests_tls()
+{
+ TEST_GROUP="TLS"
+ local lret=0
+
+ if ! mptcp_lib_kallsyms_has "mptcp_read_done"; then
+ mptcp_lib_pr_skip "TLS not supported by the kernel"
+ mptcp_lib_result_skip "${TEST_GROUP}"
+ return
+ fi
+
+ mptcp_lib_pr_info "with TLS start"
+
+ do_transfer "$ns1" "$ns2" MPTCP MPTCP "10.0.1.1" "0.0.0.0" "-o TLS"
+ lret=$?
+ if [ $lret -ne 0 ]; then
+ ret=$lret
+ return 1
+ fi
+
+ do_transfer "$ns1" "$ns2" MPTCP MPTCP "dead:beef:1::1" "::" "-o TLS"
+ lret=$?
+ if [ $lret -ne 0 ]; then
+ ret=$lret
+ return 1
+ fi
+
+ mptcp_lib_pr_info "with TLS end"
+}
+
display_time()
{
time_end=$(date +%s)
@@ -959,6 +989,9 @@ log_if_error "Tests with tproxy have failed"
run_tests_disconnect
log_if_error "Tests of the full disconnection have failed"
+run_tests_tls
+log_if_error "Tests with TLS have failed"
+
display_time
mptcp_lib_result_print_all_tap
exit ${final_ret}
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread* Re: [RFC mptcp-next v9 00/10] MPTCP KTLS support
2026-03-13 1:42 [RFC mptcp-next v9 00/10] MPTCP KTLS support Geliang Tang
` (9 preceding siblings ...)
2026-03-13 1:42 ` [RFC mptcp-next v9 10/10] selftests: mptcp: connect: add TLS tests Geliang Tang
@ 2026-03-13 4:27 ` MPTCP CI
2026-03-13 8:07 ` Geliang Tang
10 siblings, 1 reply; 15+ messages in thread
From: MPTCP CI @ 2026-03-13 4:27 UTC (permalink / raw)
To: Geliang Tang; +Cc: mptcp
Hi Geliang,
Thank you for your modifications, that's great!
Our CI did some validations and here is its report:
- KVM Validation: normal (except selftest_mptcp_join): Unstable: 1 failed test(s): selftest_mptcp_connect_splice 🔴
- KVM Validation: normal (only selftest_mptcp_join): Success! ✅
- KVM Validation: debug (except selftest_mptcp_join): Unstable: 4 failed test(s): packetdrill_dss packetdrill_mp_capable selftest_diag selftest_mptcp_connect_splice 🔴
- KVM Validation: debug (only selftest_mptcp_join): Success! ✅
- KVM Validation: btf-normal (only bpftest_all): Success! ✅
- KVM Validation: btf-debug (only bpftest_all): Success! ✅
- Task: https://github.com/multipath-tcp/mptcp_net-next/actions/runs/23032875328
Initiator: Patchew Applier
Commits: https://github.com/multipath-tcp/mptcp_net-next/commits/6363c87fa2fa
Patchwork: https://patchwork.kernel.org/project/mptcp/list/?series=1066012
If there are some issues, you can reproduce them using the same environment as
the one used by the CI thanks to a docker image, e.g.:
$ cd [kernel source code]
$ docker run -v "${PWD}:${PWD}:rw" -w "${PWD}" --privileged --rm -it \
--pull always mptcp/mptcp-upstream-virtme-docker:latest \
auto-normal
For more details:
https://github.com/multipath-tcp/mptcp-upstream-virtme-docker
Please note that despite all the efforts that have been already done to have a
stable tests suite when executed on a public CI like here, it is possible some
reported issues are not due to your modifications. Still, do not hesitate to
help us improve that ;-)
Cheers,
MPTCP GH Action bot
Bot operated by Matthieu Baerts (NGI0 Core)
^ permalink raw reply [flat|nested] 15+ messages in thread* Re: [RFC mptcp-next v9 00/10] MPTCP KTLS support
2026-03-13 4:27 ` [RFC mptcp-next v9 00/10] MPTCP KTLS support MPTCP CI
@ 2026-03-13 8:07 ` Geliang Tang
0 siblings, 0 replies; 15+ messages in thread
From: Geliang Tang @ 2026-03-13 8:07 UTC (permalink / raw)
To: mptcp, Geliang Tang
On Fri, 2026-03-13 at 04:27 +0000, MPTCP CI wrote:
> Hi Geliang,
>
> Thank you for your modifications, that's great!
>
> Our CI did some validations and here is its report:
>
> - KVM Validation: normal (except selftest_mptcp_join): Unstable: 1
> failed test(s): selftest_mptcp_connect_splice 🔴
> - KVM Validation: normal (only selftest_mptcp_join): Success! ✅
> - KVM Validation: debug (except selftest_mptcp_join): Unstable: 4
> failed test(s): packetdrill_dss packetdrill_mp_capable selftest_diag
> selftest_mptcp_connect_splice 🔴
Patch 10 broke the mptcp_connect_splice.sh test. I will find a way to
fix it.
Thanks,
-Geliang
> - KVM Validation: debug (only selftest_mptcp_join): Success! ✅
> - KVM Validation: btf-normal (only bpftest_all): Success! ✅
> - KVM Validation: btf-debug (only bpftest_all): Success! ✅
> - Task:
> https://github.com/multipath-tcp/mptcp_net-next/actions/runs/23032875328
>
> Initiator: Patchew Applier
> Commits:
> https://github.com/multipath-tcp/mptcp_net-next/commits/6363c87fa2fa
> Patchwork:
> https://patchwork.kernel.org/project/mptcp/list/?series=1066012
>
>
> If there are some issues, you can reproduce them using the same
> environment as
> the one used by the CI thanks to a docker image, e.g.:
>
> $ cd [kernel source code]
> $ docker run -v "${PWD}:${PWD}:rw" -w "${PWD}" --privileged --rm
> -it \
> --pull always mptcp/mptcp-upstream-virtme-docker:latest \
> auto-normal
>
> For more details:
>
> https://github.com/multipath-tcp/mptcp-upstream-virtme-docker
>
>
> Please note that despite all the efforts that have been already done
> to have a
> stable tests suite when executed on a public CI like here, it is
> possible some
> reported issues are not due to your modifications. Still, do not
> hesitate to
> help us improve that ;-)
>
> Cheers,
> MPTCP GH Action bot
> Bot operated by Matthieu Baerts (NGI0 Core)
^ permalink raw reply [flat|nested] 15+ messages in thread