Skip to content

Commit

Permalink
mptcp: Add handling of incoming MP_JOIN requests
Browse files Browse the repository at this point in the history
Process the MP_JOIN option in a SYN packet with the same flow
as MP_CAPABLE but when the third ACK is received add the
subflow to the MPTCP socket subflow list instead of adding it to
the TCP socket accept queue.

The subflow is added at the end of the subflow list so it will not
interfere with the existing subflows operation and no data is
expected to be transmitted on it.

Signed-off-by: Peter Krystad <peter.krystad@linux.intel.com>
  • Loading branch information
Peter Krystad authored and jenkins-tessares committed Jun 17, 2019
1 parent b9128e2 commit 8831ce3
Show file tree
Hide file tree
Showing 8 changed files with 301 additions and 15 deletions.
6 changes: 6 additions & 0 deletions include/linux/tcp.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,14 @@ struct tcp_options_received {
u8 mp_capable : 1,
mp_join : 1,
dss : 1,
backup : 1,
version : 4;
u8 flags;
u8 join_id;
u32 token;
u32 nonce;
u64 thmac;
u8 hmac[20];
u8 dss_flags;
u8 use_map:1,
dsn64:1,
Expand Down
14 changes: 14 additions & 0 deletions include/net/mptcp.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ struct mptcp_ext {
#define OPTION_MPTCP_MPC_SYN BIT(0)
#define OPTION_MPTCP_MPC_SYNACK BIT(1)
#define OPTION_MPTCP_MPC_ACK BIT(2)
#define OPTION_MPTCP_MPJ_SYN BIT(3)
#define OPTION_MPTCP_MPJ_SYNACK BIT(4)
#define OPTION_MPTCP_MPJ_ACK BIT(5)
#define OPTION_MPTCP_ADD_ADDR BIT(6)
#define OPTION_MPTCP_ADD_ADDR6 BIT(7)
#define OPTION_MPTCP_RM_ADDR BIT(8)
Expand All @@ -44,6 +47,10 @@ struct mptcp_out_options {
#endif
};
u8 addr_id;
u8 join_id;
u8 backup;
u32 nonce;
u64 thmac;
struct mptcp_ext ext_copy;
#endif
};
Expand Down Expand Up @@ -83,6 +90,8 @@ static inline bool mptcp_skb_ext_exist(const struct sk_buff *skb)

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

bool mptcp_sk_is_subflow(const struct sock *sk);

#else

static inline void mptcp_init(void)
Expand Down Expand Up @@ -140,5 +149,10 @@ static inline bool mptcp_skb_ext_exist(const struct sk_buff *skb)
return false;
}

static inline bool mptcp_sk_is_subflow(const struct sock *sk)
{
return false;
}

#endif /* CONFIG_MPTCP */
#endif /* __NET_MPTCP_H */
6 changes: 6 additions & 0 deletions net/ipv4/tcp_minisocks.c
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,12 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
if (!child)
goto listen_overflow;

if (own_req && sk_is_mptcp(child) && mptcp_sk_is_subflow(child)) {
inet_csk_reqsk_queue_drop(sk, req);
reqsk_queue_removed(&inet_csk(sk)->icsk_accept_queue, req);
return child;
}

sock_rps_save_rxhash(child, skb);
tcp_synack_rtt_meas(child, req);
*req_stolen = !own_req;
Expand Down
58 changes: 54 additions & 4 deletions net/mptcp/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,24 +54,53 @@ void mptcp_parse_option(const unsigned char *ptr, int opsize,
break;

/* MPTCPOPT_MP_JOIN
*
* Initial SYN
* 0: 4MSB=subtype, 000, 1LSB=Backup
* 1: Address ID
* 2-5: Receiver token
* 6-9: Sender random number
*
* SYN/ACK response
* 0: 4MSB=subtype, 000, 1LSB=Backup
* 1: Address ID
* 2-9: Sender truncated HMAC
* 10-13: Sender random number
*
* Third ACK
* 0: 4MSB=subtype, 0000
* 1: 0 (Reserved)
* 2-21: Sender HMAC
*/
case MPTCPOPT_MP_JOIN:
mp_opt->mp_join = 1;
if (opsize == TCPOLEN_MPTCP_MPJ_SYN) {
mp_opt->backup = *ptr++ & MPTCPOPT_BACKUP;
mp_opt->join_id = *ptr++;
mp_opt->token = get_unaligned_be32(ptr);
ptr += 4;
mp_opt->nonce = get_unaligned_be32(ptr);
ptr += 4;
pr_debug("MP_JOIN bkup=%u, id=%u, token=%u, nonce=%u",
mp_opt->backup, mp_opt->join_id,
mp_opt->token, mp_opt->nonce);
} else if (opsize == TCPOLEN_MPTCP_MPJ_SYNACK) {
mp_opt->backup = *ptr++ & MPTCPOPT_BACKUP;
mp_opt->join_id = *ptr++;
mp_opt->thmac = get_unaligned_be64(ptr);
ptr += 8;
mp_opt->nonce = get_unaligned_be32(ptr);
ptr += 4;
pr_debug("MP_JOIN bkup=%u, id=%u, thmac=%llu, nonce=%u",
mp_opt->backup, mp_opt->join_id,
mp_opt->thmac, mp_opt->nonce);
} else if (opsize == TCPOLEN_MPTCP_MPJ_ACK) {
ptr++;
memcpy(mp_opt->hmac, ptr, MPTCPOPT_HMAC_LEN);
pr_debug("MP_JOIN hmac");
} else {
pr_warn("MP_JOIN bad option size");
mp_opt->mp_join = 0;
}
break;


/* MPTCPOPT_DSS
* 0: 4MSB=subtype, 0000
Expand Down Expand Up @@ -428,10 +457,21 @@ bool mptcp_synack_options(const struct request_sock *req, unsigned int *size,
opts->sndr_key = subflow_req->local_key;
opts->rcvr_key = subflow_req->remote_key;
*size = TCPOLEN_MPTCP_MPC_SYNACK;
pr_debug("subflow_req=%p, local_key=%llu, remote_key=%llu",
pr_debug("req=%p, local_key=%llu, remote_key=%llu",
subflow_req, subflow_req->local_key,
subflow_req->remote_key);
return true;
} else if (subflow_req->mp_join) {
opts->suboptions = OPTION_MPTCP_MPJ_SYNACK;
opts->backup = subflow_req->backup;
opts->join_id = subflow_req->local_id;
opts->thmac = subflow_req->thmac;
opts->nonce = subflow_req->local_nonce;
pr_debug("req=%p, bkup=%u, id=%u, thmac=%llu, nonce=%u",
subflow_req, opts->backup, opts->join_id,
opts->thmac, opts->nonce);
*size = TCPOLEN_MPTCP_MPJ_SYNACK;
return true;
}
return false;
}
Expand Down Expand Up @@ -518,6 +558,16 @@ void mptcp_write_options(__be32 *ptr, struct mptcp_out_options *opts)
0, opts->addr_id);
}

if (OPTION_MPTCP_MPJ_SYNACK & opts->suboptions) {
*ptr++ = mptcp_option(MPTCPOPT_MP_JOIN,
TCPOLEN_MPTCP_MPJ_SYNACK,
opts->backup, opts->join_id);
put_unaligned_be64(opts->thmac, ptr);
ptr += 2;
put_unaligned_be32(opts->nonce, ptr);
ptr += 1;
}

if (opts->ext_copy.use_ack || opts->ext_copy.use_map) {
struct mptcp_ext *mpext = &opts->ext_copy;
u8 len = TCPOLEN_MPTCP_DSS_BASE;
Expand Down
21 changes: 21 additions & 0 deletions net/mptcp/protocol.c
Original file line number Diff line number Diff line change
Expand Up @@ -777,6 +777,27 @@ void mptcp_finish_connect(struct sock *sk, int mp_capable)
inet_sk_state_store(sk, TCP_ESTABLISHED);
}

void mptcp_finish_join(struct sock *conn, struct sock *sk)
{
struct subflow_context *subflow = subflow_ctx(sk);
struct mptcp_sock *msk = mptcp_sk(conn);

pr_debug("msk=%p, subflow=%p", msk, subflow);

local_bh_disable();
bh_lock_sock_nested(sk);
list_add_tail(&subflow->node, &msk->conn_list);
bh_unlock_sock(sk);
local_bh_enable();
}

bool mptcp_sk_is_subflow(const struct sock *sk)
{
struct subflow_context *subflow = subflow_ctx(sk);

return subflow->mp_join == 1;
}

static struct proto mptcp_prot = {
.name = "MPTCP",
.owner = THIS_MODULE,
Expand Down
29 changes: 27 additions & 2 deletions net/mptcp/protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
#define TCPOLEN_MPTCP_MPC_SYN 12
#define TCPOLEN_MPTCP_MPC_SYNACK 20
#define TCPOLEN_MPTCP_MPC_ACK 20
#define TCPOLEN_MPTCP_MPJ_SYN 12
#define TCPOLEN_MPTCP_MPJ_SYNACK 16
#define TCPOLEN_MPTCP_MPJ_ACK 24
#define TCPOLEN_MPTCP_DSS_BASE 4
#define TCPOLEN_MPTCP_DSS_ACK32 4
#define TCPOLEN_MPTCP_DSS_ACK64 8
Expand All @@ -34,6 +37,9 @@
#define TCPOLEN_MPTCP_ADD_ADDR6 20
#define TCPOLEN_MPTCP_RM_ADDR 4

#define MPTCPOPT_BACKUP BIT(0)
#define MPTCPOPT_HMAC_LEN 20

/* MPTCP MP_CAPABLE flags */
#define MPTCP_VERSION_MASK (0x0F)
#define MPTCP_CAP_CHECKSUM_REQD BIT(7)
Expand Down Expand Up @@ -101,11 +107,16 @@ struct subflow_request_sock {
checksum : 1,
backup : 1,
version : 4;
u8 local_id;
u8 remote_id;
u64 local_key;
u64 remote_key;
u64 idsn;
u32 token;
u32 ssn_offset;
u64 thmac;
u32 local_nonce;
u32 remote_nonce;
};

static inline
Expand All @@ -128,15 +139,23 @@ struct subflow_context {
u16 map_data_len;
u16 request_mptcp : 1, /* send MP_CAPABLE */
request_cksum : 1,
mp_capable : 1, /* remote is MPTCP capable */
mp_capable : 1, /* remote is MPTCP capable */
mp_join : 1, /* remote is JOINing */
fourth_ack : 1, /* send initial DSS */
version : 4,
conn_finished : 1,
use_checksum : 1,
map_valid : 1;
map_valid : 1,
backup : 1;
u32 remote_nonce;
u64 thmac;
u32 local_nonce;
u8 local_id;
u8 remote_id;

struct socket *tcp_sock; /* underlying tcp_sock */
struct sock *conn; /* parent mptcp_sock */

void (*tcp_sk_data_ready)(struct sock *sk);
};

Expand All @@ -161,13 +180,19 @@ void mptcp_get_options(const struct sk_buff *skb,
struct tcp_options_received *opt_rx);

void mptcp_finish_connect(struct sock *sk, int mp_capable);
void mptcp_finish_join(struct sock *conn, struct sock *sk);

void token_init(void);
void token_new_request(struct request_sock *req, const struct sk_buff *skb);
int token_join_request(struct request_sock *req, const struct sk_buff *skb);
int token_join_valid(struct request_sock *req,
struct tcp_options_received *rx_opt);
void token_destroy_request(u32 token);
void token_new_connect(struct sock *sk);
void token_new_accept(struct sock *sk);
int token_new_join(struct sock *sk);
void token_update_accept(struct sock *sk, struct sock *conn);
void token_release(u32 token);
void token_destroy(u32 token);

void crypto_init(void);
Expand Down
58 changes: 49 additions & 9 deletions net/mptcp/subflow.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ static void subflow_v4_init_req(struct request_sock *req,
memset(&rx_opt.mptcp, 0, sizeof(rx_opt.mptcp));
mptcp_get_options(skb, &rx_opt);

subflow_req->mp_capable = 0;
subflow_req->mp_join = 0;

if (rx_opt.mptcp.mp_capable && rx_opt.mptcp.mp_join)
return;

if (rx_opt.mptcp.mp_capable && listener->request_mptcp) {
subflow_req->mp_capable = 1;
if (rx_opt.mptcp.version >= listener->version)
Expand All @@ -68,8 +74,18 @@ static void subflow_v4_init_req(struct request_sock *req,
token_new_request(req, skb);
pr_debug("syn seq=%u", TCP_SKB_CB(skb)->seq);
subflow_req->ssn_offset = TCP_SKB_CB(skb)->seq;
} else {
subflow_req->mp_capable = 0;
} else if (rx_opt.mptcp.mp_join && listener->request_mptcp) {
subflow_req->mp_join = 1;
subflow_req->backup = rx_opt.mptcp.backup;
subflow_req->remote_id = rx_opt.mptcp.join_id;
subflow_req->token = rx_opt.mptcp.token;
subflow_req->remote_nonce = rx_opt.mptcp.nonce;
pr_debug("token=%u, remote_nonce=%u", subflow_req->token,
subflow_req->remote_nonce);
if (token_join_request(req, skb)) {
subflow_req->mp_join = 0;
// @@ need to trigger RST
}
}
}

Expand Down Expand Up @@ -134,25 +150,39 @@ static struct sock *subflow_syn_recv_sock(const struct sock *sk,
subflow_req->local_key != opt_rx.mptcp.rcvr_key ||
subflow_req->remote_key != opt_rx.mptcp.sndr_key)
return NULL;
} else if (subflow_req->mp_join) {
opt_rx.mptcp.mp_join = 0;
mptcp_get_options(skb, &opt_rx);
if (!opt_rx.mptcp.mp_join || token_join_valid(req, &opt_rx))
return NULL;
}

child = tcp_v4_syn_recv_sock(sk, skb, req, dst, req_unhash, own_req);

if (child && *own_req) {
struct subflow_context *ctx = subflow_ctx(child);

if (!ctx) {
pr_debug("Closing child socket");
inet_sk_set_state(child, TCP_CLOSE);
sock_set_flag(child, SOCK_DEAD);
inet_csk_destroy_sock(child);
child = NULL;
} else if (ctx->mp_capable) {
if (!ctx)
goto close_child;

if (ctx->mp_capable) {
token_new_accept(child);
} else if (ctx->mp_join) {
if (token_new_join(child))
goto close_child;
else
mptcp_finish_join(ctx->conn, child);
}
}

return child;

close_child:
pr_debug("closing child socket");
inet_sk_set_state(child, TCP_CLOSE);
sock_set_flag(child, SOCK_DEAD);
inet_csk_destroy_sock(child);
return NULL;
}

static struct inet_connection_sock_af_ops subflow_specific;
Expand Down Expand Up @@ -222,6 +252,8 @@ static void subflow_ulp_release(struct sock *sk)

pr_debug("subflow=%p", ctx);

token_release(ctx->token);

kfree(ctx);
}

Expand Down Expand Up @@ -255,6 +287,14 @@ static void subflow_ulp_clone(const struct request_sock *req,
new_ctx->ssn_offset = subflow_req->ssn_offset;
new_ctx->idsn = subflow_req->idsn;
pr_debug("token=%u", new_ctx->token);
} else if (subflow_req->mp_join) {
new_ctx->mp_join = 1;
new_ctx->fourth_ack = 1;
new_ctx->backup = subflow_req->backup;
new_ctx->local_id = subflow_req->local_id;
new_ctx->token = subflow_req->token;
new_ctx->thmac = subflow_req->thmac;
pr_debug("token=%u", new_ctx->token);
}
}

Expand Down
Loading

0 comments on commit 8831ce3

Please sign in to comment.