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))) + !(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); } 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 @@ 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); 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 @@ 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 || 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 @@ 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; 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 }