mptcp: move sockopt function into a new file
[linux-2.6-microblaze.git] / net / mptcp / sockopt.c
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
17 static struct sock *__mptcp_tcp_fallback(struct mptcp_sock *msk)
18 {
19         sock_owned_by_me((const struct sock *)msk);
20
21         if (likely(!__mptcp_check_fallback(msk)))
22                 return NULL;
23
24         return msk->first;
25 }
26
27 static int mptcp_setsockopt_sol_socket(struct mptcp_sock *msk, int optname,
28                                        sockptr_t optval, unsigned int optlen)
29 {
30         struct sock *sk = (struct sock *)msk;
31         struct socket *ssock;
32         int ret;
33
34         switch (optname) {
35         case SO_REUSEPORT:
36         case SO_REUSEADDR:
37                 lock_sock(sk);
38                 ssock = __mptcp_nmpc_socket(msk);
39                 if (!ssock) {
40                         release_sock(sk);
41                         return -EINVAL;
42                 }
43
44                 ret = sock_setsockopt(ssock, SOL_SOCKET, optname, optval, optlen);
45                 if (ret == 0) {
46                         if (optname == SO_REUSEPORT)
47                                 sk->sk_reuseport = ssock->sk->sk_reuseport;
48                         else if (optname == SO_REUSEADDR)
49                                 sk->sk_reuse = ssock->sk->sk_reuse;
50                 }
51                 release_sock(sk);
52                 return ret;
53         }
54
55         return sock_setsockopt(sk->sk_socket, SOL_SOCKET, optname, optval, optlen);
56 }
57
58 static int mptcp_setsockopt_v6(struct mptcp_sock *msk, int optname,
59                                sockptr_t optval, unsigned int optlen)
60 {
61         struct sock *sk = (struct sock *)msk;
62         int ret = -EOPNOTSUPP;
63         struct socket *ssock;
64
65         switch (optname) {
66         case IPV6_V6ONLY:
67                 lock_sock(sk);
68                 ssock = __mptcp_nmpc_socket(msk);
69                 if (!ssock) {
70                         release_sock(sk);
71                         return -EINVAL;
72                 }
73
74                 ret = tcp_setsockopt(ssock->sk, SOL_IPV6, optname, optval, optlen);
75                 if (ret == 0)
76                         sk->sk_ipv6only = ssock->sk->sk_ipv6only;
77
78                 release_sock(sk);
79                 break;
80         }
81
82         return ret;
83 }
84
85 int mptcp_setsockopt(struct sock *sk, int level, int optname,
86                      sockptr_t optval, unsigned int optlen)
87 {
88         struct mptcp_sock *msk = mptcp_sk(sk);
89         struct sock *ssk;
90
91         pr_debug("msk=%p", msk);
92
93         if (level == SOL_SOCKET)
94                 return mptcp_setsockopt_sol_socket(msk, optname, optval, optlen);
95
96         /* @@ the meaning of setsockopt() when the socket is connected and
97          * there are multiple subflows is not yet defined. It is up to the
98          * MPTCP-level socket to configure the subflows until the subflow
99          * is in TCP fallback, when TCP socket options are passed through
100          * to the one remaining subflow.
101          */
102         lock_sock(sk);
103         ssk = __mptcp_tcp_fallback(msk);
104         release_sock(sk);
105         if (ssk)
106                 return tcp_setsockopt(ssk, level, optname, optval, optlen);
107
108         if (level == SOL_IPV6)
109                 return mptcp_setsockopt_v6(msk, optname, optval, optlen);
110
111         return -EOPNOTSUPP;
112 }
113
114 int mptcp_getsockopt(struct sock *sk, int level, int optname,
115                      char __user *optval, int __user *option)
116 {
117         struct mptcp_sock *msk = mptcp_sk(sk);
118         struct sock *ssk;
119
120         pr_debug("msk=%p", msk);
121
122         /* @@ the meaning of setsockopt() when the socket is connected and
123          * there are multiple subflows is not yet defined. It is up to the
124          * MPTCP-level socket to configure the subflows until the subflow
125          * is in TCP fallback, when socket options are passed through
126          * to the one remaining subflow.
127          */
128         lock_sock(sk);
129         ssk = __mptcp_tcp_fallback(msk);
130         release_sock(sk);
131         if (ssk)
132                 return tcp_getsockopt(ssk, level, optname, optval, option);
133
134         return -EOPNOTSUPP;
135 }
136