Files
linux/include/net/mptcp.h
Matthieu Baerts (NGI0) 1c3e7e0439 tcp: allow mptcp to drop TS for some packets
With TCP-timestamps (padded) taking 12 bytes and ADD_ADDR IPv6 + port
taking 30 bytes, the 40-byte limit for the TCP options is reached. In
this case, it is then not possible to send the address signal.

The idea is to let MPTCP dropping the TCP-timestamps option for some
specific packets, to be able to send some specific pure ACK carrying >28
bytes of MPTCP options, like with this specific ADD_ADDR. A new
parameter is passed from tcp_established_options to the MPTCP side to
indicate if the TCP TS option is used, and if it should be dropped. The
next commit implements the part on MPTCP side, but split into two
patches to help TCP maintainers to identify the modifications on TCP
side. This feature will be controlled by a new add_addr_v6_port_drop_ts
MPTCP sysctl knob.

It is important to keep in mind that dropping the TCP timestamps option
for one packet of the connection could eventually disrupt some
middleboxes: even if it should be unlikely, they could drop the packet
or even block the connection. That's why this new feature will be
controlled by a sysctl knob.

Note that it would be technically possible to squeeze both options into
the header if the ADD_ADDR is first written, and then the TCP timestamps
without the NOPs preceding it. But this means more modifications on TCP
side, plus some middleboxes could still be disrupted by that.

In this implementation, an unused bit is used in mptcp_out_options
structure to avoid passing an address to a local variable. Reading and
setting it needs CONFIG_MPTCP, so the whole block now has this #if
condition: mptcp_established_options() is then no longer used without
CONFIG_MPTCP.

About alternatives, instead of passing a new boolean (has_ts), another
option would be to pass the whole option structure (opts), but
'struct tcp_out_options' is currently defined in tcp_output.c, and it
would need to be exported. Plus that means the removal of the TCP TS
option would be done on the MPTCP side, and not here on the TCP side.
It feels clearer to remove other TCP options from the TCP side, than
hiding that from the MPTCP side.

Yet an other alternative would be to pass the size already taken by the
other TCP options, and have a way to drop them all when needed. But this
feels better to target only the timestamps option where dropping it
should be safe, even if it is currently the only option that would be
set before MPTCP, when MPTCP is used.

Reviewed-by: Mat Martineau <martineau@kernel.org>
Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Link: https://patch.msgid.link/20260605-net-next-mptcp-add-addr6-port-ts-v2-5-758e7ca73f4d@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2026-06-11 15:33:37 -07:00

336 lines
7.7 KiB
C

/* SPDX-License-Identifier: GPL-2.0 */
/*
* Multipath TCP
*
* Copyright (c) 2017 - 2019, Intel Corporation.
*/
#ifndef __NET_MPTCP_H
#define __NET_MPTCP_H
#include <linux/skbuff.h>
#include <linux/tcp.h>
#include <linux/types.h>
struct mptcp_info;
struct mptcp_sock;
struct mptcp_pm_addr_entry;
struct seq_file;
/* MPTCP sk_buff extension data */
struct mptcp_ext {
union {
u64 data_ack;
u32 data_ack32;
};
u64 data_seq;
u32 subflow_seq;
u16 data_len;
__sum16 csum;
struct_group(flags,
u8 use_map:1,
dsn64:1,
data_fin:1,
use_ack:1,
ack64:1,
mpc_map:1,
frozen:1,
reset_transient:1;
u8 reset_reason:4,
csum_reqd:1,
infinite_map:1;
); /* end of flags group */
};
#define MPTCPOPT_HMAC_LEN 20
#define MPTCP_RM_IDS_MAX 8
struct mptcp_rm_list {
u8 ids[MPTCP_RM_IDS_MAX];
u8 nr;
};
struct mptcp_addr_info {
u8 id;
sa_family_t family;
__be16 port;
union {
struct in_addr addr;
#if IS_ENABLED(CONFIG_MPTCP_IPV6)
struct in6_addr addr6;
#endif
};
};
struct mptcp_out_options {
#if IS_ENABLED(CONFIG_MPTCP)
u16 suboptions;
struct mptcp_rm_list rm_list;
u8 join_id;
u8 backup;
u8 reset_reason:4,
reset_transient:1,
csum_reqd:1,
allow_join_id0:1,
drop_ts:1;
union {
struct {
u64 sndr_key;
u64 rcvr_key;
u64 data_seq;
u32 subflow_seq;
u16 data_len;
__sum16 csum;
};
struct {
struct mptcp_addr_info addr;
u64 ahmac;
};
struct {
struct mptcp_ext ext_copy;
u64 fail_seq;
};
struct {
u32 nonce;
u32 token;
u64 thmac;
u8 hmac[MPTCPOPT_HMAC_LEN];
};
};
#endif
};
#define MPTCP_SCHED_NAME_MAX 16
#define MPTCP_SCHED_MAX 128
#define MPTCP_SCHED_BUF_MAX (MPTCP_SCHED_NAME_MAX * MPTCP_SCHED_MAX)
struct mptcp_sched_ops {
int (*get_send)(struct mptcp_sock *msk);
int (*get_retrans)(struct mptcp_sock *msk);
char name[MPTCP_SCHED_NAME_MAX];
struct module *owner;
struct list_head list;
void (*init)(struct mptcp_sock *msk);
void (*release)(struct mptcp_sock *msk);
} ____cacheline_aligned_in_smp;
#define MPTCP_PM_NAME_MAX 16
#define MPTCP_PM_MAX 128
#define MPTCP_PM_BUF_MAX (MPTCP_PM_NAME_MAX * MPTCP_PM_MAX)
struct mptcp_pm_ops {
char name[MPTCP_PM_NAME_MAX];
struct module *owner;
struct list_head list;
void (*init)(struct mptcp_sock *msk);
void (*release)(struct mptcp_sock *msk);
} ____cacheline_aligned_in_smp;
#ifdef CONFIG_MPTCP
void mptcp_init(void);
static inline bool sk_is_mptcp(const struct sock *sk)
{
return tcp_sk(sk)->is_mptcp;
}
static inline bool rsk_is_mptcp(const struct request_sock *req)
{
return tcp_rsk(req)->is_mptcp;
}
static inline bool rsk_drop_req(const struct request_sock *req)
{
return tcp_rsk(req)->is_mptcp && tcp_rsk(req)->drop_req;
}
void mptcp_space(const struct sock *ssk, int *space, int *full_space);
bool mptcp_syn_options(struct sock *sk, const struct sk_buff *skb,
unsigned int *size, struct mptcp_out_options *opts);
bool mptcp_synack_options(const struct request_sock *req, unsigned int *size,
struct mptcp_out_options *opts);
int mptcp_established_options(struct sock *sk, struct sk_buff *skb,
unsigned int remaining, bool has_ts,
struct mptcp_out_options *opts);
bool mptcp_incoming_options(struct sock *sk, struct sk_buff *skb);
void mptcp_write_options(struct tcphdr *th, __be32 *ptr, struct tcp_sock *tp,
struct mptcp_out_options *opts);
void mptcp_diag_fill_info(struct mptcp_sock *msk, struct mptcp_info *info);
/* move the skb extension owership, with the assumption that 'to' is
* newly allocated
*/
static inline void mptcp_skb_ext_move(struct sk_buff *to,
struct sk_buff *from)
{
if (!skb_ext_exist(from, SKB_EXT_MPTCP))
return;
if (WARN_ON_ONCE(to->active_extensions))
skb_ext_put(to);
to->active_extensions = from->active_extensions;
to->extensions = from->extensions;
from->active_extensions = 0;
}
static inline void mptcp_skb_ext_copy(struct sk_buff *to,
struct sk_buff *from)
{
struct mptcp_ext *from_ext;
from_ext = skb_ext_find(from, SKB_EXT_MPTCP);
if (!from_ext)
return;
from_ext->frozen = 1;
skb_ext_copy(to, from);
}
static inline bool mptcp_ext_matches(const struct mptcp_ext *to_ext,
const struct mptcp_ext *from_ext)
{
/* MPTCP always clears the ext when adding it to the skb, so
* holes do not bother us here
*/
return !from_ext ||
(to_ext && from_ext &&
!memcmp(from_ext, to_ext, sizeof(struct mptcp_ext)));
}
/* check if skbs can be collapsed.
* MPTCP collapse is allowed if neither @to or @from carry an mptcp data
* mapping, or if the extension of @to is the same as @from.
* Collapsing is not possible if @to lacks an extension, but @from carries one.
*/
static inline bool mptcp_skb_can_collapse(const struct sk_buff *to,
const struct sk_buff *from)
{
return mptcp_ext_matches(skb_ext_find(to, SKB_EXT_MPTCP),
skb_ext_find(from, SKB_EXT_MPTCP));
}
void mptcp_seq_show(struct seq_file *seq);
int mptcp_subflow_init_cookie_req(struct request_sock *req,
const struct sock *sk_listener,
struct sk_buff *skb);
struct request_sock *mptcp_subflow_reqsk_alloc(const struct request_sock_ops *ops,
struct sock *sk_listener,
bool attach_listener);
__be32 mptcp_get_reset_option(const struct sk_buff *skb);
static inline __be32 mptcp_reset_option(const struct sk_buff *skb)
{
if (skb_ext_exist(skb, SKB_EXT_MPTCP))
return mptcp_get_reset_option(skb);
return htonl(0u);
}
void mptcp_active_detect_blackhole(struct sock *sk, bool expired);
#else
static inline void mptcp_init(void)
{
}
static inline bool sk_is_mptcp(const struct sock *sk)
{
return false;
}
static inline bool rsk_is_mptcp(const struct request_sock *req)
{
return false;
}
static inline bool rsk_drop_req(const struct request_sock *req)
{
return false;
}
static inline bool mptcp_syn_options(struct sock *sk, const struct sk_buff *skb,
unsigned int *size,
struct mptcp_out_options *opts)
{
return false;
}
static inline bool mptcp_synack_options(const struct request_sock *req,
unsigned int *size,
struct mptcp_out_options *opts)
{
return false;
}
static inline bool mptcp_incoming_options(struct sock *sk,
struct sk_buff *skb)
{
return true;
}
static inline void mptcp_skb_ext_move(struct sk_buff *to,
const struct sk_buff *from)
{
}
static inline void mptcp_skb_ext_copy(struct sk_buff *to,
struct sk_buff *from)
{
}
static inline bool mptcp_skb_can_collapse(const struct sk_buff *to,
const struct sk_buff *from)
{
return true;
}
static inline void mptcp_space(const struct sock *ssk, int *s, int *fs) { }
static inline void mptcp_seq_show(struct seq_file *seq) { }
static inline int mptcp_subflow_init_cookie_req(struct request_sock *req,
const struct sock *sk_listener,
struct sk_buff *skb)
{
return 0; /* TCP fallback */
}
static inline struct request_sock *mptcp_subflow_reqsk_alloc(const struct request_sock_ops *ops,
struct sock *sk_listener,
bool attach_listener)
{
return NULL;
}
static inline __be32 mptcp_reset_option(const struct sk_buff *skb) { return htonl(0u); }
static inline void mptcp_active_detect_blackhole(struct sock *sk, bool expired) { }
#endif /* CONFIG_MPTCP */
#if IS_ENABLED(CONFIG_MPTCP_IPV6)
int mptcpv6_init(void);
void mptcpv6_handle_mapped(struct sock *sk, bool mapped);
#elif IS_ENABLED(CONFIG_IPV6)
static inline int mptcpv6_init(void) { return 0; }
static inline void mptcpv6_handle_mapped(struct sock *sk, bool mapped) { }
#endif
#if defined(CONFIG_MPTCP) && defined(CONFIG_BPF_SYSCALL)
struct mptcp_sock *bpf_mptcp_sock_from_subflow(struct sock *sk);
#else
static inline struct mptcp_sock *bpf_mptcp_sock_from_subflow(struct sock *sk) { return NULL; }
#endif
#if !IS_ENABLED(CONFIG_MPTCP)
struct mptcp_sock { };
#endif
#endif /* __NET_MPTCP_H */