1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Multipath TCP |
3 | * |
4 | * Copyright (c) 2019, Tessares SA. |
5 | */ |
6 | |
7 | #ifdef CONFIG_SYSCTL |
8 | #include <linux/sysctl.h> |
9 | #endif |
10 | |
11 | #include <net/net_namespace.h> |
12 | #include <net/netns/generic.h> |
13 | |
14 | #include "protocol.h" |
15 | |
16 | #define MPTCP_SYSCTL_PATH "net/mptcp" |
17 | |
18 | static int mptcp_pernet_id; |
19 | |
20 | #ifdef CONFIG_SYSCTL |
21 | static int mptcp_pm_type_max = __MPTCP_PM_TYPE_MAX; |
22 | #endif |
23 | |
24 | struct mptcp_pernet { |
25 | #ifdef CONFIG_SYSCTL |
26 | struct ctl_table_header *ctl_table_hdr; |
27 | #endif |
28 | |
29 | unsigned int add_addr_timeout; |
30 | unsigned int close_timeout; |
31 | unsigned int stale_loss_cnt; |
32 | u8 mptcp_enabled; |
33 | u8 checksum_enabled; |
34 | u8 allow_join_initial_addr_port; |
35 | u8 pm_type; |
36 | char scheduler[MPTCP_SCHED_NAME_MAX]; |
37 | }; |
38 | |
39 | static struct mptcp_pernet *mptcp_get_pernet(const struct net *net) |
40 | { |
41 | return net_generic(net, id: mptcp_pernet_id); |
42 | } |
43 | |
44 | int mptcp_is_enabled(const struct net *net) |
45 | { |
46 | return mptcp_get_pernet(net)->mptcp_enabled; |
47 | } |
48 | |
49 | unsigned int mptcp_get_add_addr_timeout(const struct net *net) |
50 | { |
51 | return mptcp_get_pernet(net)->add_addr_timeout; |
52 | } |
53 | |
54 | int mptcp_is_checksum_enabled(const struct net *net) |
55 | { |
56 | return mptcp_get_pernet(net)->checksum_enabled; |
57 | } |
58 | |
59 | int mptcp_allow_join_id0(const struct net *net) |
60 | { |
61 | return mptcp_get_pernet(net)->allow_join_initial_addr_port; |
62 | } |
63 | |
64 | unsigned int mptcp_stale_loss_cnt(const struct net *net) |
65 | { |
66 | return mptcp_get_pernet(net)->stale_loss_cnt; |
67 | } |
68 | |
69 | unsigned int mptcp_close_timeout(const struct sock *sk) |
70 | { |
71 | if (sock_flag(sk, flag: SOCK_DEAD)) |
72 | return TCP_TIMEWAIT_LEN; |
73 | return mptcp_get_pernet(net: sock_net(sk))->close_timeout; |
74 | } |
75 | |
76 | int mptcp_get_pm_type(const struct net *net) |
77 | { |
78 | return mptcp_get_pernet(net)->pm_type; |
79 | } |
80 | |
81 | const char *mptcp_get_scheduler(const struct net *net) |
82 | { |
83 | return mptcp_get_pernet(net)->scheduler; |
84 | } |
85 | |
86 | static void mptcp_pernet_set_defaults(struct mptcp_pernet *pernet) |
87 | { |
88 | pernet->mptcp_enabled = 1; |
89 | pernet->add_addr_timeout = TCP_RTO_MAX; |
90 | pernet->close_timeout = TCP_TIMEWAIT_LEN; |
91 | pernet->checksum_enabled = 0; |
92 | pernet->allow_join_initial_addr_port = 1; |
93 | pernet->stale_loss_cnt = 4; |
94 | pernet->pm_type = MPTCP_PM_TYPE_KERNEL; |
95 | strcpy(p: pernet->scheduler, q: "default" ); |
96 | } |
97 | |
98 | #ifdef CONFIG_SYSCTL |
99 | static struct ctl_table mptcp_sysctl_table[] = { |
100 | { |
101 | .procname = "enabled" , |
102 | .maxlen = sizeof(u8), |
103 | .mode = 0644, |
104 | /* users with CAP_NET_ADMIN or root (not and) can change this |
105 | * value, same as other sysctl or the 'net' tree. |
106 | */ |
107 | .proc_handler = proc_dou8vec_minmax, |
108 | .extra1 = SYSCTL_ZERO, |
109 | .extra2 = SYSCTL_ONE |
110 | }, |
111 | { |
112 | .procname = "add_addr_timeout" , |
113 | .maxlen = sizeof(unsigned int), |
114 | .mode = 0644, |
115 | .proc_handler = proc_dointvec_jiffies, |
116 | }, |
117 | { |
118 | .procname = "checksum_enabled" , |
119 | .maxlen = sizeof(u8), |
120 | .mode = 0644, |
121 | .proc_handler = proc_dou8vec_minmax, |
122 | .extra1 = SYSCTL_ZERO, |
123 | .extra2 = SYSCTL_ONE |
124 | }, |
125 | { |
126 | .procname = "allow_join_initial_addr_port" , |
127 | .maxlen = sizeof(u8), |
128 | .mode = 0644, |
129 | .proc_handler = proc_dou8vec_minmax, |
130 | .extra1 = SYSCTL_ZERO, |
131 | .extra2 = SYSCTL_ONE |
132 | }, |
133 | { |
134 | .procname = "stale_loss_cnt" , |
135 | .maxlen = sizeof(unsigned int), |
136 | .mode = 0644, |
137 | .proc_handler = proc_douintvec_minmax, |
138 | }, |
139 | { |
140 | .procname = "pm_type" , |
141 | .maxlen = sizeof(u8), |
142 | .mode = 0644, |
143 | .proc_handler = proc_dou8vec_minmax, |
144 | .extra1 = SYSCTL_ZERO, |
145 | .extra2 = &mptcp_pm_type_max |
146 | }, |
147 | { |
148 | .procname = "scheduler" , |
149 | .maxlen = MPTCP_SCHED_NAME_MAX, |
150 | .mode = 0644, |
151 | .proc_handler = proc_dostring, |
152 | }, |
153 | { |
154 | .procname = "close_timeout" , |
155 | .maxlen = sizeof(unsigned int), |
156 | .mode = 0644, |
157 | .proc_handler = proc_dointvec_jiffies, |
158 | }, |
159 | {} |
160 | }; |
161 | |
162 | static int mptcp_pernet_new_table(struct net *net, struct mptcp_pernet *pernet) |
163 | { |
164 | struct ctl_table_header *hdr; |
165 | struct ctl_table *table; |
166 | |
167 | table = mptcp_sysctl_table; |
168 | if (!net_eq(net1: net, net2: &init_net)) { |
169 | table = kmemdup(p: table, size: sizeof(mptcp_sysctl_table), GFP_KERNEL); |
170 | if (!table) |
171 | goto err_alloc; |
172 | } |
173 | |
174 | table[0].data = &pernet->mptcp_enabled; |
175 | table[1].data = &pernet->add_addr_timeout; |
176 | table[2].data = &pernet->checksum_enabled; |
177 | table[3].data = &pernet->allow_join_initial_addr_port; |
178 | table[4].data = &pernet->stale_loss_cnt; |
179 | table[5].data = &pernet->pm_type; |
180 | table[6].data = &pernet->scheduler; |
181 | table[7].data = &pernet->close_timeout; |
182 | |
183 | hdr = register_net_sysctl_sz(net, MPTCP_SYSCTL_PATH, table, |
184 | ARRAY_SIZE(mptcp_sysctl_table)); |
185 | if (!hdr) |
186 | goto err_reg; |
187 | |
188 | pernet->ctl_table_hdr = hdr; |
189 | |
190 | return 0; |
191 | |
192 | err_reg: |
193 | if (!net_eq(net1: net, net2: &init_net)) |
194 | kfree(objp: table); |
195 | err_alloc: |
196 | return -ENOMEM; |
197 | } |
198 | |
199 | static void mptcp_pernet_del_table(struct mptcp_pernet *pernet) |
200 | { |
201 | struct ctl_table *table = pernet->ctl_table_hdr->ctl_table_arg; |
202 | |
203 | unregister_net_sysctl_table(header: pernet->ctl_table_hdr); |
204 | |
205 | kfree(objp: table); |
206 | } |
207 | |
208 | #else |
209 | |
210 | static int mptcp_pernet_new_table(struct net *net, struct mptcp_pernet *pernet) |
211 | { |
212 | return 0; |
213 | } |
214 | |
215 | static void mptcp_pernet_del_table(struct mptcp_pernet *pernet) {} |
216 | |
217 | #endif /* CONFIG_SYSCTL */ |
218 | |
219 | static int __net_init mptcp_net_init(struct net *net) |
220 | { |
221 | struct mptcp_pernet *pernet = mptcp_get_pernet(net); |
222 | |
223 | mptcp_pernet_set_defaults(pernet); |
224 | |
225 | return mptcp_pernet_new_table(net, pernet); |
226 | } |
227 | |
228 | /* Note: the callback will only be called per extra netns */ |
229 | static void __net_exit mptcp_net_exit(struct net *net) |
230 | { |
231 | struct mptcp_pernet *pernet = mptcp_get_pernet(net); |
232 | |
233 | mptcp_pernet_del_table(pernet); |
234 | } |
235 | |
236 | static struct pernet_operations mptcp_pernet_ops = { |
237 | .init = mptcp_net_init, |
238 | .exit = mptcp_net_exit, |
239 | .id = &mptcp_pernet_id, |
240 | .size = sizeof(struct mptcp_pernet), |
241 | }; |
242 | |
243 | void __init mptcp_init(void) |
244 | { |
245 | mptcp_join_cookie_init(); |
246 | mptcp_proto_init(); |
247 | |
248 | if (register_pernet_subsys(&mptcp_pernet_ops) < 0) |
249 | panic(fmt: "Failed to register MPTCP pernet subsystem.\n" ); |
250 | } |
251 | |
252 | #if IS_ENABLED(CONFIG_MPTCP_IPV6) |
253 | int __init mptcpv6_init(void) |
254 | { |
255 | int err; |
256 | |
257 | err = mptcp_proto_v6_init(); |
258 | |
259 | return err; |
260 | } |
261 | #endif |
262 | |