1784325e9SMatthieu Baerts // SPDX-License-Identifier: GPL-2.0
2784325e9SMatthieu Baerts /* Multipath TCP
3784325e9SMatthieu Baerts *
4784325e9SMatthieu Baerts * Copyright (c) 2019, Tessares SA.
5784325e9SMatthieu Baerts */
6784325e9SMatthieu Baerts
7804c72eeSMatthieu Baerts #ifdef CONFIG_SYSCTL
8784325e9SMatthieu Baerts #include <linux/sysctl.h>
9804c72eeSMatthieu Baerts #endif
10784325e9SMatthieu Baerts
11784325e9SMatthieu Baerts #include <net/net_namespace.h>
12784325e9SMatthieu Baerts #include <net/netns/generic.h>
13784325e9SMatthieu Baerts
14784325e9SMatthieu Baerts #include "protocol.h"
15784325e9SMatthieu Baerts
16784325e9SMatthieu Baerts #define MPTCP_SYSCTL_PATH "net/mptcp"
17784325e9SMatthieu Baerts
18784325e9SMatthieu Baerts static int mptcp_pernet_id;
196bb63cccSMat Martineau
206bb63cccSMat Martineau #ifdef CONFIG_SYSCTL
216bb63cccSMat Martineau static int mptcp_pm_type_max = __MPTCP_PM_TYPE_MAX;
226bb63cccSMat Martineau #endif
236bb63cccSMat Martineau
24784325e9SMatthieu Baerts struct mptcp_pernet {
25804c72eeSMatthieu Baerts #ifdef CONFIG_SYSCTL
26784325e9SMatthieu Baerts struct ctl_table_header *ctl_table_hdr;
27804c72eeSMatthieu Baerts #endif
28784325e9SMatthieu Baerts
2993f323b9SGeliang Tang unsigned int add_addr_timeout;
30ff5a0b42SPaolo Abeni unsigned int stale_loss_cnt;
316da14d74SPaolo Abeni u8 mptcp_enabled;
32fc3c82eeSGeliang Tang u8 checksum_enabled;
33d2f77960SGeliang Tang u8 allow_join_initial_addr_port;
346bb63cccSMat Martineau u8 pm_type;
35e3b2870bSGeliang Tang char scheduler[MPTCP_SCHED_NAME_MAX];
36784325e9SMatthieu Baerts };
37784325e9SMatthieu Baerts
mptcp_get_pernet(const struct net * net)386da14d74SPaolo Abeni static struct mptcp_pernet *mptcp_get_pernet(const struct net *net)
39784325e9SMatthieu Baerts {
40784325e9SMatthieu Baerts return net_generic(net, mptcp_pernet_id);
41784325e9SMatthieu Baerts }
42784325e9SMatthieu Baerts
mptcp_is_enabled(const struct net * net)436da14d74SPaolo Abeni int mptcp_is_enabled(const struct net *net)
44784325e9SMatthieu Baerts {
45784325e9SMatthieu Baerts return mptcp_get_pernet(net)->mptcp_enabled;
46784325e9SMatthieu Baerts }
47784325e9SMatthieu Baerts
mptcp_get_add_addr_timeout(const struct net * net)486da14d74SPaolo Abeni unsigned int mptcp_get_add_addr_timeout(const struct net *net)
4993f323b9SGeliang Tang {
5093f323b9SGeliang Tang return mptcp_get_pernet(net)->add_addr_timeout;
5193f323b9SGeliang Tang }
5293f323b9SGeliang Tang
mptcp_is_checksum_enabled(const struct net * net)536da14d74SPaolo Abeni int mptcp_is_checksum_enabled(const struct net *net)
54fc3c82eeSGeliang Tang {
55fc3c82eeSGeliang Tang return mptcp_get_pernet(net)->checksum_enabled;
56fc3c82eeSGeliang Tang }
57fc3c82eeSGeliang Tang
mptcp_allow_join_id0(const struct net * net)586da14d74SPaolo Abeni int mptcp_allow_join_id0(const struct net *net)
59d2f77960SGeliang Tang {
60d2f77960SGeliang Tang return mptcp_get_pernet(net)->allow_join_initial_addr_port;
61d2f77960SGeliang Tang }
62d2f77960SGeliang Tang
mptcp_stale_loss_cnt(const struct net * net)63ff5a0b42SPaolo Abeni unsigned int mptcp_stale_loss_cnt(const struct net *net)
64ff5a0b42SPaolo Abeni {
65ff5a0b42SPaolo Abeni return mptcp_get_pernet(net)->stale_loss_cnt;
66ff5a0b42SPaolo Abeni }
67ff5a0b42SPaolo Abeni
mptcp_get_pm_type(const struct net * net)686bb63cccSMat Martineau int mptcp_get_pm_type(const struct net *net)
696bb63cccSMat Martineau {
706bb63cccSMat Martineau return mptcp_get_pernet(net)->pm_type;
716bb63cccSMat Martineau }
726bb63cccSMat Martineau
mptcp_get_scheduler(const struct net * net)73e3b2870bSGeliang Tang const char *mptcp_get_scheduler(const struct net *net)
74e3b2870bSGeliang Tang {
75e3b2870bSGeliang Tang return mptcp_get_pernet(net)->scheduler;
76e3b2870bSGeliang Tang }
77e3b2870bSGeliang Tang
mptcp_pernet_set_defaults(struct mptcp_pernet * pernet)78804c72eeSMatthieu Baerts static void mptcp_pernet_set_defaults(struct mptcp_pernet *pernet)
79804c72eeSMatthieu Baerts {
80804c72eeSMatthieu Baerts pernet->mptcp_enabled = 1;
81804c72eeSMatthieu Baerts pernet->add_addr_timeout = TCP_RTO_MAX;
82fc3c82eeSGeliang Tang pernet->checksum_enabled = 0;
83d2f77960SGeliang Tang pernet->allow_join_initial_addr_port = 1;
84ff5a0b42SPaolo Abeni pernet->stale_loss_cnt = 4;
856bb63cccSMat Martineau pernet->pm_type = MPTCP_PM_TYPE_KERNEL;
86e3b2870bSGeliang Tang strcpy(pernet->scheduler, "default");
87804c72eeSMatthieu Baerts }
88804c72eeSMatthieu Baerts
89804c72eeSMatthieu Baerts #ifdef CONFIG_SYSCTL
mptcp_set_scheduler(char * scheduler,const char * name)90*c0e394fdSMatthieu Baerts (NGI0) static int mptcp_set_scheduler(char *scheduler, const char *name)
91daad878aSGregory Detal {
92daad878aSGregory Detal struct mptcp_sched_ops *sched;
93daad878aSGregory Detal int ret = 0;
94daad878aSGregory Detal
95daad878aSGregory Detal rcu_read_lock();
96daad878aSGregory Detal sched = mptcp_sched_find(name);
97daad878aSGregory Detal if (sched)
98*c0e394fdSMatthieu Baerts (NGI0) strscpy(scheduler, name, MPTCP_SCHED_NAME_MAX);
99daad878aSGregory Detal else
100daad878aSGregory Detal ret = -ENOENT;
101daad878aSGregory Detal rcu_read_unlock();
102daad878aSGregory Detal
103daad878aSGregory Detal return ret;
104daad878aSGregory Detal }
105daad878aSGregory Detal
proc_scheduler(struct ctl_table * ctl,int write,void * buffer,size_t * lenp,loff_t * ppos)106daad878aSGregory Detal static int proc_scheduler(struct ctl_table *ctl, int write,
107daad878aSGregory Detal void *buffer, size_t *lenp, loff_t *ppos)
108daad878aSGregory Detal {
109*c0e394fdSMatthieu Baerts (NGI0) char (*scheduler)[MPTCP_SCHED_NAME_MAX] = ctl->data;
110daad878aSGregory Detal char val[MPTCP_SCHED_NAME_MAX];
111daad878aSGregory Detal struct ctl_table tbl = {
112daad878aSGregory Detal .data = val,
113daad878aSGregory Detal .maxlen = MPTCP_SCHED_NAME_MAX,
114daad878aSGregory Detal };
115daad878aSGregory Detal int ret;
116daad878aSGregory Detal
117*c0e394fdSMatthieu Baerts (NGI0) strscpy(val, *scheduler, MPTCP_SCHED_NAME_MAX);
118daad878aSGregory Detal
119daad878aSGregory Detal ret = proc_dostring(&tbl, write, buffer, lenp, ppos);
120daad878aSGregory Detal if (write && ret == 0)
121*c0e394fdSMatthieu Baerts (NGI0) ret = mptcp_set_scheduler(*scheduler, val);
122daad878aSGregory Detal
123daad878aSGregory Detal return ret;
124daad878aSGregory Detal }
125daad878aSGregory Detal
126784325e9SMatthieu Baerts static struct ctl_table mptcp_sysctl_table[] = {
127784325e9SMatthieu Baerts {
128784325e9SMatthieu Baerts .procname = "enabled",
129744ee140SMatthieu Baerts .maxlen = sizeof(u8),
130784325e9SMatthieu Baerts .mode = 0644,
131784325e9SMatthieu Baerts /* users with CAP_NET_ADMIN or root (not and) can change this
132784325e9SMatthieu Baerts * value, same as other sysctl or the 'net' tree.
133784325e9SMatthieu Baerts */
134744ee140SMatthieu Baerts .proc_handler = proc_dou8vec_minmax,
135744ee140SMatthieu Baerts .extra1 = SYSCTL_ZERO,
136744ee140SMatthieu Baerts .extra2 = SYSCTL_ONE
137784325e9SMatthieu Baerts },
13893f323b9SGeliang Tang {
13993f323b9SGeliang Tang .procname = "add_addr_timeout",
14093f323b9SGeliang Tang .maxlen = sizeof(unsigned int),
14193f323b9SGeliang Tang .mode = 0644,
14293f323b9SGeliang Tang .proc_handler = proc_dointvec_jiffies,
14393f323b9SGeliang Tang },
144fc3c82eeSGeliang Tang {
145fc3c82eeSGeliang Tang .procname = "checksum_enabled",
146fc3c82eeSGeliang Tang .maxlen = sizeof(u8),
147fc3c82eeSGeliang Tang .mode = 0644,
148fc3c82eeSGeliang Tang .proc_handler = proc_dou8vec_minmax,
149fc3c82eeSGeliang Tang .extra1 = SYSCTL_ZERO,
150fc3c82eeSGeliang Tang .extra2 = SYSCTL_ONE
151fc3c82eeSGeliang Tang },
152d2f77960SGeliang Tang {
153d2f77960SGeliang Tang .procname = "allow_join_initial_addr_port",
154d2f77960SGeliang Tang .maxlen = sizeof(u8),
155d2f77960SGeliang Tang .mode = 0644,
156d2f77960SGeliang Tang .proc_handler = proc_dou8vec_minmax,
157d2f77960SGeliang Tang .extra1 = SYSCTL_ZERO,
158d2f77960SGeliang Tang .extra2 = SYSCTL_ONE
159d2f77960SGeliang Tang },
160ff5a0b42SPaolo Abeni {
161ff5a0b42SPaolo Abeni .procname = "stale_loss_cnt",
162ff5a0b42SPaolo Abeni .maxlen = sizeof(unsigned int),
163ff5a0b42SPaolo Abeni .mode = 0644,
164ff5a0b42SPaolo Abeni .proc_handler = proc_douintvec_minmax,
165ff5a0b42SPaolo Abeni },
1666bb63cccSMat Martineau {
1676bb63cccSMat Martineau .procname = "pm_type",
1686bb63cccSMat Martineau .maxlen = sizeof(u8),
1696bb63cccSMat Martineau .mode = 0644,
1706bb63cccSMat Martineau .proc_handler = proc_dou8vec_minmax,
1716bb63cccSMat Martineau .extra1 = SYSCTL_ZERO,
1726bb63cccSMat Martineau .extra2 = &mptcp_pm_type_max
1736bb63cccSMat Martineau },
174e3b2870bSGeliang Tang {
175e3b2870bSGeliang Tang .procname = "scheduler",
176e3b2870bSGeliang Tang .maxlen = MPTCP_SCHED_NAME_MAX,
177e3b2870bSGeliang Tang .mode = 0644,
178daad878aSGregory Detal .proc_handler = proc_scheduler,
179e3b2870bSGeliang Tang },
180784325e9SMatthieu Baerts {}
181784325e9SMatthieu Baerts };
182784325e9SMatthieu Baerts
mptcp_pernet_new_table(struct net * net,struct mptcp_pernet * pernet)183784325e9SMatthieu Baerts static int mptcp_pernet_new_table(struct net *net, struct mptcp_pernet *pernet)
184784325e9SMatthieu Baerts {
185784325e9SMatthieu Baerts struct ctl_table_header *hdr;
186784325e9SMatthieu Baerts struct ctl_table *table;
187784325e9SMatthieu Baerts
188784325e9SMatthieu Baerts table = mptcp_sysctl_table;
189784325e9SMatthieu Baerts if (!net_eq(net, &init_net)) {
190784325e9SMatthieu Baerts table = kmemdup(table, sizeof(mptcp_sysctl_table), GFP_KERNEL);
191784325e9SMatthieu Baerts if (!table)
192784325e9SMatthieu Baerts goto err_alloc;
193784325e9SMatthieu Baerts }
194784325e9SMatthieu Baerts
195784325e9SMatthieu Baerts table[0].data = &pernet->mptcp_enabled;
19693f323b9SGeliang Tang table[1].data = &pernet->add_addr_timeout;
197fc3c82eeSGeliang Tang table[2].data = &pernet->checksum_enabled;
198d2f77960SGeliang Tang table[3].data = &pernet->allow_join_initial_addr_port;
199ff5a0b42SPaolo Abeni table[4].data = &pernet->stale_loss_cnt;
2006bb63cccSMat Martineau table[5].data = &pernet->pm_type;
201e3b2870bSGeliang Tang table[6].data = &pernet->scheduler;
202784325e9SMatthieu Baerts
203c899710fSJoel Granados hdr = register_net_sysctl_sz(net, MPTCP_SYSCTL_PATH, table,
204c899710fSJoel Granados ARRAY_SIZE(mptcp_sysctl_table));
205784325e9SMatthieu Baerts if (!hdr)
206784325e9SMatthieu Baerts goto err_reg;
207784325e9SMatthieu Baerts
208784325e9SMatthieu Baerts pernet->ctl_table_hdr = hdr;
209784325e9SMatthieu Baerts
210784325e9SMatthieu Baerts return 0;
211784325e9SMatthieu Baerts
212784325e9SMatthieu Baerts err_reg:
213784325e9SMatthieu Baerts if (!net_eq(net, &init_net))
214784325e9SMatthieu Baerts kfree(table);
215784325e9SMatthieu Baerts err_alloc:
216784325e9SMatthieu Baerts return -ENOMEM;
217784325e9SMatthieu Baerts }
218784325e9SMatthieu Baerts
mptcp_pernet_del_table(struct mptcp_pernet * pernet)219784325e9SMatthieu Baerts static void mptcp_pernet_del_table(struct mptcp_pernet *pernet)
220784325e9SMatthieu Baerts {
221784325e9SMatthieu Baerts struct ctl_table *table = pernet->ctl_table_hdr->ctl_table_arg;
222784325e9SMatthieu Baerts
223784325e9SMatthieu Baerts unregister_net_sysctl_table(pernet->ctl_table_hdr);
224784325e9SMatthieu Baerts
225784325e9SMatthieu Baerts kfree(table);
226784325e9SMatthieu Baerts }
227784325e9SMatthieu Baerts
228804c72eeSMatthieu Baerts #else
229804c72eeSMatthieu Baerts
mptcp_pernet_new_table(struct net * net,struct mptcp_pernet * pernet)230804c72eeSMatthieu Baerts static int mptcp_pernet_new_table(struct net *net, struct mptcp_pernet *pernet)
231804c72eeSMatthieu Baerts {
232804c72eeSMatthieu Baerts return 0;
233804c72eeSMatthieu Baerts }
234804c72eeSMatthieu Baerts
mptcp_pernet_del_table(struct mptcp_pernet * pernet)235804c72eeSMatthieu Baerts static void mptcp_pernet_del_table(struct mptcp_pernet *pernet) {}
236804c72eeSMatthieu Baerts
237804c72eeSMatthieu Baerts #endif /* CONFIG_SYSCTL */
238804c72eeSMatthieu Baerts
mptcp_net_init(struct net * net)239784325e9SMatthieu Baerts static int __net_init mptcp_net_init(struct net *net)
240784325e9SMatthieu Baerts {
241784325e9SMatthieu Baerts struct mptcp_pernet *pernet = mptcp_get_pernet(net);
242784325e9SMatthieu Baerts
243784325e9SMatthieu Baerts mptcp_pernet_set_defaults(pernet);
244784325e9SMatthieu Baerts
245784325e9SMatthieu Baerts return mptcp_pernet_new_table(net, pernet);
246784325e9SMatthieu Baerts }
247784325e9SMatthieu Baerts
248784325e9SMatthieu Baerts /* Note: the callback will only be called per extra netns */
mptcp_net_exit(struct net * net)249784325e9SMatthieu Baerts static void __net_exit mptcp_net_exit(struct net *net)
250784325e9SMatthieu Baerts {
251784325e9SMatthieu Baerts struct mptcp_pernet *pernet = mptcp_get_pernet(net);
252784325e9SMatthieu Baerts
253784325e9SMatthieu Baerts mptcp_pernet_del_table(pernet);
254784325e9SMatthieu Baerts }
255784325e9SMatthieu Baerts
256784325e9SMatthieu Baerts static struct pernet_operations mptcp_pernet_ops = {
257784325e9SMatthieu Baerts .init = mptcp_net_init,
258784325e9SMatthieu Baerts .exit = mptcp_net_exit,
259784325e9SMatthieu Baerts .id = &mptcp_pernet_id,
260784325e9SMatthieu Baerts .size = sizeof(struct mptcp_pernet),
261784325e9SMatthieu Baerts };
262784325e9SMatthieu Baerts
mptcp_init(void)263784325e9SMatthieu Baerts void __init mptcp_init(void)
264784325e9SMatthieu Baerts {
2659466a1ccSFlorian Westphal mptcp_join_cookie_init();
266784325e9SMatthieu Baerts mptcp_proto_init();
267784325e9SMatthieu Baerts
268784325e9SMatthieu Baerts if (register_pernet_subsys(&mptcp_pernet_ops) < 0)
269784325e9SMatthieu Baerts panic("Failed to register MPTCP pernet subsystem.\n");
270784325e9SMatthieu Baerts }
271784325e9SMatthieu Baerts
272784325e9SMatthieu Baerts #if IS_ENABLED(CONFIG_MPTCP_IPV6)
mptcpv6_init(void)273784325e9SMatthieu Baerts int __init mptcpv6_init(void)
274784325e9SMatthieu Baerts {
275784325e9SMatthieu Baerts int err;
276784325e9SMatthieu Baerts
277784325e9SMatthieu Baerts err = mptcp_proto_v6_init();
278784325e9SMatthieu Baerts
279784325e9SMatthieu Baerts return err;
280784325e9SMatthieu Baerts }
281784325e9SMatthieu Baerts #endif
282