From 58cd405b83b388c75a4c24926e5d4aeae8d1eaf5 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Thu, 6 Jan 2022 16:20:14 -0800 Subject: [PATCH 01/13] mptcp: keep snd_una updated for fallback socket After shutdown, for fallback MPTCP sockets, we always have write_seq == snd_una+1 The above will foul OUTQ ioctl(). Keep snd_una in sync with write_seq even after shutdown. Signed-off-by: Paolo Abeni Signed-off-by: Mat Martineau Signed-off-by: David S. Miller --- net/mptcp/protocol.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index df5a0cf431c1..f6fc0f0f66f0 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -2666,6 +2666,7 @@ static void __mptcp_check_send_data_fin(struct sock *sk) * state now */ if (__mptcp_check_fallback(msk)) { + WRITE_ONCE(msk->snd_una, msk->write_seq); if ((1 << sk->sk_state) & (TCPF_CLOSING | TCPF_LAST_ACK)) { inet_sk_state_store(sk, TCP_CLOSE); mptcp_close_wake_up(sk); From f284c0c7732189fa77567dc061c5f4205c4fa05b Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Thu, 6 Jan 2022 16:20:15 -0800 Subject: [PATCH 02/13] mptcp: implement fastclose xmit path Allow the MPTCP xmit path to add MP_FASTCLOSE suboption on RST egress packets. Additionally reorder related options writing to reduce the number of conditionals required in the fast path. Co-developed-by: Geliang Tang Signed-off-by: Geliang Tang Co-developed-by: Matthieu Baerts Signed-off-by: Matthieu Baerts Signed-off-by: Paolo Abeni Signed-off-by: Mat Martineau Signed-off-by: David S. Miller --- net/mptcp/options.c | 57 ++++++++++++++++++++++++++++++++++---------- net/mptcp/protocol.h | 1 + 2 files changed, 46 insertions(+), 12 deletions(-) diff --git a/net/mptcp/options.c b/net/mptcp/options.c index fe98e4f475ba..46d35a235f35 100644 --- a/net/mptcp/options.c +++ b/net/mptcp/options.c @@ -768,6 +768,28 @@ 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, + 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); + + if (likely(!subflow->send_fastclose)) + return false; + + if (remaining < TCPOLEN_MPTCP_FASTCLOSE) + return false; + + *size = TCPOLEN_MPTCP_FASTCLOSE; + opts->suboptions |= OPTION_MPTCP_FASTCLOSE; + opts->rcvr_key = msk->remote_key; + + pr_debug("FASTCLOSE key=%llu", opts->rcvr_key); + return true; +} + static bool mptcp_established_options_mp_fail(struct sock *sk, unsigned int *size, unsigned int remaining, @@ -806,10 +828,12 @@ bool mptcp_established_options(struct sock *sk, struct sk_buff *skb, return false; if (unlikely(skb && TCP_SKB_CB(skb)->tcp_flags & TCPHDR_RST)) { - if (mptcp_established_options_mp_fail(sk, &opt_size, remaining, opts)) { + if (mptcp_established_options_fastclose(sk, &opt_size, remaining, opts) || + mptcp_established_options_mp_fail(sk, &opt_size, remaining, opts)) { *size += opt_size; 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)) { *size += opt_size; remaining -= opt_size; @@ -1251,17 +1275,8 @@ void mptcp_write_options(__be32 *ptr, const struct tcp_sock *tp, ptr += 2; } - /* RST is mutually exclusive with everything else */ - if (unlikely(OPTION_MPTCP_RST & opts->suboptions)) { - *ptr++ = mptcp_option(MPTCPOPT_RST, - TCPOLEN_MPTCP_RST, - opts->reset_transient, - opts->reset_reason); - return; - } - - /* DSS, MPC, MPJ and ADD_ADDR are mutually exclusive, see - * mptcp_established_options*() + /* DSS, MPC, MPJ, ADD_ADDR, FASTCLOSE and RST are mutually exclusive, + * see mptcp_established_options*() */ if (likely(OPTION_MPTCP_DSS & opts->suboptions)) { struct mptcp_ext *mpext = &opts->ext_copy; @@ -1447,6 +1462,24 @@ void mptcp_write_options(__be32 *ptr, const struct tcp_sock *tp, ptr += 1; } } + } else if (unlikely(OPTION_MPTCP_FASTCLOSE & opts->suboptions)) { + /* FASTCLOSE is mutually exclusive with others except RST */ + *ptr++ = mptcp_option(MPTCPOPT_MP_FASTCLOSE, + TCPOLEN_MPTCP_FASTCLOSE, + 0, 0); + put_unaligned_be64(opts->rcvr_key, ptr); + ptr += 2; + + if (OPTION_MPTCP_RST & opts->suboptions) + goto mp_rst; + return; + } else if (unlikely(OPTION_MPTCP_RST & opts->suboptions)) { +mp_rst: + *ptr++ = mptcp_option(MPTCPOPT_RST, + TCPOLEN_MPTCP_RST, + opts->reset_transient, + opts->reset_reason); + return; } if (OPTION_MPTCP_PRIO & opts->suboptions) { diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index 0486c9f5b38b..f177936ff67d 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -423,6 +423,7 @@ struct mptcp_subflow_context { backup : 1, send_mp_prio : 1, send_mp_fail : 1, + send_fastclose : 1, rx_eof : 1, can_ack : 1, /* only after processing the remote a key */ disposable : 1, /* ctx can be free at ulp release time */ From b29fcfb54cd70caca5b11c80d8d238854938884a Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Thu, 6 Jan 2022 16:20:16 -0800 Subject: [PATCH 03/13] mptcp: full disconnect implementation The current mptcp_disconnect() implementation lacks several steps, we additionally need to reset the msk socket state and flush the subflow list. Factor out the needed helper to avoid code duplication. Additionally ensure that the initial subflow is disposed only after mptcp_close(), just reset it at disconnect time. Signed-off-by: Paolo Abeni Signed-off-by: Mat Martineau Signed-off-by: David S. Miller --- net/mptcp/pm.c | 10 +++-- net/mptcp/protocol.c | 101 ++++++++++++++++++++++++++++++++----------- net/mptcp/protocol.h | 14 ++++++ net/mptcp/token.c | 1 + 4 files changed, 98 insertions(+), 28 deletions(-) diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c index 6ab386ff3294..6b2bfe89d445 100644 --- a/net/mptcp/pm.c +++ b/net/mptcp/pm.c @@ -356,7 +356,7 @@ void mptcp_pm_subflow_chk_stale(const struct mptcp_sock *msk, struct sock *ssk) } } -void mptcp_pm_data_init(struct mptcp_sock *msk) +void mptcp_pm_data_reset(struct mptcp_sock *msk) { msk->pm.add_addr_signaled = 0; msk->pm.add_addr_accepted = 0; @@ -371,10 +371,14 @@ void mptcp_pm_data_init(struct mptcp_sock *msk) WRITE_ONCE(msk->pm.remote_deny_join_id0, false); msk->pm.status = 0; + mptcp_pm_nl_data_init(msk); +} + +void mptcp_pm_data_init(struct mptcp_sock *msk) +{ spin_lock_init(&msk->pm.lock); INIT_LIST_HEAD(&msk->pm.anno_list); - - mptcp_pm_nl_data_init(msk); + mptcp_pm_data_reset(msk); } void __init mptcp_pm_init(void) diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index f6fc0f0f66f0..83f6d6c3e3eb 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -2253,6 +2253,10 @@ bool __mptcp_retransmit_pending_data(struct sock *sk) return true; } +/* flags for __mptcp_close_ssk() */ +#define MPTCP_CF_PUSH BIT(1) +#define MPTCP_CF_FASTCLOSE BIT(2) + /* subflow sockets can be either outgoing (connect) or incoming * (accept). * @@ -2262,22 +2266,37 @@ bool __mptcp_retransmit_pending_data(struct sock *sk) * parent socket. */ static void __mptcp_close_ssk(struct sock *sk, struct sock *ssk, - struct mptcp_subflow_context *subflow) + struct mptcp_subflow_context *subflow, + unsigned int flags) { struct mptcp_sock *msk = mptcp_sk(sk); - bool need_push; + bool need_push, dispose_it; - list_del(&subflow->node); + dispose_it = !msk->subflow || ssk != msk->subflow->sk; + if (dispose_it) + list_del(&subflow->node); lock_sock_nested(ssk, SINGLE_DEPTH_NESTING); + if (flags & MPTCP_CF_FASTCLOSE) + subflow->send_fastclose = 1; + + need_push = (flags & MPTCP_CF_PUSH) && __mptcp_retransmit_pending_data(sk); + if (!dispose_it) { + tcp_disconnect(ssk, 0); + msk->subflow->state = SS_UNCONNECTED; + mptcp_subflow_ctx_reset(subflow); + release_sock(ssk); + + goto out; + } + /* if we are invoked by the msk cleanup code, the subflow is * already orphaned */ if (ssk->sk_socket) sock_orphan(ssk); - need_push = __mptcp_retransmit_pending_data(sk); subflow->disposable = 1; /* if ssk hit tcp_done(), tcp_cleanup_ulp() cleared the related ops @@ -2297,14 +2316,12 @@ static void __mptcp_close_ssk(struct sock *sk, struct sock *ssk, sock_put(ssk); - if (ssk == msk->last_snd) - msk->last_snd = NULL; - if (ssk == msk->first) msk->first = NULL; - if (msk->subflow && ssk == msk->subflow->sk) - mptcp_dispose_initial_subflow(msk); +out: + if (ssk == msk->last_snd) + msk->last_snd = NULL; if (need_push) __mptcp_push_pending(sk, 0); @@ -2315,7 +2332,7 @@ void mptcp_close_ssk(struct sock *sk, struct sock *ssk, { if (sk->sk_state == TCP_ESTABLISHED) mptcp_event(MPTCP_EVENT_SUB_CLOSED, mptcp_sk(sk), ssk, GFP_KERNEL); - __mptcp_close_ssk(sk, ssk, subflow); + __mptcp_close_ssk(sk, ssk, subflow, MPTCP_CF_PUSH); } static unsigned int mptcp_sync_mss(struct sock *sk, u32 pmtu) @@ -2533,9 +2550,20 @@ static int __mptcp_init_sock(struct sock *sk) return 0; } -static int mptcp_init_sock(struct sock *sk) +static void mptcp_ca_reset(struct sock *sk) { struct inet_connection_sock *icsk = inet_csk(sk); + + tcp_assign_congestion_control(sk); + strcpy(mptcp_sk(sk)->ca_name, icsk->icsk_ca_ops->name); + + /* no need to keep a reference to the ops, the name will suffice */ + tcp_cleanup_congestion_control(sk); + icsk->icsk_ca_ops = NULL; +} + +static int mptcp_init_sock(struct sock *sk) +{ struct net *net = sock_net(sk); int ret; @@ -2556,12 +2584,7 @@ static int mptcp_init_sock(struct sock *sk) /* fetch the ca name; do it outside __mptcp_init_sock(), so that clone will * propagate the correct value */ - tcp_assign_congestion_control(sk); - strcpy(mptcp_sk(sk)->ca_name, icsk->icsk_ca_ops->name); - - /* no need to keep a reference to the ops, the name will suffice */ - tcp_cleanup_congestion_control(sk); - icsk->icsk_ca_ops = NULL; + mptcp_ca_reset(sk); sk_sockets_allocated_inc(sk); sk->sk_rcvbuf = sock_net(sk)->ipv4.sysctl_tcp_rmem[1]; @@ -2720,9 +2743,13 @@ static void __mptcp_destroy_sock(struct sock *sk) sk_stop_timer(sk, &sk->sk_timer); msk->pm.status = 0; + /* clears msk->subflow, allowing the following loop to close + * even the initial subflow + */ + mptcp_dispose_initial_subflow(msk); list_for_each_entry_safe(subflow, tmp, &conn_list, node) { struct sock *ssk = mptcp_subflow_tcp_sock(subflow); - __mptcp_close_ssk(sk, ssk, subflow); + __mptcp_close_ssk(sk, ssk, subflow, 0); } sk->sk_prot->destroy(sk); @@ -2733,7 +2760,6 @@ static void __mptcp_destroy_sock(struct sock *sk) xfrm_sk_free_policy(sk); sk_refcnt_debug_release(sk); - mptcp_dispose_initial_subflow(msk); sock_put(sk); } @@ -2769,6 +2795,9 @@ static void mptcp_close(struct sock *sk, long timeout) sock_hold(sk); pr_debug("msk=%p state=%d", sk, sk->sk_state); + if (mptcp_sk(sk)->token) + mptcp_event(MPTCP_EVENT_CLOSED, mptcp_sk(sk), NULL, GFP_KERNEL); + if (sk->sk_state == TCP_CLOSE) { __mptcp_destroy_sock(sk); do_cancel_work = true; @@ -2779,9 +2808,6 @@ static void mptcp_close(struct sock *sk, long timeout) if (do_cancel_work) mptcp_cancel_work(sk); - if (mptcp_sk(sk)->token) - mptcp_event(MPTCP_EVENT_CLOSED, mptcp_sk(sk), NULL, GFP_KERNEL); - sock_put(sk); } @@ -2815,13 +2841,36 @@ static int mptcp_disconnect(struct sock *sk, int flags) mptcp_do_flush_join_list(msk); + inet_sk_state_store(sk, TCP_CLOSE); + mptcp_for_each_subflow(msk, subflow) { struct sock *ssk = mptcp_subflow_tcp_sock(subflow); - lock_sock(ssk); - tcp_disconnect(ssk, flags); - release_sock(ssk); + __mptcp_close_ssk(sk, ssk, subflow, MPTCP_CF_FASTCLOSE); } + + sk_stop_timer(sk, &msk->sk.icsk_retransmit_timer); + sk_stop_timer(sk, &sk->sk_timer); + + if (mptcp_sk(sk)->token) + mptcp_event(MPTCP_EVENT_CLOSED, mptcp_sk(sk), NULL, GFP_KERNEL); + + mptcp_destroy_common(msk); + msk->last_snd = NULL; + msk->flags = 0; + msk->recovery = false; + msk->can_ack = false; + msk->fully_established = false; + msk->rcv_data_fin = false; + msk->snd_data_fin_enable = false; + msk->rcv_fastclose = false; + msk->use_64bit_ack = false; + WRITE_ONCE(msk->csum_enabled, mptcp_is_checksum_enabled(sock_net(sk))); + mptcp_pm_data_reset(msk); + mptcp_ca_reset(sk); + + sk->sk_shutdown = 0; + sk_error_report(sk); return 0; } @@ -2961,9 +3010,11 @@ void mptcp_destroy_common(struct mptcp_sock *msk) __mptcp_clear_xmit(sk); /* move to sk_receive_queue, sk_stream_kill_queues will purge it */ + mptcp_data_lock(sk); skb_queue_splice_tail_init(&msk->receive_queue, &sk->sk_receive_queue); __skb_queue_purge(&sk->sk_receive_queue); skb_rbtree_purge(&msk->out_of_order_queue); + mptcp_data_unlock(sk); /* move all the rx fwd alloc into the sk_mem_reclaim_final in * inet_sock_destruct() will dispose it diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index f177936ff67d..1a4ca5a202c8 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -395,6 +395,9 @@ DECLARE_PER_CPU(struct mptcp_delegated_action, mptcp_delegated_actions); /* MPTCP subflow context */ struct mptcp_subflow_context { struct list_head node;/* conn_list of subflows */ + + char reset_start[0]; + unsigned long avg_pacing_rate; /* protected by msk socket lock */ u64 local_key; u64 remote_key; @@ -442,6 +445,9 @@ struct mptcp_subflow_context { u8 stale_count; long delegated_status; + + char reset_end[0]; + struct list_head delegated_node; /* link into delegated_action, protected by local BH */ u32 setsockopt_seq; @@ -473,6 +479,13 @@ mptcp_subflow_tcp_sock(const struct mptcp_subflow_context *subflow) return subflow->tcp_sock; } +static inline void +mptcp_subflow_ctx_reset(struct mptcp_subflow_context *subflow) +{ + memset(subflow->reset_start, 0, subflow->reset_end - subflow->reset_start); + subflow->request_mptcp = 1; +} + static inline u64 mptcp_subflow_get_map_offset(const struct mptcp_subflow_context *subflow) { @@ -713,6 +726,7 @@ void mptcp_crypto_hmac_sha(u64 key1, u64 key2, u8 *msg, int len, void *hmac); void __init mptcp_pm_init(void); void mptcp_pm_data_init(struct mptcp_sock *msk); +void mptcp_pm_data_reset(struct mptcp_sock *msk); void mptcp_pm_subflow_chk_stale(const struct mptcp_sock *msk, struct sock *ssk); void mptcp_pm_nl_subflow_chk_stale(const struct mptcp_sock *msk, struct sock *ssk); void mptcp_pm_new_connection(struct mptcp_sock *msk, const struct sock *ssk, int server_side); diff --git a/net/mptcp/token.c b/net/mptcp/token.c index e581b341c5be..f52ee7b26aed 100644 --- a/net/mptcp/token.c +++ b/net/mptcp/token.c @@ -384,6 +384,7 @@ void mptcp_token_destroy(struct mptcp_sock *msk) bucket->chain_len--; } spin_unlock_bh(&bucket->lock); + WRITE_ONCE(msk->token, 0); } void __init mptcp_token_init(void) From 71ba088ce0aa87370b18a1d35cd742f352d51c24 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Thu, 6 Jan 2022 16:20:17 -0800 Subject: [PATCH 04/13] mptcp: cleanup accept and poll After the previous patch, msk->subflow will never be deleted during the whole msk lifetime. We don't need anymore to acquire references to it in mptcp_stream_accept() and we can use the listener subflow accept queue to simplify mptcp_poll() for listener socket. Overall this removes a lock pair and 4 more atomic operations per accept(). Signed-off-by: Paolo Abeni Signed-off-by: Mat Martineau Signed-off-by: David S. Miller --- net/mptcp/protocol.c | 25 +++++++------------------ net/mptcp/protocol.h | 1 - net/mptcp/subflow.c | 1 - 3 files changed, 7 insertions(+), 20 deletions(-) diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index 83f6d6c3e3eb..628cd60c9d0f 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -3493,17 +3493,9 @@ static int mptcp_stream_accept(struct socket *sock, struct socket *newsock, pr_debug("msk=%p", msk); - lock_sock(sock->sk); - if (sock->sk->sk_state != TCP_LISTEN) - goto unlock_fail; - ssock = __mptcp_nmpc_socket(msk); if (!ssock) - goto unlock_fail; - - clear_bit(MPTCP_DATA_READY, &msk->flags); - sock_hold(ssock->sk); - release_sock(sock->sk); + return -EINVAL; err = ssock->ops->accept(sock, newsock, flags, kern); if (err == 0 && !mptcp_is_tcpsk(newsock->sk)) { @@ -3543,14 +3535,7 @@ static int mptcp_stream_accept(struct socket *sock, struct socket *newsock, release_sock(newsk); } - if (inet_csk_listen_poll(ssock->sk)) - set_bit(MPTCP_DATA_READY, &msk->flags); - sock_put(ssock->sk); return err; - -unlock_fail: - release_sock(sock->sk); - return -EINVAL; } static __poll_t mptcp_check_readable(struct mptcp_sock *msk) @@ -3596,8 +3581,12 @@ static __poll_t mptcp_poll(struct file *file, struct socket *sock, state = inet_sk_state_load(sk); pr_debug("msk=%p state=%d flags=%lx", msk, state, msk->flags); - if (state == TCP_LISTEN) - return test_bit(MPTCP_DATA_READY, &msk->flags) ? EPOLLIN | EPOLLRDNORM : 0; + if (state == TCP_LISTEN) { + if (WARN_ON_ONCE(!msk->subflow || !msk->subflow->sk)) + return 0; + + return inet_csk_listen_poll(msk->subflow->sk); + } if (state != TCP_SYN_SENT && state != TCP_SYN_RECV) { mask |= mptcp_check_readable(msk); diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index 1a4ca5a202c8..386d1a98ff1d 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -111,7 +111,6 @@ #define MPTCP_RST_TRANSIENT BIT(0) /* MPTCP socket flags */ -#define MPTCP_DATA_READY 0 #define MPTCP_NOSPACE 1 #define MPTCP_WORK_RTX 2 #define MPTCP_WORK_EOF 3 diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c index 24bc9d5e87be..d861307f7efe 100644 --- a/net/mptcp/subflow.c +++ b/net/mptcp/subflow.c @@ -1293,7 +1293,6 @@ static void subflow_data_ready(struct sock *sk) if (reqsk_queue_empty(&inet_csk(sk)->icsk_accept_queue)) return; - set_bit(MPTCP_DATA_READY, &msk->flags); parent->sk_data_ready(parent); return; } From 3d1d6d66e15612801bec79b190af746622f0d427 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Thu, 6 Jan 2022 16:20:18 -0800 Subject: [PATCH 05/13] mptcp: implement support for user-space disconnect Handle explicitly AF_UNSPEC in mptcp_stream_connnect() to allow user-space to disconnect established MPTCP connections Signed-off-by: Paolo Abeni Signed-off-by: Mat Martineau Signed-off-by: David S. Miller --- net/mptcp/protocol.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index 628cd60c9d0f..667e153e6e24 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -3404,9 +3404,20 @@ static int mptcp_stream_connect(struct socket *sock, struct sockaddr *uaddr, struct mptcp_sock *msk = mptcp_sk(sock->sk); struct mptcp_subflow_context *subflow; struct socket *ssock; - int err; + int err = -EINVAL; lock_sock(sock->sk); + if (uaddr) { + if (addr_len < sizeof(uaddr->sa_family)) + goto unlock; + + if (uaddr->sa_family == AF_UNSPEC) { + err = mptcp_disconnect(sock->sk, flags); + sock->state = err ? SS_DISCONNECTING : SS_UNCONNECTED; + goto unlock; + } + } + if (sock->state != SS_UNCONNECTED && msk->subflow) { /* pending connection or invalid state, let existing subflow * cope with that @@ -3416,10 +3427,8 @@ static int mptcp_stream_connect(struct socket *sock, struct sockaddr *uaddr, } ssock = __mptcp_nmpc_socket(msk); - if (!ssock) { - err = -EINVAL; + if (!ssock) goto unlock; - } mptcp_token_destroy(msk); inet_sk_state_store(sock->sk, TCP_SYN_SENT); From 05be5e273c84f6329b3cac4f5bb7b1fe6eaefd5d Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Thu, 6 Jan 2022 16:20:19 -0800 Subject: [PATCH 06/13] selftests: mptcp: add disconnect tests Performs several disconnect/reconnect on the same socket, ensuring the overall transfer is succesful. The new test leverages ioctl(SIOCOUTQ) to ensure all the pending data is acked before disconnecting. Additionally order alphabetically the test program arguments list for better maintainability. Signed-off-by: Paolo Abeni Signed-off-by: Mat Martineau Signed-off-by: David S. Miller --- .../selftests/net/mptcp/mptcp_connect.c | 148 +++++++++++++++--- .../selftests/net/mptcp/mptcp_connect.sh | 39 ++++- 2 files changed, 160 insertions(+), 27 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_connect.c b/tools/testing/selftests/net/mptcp/mptcp_connect.c index a30e93c5c549..8628aa61b763 100644 --- a/tools/testing/selftests/net/mptcp/mptcp_connect.c +++ b/tools/testing/selftests/net/mptcp/mptcp_connect.c @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -28,6 +29,7 @@ #include #include +#include extern int optind; @@ -68,6 +70,8 @@ static unsigned int cfg_time; static unsigned int cfg_do_w; static int cfg_wait; static uint32_t cfg_mark; +static char *cfg_input; +static int cfg_repeat = 1; struct cfg_cmsg_types { unsigned int cmsg_enabled:1; @@ -91,22 +95,31 @@ static struct cfg_sockopt_types cfg_sockopt_types; static void die_usage(void) { - fprintf(stderr, "Usage: mptcp_connect [-6] [-u] [-s MPTCP|TCP] [-p port] [-m mode]" - "[-l] [-w sec] [-t num] [-T num] connect_address\n"); + fprintf(stderr, "Usage: mptcp_connect [-6] [-c cmsg] [-i file] [-I num] [-j] [-l] " + "[-m mode] [-M mark] [-o option] [-p port] [-P mode] [-j] [-l] [-r num] " + "[-s MPTCP|TCP] [-S num] [-r num] [-t num] [-T num] [-u] [-w sec] connect_address\n"); fprintf(stderr, "\t-6 use ipv6\n"); - fprintf(stderr, "\t-t num -- set poll timeout to num\n"); - fprintf(stderr, "\t-T num -- set expected runtime to num ms\n"); - fprintf(stderr, "\t-S num -- set SO_SNDBUF to num\n"); - fprintf(stderr, "\t-R num -- set SO_RCVBUF to num\n"); - fprintf(stderr, "\t-p num -- use port num\n"); - fprintf(stderr, "\t-s [MPTCP|TCP] -- use mptcp(default) or tcp sockets\n"); + fprintf(stderr, "\t-c cmsg -- test cmsg type \n"); + fprintf(stderr, "\t-i file -- read the data to send from the given file instead of stdin"); + fprintf(stderr, "\t-I num -- repeat the transfer 'num' times. In listen mode accepts num " + "incoming connections, in client mode, disconnect and reconnect to the server\n"); + fprintf(stderr, "\t-j -- add additional sleep at connection start and tear down " + "-- for MPJ tests\n"); + fprintf(stderr, "\t-l -- listens mode, accepts incoming connection\n"); fprintf(stderr, "\t-m [poll|mmap|sendfile] -- use poll(default)/mmap+write/sendfile\n"); fprintf(stderr, "\t-M mark -- set socket packet mark\n"); - fprintf(stderr, "\t-w num -- wait num sec before closing the socket\n"); - fprintf(stderr, "\t-c cmsg -- test cmsg type \n"); fprintf(stderr, "\t-o option -- test sockopt