Commit | Line | Data |
---|---|---|
0abdde82 PA |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Multipath TCP | |
3 | * | |
4 | * Copyright (c) 2021, Red Hat. | |
5 | */ | |
6 | ||
7 | #define pr_fmt(fmt) "MPTCP: " fmt | |
8 | ||
9 | #include <linux/kernel.h> | |
10 | #include <linux/module.h> | |
11 | #include <net/sock.h> | |
12 | #include <net/protocol.h> | |
13 | #include <net/tcp.h> | |
14 | #include <net/mptcp.h> | |
15 | #include "protocol.h" | |
16 | ||
06f15cee FW |
17 | #define MIN_INFO_OPTLEN_SIZE 16 |
18 | ||
0abdde82 PA |
19 | static struct sock *__mptcp_tcp_fallback(struct mptcp_sock *msk) |
20 | { | |
21 | sock_owned_by_me((const struct sock *)msk); | |
22 | ||
23 | if (likely(!__mptcp_check_fallback(msk))) | |
24 | return NULL; | |
25 | ||
26 | return msk->first; | |
27 | } | |
28 | ||
df00b087 FW |
29 | static u32 sockopt_seq_reset(const struct sock *sk) |
30 | { | |
31 | sock_owned_by_me(sk); | |
32 | ||
33 | /* Highbits contain state. Allows to distinguish sockopt_seq | |
34 | * of listener and established: | |
35 | * s0 = new_listener() | |
36 | * sockopt(s0) - seq is 1 | |
37 | * s1 = accept(s0) - s1 inherits seq 1 if listener sk (s0) | |
38 | * sockopt(s0) - seq increments to 2 on s0 | |
39 | * sockopt(s1) // seq increments to 2 on s1 (different option) | |
40 | * new ssk completes join, inherits options from s0 // seq 2 | |
41 | * Needs sync from mptcp join logic, but ssk->seq == msk->seq | |
42 | * | |
43 | * Set High order bits to sk_state so ssk->seq == msk->seq test | |
44 | * will fail. | |
45 | */ | |
46 | ||
47 | return (u32)sk->sk_state << 24u; | |
48 | } | |
49 | ||
1b3e7ede FW |
50 | static void sockopt_seq_inc(struct mptcp_sock *msk) |
51 | { | |
52 | u32 seq = (msk->setsockopt_seq + 1) & 0x00ffffff; | |
53 | ||
54 | msk->setsockopt_seq = sockopt_seq_reset((struct sock *)msk) + seq; | |
55 | } | |
56 | ||
57 | static int mptcp_get_int_option(struct mptcp_sock *msk, sockptr_t optval, | |
58 | unsigned int optlen, int *val) | |
59 | { | |
60 | if (optlen < sizeof(int)) | |
61 | return -EINVAL; | |
62 | ||
63 | if (copy_from_sockptr(val, optval, sizeof(*val))) | |
64 | return -EFAULT; | |
65 | ||
66 | return 0; | |
67 | } | |
68 | ||
69 | static void mptcp_sol_socket_sync_intval(struct mptcp_sock *msk, int optname, int val) | |
70 | { | |
71 | struct mptcp_subflow_context *subflow; | |
72 | struct sock *sk = (struct sock *)msk; | |
73 | ||
74 | lock_sock(sk); | |
75 | sockopt_seq_inc(msk); | |
76 | ||
77 | mptcp_for_each_subflow(msk, subflow) { | |
78 | struct sock *ssk = mptcp_subflow_tcp_sock(subflow); | |
79 | bool slow = lock_sock_fast(ssk); | |
80 | ||
81 | switch (optname) { | |
a03c99b2 FW |
82 | case SO_DEBUG: |
83 | sock_valbool_flag(ssk, SOCK_DBG, !!val); | |
84 | break; | |
1b3e7ede FW |
85 | case SO_KEEPALIVE: |
86 | if (ssk->sk_prot->keepalive) | |
87 | ssk->sk_prot->keepalive(ssk, !!val); | |
88 | sock_valbool_flag(ssk, SOCK_KEEPOPEN, !!val); | |
89 | break; | |
90 | case SO_PRIORITY: | |
91 | ssk->sk_priority = val; | |
92 | break; | |
5d0a6bc8 FW |
93 | case SO_SNDBUF: |
94 | case SO_SNDBUFFORCE: | |
95 | ssk->sk_userlocks |= SOCK_SNDBUF_LOCK; | |
96 | WRITE_ONCE(ssk->sk_sndbuf, sk->sk_sndbuf); | |
97 | break; | |
98 | case SO_RCVBUF: | |
99 | case SO_RCVBUFFORCE: | |
100 | ssk->sk_userlocks |= SOCK_RCVBUF_LOCK; | |
101 | WRITE_ONCE(ssk->sk_rcvbuf, sk->sk_rcvbuf); | |
102 | break; | |
36704413 FW |
103 | case SO_MARK: |
104 | if (READ_ONCE(ssk->sk_mark) != sk->sk_mark) { | |
105 | ssk->sk_mark = sk->sk_mark; | |
106 | sk_dst_reset(ssk); | |
107 | } | |
108 | break; | |
6f0d7198 FW |
109 | case SO_INCOMING_CPU: |
110 | WRITE_ONCE(ssk->sk_incoming_cpu, val); | |
111 | break; | |
1b3e7ede FW |
112 | } |
113 | ||
114 | subflow->setsockopt_seq = msk->setsockopt_seq; | |
115 | unlock_sock_fast(ssk, slow); | |
116 | } | |
117 | ||
118 | release_sock(sk); | |
119 | } | |
120 | ||
121 | static int mptcp_sol_socket_intval(struct mptcp_sock *msk, int optname, int val) | |
122 | { | |
123 | sockptr_t optval = KERNEL_SOCKPTR(&val); | |
124 | struct sock *sk = (struct sock *)msk; | |
125 | int ret; | |
126 | ||
127 | ret = sock_setsockopt(sk->sk_socket, SOL_SOCKET, optname, | |
128 | optval, sizeof(val)); | |
129 | if (ret) | |
130 | return ret; | |
131 | ||
132 | mptcp_sol_socket_sync_intval(msk, optname, val); | |
133 | return 0; | |
134 | } | |
135 | ||
6f0d7198 FW |
136 | static void mptcp_so_incoming_cpu(struct mptcp_sock *msk, int val) |
137 | { | |
138 | struct sock *sk = (struct sock *)msk; | |
139 | ||
140 | WRITE_ONCE(sk->sk_incoming_cpu, val); | |
141 | ||
142 | mptcp_sol_socket_sync_intval(msk, SO_INCOMING_CPU, val); | |
143 | } | |
144 | ||
9061f24b FW |
145 | static int mptcp_setsockopt_sol_socket_tstamp(struct mptcp_sock *msk, int optname, int val) |
146 | { | |
147 | sockptr_t optval = KERNEL_SOCKPTR(&val); | |
148 | struct mptcp_subflow_context *subflow; | |
149 | struct sock *sk = (struct sock *)msk; | |
150 | int ret; | |
151 | ||
152 | ret = sock_setsockopt(sk->sk_socket, SOL_SOCKET, optname, | |
153 | optval, sizeof(val)); | |
154 | if (ret) | |
155 | return ret; | |
156 | ||
157 | lock_sock(sk); | |
158 | mptcp_for_each_subflow(msk, subflow) { | |
159 | struct sock *ssk = mptcp_subflow_tcp_sock(subflow); | |
160 | bool slow = lock_sock_fast(ssk); | |
161 | ||
6c9a0a0f | 162 | sock_set_timestamp(sk, optname, !!val); |
9061f24b FW |
163 | unlock_sock_fast(ssk, slow); |
164 | } | |
165 | ||
166 | release_sock(sk); | |
167 | return 0; | |
168 | } | |
169 | ||
1b3e7ede | 170 | static int mptcp_setsockopt_sol_socket_int(struct mptcp_sock *msk, int optname, |
6c9a0a0f YL |
171 | sockptr_t optval, |
172 | unsigned int optlen) | |
1b3e7ede FW |
173 | { |
174 | int val, ret; | |
175 | ||
176 | ret = mptcp_get_int_option(msk, optval, optlen, &val); | |
177 | if (ret) | |
178 | return ret; | |
179 | ||
180 | switch (optname) { | |
181 | case SO_KEEPALIVE: | |
182 | mptcp_sol_socket_sync_intval(msk, optname, val); | |
183 | return 0; | |
a03c99b2 | 184 | case SO_DEBUG: |
36704413 | 185 | case SO_MARK: |
1b3e7ede | 186 | case SO_PRIORITY: |
5d0a6bc8 FW |
187 | case SO_SNDBUF: |
188 | case SO_SNDBUFFORCE: | |
189 | case SO_RCVBUF: | |
190 | case SO_RCVBUFFORCE: | |
1b3e7ede | 191 | return mptcp_sol_socket_intval(msk, optname, val); |
6f0d7198 FW |
192 | case SO_INCOMING_CPU: |
193 | mptcp_so_incoming_cpu(msk, val); | |
194 | return 0; | |
9061f24b FW |
195 | case SO_TIMESTAMP_OLD: |
196 | case SO_TIMESTAMP_NEW: | |
197 | case SO_TIMESTAMPNS_OLD: | |
198 | case SO_TIMESTAMPNS_NEW: | |
9061f24b | 199 | return mptcp_setsockopt_sol_socket_tstamp(msk, optname, val); |
1b3e7ede FW |
200 | } |
201 | ||
202 | return -ENOPROTOOPT; | |
203 | } | |
204 | ||
6c9a0a0f YL |
205 | static int mptcp_setsockopt_sol_socket_timestamping(struct mptcp_sock *msk, |
206 | int optname, | |
207 | sockptr_t optval, | |
208 | unsigned int optlen) | |
209 | { | |
210 | struct mptcp_subflow_context *subflow; | |
211 | struct sock *sk = (struct sock *)msk; | |
d463126e YL |
212 | struct so_timestamping timestamping; |
213 | int ret; | |
6c9a0a0f | 214 | |
d463126e YL |
215 | if (optlen == sizeof(timestamping)) { |
216 | if (copy_from_sockptr(×tamping, optval, | |
217 | sizeof(timestamping))) | |
218 | return -EFAULT; | |
219 | } else if (optlen == sizeof(int)) { | |
220 | memset(×tamping, 0, sizeof(timestamping)); | |
221 | ||
222 | if (copy_from_sockptr(×tamping.flags, optval, sizeof(int))) | |
223 | return -EFAULT; | |
224 | } else { | |
225 | return -EINVAL; | |
226 | } | |
6c9a0a0f YL |
227 | |
228 | ret = sock_setsockopt(sk->sk_socket, SOL_SOCKET, optname, | |
d463126e YL |
229 | KERNEL_SOCKPTR(×tamping), |
230 | sizeof(timestamping)); | |
6c9a0a0f YL |
231 | if (ret) |
232 | return ret; | |
233 | ||
234 | lock_sock(sk); | |
235 | ||
236 | mptcp_for_each_subflow(msk, subflow) { | |
237 | struct sock *ssk = mptcp_subflow_tcp_sock(subflow); | |
238 | bool slow = lock_sock_fast(ssk); | |
239 | ||
d463126e | 240 | sock_set_timestamping(sk, optname, timestamping); |
6c9a0a0f YL |
241 | unlock_sock_fast(ssk, slow); |
242 | } | |
243 | ||
244 | release_sock(sk); | |
245 | ||
246 | return 0; | |
247 | } | |
248 | ||
268b1238 FW |
249 | static int mptcp_setsockopt_sol_socket_linger(struct mptcp_sock *msk, sockptr_t optval, |
250 | unsigned int optlen) | |
251 | { | |
252 | struct mptcp_subflow_context *subflow; | |
253 | struct sock *sk = (struct sock *)msk; | |
254 | struct linger ling; | |
255 | sockptr_t kopt; | |
256 | int ret; | |
257 | ||
258 | if (optlen < sizeof(ling)) | |
259 | return -EINVAL; | |
260 | ||
261 | if (copy_from_sockptr(&ling, optval, sizeof(ling))) | |
262 | return -EFAULT; | |
263 | ||
264 | kopt = KERNEL_SOCKPTR(&ling); | |
265 | ret = sock_setsockopt(sk->sk_socket, SOL_SOCKET, SO_LINGER, kopt, sizeof(ling)); | |
266 | if (ret) | |
267 | return ret; | |
268 | ||
269 | lock_sock(sk); | |
270 | sockopt_seq_inc(msk); | |
271 | mptcp_for_each_subflow(msk, subflow) { | |
272 | struct sock *ssk = mptcp_subflow_tcp_sock(subflow); | |
273 | bool slow = lock_sock_fast(ssk); | |
274 | ||
275 | if (!ling.l_onoff) { | |
276 | sock_reset_flag(ssk, SOCK_LINGER); | |
277 | } else { | |
278 | ssk->sk_lingertime = sk->sk_lingertime; | |
279 | sock_set_flag(ssk, SOCK_LINGER); | |
280 | } | |
281 | ||
282 | subflow->setsockopt_seq = msk->setsockopt_seq; | |
283 | unlock_sock_fast(ssk, slow); | |
284 | } | |
285 | ||
286 | release_sock(sk); | |
287 | return 0; | |
288 | } | |
289 | ||
0abdde82 PA |
290 | static int mptcp_setsockopt_sol_socket(struct mptcp_sock *msk, int optname, |
291 | sockptr_t optval, unsigned int optlen) | |
292 | { | |
293 | struct sock *sk = (struct sock *)msk; | |
294 | struct socket *ssock; | |
295 | int ret; | |
296 | ||
297 | switch (optname) { | |
298 | case SO_REUSEPORT: | |
299 | case SO_REUSEADDR: | |
5d0a6bc8 FW |
300 | case SO_BINDTODEVICE: |
301 | case SO_BINDTOIFINDEX: | |
0abdde82 PA |
302 | lock_sock(sk); |
303 | ssock = __mptcp_nmpc_socket(msk); | |
304 | if (!ssock) { | |
305 | release_sock(sk); | |
306 | return -EINVAL; | |
307 | } | |
308 | ||
309 | ret = sock_setsockopt(ssock, SOL_SOCKET, optname, optval, optlen); | |
310 | if (ret == 0) { | |
311 | if (optname == SO_REUSEPORT) | |
312 | sk->sk_reuseport = ssock->sk->sk_reuseport; | |
313 | else if (optname == SO_REUSEADDR) | |
314 | sk->sk_reuse = ssock->sk->sk_reuse; | |
5d0a6bc8 FW |
315 | else if (optname == SO_BINDTODEVICE) |
316 | sk->sk_bound_dev_if = ssock->sk->sk_bound_dev_if; | |
317 | else if (optname == SO_BINDTOIFINDEX) | |
318 | sk->sk_bound_dev_if = ssock->sk->sk_bound_dev_if; | |
0abdde82 PA |
319 | } |
320 | release_sock(sk); | |
321 | return ret; | |
1b3e7ede FW |
322 | case SO_KEEPALIVE: |
323 | case SO_PRIORITY: | |
5d0a6bc8 FW |
324 | case SO_SNDBUF: |
325 | case SO_SNDBUFFORCE: | |
326 | case SO_RCVBUF: | |
327 | case SO_RCVBUFFORCE: | |
36704413 | 328 | case SO_MARK: |
6f0d7198 | 329 | case SO_INCOMING_CPU: |
a03c99b2 | 330 | case SO_DEBUG: |
9061f24b FW |
331 | case SO_TIMESTAMP_OLD: |
332 | case SO_TIMESTAMP_NEW: | |
333 | case SO_TIMESTAMPNS_OLD: | |
334 | case SO_TIMESTAMPNS_NEW: | |
6c9a0a0f YL |
335 | return mptcp_setsockopt_sol_socket_int(msk, optname, optval, |
336 | optlen); | |
9061f24b FW |
337 | case SO_TIMESTAMPING_OLD: |
338 | case SO_TIMESTAMPING_NEW: | |
6c9a0a0f YL |
339 | return mptcp_setsockopt_sol_socket_timestamping(msk, optname, |
340 | optval, optlen); | |
268b1238 FW |
341 | case SO_LINGER: |
342 | return mptcp_setsockopt_sol_socket_linger(msk, optval, optlen); | |
7a009a70 FW |
343 | case SO_RCVLOWAT: |
344 | case SO_RCVTIMEO_OLD: | |
345 | case SO_RCVTIMEO_NEW: | |
346 | case SO_BUSY_POLL: | |
347 | case SO_PREFER_BUSY_POLL: | |
348 | case SO_BUSY_POLL_BUDGET: | |
349 | /* No need to copy: only relevant for msk */ | |
350 | return sock_setsockopt(sk->sk_socket, SOL_SOCKET, optname, optval, optlen); | |
a03c99b2 FW |
351 | case SO_NO_CHECK: |
352 | case SO_DONTROUTE: | |
353 | case SO_BROADCAST: | |
354 | case SO_BSDCOMPAT: | |
355 | case SO_PASSCRED: | |
356 | case SO_PASSSEC: | |
357 | case SO_RXQ_OVFL: | |
358 | case SO_WIFI_STATUS: | |
359 | case SO_NOFCS: | |
360 | case SO_SELECT_ERR_QUEUE: | |
361 | return 0; | |
0abdde82 PA |
362 | } |
363 | ||
7a009a70 FW |
364 | /* SO_OOBINLINE is not supported, let's avoid the related mess |
365 | * SO_ATTACH_FILTER, SO_ATTACH_BPF, SO_ATTACH_REUSEPORT_CBPF, | |
366 | * SO_DETACH_REUSEPORT_BPF, SO_DETACH_FILTER, SO_LOCK_FILTER, | |
367 | * we must be careful with subflows | |
368 | * | |
369 | * SO_ATTACH_REUSEPORT_EBPF is not supported, at it checks | |
370 | * explicitly the sk_protocol field | |
371 | * | |
372 | * SO_PEEK_OFF is unsupported, as it is for plain TCP | |
373 | * SO_MAX_PACING_RATE is unsupported, we must be careful with subflows | |
374 | * SO_CNX_ADVICE is currently unsupported, could possibly be relevant, | |
375 | * but likely needs careful design | |
376 | * | |
377 | * SO_ZEROCOPY is currently unsupported, TODO in sndmsg | |
378 | * SO_TXTIME is currently unsupported | |
379 | */ | |
380 | ||
381 | return -EOPNOTSUPP; | |
0abdde82 PA |
382 | } |
383 | ||
384 | static int mptcp_setsockopt_v6(struct mptcp_sock *msk, int optname, | |
385 | sockptr_t optval, unsigned int optlen) | |
386 | { | |
387 | struct sock *sk = (struct sock *)msk; | |
388 | int ret = -EOPNOTSUPP; | |
389 | struct socket *ssock; | |
390 | ||
391 | switch (optname) { | |
392 | case IPV6_V6ONLY: | |
393 | lock_sock(sk); | |
394 | ssock = __mptcp_nmpc_socket(msk); | |
395 | if (!ssock) { | |
396 | release_sock(sk); | |
397 | return -EINVAL; | |
398 | } | |
399 | ||
400 | ret = tcp_setsockopt(ssock->sk, SOL_IPV6, optname, optval, optlen); | |
401 | if (ret == 0) | |
402 | sk->sk_ipv6only = ssock->sk->sk_ipv6only; | |
403 | ||
404 | release_sock(sk); | |
405 | break; | |
406 | } | |
407 | ||
408 | return ret; | |
409 | } | |
410 | ||
d9e4c129 PA |
411 | static bool mptcp_supported_sockopt(int level, int optname) |
412 | { | |
d9e4c129 PA |
413 | if (level == SOL_IP) { |
414 | switch (optname) { | |
415 | /* should work fine */ | |
416 | case IP_FREEBIND: | |
417 | case IP_TRANSPARENT: | |
418 | ||
419 | /* the following are control cmsg related */ | |
420 | case IP_PKTINFO: | |
421 | case IP_RECVTTL: | |
422 | case IP_RECVTOS: | |
423 | case IP_RECVOPTS: | |
424 | case IP_RETOPTS: | |
425 | case IP_PASSSEC: | |
426 | case IP_RECVORIGDSTADDR: | |
427 | case IP_CHECKSUM: | |
428 | case IP_RECVFRAGSIZE: | |
429 | ||
430 | /* common stuff that need some love */ | |
431 | case IP_TOS: | |
432 | case IP_TTL: | |
433 | case IP_BIND_ADDRESS_NO_PORT: | |
434 | case IP_MTU_DISCOVER: | |
435 | case IP_RECVERR: | |
436 | ||
437 | /* possibly less common may deserve some love */ | |
438 | case IP_MINTTL: | |
439 | ||
440 | /* the following is apparently a no-op for plain TCP */ | |
441 | case IP_RECVERR_RFC4884: | |
442 | return true; | |
443 | } | |
444 | ||
445 | /* IP_OPTIONS is not supported, needs subflow care */ | |
446 | /* IP_HDRINCL, IP_NODEFRAG are not supported, RAW specific */ | |
447 | /* IP_MULTICAST_TTL, IP_MULTICAST_LOOP, IP_UNICAST_IF, | |
448 | * IP_ADD_MEMBERSHIP, IP_ADD_SOURCE_MEMBERSHIP, IP_DROP_MEMBERSHIP, | |
449 | * IP_DROP_SOURCE_MEMBERSHIP, IP_BLOCK_SOURCE, IP_UNBLOCK_SOURCE, | |
450 | * MCAST_JOIN_GROUP, MCAST_LEAVE_GROUP MCAST_JOIN_SOURCE_GROUP, | |
451 | * MCAST_LEAVE_SOURCE_GROUP, MCAST_BLOCK_SOURCE, MCAST_UNBLOCK_SOURCE, | |
452 | * MCAST_MSFILTER, IP_MULTICAST_ALL are not supported, better not deal | |
453 | * with mcast stuff | |
454 | */ | |
455 | /* IP_IPSEC_POLICY, IP_XFRM_POLICY are nut supported, unrelated here */ | |
456 | return false; | |
457 | } | |
458 | if (level == SOL_IPV6) { | |
459 | switch (optname) { | |
460 | case IPV6_V6ONLY: | |
461 | ||
462 | /* the following are control cmsg related */ | |
463 | case IPV6_RECVPKTINFO: | |
464 | case IPV6_2292PKTINFO: | |
465 | case IPV6_RECVHOPLIMIT: | |
466 | case IPV6_2292HOPLIMIT: | |
467 | case IPV6_RECVRTHDR: | |
468 | case IPV6_2292RTHDR: | |
469 | case IPV6_RECVHOPOPTS: | |
470 | case IPV6_2292HOPOPTS: | |
471 | case IPV6_RECVDSTOPTS: | |
472 | case IPV6_2292DSTOPTS: | |
473 | case IPV6_RECVTCLASS: | |
474 | case IPV6_FLOWINFO: | |
475 | case IPV6_RECVPATHMTU: | |
476 | case IPV6_RECVORIGDSTADDR: | |
477 | case IPV6_RECVFRAGSIZE: | |
478 | ||
479 | /* the following ones need some love but are quite common */ | |
480 | case IPV6_TCLASS: | |
481 | case IPV6_TRANSPARENT: | |
482 | case IPV6_FREEBIND: | |
483 | case IPV6_PKTINFO: | |
484 | case IPV6_2292PKTOPTIONS: | |
485 | case IPV6_UNICAST_HOPS: | |
486 | case IPV6_MTU_DISCOVER: | |
487 | case IPV6_MTU: | |
488 | case IPV6_RECVERR: | |
489 | case IPV6_FLOWINFO_SEND: | |
490 | case IPV6_FLOWLABEL_MGR: | |
491 | case IPV6_MINHOPCOUNT: | |
492 | case IPV6_DONTFRAG: | |
493 | case IPV6_AUTOFLOWLABEL: | |
494 | ||
495 | /* the following one is a no-op for plain TCP */ | |
496 | case IPV6_RECVERR_RFC4884: | |
497 | return true; | |
498 | } | |
499 | ||
500 | /* IPV6_HOPOPTS, IPV6_RTHDRDSTOPTS, IPV6_RTHDR, IPV6_DSTOPTS are | |
501 | * not supported | |
502 | */ | |
503 | /* IPV6_MULTICAST_HOPS, IPV6_MULTICAST_LOOP, IPV6_UNICAST_IF, | |
504 | * IPV6_MULTICAST_IF, IPV6_ADDRFORM, | |
505 | * IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP, IPV6_JOIN_ANYCAST, | |
506 | * IPV6_LEAVE_ANYCAST, IPV6_MULTICAST_ALL, MCAST_JOIN_GROUP, MCAST_LEAVE_GROUP, | |
507 | * MCAST_JOIN_SOURCE_GROUP, MCAST_LEAVE_SOURCE_GROUP, | |
508 | * MCAST_BLOCK_SOURCE, MCAST_UNBLOCK_SOURCE, MCAST_MSFILTER | |
509 | * are not supported better not deal with mcast | |
510 | */ | |
511 | /* IPV6_ROUTER_ALERT, IPV6_ROUTER_ALERT_ISOLATE are not supported, since are evil */ | |
512 | ||
513 | /* IPV6_IPSEC_POLICY, IPV6_XFRM_POLICY are not supported */ | |
514 | /* IPV6_ADDR_PREFERENCES is not supported, we must be careful with subflows */ | |
515 | return false; | |
516 | } | |
517 | if (level == SOL_TCP) { | |
518 | switch (optname) { | |
519 | /* the following are no-op or should work just fine */ | |
520 | case TCP_THIN_DUPACK: | |
521 | case TCP_DEFER_ACCEPT: | |
522 | ||
523 | /* the following need some love */ | |
524 | case TCP_MAXSEG: | |
525 | case TCP_NODELAY: | |
526 | case TCP_THIN_LINEAR_TIMEOUTS: | |
527 | case TCP_CONGESTION: | |
528 | case TCP_ULP: | |
529 | case TCP_CORK: | |
530 | case TCP_KEEPIDLE: | |
531 | case TCP_KEEPINTVL: | |
532 | case TCP_KEEPCNT: | |
533 | case TCP_SYNCNT: | |
534 | case TCP_SAVE_SYN: | |
535 | case TCP_LINGER2: | |
536 | case TCP_WINDOW_CLAMP: | |
537 | case TCP_QUICKACK: | |
538 | case TCP_USER_TIMEOUT: | |
539 | case TCP_TIMESTAMP: | |
540 | case TCP_NOTSENT_LOWAT: | |
541 | case TCP_TX_DELAY: | |
542 | return true; | |
543 | } | |
544 | ||
545 | /* TCP_MD5SIG, TCP_MD5SIG_EXT are not supported, MD5 is not compatible with MPTCP */ | |
546 | ||
547 | /* TCP_REPAIR, TCP_REPAIR_QUEUE, TCP_QUEUE_SEQ, TCP_REPAIR_OPTIONS, | |
548 | * TCP_REPAIR_WINDOW are not supported, better avoid this mess | |
549 | */ | |
550 | /* TCP_FASTOPEN_KEY, TCP_FASTOPEN TCP_FASTOPEN_CONNECT, TCP_FASTOPEN_NO_COOKIE, | |
551 | * are not supported fastopen is currently unsupported | |
552 | */ | |
553 | /* TCP_INQ is currently unsupported, needs some recvmsg work */ | |
554 | } | |
555 | return false; | |
556 | } | |
557 | ||
aa1fbd94 FW |
558 | static int mptcp_setsockopt_sol_tcp_congestion(struct mptcp_sock *msk, sockptr_t optval, |
559 | unsigned int optlen) | |
560 | { | |
561 | struct mptcp_subflow_context *subflow; | |
562 | struct sock *sk = (struct sock *)msk; | |
563 | char name[TCP_CA_NAME_MAX]; | |
564 | bool cap_net_admin; | |
565 | int ret; | |
566 | ||
567 | if (optlen < 1) | |
568 | return -EINVAL; | |
569 | ||
570 | ret = strncpy_from_sockptr(name, optval, | |
571 | min_t(long, TCP_CA_NAME_MAX - 1, optlen)); | |
572 | if (ret < 0) | |
573 | return -EFAULT; | |
574 | ||
575 | name[ret] = 0; | |
576 | ||
577 | cap_net_admin = ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN); | |
578 | ||
579 | ret = 0; | |
580 | lock_sock(sk); | |
581 | sockopt_seq_inc(msk); | |
582 | mptcp_for_each_subflow(msk, subflow) { | |
583 | struct sock *ssk = mptcp_subflow_tcp_sock(subflow); | |
584 | int err; | |
585 | ||
586 | lock_sock(ssk); | |
587 | err = tcp_set_congestion_control(ssk, name, true, cap_net_admin); | |
588 | if (err < 0 && ret == 0) | |
589 | ret = err; | |
590 | subflow->setsockopt_seq = msk->setsockopt_seq; | |
591 | release_sock(ssk); | |
592 | } | |
593 | ||
594 | if (ret == 0) | |
20b5759f | 595 | strcpy(msk->ca_name, name); |
aa1fbd94 FW |
596 | |
597 | release_sock(sk); | |
598 | return ret; | |
599 | } | |
600 | ||
601 | static int mptcp_setsockopt_sol_tcp(struct mptcp_sock *msk, int optname, | |
602 | sockptr_t optval, unsigned int optlen) | |
603 | { | |
604 | switch (optname) { | |
605 | case TCP_ULP: | |
606 | return -EOPNOTSUPP; | |
607 | case TCP_CONGESTION: | |
608 | return mptcp_setsockopt_sol_tcp_congestion(msk, optval, optlen); | |
609 | } | |
610 | ||
611 | return -EOPNOTSUPP; | |
612 | } | |
613 | ||
0abdde82 PA |
614 | int mptcp_setsockopt(struct sock *sk, int level, int optname, |
615 | sockptr_t optval, unsigned int optlen) | |
616 | { | |
617 | struct mptcp_sock *msk = mptcp_sk(sk); | |
618 | struct sock *ssk; | |
619 | ||
620 | pr_debug("msk=%p", msk); | |
621 | ||
622 | if (level == SOL_SOCKET) | |
623 | return mptcp_setsockopt_sol_socket(msk, optname, optval, optlen); | |
624 | ||
7a009a70 FW |
625 | if (!mptcp_supported_sockopt(level, optname)) |
626 | return -ENOPROTOOPT; | |
627 | ||
0abdde82 PA |
628 | /* @@ the meaning of setsockopt() when the socket is connected and |
629 | * there are multiple subflows is not yet defined. It is up to the | |
630 | * MPTCP-level socket to configure the subflows until the subflow | |
631 | * is in TCP fallback, when TCP socket options are passed through | |
632 | * to the one remaining subflow. | |
633 | */ | |
634 | lock_sock(sk); | |
635 | ssk = __mptcp_tcp_fallback(msk); | |
636 | release_sock(sk); | |
637 | if (ssk) | |
638 | return tcp_setsockopt(ssk, level, optname, optval, optlen); | |
639 | ||
640 | if (level == SOL_IPV6) | |
641 | return mptcp_setsockopt_v6(msk, optname, optval, optlen); | |
642 | ||
aa1fbd94 FW |
643 | if (level == SOL_TCP) |
644 | return mptcp_setsockopt_sol_tcp(msk, optname, optval, optlen); | |
645 | ||
646 | return -EOPNOTSUPP; | |
647 | } | |
648 | ||
649 | static int mptcp_getsockopt_first_sf_only(struct mptcp_sock *msk, int level, int optname, | |
650 | char __user *optval, int __user *optlen) | |
651 | { | |
652 | struct sock *sk = (struct sock *)msk; | |
653 | struct socket *ssock; | |
654 | int ret = -EINVAL; | |
655 | struct sock *ssk; | |
656 | ||
657 | lock_sock(sk); | |
658 | ssk = msk->first; | |
659 | if (ssk) { | |
660 | ret = tcp_getsockopt(ssk, level, optname, optval, optlen); | |
661 | goto out; | |
662 | } | |
663 | ||
664 | ssock = __mptcp_nmpc_socket(msk); | |
665 | if (!ssock) | |
666 | goto out; | |
667 | ||
668 | ret = tcp_getsockopt(ssock->sk, level, optname, optval, optlen); | |
669 | ||
670 | out: | |
671 | release_sock(sk); | |
672 | return ret; | |
673 | } | |
674 | ||
61bc6e82 FW |
675 | void mptcp_diag_fill_info(struct mptcp_sock *msk, struct mptcp_info *info) |
676 | { | |
677 | struct sock *sk = &msk->sk.icsk_inet.sk; | |
61bc6e82 | 678 | u32 flags = 0; |
55c42fa7 | 679 | bool slow; |
61bc6e82 FW |
680 | u8 val; |
681 | ||
55c42fa7 FW |
682 | memset(info, 0, sizeof(*info)); |
683 | ||
684 | slow = lock_sock_fast(sk); | |
685 | ||
61bc6e82 FW |
686 | info->mptcpi_subflows = READ_ONCE(msk->pm.subflows); |
687 | info->mptcpi_add_addr_signal = READ_ONCE(msk->pm.add_addr_signaled); | |
688 | info->mptcpi_add_addr_accepted = READ_ONCE(msk->pm.add_addr_accepted); | |
689 | info->mptcpi_local_addr_used = READ_ONCE(msk->pm.local_addr_used); | |
690 | info->mptcpi_subflows_max = mptcp_pm_get_subflows_max(msk); | |
691 | val = mptcp_pm_get_add_addr_signal_max(msk); | |
692 | info->mptcpi_add_addr_signal_max = val; | |
693 | val = mptcp_pm_get_add_addr_accept_max(msk); | |
694 | info->mptcpi_add_addr_accepted_max = val; | |
695 | info->mptcpi_local_addr_max = mptcp_pm_get_local_addr_max(msk); | |
696 | if (test_bit(MPTCP_FALLBACK_DONE, &msk->flags)) | |
697 | flags |= MPTCP_INFO_FLAG_FALLBACK; | |
698 | if (READ_ONCE(msk->can_ack)) | |
699 | flags |= MPTCP_INFO_FLAG_REMOTE_KEY_RECEIVED; | |
700 | info->mptcpi_flags = flags; | |
701 | info->mptcpi_token = READ_ONCE(msk->token); | |
702 | info->mptcpi_write_seq = READ_ONCE(msk->write_seq); | |
703 | info->mptcpi_snd_una = READ_ONCE(msk->snd_una); | |
704 | info->mptcpi_rcv_nxt = READ_ONCE(msk->ack_seq); | |
705 | info->mptcpi_csum_enabled = READ_ONCE(msk->csum_enabled); | |
706 | ||
707 | unlock_sock_fast(sk, slow); | |
708 | } | |
709 | EXPORT_SYMBOL_GPL(mptcp_diag_fill_info); | |
710 | ||
55c42fa7 FW |
711 | static int mptcp_getsockopt_info(struct mptcp_sock *msk, char __user *optval, int __user *optlen) |
712 | { | |
713 | struct mptcp_info m_info; | |
714 | int len; | |
715 | ||
716 | if (get_user(len, optlen)) | |
717 | return -EFAULT; | |
718 | ||
719 | len = min_t(unsigned int, len, sizeof(struct mptcp_info)); | |
720 | ||
721 | mptcp_diag_fill_info(msk, &m_info); | |
722 | ||
723 | if (put_user(len, optlen)) | |
724 | return -EFAULT; | |
725 | ||
726 | if (copy_to_user(optval, &m_info, len)) | |
727 | return -EFAULT; | |
728 | ||
729 | return 0; | |
730 | } | |
731 | ||
06f15cee FW |
732 | static int mptcp_put_subflow_data(struct mptcp_subflow_data *sfd, |
733 | char __user *optval, | |
734 | u32 copied, | |
735 | int __user *optlen) | |
736 | { | |
737 | u32 copylen = min_t(u32, sfd->size_subflow_data, sizeof(*sfd)); | |
738 | ||
739 | if (copied) | |
740 | copied += sfd->size_subflow_data; | |
741 | else | |
742 | copied = copylen; | |
743 | ||
744 | if (put_user(copied, optlen)) | |
745 | return -EFAULT; | |
746 | ||
747 | if (copy_to_user(optval, sfd, copylen)) | |
748 | return -EFAULT; | |
749 | ||
750 | return 0; | |
751 | } | |
752 | ||
753 | static int mptcp_get_subflow_data(struct mptcp_subflow_data *sfd, | |
754 | char __user *optval, int __user *optlen) | |
755 | { | |
756 | int len, copylen; | |
757 | ||
758 | if (get_user(len, optlen)) | |
759 | return -EFAULT; | |
760 | ||
761 | /* if mptcp_subflow_data size is changed, need to adjust | |
762 | * this function to deal with programs using old version. | |
763 | */ | |
764 | BUILD_BUG_ON(sizeof(*sfd) != MIN_INFO_OPTLEN_SIZE); | |
765 | ||
766 | if (len < MIN_INFO_OPTLEN_SIZE) | |
767 | return -EINVAL; | |
768 | ||
769 | memset(sfd, 0, sizeof(*sfd)); | |
770 | ||
771 | copylen = min_t(unsigned int, len, sizeof(*sfd)); | |
772 | if (copy_from_user(sfd, optval, copylen)) | |
773 | return -EFAULT; | |
774 | ||
775 | /* size_subflow_data is u32, but len is signed */ | |
776 | if (sfd->size_subflow_data > INT_MAX || | |
777 | sfd->size_user > INT_MAX) | |
778 | return -EINVAL; | |
779 | ||
780 | if (sfd->size_subflow_data < MIN_INFO_OPTLEN_SIZE || | |
781 | sfd->size_subflow_data > len) | |
782 | return -EINVAL; | |
783 | ||
784 | if (sfd->num_subflows || sfd->size_kernel) | |
785 | return -EINVAL; | |
786 | ||
787 | return len - sfd->size_subflow_data; | |
788 | } | |
789 | ||
790 | static int mptcp_getsockopt_tcpinfo(struct mptcp_sock *msk, char __user *optval, | |
791 | int __user *optlen) | |
792 | { | |
793 | struct mptcp_subflow_context *subflow; | |
794 | struct sock *sk = &msk->sk.icsk_inet.sk; | |
795 | unsigned int sfcount = 0, copied = 0; | |
796 | struct mptcp_subflow_data sfd; | |
797 | char __user *infoptr; | |
798 | int len; | |
799 | ||
800 | len = mptcp_get_subflow_data(&sfd, optval, optlen); | |
801 | if (len < 0) | |
802 | return len; | |
803 | ||
804 | sfd.size_kernel = sizeof(struct tcp_info); | |
805 | sfd.size_user = min_t(unsigned int, sfd.size_user, | |
806 | sizeof(struct tcp_info)); | |
807 | ||
808 | infoptr = optval + sfd.size_subflow_data; | |
809 | ||
810 | lock_sock(sk); | |
811 | ||
812 | mptcp_for_each_subflow(msk, subflow) { | |
813 | struct sock *ssk = mptcp_subflow_tcp_sock(subflow); | |
814 | ||
815 | ++sfcount; | |
816 | ||
817 | if (len && len >= sfd.size_user) { | |
818 | struct tcp_info info; | |
819 | ||
820 | tcp_get_info(ssk, &info); | |
821 | ||
822 | if (copy_to_user(infoptr, &info, sfd.size_user)) { | |
823 | release_sock(sk); | |
824 | return -EFAULT; | |
825 | } | |
826 | ||
827 | infoptr += sfd.size_user; | |
828 | copied += sfd.size_user; | |
829 | len -= sfd.size_user; | |
830 | } | |
831 | } | |
832 | ||
833 | release_sock(sk); | |
834 | ||
835 | sfd.num_subflows = sfcount; | |
836 | ||
837 | if (mptcp_put_subflow_data(&sfd, optval, copied, optlen)) | |
838 | return -EFAULT; | |
839 | ||
840 | return 0; | |
841 | } | |
842 | ||
c11c5906 FW |
843 | static void mptcp_get_sub_addrs(const struct sock *sk, struct mptcp_subflow_addrs *a) |
844 | { | |
845 | struct inet_sock *inet = inet_sk(sk); | |
846 | ||
847 | memset(a, 0, sizeof(*a)); | |
848 | ||
849 | if (sk->sk_family == AF_INET) { | |
850 | a->sin_local.sin_family = AF_INET; | |
851 | a->sin_local.sin_port = inet->inet_sport; | |
852 | a->sin_local.sin_addr.s_addr = inet->inet_rcv_saddr; | |
853 | ||
854 | if (!a->sin_local.sin_addr.s_addr) | |
855 | a->sin_local.sin_addr.s_addr = inet->inet_saddr; | |
856 | ||
857 | a->sin_remote.sin_family = AF_INET; | |
858 | a->sin_remote.sin_port = inet->inet_dport; | |
859 | a->sin_remote.sin_addr.s_addr = inet->inet_daddr; | |
860 | #if IS_ENABLED(CONFIG_IPV6) | |
861 | } else if (sk->sk_family == AF_INET6) { | |
862 | const struct ipv6_pinfo *np = inet6_sk(sk); | |
863 | ||
864 | a->sin6_local.sin6_family = AF_INET6; | |
865 | a->sin6_local.sin6_port = inet->inet_sport; | |
866 | ||
867 | if (ipv6_addr_any(&sk->sk_v6_rcv_saddr)) | |
868 | a->sin6_local.sin6_addr = np->saddr; | |
869 | else | |
870 | a->sin6_local.sin6_addr = sk->sk_v6_rcv_saddr; | |
871 | ||
872 | a->sin6_remote.sin6_family = AF_INET6; | |
873 | a->sin6_remote.sin6_port = inet->inet_dport; | |
874 | a->sin6_remote.sin6_addr = sk->sk_v6_daddr; | |
875 | #endif | |
876 | } | |
877 | } | |
878 | ||
879 | static int mptcp_getsockopt_subflow_addrs(struct mptcp_sock *msk, char __user *optval, | |
880 | int __user *optlen) | |
881 | { | |
882 | struct sock *sk = &msk->sk.icsk_inet.sk; | |
883 | struct mptcp_subflow_context *subflow; | |
884 | unsigned int sfcount = 0, copied = 0; | |
885 | struct mptcp_subflow_data sfd; | |
886 | char __user *addrptr; | |
887 | int len; | |
888 | ||
889 | len = mptcp_get_subflow_data(&sfd, optval, optlen); | |
890 | if (len < 0) | |
891 | return len; | |
892 | ||
893 | sfd.size_kernel = sizeof(struct mptcp_subflow_addrs); | |
894 | sfd.size_user = min_t(unsigned int, sfd.size_user, | |
895 | sizeof(struct mptcp_subflow_addrs)); | |
896 | ||
897 | addrptr = optval + sfd.size_subflow_data; | |
898 | ||
899 | lock_sock(sk); | |
900 | ||
901 | mptcp_for_each_subflow(msk, subflow) { | |
902 | struct sock *ssk = mptcp_subflow_tcp_sock(subflow); | |
903 | ||
904 | ++sfcount; | |
905 | ||
906 | if (len && len >= sfd.size_user) { | |
907 | struct mptcp_subflow_addrs a; | |
908 | ||
909 | mptcp_get_sub_addrs(ssk, &a); | |
910 | ||
911 | if (copy_to_user(addrptr, &a, sfd.size_user)) { | |
912 | release_sock(sk); | |
913 | return -EFAULT; | |
914 | } | |
915 | ||
916 | addrptr += sfd.size_user; | |
917 | copied += sfd.size_user; | |
918 | len -= sfd.size_user; | |
919 | } | |
920 | } | |
921 | ||
922 | release_sock(sk); | |
923 | ||
924 | sfd.num_subflows = sfcount; | |
925 | ||
926 | if (mptcp_put_subflow_data(&sfd, optval, copied, optlen)) | |
927 | return -EFAULT; | |
928 | ||
929 | return 0; | |
930 | } | |
931 | ||
aa1fbd94 FW |
932 | static int mptcp_getsockopt_sol_tcp(struct mptcp_sock *msk, int optname, |
933 | char __user *optval, int __user *optlen) | |
934 | { | |
935 | switch (optname) { | |
936 | case TCP_ULP: | |
937 | case TCP_CONGESTION: | |
938 | case TCP_INFO: | |
939 | case TCP_CC_INFO: | |
940 | return mptcp_getsockopt_first_sf_only(msk, SOL_TCP, optname, | |
941 | optval, optlen); | |
942 | } | |
0abdde82 PA |
943 | return -EOPNOTSUPP; |
944 | } | |
945 | ||
55c42fa7 FW |
946 | static int mptcp_getsockopt_sol_mptcp(struct mptcp_sock *msk, int optname, |
947 | char __user *optval, int __user *optlen) | |
948 | { | |
949 | switch (optname) { | |
950 | case MPTCP_INFO: | |
951 | return mptcp_getsockopt_info(msk, optval, optlen); | |
06f15cee FW |
952 | case MPTCP_TCPINFO: |
953 | return mptcp_getsockopt_tcpinfo(msk, optval, optlen); | |
c11c5906 FW |
954 | case MPTCP_SUBFLOW_ADDRS: |
955 | return mptcp_getsockopt_subflow_addrs(msk, optval, optlen); | |
55c42fa7 FW |
956 | } |
957 | ||
958 | return -EOPNOTSUPP; | |
959 | } | |
960 | ||
0abdde82 PA |
961 | int mptcp_getsockopt(struct sock *sk, int level, int optname, |
962 | char __user *optval, int __user *option) | |
963 | { | |
964 | struct mptcp_sock *msk = mptcp_sk(sk); | |
965 | struct sock *ssk; | |
966 | ||
967 | pr_debug("msk=%p", msk); | |
968 | ||
969 | /* @@ the meaning of setsockopt() when the socket is connected and | |
970 | * there are multiple subflows is not yet defined. It is up to the | |
971 | * MPTCP-level socket to configure the subflows until the subflow | |
972 | * is in TCP fallback, when socket options are passed through | |
973 | * to the one remaining subflow. | |
974 | */ | |
975 | lock_sock(sk); | |
976 | ssk = __mptcp_tcp_fallback(msk); | |
977 | release_sock(sk); | |
978 | if (ssk) | |
979 | return tcp_getsockopt(ssk, level, optname, optval, option); | |
980 | ||
aa1fbd94 FW |
981 | if (level == SOL_TCP) |
982 | return mptcp_getsockopt_sol_tcp(msk, optname, optval, option); | |
55c42fa7 FW |
983 | if (level == SOL_MPTCP) |
984 | return mptcp_getsockopt_sol_mptcp(msk, optname, optval, option); | |
0abdde82 PA |
985 | return -EOPNOTSUPP; |
986 | } | |
987 | ||
1b3e7ede FW |
988 | static void sync_socket_options(struct mptcp_sock *msk, struct sock *ssk) |
989 | { | |
5d0a6bc8 | 990 | static const unsigned int tx_rx_locks = SOCK_RCVBUF_LOCK | SOCK_SNDBUF_LOCK; |
1b3e7ede FW |
991 | struct sock *sk = (struct sock *)msk; |
992 | ||
993 | if (ssk->sk_prot->keepalive) { | |
994 | if (sock_flag(sk, SOCK_KEEPOPEN)) | |
995 | ssk->sk_prot->keepalive(ssk, 1); | |
996 | else | |
997 | ssk->sk_prot->keepalive(ssk, 0); | |
998 | } | |
999 | ||
1000 | ssk->sk_priority = sk->sk_priority; | |
5d0a6bc8 FW |
1001 | ssk->sk_bound_dev_if = sk->sk_bound_dev_if; |
1002 | ssk->sk_incoming_cpu = sk->sk_incoming_cpu; | |
1003 | ||
1004 | if (sk->sk_userlocks & tx_rx_locks) { | |
1005 | ssk->sk_userlocks |= sk->sk_userlocks & tx_rx_locks; | |
1006 | if (sk->sk_userlocks & SOCK_SNDBUF_LOCK) | |
1007 | WRITE_ONCE(ssk->sk_sndbuf, sk->sk_sndbuf); | |
1008 | if (sk->sk_userlocks & SOCK_RCVBUF_LOCK) | |
1009 | WRITE_ONCE(ssk->sk_rcvbuf, sk->sk_rcvbuf); | |
1010 | } | |
1011 | ||
1012 | if (sock_flag(sk, SOCK_LINGER)) { | |
1013 | ssk->sk_lingertime = sk->sk_lingertime; | |
1014 | sock_set_flag(ssk, SOCK_LINGER); | |
1015 | } else { | |
1016 | sock_reset_flag(ssk, SOCK_LINGER); | |
1017 | } | |
1018 | ||
1019 | if (sk->sk_mark != ssk->sk_mark) { | |
1020 | ssk->sk_mark = sk->sk_mark; | |
1021 | sk_dst_reset(ssk); | |
1022 | } | |
1023 | ||
1024 | sock_valbool_flag(ssk, SOCK_DBG, sock_flag(sk, SOCK_DBG)); | |
1025 | ||
1026 | if (inet_csk(sk)->icsk_ca_ops != inet_csk(ssk)->icsk_ca_ops) | |
20b5759f | 1027 | tcp_set_congestion_control(ssk, msk->ca_name, false, true); |
1b3e7ede FW |
1028 | } |
1029 | ||
df00b087 FW |
1030 | static void __mptcp_sockopt_sync(struct mptcp_sock *msk, struct sock *ssk) |
1031 | { | |
1b3e7ede FW |
1032 | bool slow = lock_sock_fast(ssk); |
1033 | ||
1034 | sync_socket_options(msk, ssk); | |
1035 | ||
1036 | unlock_sock_fast(ssk, slow); | |
df00b087 FW |
1037 | } |
1038 | ||
78962489 FW |
1039 | void mptcp_sockopt_sync(struct mptcp_sock *msk, struct sock *ssk) |
1040 | { | |
df00b087 FW |
1041 | struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(ssk); |
1042 | ||
78962489 | 1043 | msk_owned_by_me(msk); |
df00b087 FW |
1044 | |
1045 | if (READ_ONCE(subflow->setsockopt_seq) != msk->setsockopt_seq) { | |
1046 | __mptcp_sockopt_sync(msk, ssk); | |
1047 | ||
1048 | subflow->setsockopt_seq = msk->setsockopt_seq; | |
1049 | } | |
78962489 FW |
1050 | } |
1051 | ||
1052 | void mptcp_sockopt_sync_all(struct mptcp_sock *msk) | |
1053 | { | |
1054 | struct mptcp_subflow_context *subflow; | |
df00b087 FW |
1055 | struct sock *sk = (struct sock *)msk; |
1056 | u32 seq; | |
78962489 | 1057 | |
df00b087 | 1058 | seq = sockopt_seq_reset(sk); |
78962489 FW |
1059 | |
1060 | mptcp_for_each_subflow(msk, subflow) { | |
1061 | struct sock *ssk = mptcp_subflow_tcp_sock(subflow); | |
df00b087 | 1062 | u32 sseq = READ_ONCE(subflow->setsockopt_seq); |
78962489 | 1063 | |
df00b087 FW |
1064 | if (sseq != msk->setsockopt_seq) { |
1065 | __mptcp_sockopt_sync(msk, ssk); | |
1066 | WRITE_ONCE(subflow->setsockopt_seq, seq); | |
1067 | } else if (sseq != seq) { | |
1068 | WRITE_ONCE(subflow->setsockopt_seq, seq); | |
1069 | } | |
78962489 FW |
1070 | |
1071 | cond_resched(); | |
1072 | } | |
df00b087 FW |
1073 | |
1074 | msk->setsockopt_seq = seq; | |
78962489 | 1075 | } |