Merge branch 'mptcp-pm-drop-tcp-ts-with-add_addrv6-port'

Matthieu Baerts says:

====================
mptcp: pm: drop TCP TS with ADD_ADDRv6 + port

Up to this series, it was possible to add a "signal" MPTCP endpoint with
an IPv6 address and a port, or to directly request to send an ADD_ADDR
with a v6 address and a port, but the expected ADD_ADDR wasn't sent when
TCP timestamps was used for the connection.

In fact, such signalling option cannot be sent when TCP timestamps is
used due to a lack of option space: the limit is at 40 bytes, and, with
padding, TCP timestamps is taking 12 bytes, while an ADD_ADDR IPv6 +
port is taking 30 bytes. The selected solution here is to simply drop
the TCP timestamps option when such ADD_ADDR of 30 bytes needs to be
sent.

- Patches 1-3: small cleanups to avoid computing ADD/RM_ADDR twice.

- Patches 4-7: the new feature, controlled by a new sysctl knob.

- Patch 8: extra checks in the MPTCP Join selftests.

- Patches 9-15: A bunch of refactoring: renamed confusing helpers and
  variables, and prevent future misused functions.
====================

Link: https://patch.msgid.link/20260605-net-next-mptcp-add-addr6-port-ts-v2-0-758e7ca73f4d@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-06-11 15:33:41 -07:00
commit 3fbbb5a628
11 changed files with 248 additions and 228 deletions

View File

@ -21,6 +21,19 @@ add_addr_timeout - INTEGER (seconds)
Default: 120
add_addr_v6_port_drop_ts - BOOLEAN
Control whether preparing an ADD_ADDR with an IPv6 address and a port
should drop the TCP timestamps option to have enough option space to
send the signal.
If there is not enough option space, and the TCP timestamps option
cannot be dropped, the signal cannot be sent. Note that dropping the TCP
timestamps option for one packet of the connection could disrupt some
middleboxes: even if it should be unlikely, they could drop the packet
or block the connection. This is a per-namespace sysctl.
Default: 1 (enabled)
allow_join_initial_addr_port - BOOLEAN
Allow peers to send join requests to the IP address and port number used
by the initial subflow if the value is 1. This controls a flag that is

View File

@ -72,7 +72,8 @@ struct mptcp_out_options {
u8 reset_reason:4,
reset_transient:1,
csum_reqd:1,
allow_join_id0:1;
allow_join_id0:1,
drop_ts:1;
union {
struct {
u64 sndr_key;
@ -153,7 +154,7 @@ bool mptcp_syn_options(struct sock *sk, const struct sk_buff *skb,
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,
unsigned int remaining, bool has_ts,
struct mptcp_out_options *opts);
bool mptcp_incoming_options(struct sock *sk, struct sk_buff *skb);
@ -269,14 +270,6 @@ static inline bool mptcp_synack_options(const struct request_sock *req,
return false;
}
static inline int mptcp_established_options(struct sock *sk,
struct sk_buff *skb,
unsigned int remaining,
struct mptcp_out_options *opts)
{
return -1;
}
static inline bool mptcp_incoming_options(struct sock *sk,
struct sk_buff *skb)
{

View File

@ -1175,6 +1175,7 @@ static unsigned int tcp_established_options(struct sock *sk, struct sk_buff *skb
size += TCPOLEN_TSTAMP_ALIGNED;
}
#if IS_ENABLED(CONFIG_MPTCP)
/* MPTCP options have precedence over SACK for the limited TCP
* option space because a MPTCP connection would be forced to
* fall back to regular TCP if a required multipath option is
@ -1183,15 +1184,22 @@ static unsigned int tcp_established_options(struct sock *sk, struct sk_buff *skb
*/
if (sk_is_mptcp(sk)) {
unsigned int remaining = MAX_TCP_OPTION_SPACE - size;
bool has_ts = opts->options & OPTION_TS;
int opt_size;
opt_size = mptcp_established_options(sk, skb, remaining,
opts->mptcp.drop_ts = 0;
opt_size = mptcp_established_options(sk, skb, remaining, has_ts,
&opts->mptcp);
if (opt_size >= 0) {
opts->options |= OPTION_MPTCP;
size += opt_size;
if (opts->mptcp.drop_ts)
opts->options &= ~OPTION_TS;
}
}
#endif
eff_sacks = tp->rx_opt.num_sacks + tp->rx_opt.dsack;
if (unlikely(eff_sacks)) {

View File

@ -32,12 +32,13 @@ struct mptcp_pernet {
unsigned int close_timeout;
unsigned int stale_loss_cnt;
atomic_t active_disable_times;
u8 syn_retrans_before_tcp_fallback;
unsigned long active_disable_stamp;
u8 syn_retrans_before_tcp_fallback;
u8 mptcp_enabled;
u8 checksum_enabled;
u8 allow_join_initial_addr_port;
u8 pm_type;
u8 add_addr_v6_port_drop_ts;
char scheduler[MPTCP_SCHED_NAME_MAX];
char path_manager[MPTCP_PM_NAME_MAX];
};
@ -94,6 +95,11 @@ const char *mptcp_get_scheduler(const struct net *net)
return mptcp_get_pernet(net)->scheduler;
}
unsigned int mptcp_add_addr_v6_port_drop_ts(const struct net *net)
{
return READ_ONCE(mptcp_get_pernet(net)->add_addr_v6_port_drop_ts);
}
static void mptcp_pernet_set_defaults(struct mptcp_pernet *pernet)
{
pernet->mptcp_enabled = 1;
@ -108,6 +114,7 @@ static void mptcp_pernet_set_defaults(struct mptcp_pernet *pernet)
pernet->pm_type = MPTCP_PM_TYPE_KERNEL;
strscpy(pernet->scheduler, "default", sizeof(pernet->scheduler));
strscpy(pernet->path_manager, "kernel", sizeof(pernet->path_manager));
pernet->add_addr_v6_port_drop_ts = 1;
}
#ifdef CONFIG_SYSCTL
@ -362,6 +369,14 @@ static struct ctl_table mptcp_sysctl_table[] = {
.mode = 0444,
.proc_handler = proc_available_path_managers,
},
{
.procname = "add_addr_v6_port_drop_ts",
.maxlen = sizeof(u8),
.mode = 0644,
.proc_handler = proc_dou8vec_minmax,
.extra1 = SYSCTL_ZERO,
.extra2 = SYSCTL_ONE
},
};
static int mptcp_pernet_new_table(struct net *net, struct mptcp_pernet *pernet)
@ -389,6 +404,7 @@ static int mptcp_pernet_new_table(struct net *net, struct mptcp_pernet *pernet)
table[10].data = &pernet->syn_retrans_before_tcp_fallback;
table[11].data = &pernet->path_manager;
/* table[12] is for available_path_managers which is read-only info */
table[13].data = &pernet->add_addr_v6_port_drop_ts;
hdr = register_net_sysctl_sz(net, MPTCP_SYSCTL_PATH, table,
ARRAY_SIZE(mptcp_sysctl_table));

View File

@ -447,8 +447,7 @@ static void clear_3rdack_retransmission(struct sock *sk)
}
static bool mptcp_established_options_mp(struct sock *sk, struct sk_buff *skb,
bool snd_data_fin_enable,
unsigned int *size,
bool snd_data_fin_enable, int *size,
struct mptcp_out_options *opts)
{
struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(sk);
@ -560,8 +559,7 @@ static void mptcp_write_data_fin(struct mptcp_subflow_context *subflow,
}
static bool mptcp_established_options_dss(struct sock *sk, struct sk_buff *skb,
bool snd_data_fin_enable,
unsigned int *size,
bool snd_data_fin_enable, int *size,
struct mptcp_out_options *opts)
{
struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(sk);
@ -658,17 +656,17 @@ static u64 add_addr_generate_hmac(u64 key1, u64 key2,
return get_unaligned_be64(&hmac[SHA256_DIGEST_SIZE - sizeof(u64)]);
}
static bool mptcp_established_options_add_addr(struct sock *sk, struct sk_buff *skb,
unsigned int *size,
static bool mptcp_established_options_add_addr(struct sock *sk,
struct sk_buff *skb, int *size,
unsigned int remaining,
bool has_ts,
struct mptcp_out_options *opts)
{
struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(sk);
struct mptcp_sock *msk = mptcp_sk(subflow->conn);
unsigned int opt_size = *size;
struct mptcp_addr_info addr;
bool drop_ts = has_ts;
bool echo;
int len;
/* add addr will strip the existing options, be sure to avoid breaking
* MPC/MPJ handshakes
@ -676,21 +674,14 @@ static bool mptcp_established_options_add_addr(struct sock *sk, struct sk_buff *
if (!mptcp_pm_should_add_signal(msk) ||
(opts->suboptions & (OPTION_MPTCP_MPJ_ACK | OPTION_MPTCP_MPC_ACK)) ||
!skb || !skb_is_tcp_pure_ack(skb) ||
!mptcp_pm_add_addr_signal(msk, opt_size, remaining, &addr, &echo))
!mptcp_pm_add_addr_signal(msk, size, remaining, &addr, &echo,
&drop_ts))
return false;
remaining += opt_size;
len = mptcp_add_addr_len(addr.family, echo, !!addr.port);
if (remaining < len)
return false;
*size = len;
pr_debug("drop other suboptions\n");
opts->suboptions = 0;
*size -= opt_size;
opts->suboptions = OPTION_MPTCP_ADD_ADDR;
opts->drop_ts = drop_ts;
opts->addr = addr;
opts->suboptions |= OPTION_MPTCP_ADD_ADDR;
if (!echo) {
MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_ADDADDRTX);
opts->ahmac = add_addr_generate_hmac(READ_ONCE(msk->local_key),
@ -706,27 +697,19 @@ static bool mptcp_established_options_add_addr(struct sock *sk, struct sk_buff *
return true;
}
static bool mptcp_established_options_rm_addr(struct sock *sk,
unsigned int *size,
static bool mptcp_established_options_rm_addr(struct sock *sk, int *size,
unsigned int remaining,
struct mptcp_out_options *opts)
{
struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(sk);
struct mptcp_sock *msk = mptcp_sk(subflow->conn);
struct mptcp_rm_list rm_list;
int i, len;
int i;
if (!mptcp_pm_should_rm_signal(msk) ||
!(mptcp_pm_rm_addr_signal(msk, remaining, &rm_list)))
!(mptcp_pm_rm_addr_signal(msk, remaining, &rm_list, size)))
return false;
len = mptcp_rm_addr_len(&rm_list);
if (len < 0)
return false;
if (remaining < len)
return false;
*size = len;
opts->suboptions |= OPTION_MPTCP_RM_ADDR;
opts->rm_list = rm_list;
@ -736,8 +719,7 @@ static bool mptcp_established_options_rm_addr(struct sock *sk,
return true;
}
static bool mptcp_established_options_mp_prio(struct sock *sk,
unsigned int *size,
static bool mptcp_established_options_mp_prio(struct sock *sk, int *size,
unsigned int remaining,
struct mptcp_out_options *opts)
{
@ -762,8 +744,8 @@ static bool mptcp_established_options_mp_prio(struct sock *sk,
return true;
}
static noinline bool mptcp_established_options_rst(struct sock *sk, struct sk_buff *skb,
unsigned int *size,
static noinline bool mptcp_established_options_rst(struct sock *sk,
int *size,
unsigned int remaining,
struct mptcp_out_options *opts)
{
@ -781,8 +763,7 @@ static noinline bool mptcp_established_options_rst(struct sock *sk, struct sk_bu
return true;
}
static bool mptcp_established_options_fastclose(struct sock *sk,
unsigned int *size,
static bool mptcp_established_options_fastclose(struct sock *sk, int *size,
unsigned int remaining,
struct mptcp_out_options *opts)
{
@ -804,8 +785,7 @@ static bool mptcp_established_options_fastclose(struct sock *sk,
return true;
}
static bool mptcp_established_options_mp_fail(struct sock *sk,
unsigned int *size,
static bool mptcp_established_options_mp_fail(struct sock *sk, int *size,
unsigned int remaining,
struct mptcp_out_options *opts)
{
@ -828,15 +808,15 @@ static bool mptcp_established_options_mp_fail(struct sock *sk,
}
int mptcp_established_options(struct sock *sk, struct sk_buff *skb,
unsigned int remaining,
unsigned int remaining, bool has_ts,
struct mptcp_out_options *opts)
{
struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(sk);
struct mptcp_sock *msk = mptcp_sk(subflow->conn);
unsigned int opt_size = 0;
int total_size = 0;
bool snd_data_fin;
bool ret = false;
int opt_size = 0;
opts->suboptions = 0;
@ -853,7 +833,7 @@ int mptcp_established_options(struct sock *sk, struct sk_buff *skb,
remaining -= opt_size;
}
/* MP_RST can be used with MP_FASTCLOSE and MP_FAIL if there is room */
if (mptcp_established_options_rst(sk, skb, &opt_size, remaining, opts)) {
if (mptcp_established_options_rst(sk, &opt_size, remaining, opts)) {
total_size += opt_size;
remaining -= opt_size;
}
@ -864,7 +844,7 @@ int mptcp_established_options(struct sock *sk, struct sk_buff *skb,
if (mptcp_established_options_mp(sk, skb, snd_data_fin, &opt_size, opts))
ret = true;
else if (mptcp_established_options_dss(sk, skb, snd_data_fin, &opt_size, opts)) {
unsigned int mp_fail_size;
int mp_fail_size;
ret = true;
if (mptcp_established_options_mp_fail(sk, &mp_fail_size,
@ -883,7 +863,8 @@ int mptcp_established_options(struct sock *sk, struct sk_buff *skb,
total_size += opt_size;
remaining -= opt_size;
if (mptcp_established_options_add_addr(sk, skb, &opt_size, remaining, opts)) {
if (mptcp_established_options_add_addr(sk, skb, &opt_size, remaining,
has_ts, opts)) {
total_size += opt_size;
remaining -= opt_size;
ret = true;
@ -1202,7 +1183,6 @@ bool mptcp_incoming_options(struct sock *sk, struct sk_buff *skb)
MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_ADDADDR);
} else {
mptcp_pm_add_addr_echoed(msk, &mp_opt.addr);
mptcp_pm_del_add_timer(msk, &mp_opt.addr, true);
MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_ECHOADD);
}

View File

@ -12,12 +12,12 @@
#define ADD_ADDR_RETRANS_MAX 3
struct mptcp_pm_add_entry {
struct mptcp_pm_add_addr {
struct list_head list;
struct mptcp_addr_info addr;
u8 retrans_times;
bool timer_done;
struct timer_list add_timer;
struct timer_list timer;
struct mptcp_sock *sock;
struct rcu_head rcu;
};
@ -115,14 +115,14 @@ static bool mptcp_pm_is_init_remote_addr(struct mptcp_sock *msk,
return mptcp_addresses_equal(&mpc_remote, remote, remote->port);
}
bool mptcp_lookup_subflow_by_saddr(const struct list_head *list,
const struct mptcp_addr_info *saddr)
bool mptcp_pm_has_subflow_saddr(const struct mptcp_sock *msk,
const struct mptcp_addr_info *saddr)
{
struct mptcp_subflow_context *subflow;
struct mptcp_addr_info cur;
struct sock_common *skc;
list_for_each_entry(subflow, list, node) {
mptcp_for_each_subflow(msk, subflow) {
skc = (struct sock_common *)mptcp_subflow_tcp_sock(subflow);
mptcp_local_address(skc, &cur);
@ -133,11 +133,11 @@ bool mptcp_lookup_subflow_by_saddr(const struct list_head *list,
return false;
}
static struct mptcp_pm_add_entry *
mptcp_lookup_anno_list_by_saddr(const struct mptcp_sock *msk,
const struct mptcp_addr_info *addr)
static struct mptcp_pm_add_addr *
mptcp_pm_announced_lookup(const struct mptcp_sock *msk,
const struct mptcp_addr_info *addr)
{
struct mptcp_pm_add_entry *entry;
struct mptcp_pm_add_addr *entry;
lockdep_assert_held(&msk->pm.lock);
@ -149,26 +149,60 @@ mptcp_lookup_anno_list_by_saddr(const struct mptcp_sock *msk,
return NULL;
}
bool mptcp_remove_anno_list_by_saddr(struct mptcp_sock *msk,
const struct mptcp_addr_info *addr)
static struct mptcp_pm_add_addr *
mptcp_pm_announced_del_timer(struct mptcp_sock *msk,
const struct mptcp_addr_info *addr, bool check_id)
{
struct mptcp_pm_add_entry *entry;
struct sock *sk = (struct sock *)msk;
struct mptcp_pm_add_addr *entry;
bool stop_timer = false;
rcu_read_lock();
spin_lock_bh(&msk->pm.lock);
entry = mptcp_pm_announced_lookup(msk, addr);
if (entry && (!check_id || entry->addr.id == addr->id)) {
entry->retrans_times = ADD_ADDR_RETRANS_MAX;
stop_timer = true;
}
if (!check_id && entry)
list_del(&entry->list);
spin_unlock_bh(&msk->pm.lock);
/* Note: entry might have been removed by another thread.
* We hold rcu_read_lock() to ensure it is not freed under us.
*/
if (stop_timer) {
if (check_id)
sk_stop_timer(sk, &entry->timer);
else
sk_stop_timer_sync(sk, &entry->timer);
}
rcu_read_unlock();
return entry;
}
bool mptcp_pm_announced_remove(struct mptcp_sock *msk,
const struct mptcp_addr_info *addr)
{
struct mptcp_pm_add_addr *entry;
bool ret;
entry = mptcp_pm_del_add_timer(msk, addr, false);
entry = mptcp_pm_announced_del_timer(msk, addr, false);
ret = entry;
kfree_rcu(entry, rcu);
return ret;
}
bool mptcp_pm_sport_in_anno_list(struct mptcp_sock *msk, const struct sock *sk)
bool mptcp_pm_announced_has_ssk(struct mptcp_sock *msk, const struct sock *ssk)
{
struct mptcp_pm_add_entry *entry;
struct mptcp_pm_add_addr *entry;
struct mptcp_addr_info saddr;
bool ret = false;
mptcp_local_address((struct sock_common *)sk, &saddr);
mptcp_local_address((struct sock_common *)ssk, &saddr);
spin_lock_bh(&msk->pm.lock);
list_for_each_entry(entry, &msk->pm.anno_list, list) {
@ -226,6 +260,7 @@ static bool subflow_in_rm_list(const struct mptcp_subflow_context *subflow,
return false;
}
static void mptcp_pm_add_addr_send_ack(struct mptcp_sock *msk);
static void
mptcp_pm_addr_send_ack_avoid_list(struct mptcp_sock *msk,
const struct mptcp_rm_list *rm_list)
@ -338,10 +373,10 @@ static unsigned int mptcp_adjust_add_addr_timeout(struct mptcp_sock *msk)
return rto;
}
static void mptcp_pm_add_timer(struct timer_list *timer)
static void mptcp_pm_add_addr_timer(struct timer_list *timer)
{
struct mptcp_pm_add_entry *entry = timer_container_of(entry, timer,
add_timer);
struct mptcp_pm_add_addr *entry = timer_container_of(entry, timer,
timer);
struct mptcp_sock *msk = entry->sock;
struct sock *sk = (struct sock *)msk;
unsigned int timeout = 0;
@ -364,7 +399,7 @@ static void mptcp_pm_add_timer(struct timer_list *timer)
spin_lock_bh(&msk->pm.lock);
/* The cancel path (mptcp_pm_del_add_timer()) can race with this
/* The cancel path (mptcp_pm_announced_del_timer()) can race with this
* callback. Once cancel updates retrans_times to MAX, suppress further
* retransmissions here. If this callback acquires pm.lock first, one
* final transmit attempt is still possible.
@ -397,51 +432,16 @@ static void mptcp_pm_add_timer(struct timer_list *timer)
sock_put(sk);
}
struct mptcp_pm_add_entry *
mptcp_pm_del_add_timer(struct mptcp_sock *msk,
const struct mptcp_addr_info *addr, bool check_id)
{
struct mptcp_pm_add_entry *entry;
struct sock *sk = (struct sock *)msk;
bool stop_timer = false;
rcu_read_lock();
spin_lock_bh(&msk->pm.lock);
entry = mptcp_lookup_anno_list_by_saddr(msk, addr);
if (entry && (!check_id || entry->addr.id == addr->id)) {
entry->retrans_times = ADD_ADDR_RETRANS_MAX;
stop_timer = true;
}
if (!check_id && entry)
list_del(&entry->list);
spin_unlock_bh(&msk->pm.lock);
/* Note: entry might have been removed by another thread.
* We hold rcu_read_lock() to ensure it is not freed under us.
*/
if (stop_timer) {
if (check_id)
sk_stop_timer(sk, &entry->add_timer);
else
sk_stop_timer_sync(sk, &entry->add_timer);
}
rcu_read_unlock();
return entry;
}
bool mptcp_pm_alloc_anno_list(struct mptcp_sock *msk,
bool mptcp_pm_announced_alloc(struct mptcp_sock *msk,
const struct mptcp_addr_info *addr)
{
struct mptcp_pm_add_entry *add_entry = NULL;
struct mptcp_pm_add_addr *add_entry = NULL;
struct sock *sk = (struct sock *)msk;
unsigned int timeout;
lockdep_assert_held(&msk->pm.lock);
add_entry = mptcp_lookup_anno_list_by_saddr(msk, addr);
add_entry = mptcp_pm_announced_lookup(msk, addr);
if (add_entry) {
if (WARN_ON_ONCE(mptcp_pm_is_kernel(msk)))
return false;
@ -459,19 +459,19 @@ bool mptcp_pm_alloc_anno_list(struct mptcp_sock *msk,
add_entry->sock = msk;
add_entry->retrans_times = 0;
timer_setup(&add_entry->add_timer, mptcp_pm_add_timer, 0);
timer_setup(&add_entry->timer, mptcp_pm_add_addr_timer, 0);
reset_timer:
add_entry->timer_done = false;
timeout = mptcp_adjust_add_addr_timeout(msk);
if (timeout)
sk_reset_timer(sk, &add_entry->add_timer, jiffies + timeout);
sk_reset_timer(sk, &add_entry->timer, jiffies + timeout);
return true;
}
static void mptcp_pm_free_anno_list(struct mptcp_sock *msk)
static void mptcp_pm_free_announced_list(struct mptcp_sock *msk)
{
struct mptcp_pm_add_entry *entry, *tmp;
struct mptcp_pm_add_addr *entry, *tmp;
struct sock *sk = (struct sock *)msk;
LIST_HEAD(free_list);
@ -483,7 +483,7 @@ static void mptcp_pm_free_anno_list(struct mptcp_sock *msk)
list_for_each_entry_safe(entry, tmp, &free_list, list) {
if (!entry->timer_done)
sk_stop_timer_sync(sk, &entry->add_timer);
sk_stop_timer_sync(sk, &entry->timer);
kfree_rcu(entry, rcu);
}
}
@ -730,21 +730,25 @@ void mptcp_pm_add_addr_echoed(struct mptcp_sock *msk,
const struct mptcp_addr_info *addr)
{
struct mptcp_pm_data *pm = &msk->pm;
struct mptcp_pm_add_addr *entry;
pr_debug("msk=%p\n", msk);
if (!READ_ONCE(pm->work_pending))
entry = mptcp_pm_announced_del_timer(msk, addr, true);
if (!entry || !READ_ONCE(pm->work_pending))
return;
spin_lock_bh(&pm->lock);
if (mptcp_lookup_anno_list_by_saddr(msk, addr) && READ_ONCE(pm->work_pending))
if (READ_ONCE(pm->work_pending))
mptcp_pm_schedule_work(msk, MPTCP_PM_SUBFLOW_ESTABLISHED);
spin_unlock_bh(&pm->lock);
}
void mptcp_pm_add_addr_send_ack(struct mptcp_sock *msk)
/* To be called while pm->lock is held */
static void mptcp_pm_add_addr_send_ack(struct mptcp_sock *msk)
{
if (!mptcp_pm_should_add_signal(msk))
return;
@ -887,13 +891,29 @@ void mptcp_pm_mp_fail_received(struct sock *sk, u64 fail_seq)
}
}
bool mptcp_pm_add_addr_signal(struct mptcp_sock *msk, unsigned int opt_size,
unsigned int remaining,
struct mptcp_addr_info *addr, bool *echo)
static int mptcp_add_addr_len(int family, bool echo, bool port)
{
int len = TCPOLEN_MPTCP_ADD_ADDR_BASE;
if (family == AF_INET6)
len = TCPOLEN_MPTCP_ADD_ADDR6_BASE;
if (!echo)
len += MPTCPOPT_THMAC_LEN;
/* account for 2 trailing 'nop' options */
if (port)
len += TCPOLEN_MPTCP_PORT_LEN + TCPOLEN_MPTCP_PORT_ALIGN;
return len;
}
bool mptcp_pm_add_addr_signal(struct mptcp_sock *msk, int *size, int remaining,
struct mptcp_addr_info *addr, bool *echo,
bool *drop_ts)
{
bool skip_add_addr = false;
int ret = false;
bool ret = false;
u8 add_addr;
int len = 0;
u8 family;
bool port;
@ -907,7 +927,7 @@ bool mptcp_pm_add_addr_signal(struct mptcp_sock *msk, unsigned int opt_size,
* plain dup-ack from TCP perspective. The other MPTCP-relevant info,
* if any, will be carried by the 'original' TCP ack
*/
remaining += opt_size;
len -= *size;
*echo = mptcp_pm_should_add_signal_echo(msk);
if (*echo) {
@ -922,9 +942,17 @@ bool mptcp_pm_add_addr_signal(struct mptcp_sock *msk, unsigned int opt_size,
family = msk->pm.local.family;
}
if (remaining < mptcp_add_addr_len(family, *echo, port)) {
len += mptcp_add_addr_len(family, *echo, port);
if (len > remaining) {
struct net *net = sock_net((struct sock *)msk);
if (*drop_ts && mptcp_add_addr_v6_port_drop_ts(net)) {
/* OK without TCP Timestamps? */
len -= TCPOLEN_TSTAMP_ALIGNED;
if (len <= remaining)
goto enough_space;
}
if (*echo) {
MPTCP_INC_STATS(net, MPTCP_MIB_ECHOADDTXDROP);
} else {
@ -934,7 +962,11 @@ bool mptcp_pm_add_addr_signal(struct mptcp_sock *msk, unsigned int opt_size,
goto drop_signal_mark;
}
*drop_ts = false;
enough_space:
ret = true;
*size = len;
drop_signal_mark:
WRITE_ONCE(msk->pm.addr_signal, add_addr);
@ -947,14 +979,22 @@ bool mptcp_pm_add_addr_signal(struct mptcp_sock *msk, unsigned int opt_size,
* let the PM state machine progress.
*/
if (skip_add_addr) {
mptcp_pm_del_add_timer(msk, addr, true);
mptcp_pm_announced_del_timer(msk, addr, true);
mptcp_pm_subflow_established(msk);
}
return ret;
}
static int mptcp_rm_addr_len(const struct mptcp_rm_list *rm_list)
{
if (rm_list->nr == 0 || rm_list->nr > MPTCP_RM_IDS_MAX)
return -EINVAL;
return TCPOLEN_MPTCP_RM_ADDR_BASE + roundup(rm_list->nr - 1, 4) + 1;
}
bool mptcp_pm_rm_addr_signal(struct mptcp_sock *msk, unsigned int remaining,
struct mptcp_rm_list *rm_list)
struct mptcp_rm_list *rm_list, int *size)
{
int ret = false, len;
u8 rm_addr;
@ -974,6 +1014,7 @@ bool mptcp_pm_rm_addr_signal(struct mptcp_sock *msk, unsigned int remaining,
if (remaining < len)
goto out_unlock;
*size = len;
*rm_list = msk->pm.rm_list_tx;
WRITE_ONCE(msk->pm.addr_signal, rm_addr);
ret = true;
@ -1102,7 +1143,7 @@ void mptcp_pm_worker(struct mptcp_sock *msk)
void mptcp_pm_destroy(struct mptcp_sock *msk)
{
mptcp_pm_free_anno_list(msk);
mptcp_pm_free_announced_list(msk);
if (mptcp_pm_is_userspace(msk))
mptcp_userspace_pm_free_local_addr_list(msk);

View File

@ -96,13 +96,13 @@ u8 mptcp_pm_get_limit_extra_subflows(const struct mptcp_sock *msk)
}
EXPORT_SYMBOL_GPL(mptcp_pm_get_limit_extra_subflows);
static bool lookup_subflow_by_daddr(const struct list_head *list,
const struct mptcp_addr_info *daddr)
static bool has_subflow_daddr(const struct mptcp_sock *msk,
const struct mptcp_addr_info *daddr)
{
struct mptcp_subflow_context *subflow;
struct mptcp_addr_info cur;
list_for_each_entry(subflow, list, node) {
mptcp_for_each_subflow(msk, subflow) {
struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
if (!((1 << inet_sk_state_load(ssk)) &
@ -374,7 +374,7 @@ static void mptcp_pm_create_subflow_or_signal_addr(struct mptcp_sock *msk)
/* If the alloc fails, we are on memory pressure, not worth
* continuing, and trying to create subflows.
*/
if (!mptcp_pm_alloc_anno_list(msk, &local.addr))
if (!mptcp_pm_announced_alloc(msk, &local.addr))
return;
__clear_bit(endp_id, msk->pm.id_avail_bitmap);
@ -673,7 +673,7 @@ static void mptcp_pm_nl_add_addr_received(struct mptcp_sock *msk)
mptcp_pm_addr_send_ack(msk);
mptcp_mpc_endpoint_setup(msk);
if (lookup_subflow_by_daddr(&msk->conn_list, &remote))
if (has_subflow_daddr(msk, &remote))
return;
/* pick id 0 port, if none is provided the remote address */
@ -1053,7 +1053,7 @@ int mptcp_pm_nl_add_addr_doit(struct sk_buff *skb, struct genl_info *info)
return ret;
}
static void mptcp_pm_remove_anno_addr(struct mptcp_sock *msk,
static void mptcp_pm_remove_announced(struct mptcp_sock *msk,
const struct mptcp_addr_info *addr,
bool force)
{
@ -1062,7 +1062,7 @@ static void mptcp_pm_remove_anno_addr(struct mptcp_sock *msk,
list.ids[list.nr++] = mptcp_endp_get_local_id(msk, addr);
announced = mptcp_remove_anno_list_by_saddr(msk, addr);
announced = mptcp_pm_announced_remove(msk, addr);
if (announced || force) {
spin_lock_bh(&msk->pm.lock);
if (announced)
@ -1098,8 +1098,8 @@ static int mptcp_nl_remove_subflow_and_signal_addr(struct net *net,
goto next;
lock_sock(sk);
remove_subflow = mptcp_lookup_subflow_by_saddr(&msk->conn_list, addr);
mptcp_pm_remove_anno_addr(msk, addr, remove_subflow &&
remove_subflow = mptcp_pm_has_subflow_saddr(msk, addr);
mptcp_pm_remove_announced(msk, addr, remove_subflow &&
!(entry->flags & MPTCP_PM_ADDR_FLAG_IMPLICIT));
list.ids[0] = mptcp_endp_get_local_id(msk, addr);
@ -1236,10 +1236,10 @@ static void mptcp_pm_flush_addrs_and_subflows(struct mptcp_sock *msk,
entry = list_prepare_entry(entry, rm_list, list);
list_for_each_entry_continue(entry, rm_list, list) {
if (mptcp_lookup_subflow_by_saddr(&msk->conn_list, &entry->addr))
if (mptcp_pm_has_subflow_saddr(msk, &entry->addr))
slist.ids[slist.nr++] = mptcp_endp_get_local_id(msk, &entry->addr);
if (mptcp_remove_anno_list_by_saddr(msk, &entry->addr))
if (mptcp_pm_announced_remove(msk, &entry->addr))
alist.ids[alist.nr++] = mptcp_endp_get_local_id(msk, &entry->addr);
if (slist.nr == MPTCP_RM_IDS_MAX ||

View File

@ -228,7 +228,7 @@ int mptcp_pm_nl_announce_doit(struct sk_buff *skb, struct genl_info *info)
lock_sock(sk);
spin_lock_bh(&msk->pm.lock);
if (mptcp_pm_alloc_anno_list(msk, &addr_val.addr)) {
if (mptcp_pm_announced_alloc(msk, &addr_val.addr)) {
msk->pm.add_addr_signaled++;
mptcp_pm_announce_addr(msk, &addr_val.addr, false);
mptcp_pm_addr_send_ack(msk);
@ -281,9 +281,9 @@ void mptcp_pm_remove_addr_entry(struct mptcp_sock *msk,
int anno_nr = 0;
/* only delete if either announced or matching a subflow */
if (mptcp_remove_anno_list_by_saddr(msk, &entry->addr))
if (mptcp_pm_announced_remove(msk, &entry->addr))
anno_nr++;
else if (!mptcp_lookup_subflow_by_saddr(&msk->conn_list, &entry->addr))
else if (!mptcp_pm_has_subflow_saddr(msk, &entry->addr))
return;
alist.ids[alist.nr++] = entry->addr.id;

View File

@ -798,6 +798,7 @@ unsigned int mptcp_close_timeout(const struct sock *sk);
int mptcp_get_pm_type(const struct net *net);
const char *mptcp_get_path_manager(const struct net *net);
const char *mptcp_get_scheduler(const struct net *net);
unsigned int mptcp_add_addr_v6_port_drop_ts(const struct net *net);
void mptcp_active_disable(struct sock *sk);
bool mptcp_active_should_disable(struct sock *ssk);
@ -1113,7 +1114,6 @@ void mptcp_pm_add_addr_received(const struct sock *ssk,
const struct mptcp_addr_info *addr);
void mptcp_pm_add_addr_echoed(struct mptcp_sock *msk,
const struct mptcp_addr_info *addr);
void mptcp_pm_add_addr_send_ack(struct mptcp_sock *msk);
void mptcp_pm_send_ack(struct mptcp_sock *msk,
struct mptcp_subflow_context *subflow,
bool prio, bool backup);
@ -1129,16 +1129,13 @@ int mptcp_pm_mp_prio_send_ack(struct mptcp_sock *msk,
struct mptcp_addr_info *addr,
struct mptcp_addr_info *rem,
u8 bkup);
bool mptcp_pm_alloc_anno_list(struct mptcp_sock *msk,
bool mptcp_pm_announced_alloc(struct mptcp_sock *msk,
const struct mptcp_addr_info *addr);
bool mptcp_pm_sport_in_anno_list(struct mptcp_sock *msk, const struct sock *sk);
struct mptcp_pm_add_entry *
mptcp_pm_del_add_timer(struct mptcp_sock *msk,
const struct mptcp_addr_info *addr, bool check_id);
bool mptcp_lookup_subflow_by_saddr(const struct list_head *list,
const struct mptcp_addr_info *saddr);
bool mptcp_remove_anno_list_by_saddr(struct mptcp_sock *msk,
const struct mptcp_addr_info *addr);
bool mptcp_pm_announced_remove(struct mptcp_sock *msk,
const struct mptcp_addr_info *addr);
bool mptcp_pm_announced_has_ssk(struct mptcp_sock *msk, const struct sock *ssk);
bool mptcp_pm_has_subflow_saddr(const struct mptcp_sock *msk,
const struct mptcp_addr_info *saddr);
int mptcp_pm_nl_set_flags(struct mptcp_pm_addr_entry *local,
struct genl_info *info);
int mptcp_userspace_pm_set_flags(struct mptcp_pm_addr_entry *local,
@ -1206,34 +1203,11 @@ static inline bool mptcp_pm_is_kernel(const struct mptcp_sock *msk)
return READ_ONCE(msk->pm.pm_type) == MPTCP_PM_TYPE_KERNEL;
}
static inline unsigned int mptcp_add_addr_len(int family, bool echo, bool port)
{
u8 len = TCPOLEN_MPTCP_ADD_ADDR_BASE;
if (family == AF_INET6)
len = TCPOLEN_MPTCP_ADD_ADDR6_BASE;
if (!echo)
len += MPTCPOPT_THMAC_LEN;
/* account for 2 trailing 'nop' options */
if (port)
len += TCPOLEN_MPTCP_PORT_LEN + TCPOLEN_MPTCP_PORT_ALIGN;
return len;
}
static inline int mptcp_rm_addr_len(const struct mptcp_rm_list *rm_list)
{
if (rm_list->nr == 0 || rm_list->nr > MPTCP_RM_IDS_MAX)
return -EINVAL;
return TCPOLEN_MPTCP_RM_ADDR_BASE + roundup(rm_list->nr - 1, 4) + 1;
}
bool mptcp_pm_add_addr_signal(struct mptcp_sock *msk, unsigned int opt_size,
unsigned int remaining,
struct mptcp_addr_info *addr, bool *echo);
bool mptcp_pm_add_addr_signal(struct mptcp_sock *msk, int *size, int remaining,
struct mptcp_addr_info *addr, bool *echo,
bool *drop_ts);
bool mptcp_pm_rm_addr_signal(struct mptcp_sock *msk, unsigned int remaining,
struct mptcp_rm_list *rm_list);
struct mptcp_rm_list *rm_list, int *len);
int mptcp_pm_get_local_id(struct mptcp_sock *msk, struct sock_common *skc);
int mptcp_pm_nl_get_local_id(struct mptcp_sock *msk,
struct mptcp_pm_addr_entry *skc);

View File

@ -235,7 +235,7 @@ static int subflow_check_req(struct request_sock *req,
pr_debug("syn inet_sport=%d %d\n",
ntohs(inet_sk(sk_listener)->inet_sport),
ntohs(inet_sk((struct sock *)subflow_req->msk)->inet_sport));
if (!mptcp_pm_sport_in_anno_list(subflow_req->msk, sk_listener)) {
if (!mptcp_pm_announced_has_ssk(subflow_req->msk, sk_listener)) {
SUBFLOW_REQ_INC_STATS(req, MPTCP_MIB_MISMATCHPORTSYNRX);
subflow_add_reset_reason(skb, MPTCP_RST_EPROHIBIT);
return -EPERM;
@ -926,7 +926,7 @@ static struct sock *subflow_syn_recv_sock(const struct sock *sk,
pr_debug("ack inet_sport=%d %d\n",
ntohs(inet_sk(sk)->inet_sport),
ntohs(inet_sk((struct sock *)owner)->inet_sport));
if (!mptcp_pm_sport_in_anno_list(owner, sk)) {
if (!mptcp_pm_announced_has_ssk(owner, sk)) {
SUBFLOW_REQ_INC_STATS(req, MPTCP_MIB_MISMATCHPORTACKRX);
subflow_add_reset_reason(skb, MPTCP_RST_EPROHIBIT);
goto dispose_child;

View File

@ -87,6 +87,10 @@ unset fb_mpc_data
unset fb_md5_sig
unset fb_dss
unset add_addr_tx_nr
unset add_addr_echo_tx_nr
unset add_addr_drop_tx_nr
# generated using "nfbpf_compile '(ip && (ip[54] & 0xf0) == 0x30) ||
# (ip6 && (ip6[74] & 0xf0) == 0x30)'"
CBPF_MPTCP_SUBOPTION_ADD_ADDR="14,
@ -1710,6 +1714,9 @@ chk_add_nr()
local ack_nr=$port_nr
local mis_syn_nr=0
local mis_ack_nr=0
local add_tx_nr=${add_addr_tx_nr:-${add_nr}}
local echo_tx_nr=${add_addr_echo_tx_nr:-${echo_nr}}
local drop_tx_nr=${add_addr_drop_tx_nr:-0}
local ns_tx=$ns1
local ns_rx=$ns2
local tx=""
@ -1811,50 +1818,25 @@ chk_add_nr()
print_ok
fi
fi
}
chk_add_tx_nr()
{
local add_tx_nr=$1
local echo_tx_nr=$2
local count
print_check "add addr tx"
count=$(mptcp_lib_get_counter ${ns1} "MPTcpExtAddAddrTx")
if [ -z "$count" ]; then
print_skip
count=$(mptcp_lib_get_counter ${ns_tx} "MPTcpExtAddAddrTx")
# Tolerate more ADD_ADDR then expected (if any), due to retransmissions
elif [ "$count" != "$add_tx_nr" ] &&
{ [ "$add_tx_nr" -eq 0 ] || [ "$count" -lt "$add_tx_nr" ]; }; then
if [ -n "$count" ] && [ "$count" != "$add_tx_nr" ] &&
{ [ "$add_tx_nr" -eq 0 ] || [ "$count" -lt "$add_tx_nr" ]; }; then
print_check "add addr tx"
fail_test "got $count ADD_ADDR[s] TX, expected $add_tx_nr"
else
print_ok
fi
print_check "add addr echo tx"
count=$(mptcp_lib_get_counter ${ns2} "MPTcpExtEchoAddTx")
if [ -z "$count" ]; then
print_skip
elif [ "$count" != "$echo_tx_nr" ]; then
count=$(mptcp_lib_get_counter ${ns_rx} "MPTcpExtEchoAddTx")
if [ -n "$count" ] && [ "$count" != "$echo_tx_nr" ]; then
print_check "add addr echo tx"
fail_test "got $count ADD_ADDR echo[s] TX, expected $echo_tx_nr"
else
print_ok
fi
}
chk_add_drop_tx_nr()
{
local drop_tx_nr=$1
local count
print_check "add addr tx drop"
count=$(mptcp_lib_get_counter ${ns1} "MPTcpExtAddAddrTxDrop")
if [ -z "$count" ]; then
print_skip
elif [ "$count" != "$drop_tx_nr" ]; then
count=$(mptcp_lib_get_counter ${ns_tx} "MPTcpExtAddAddrTxDrop")
if [ -n "$count" ] && [ "$count" != "$drop_tx_nr" ]; then
print_check "add addr tx drop"
fail_test "got $count ADD_ADDR drop[s] TX, expected $drop_tx_nr"
else
print_ok
fi
}
@ -2267,7 +2249,6 @@ signal_address_tests()
pm_nl_add_endpoint $ns1 10.0.2.1 flags signal
run_tests $ns1 $ns2 10.0.1.1
chk_join_nr 0 0 0
chk_add_tx_nr 1 1
chk_add_nr 1 1
fi
@ -2545,8 +2526,8 @@ add_addr_timeout_tests()
speed=slow \
run_tests $ns1 $ns2 10.0.1.1
chk_join_nr 1 1 1
chk_add_tx_nr 4 4
chk_add_nr 4 0
add_addr_echo_tx_nr=4 \
chk_add_nr 4 0
fi
# add_addr timeout IPv6
@ -2557,7 +2538,8 @@ add_addr_timeout_tests()
speed=slow \
run_tests $ns1 $ns2 dead:beef:1::1
chk_join_nr 1 1 1
chk_add_nr 4 0
add_addr_echo_tx_nr=4 \
chk_add_nr 4 0
fi
# signal addresses timeout
@ -2569,7 +2551,8 @@ add_addr_timeout_tests()
speed=10 \
run_tests $ns1 $ns2 10.0.1.1
chk_join_nr 2 2 2
chk_add_nr 8 0
add_addr_echo_tx_nr=8 \
chk_add_nr 8 0
fi
# signal invalid addresses timeout
@ -2582,7 +2565,8 @@ add_addr_timeout_tests()
run_tests $ns1 $ns2 10.0.1.1
join_syn_tx=2 \
chk_join_nr 1 1 1
chk_add_nr 8 0
add_addr_echo_tx_nr=7 \
chk_add_nr 8 0
fi
}
@ -3214,6 +3198,17 @@ add_addr_ports_tests()
chk_add_nr 1 1 1
fi
# signal address v6 with port
if reset "signal address v6 with port" &&
continue_if mptcp_lib_has_file '/proc/sys/net/mptcp/add_addr_v6_port_drop_ts'; then
pm_nl_set_limits $ns1 0 1
pm_nl_set_limits $ns2 1 1
pm_nl_add_endpoint $ns1 dead:beef:2::1 flags signal port 10100
run_tests $ns1 $ns2 dead:beef:1::1
chk_join_nr 1 1 1
chk_add_nr 1 1 1
fi
# subflow and signal with port
if reset "subflow and signal with port"; then
pm_nl_add_endpoint $ns1 10.0.2.1 flags signal port 10100
@ -3313,15 +3308,15 @@ add_addr_ports_tests()
if reset "signal addr list progresses after tx drop"; then
pm_nl_set_limits $ns1 0 2
pm_nl_set_limits $ns2 1 0
ip netns exec $ns1 sysctl -q net.mptcp.add_addr_v6_port_drop_ts=0 2>/dev/null || true
ip netns exec $ns1 sysctl -q net.ipv4.tcp_timestamps=1
ip netns exec $ns2 sysctl -q net.ipv4.tcp_timestamps=1
pm_nl_add_endpoint $ns1 dead:beef:2::1 flags signal port 10100
pm_nl_add_endpoint $ns1 dead:beef:3::1 flags signal
run_tests $ns1 $ns2 dead:beef:1::1
chk_add_drop_tx_nr 1
chk_add_tx_nr 1 1
chk_add_nr 1 1 0
add_addr_drop_tx_nr=1 \
chk_add_nr 1 1 0
fi
}