Merge branch 'mptcp-more-miscellaneous-mptcp-fixes'
Mat Martineau says: ==================== mptcp: More miscellaneous MPTCP fixes Here's another batch of fixup and enhancement patches that we have collected in the MPTCP tree. Patch 1 removes an unnecessary flag and related code. Patch 2 fixes a bug encountered when closing fallback sockets. Patches 3 and 4 choose a better transmit subflow, with a self test. Patch 5 adjusts tracking of unaccepted subflows Patches 6-8 improve handling of long ADD_ADDR options, with a test. Patch 9 more reliably tracks the MPTCP-level window shared with peers. Patch 10 sends MPTCP-level acknowledgements more aggressively, so the peer can send more data without extra delay. ==================== Link: https://lore.kernel.org/r/20201119194603.103158-1-mathew.j.martineau@linux.intel.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
9e8ac63fe1
@ -88,7 +88,8 @@ bool mptcp_established_options(struct sock *sk, struct sk_buff *skb,
|
||||
struct mptcp_out_options *opts);
|
||||
void mptcp_incoming_options(struct sock *sk, struct sk_buff *skb);
|
||||
|
||||
void mptcp_write_options(__be32 *ptr, struct mptcp_out_options *opts);
|
||||
void mptcp_write_options(__be32 *ptr, const struct tcp_sock *tp,
|
||||
struct mptcp_out_options *opts);
|
||||
|
||||
/* move the skb extension owership, with the assumption that 'to' is
|
||||
* newly allocated
|
||||
|
@ -445,11 +445,12 @@ struct tcp_out_options {
|
||||
struct mptcp_out_options mptcp;
|
||||
};
|
||||
|
||||
static void mptcp_options_write(__be32 *ptr, struct tcp_out_options *opts)
|
||||
static void mptcp_options_write(__be32 *ptr, const struct tcp_sock *tp,
|
||||
struct tcp_out_options *opts)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_MPTCP)
|
||||
if (unlikely(OPTION_MPTCP & opts->options))
|
||||
mptcp_write_options(ptr, &opts->mptcp);
|
||||
mptcp_write_options(ptr, tp, &opts->mptcp);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -701,7 +702,7 @@ static void tcp_options_write(__be32 *ptr, struct tcp_sock *tp,
|
||||
|
||||
smc_options_write(ptr, &options);
|
||||
|
||||
mptcp_options_write(ptr, opts);
|
||||
mptcp_options_write(ptr, tp, opts);
|
||||
}
|
||||
|
||||
static void smc_set_option(const struct tcp_sock *tp,
|
||||
@ -1346,7 +1347,6 @@ static int __tcp_transmit_skb(struct sock *sk, struct sk_buff *skb,
|
||||
}
|
||||
}
|
||||
|
||||
tcp_options_write((__be32 *)(th + 1), tp, &opts);
|
||||
skb_shinfo(skb)->gso_type = sk->sk_gso_type;
|
||||
if (likely(!(tcb->tcp_flags & TCPHDR_SYN))) {
|
||||
th->window = htons(tcp_select_window(sk));
|
||||
@ -1357,6 +1357,9 @@ static int __tcp_transmit_skb(struct sock *sk, struct sk_buff *skb,
|
||||
*/
|
||||
th->window = htons(min(tp->rcv_wnd, 65535U));
|
||||
}
|
||||
|
||||
tcp_options_write((__be32 *)(th + 1), tp, &opts);
|
||||
|
||||
#ifdef CONFIG_TCP_MD5SIG
|
||||
/* Calculate the MD5 hash, as we have all we need now */
|
||||
if (md5) {
|
||||
|
@ -242,7 +242,9 @@ static void mptcp_parse_option(const struct sk_buff *skb,
|
||||
|
||||
mp_opt->add_addr = 1;
|
||||
mp_opt->addr_id = *ptr++;
|
||||
pr_debug("ADD_ADDR: id=%d, echo=%d", mp_opt->addr_id, mp_opt->echo);
|
||||
pr_debug("ADD_ADDR%s: id=%d, echo=%d",
|
||||
(mp_opt->family == MPTCP_ADDR_IPVERSION_6) ? "6" : "",
|
||||
mp_opt->addr_id, mp_opt->echo);
|
||||
if (mp_opt->family == MPTCP_ADDR_IPVERSION_4) {
|
||||
memcpy((u8 *)&mp_opt->addr.s_addr, (u8 *)ptr, 4);
|
||||
ptr += 4;
|
||||
@ -528,6 +530,7 @@ static bool mptcp_established_options_dss(struct sock *sk, struct sk_buff *skb,
|
||||
opts->ext_copy.ack64 = 0;
|
||||
}
|
||||
opts->ext_copy.use_ack = 1;
|
||||
WRITE_ONCE(msk->old_wspace, __mptcp_space((struct sock *)msk));
|
||||
|
||||
/* Add kind/length/subtype/flag overhead if mapping is not populated */
|
||||
if (dss_size == 0)
|
||||
@ -573,17 +576,27 @@ static u64 add_addr6_generate_hmac(u64 key1, u64 key2, u8 addr_id,
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool mptcp_established_options_add_addr(struct sock *sk,
|
||||
static bool mptcp_established_options_add_addr(struct sock *sk, struct sk_buff *skb,
|
||||
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);
|
||||
bool drop_other_suboptions = false;
|
||||
unsigned int opt_size = *size;
|
||||
struct mptcp_addr_info saddr;
|
||||
bool echo;
|
||||
int len;
|
||||
|
||||
if (mptcp_pm_should_add_signal_ipv6(msk) &&
|
||||
skb && skb_is_tcp_pure_ack(skb)) {
|
||||
pr_debug("drop other suboptions");
|
||||
opts->suboptions = 0;
|
||||
remaining += opt_size;
|
||||
drop_other_suboptions = true;
|
||||
}
|
||||
|
||||
if (!mptcp_pm_should_add_signal(msk) ||
|
||||
!(mptcp_pm_add_addr_signal(msk, remaining, &saddr, &echo)))
|
||||
return false;
|
||||
@ -593,6 +606,8 @@ static bool mptcp_established_options_add_addr(struct sock *sk,
|
||||
return false;
|
||||
|
||||
*size = len;
|
||||
if (drop_other_suboptions)
|
||||
*size -= opt_size;
|
||||
opts->addr_id = saddr.id;
|
||||
if (saddr.family == AF_INET) {
|
||||
opts->suboptions |= OPTION_MPTCP_ADD_ADDR;
|
||||
@ -678,7 +693,7 @@ bool mptcp_established_options(struct sock *sk, struct sk_buff *skb,
|
||||
|
||||
*size += opt_size;
|
||||
remaining -= opt_size;
|
||||
if (mptcp_established_options_add_addr(sk, &opt_size, remaining, opts)) {
|
||||
if (mptcp_established_options_add_addr(sk, skb, &opt_size, remaining, opts)) {
|
||||
*size += opt_size;
|
||||
remaining -= opt_size;
|
||||
ret = true;
|
||||
@ -759,6 +774,11 @@ static bool check_fully_established(struct mptcp_sock *msk, struct sock *ssk,
|
||||
goto fully_established;
|
||||
}
|
||||
|
||||
if (mp_opt->add_addr) {
|
||||
WRITE_ONCE(msk->fully_established, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* If the first established packet does not contain MP_CAPABLE + data
|
||||
* then fallback to TCP. Fallback scenarios requires a reset for
|
||||
* MP_JOIN subflows.
|
||||
@ -991,7 +1011,24 @@ void mptcp_incoming_options(struct sock *sk, struct sk_buff *skb)
|
||||
}
|
||||
}
|
||||
|
||||
void mptcp_write_options(__be32 *ptr, struct mptcp_out_options *opts)
|
||||
static void mptcp_set_rwin(const struct tcp_sock *tp)
|
||||
{
|
||||
const struct sock *ssk = (const struct sock *)tp;
|
||||
const struct mptcp_subflow_context *subflow;
|
||||
struct mptcp_sock *msk;
|
||||
u64 ack_seq;
|
||||
|
||||
subflow = mptcp_subflow_ctx(ssk);
|
||||
msk = mptcp_sk(subflow->conn);
|
||||
|
||||
ack_seq = READ_ONCE(msk->ack_seq) + tp->rcv_wnd;
|
||||
|
||||
if (after64(ack_seq, READ_ONCE(msk->rcv_wnd_sent)))
|
||||
WRITE_ONCE(msk->rcv_wnd_sent, ack_seq);
|
||||
}
|
||||
|
||||
void mptcp_write_options(__be32 *ptr, const struct tcp_sock *tp,
|
||||
struct mptcp_out_options *opts)
|
||||
{
|
||||
if ((OPTION_MPTCP_MPC_SYN | OPTION_MPTCP_MPC_SYNACK |
|
||||
OPTION_MPTCP_MPC_ACK) & opts->suboptions) {
|
||||
@ -1148,4 +1185,7 @@ mp_capable_done:
|
||||
TCPOPT_NOP << 8 | TCPOPT_NOP, ptr);
|
||||
}
|
||||
}
|
||||
|
||||
if (tp)
|
||||
mptcp_set_rwin(tp);
|
||||
}
|
||||
|
@ -16,11 +16,17 @@ int mptcp_pm_announce_addr(struct mptcp_sock *msk,
|
||||
const struct mptcp_addr_info *addr,
|
||||
bool echo)
|
||||
{
|
||||
u8 add_addr = READ_ONCE(msk->pm.add_addr_signal);
|
||||
|
||||
pr_debug("msk=%p, local_id=%d", msk, addr->id);
|
||||
|
||||
msk->pm.local = *addr;
|
||||
WRITE_ONCE(msk->pm.add_addr_echo, echo);
|
||||
WRITE_ONCE(msk->pm.add_addr_signal, true);
|
||||
add_addr |= BIT(MPTCP_ADD_ADDR_SIGNAL);
|
||||
if (echo)
|
||||
add_addr |= BIT(MPTCP_ADD_ADDR_ECHO);
|
||||
if (addr->family == AF_INET6)
|
||||
add_addr |= BIT(MPTCP_ADD_ADDR_IPV6);
|
||||
WRITE_ONCE(msk->pm.add_addr_signal, add_addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -149,14 +155,24 @@ void mptcp_pm_add_addr_received(struct mptcp_sock *msk,
|
||||
|
||||
spin_lock_bh(&pm->lock);
|
||||
|
||||
if (!READ_ONCE(pm->accept_addr))
|
||||
if (!READ_ONCE(pm->accept_addr)) {
|
||||
mptcp_pm_announce_addr(msk, addr, true);
|
||||
else if (mptcp_pm_schedule_work(msk, MPTCP_PM_ADD_ADDR_RECEIVED))
|
||||
mptcp_pm_add_addr_send_ack(msk);
|
||||
} else if (mptcp_pm_schedule_work(msk, MPTCP_PM_ADD_ADDR_RECEIVED)) {
|
||||
pm->remote = *addr;
|
||||
}
|
||||
|
||||
spin_unlock_bh(&pm->lock);
|
||||
}
|
||||
|
||||
void mptcp_pm_add_addr_send_ack(struct mptcp_sock *msk)
|
||||
{
|
||||
if (!mptcp_pm_should_add_signal_ipv6(msk))
|
||||
return;
|
||||
|
||||
mptcp_pm_schedule_work(msk, MPTCP_PM_ADD_ADDR_SEND_ACK);
|
||||
}
|
||||
|
||||
void mptcp_pm_rm_addr_received(struct mptcp_sock *msk, u8 rm_id)
|
||||
{
|
||||
struct mptcp_pm_data *pm = &msk->pm;
|
||||
@ -182,13 +198,13 @@ bool mptcp_pm_add_addr_signal(struct mptcp_sock *msk, unsigned int remaining,
|
||||
if (!mptcp_pm_should_add_signal(msk))
|
||||
goto out_unlock;
|
||||
|
||||
*echo = READ_ONCE(msk->pm.add_addr_echo);
|
||||
*echo = mptcp_pm_should_add_signal_echo(msk);
|
||||
|
||||
if (remaining < mptcp_add_addr_len(msk->pm.local.family, *echo))
|
||||
goto out_unlock;
|
||||
|
||||
*saddr = msk->pm.local;
|
||||
WRITE_ONCE(msk->pm.add_addr_signal, false);
|
||||
WRITE_ONCE(msk->pm.add_addr_signal, 0);
|
||||
ret = true;
|
||||
|
||||
out_unlock:
|
||||
@ -232,11 +248,10 @@ void mptcp_pm_data_init(struct mptcp_sock *msk)
|
||||
msk->pm.subflows = 0;
|
||||
msk->pm.rm_id = 0;
|
||||
WRITE_ONCE(msk->pm.work_pending, false);
|
||||
WRITE_ONCE(msk->pm.add_addr_signal, false);
|
||||
WRITE_ONCE(msk->pm.add_addr_signal, 0);
|
||||
WRITE_ONCE(msk->pm.rm_addr_signal, false);
|
||||
WRITE_ONCE(msk->pm.accept_addr, false);
|
||||
WRITE_ONCE(msk->pm.accept_subflow, false);
|
||||
WRITE_ONCE(msk->pm.add_addr_echo, false);
|
||||
msk->pm.status = 0;
|
||||
|
||||
spin_lock_init(&msk->pm.lock);
|
||||
|
@ -228,6 +228,7 @@ static void mptcp_pm_add_timer(struct timer_list *timer)
|
||||
if (!mptcp_pm_should_add_signal(msk)) {
|
||||
pr_debug("retransmit ADD_ADDR id=%d", entry->addr.id);
|
||||
mptcp_pm_announce_addr(msk, &entry->addr, false);
|
||||
mptcp_pm_add_addr_send_ack(msk);
|
||||
entry->retrans_times++;
|
||||
}
|
||||
|
||||
@ -328,6 +329,7 @@ static void mptcp_pm_create_subflow_or_signal_addr(struct mptcp_sock *msk)
|
||||
if (mptcp_pm_alloc_anno_list(msk, local)) {
|
||||
msk->pm.add_addr_signaled++;
|
||||
mptcp_pm_announce_addr(msk, &local->addr, false);
|
||||
mptcp_pm_nl_add_addr_send_ack(msk);
|
||||
}
|
||||
} else {
|
||||
/* pick failed, avoid fourther attempts later */
|
||||
@ -398,6 +400,33 @@ void mptcp_pm_nl_add_addr_received(struct mptcp_sock *msk)
|
||||
spin_lock_bh(&msk->pm.lock);
|
||||
|
||||
mptcp_pm_announce_addr(msk, &remote, true);
|
||||
mptcp_pm_nl_add_addr_send_ack(msk);
|
||||
}
|
||||
|
||||
void mptcp_pm_nl_add_addr_send_ack(struct mptcp_sock *msk)
|
||||
{
|
||||
struct mptcp_subflow_context *subflow;
|
||||
|
||||
if (!mptcp_pm_should_add_signal_ipv6(msk))
|
||||
return;
|
||||
|
||||
__mptcp_flush_join_list(msk);
|
||||
subflow = list_first_entry_or_null(&msk->conn_list, typeof(*subflow), node);
|
||||
if (subflow) {
|
||||
struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
|
||||
u8 add_addr;
|
||||
|
||||
spin_unlock_bh(&msk->pm.lock);
|
||||
pr_debug("send ack for add_addr6");
|
||||
lock_sock(ssk);
|
||||
tcp_send_ack(ssk);
|
||||
release_sock(ssk);
|
||||
spin_lock_bh(&msk->pm.lock);
|
||||
|
||||
add_addr = READ_ONCE(msk->pm.add_addr_signal);
|
||||
add_addr &= ~BIT(MPTCP_ADD_ADDR_IPV6);
|
||||
WRITE_ONCE(msk->pm.add_addr_signal, add_addr);
|
||||
}
|
||||
}
|
||||
|
||||
void mptcp_pm_nl_rm_addr_received(struct mptcp_sock *msk)
|
||||
|
@ -168,19 +168,19 @@ static void mptcp_data_queue_ofo(struct mptcp_sock *msk, struct sk_buff *skb)
|
||||
struct rb_node **p, *parent;
|
||||
u64 seq, end_seq, max_seq;
|
||||
struct sk_buff *skb1;
|
||||
int space;
|
||||
|
||||
seq = MPTCP_SKB_CB(skb)->map_seq;
|
||||
end_seq = MPTCP_SKB_CB(skb)->end_seq;
|
||||
space = tcp_space(sk);
|
||||
max_seq = space > 0 ? space + msk->ack_seq : msk->ack_seq;
|
||||
max_seq = READ_ONCE(msk->rcv_wnd_sent);
|
||||
|
||||
pr_debug("msk=%p seq=%llx limit=%llx empty=%d", msk, seq, max_seq,
|
||||
RB_EMPTY_ROOT(&msk->out_of_order_queue));
|
||||
if (after64(seq, max_seq)) {
|
||||
if (after64(end_seq, max_seq)) {
|
||||
/* out of window */
|
||||
mptcp_drop(sk, skb);
|
||||
pr_debug("oow by %ld", (unsigned long)seq - (unsigned long)max_seq);
|
||||
pr_debug("oow by %lld, rcv_wnd_sent %llu\n",
|
||||
(unsigned long long)end_seq - (unsigned long)max_seq,
|
||||
(unsigned long long)msk->rcv_wnd_sent);
|
||||
MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_NODSSWINDOW);
|
||||
return;
|
||||
}
|
||||
@ -407,16 +407,42 @@ static void mptcp_set_timeout(const struct sock *sk, const struct sock *ssk)
|
||||
mptcp_sk(sk)->timer_ival = tout > 0 ? tout : TCP_RTO_MIN;
|
||||
}
|
||||
|
||||
static void mptcp_send_ack(struct mptcp_sock *msk)
|
||||
static bool mptcp_subflow_active(struct mptcp_subflow_context *subflow)
|
||||
{
|
||||
struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
|
||||
|
||||
/* can't send if JOIN hasn't completed yet (i.e. is usable for mptcp) */
|
||||
if (subflow->request_join && !subflow->fully_established)
|
||||
return false;
|
||||
|
||||
/* only send if our side has not closed yet */
|
||||
return ((1 << ssk->sk_state) & (TCPF_ESTABLISHED | TCPF_CLOSE_WAIT));
|
||||
}
|
||||
|
||||
static void mptcp_send_ack(struct mptcp_sock *msk, bool force)
|
||||
{
|
||||
struct mptcp_subflow_context *subflow;
|
||||
struct sock *pick = NULL;
|
||||
|
||||
mptcp_for_each_subflow(msk, subflow) {
|
||||
struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
|
||||
|
||||
lock_sock(ssk);
|
||||
tcp_send_ack(ssk);
|
||||
release_sock(ssk);
|
||||
if (force) {
|
||||
lock_sock(ssk);
|
||||
tcp_send_ack(ssk);
|
||||
release_sock(ssk);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* if the hintes ssk is still active, use it */
|
||||
pick = ssk;
|
||||
if (ssk == msk->ack_hint)
|
||||
break;
|
||||
}
|
||||
if (!force && pick) {
|
||||
lock_sock(pick);
|
||||
tcp_cleanup_rbuf(pick, 1);
|
||||
release_sock(pick);
|
||||
}
|
||||
}
|
||||
|
||||
@ -468,7 +494,7 @@ static bool mptcp_check_data_fin(struct sock *sk)
|
||||
|
||||
ret = true;
|
||||
mptcp_set_timeout(sk, NULL);
|
||||
mptcp_send_ack(msk);
|
||||
mptcp_send_ack(msk, true);
|
||||
mptcp_close_wake_up(sk);
|
||||
}
|
||||
return ret;
|
||||
@ -483,7 +509,6 @@ static bool __mptcp_move_skbs_from_subflow(struct mptcp_sock *msk,
|
||||
unsigned int moved = 0;
|
||||
bool more_data_avail;
|
||||
struct tcp_sock *tp;
|
||||
u32 old_copied_seq;
|
||||
bool done = false;
|
||||
int sk_rbuf;
|
||||
|
||||
@ -500,7 +525,6 @@ static bool __mptcp_move_skbs_from_subflow(struct mptcp_sock *msk,
|
||||
|
||||
pr_debug("msk=%p ssk=%p", msk, ssk);
|
||||
tp = tcp_sk(ssk);
|
||||
old_copied_seq = tp->copied_seq;
|
||||
do {
|
||||
u32 map_remaining, offset;
|
||||
u32 seq = tp->copied_seq;
|
||||
@ -564,11 +588,9 @@ static bool __mptcp_move_skbs_from_subflow(struct mptcp_sock *msk,
|
||||
break;
|
||||
}
|
||||
} while (more_data_avail);
|
||||
msk->ack_hint = ssk;
|
||||
|
||||
*bytes += moved;
|
||||
if (tp->copied_seq != old_copied_seq)
|
||||
tcp_cleanup_rbuf(ssk, 1);
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
@ -672,25 +694,14 @@ void mptcp_data_ready(struct sock *sk, struct sock *ssk)
|
||||
if (atomic_read(&sk->sk_rmem_alloc) > sk_rbuf)
|
||||
goto wake;
|
||||
|
||||
if (move_skbs_to_msk(msk, ssk))
|
||||
goto wake;
|
||||
move_skbs_to_msk(msk, ssk);
|
||||
|
||||
/* mptcp socket is owned, release_cb should retry */
|
||||
if (!test_and_set_bit(TCP_DELACK_TIMER_DEFERRED,
|
||||
&sk->sk_tsq_flags)) {
|
||||
sock_hold(sk);
|
||||
|
||||
/* need to try again, its possible release_cb() has already
|
||||
* been called after the test_and_set_bit() above.
|
||||
*/
|
||||
move_skbs_to_msk(msk, ssk);
|
||||
}
|
||||
wake:
|
||||
if (wake)
|
||||
sk->sk_data_ready(sk);
|
||||
}
|
||||
|
||||
static void __mptcp_flush_join_list(struct mptcp_sock *msk)
|
||||
void __mptcp_flush_join_list(struct mptcp_sock *msk)
|
||||
{
|
||||
if (likely(list_empty(&msk->join_list)))
|
||||
return;
|
||||
@ -777,7 +788,9 @@ static void mptcp_check_for_eof(struct mptcp_sock *msk)
|
||||
inet_sk_state_store(sk, TCP_CLOSE_WAIT);
|
||||
break;
|
||||
case TCP_FIN_WAIT1:
|
||||
/* fallback sockets skip TCP_CLOSING - TCP will take care */
|
||||
inet_sk_state_store(sk, TCP_CLOSING);
|
||||
break;
|
||||
case TCP_FIN_WAIT2:
|
||||
inet_sk_state_store(sk, TCP_CLOSE);
|
||||
break;
|
||||
default:
|
||||
@ -1093,18 +1106,6 @@ static void mptcp_nospace(struct mptcp_sock *msk)
|
||||
mptcp_clean_una((struct sock *)msk);
|
||||
}
|
||||
|
||||
static bool mptcp_subflow_active(struct mptcp_subflow_context *subflow)
|
||||
{
|
||||
struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
|
||||
|
||||
/* can't send if JOIN hasn't completed yet (i.e. is usable for mptcp) */
|
||||
if (subflow->request_join && !subflow->fully_established)
|
||||
return false;
|
||||
|
||||
/* only send if our side has not closed yet */
|
||||
return ((1 << ssk->sk_state) & (TCPF_ESTABLISHED | TCPF_CLOSE_WAIT));
|
||||
}
|
||||
|
||||
#define MPTCP_SEND_BURST_SIZE ((1 << 16) - \
|
||||
sizeof(struct tcphdr) - \
|
||||
MAX_TCP_OPTION_SPACE - \
|
||||
@ -1532,7 +1533,7 @@ new_measure:
|
||||
msk->rcvq_space.time = mstamp;
|
||||
}
|
||||
|
||||
static bool __mptcp_move_skbs(struct mptcp_sock *msk)
|
||||
static bool __mptcp_move_skbs(struct mptcp_sock *msk, unsigned int rcv)
|
||||
{
|
||||
unsigned int moved = 0;
|
||||
bool done;
|
||||
@ -1551,12 +1552,16 @@ static bool __mptcp_move_skbs(struct mptcp_sock *msk)
|
||||
|
||||
slowpath = lock_sock_fast(ssk);
|
||||
done = __mptcp_move_skbs_from_subflow(msk, ssk, &moved);
|
||||
if (moved && rcv) {
|
||||
WRITE_ONCE(msk->rmem_pending, min(rcv, moved));
|
||||
tcp_cleanup_rbuf(ssk, 1);
|
||||
WRITE_ONCE(msk->rmem_pending, 0);
|
||||
}
|
||||
unlock_sock_fast(ssk, slowpath);
|
||||
} while (!done);
|
||||
|
||||
if (mptcp_ofo_queue(msk) || moved > 0) {
|
||||
if (!mptcp_check_data_fin((struct sock *)msk))
|
||||
mptcp_send_ack(msk);
|
||||
mptcp_check_data_fin((struct sock *)msk);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -1580,8 +1585,8 @@ static int mptcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
|
||||
target = sock_rcvlowat(sk, flags & MSG_WAITALL, len);
|
||||
__mptcp_flush_join_list(msk);
|
||||
|
||||
while (len > (size_t)copied) {
|
||||
int bytes_read;
|
||||
for (;;) {
|
||||
int bytes_read, old_space;
|
||||
|
||||
bytes_read = __mptcp_recvmsg_mskq(msk, msg, len - copied);
|
||||
if (unlikely(bytes_read < 0)) {
|
||||
@ -1593,9 +1598,14 @@ static int mptcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
|
||||
copied += bytes_read;
|
||||
|
||||
if (skb_queue_empty(&sk->sk_receive_queue) &&
|
||||
__mptcp_move_skbs(msk))
|
||||
__mptcp_move_skbs(msk, len - copied))
|
||||
continue;
|
||||
|
||||
/* be sure to advertise window change */
|
||||
old_space = READ_ONCE(msk->old_wspace);
|
||||
if ((tcp_space(sk) - old_space) >= old_space)
|
||||
mptcp_send_ack(msk, false);
|
||||
|
||||
/* only the master socket status is relevant here. The exit
|
||||
* conditions mirror closely tcp_recvmsg()
|
||||
*/
|
||||
@ -1648,7 +1658,7 @@ static int mptcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
|
||||
/* .. race-breaker: ssk might have gotten new data
|
||||
* after last __mptcp_move_skbs() returned false.
|
||||
*/
|
||||
if (unlikely(__mptcp_move_skbs(msk)))
|
||||
if (unlikely(__mptcp_move_skbs(msk, 0)))
|
||||
set_bit(MPTCP_DATA_READY, &msk->flags);
|
||||
} else if (unlikely(!test_bit(MPTCP_DATA_READY, &msk->flags))) {
|
||||
/* data to read but mptcp_wait_data() cleared DATA_READY */
|
||||
@ -1724,8 +1734,11 @@ static struct sock *mptcp_subflow_get_retrans(const struct mptcp_sock *msk)
|
||||
continue;
|
||||
|
||||
/* still data outstanding at TCP level? Don't retransmit. */
|
||||
if (!tcp_write_queue_empty(ssk))
|
||||
if (!tcp_write_queue_empty(ssk)) {
|
||||
if (inet_csk(ssk)->icsk_ca_state >= TCP_CA_Loss)
|
||||
continue;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (subflow->backup) {
|
||||
if (!backup)
|
||||
@ -1803,6 +1816,10 @@ static void pm_work(struct mptcp_sock *msk)
|
||||
pm->status &= ~BIT(MPTCP_PM_ADD_ADDR_RECEIVED);
|
||||
mptcp_pm_nl_add_addr_received(msk);
|
||||
}
|
||||
if (pm->status & BIT(MPTCP_PM_ADD_ADDR_SEND_ACK)) {
|
||||
pm->status &= ~BIT(MPTCP_PM_ADD_ADDR_SEND_ACK);
|
||||
mptcp_pm_nl_add_addr_send_ack(msk);
|
||||
}
|
||||
if (pm->status & BIT(MPTCP_PM_RM_ADDR_RECEIVED)) {
|
||||
pm->status &= ~BIT(MPTCP_PM_RM_ADDR_RECEIVED);
|
||||
mptcp_pm_nl_rm_addr_received(msk);
|
||||
@ -1862,7 +1879,6 @@ static void mptcp_worker(struct work_struct *work)
|
||||
int state, ret;
|
||||
|
||||
lock_sock(sk);
|
||||
set_bit(MPTCP_WORKER_RUNNING, &msk->flags);
|
||||
state = sk->sk_state;
|
||||
if (unlikely(state == TCP_CLOSE))
|
||||
goto unlock;
|
||||
@ -1873,7 +1889,6 @@ static void mptcp_worker(struct work_struct *work)
|
||||
if (test_and_clear_bit(MPTCP_WORK_CLOSE_SUBFLOW, &msk->flags))
|
||||
__mptcp_close_subflow(msk);
|
||||
|
||||
__mptcp_move_skbs(msk);
|
||||
if (mptcp_send_head(sk))
|
||||
mptcp_push_pending(sk, 0);
|
||||
|
||||
@ -1940,7 +1955,6 @@ reset_unlock:
|
||||
mptcp_reset_timer(sk);
|
||||
|
||||
unlock:
|
||||
clear_bit(MPTCP_WORKER_RUNNING, &msk->flags);
|
||||
release_sock(sk);
|
||||
sock_put(sk);
|
||||
}
|
||||
@ -1958,6 +1972,7 @@ static int __mptcp_init_sock(struct sock *sk)
|
||||
msk->out_of_order_queue = RB_ROOT;
|
||||
msk->first_pending = NULL;
|
||||
|
||||
msk->ack_hint = NULL;
|
||||
msk->first = NULL;
|
||||
inet_csk(sk)->icsk_sync_mss = mptcp_sync_mss;
|
||||
|
||||
@ -2011,11 +2026,7 @@ static void mptcp_cancel_work(struct sock *sk)
|
||||
{
|
||||
struct mptcp_sock *msk = mptcp_sk(sk);
|
||||
|
||||
/* if called by the work itself, do not try to cancel the work, or
|
||||
* we will hang.
|
||||
*/
|
||||
if (!test_bit(MPTCP_WORKER_RUNNING, &msk->flags) &&
|
||||
cancel_work_sync(&msk->work))
|
||||
if (cancel_work_sync(&msk->work))
|
||||
__sock_put(sk);
|
||||
}
|
||||
|
||||
@ -2092,10 +2103,16 @@ static void __mptcp_check_send_data_fin(struct sock *sk)
|
||||
|
||||
WRITE_ONCE(msk->snd_nxt, msk->write_seq);
|
||||
|
||||
/* fallback socket will not get data_fin/ack, can move to close now */
|
||||
if (__mptcp_check_fallback(msk) && sk->sk_state == TCP_LAST_ACK) {
|
||||
inet_sk_state_store(sk, TCP_CLOSE);
|
||||
mptcp_close_wake_up(sk);
|
||||
/* fallback socket will not get data_fin/ack, can move to the next
|
||||
* state now
|
||||
*/
|
||||
if (__mptcp_check_fallback(msk)) {
|
||||
if ((1 << sk->sk_state) & (TCPF_CLOSING | TCPF_LAST_ACK)) {
|
||||
inet_sk_state_store(sk, TCP_CLOSE);
|
||||
mptcp_close_wake_up(sk);
|
||||
} else if (sk->sk_state == TCP_FIN_WAIT1) {
|
||||
inet_sk_state_store(sk, TCP_FIN_WAIT2);
|
||||
}
|
||||
}
|
||||
|
||||
__mptcp_flush_join_list(msk);
|
||||
@ -2286,6 +2303,7 @@ struct sock *mptcp_sk_clone(const struct sock *sk,
|
||||
mptcp_crypto_key_sha(msk->remote_key, NULL, &ack_seq);
|
||||
ack_seq++;
|
||||
WRITE_ONCE(msk->ack_seq, ack_seq);
|
||||
WRITE_ONCE(msk->rcv_wnd_sent, ack_seq);
|
||||
}
|
||||
|
||||
sock_reset_flag(nsk, SOCK_RCU_FREE);
|
||||
@ -2338,7 +2356,6 @@ static struct sock *mptcp_accept(struct sock *sk, int flags, int *err,
|
||||
if (sk_is_mptcp(newsk)) {
|
||||
struct mptcp_subflow_context *subflow;
|
||||
struct sock *new_mptcp_sock;
|
||||
struct sock *ssk = newsk;
|
||||
|
||||
subflow = mptcp_subflow_ctx(newsk);
|
||||
new_mptcp_sock = subflow->conn;
|
||||
@ -2353,22 +2370,8 @@ static struct sock *mptcp_accept(struct sock *sk, int flags, int *err,
|
||||
|
||||
/* acquire the 2nd reference for the owning socket */
|
||||
sock_hold(new_mptcp_sock);
|
||||
|
||||
local_bh_disable();
|
||||
bh_lock_sock(new_mptcp_sock);
|
||||
msk = mptcp_sk(new_mptcp_sock);
|
||||
msk->first = newsk;
|
||||
|
||||
newsk = new_mptcp_sock;
|
||||
mptcp_copy_inaddrs(newsk, ssk);
|
||||
list_add(&subflow->node, &msk->conn_list);
|
||||
sock_hold(ssk);
|
||||
|
||||
mptcp_rcv_space_init(msk, ssk);
|
||||
bh_unlock_sock(new_mptcp_sock);
|
||||
|
||||
__MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_MPCAPABLEPASSIVEACK);
|
||||
local_bh_enable();
|
||||
MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_MPCAPABLEPASSIVEACK);
|
||||
} else {
|
||||
MPTCP_INC_STATS(sock_net(sk),
|
||||
MPTCP_MIB_MPCAPABLEPASSIVEFALLBACK);
|
||||
@ -2505,8 +2508,7 @@ static int mptcp_getsockopt(struct sock *sk, int level, int optname,
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
#define MPTCP_DEFERRED_ALL (TCPF_DELACK_TIMER_DEFERRED | \
|
||||
TCPF_WRITE_TIMER_DEFERRED)
|
||||
#define MPTCP_DEFERRED_ALL (TCPF_WRITE_TIMER_DEFERRED)
|
||||
|
||||
/* this is very alike tcp_release_cb() but we must handle differently a
|
||||
* different set of events
|
||||
@ -2524,16 +2526,6 @@ static void mptcp_release_cb(struct sock *sk)
|
||||
|
||||
sock_release_ownership(sk);
|
||||
|
||||
if (flags & TCPF_DELACK_TIMER_DEFERRED) {
|
||||
struct mptcp_sock *msk = mptcp_sk(sk);
|
||||
struct sock *ssk;
|
||||
|
||||
ssk = mptcp_subflow_recv_lookup(msk);
|
||||
if (!ssk || sk->sk_state == TCP_CLOSE ||
|
||||
!schedule_work(&msk->work))
|
||||
__sock_put(sk);
|
||||
}
|
||||
|
||||
if (flags & TCPF_WRITE_TIMER_DEFERRED) {
|
||||
mptcp_retransmit_handler(sk);
|
||||
__sock_put(sk);
|
||||
@ -2593,6 +2585,7 @@ void mptcp_finish_connect(struct sock *ssk)
|
||||
WRITE_ONCE(msk->write_seq, subflow->idsn + 1);
|
||||
WRITE_ONCE(msk->snd_nxt, msk->write_seq);
|
||||
WRITE_ONCE(msk->ack_seq, ack_seq);
|
||||
WRITE_ONCE(msk->rcv_wnd_sent, ack_seq);
|
||||
WRITE_ONCE(msk->can_ack, 1);
|
||||
atomic64_set(&msk->snd_una, msk->write_seq);
|
||||
|
||||
@ -2819,6 +2812,12 @@ static int mptcp_stream_accept(struct socket *sock, struct socket *newsock,
|
||||
if (err == 0 && !mptcp_is_tcpsk(newsock->sk)) {
|
||||
struct mptcp_sock *msk = mptcp_sk(newsock->sk);
|
||||
struct mptcp_subflow_context *subflow;
|
||||
struct sock *newsk = newsock->sk;
|
||||
bool slowpath;
|
||||
|
||||
slowpath = lock_sock_fast(newsk);
|
||||
mptcp_copy_inaddrs(newsk, msk->first);
|
||||
mptcp_rcv_space_init(msk, msk->first);
|
||||
|
||||
/* set ssk->sk_socket of accept()ed flows to mptcp socket.
|
||||
* This is needed so NOSPACE flag can be set from tcp stack.
|
||||
@ -2830,6 +2829,7 @@ static int mptcp_stream_accept(struct socket *sock, struct socket *newsock,
|
||||
if (!ssk->sk_socket)
|
||||
mptcp_sock_graft(ssk, newsock);
|
||||
}
|
||||
unlock_sock_fast(newsk, slowpath);
|
||||
}
|
||||
|
||||
if (inet_csk_listen_poll(ssock->sk))
|
||||
|
@ -91,7 +91,6 @@
|
||||
#define MPTCP_WORK_EOF 3
|
||||
#define MPTCP_FALLBACK_DONE 4
|
||||
#define MPTCP_WORK_CLOSE_SUBFLOW 5
|
||||
#define MPTCP_WORKER_RUNNING 6
|
||||
|
||||
static inline bool before64(__u64 seq1, __u64 seq2)
|
||||
{
|
||||
@ -161,11 +160,18 @@ struct mptcp_addr_info {
|
||||
|
||||
enum mptcp_pm_status {
|
||||
MPTCP_PM_ADD_ADDR_RECEIVED,
|
||||
MPTCP_PM_ADD_ADDR_SEND_ACK,
|
||||
MPTCP_PM_RM_ADDR_RECEIVED,
|
||||
MPTCP_PM_ESTABLISHED,
|
||||
MPTCP_PM_SUBFLOW_ESTABLISHED,
|
||||
};
|
||||
|
||||
enum mptcp_add_addr_status {
|
||||
MPTCP_ADD_ADDR_SIGNAL,
|
||||
MPTCP_ADD_ADDR_ECHO,
|
||||
MPTCP_ADD_ADDR_IPV6,
|
||||
};
|
||||
|
||||
struct mptcp_pm_data {
|
||||
struct mptcp_addr_info local;
|
||||
struct mptcp_addr_info remote;
|
||||
@ -173,13 +179,12 @@ struct mptcp_pm_data {
|
||||
|
||||
spinlock_t lock; /*protects the whole PM data */
|
||||
|
||||
bool add_addr_signal;
|
||||
u8 add_addr_signal;
|
||||
bool rm_addr_signal;
|
||||
bool server_side;
|
||||
bool work_pending;
|
||||
bool accept_addr;
|
||||
bool accept_subflow;
|
||||
bool add_addr_echo;
|
||||
u8 add_addr_signaled;
|
||||
u8 add_addr_accepted;
|
||||
u8 local_addr_used;
|
||||
@ -211,13 +216,16 @@ struct mptcp_sock {
|
||||
u64 write_seq;
|
||||
u64 snd_nxt;
|
||||
u64 ack_seq;
|
||||
u64 rcv_wnd_sent;
|
||||
u64 rcv_data_fin_seq;
|
||||
struct sock *last_snd;
|
||||
int snd_burst;
|
||||
int old_wspace;
|
||||
atomic64_t snd_una;
|
||||
atomic64_t wnd_end;
|
||||
unsigned long timer_ival;
|
||||
u32 token;
|
||||
int rmem_pending;
|
||||
unsigned long flags;
|
||||
bool can_ack;
|
||||
bool fully_established;
|
||||
@ -225,6 +233,7 @@ struct mptcp_sock {
|
||||
bool snd_data_fin_enable;
|
||||
bool use_64bit_ack; /* Set when we received a 64-bit DSN */
|
||||
spinlock_t join_list_lock;
|
||||
struct sock *ack_hint;
|
||||
struct work_struct work;
|
||||
struct sk_buff *ooo_last_skb;
|
||||
struct rb_root out_of_order_queue;
|
||||
@ -252,6 +261,11 @@ static inline struct mptcp_sock *mptcp_sk(const struct sock *sk)
|
||||
return (struct mptcp_sock *)sk;
|
||||
}
|
||||
|
||||
static inline int __mptcp_space(const struct sock *sk)
|
||||
{
|
||||
return tcp_space(sk) + READ_ONCE(mptcp_sk(sk)->rmem_pending);
|
||||
}
|
||||
|
||||
static inline struct mptcp_data_frag *mptcp_send_head(const struct sock *sk)
|
||||
{
|
||||
const struct mptcp_sock *msk = mptcp_sk(sk);
|
||||
@ -404,6 +418,15 @@ mptcp_subflow_get_mapped_dsn(const struct mptcp_subflow_context *subflow)
|
||||
return subflow->map_seq + mptcp_subflow_get_map_offset(subflow);
|
||||
}
|
||||
|
||||
static inline void mptcp_add_pending_subflow(struct mptcp_sock *msk,
|
||||
struct mptcp_subflow_context *subflow)
|
||||
{
|
||||
sock_hold(mptcp_subflow_tcp_sock(subflow));
|
||||
spin_lock_bh(&msk->join_list_lock);
|
||||
list_add_tail(&subflow->node, &msk->join_list);
|
||||
spin_unlock_bh(&msk->join_list_lock);
|
||||
}
|
||||
|
||||
int mptcp_is_enabled(struct net *net);
|
||||
unsigned int mptcp_get_add_addr_timeout(struct net *net);
|
||||
void mptcp_subflow_fully_established(struct mptcp_subflow_context *subflow,
|
||||
@ -454,6 +477,7 @@ bool mptcp_schedule_work(struct sock *sk);
|
||||
void mptcp_data_acked(struct sock *sk);
|
||||
void mptcp_subflow_eof(struct sock *sk);
|
||||
bool mptcp_update_rcv_data_fin(struct mptcp_sock *msk, u64 data_fin_seq, bool use_64bit);
|
||||
void __mptcp_flush_join_list(struct mptcp_sock *msk);
|
||||
static inline bool mptcp_data_fin_enabled(const struct mptcp_sock *msk)
|
||||
{
|
||||
return READ_ONCE(msk->snd_data_fin_enable) &&
|
||||
@ -494,6 +518,7 @@ void mptcp_pm_subflow_established(struct mptcp_sock *msk,
|
||||
void mptcp_pm_subflow_closed(struct mptcp_sock *msk, u8 id);
|
||||
void mptcp_pm_add_addr_received(struct mptcp_sock *msk,
|
||||
const struct mptcp_addr_info *addr);
|
||||
void mptcp_pm_add_addr_send_ack(struct mptcp_sock *msk);
|
||||
void mptcp_pm_rm_addr_received(struct mptcp_sock *msk, u8 rm_id);
|
||||
void mptcp_pm_free_anno_list(struct mptcp_sock *msk);
|
||||
struct mptcp_pm_add_entry *
|
||||
@ -508,7 +533,17 @@ int mptcp_pm_remove_subflow(struct mptcp_sock *msk, u8 local_id);
|
||||
|
||||
static inline bool mptcp_pm_should_add_signal(struct mptcp_sock *msk)
|
||||
{
|
||||
return READ_ONCE(msk->pm.add_addr_signal);
|
||||
return READ_ONCE(msk->pm.add_addr_signal) & BIT(MPTCP_ADD_ADDR_SIGNAL);
|
||||
}
|
||||
|
||||
static inline bool mptcp_pm_should_add_signal_echo(struct mptcp_sock *msk)
|
||||
{
|
||||
return READ_ONCE(msk->pm.add_addr_signal) & BIT(MPTCP_ADD_ADDR_ECHO);
|
||||
}
|
||||
|
||||
static inline bool mptcp_pm_should_add_signal_ipv6(struct mptcp_sock *msk)
|
||||
{
|
||||
return READ_ONCE(msk->pm.add_addr_signal) & BIT(MPTCP_ADD_ADDR_IPV6);
|
||||
}
|
||||
|
||||
static inline bool mptcp_pm_should_rm_signal(struct mptcp_sock *msk)
|
||||
@ -535,6 +570,7 @@ void mptcp_pm_nl_data_init(struct mptcp_sock *msk);
|
||||
void mptcp_pm_nl_fully_established(struct mptcp_sock *msk);
|
||||
void mptcp_pm_nl_subflow_established(struct mptcp_sock *msk);
|
||||
void mptcp_pm_nl_add_addr_received(struct mptcp_sock *msk);
|
||||
void mptcp_pm_nl_add_addr_send_ack(struct mptcp_sock *msk);
|
||||
void mptcp_pm_nl_rm_addr_received(struct mptcp_sock *msk);
|
||||
void mptcp_pm_nl_rm_subflow_received(struct mptcp_sock *msk, u8 rm_id);
|
||||
int mptcp_pm_nl_get_local_id(struct mptcp_sock *msk, struct sock_common *skc);
|
||||
|
@ -578,6 +578,10 @@ create_child:
|
||||
*/
|
||||
inet_sk_state_store((void *)new_msk, TCP_ESTABLISHED);
|
||||
|
||||
/* link the newly created socket to the msk */
|
||||
mptcp_add_pending_subflow(mptcp_sk(new_msk), ctx);
|
||||
WRITE_ONCE(mptcp_sk(new_msk)->first, child);
|
||||
|
||||
/* new mpc subflow takes ownership of the newly
|
||||
* created mptcp socket
|
||||
*/
|
||||
@ -846,8 +850,6 @@ static void mptcp_subflow_discard_data(struct sock *ssk, struct sk_buff *skb,
|
||||
sk_eat_skb(ssk, skb);
|
||||
if (mptcp_subflow_get_map_offset(subflow) >= subflow->map_data_len)
|
||||
subflow->map_valid = 0;
|
||||
if (incr)
|
||||
tcp_cleanup_rbuf(ssk, incr);
|
||||
}
|
||||
|
||||
static bool subflow_check_data_avail(struct sock *ssk)
|
||||
@ -969,7 +971,7 @@ void mptcp_space(const struct sock *ssk, int *space, int *full_space)
|
||||
const struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(ssk);
|
||||
const struct sock *sk = subflow->conn;
|
||||
|
||||
*space = tcp_space(sk);
|
||||
*space = __mptcp_space(sk);
|
||||
*full_space = tcp_full_space(sk);
|
||||
}
|
||||
|
||||
@ -1124,11 +1126,7 @@ int __mptcp_subflow_connect(struct sock *sk, const struct mptcp_addr_info *loc,
|
||||
if (err && err != -EINPROGRESS)
|
||||
goto failed;
|
||||
|
||||
sock_hold(ssk);
|
||||
spin_lock_bh(&msk->join_list_lock);
|
||||
list_add_tail(&subflow->node, &msk->join_list);
|
||||
spin_unlock_bh(&msk->join_list_lock);
|
||||
|
||||
mptcp_add_pending_subflow(msk, subflow);
|
||||
return err;
|
||||
|
||||
failed:
|
||||
|
@ -5,6 +5,7 @@ ret=0
|
||||
sin=""
|
||||
sout=""
|
||||
cin=""
|
||||
cinsent=""
|
||||
cout=""
|
||||
ksft_skip=4
|
||||
timeout=30
|
||||
@ -81,7 +82,7 @@ cleanup_partial()
|
||||
cleanup()
|
||||
{
|
||||
rm -f "$cin" "$cout"
|
||||
rm -f "$sin" "$sout"
|
||||
rm -f "$sin" "$sout" "$cinsent"
|
||||
cleanup_partial
|
||||
}
|
||||
|
||||
@ -144,6 +145,13 @@ if [ $? -ne 0 ];then
|
||||
exit $ksft_skip
|
||||
fi
|
||||
|
||||
print_file_err()
|
||||
{
|
||||
ls -l "$1" 1>&2
|
||||
echo "Trailing bytes are: "
|
||||
tail -c 27 "$1"
|
||||
}
|
||||
|
||||
check_transfer()
|
||||
{
|
||||
in=$1
|
||||
@ -155,6 +163,7 @@ check_transfer()
|
||||
echo "[ FAIL ] $what does not match (in, out):"
|
||||
print_file_err "$in"
|
||||
print_file_err "$out"
|
||||
ret=1
|
||||
|
||||
return 1
|
||||
fi
|
||||
@ -175,6 +184,23 @@ do_ping()
|
||||
fi
|
||||
}
|
||||
|
||||
link_failure()
|
||||
{
|
||||
ns="$1"
|
||||
|
||||
l=$((RANDOM%4))
|
||||
l=$((l+1))
|
||||
|
||||
veth="ns1eth$l"
|
||||
ip -net "$ns" link set "$veth" down
|
||||
}
|
||||
|
||||
# $1: IP address
|
||||
is_v6()
|
||||
{
|
||||
[ -z "${1##*:*}" ]
|
||||
}
|
||||
|
||||
do_transfer()
|
||||
{
|
||||
listener_ns="$1"
|
||||
@ -182,9 +208,10 @@ do_transfer()
|
||||
cl_proto="$3"
|
||||
srv_proto="$4"
|
||||
connect_addr="$5"
|
||||
rm_nr_ns1="$6"
|
||||
rm_nr_ns2="$7"
|
||||
speed="$8"
|
||||
test_link_fail="$6"
|
||||
rm_nr_ns1="$7"
|
||||
rm_nr_ns2="$8"
|
||||
speed="$9"
|
||||
|
||||
port=$((10000+$TEST_COUNT))
|
||||
TEST_COUNT=$((TEST_COUNT+1))
|
||||
@ -215,12 +242,25 @@ do_transfer()
|
||||
mptcp_connect="./mptcp_connect -r"
|
||||
fi
|
||||
|
||||
ip netns exec ${listener_ns} $mptcp_connect -t $timeout -l -p $port -s ${srv_proto} 0.0.0.0 < "$sin" > "$sout" &
|
||||
local local_addr
|
||||
if is_v6 "${connect_addr}"; then
|
||||
local_addr="::"
|
||||
else
|
||||
local_addr="0.0.0.0"
|
||||
fi
|
||||
|
||||
ip netns exec ${listener_ns} $mptcp_connect -t $timeout -l -p $port \
|
||||
-s ${srv_proto} ${local_addr} < "$sin" > "$sout" &
|
||||
spid=$!
|
||||
|
||||
sleep 1
|
||||
|
||||
ip netns exec ${connector_ns} $mptcp_connect -t $timeout -p $port -s ${cl_proto} $connect_addr < "$cin" > "$cout" &
|
||||
if [ "$test_link_fail" -eq 0 ];then
|
||||
ip netns exec ${connector_ns} $mptcp_connect -t $timeout -p $port -s ${cl_proto} $connect_addr < "$cin" > "$cout" &
|
||||
else
|
||||
( cat "$cin" ; sleep 2; link_failure $listener_ns ; cat "$cin" ) | tee "$cinsent" | \
|
||||
ip netns exec ${connector_ns} $mptcp_connect -t $timeout -p $port -s ${cl_proto} $connect_addr > "$cout" &
|
||||
fi
|
||||
cpid=$!
|
||||
|
||||
if [ $rm_nr_ns1 -gt 0 ]; then
|
||||
@ -265,12 +305,17 @@ do_transfer()
|
||||
ip netns exec ${connector_ns} ss -nita 1>&2 -o "dport = :$port"
|
||||
|
||||
cat "$capout"
|
||||
ret=1
|
||||
return 1
|
||||
fi
|
||||
|
||||
check_transfer $sin $cout "file received by client"
|
||||
retc=$?
|
||||
check_transfer $cin $sout "file received by server"
|
||||
if [ "$test_link_fail" -eq 0 ];then
|
||||
check_transfer $cin $sout "file received by server"
|
||||
else
|
||||
check_transfer $cinsent $sout "file received by server"
|
||||
fi
|
||||
rets=$?
|
||||
|
||||
if [ $retc -eq 0 ] && [ $rets -eq 0 ];then
|
||||
@ -286,13 +331,12 @@ make_file()
|
||||
{
|
||||
name=$1
|
||||
who=$2
|
||||
size=$3
|
||||
|
||||
SIZE=1
|
||||
|
||||
dd if=/dev/urandom of="$name" bs=1024 count=$SIZE 2> /dev/null
|
||||
dd if=/dev/urandom of="$name" bs=1024 count=$size 2> /dev/null
|
||||
echo -e "\nMPTCP_TEST_FILE_END_MARKER" >> "$name"
|
||||
|
||||
echo "Created $name (size $SIZE KB) containing data sent by $who"
|
||||
echo "Created $name (size $size KB) containing data sent by $who"
|
||||
}
|
||||
|
||||
run_tests()
|
||||
@ -300,14 +344,32 @@ run_tests()
|
||||
listener_ns="$1"
|
||||
connector_ns="$2"
|
||||
connect_addr="$3"
|
||||
rm_nr_ns1="${4:-0}"
|
||||
rm_nr_ns2="${5:-0}"
|
||||
speed="${6:-fast}"
|
||||
test_linkfail="${4:-0}"
|
||||
rm_nr_ns1="${5:-0}"
|
||||
rm_nr_ns2="${6:-0}"
|
||||
speed="${7:-fast}"
|
||||
lret=0
|
||||
oldin=""
|
||||
|
||||
if [ "$test_linkfail" -eq 1 ];then
|
||||
size=$((RANDOM%1024))
|
||||
size=$((size+1))
|
||||
size=$((size*128))
|
||||
|
||||
oldin=$(mktemp)
|
||||
cp "$cin" "$oldin"
|
||||
make_file "$cin" "client" $size
|
||||
fi
|
||||
|
||||
do_transfer ${listener_ns} ${connector_ns} MPTCP MPTCP ${connect_addr} \
|
||||
${rm_nr_ns1} ${rm_nr_ns2} ${speed}
|
||||
${test_linkfail} ${rm_nr_ns1} ${rm_nr_ns2} ${speed}
|
||||
lret=$?
|
||||
|
||||
if [ "$test_linkfail" -eq 1 ];then
|
||||
cp "$oldin" "$cin"
|
||||
rm -f "$oldin"
|
||||
fi
|
||||
|
||||
if [ $lret -ne 0 ]; then
|
||||
ret=$lret
|
||||
return
|
||||
@ -440,10 +502,11 @@ chk_rm_nr()
|
||||
sin=$(mktemp)
|
||||
sout=$(mktemp)
|
||||
cin=$(mktemp)
|
||||
cinsent=$(mktemp)
|
||||
cout=$(mktemp)
|
||||
init
|
||||
make_file "$cin" "client"
|
||||
make_file "$sin" "server"
|
||||
make_file "$cin" "client" 1
|
||||
make_file "$sin" "server" 1
|
||||
trap cleanup EXIT
|
||||
|
||||
run_tests $ns1 $ns2 10.0.1.1
|
||||
@ -528,12 +591,23 @@ run_tests $ns1 $ns2 10.0.1.1
|
||||
chk_join_nr "multiple subflows and signal" 3 3 3
|
||||
chk_add_nr 1 1
|
||||
|
||||
# accept and use add_addr with additional subflows and link loss
|
||||
reset
|
||||
ip netns exec $ns1 ./pm_nl_ctl limits 0 3
|
||||
ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 flags signal
|
||||
ip netns exec $ns2 ./pm_nl_ctl limits 1 3
|
||||
ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow
|
||||
ip netns exec $ns2 ./pm_nl_ctl add 10.0.4.2 flags subflow
|
||||
run_tests $ns1 $ns2 10.0.1.1 1
|
||||
chk_join_nr "multiple flows, signal, link failure" 3 3 3
|
||||
chk_add_nr 1 1
|
||||
|
||||
# add_addr timeout
|
||||
reset_with_add_addr_timeout
|
||||
ip netns exec $ns1 ./pm_nl_ctl limits 0 1
|
||||
ip netns exec $ns2 ./pm_nl_ctl limits 1 1
|
||||
ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 flags signal
|
||||
run_tests $ns1 $ns2 10.0.1.1 0 0 slow
|
||||
run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow
|
||||
chk_join_nr "signal address, ADD_ADDR timeout" 1 1 1
|
||||
chk_add_nr 4 0
|
||||
|
||||
@ -542,7 +616,7 @@ reset
|
||||
ip netns exec $ns1 ./pm_nl_ctl limits 0 1
|
||||
ip netns exec $ns2 ./pm_nl_ctl limits 0 1
|
||||
ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow
|
||||
run_tests $ns1 $ns2 10.0.1.1 0 1 slow
|
||||
run_tests $ns1 $ns2 10.0.1.1 0 0 1 slow
|
||||
chk_join_nr "remove single subflow" 1 1 1
|
||||
chk_rm_nr 1 1
|
||||
|
||||
@ -552,7 +626,7 @@ ip netns exec $ns1 ./pm_nl_ctl limits 0 2
|
||||
ip netns exec $ns2 ./pm_nl_ctl limits 0 2
|
||||
ip netns exec $ns2 ./pm_nl_ctl add 10.0.2.2 flags subflow
|
||||
ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow
|
||||
run_tests $ns1 $ns2 10.0.1.1 0 2 slow
|
||||
run_tests $ns1 $ns2 10.0.1.1 0 0 2 slow
|
||||
chk_join_nr "remove multiple subflows" 2 2 2
|
||||
chk_rm_nr 2 2
|
||||
|
||||
@ -561,7 +635,7 @@ reset
|
||||
ip netns exec $ns1 ./pm_nl_ctl limits 0 1
|
||||
ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 flags signal
|
||||
ip netns exec $ns2 ./pm_nl_ctl limits 1 1
|
||||
run_tests $ns1 $ns2 10.0.1.1 1 0 slow
|
||||
run_tests $ns1 $ns2 10.0.1.1 0 1 0 slow
|
||||
chk_join_nr "remove single address" 1 1 1
|
||||
chk_add_nr 1 1
|
||||
chk_rm_nr 0 0
|
||||
@ -572,7 +646,7 @@ ip netns exec $ns1 ./pm_nl_ctl limits 0 2
|
||||
ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 flags signal
|
||||
ip netns exec $ns2 ./pm_nl_ctl limits 1 2
|
||||
ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow
|
||||
run_tests $ns1 $ns2 10.0.1.1 1 1 slow
|
||||
run_tests $ns1 $ns2 10.0.1.1 0 1 1 slow
|
||||
chk_join_nr "remove subflow and signal" 2 2 2
|
||||
chk_add_nr 1 1
|
||||
chk_rm_nr 1 1
|
||||
@ -584,11 +658,65 @@ ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 flags signal
|
||||
ip netns exec $ns2 ./pm_nl_ctl limits 1 3
|
||||
ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow
|
||||
ip netns exec $ns2 ./pm_nl_ctl add 10.0.4.2 flags subflow
|
||||
run_tests $ns1 $ns2 10.0.1.1 1 2 slow
|
||||
run_tests $ns1 $ns2 10.0.1.1 0 1 2 slow
|
||||
chk_join_nr "remove subflows and signal" 3 3 3
|
||||
chk_add_nr 1 1
|
||||
chk_rm_nr 2 2
|
||||
|
||||
# subflow IPv6
|
||||
reset
|
||||
ip netns exec $ns1 ./pm_nl_ctl limits 0 1
|
||||
ip netns exec $ns2 ./pm_nl_ctl limits 0 1
|
||||
ip netns exec $ns2 ./pm_nl_ctl add dead:beef:3::2 flags subflow
|
||||
run_tests $ns1 $ns2 dead:beef:1::1 0 0 0 slow
|
||||
chk_join_nr "single subflow IPv6" 1 1 1
|
||||
|
||||
# add_address, unused IPv6
|
||||
reset
|
||||
ip netns exec $ns1 ./pm_nl_ctl add dead:beef:2::1 flags signal
|
||||
run_tests $ns1 $ns2 dead:beef:1::1 0 0 0 slow
|
||||
chk_join_nr "unused signal address IPv6" 0 0 0
|
||||
chk_add_nr 1 1
|
||||
|
||||
# signal address IPv6
|
||||
reset
|
||||
ip netns exec $ns1 ./pm_nl_ctl limits 0 1
|
||||
ip netns exec $ns1 ./pm_nl_ctl add dead:beef:2::1 flags signal
|
||||
ip netns exec $ns2 ./pm_nl_ctl limits 1 1
|
||||
run_tests $ns1 $ns2 dead:beef:1::1 0 0 0 slow
|
||||
chk_join_nr "single address IPv6" 1 1 1
|
||||
chk_add_nr 1 1
|
||||
|
||||
# add_addr timeout IPv6
|
||||
reset_with_add_addr_timeout 6
|
||||
ip netns exec $ns1 ./pm_nl_ctl limits 0 1
|
||||
ip netns exec $ns2 ./pm_nl_ctl limits 1 1
|
||||
ip netns exec $ns1 ./pm_nl_ctl add dead:beef:2::1 flags signal
|
||||
run_tests $ns1 $ns2 dead:beef:1::1 0 0 0 slow
|
||||
chk_join_nr "signal address, ADD_ADDR6 timeout" 1 1 1
|
||||
chk_add_nr 4 0
|
||||
|
||||
# single address IPv6, remove
|
||||
reset
|
||||
ip netns exec $ns1 ./pm_nl_ctl limits 0 1
|
||||
ip netns exec $ns1 ./pm_nl_ctl add dead:beef:2::1 flags signal
|
||||
ip netns exec $ns2 ./pm_nl_ctl limits 1 1
|
||||
run_tests $ns1 $ns2 dead:beef:1::1 0 1 0 slow
|
||||
chk_join_nr "remove single address IPv6" 1 1 1
|
||||
chk_add_nr 1 1
|
||||
chk_rm_nr 0 0
|
||||
|
||||
# subflow and signal IPv6, remove
|
||||
reset
|
||||
ip netns exec $ns1 ./pm_nl_ctl limits 0 2
|
||||
ip netns exec $ns1 ./pm_nl_ctl add dead:beef:2::1 flags signal
|
||||
ip netns exec $ns2 ./pm_nl_ctl limits 1 2
|
||||
ip netns exec $ns2 ./pm_nl_ctl add dead:beef:3::2 flags subflow
|
||||
run_tests $ns1 $ns2 dead:beef:1::1 0 1 1 slow
|
||||
chk_join_nr "remove subflow and signal IPv6" 2 2 2
|
||||
chk_add_nr 1 1
|
||||
chk_rm_nr 1 1
|
||||
|
||||
# single subflow, syncookies
|
||||
reset_with_cookies
|
||||
ip netns exec $ns1 ./pm_nl_ctl limits 0 1
|
||||
|
Loading…
x
Reference in New Issue
Block a user