Skip to content

Commit

Permalink
Merge branch 'mptcp-Exchange-MPTCP-DATA_FIN-DATA_ACK-before-TCP-FIN'
Browse files Browse the repository at this point in the history
Mat Martineau says:

====================
mptcp: Exchange MPTCP DATA_FIN/DATA_ACK before TCP FIN

This series allows the MPTCP-level connection to be closed with the
peers exchanging DATA_FIN and DATA_ACK according to the state machine in
appendix D of RFC 8684. The process is very similar to the TCP
disconnect state machine.

The prior code sends DATA_FIN only when TCP FIN packets are sent, and
does not allow for the MPTCP-level connection to be half-closed.

Patch 8 ("mptcp: Use full MPTCP-level disconnect state machine") is the
core of the series. Earlier patches in the series have some small fixes
and helpers in preparation, and the final four small patches do some
cleanup.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Jul 29, 2020
2 parents 0003041 + 721e908 commit 323410e
Show file tree
Hide file tree
Showing 4 changed files with 306 additions and 66 deletions.
57 changes: 43 additions & 14 deletions net/mptcp/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -451,17 +451,22 @@ static bool mptcp_established_options_mp(struct sock *sk, struct sk_buff *skb,
static void mptcp_write_data_fin(struct mptcp_subflow_context *subflow,
struct sk_buff *skb, struct mptcp_ext *ext)
{
u64 data_fin_tx_seq = READ_ONCE(mptcp_sk(subflow->conn)->write_seq);

if (!ext->use_map || !skb->len) {
/* RFC6824 requires a DSS mapping with specific values
* if DATA_FIN is set but no data payload is mapped
*/
ext->data_fin = 1;
ext->use_map = 1;
ext->dsn64 = 1;
ext->data_seq = subflow->data_fin_tx_seq;
/* The write_seq value has already been incremented, so
* the actual sequence number for the DATA_FIN is one less.
*/
ext->data_seq = data_fin_tx_seq - 1;
ext->subflow_seq = 0;
ext->data_len = 1;
} else if (ext->data_seq + ext->data_len == subflow->data_fin_tx_seq) {
} else if (ext->data_seq + ext->data_len == data_fin_tx_seq) {
/* If there's an existing DSS mapping and it is the
* final mapping, DATA_FIN consumes 1 additional byte of
* mapping space.
Expand All @@ -477,22 +482,17 @@ static bool mptcp_established_options_dss(struct sock *sk, struct sk_buff *skb,
struct mptcp_out_options *opts)
{
struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(sk);
struct mptcp_sock *msk = mptcp_sk(subflow->conn);
unsigned int dss_size = 0;
u64 snd_data_fin_enable;
struct mptcp_ext *mpext;
struct mptcp_sock *msk;
unsigned int ack_size;
bool ret = false;
u8 tcp_fin;

if (skb) {
mpext = mptcp_get_ext(skb);
tcp_fin = TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN;
} else {
mpext = NULL;
tcp_fin = 0;
}
mpext = skb ? mptcp_get_ext(skb) : NULL;
snd_data_fin_enable = READ_ONCE(msk->snd_data_fin_enable);

if (!skb || (mpext && mpext->use_map) || tcp_fin) {
if (!skb || (mpext && mpext->use_map) || snd_data_fin_enable) {
unsigned int map_size;

map_size = TCPOLEN_MPTCP_DSS_BASE + TCPOLEN_MPTCP_DSS_MAP64;
Expand All @@ -502,7 +502,7 @@ static bool mptcp_established_options_dss(struct sock *sk, struct sk_buff *skb,
if (mpext)
opts->ext_copy = *mpext;

if (skb && tcp_fin && subflow->data_fin_tx_enable)
if (skb && snd_data_fin_enable)
mptcp_write_data_fin(subflow, skb, &opts->ext_copy);
ret = true;
}
Expand All @@ -511,7 +511,6 @@ static bool mptcp_established_options_dss(struct sock *sk, struct sk_buff *skb,
* if the first subflow may have the already the remote key handy
*/
opts->ext_copy.use_ack = 0;
msk = mptcp_sk(subflow->conn);
if (!READ_ONCE(msk->can_ack)) {
*size = ALIGN(dss_size, 4);
return ret;
Expand Down Expand Up @@ -783,6 +782,22 @@ static void update_una(struct mptcp_sock *msk,
}
}

bool mptcp_update_rcv_data_fin(struct mptcp_sock *msk, u64 data_fin_seq)
{
/* Skip if DATA_FIN was already received.
* If updating simultaneously with the recvmsg loop, values
* should match. If they mismatch, the peer is misbehaving and
* we will prefer the most recent information.
*/
if (READ_ONCE(msk->rcv_data_fin) || !READ_ONCE(msk->first))
return false;

WRITE_ONCE(msk->rcv_data_fin_seq, data_fin_seq);
WRITE_ONCE(msk->rcv_data_fin, 1);

return true;
}

static bool add_addr_hmac_valid(struct mptcp_sock *msk,
struct mptcp_options_received *mp_opt)
{
Expand Down Expand Up @@ -853,6 +868,20 @@ void mptcp_incoming_options(struct sock *sk, struct sk_buff *skb,
if (mp_opt.use_ack)
update_una(msk, &mp_opt);

/* Zero-data-length packets are dropped by the caller and not
* propagated to the MPTCP layer, so the skb extension does not
* need to be allocated or populated. DATA_FIN information, if
* present, needs to be updated here before the skb is freed.
*/
if (TCP_SKB_CB(skb)->seq == TCP_SKB_CB(skb)->end_seq) {
if (mp_opt.data_fin && mp_opt.data_len == 1 &&
mptcp_update_rcv_data_fin(msk, mp_opt.data_seq) &&
schedule_work(&msk->work))
sock_hold(subflow->conn);

return;
}

mpext = skb_ext_add(skb, SKB_EXT_MPTCP);
if (!mpext)
return;
Expand Down
Loading

0 comments on commit 323410e

Please sign in to comment.