1/*
2 * sysctl_net_ipv6.c: sysctl interface to net IPV6 subsystem.
3 *
4 * Changes:
5 * YOSHIFUJI Hideaki @USAGI:	added icmp sysctl table.
6 */
7
8#include <linux/mm.h>
9#include <linux/sysctl.h>
10#include <linux/in6.h>
11#include <linux/ipv6.h>
12#include <linux/slab.h>
13#include <linux/export.h>
14#include <net/ndisc.h>
15#include <net/ipv6.h>
16#include <net/addrconf.h>
17#include <net/inet_frag.h>
18
19static ctl_table ipv6_table_template[] = {
20	{
21		.procname	= "bindv6only",
22		.data		= &init_net.ipv6.sysctl.bindv6only,
23		.maxlen		= sizeof(int),
24		.mode		= 0644,
25		.proc_handler	= proc_dointvec
26	},
27	{
28		.procname	= "fwmark_reflect",
29		.data		= &init_net.ipv6.sysctl.fwmark_reflect,
30		.maxlen		= sizeof(int),
31		.mode		= 0644,
32		.proc_handler	= proc_dointvec
33	},
34	{ }
35};
36
37static ctl_table ipv6_rotable[] = {
38	{
39		.procname	= "mld_max_msf",
40		.data		= &sysctl_mld_max_msf,
41		.maxlen		= sizeof(int),
42		.mode		= 0644,
43		.proc_handler	= proc_dointvec
44	},
45	{ }
46};
47
48static int __net_init ipv6_sysctl_net_init(struct net *net)
49{
50	struct ctl_table *ipv6_table;
51	struct ctl_table *ipv6_route_table;
52	struct ctl_table *ipv6_icmp_table;
53	int err;
54
55	err = -ENOMEM;
56	ipv6_table = kmemdup(ipv6_table_template, sizeof(ipv6_table_template),
57			     GFP_KERNEL);
58	if (!ipv6_table)
59		goto out;
60	ipv6_table[0].data = &net->ipv6.sysctl.bindv6only;
61
62	ipv6_route_table = ipv6_route_sysctl_init(net);
63	if (!ipv6_route_table)
64		goto out_ipv6_table;
65
66	ipv6_icmp_table = ipv6_icmp_sysctl_init(net);
67	if (!ipv6_icmp_table)
68		goto out_ipv6_route_table;
69
70	net->ipv6.sysctl.hdr = register_net_sysctl(net, "net/ipv6", ipv6_table);
71	if (!net->ipv6.sysctl.hdr)
72		goto out_ipv6_icmp_table;
73
74	net->ipv6.sysctl.route_hdr =
75		register_net_sysctl(net, "net/ipv6/route", ipv6_route_table);
76	if (!net->ipv6.sysctl.route_hdr)
77		goto out_unregister_ipv6_table;
78
79	net->ipv6.sysctl.icmp_hdr =
80		register_net_sysctl(net, "net/ipv6/icmp", ipv6_icmp_table);
81	if (!net->ipv6.sysctl.icmp_hdr)
82		goto out_unregister_route_table;
83
84	err = 0;
85out:
86	return err;
87out_unregister_route_table:
88	unregister_net_sysctl_table(net->ipv6.sysctl.route_hdr);
89out_unregister_ipv6_table:
90	unregister_net_sysctl_table(net->ipv6.sysctl.hdr);
91out_ipv6_icmp_table:
92	kfree(ipv6_icmp_table);
93out_ipv6_route_table:
94	kfree(ipv6_route_table);
95out_ipv6_table:
96	kfree(ipv6_table);
97	goto out;
98}
99
100static void __net_exit ipv6_sysctl_net_exit(struct net *net)
101{
102	struct ctl_table *ipv6_table;
103	struct ctl_table *ipv6_route_table;
104	struct ctl_table *ipv6_icmp_table;
105
106	ipv6_table = net->ipv6.sysctl.hdr->ctl_table_arg;
107	ipv6_route_table = net->ipv6.sysctl.route_hdr->ctl_table_arg;
108	ipv6_icmp_table = net->ipv6.sysctl.icmp_hdr->ctl_table_arg;
109
110	unregister_net_sysctl_table(net->ipv6.sysctl.icmp_hdr);
111	unregister_net_sysctl_table(net->ipv6.sysctl.route_hdr);
112	unregister_net_sysctl_table(net->ipv6.sysctl.hdr);
113
114	kfree(ipv6_table);
115	kfree(ipv6_route_table);
116	kfree(ipv6_icmp_table);
117}
118
119static struct pernet_operations ipv6_sysctl_net_ops = {
120	.init = ipv6_sysctl_net_init,
121	.exit = ipv6_sysctl_net_exit,
122};
123
124static struct ctl_table_header *ip6_header;
125
126int ipv6_sysctl_register(void)
127{
128	int err = -ENOMEM;
129
130	ip6_header = register_net_sysctl(&init_net, "net/ipv6", ipv6_rotable);
131	if (ip6_header == NULL)
132		goto out;
133
134	err = register_pernet_subsys(&ipv6_sysctl_net_ops);
135	if (err)
136		goto err_pernet;
137out:
138	return err;
139
140err_pernet:
141	unregister_net_sysctl_table(ip6_header);
142	goto out;
143}
144
145void ipv6_sysctl_unregister(void)
146{
147	unregister_net_sysctl_table(ip6_header);
148	unregister_pernet_subsys(&ipv6_sysctl_net_ops);
149}
150