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 19 static int one = 1; 20 static int auto_flowlabels_min; 21 static int auto_flowlabels_max = IP6_AUTO_FLOW_LABEL_MAX; 22 23 24 static struct ctl_table ipv6_table_template[] = { 25 { 26 .procname = "bindv6only", 27 .data = &init_net.ipv6.sysctl.bindv6only, 28 .maxlen = sizeof(int), 29 .mode = 0644, 30 .proc_handler = proc_dointvec 31 }, 32 { 33 .procname = "anycast_src_echo_reply", 34 .data = &init_net.ipv6.sysctl.anycast_src_echo_reply, 35 .maxlen = sizeof(int), 36 .mode = 0644, 37 .proc_handler = proc_dointvec 38 }, 39 { 40 .procname = "flowlabel_consistency", 41 .data = &init_net.ipv6.sysctl.flowlabel_consistency, 42 .maxlen = sizeof(int), 43 .mode = 0644, 44 .proc_handler = proc_dointvec 45 }, 46 { 47 .procname = "auto_flowlabels", 48 .data = &init_net.ipv6.sysctl.auto_flowlabels, 49 .maxlen = sizeof(int), 50 .mode = 0644, 51 .proc_handler = proc_dointvec_minmax, 52 .extra1 = &auto_flowlabels_min, 53 .extra2 = &auto_flowlabels_max 54 }, 55 { 56 .procname = "fwmark_reflect", 57 .data = &init_net.ipv6.sysctl.fwmark_reflect, 58 .maxlen = sizeof(int), 59 .mode = 0644, 60 .proc_handler = proc_dointvec 61 }, 62 { 63 .procname = "idgen_retries", 64 .data = &init_net.ipv6.sysctl.idgen_retries, 65 .maxlen = sizeof(int), 66 .mode = 0644, 67 .proc_handler = proc_dointvec, 68 }, 69 { 70 .procname = "idgen_delay", 71 .data = &init_net.ipv6.sysctl.idgen_delay, 72 .maxlen = sizeof(int), 73 .mode = 0644, 74 .proc_handler = proc_dointvec_jiffies, 75 }, 76 { 77 .procname = "flowlabel_state_ranges", 78 .data = &init_net.ipv6.sysctl.flowlabel_state_ranges, 79 .maxlen = sizeof(int), 80 .mode = 0644, 81 .proc_handler = proc_dointvec 82 }, 83 { 84 .procname = "ip_nonlocal_bind", 85 .data = &init_net.ipv6.sysctl.ip_nonlocal_bind, 86 .maxlen = sizeof(int), 87 .mode = 0644, 88 .proc_handler = proc_dointvec 89 }, 90 { } 91 }; 92 93 static struct ctl_table ipv6_rotable[] = { 94 { 95 .procname = "mld_max_msf", 96 .data = &sysctl_mld_max_msf, 97 .maxlen = sizeof(int), 98 .mode = 0644, 99 .proc_handler = proc_dointvec 100 }, 101 { 102 .procname = "mld_qrv", 103 .data = &sysctl_mld_qrv, 104 .maxlen = sizeof(int), 105 .mode = 0644, 106 .proc_handler = proc_dointvec_minmax, 107 .extra1 = &one 108 }, 109 { } 110 }; 111 112 static int __net_init ipv6_sysctl_net_init(struct net *net) 113 { 114 struct ctl_table *ipv6_table; 115 struct ctl_table *ipv6_route_table; 116 struct ctl_table *ipv6_icmp_table; 117 int err; 118 119 err = -ENOMEM; 120 ipv6_table = kmemdup(ipv6_table_template, sizeof(ipv6_table_template), 121 GFP_KERNEL); 122 if (!ipv6_table) 123 goto out; 124 ipv6_table[0].data = &net->ipv6.sysctl.bindv6only; 125 ipv6_table[1].data = &net->ipv6.sysctl.anycast_src_echo_reply; 126 ipv6_table[2].data = &net->ipv6.sysctl.flowlabel_consistency; 127 ipv6_table[3].data = &net->ipv6.sysctl.auto_flowlabels; 128 ipv6_table[4].data = &net->ipv6.sysctl.fwmark_reflect; 129 ipv6_table[5].data = &net->ipv6.sysctl.idgen_retries; 130 ipv6_table[6].data = &net->ipv6.sysctl.idgen_delay; 131 ipv6_table[7].data = &net->ipv6.sysctl.flowlabel_state_ranges; 132 ipv6_table[8].data = &net->ipv6.sysctl.ip_nonlocal_bind; 133 134 ipv6_route_table = ipv6_route_sysctl_init(net); 135 if (!ipv6_route_table) 136 goto out_ipv6_table; 137 138 ipv6_icmp_table = ipv6_icmp_sysctl_init(net); 139 if (!ipv6_icmp_table) 140 goto out_ipv6_route_table; 141 142 net->ipv6.sysctl.hdr = register_net_sysctl(net, "net/ipv6", ipv6_table); 143 if (!net->ipv6.sysctl.hdr) 144 goto out_ipv6_icmp_table; 145 146 net->ipv6.sysctl.route_hdr = 147 register_net_sysctl(net, "net/ipv6/route", ipv6_route_table); 148 if (!net->ipv6.sysctl.route_hdr) 149 goto out_unregister_ipv6_table; 150 151 net->ipv6.sysctl.icmp_hdr = 152 register_net_sysctl(net, "net/ipv6/icmp", ipv6_icmp_table); 153 if (!net->ipv6.sysctl.icmp_hdr) 154 goto out_unregister_route_table; 155 156 err = 0; 157 out: 158 return err; 159 out_unregister_route_table: 160 unregister_net_sysctl_table(net->ipv6.sysctl.route_hdr); 161 out_unregister_ipv6_table: 162 unregister_net_sysctl_table(net->ipv6.sysctl.hdr); 163 out_ipv6_icmp_table: 164 kfree(ipv6_icmp_table); 165 out_ipv6_route_table: 166 kfree(ipv6_route_table); 167 out_ipv6_table: 168 kfree(ipv6_table); 169 goto out; 170 } 171 172 static void __net_exit ipv6_sysctl_net_exit(struct net *net) 173 { 174 struct ctl_table *ipv6_table; 175 struct ctl_table *ipv6_route_table; 176 struct ctl_table *ipv6_icmp_table; 177 178 ipv6_table = net->ipv6.sysctl.hdr->ctl_table_arg; 179 ipv6_route_table = net->ipv6.sysctl.route_hdr->ctl_table_arg; 180 ipv6_icmp_table = net->ipv6.sysctl.icmp_hdr->ctl_table_arg; 181 182 unregister_net_sysctl_table(net->ipv6.sysctl.icmp_hdr); 183 unregister_net_sysctl_table(net->ipv6.sysctl.route_hdr); 184 unregister_net_sysctl_table(net->ipv6.sysctl.hdr); 185 186 kfree(ipv6_table); 187 kfree(ipv6_route_table); 188 kfree(ipv6_icmp_table); 189 } 190 191 static struct pernet_operations ipv6_sysctl_net_ops = { 192 .init = ipv6_sysctl_net_init, 193 .exit = ipv6_sysctl_net_exit, 194 }; 195 196 static struct ctl_table_header *ip6_header; 197 198 int ipv6_sysctl_register(void) 199 { 200 int err = -ENOMEM; 201 202 ip6_header = register_net_sysctl(&init_net, "net/ipv6", ipv6_rotable); 203 if (!ip6_header) 204 goto out; 205 206 err = register_pernet_subsys(&ipv6_sysctl_net_ops); 207 if (err) 208 goto err_pernet; 209 out: 210 return err; 211 212 err_pernet: 213 unregister_net_sysctl_table(ip6_header); 214 goto out; 215 } 216 217 void ipv6_sysctl_unregister(void) 218 { 219 unregister_net_sysctl_table(ip6_header); 220 unregister_pernet_subsys(&ipv6_sysctl_net_ops); 221 } 222