18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/* Multipath TCP
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (c) 2019, Tessares SA.
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/sysctl.h>
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <net/net_namespace.h>
108c2ecf20Sopenharmony_ci#include <net/netns/generic.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include "protocol.h"
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#define MPTCP_SYSCTL_PATH "net/mptcp"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_cistatic int mptcp_pernet_id;
178c2ecf20Sopenharmony_cistruct mptcp_pernet {
188c2ecf20Sopenharmony_ci	struct ctl_table_header *ctl_table_hdr;
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci	int mptcp_enabled;
218c2ecf20Sopenharmony_ci};
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistatic struct mptcp_pernet *mptcp_get_pernet(struct net *net)
248c2ecf20Sopenharmony_ci{
258c2ecf20Sopenharmony_ci	return net_generic(net, mptcp_pernet_id);
268c2ecf20Sopenharmony_ci}
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ciint mptcp_is_enabled(struct net *net)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	return mptcp_get_pernet(net)->mptcp_enabled;
318c2ecf20Sopenharmony_ci}
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistatic struct ctl_table mptcp_sysctl_table[] = {
348c2ecf20Sopenharmony_ci	{
358c2ecf20Sopenharmony_ci		.procname = "enabled",
368c2ecf20Sopenharmony_ci		.maxlen = sizeof(int),
378c2ecf20Sopenharmony_ci		.mode = 0644,
388c2ecf20Sopenharmony_ci		/* users with CAP_NET_ADMIN or root (not and) can change this
398c2ecf20Sopenharmony_ci		 * value, same as other sysctl or the 'net' tree.
408c2ecf20Sopenharmony_ci		 */
418c2ecf20Sopenharmony_ci		.proc_handler = proc_dointvec,
428c2ecf20Sopenharmony_ci	},
438c2ecf20Sopenharmony_ci	{}
448c2ecf20Sopenharmony_ci};
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic void mptcp_pernet_set_defaults(struct mptcp_pernet *pernet)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	pernet->mptcp_enabled = 1;
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic int mptcp_pernet_new_table(struct net *net, struct mptcp_pernet *pernet)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	struct ctl_table_header *hdr;
548c2ecf20Sopenharmony_ci	struct ctl_table *table;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	table = mptcp_sysctl_table;
578c2ecf20Sopenharmony_ci	if (!net_eq(net, &init_net)) {
588c2ecf20Sopenharmony_ci		table = kmemdup(table, sizeof(mptcp_sysctl_table), GFP_KERNEL);
598c2ecf20Sopenharmony_ci		if (!table)
608c2ecf20Sopenharmony_ci			goto err_alloc;
618c2ecf20Sopenharmony_ci	}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	table[0].data = &pernet->mptcp_enabled;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	hdr = register_net_sysctl(net, MPTCP_SYSCTL_PATH, table);
668c2ecf20Sopenharmony_ci	if (!hdr)
678c2ecf20Sopenharmony_ci		goto err_reg;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	pernet->ctl_table_hdr = hdr;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	return 0;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cierr_reg:
748c2ecf20Sopenharmony_ci	if (!net_eq(net, &init_net))
758c2ecf20Sopenharmony_ci		kfree(table);
768c2ecf20Sopenharmony_cierr_alloc:
778c2ecf20Sopenharmony_ci	return -ENOMEM;
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic void mptcp_pernet_del_table(struct mptcp_pernet *pernet)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	struct ctl_table *table = pernet->ctl_table_hdr->ctl_table_arg;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	unregister_net_sysctl_table(pernet->ctl_table_hdr);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	kfree(table);
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic int __net_init mptcp_net_init(struct net *net)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	struct mptcp_pernet *pernet = mptcp_get_pernet(net);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	mptcp_pernet_set_defaults(pernet);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	return mptcp_pernet_new_table(net, pernet);
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci/* Note: the callback will only be called per extra netns */
998c2ecf20Sopenharmony_cistatic void __net_exit mptcp_net_exit(struct net *net)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	struct mptcp_pernet *pernet = mptcp_get_pernet(net);
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	mptcp_pernet_del_table(pernet);
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic struct pernet_operations mptcp_pernet_ops = {
1078c2ecf20Sopenharmony_ci	.init = mptcp_net_init,
1088c2ecf20Sopenharmony_ci	.exit = mptcp_net_exit,
1098c2ecf20Sopenharmony_ci	.id = &mptcp_pernet_id,
1108c2ecf20Sopenharmony_ci	.size = sizeof(struct mptcp_pernet),
1118c2ecf20Sopenharmony_ci};
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_civoid __init mptcp_init(void)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	mptcp_join_cookie_init();
1168c2ecf20Sopenharmony_ci	mptcp_proto_init();
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	if (register_pernet_subsys(&mptcp_pernet_ops) < 0)
1198c2ecf20Sopenharmony_ci		panic("Failed to register MPTCP pernet subsystem.\n");
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_MPTCP_IPV6)
1238c2ecf20Sopenharmony_ciint __init mptcpv6_init(void)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	int err;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	err = mptcp_proto_v6_init();
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	return err;
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci#endif
132