Commit 648ef4b8 authored by Mat Martineau's avatar Mat Martineau Committed by David S. Miller

mptcp: Implement MPTCP receive path

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: default avatarPaolo Abeni <pabeni@redhat.com>
Signed-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
Co-developed-by: default avatarPeter Krystad <peter.krystad@linux.intel.com>
Signed-off-by: default avatarPeter Krystad <peter.krystad@linux.intel.com>
Co-developed-by: default avatarDavide Caratti <dcaratti@redhat.com>
Signed-off-by: default avatarDavide Caratti <dcaratti@redhat.com>
Co-developed-by: default avatarMatthieu Baerts <matthieu.baerts@tessares.net>
Signed-off-by: default avatarMatthieu Baerts <matthieu.baerts@tessares.net>
Co-developed-by: default avatarFlorian Westphal <fw@strlen.de>
Signed-off-by: default avatarFlorian Westphal <fw@strlen.de>
Signed-off-by: default avatarMat Martineau <mathew.j.martineau@linux.intel.com>
Signed-off-by: default avatarChristoph Paasch <cpaasch@apple.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 6d0060f6
...@@ -82,9 +82,19 @@ struct tcp_sack_block { ...@@ -82,9 +82,19 @@ struct tcp_sack_block {
struct mptcp_options_received { struct mptcp_options_received {
u64 sndr_key; u64 sndr_key;
u64 rcvr_key; u64 rcvr_key;
u64 data_ack;
u64 data_seq;
u32 subflow_seq;
u16 data_len;
u8 mp_capable : 1, u8 mp_capable : 1,
mp_join : 1, mp_join : 1,
dss : 1; dss : 1;
u8 use_map:1,
dsn64:1,
data_fin:1,
use_ack:1,
ack64:1,
__unused:3;
}; };
#endif #endif
......
...@@ -60,6 +60,8 @@ bool mptcp_synack_options(const struct request_sock *req, unsigned int *size, ...@@ -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, bool mptcp_established_options(struct sock *sk, struct sk_buff *skb,
unsigned int *size, unsigned int remaining, unsigned int *size, unsigned int remaining,
struct mptcp_out_options *opts); 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); void mptcp_write_options(__be32 *ptr, struct mptcp_out_options *opts);
...@@ -150,6 +152,12 @@ static inline bool mptcp_established_options(struct sock *sk, ...@@ -150,6 +152,12 @@ static inline bool mptcp_established_options(struct sock *sk,
return false; 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, static inline void mptcp_skb_ext_move(struct sk_buff *to,
const struct sk_buff *from) const struct sk_buff *from)
{ {
......
...@@ -4770,6 +4770,9 @@ static void tcp_data_queue(struct sock *sk, struct sk_buff *skb) ...@@ -4770,6 +4770,9 @@ static void tcp_data_queue(struct sock *sk, struct sk_buff *skb)
bool fragstolen; bool fragstolen;
int eaten; 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) { if (TCP_SKB_CB(skb)->seq == TCP_SKB_CB(skb)->end_seq) {
__kfree_skb(skb); __kfree_skb(skb);
return; return;
...@@ -6346,8 +6349,11 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb) ...@@ -6346,8 +6349,11 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb)
case TCP_CLOSE_WAIT: case TCP_CLOSE_WAIT:
case TCP_CLOSING: case TCP_CLOSING:
case TCP_LAST_ACK: 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; break;
}
/* fall through */ /* fall through */
case TCP_FIN_WAIT1: case TCP_FIN_WAIT1:
case TCP_FIN_WAIT2: case TCP_FIN_WAIT2:
......
...@@ -14,6 +14,7 @@ void mptcp_parse_option(const unsigned char *ptr, int opsize, ...@@ -14,6 +14,7 @@ void mptcp_parse_option(const unsigned char *ptr, int opsize,
{ {
struct mptcp_options_received *mp_opt = &opt_rx->mptcp; struct mptcp_options_received *mp_opt = &opt_rx->mptcp;
u8 subtype = *ptr >> 4; u8 subtype = *ptr >> 4;
int expected_opsize;
u8 version; u8 version;
u8 flags; u8 flags;
...@@ -64,7 +65,79 @@ void mptcp_parse_option(const unsigned char *ptr, int opsize, ...@@ -64,7 +65,79 @@ void mptcp_parse_option(const unsigned char *ptr, int opsize,
case MPTCPOPT_DSS: case MPTCPOPT_DSS:
pr_debug("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; 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; break;
default: default:
...@@ -275,6 +348,40 @@ bool mptcp_synack_options(const struct request_sock *req, unsigned int *size, ...@@ -275,6 +348,40 @@ bool mptcp_synack_options(const struct request_sock *req, unsigned int *size,
return false; 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) void mptcp_write_options(__be32 *ptr, struct mptcp_out_options *opts)
{ {
if ((OPTION_MPTCP_MPC_SYN | if ((OPTION_MPTCP_MPC_SYN |
......
...@@ -224,6 +224,33 @@ static int mptcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) ...@@ -224,6 +224,33 @@ static int mptcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
return ret; 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, static int mptcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
int nonblock, int flags, int *addr_len) int nonblock, int flags, int *addr_len)
{ {
...@@ -414,7 +441,10 @@ static struct sock *mptcp_accept(struct sock *sk, int flags, int *err, ...@@ -414,7 +441,10 @@ static struct sock *mptcp_accept(struct sock *sk, int flags, int *err,
msk->write_seq = subflow->idsn + 1; msk->write_seq = subflow->idsn + 1;
ack_seq++; ack_seq++;
msk->ack_seq = ack_seq; msk->ack_seq = ack_seq;
subflow->map_seq = ack_seq;
subflow->map_subflow_seq = 1;
subflow->rel_write_seq = 1; subflow->rel_write_seq = 1;
subflow->tcp_sock = ssk;
newsk = new_mptcp_sock; newsk = new_mptcp_sock;
mptcp_copy_inaddrs(newsk, ssk); mptcp_copy_inaddrs(newsk, ssk);
list_add(&subflow->node, &msk->conn_list); list_add(&subflow->node, &msk->conn_list);
...@@ -519,8 +549,12 @@ void mptcp_finish_connect(struct sock *ssk) ...@@ -519,8 +549,12 @@ void mptcp_finish_connect(struct sock *ssk)
sk = subflow->conn; sk = subflow->conn;
msk = mptcp_sk(sk); msk = mptcp_sk(sk);
pr_debug("msk=%p, token=%u", sk, subflow->token);
mptcp_crypto_key_sha(subflow->remote_key, NULL, &ack_seq); mptcp_crypto_key_sha(subflow->remote_key, NULL, &ack_seq);
ack_seq++; ack_seq++;
subflow->map_seq = ack_seq;
subflow->map_subflow_seq = 1;
subflow->rel_write_seq = 1; subflow->rel_write_seq = 1;
/* the socket is not connected yet, no msk/subflow ops can access/race /* the socket is not connected yet, no msk/subflow ops can access/race
......
...@@ -33,7 +33,9 @@ ...@@ -33,7 +33,9 @@
#define TCPOLEN_MPTCP_MPC_SYNACK 12 #define TCPOLEN_MPTCP_MPC_SYNACK 12
#define TCPOLEN_MPTCP_MPC_ACK 20 #define TCPOLEN_MPTCP_MPC_ACK 20
#define TCPOLEN_MPTCP_DSS_BASE 4 #define TCPOLEN_MPTCP_DSS_BASE 4
#define TCPOLEN_MPTCP_DSS_ACK32 4
#define TCPOLEN_MPTCP_DSS_ACK64 8 #define TCPOLEN_MPTCP_DSS_ACK64 8
#define TCPOLEN_MPTCP_DSS_MAP32 10
#define TCPOLEN_MPTCP_DSS_MAP64 14 #define TCPOLEN_MPTCP_DSS_MAP64 14
#define TCPOLEN_MPTCP_DSS_CHECKSUM 2 #define TCPOLEN_MPTCP_DSS_CHECKSUM 2
...@@ -50,6 +52,10 @@ ...@@ -50,6 +52,10 @@
#define MPTCP_DSS_HAS_MAP BIT(2) #define MPTCP_DSS_HAS_MAP BIT(2)
#define MPTCP_DSS_ACK64 BIT(1) #define MPTCP_DSS_ACK64 BIT(1)
#define MPTCP_DSS_HAS_ACK BIT(0) #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 */ /* MPTCP connection sock */
struct mptcp_sock { struct mptcp_sock {
...@@ -60,6 +66,7 @@ struct mptcp_sock { ...@@ -60,6 +66,7 @@ struct mptcp_sock {
u64 write_seq; u64 write_seq;
u64 ack_seq; u64 ack_seq;
u32 token; u32 token;
unsigned long flags;
struct list_head conn_list; struct list_head conn_list;
struct skb_ext *cached_ext; /* for the next sendmsg */ struct skb_ext *cached_ext; /* for the next sendmsg */
struct socket *subflow; /* outgoing connect/listener/!mp_capable */ struct socket *subflow; /* outgoing connect/listener/!mp_capable */
...@@ -82,6 +89,7 @@ struct mptcp_subflow_request_sock { ...@@ -82,6 +89,7 @@ struct mptcp_subflow_request_sock {
u64 remote_key; u64 remote_key;
u64 idsn; u64 idsn;
u32 token; u32 token;
u32 ssn_offset;
}; };
static inline struct mptcp_subflow_request_sock * static inline struct mptcp_subflow_request_sock *
...@@ -96,15 +104,27 @@ struct mptcp_subflow_context { ...@@ -96,15 +104,27 @@ struct mptcp_subflow_context {
u64 local_key; u64 local_key;
u64 remote_key; u64 remote_key;
u64 idsn; u64 idsn;
u64 map_seq;
u32 token; u32 token;
u32 rel_write_seq; u32 rel_write_seq;
u32 map_subflow_seq;
u32 ssn_offset;
u32 map_data_len;
u32 request_mptcp : 1, /* send MP_CAPABLE */ u32 request_mptcp : 1, /* send MP_CAPABLE */
mp_capable : 1, /* remote is MPTCP capable */ mp_capable : 1, /* remote is MPTCP capable */
fourth_ack : 1, /* send initial DSS */ 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 *tcp_sock; /* tcp sk backpointer */
struct sock *conn; /* parent mptcp_sock */ struct sock *conn; /* parent mptcp_sock */
const struct inet_connection_sock_af_ops *icsk_af_ops; 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; struct rcu_head rcu;
}; };
...@@ -123,14 +143,49 @@ mptcp_subflow_tcp_sock(const struct mptcp_subflow_context *subflow) ...@@ -123,14 +143,49 @@ mptcp_subflow_tcp_sock(const struct mptcp_subflow_context *subflow)
return subflow->tcp_sock; 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); void mptcp_subflow_init(void);
int mptcp_subflow_create_socket(struct sock *sk, struct socket **new_sock); 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; extern const struct inet_connection_sock_af_ops ipv4_specific;
#if IS_ENABLED(CONFIG_MPTCP_IPV6) #if IS_ENABLED(CONFIG_MPTCP_IPV6)
extern const struct inet_connection_sock_af_ops ipv6_specific; extern const struct inet_connection_sock_af_ops ipv6_specific;
#endif #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, void mptcp_get_options(const struct sk_buff *skb,
struct tcp_options_received *opt_rx); struct tcp_options_received *opt_rx);
...@@ -164,4 +219,11 @@ static inline struct mptcp_ext *mptcp_get_ext(struct sk_buff *skb) ...@@ -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); 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 */ #endif /* __MPTCP_PROTOCOL_H */
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment