Skip to content

Commit cec37a6

Browse files
Peter Krystaddavem330
authored andcommitted
mptcp: Handle MP_CAPABLE options for outgoing connections
Add hooks to tcp_output.c to add MP_CAPABLE to an outgoing SYN request, to capture the MP_CAPABLE in the received SYN-ACK, to add MP_CAPABLE to the final ACK of the three-way handshake. Use the .sk_rx_dst_set() handler in the subflow proto to capture when the responding SYN-ACK is received and notify the MPTCP connection layer. Co-developed-by: Paolo Abeni <[email protected]> Signed-off-by: Paolo Abeni <[email protected]> Co-developed-by: Florian Westphal <[email protected]> Signed-off-by: Florian Westphal <[email protected]> Signed-off-by: Peter Krystad <[email protected]> Signed-off-by: Christoph Paasch <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 2303f99 commit cec37a6

File tree

9 files changed

+663
-24
lines changed

9 files changed

+663
-24
lines changed

include/linux/tcp.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,9 @@ struct tcp_request_sock {
137137
const struct tcp_request_sock_ops *af_specific;
138138
u64 snt_synack; /* first SYNACK sent time */
139139
bool tfo_listener;
140+
#if IS_ENABLED(CONFIG_MPTCP)
141+
bool is_mptcp;
142+
#endif
140143
u32 txhash;
141144
u32 rcv_isn;
142145
u32 snt_isn;

include/net/mptcp.h

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,27 @@ struct mptcp_out_options {
3939

4040
void mptcp_init(void);
4141

42+
static inline bool sk_is_mptcp(const struct sock *sk)
43+
{
44+
return tcp_sk(sk)->is_mptcp;
45+
}
46+
47+
static inline bool rsk_is_mptcp(const struct request_sock *req)
48+
{
49+
return tcp_rsk(req)->is_mptcp;
50+
}
51+
4252
void mptcp_parse_option(const unsigned char *ptr, int opsize,
4353
struct tcp_options_received *opt_rx);
54+
bool mptcp_syn_options(struct sock *sk, unsigned int *size,
55+
struct mptcp_out_options *opts);
56+
void mptcp_rcv_synsent(struct sock *sk);
57+
bool mptcp_synack_options(const struct request_sock *req, unsigned int *size,
58+
struct mptcp_out_options *opts);
59+
bool mptcp_established_options(struct sock *sk, struct sk_buff *skb,
60+
unsigned int *size, unsigned int remaining,
61+
struct mptcp_out_options *opts);
62+
4463
void mptcp_write_options(__be32 *ptr, struct mptcp_out_options *opts);
4564

4665
/* move the skb extension owership, with the assumption that 'to' is
@@ -89,11 +108,47 @@ static inline void mptcp_init(void)
89108
{
90109
}
91110

111+
static inline bool sk_is_mptcp(const struct sock *sk)
112+
{
113+
return false;
114+
}
115+
116+
static inline bool rsk_is_mptcp(const struct request_sock *req)
117+
{
118+
return false;
119+
}
120+
92121
static inline void mptcp_parse_option(const unsigned char *ptr, int opsize,
93122
struct tcp_options_received *opt_rx)
94123
{
95124
}
96125

126+
static inline bool mptcp_syn_options(struct sock *sk, unsigned int *size,
127+
struct mptcp_out_options *opts)
128+
{
129+
return false;
130+
}
131+
132+
static inline void mptcp_rcv_synsent(struct sock *sk)
133+
{
134+
}
135+
136+
static inline bool mptcp_synack_options(const struct request_sock *req,
137+
unsigned int *size,
138+
struct mptcp_out_options *opts)
139+
{
140+
return false;
141+
}
142+
143+
static inline bool mptcp_established_options(struct sock *sk,
144+
struct sk_buff *skb,
145+
unsigned int *size,
146+
unsigned int remaining,
147+
struct mptcp_out_options *opts)
148+
{
149+
return false;
150+
}
151+
97152
static inline void mptcp_skb_ext_move(struct sk_buff *to,
98153
const struct sk_buff *from)
99154
{
@@ -107,6 +162,8 @@ static inline bool mptcp_skb_can_collapse(const struct sk_buff *to,
107162

108163
#endif /* CONFIG_MPTCP */
109164

165+
void mptcp_handle_ipv6_mapped(struct sock *sk, bool mapped);
166+
110167
#if IS_ENABLED(CONFIG_MPTCP_IPV6)
111168
int mptcpv6_init(void);
112169
#elif IS_ENABLED(CONFIG_IPV6)

net/ipv4/tcp_input.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5978,6 +5978,9 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,
59785978
tcp_sync_mss(sk, icsk->icsk_pmtu_cookie);
59795979
tcp_initialize_rcv_mss(sk);
59805980

5981+
if (sk_is_mptcp(sk))
5982+
mptcp_rcv_synsent(sk);
5983+
59815984
/* Remember, tcp_poll() does not lock socket!
59825985
* Change state from SYN-SENT only after copied_seq
59835986
* is initialized. */
@@ -6600,6 +6603,9 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops,
66006603

66016604
tcp_rsk(req)->af_specific = af_ops;
66026605
tcp_rsk(req)->ts_off = 0;
6606+
#if IS_ENABLED(CONFIG_MPTCP)
6607+
tcp_rsk(req)->is_mptcp = 0;
6608+
#endif
66036609

66046610
tcp_clear_options(&tmp_opt);
66056611
tmp_opt.mss_clamp = af_ops->mss_clamp;

net/ipv4/tcp_output.c

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,22 @@ static void smc_set_option_cond(const struct tcp_sock *tp,
597597
#endif
598598
}
599599

600+
static void mptcp_set_option_cond(const struct request_sock *req,
601+
struct tcp_out_options *opts,
602+
unsigned int *remaining)
603+
{
604+
if (rsk_is_mptcp(req)) {
605+
unsigned int size;
606+
607+
if (mptcp_synack_options(req, &size, &opts->mptcp)) {
608+
if (*remaining >= size) {
609+
opts->options |= OPTION_MPTCP;
610+
*remaining -= size;
611+
}
612+
}
613+
}
614+
}
615+
600616
/* Compute TCP options for SYN packets. This is not the final
601617
* network wire format yet.
602618
*/
@@ -666,6 +682,15 @@ static unsigned int tcp_syn_options(struct sock *sk, struct sk_buff *skb,
666682

667683
smc_set_option(tp, opts, &remaining);
668684

685+
if (sk_is_mptcp(sk)) {
686+
unsigned int size;
687+
688+
if (mptcp_syn_options(sk, &size, &opts->mptcp)) {
689+
opts->options |= OPTION_MPTCP;
690+
remaining -= size;
691+
}
692+
}
693+
669694
return MAX_TCP_OPTION_SPACE - remaining;
670695
}
671696

@@ -727,6 +752,8 @@ static unsigned int tcp_synack_options(const struct sock *sk,
727752
}
728753
}
729754

755+
mptcp_set_option_cond(req, opts, &remaining);
756+
730757
smc_set_option_cond(tcp_sk(sk), ireq, opts, &remaining);
731758

732759
return MAX_TCP_OPTION_SPACE - remaining;
@@ -764,6 +791,23 @@ static unsigned int tcp_established_options(struct sock *sk, struct sk_buff *skb
764791
size += TCPOLEN_TSTAMP_ALIGNED;
765792
}
766793

794+
/* MPTCP options have precedence over SACK for the limited TCP
795+
* option space because a MPTCP connection would be forced to
796+
* fall back to regular TCP if a required multipath option is
797+
* missing. SACK still gets a chance to use whatever space is
798+
* left.
799+
*/
800+
if (sk_is_mptcp(sk)) {
801+
unsigned int remaining = MAX_TCP_OPTION_SPACE - size;
802+
unsigned int opt_size = 0;
803+
804+
if (mptcp_established_options(sk, skb, &opt_size, remaining,
805+
&opts->mptcp)) {
806+
opts->options |= OPTION_MPTCP;
807+
size += opt_size;
808+
}
809+
}
810+
767811
eff_sacks = tp->rx_opt.num_sacks + tp->rx_opt.dsack;
768812
if (unlikely(eff_sacks)) {
769813
const unsigned int remaining = MAX_TCP_OPTION_SPACE - size;

net/ipv6/tcp_ipv6.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,8 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
238238
sin.sin_addr.s_addr = usin->sin6_addr.s6_addr32[3];
239239

240240
icsk->icsk_af_ops = &ipv6_mapped;
241+
if (sk_is_mptcp(sk))
242+
mptcp_handle_ipv6_mapped(sk, true);
241243
sk->sk_backlog_rcv = tcp_v4_do_rcv;
242244
#ifdef CONFIG_TCP_MD5SIG
243245
tp->af_specific = &tcp_sock_ipv6_mapped_specific;
@@ -248,6 +250,8 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
248250
if (err) {
249251
icsk->icsk_ext_hdr_len = exthdrlen;
250252
icsk->icsk_af_ops = &ipv6_specific;
253+
if (sk_is_mptcp(sk))
254+
mptcp_handle_ipv6_mapped(sk, false);
251255
sk->sk_backlog_rcv = tcp_v6_do_rcv;
252256
#ifdef CONFIG_TCP_MD5SIG
253257
tp->af_specific = &tcp_sock_ipv6_specific;
@@ -1203,6 +1207,8 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff *
12031207
newnp->saddr = newsk->sk_v6_rcv_saddr;
12041208

12051209
inet_csk(newsk)->icsk_af_ops = &ipv6_mapped;
1210+
if (sk_is_mptcp(newsk))
1211+
mptcp_handle_ipv6_mapped(newsk, true);
12061212
newsk->sk_backlog_rcv = tcp_v4_do_rcv;
12071213
#ifdef CONFIG_TCP_MD5SIG
12081214
newtp->af_specific = &tcp_sock_ipv6_mapped_specific;

net/mptcp/options.c

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,114 @@ void mptcp_parse_option(const unsigned char *ptr, int opsize,
7272
}
7373
}
7474

75+
void mptcp_get_options(const struct sk_buff *skb,
76+
struct tcp_options_received *opt_rx)
77+
{
78+
const unsigned char *ptr;
79+
const struct tcphdr *th = tcp_hdr(skb);
80+
int length = (th->doff * 4) - sizeof(struct tcphdr);
81+
82+
ptr = (const unsigned char *)(th + 1);
83+
84+
while (length > 0) {
85+
int opcode = *ptr++;
86+
int opsize;
87+
88+
switch (opcode) {
89+
case TCPOPT_EOL:
90+
return;
91+
case TCPOPT_NOP: /* Ref: RFC 793 section 3.1 */
92+
length--;
93+
continue;
94+
default:
95+
opsize = *ptr++;
96+
if (opsize < 2) /* "silly options" */
97+
return;
98+
if (opsize > length)
99+
return; /* don't parse partial options */
100+
if (opcode == TCPOPT_MPTCP)
101+
mptcp_parse_option(ptr, opsize, opt_rx);
102+
ptr += opsize - 2;
103+
length -= opsize;
104+
}
105+
}
106+
}
107+
108+
bool mptcp_syn_options(struct sock *sk, unsigned int *size,
109+
struct mptcp_out_options *opts)
110+
{
111+
struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(sk);
112+
113+
if (subflow->request_mptcp) {
114+
pr_debug("local_key=%llu", subflow->local_key);
115+
opts->suboptions = OPTION_MPTCP_MPC_SYN;
116+
opts->sndr_key = subflow->local_key;
117+
*size = TCPOLEN_MPTCP_MPC_SYN;
118+
return true;
119+
}
120+
return false;
121+
}
122+
123+
void mptcp_rcv_synsent(struct sock *sk)
124+
{
125+
struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(sk);
126+
struct tcp_sock *tp = tcp_sk(sk);
127+
128+
pr_debug("subflow=%p", subflow);
129+
if (subflow->request_mptcp && tp->rx_opt.mptcp.mp_capable) {
130+
subflow->mp_capable = 1;
131+
subflow->remote_key = tp->rx_opt.mptcp.sndr_key;
132+
} else {
133+
tcp_sk(sk)->is_mptcp = 0;
134+
}
135+
}
136+
137+
bool mptcp_established_options(struct sock *sk, struct sk_buff *skb,
138+
unsigned int *size, unsigned int remaining,
139+
struct mptcp_out_options *opts)
140+
{
141+
struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(sk);
142+
143+
if (subflow->mp_capable && !subflow->fourth_ack) {
144+
opts->suboptions = OPTION_MPTCP_MPC_ACK;
145+
opts->sndr_key = subflow->local_key;
146+
opts->rcvr_key = subflow->remote_key;
147+
*size = TCPOLEN_MPTCP_MPC_ACK;
148+
subflow->fourth_ack = 1;
149+
pr_debug("subflow=%p, local_key=%llu, remote_key=%llu",
150+
subflow, subflow->local_key, subflow->remote_key);
151+
return true;
152+
}
153+
return false;
154+
}
155+
156+
bool mptcp_synack_options(const struct request_sock *req, unsigned int *size,
157+
struct mptcp_out_options *opts)
158+
{
159+
struct mptcp_subflow_request_sock *subflow_req = mptcp_subflow_rsk(req);
160+
161+
if (subflow_req->mp_capable) {
162+
opts->suboptions = OPTION_MPTCP_MPC_SYNACK;
163+
opts->sndr_key = subflow_req->local_key;
164+
*size = TCPOLEN_MPTCP_MPC_SYNACK;
165+
pr_debug("subflow_req=%p, local_key=%llu",
166+
subflow_req, subflow_req->local_key);
167+
return true;
168+
}
169+
return false;
170+
}
171+
75172
void mptcp_write_options(__be32 *ptr, struct mptcp_out_options *opts)
76173
{
77174
if ((OPTION_MPTCP_MPC_SYN |
175+
OPTION_MPTCP_MPC_SYNACK |
78176
OPTION_MPTCP_MPC_ACK) & opts->suboptions) {
79177
u8 len;
80178

81179
if (OPTION_MPTCP_MPC_SYN & opts->suboptions)
82180
len = TCPOLEN_MPTCP_MPC_SYN;
181+
else if (OPTION_MPTCP_MPC_SYNACK & opts->suboptions)
182+
len = TCPOLEN_MPTCP_MPC_SYNACK;
83183
else
84184
len = TCPOLEN_MPTCP_MPC_ACK;
85185

0 commit comments

Comments
 (0)