162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* Multipath TCP
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (c) 2019, Tessares SA.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL
862306a36Sopenharmony_ci#include <linux/sysctl.h>
962306a36Sopenharmony_ci#endif
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <net/net_namespace.h>
1262306a36Sopenharmony_ci#include <net/netns/generic.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include "protocol.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#define MPTCP_SYSCTL_PATH "net/mptcp"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistatic int mptcp_pernet_id;
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL
2162306a36Sopenharmony_cistatic int mptcp_pm_type_max = __MPTCP_PM_TYPE_MAX;
2262306a36Sopenharmony_ci#endif
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistruct mptcp_pernet {
2562306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL
2662306a36Sopenharmony_ci	struct ctl_table_header *ctl_table_hdr;
2762306a36Sopenharmony_ci#endif
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	unsigned int add_addr_timeout;
3062306a36Sopenharmony_ci	unsigned int stale_loss_cnt;
3162306a36Sopenharmony_ci	u8 mptcp_enabled;
3262306a36Sopenharmony_ci	u8 checksum_enabled;
3362306a36Sopenharmony_ci	u8 allow_join_initial_addr_port;
3462306a36Sopenharmony_ci	u8 pm_type;
3562306a36Sopenharmony_ci	char scheduler[MPTCP_SCHED_NAME_MAX];
3662306a36Sopenharmony_ci};
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic struct mptcp_pernet *mptcp_get_pernet(const struct net *net)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	return net_generic(net, mptcp_pernet_id);
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ciint mptcp_is_enabled(const struct net *net)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	return mptcp_get_pernet(net)->mptcp_enabled;
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ciunsigned int mptcp_get_add_addr_timeout(const struct net *net)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	return mptcp_get_pernet(net)->add_addr_timeout;
5162306a36Sopenharmony_ci}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ciint mptcp_is_checksum_enabled(const struct net *net)
5462306a36Sopenharmony_ci{
5562306a36Sopenharmony_ci	return mptcp_get_pernet(net)->checksum_enabled;
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ciint mptcp_allow_join_id0(const struct net *net)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	return mptcp_get_pernet(net)->allow_join_initial_addr_port;
6162306a36Sopenharmony_ci}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ciunsigned int mptcp_stale_loss_cnt(const struct net *net)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	return mptcp_get_pernet(net)->stale_loss_cnt;
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ciint mptcp_get_pm_type(const struct net *net)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	return mptcp_get_pernet(net)->pm_type;
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ciconst char *mptcp_get_scheduler(const struct net *net)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	return mptcp_get_pernet(net)->scheduler;
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic void mptcp_pernet_set_defaults(struct mptcp_pernet *pernet)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	pernet->mptcp_enabled = 1;
8162306a36Sopenharmony_ci	pernet->add_addr_timeout = TCP_RTO_MAX;
8262306a36Sopenharmony_ci	pernet->checksum_enabled = 0;
8362306a36Sopenharmony_ci	pernet->allow_join_initial_addr_port = 1;
8462306a36Sopenharmony_ci	pernet->stale_loss_cnt = 4;
8562306a36Sopenharmony_ci	pernet->pm_type = MPTCP_PM_TYPE_KERNEL;
8662306a36Sopenharmony_ci	strcpy(pernet->scheduler, "default");
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL
9062306a36Sopenharmony_cistatic struct ctl_table mptcp_sysctl_table[] = {
9162306a36Sopenharmony_ci	{
9262306a36Sopenharmony_ci		.procname = "enabled",
9362306a36Sopenharmony_ci		.maxlen = sizeof(u8),
9462306a36Sopenharmony_ci		.mode = 0644,
9562306a36Sopenharmony_ci		/* users with CAP_NET_ADMIN or root (not and) can change this
9662306a36Sopenharmony_ci		 * value, same as other sysctl or the 'net' tree.
9762306a36Sopenharmony_ci		 */
9862306a36Sopenharmony_ci		.proc_handler = proc_dou8vec_minmax,
9962306a36Sopenharmony_ci		.extra1       = SYSCTL_ZERO,
10062306a36Sopenharmony_ci		.extra2       = SYSCTL_ONE
10162306a36Sopenharmony_ci	},
10262306a36Sopenharmony_ci	{
10362306a36Sopenharmony_ci		.procname = "add_addr_timeout",
10462306a36Sopenharmony_ci		.maxlen = sizeof(unsigned int),
10562306a36Sopenharmony_ci		.mode = 0644,
10662306a36Sopenharmony_ci		.proc_handler = proc_dointvec_jiffies,
10762306a36Sopenharmony_ci	},
10862306a36Sopenharmony_ci	{
10962306a36Sopenharmony_ci		.procname = "checksum_enabled",
11062306a36Sopenharmony_ci		.maxlen = sizeof(u8),
11162306a36Sopenharmony_ci		.mode = 0644,
11262306a36Sopenharmony_ci		.proc_handler = proc_dou8vec_minmax,
11362306a36Sopenharmony_ci		.extra1       = SYSCTL_ZERO,
11462306a36Sopenharmony_ci		.extra2       = SYSCTL_ONE
11562306a36Sopenharmony_ci	},
11662306a36Sopenharmony_ci	{
11762306a36Sopenharmony_ci		.procname = "allow_join_initial_addr_port",
11862306a36Sopenharmony_ci		.maxlen = sizeof(u8),
11962306a36Sopenharmony_ci		.mode = 0644,
12062306a36Sopenharmony_ci		.proc_handler = proc_dou8vec_minmax,
12162306a36Sopenharmony_ci		.extra1       = SYSCTL_ZERO,
12262306a36Sopenharmony_ci		.extra2       = SYSCTL_ONE
12362306a36Sopenharmony_ci	},
12462306a36Sopenharmony_ci	{
12562306a36Sopenharmony_ci		.procname = "stale_loss_cnt",
12662306a36Sopenharmony_ci		.maxlen = sizeof(unsigned int),
12762306a36Sopenharmony_ci		.mode = 0644,
12862306a36Sopenharmony_ci		.proc_handler = proc_douintvec_minmax,
12962306a36Sopenharmony_ci	},
13062306a36Sopenharmony_ci	{
13162306a36Sopenharmony_ci		.procname = "pm_type",
13262306a36Sopenharmony_ci		.maxlen = sizeof(u8),
13362306a36Sopenharmony_ci		.mode = 0644,
13462306a36Sopenharmony_ci		.proc_handler = proc_dou8vec_minmax,
13562306a36Sopenharmony_ci		.extra1       = SYSCTL_ZERO,
13662306a36Sopenharmony_ci		.extra2       = &mptcp_pm_type_max
13762306a36Sopenharmony_ci	},
13862306a36Sopenharmony_ci	{
13962306a36Sopenharmony_ci		.procname = "scheduler",
14062306a36Sopenharmony_ci		.maxlen	= MPTCP_SCHED_NAME_MAX,
14162306a36Sopenharmony_ci		.mode = 0644,
14262306a36Sopenharmony_ci		.proc_handler = proc_dostring,
14362306a36Sopenharmony_ci	},
14462306a36Sopenharmony_ci	{}
14562306a36Sopenharmony_ci};
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic int mptcp_pernet_new_table(struct net *net, struct mptcp_pernet *pernet)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	struct ctl_table_header *hdr;
15062306a36Sopenharmony_ci	struct ctl_table *table;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	table = mptcp_sysctl_table;
15362306a36Sopenharmony_ci	if (!net_eq(net, &init_net)) {
15462306a36Sopenharmony_ci		table = kmemdup(table, sizeof(mptcp_sysctl_table), GFP_KERNEL);
15562306a36Sopenharmony_ci		if (!table)
15662306a36Sopenharmony_ci			goto err_alloc;
15762306a36Sopenharmony_ci	}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	table[0].data = &pernet->mptcp_enabled;
16062306a36Sopenharmony_ci	table[1].data = &pernet->add_addr_timeout;
16162306a36Sopenharmony_ci	table[2].data = &pernet->checksum_enabled;
16262306a36Sopenharmony_ci	table[3].data = &pernet->allow_join_initial_addr_port;
16362306a36Sopenharmony_ci	table[4].data = &pernet->stale_loss_cnt;
16462306a36Sopenharmony_ci	table[5].data = &pernet->pm_type;
16562306a36Sopenharmony_ci	table[6].data = &pernet->scheduler;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	hdr = register_net_sysctl_sz(net, MPTCP_SYSCTL_PATH, table,
16862306a36Sopenharmony_ci				     ARRAY_SIZE(mptcp_sysctl_table));
16962306a36Sopenharmony_ci	if (!hdr)
17062306a36Sopenharmony_ci		goto err_reg;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	pernet->ctl_table_hdr = hdr;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	return 0;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cierr_reg:
17762306a36Sopenharmony_ci	if (!net_eq(net, &init_net))
17862306a36Sopenharmony_ci		kfree(table);
17962306a36Sopenharmony_cierr_alloc:
18062306a36Sopenharmony_ci	return -ENOMEM;
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_cistatic void mptcp_pernet_del_table(struct mptcp_pernet *pernet)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	struct ctl_table *table = pernet->ctl_table_hdr->ctl_table_arg;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	unregister_net_sysctl_table(pernet->ctl_table_hdr);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	kfree(table);
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci#else
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cistatic int mptcp_pernet_new_table(struct net *net, struct mptcp_pernet *pernet)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	return 0;
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic void mptcp_pernet_del_table(struct mptcp_pernet *pernet) {}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci#endif /* CONFIG_SYSCTL */
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic int __net_init mptcp_net_init(struct net *net)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	struct mptcp_pernet *pernet = mptcp_get_pernet(net);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	mptcp_pernet_set_defaults(pernet);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	return mptcp_pernet_new_table(net, pernet);
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci/* Note: the callback will only be called per extra netns */
21362306a36Sopenharmony_cistatic void __net_exit mptcp_net_exit(struct net *net)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	struct mptcp_pernet *pernet = mptcp_get_pernet(net);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	mptcp_pernet_del_table(pernet);
21862306a36Sopenharmony_ci}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_cistatic struct pernet_operations mptcp_pernet_ops = {
22162306a36Sopenharmony_ci	.init = mptcp_net_init,
22262306a36Sopenharmony_ci	.exit = mptcp_net_exit,
22362306a36Sopenharmony_ci	.id = &mptcp_pernet_id,
22462306a36Sopenharmony_ci	.size = sizeof(struct mptcp_pernet),
22562306a36Sopenharmony_ci};
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_civoid __init mptcp_init(void)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	mptcp_join_cookie_init();
23062306a36Sopenharmony_ci	mptcp_proto_init();
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	if (register_pernet_subsys(&mptcp_pernet_ops) < 0)
23362306a36Sopenharmony_ci		panic("Failed to register MPTCP pernet subsystem.\n");
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_MPTCP_IPV6)
23762306a36Sopenharmony_ciint __init mptcpv6_init(void)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	int err;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	err = mptcp_proto_v6_init();
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	return err;
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ci#endif
246