Skip to content

Commit

Permalink
mptcp: Implement MPTCP receive path
Browse files Browse the repository at this point in the history
Parses incoming DSS options and populates outgoing MPTCP ACK
fields. MPTCP fields are parsed from the TCP option header and placed in
an skb extension, allowing the upper MPTCP layer to access MPTCP
options after the skb has gone through the TCP stack.

The subflow implements its own data_ready() ops, which ensures that the
pending data is in sequence - according to MPTCP seq number - dropping
out-of-seq skbs. The DATA_READY bit flag is set if this is the case.
This allows the MPTCP socket layer to determine if more data is
available without having to consult the individual subflows.

It additionally validates the current mapping and propagates EoF events
to the connection socket.

Co-developed-by: Paolo Abeni <pabeni@redhat.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Co-developed-by: Peter Krystad <peter.krystad@linux.intel.com>
Signed-off-by: Peter Krystad <peter.krystad@linux.intel.com>
Co-developed-by: Davide Caratti <dcaratti@redhat.com>
Signed-off-by: Davide Caratti <dcaratti@redhat.com>
Co-developed-by: Matthieu Baerts <matthieu.baerts@tessares.net>
Signed-off-by: Matthieu Baerts <matthieu.baerts@tessares.net>
Co-developed-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Mat Martineau <mathew.j.martineau@linux.intel.com>
Signed-off-by: Christoph Paasch <cpaasch@apple.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
mjmartineau authored and davem330 committed Jan 24, 2020
1 parent 6d0060f commit 648ef4b
Show file tree
Hide file tree
Showing 7 changed files with 609 additions and 5 deletions.
10 changes: 10 additions & 0 deletions include/linux/tcp.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,19 @@ struct tcp_sack_block {
struct mptcp_options_received {
u64 sndr_key;
u64 rcvr_key;
u64 data_ack;
u64 data_seq;
u32 subflow_seq;
u16 data_len;
u8 mp_capable : 1,
mp_join : 1,
dss : 1;
u8 use_map:1,
dsn64:1,
data_fin:1,
use_ack:1,
ack64:1,
__unused:3;
};
#endif

Expand Down
8 changes: 8 additions & 0 deletions include/net/mptcp.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ bool mptcp_synack_options(const struct request_sock *req, unsigned int *size,
bool mptcp_established_options(struct sock *sk, struct sk_buff *skb,
unsigned int *size, unsigned int remaining,
struct mptcp_out_options *opts);
void mptcp_incoming_options(struct sock *sk, struct sk_buff *skb,
struct tcp_options_received *opt_rx);

void mptcp_write_options(__be32 *ptr, struct mptcp_out_options *opts);

Expand Down Expand Up @@ -150,6 +152,12 @@ static inline bool mptcp_established_options(struct sock *sk,
return false;
}

static inline void mptcp_incoming_options(struct sock *sk,
struct sk_buff *skb,
struct tcp_options_received *opt_rx)
{
}

static inline void mptcp_skb_ext_move(struct sk_buff *to,
const struct sk_buff *from)
{
Expand Down
8 changes: 7 additions & 1 deletion net/ipv4/tcp_input.c
Original file line number Diff line number Diff line change
Expand Up @@ -4770,6 +4770,9 @@ static void tcp_data_queue(struct sock *sk, struct sk_buff *skb)
bool fragstolen;
int eaten;

if (sk_is_mptcp(sk))
mptcp_incoming_options(sk, skb, &tp->rx_opt);

if (TCP_SKB_CB(skb)->seq == TCP_SKB_CB(skb)->end_seq) {
__kfree_skb(skb);
return;
Expand Down Expand Up @@ -6346,8 +6349,11 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb)
case TCP_CLOSE_WAIT:
case TCP_CLOSING:
case TCP_LAST_ACK:
if (!before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt))
if (!before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt)) {
if (sk_is_mptcp(sk))
mptcp_incoming_options(sk, skb, &tp->rx_opt);
break;
}
/* fall through */
case TCP_FIN_WAIT1:
case TCP_FIN_WAIT2:
Expand Down
107 changes: 107 additions & 0 deletions net/mptcp/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ void mptcp_parse_option(const unsigned char *ptr, int opsize,
{
struct mptcp_options_received *mp_opt = &opt_rx->mptcp;
u8 subtype = *ptr >> 4;
int expected_opsize;
u8 version;
u8 flags;

Expand Down Expand Up @@ -64,7 +65,79 @@ void mptcp_parse_option(const unsigned char *ptr, int opsize,

case MPTCPOPT_DSS:
pr_debug("DSS");
ptr++;

flags = (*ptr++) & MPTCP_DSS_FLAG_MASK;
mp_opt->data_fin = (flags & MPTCP_DSS_DATA_FIN) != 0;
mp_opt->dsn64 = (flags & MPTCP_DSS_DSN64) != 0;
mp_opt->use_map = (flags & MPTCP_DSS_HAS_MAP) != 0;
mp_opt->ack64 = (flags & MPTCP_DSS_ACK64) != 0;
mp_opt->use_ack = (flags & MPTCP_DSS_HAS_ACK);

pr_debug("data_fin=%d dsn64=%d use_map=%d ack64=%d use_ack=%d",
mp_opt->data_fin, mp_opt->dsn64,
mp_opt->use_map, mp_opt->ack64,
mp_opt->use_ack);

expected_opsize = TCPOLEN_MPTCP_DSS_BASE;

if (mp_opt->use_ack) {
if (mp_opt->ack64)
expected_opsize += TCPOLEN_MPTCP_DSS_ACK64;
else
expected_opsize += TCPOLEN_MPTCP_DSS_ACK32;
}

if (mp_opt->use_map) {
if (mp_opt->dsn64)
expected_opsize += TCPOLEN_MPTCP_DSS_MAP64;
else
expected_opsize += TCPOLEN_MPTCP_DSS_MAP32;
}

/* RFC 6824, Section 3.3:
* If a checksum is present, but its use had
* not been negotiated in the MP_CAPABLE handshake,
* the checksum field MUST be ignored.
*/
if (opsize != expected_opsize &&
opsize != expected_opsize + TCPOLEN_MPTCP_DSS_CHECKSUM)
break;

mp_opt->dss = 1;

if (mp_opt->use_ack) {
if (mp_opt->ack64) {
mp_opt->data_ack = get_unaligned_be64(ptr);
ptr += 8;
} else {
mp_opt->data_ack = get_unaligned_be32(ptr);
ptr += 4;
}

pr_debug("data_ack=%llu", mp_opt->data_ack);
}

if (mp_opt->use_map) {
if (mp_opt->dsn64) {
mp_opt->data_seq = get_unaligned_be64(ptr);
ptr += 8;
} else {
mp_opt->data_seq = get_unaligned_be32(ptr);
ptr += 4;
}

mp_opt->subflow_seq = get_unaligned_be32(ptr);
ptr += 4;

mp_opt->data_len = get_unaligned_be16(ptr);
ptr += 2;

pr_debug("data_seq=%llu subflow_seq=%u data_len=%u",
mp_opt->data_seq, mp_opt->subflow_seq,
mp_opt->data_len);
}

break;

default:
Expand Down Expand Up @@ -275,6 +348,40 @@ bool mptcp_synack_options(const struct request_sock *req, unsigned int *size,
return false;
}

void mptcp_incoming_options(struct sock *sk, struct sk_buff *skb,
struct tcp_options_received *opt_rx)
{
struct mptcp_options_received *mp_opt;
struct mptcp_ext *mpext;

mp_opt = &opt_rx->mptcp;

if (!mp_opt->dss)
return;

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

memset(mpext, 0, sizeof(*mpext));

if (mp_opt->use_map) {
mpext->data_seq = mp_opt->data_seq;
mpext->subflow_seq = mp_opt->subflow_seq;
mpext->data_len = mp_opt->data_len;
mpext->use_map = 1;
mpext->dsn64 = mp_opt->dsn64;
}

if (mp_opt->use_ack) {
mpext->data_ack = mp_opt->data_ack;
mpext->use_ack = 1;
mpext->ack64 = mp_opt->ack64;
}

mpext->data_fin = mp_opt->data_fin;
}

void mptcp_write_options(__be32 *ptr, struct mptcp_out_options *opts)
{
if ((OPTION_MPTCP_MPC_SYN |
Expand Down
34 changes: 34 additions & 0 deletions net/mptcp/protocol.c
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,33 @@ static int mptcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
return ret;
}

int mptcp_read_actor(read_descriptor_t *desc, struct sk_buff *skb,
unsigned int offset, size_t len)
{
struct mptcp_read_arg *arg = desc->arg.data;
size_t copy_len;

copy_len = min(desc->count, len);

if (likely(arg->msg)) {
int err;

err = skb_copy_datagram_msg(skb, offset, arg->msg, copy_len);
if (err) {
pr_debug("error path");
desc->error = err;
return err;
}
} else {
pr_debug("Flushing skb payload");
}

desc->count -= copy_len;

pr_debug("consumed %zu bytes, %zu left", copy_len, desc->count);
return copy_len;
}

static int mptcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
int nonblock, int flags, int *addr_len)
{
Expand Down Expand Up @@ -414,7 +441,10 @@ static struct sock *mptcp_accept(struct sock *sk, int flags, int *err,
msk->write_seq = subflow->idsn + 1;
ack_seq++;
msk->ack_seq = ack_seq;
subflow->map_seq = ack_seq;
subflow->map_subflow_seq = 1;
subflow->rel_write_seq = 1;
subflow->tcp_sock = ssk;
newsk = new_mptcp_sock;
mptcp_copy_inaddrs(newsk, ssk);
list_add(&subflow->node, &msk->conn_list);
Expand Down Expand Up @@ -519,8 +549,12 @@ void mptcp_finish_connect(struct sock *ssk)
sk = subflow->conn;
msk = mptcp_sk(sk);

pr_debug("msk=%p, token=%u", sk, subflow->token);

mptcp_crypto_key_sha(subflow->remote_key, NULL, &ack_seq);
ack_seq++;
subflow->map_seq = ack_seq;
subflow->map_subflow_seq = 1;
subflow->rel_write_seq = 1;

/* the socket is not connected yet, no msk/subflow ops can access/race
Expand Down
64 changes: 63 additions & 1 deletion net/mptcp/protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@
#define TCPOLEN_MPTCP_MPC_SYNACK 12
#define TCPOLEN_MPTCP_MPC_ACK 20
#define TCPOLEN_MPTCP_DSS_BASE 4
#define TCPOLEN_MPTCP_DSS_ACK32 4
#define TCPOLEN_MPTCP_DSS_ACK64 8
#define TCPOLEN_MPTCP_DSS_MAP32 10
#define TCPOLEN_MPTCP_DSS_MAP64 14
#define TCPOLEN_MPTCP_DSS_CHECKSUM 2

Expand All @@ -50,6 +52,10 @@
#define MPTCP_DSS_HAS_MAP BIT(2)
#define MPTCP_DSS_ACK64 BIT(1)
#define MPTCP_DSS_HAS_ACK BIT(0)
#define MPTCP_DSS_FLAG_MASK (0x1F)

/* MPTCP socket flags */
#define MPTCP_DATA_READY BIT(0)

/* MPTCP connection sock */
struct mptcp_sock {
Expand All @@ -60,6 +66,7 @@ struct mptcp_sock {
u64 write_seq;
u64 ack_seq;
u32 token;
unsigned long flags;
struct list_head conn_list;
struct skb_ext *cached_ext; /* for the next sendmsg */
struct socket *subflow; /* outgoing connect/listener/!mp_capable */
Expand All @@ -82,6 +89,7 @@ struct mptcp_subflow_request_sock {
u64 remote_key;
u64 idsn;
u32 token;
u32 ssn_offset;
};

static inline struct mptcp_subflow_request_sock *
Expand All @@ -96,15 +104,27 @@ struct mptcp_subflow_context {
u64 local_key;
u64 remote_key;
u64 idsn;
u64 map_seq;
u32 token;
u32 rel_write_seq;
u32 map_subflow_seq;
u32 ssn_offset;
u32 map_data_len;
u32 request_mptcp : 1, /* send MP_CAPABLE */
mp_capable : 1, /* remote is MPTCP capable */
fourth_ack : 1, /* send initial DSS */
conn_finished : 1;
conn_finished : 1,
map_valid : 1,
data_avail : 1,
rx_eof : 1;

struct sock *tcp_sock; /* tcp sk backpointer */
struct sock *conn; /* parent mptcp_sock */
const struct inet_connection_sock_af_ops *icsk_af_ops;
void (*tcp_data_ready)(struct sock *sk);
void (*tcp_state_change)(struct sock *sk);
void (*tcp_write_space)(struct sock *sk);

struct rcu_head rcu;
};

Expand All @@ -123,14 +143,49 @@ mptcp_subflow_tcp_sock(const struct mptcp_subflow_context *subflow)
return subflow->tcp_sock;
}

static inline u64
mptcp_subflow_get_map_offset(const struct mptcp_subflow_context *subflow)
{
return tcp_sk(mptcp_subflow_tcp_sock(subflow))->copied_seq -
subflow->ssn_offset -
subflow->map_subflow_seq;
}

static inline u64
mptcp_subflow_get_mapped_dsn(const struct mptcp_subflow_context *subflow)
{
return subflow->map_seq + mptcp_subflow_get_map_offset(subflow);
}

int mptcp_is_enabled(struct net *net);
bool mptcp_subflow_data_available(struct sock *sk);
void mptcp_subflow_init(void);
int mptcp_subflow_create_socket(struct sock *sk, struct socket **new_sock);

static inline void mptcp_subflow_tcp_fallback(struct sock *sk,
struct mptcp_subflow_context *ctx)
{
sk->sk_data_ready = ctx->tcp_data_ready;
sk->sk_state_change = ctx->tcp_state_change;
sk->sk_write_space = ctx->tcp_write_space;

inet_csk(sk)->icsk_af_ops = ctx->icsk_af_ops;
}

extern const struct inet_connection_sock_af_ops ipv4_specific;
#if IS_ENABLED(CONFIG_MPTCP_IPV6)
extern const struct inet_connection_sock_af_ops ipv6_specific;
#endif

void mptcp_proto_init(void);

struct mptcp_read_arg {
struct msghdr *msg;
};

int mptcp_read_actor(read_descriptor_t *desc, struct sk_buff *skb,
unsigned int offset, size_t len);

void mptcp_get_options(const struct sk_buff *skb,
struct tcp_options_received *opt_rx);

Expand Down Expand Up @@ -164,4 +219,11 @@ static inline struct mptcp_ext *mptcp_get_ext(struct sk_buff *skb)
return (struct mptcp_ext *)skb_ext_find(skb, SKB_EXT_MPTCP);
}

static inline bool before64(__u64 seq1, __u64 seq2)
{
return (__s64)(seq1 - seq2) < 0;
}

#define after64(seq2, seq1) before64(seq1, seq2)

#endif /* __MPTCP_PROTOCOL_H */
Loading

0 comments on commit 648ef4b

Please sign in to comment.