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 <net/ndisc.h>
14 #include <net/ipv6.h>
15 #include <net/addrconf.h>
16 #include <net/inet_frag.h>
17 
18 static struct ctl_table empty[1];
19 
20 static ctl_table ipv6_static_skeleton[] = {
21 	{
22 		.procname	= "neigh",
23 		.maxlen		= 0,
24 		.mode		= 0555,
25 		.child		= empty,
26 	},
27 	{ }
28 };
29 
30 static ctl_table ipv6_table_template[] = {
31 	{
32 		.procname	= "route",
33 		.maxlen		= 0,
34 		.mode		= 0555,
35 		.child		= ipv6_route_table_template
36 	},
37 	{
38 		.procname	= "icmp",
39 		.maxlen		= 0,
40 		.mode		= 0555,
41 		.child		= ipv6_icmp_table_template
42 	},
43 	{
44 		.procname	= "bindv6only",
45 		.data		= &init_net.ipv6.sysctl.bindv6only,
46 		.maxlen		= sizeof(int),
47 		.mode		= 0644,
48 		.proc_handler	= proc_dointvec
49 	},
50 	{ }
51 };
52 
53 static ctl_table ipv6_rotable[] = {
54 	{
55 		.procname	= "mld_max_msf",
56 		.data		= &sysctl_mld_max_msf,
57 		.maxlen		= sizeof(int),
58 		.mode		= 0644,
59 		.proc_handler	= proc_dointvec
60 	},
61 	{ }
62 };
63 
64 struct ctl_path net_ipv6_ctl_path[] = {
65 	{ .procname = "net", },
66 	{ .procname = "ipv6", },
67 	{ },
68 };
69 EXPORT_SYMBOL_GPL(net_ipv6_ctl_path);
70 
ipv6_sysctl_net_init(struct net * net)71 static int __net_init ipv6_sysctl_net_init(struct net *net)
72 {
73 	struct ctl_table *ipv6_table;
74 	struct ctl_table *ipv6_route_table;
75 	struct ctl_table *ipv6_icmp_table;
76 	int err;
77 
78 	err = -ENOMEM;
79 	ipv6_table = kmemdup(ipv6_table_template, sizeof(ipv6_table_template),
80 			     GFP_KERNEL);
81 	if (!ipv6_table)
82 		goto out;
83 
84 	ipv6_route_table = ipv6_route_sysctl_init(net);
85 	if (!ipv6_route_table)
86 		goto out_ipv6_table;
87 	ipv6_table[0].child = ipv6_route_table;
88 
89 	ipv6_icmp_table = ipv6_icmp_sysctl_init(net);
90 	if (!ipv6_icmp_table)
91 		goto out_ipv6_route_table;
92 	ipv6_table[1].child = ipv6_icmp_table;
93 
94 	ipv6_table[2].data = &net->ipv6.sysctl.bindv6only;
95 
96 	net->ipv6.sysctl.table = register_net_sysctl_table(net, net_ipv6_ctl_path,
97 							   ipv6_table);
98 	if (!net->ipv6.sysctl.table)
99 		goto out_ipv6_icmp_table;
100 
101 	err = 0;
102 out:
103 	return err;
104 
105 out_ipv6_icmp_table:
106 	kfree(ipv6_icmp_table);
107 out_ipv6_route_table:
108 	kfree(ipv6_route_table);
109 out_ipv6_table:
110 	kfree(ipv6_table);
111 	goto out;
112 }
113 
ipv6_sysctl_net_exit(struct net * net)114 static void __net_exit ipv6_sysctl_net_exit(struct net *net)
115 {
116 	struct ctl_table *ipv6_table;
117 	struct ctl_table *ipv6_route_table;
118 	struct ctl_table *ipv6_icmp_table;
119 
120 	ipv6_table = net->ipv6.sysctl.table->ctl_table_arg;
121 	ipv6_route_table = ipv6_table[0].child;
122 	ipv6_icmp_table = ipv6_table[1].child;
123 
124 	unregister_net_sysctl_table(net->ipv6.sysctl.table);
125 
126 	kfree(ipv6_table);
127 	kfree(ipv6_route_table);
128 	kfree(ipv6_icmp_table);
129 }
130 
131 static struct pernet_operations ipv6_sysctl_net_ops = {
132 	.init = ipv6_sysctl_net_init,
133 	.exit = ipv6_sysctl_net_exit,
134 };
135 
136 static struct ctl_table_header *ip6_header;
137 
ipv6_sysctl_register(void)138 int ipv6_sysctl_register(void)
139 {
140 	int err = -ENOMEM;
141 
142 	ip6_header = register_net_sysctl_rotable(net_ipv6_ctl_path, ipv6_rotable);
143 	if (ip6_header == NULL)
144 		goto out;
145 
146 	err = register_pernet_subsys(&ipv6_sysctl_net_ops);
147 	if (err)
148 		goto err_pernet;
149 out:
150 	return err;
151 
152 err_pernet:
153 	unregister_net_sysctl_table(ip6_header);
154 	goto out;
155 }
156 
ipv6_sysctl_unregister(void)157 void ipv6_sysctl_unregister(void)
158 {
159 	unregister_net_sysctl_table(ip6_header);
160 	unregister_pernet_subsys(&ipv6_sysctl_net_ops);
161 }
162 
163 static struct ctl_table_header *ip6_base;
164 
ipv6_static_sysctl_register(void)165 int ipv6_static_sysctl_register(void)
166 {
167 	ip6_base = register_sysctl_paths(net_ipv6_ctl_path, ipv6_static_skeleton);
168 	if (ip6_base == NULL)
169 		return -ENOMEM;
170 	return 0;
171 }
172 
ipv6_static_sysctl_unregister(void)173 void ipv6_static_sysctl_unregister(void)
174 {
175 	unregister_net_sysctl_table(ip6_base);
176 }
177