From: Geliang Tang <tanggeliang@kylinos.cn>
The TCP_MAXSEG socket option is currently not supported by MPTCP, mainly
because it has never been requested before. But there are still valid
use-cases, e.g. with HAProxy.
This patch adds its support in MPTCP by propagating the value to all
subflows.
Similar to mptcp_setsockopt_first_sf_only(), a generic helper
mptcp_setsockopt_all_subflows() is added to set sockopt for each
subflows of the mptcp socket.
Add a new member for struct mptcp_sock to store the TCP_MAXSEG value,
and return this value in getsockopt.
Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/515
Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn>
---
net/mptcp/protocol.h | 1 +
net/mptcp/sockopt.c | 24 ++++++++++++++++++++++++
2 files changed, 25 insertions(+)
diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
index 64aa091cb685..91aaed17fe56 100644
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -327,6 +327,7 @@ struct mptcp_sock {
int keepalive_cnt;
int keepalive_idle;
int keepalive_intvl;
+ int maxseg;
struct work_struct work;
struct sk_buff *ooo_last_skb;
struct rb_root out_of_order_queue;
diff --git a/net/mptcp/sockopt.c b/net/mptcp/sockopt.c
index 3caa0a9d3b38..a7992cbc5ed9 100644
--- a/net/mptcp/sockopt.c
+++ b/net/mptcp/sockopt.c
@@ -798,6 +798,22 @@ static int mptcp_setsockopt_first_sf_only(struct mptcp_sock *msk, int level, int
return ret;
}
+static int mptcp_setsockopt_all_subflows(struct mptcp_sock *msk, int level, int optname,
+ sockptr_t optval, unsigned int optlen)
+{
+ struct mptcp_subflow_context *subflow;
+
+ mptcp_for_each_subflow(msk, subflow) {
+ struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
+ int ret = 0;
+
+ ret = tcp_setsockopt(ssk, level, optname, optval, optlen);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
static int mptcp_setsockopt_sol_tcp(struct mptcp_sock *msk, int optname,
sockptr_t optval, unsigned int optlen)
{
@@ -859,6 +875,11 @@ static int mptcp_setsockopt_sol_tcp(struct mptcp_sock *msk, int optname,
&msk->keepalive_cnt,
val);
break;
+ case TCP_MAXSEG:
+ msk->maxseg = val;
+ ret = mptcp_setsockopt_all_subflows(msk, SOL_TCP, optname,
+ optval, optlen);
+ break;
default:
ret = -ENOPROTOOPT;
}
@@ -1407,6 +1428,8 @@ static int mptcp_getsockopt_sol_tcp(struct mptcp_sock *msk, int optname,
return mptcp_put_int_option(msk, optval, optlen, msk->notsent_lowat);
case TCP_IS_MPTCP:
return mptcp_put_int_option(msk, optval, optlen, 1);
+ case TCP_MAXSEG:
+ return mptcp_put_int_option(msk, optval, optlen, msk->maxseg);
}
return -EOPNOTSUPP;
}
@@ -1553,6 +1576,7 @@ static void sync_socket_options(struct mptcp_sock *msk, struct sock *ssk)
tcp_sock_set_keepidle_locked(ssk, msk->keepalive_idle);
tcp_sock_set_keepintvl(ssk, msk->keepalive_intvl);
tcp_sock_set_keepcnt(ssk, msk->keepalive_cnt);
+ tcp_sock_set_maxseg(ssk, msk->maxseg);
inet_assign_bit(TRANSPARENT, ssk, inet_test_bit(TRANSPARENT, sk));
inet_assign_bit(FREEBIND, ssk, inet_test_bit(FREEBIND, sk));
--
2.43.0
Hi Geliang,
On 23/04/2025 11:40, Geliang Tang wrote:
> From: Geliang Tang <tanggeliang@kylinos.cn>
>
> The TCP_MAXSEG socket option is currently not supported by MPTCP, mainly
> because it has never been requested before. But there are still valid
> use-cases, e.g. with HAProxy.
>
> This patch adds its support in MPTCP by propagating the value to all
> subflows.
>
> Similar to mptcp_setsockopt_first_sf_only(), a generic helper
> mptcp_setsockopt_all_subflows() is added to set sockopt for each
> subflows of the mptcp socket.
>
> Add a new member for struct mptcp_sock to store the TCP_MAXSEG value,
> and return this value in getsockopt.
>
> Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/515
> Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn>
> ---
> net/mptcp/protocol.h | 1 +
> net/mptcp/sockopt.c | 24 ++++++++++++++++++++++++
> 2 files changed, 25 insertions(+)
>
> diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
> index 64aa091cb685..91aaed17fe56 100644
> --- a/net/mptcp/protocol.h
> +++ b/net/mptcp/protocol.h
> @@ -327,6 +327,7 @@ struct mptcp_sock {
> int keepalive_cnt;
> int keepalive_idle;
> int keepalive_intvl;
> + int maxseg;
> struct work_struct work;
> struct sk_buff *ooo_last_skb;
> struct rb_root out_of_order_queue;
> diff --git a/net/mptcp/sockopt.c b/net/mptcp/sockopt.c
> index 3caa0a9d3b38..a7992cbc5ed9 100644
> --- a/net/mptcp/sockopt.c
> +++ b/net/mptcp/sockopt.c
> @@ -798,6 +798,22 @@ static int mptcp_setsockopt_first_sf_only(struct mptcp_sock *msk, int level, int
> return ret;
> }
>
> +static int mptcp_setsockopt_all_subflows(struct mptcp_sock *msk, int level, int optname,
> + sockptr_t optval, unsigned int optlen)
> +{
> + struct mptcp_subflow_context *subflow;
> +
> + mptcp_for_each_subflow(msk, subflow) {
> + struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
> + int ret = 0;
> +
> + ret = tcp_setsockopt(ssk, level, optname, optval, optlen);
> + if (ret)
> + return ret;
> + }
> + return 0;
> +}
> +
> static int mptcp_setsockopt_sol_tcp(struct mptcp_sock *msk, int optname,
> sockptr_t optval, unsigned int optlen)
> {
> @@ -859,6 +875,11 @@ static int mptcp_setsockopt_sol_tcp(struct mptcp_sock *msk, int optname,
> &msk->keepalive_cnt,
> val);
> break;
> + case TCP_MAXSEG:
> + msk->maxseg = val;
> + ret = mptcp_setsockopt_all_subflows(msk, SOL_TCP, optname,
> + optval, optlen);
> + break;
> default:
> ret = -ENOPROTOOPT;
> }
> @@ -1407,6 +1428,8 @@ static int mptcp_getsockopt_sol_tcp(struct mptcp_sock *msk, int optname,
> return mptcp_put_int_option(msk, optval, optlen, msk->notsent_lowat);
> case TCP_IS_MPTCP:
> return mptcp_put_int_option(msk, optval, optlen, 1);
> + case TCP_MAXSEG:
> + return mptcp_put_int_option(msk, optval, optlen, msk->maxseg);
I just checked what TCP is doing, and I see that this is different: with
TCP, the cached MSS is returned when the connection is not closed (and
something else for TCP_REPAIR). We should then do the same.
Should we then simply call mptcp_getsockopt_first_sf_only()?
> }
> return -EOPNOTSUPP;
> }
> @@ -1553,6 +1576,7 @@ static void sync_socket_options(struct mptcp_sock *msk, struct sock *ssk)
> tcp_sock_set_keepidle_locked(ssk, msk->keepalive_idle);
> tcp_sock_set_keepintvl(ssk, msk->keepalive_intvl);
> tcp_sock_set_keepcnt(ssk, msk->keepalive_cnt);
> + tcp_sock_set_maxseg(ssk, msk->maxseg);
>
> inet_assign_bit(TRANSPARENT, ssk, inet_test_bit(TRANSPARENT, sk));
> inet_assign_bit(FREEBIND, ssk, inet_test_bit(FREEBIND, sk));
Cheers,
Matt
--
Sponsored by the NGI0 Core fund.
© 2016 - 2025 Red Hat, Inc.