summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/networking/mptcp-sysctl.rst13
-rw-r--r--include/net/mptcp.h13
-rw-r--r--net/ipv4/tcp_output.c10
-rw-r--r--net/mptcp/ctrl.c18
-rw-r--r--net/mptcp/options.c68
-rw-r--r--net/mptcp/pm.c191
-rw-r--r--net/mptcp/pm_kernel.c22
-rw-r--r--net/mptcp/pm_userspace.c6
-rw-r--r--net/mptcp/protocol.h48
-rw-r--r--net/mptcp/subflow.c4
-rwxr-xr-xtools/testing/selftests/net/mptcp/mptcp_join.sh83
11 files changed, 248 insertions, 228 deletions
diff --git a/Documentation/networking/mptcp-sysctl.rst b/Documentation/networking/mptcp-sysctl.rst
index 1eb6af26b4a7..b9b5f58e0625 100644
--- a/Documentation/networking/mptcp-sysctl.rst
+++ b/Documentation/networking/mptcp-sysctl.rst
@@ -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
diff --git a/include/net/mptcp.h b/include/net/mptcp.h
index 24d1016a4664..71b9fc5a5796 100644
--- a/include/net/mptcp.h
+++ b/include/net/mptcp.h
@@ -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)
{
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index d3b8e61d3c5e..26dd751ec72a 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -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)) {
diff --git a/net/mptcp/ctrl.c b/net/mptcp/ctrl.c
index d96130e49942..63c5747f0f63 100644
--- a/net/mptcp/ctrl.c
+++ b/net/mptcp/ctrl.c
@@ -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));
diff --git a/net/mptcp/options.c b/net/mptcp/options.c
index 70b939f3c33d..dff3fd5d3b55 100644
--- a/net/mptcp/options.c
+++ b/net/mptcp/options.c
@@ -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)))
- return false;
-
- len = mptcp_rm_addr_len(&rm_list);
- if (len < 0)
- return false;
- if (remaining < len)
+ !(mptcp_pm_rm_addr_signal(msk, remaining, &rm_list, size)))
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);
}
diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c
index 470501470fe5..6afd39aea110 100644
--- a/net/mptcp/pm.c
+++ b/net/mptcp/pm.c
@@ -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 @@ out:
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 @@ out_unlock:
* 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);
diff --git a/net/mptcp/pm_kernel.c b/net/mptcp/pm_kernel.c
index 4ba4346d7adc..424f1a7f9248 100644
--- a/net/mptcp/pm_kernel.c
+++ b/net/mptcp/pm_kernel.c
@@ -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 @@ out_free:
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 @@ again:
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 ||
diff --git a/net/mptcp/pm_userspace.c b/net/mptcp/pm_userspace.c
index 0d3a95e676f1..d100867e9202 100644
--- a/net/mptcp/pm_userspace.c
+++ b/net/mptcp/pm_userspace.c
@@ -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;
diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
index b93b878478d2..4a2d40cd7b13 100644
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -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);
diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c
index d562e149606f..8e386899ceb9 100644
--- a/net/mptcp/subflow.c
+++ b/net/mptcp/subflow.c
@@ -235,7 +235,7 @@ again:
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 @@ create_child:
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;
diff --git a/tools/testing/selftests/net/mptcp/mptcp_join.sh b/tools/testing/selftests/net/mptcp/mptcp_join.sh
index ac8dc7051aae..c0aeffd5cb71 100755
--- a/tools/testing/selftests/net/mptcp/mptcp_join.sh
+++ b/tools/testing/selftests/net/mptcp/mptcp_join.sh
@@ -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
}