1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * sysctl_net_ipv6.c: sysctl interface to net IPV6 subsystem. 4 * 5 * Changes: 6 * YOSHIFUJI Hideaki @USAGI: added icmp sysctl table. 7 */ 8 9 #include <linux/mm.h> 10 #include <linux/sysctl.h> 11 #include <linux/in6.h> 12 #include <linux/ipv6.h> 13 #include <linux/slab.h> 14 #include <linux/export.h> 15 #include <net/ndisc.h> 16 #include <net/ipv6.h> 17 #include <net/addrconf.h> 18 #include <net/inet_frag.h> 19 #include <net/netevent.h> 20 #include <net/ip_fib.h> 21 #ifdef CONFIG_NETLABEL 22 #include <net/calipso.h> 23 #endif 24 #include <linux/ioam6.h> 25 26 static int flowlabel_reflect_max = 0x7; 27 static int auto_flowlabels_max = IP6_AUTO_FLOW_LABEL_MAX; 28 static u32 rt6_multipath_hash_fields_all_mask = 29 FIB_MULTIPATH_HASH_FIELD_ALL_MASK; 30 static u32 ioam6_id_max = IOAM6_DEFAULT_ID; 31 static u64 ioam6_id_wide_max = IOAM6_DEFAULT_ID_WIDE; 32 33 static int proc_rt6_multipath_hash_policy(const struct ctl_table *table, int write, 34 void *buffer, size_t *lenp, loff_t *ppos) 35 { 36 struct net *net; 37 int ret; 38 39 net = container_of(table->data, struct net, 40 ipv6.sysctl.multipath_hash_policy); 41 ret = proc_dou8vec_minmax(table, write, buffer, lenp, ppos); 42 if (write && ret == 0) 43 call_netevent_notifiers(NETEVENT_IPV6_MPATH_HASH_UPDATE, net); 44 45 return ret; 46 } 47 48 static int 49 proc_rt6_multipath_hash_fields(const struct ctl_table *table, int write, void *buffer, 50 size_t *lenp, loff_t *ppos) 51 { 52 struct net *net; 53 int ret; 54 55 net = container_of(table->data, struct net, 56 ipv6.sysctl.multipath_hash_fields); 57 ret = proc_douintvec_minmax(table, write, buffer, lenp, ppos); 58 if (write && ret == 0) 59 call_netevent_notifiers(NETEVENT_IPV6_MPATH_HASH_UPDATE, net); 60 61 return ret; 62 } 63 64 static struct ctl_table ipv6_table_template[] = { 65 { 66 .procname = "bindv6only", 67 .data = &init_net.ipv6.sysctl.bindv6only, 68 .maxlen = sizeof(u8), 69 .mode = 0644, 70 .proc_handler = proc_dou8vec_minmax, 71 }, 72 { 73 .procname = "anycast_src_echo_reply", 74 .data = &init_net.ipv6.sysctl.anycast_src_echo_reply, 75 .maxlen = sizeof(u8), 76 .mode = 0644, 77 .proc_handler = proc_dou8vec_minmax, 78 }, 79 { 80 .procname = "flowlabel_consistency", 81 .data = &init_net.ipv6.sysctl.flowlabel_consistency, 82 .maxlen = sizeof(u8), 83 .mode = 0644, 84 .proc_handler = proc_dou8vec_minmax, 85 }, 86 { 87 .procname = "auto_flowlabels", 88 .data = &init_net.ipv6.sysctl.auto_flowlabels, 89 .maxlen = sizeof(u8), 90 .mode = 0644, 91 .proc_handler = proc_dou8vec_minmax, 92 .extra2 = &auto_flowlabels_max 93 }, 94 { 95 .procname = "fwmark_reflect", 96 .data = &init_net.ipv6.sysctl.fwmark_reflect, 97 .maxlen = sizeof(u8), 98 .mode = 0644, 99 .proc_handler = proc_dou8vec_minmax, 100 }, 101 { 102 .procname = "idgen_retries", 103 .data = &init_net.ipv6.sysctl.idgen_retries, 104 .maxlen = sizeof(int), 105 .mode = 0644, 106 .proc_handler = proc_dointvec, 107 }, 108 { 109 .procname = "idgen_delay", 110 .data = &init_net.ipv6.sysctl.idgen_delay, 111 .maxlen = sizeof(int), 112 .mode = 0644, 113 .proc_handler = proc_dointvec_jiffies, 114 }, 115 { 116 .procname = "flowlabel_state_ranges", 117 .data = &init_net.ipv6.sysctl.flowlabel_state_ranges, 118 .maxlen = sizeof(u8), 119 .mode = 0644, 120 .proc_handler = proc_dou8vec_minmax, 121 }, 122 { 123 .procname = "ip_nonlocal_bind", 124 .data = &init_net.ipv6.sysctl.ip_nonlocal_bind, 125 .maxlen = sizeof(u8), 126 .mode = 0644, 127 .proc_handler = proc_dou8vec_minmax, 128 }, 129 { 130 .procname = "flowlabel_reflect", 131 .data = &init_net.ipv6.sysctl.flowlabel_reflect, 132 .maxlen = sizeof(int), 133 .mode = 0644, 134 .proc_handler = proc_dointvec_minmax, 135 .extra1 = SYSCTL_ZERO, 136 .extra2 = &flowlabel_reflect_max, 137 }, 138 { 139 .procname = "max_dst_opts_number", 140 .data = &init_net.ipv6.sysctl.max_dst_opts_cnt, 141 .maxlen = sizeof(int), 142 .mode = 0644, 143 .proc_handler = proc_dointvec 144 }, 145 { 146 .procname = "max_hbh_opts_number", 147 .data = &init_net.ipv6.sysctl.max_hbh_opts_cnt, 148 .maxlen = sizeof(int), 149 .mode = 0644, 150 .proc_handler = proc_dointvec 151 }, 152 { 153 .procname = "max_dst_opts_length", 154 .data = &init_net.ipv6.sysctl.max_dst_opts_len, 155 .maxlen = sizeof(int), 156 .mode = 0644, 157 .proc_handler = proc_dointvec 158 }, 159 { 160 .procname = "max_hbh_length", 161 .data = &init_net.ipv6.sysctl.max_hbh_opts_len, 162 .maxlen = sizeof(int), 163 .mode = 0644, 164 .proc_handler = proc_dointvec 165 }, 166 { 167 .procname = "fib_multipath_hash_policy", 168 .data = &init_net.ipv6.sysctl.multipath_hash_policy, 169 .maxlen = sizeof(u8), 170 .mode = 0644, 171 .proc_handler = proc_rt6_multipath_hash_policy, 172 .extra1 = SYSCTL_ZERO, 173 .extra2 = SYSCTL_THREE, 174 }, 175 { 176 .procname = "fib_multipath_hash_fields", 177 .data = &init_net.ipv6.sysctl.multipath_hash_fields, 178 .maxlen = sizeof(u32), 179 .mode = 0644, 180 .proc_handler = proc_rt6_multipath_hash_fields, 181 .extra1 = SYSCTL_ONE, 182 .extra2 = &rt6_multipath_hash_fields_all_mask, 183 }, 184 { 185 .procname = "seg6_flowlabel", 186 .data = &init_net.ipv6.sysctl.seg6_flowlabel, 187 .maxlen = sizeof(int), 188 .mode = 0644, 189 .proc_handler = proc_dointvec 190 }, 191 { 192 .procname = "fib_notify_on_flag_change", 193 .data = &init_net.ipv6.sysctl.fib_notify_on_flag_change, 194 .maxlen = sizeof(u8), 195 .mode = 0644, 196 .proc_handler = proc_dou8vec_minmax, 197 .extra1 = SYSCTL_ZERO, 198 .extra2 = SYSCTL_TWO, 199 }, 200 { 201 .procname = "ioam6_id", 202 .data = &init_net.ipv6.sysctl.ioam6_id, 203 .maxlen = sizeof(u32), 204 .mode = 0644, 205 .proc_handler = proc_douintvec_minmax, 206 .extra2 = &ioam6_id_max, 207 }, 208 { 209 .procname = "ioam6_id_wide", 210 .data = &init_net.ipv6.sysctl.ioam6_id_wide, 211 .maxlen = sizeof(u64), 212 .mode = 0644, 213 .proc_handler = proc_doulongvec_minmax, 214 .extra2 = &ioam6_id_wide_max, 215 }, 216 }; 217 218 static struct ctl_table ipv6_rotable[] = { 219 { 220 .procname = "mld_max_msf", 221 .data = &sysctl_mld_max_msf, 222 .maxlen = sizeof(int), 223 .mode = 0644, 224 .proc_handler = proc_dointvec 225 }, 226 { 227 .procname = "mld_qrv", 228 .data = &sysctl_mld_qrv, 229 .maxlen = sizeof(int), 230 .mode = 0644, 231 .proc_handler = proc_dointvec_minmax, 232 .extra1 = SYSCTL_ONE 233 }, 234 #ifdef CONFIG_NETLABEL 235 { 236 .procname = "calipso_cache_enable", 237 .data = &calipso_cache_enabled, 238 .maxlen = sizeof(int), 239 .mode = 0644, 240 .proc_handler = proc_dointvec, 241 }, 242 { 243 .procname = "calipso_cache_bucket_size", 244 .data = &calipso_cache_bucketsize, 245 .maxlen = sizeof(int), 246 .mode = 0644, 247 .proc_handler = proc_dointvec, 248 }, 249 #endif /* CONFIG_NETLABEL */ 250 }; 251 252 static int __net_init ipv6_sysctl_net_init(struct net *net) 253 { 254 size_t table_size = ARRAY_SIZE(ipv6_table_template); 255 struct ctl_table *ipv6_table; 256 struct ctl_table *ipv6_route_table; 257 struct ctl_table *ipv6_icmp_table; 258 int err, i; 259 260 err = -ENOMEM; 261 ipv6_table = kmemdup(ipv6_table_template, sizeof(ipv6_table_template), 262 GFP_KERNEL); 263 if (!ipv6_table) 264 goto out; 265 /* Update the variables to point into the current struct net */ 266 for (i = 0; i < table_size; i++) 267 ipv6_table[i].data += (void *)net - (void *)&init_net; 268 269 ipv6_route_table = ipv6_route_sysctl_init(net); 270 if (!ipv6_route_table) 271 goto out_ipv6_table; 272 273 ipv6_icmp_table = ipv6_icmp_sysctl_init(net); 274 if (!ipv6_icmp_table) 275 goto out_ipv6_route_table; 276 277 net->ipv6.sysctl.hdr = register_net_sysctl_sz(net, "net/ipv6", 278 ipv6_table, table_size); 279 if (!net->ipv6.sysctl.hdr) 280 goto out_ipv6_icmp_table; 281 282 net->ipv6.sysctl.route_hdr = register_net_sysctl_sz(net, 283 "net/ipv6/route", 284 ipv6_route_table, 285 ipv6_route_sysctl_table_size(net)); 286 if (!net->ipv6.sysctl.route_hdr) 287 goto out_unregister_ipv6_table; 288 289 net->ipv6.sysctl.icmp_hdr = register_net_sysctl_sz(net, 290 "net/ipv6/icmp", 291 ipv6_icmp_table, 292 ipv6_icmp_sysctl_table_size()); 293 if (!net->ipv6.sysctl.icmp_hdr) 294 goto out_unregister_route_table; 295 296 err = 0; 297 out: 298 return err; 299 out_unregister_route_table: 300 unregister_net_sysctl_table(net->ipv6.sysctl.route_hdr); 301 out_unregister_ipv6_table: 302 unregister_net_sysctl_table(net->ipv6.sysctl.hdr); 303 out_ipv6_icmp_table: 304 kfree(ipv6_icmp_table); 305 out_ipv6_route_table: 306 kfree(ipv6_route_table); 307 out_ipv6_table: 308 kfree(ipv6_table); 309 goto out; 310 } 311 312 static void __net_exit ipv6_sysctl_net_exit(struct net *net) 313 { 314 const struct ctl_table *ipv6_table; 315 const struct ctl_table *ipv6_route_table; 316 const struct ctl_table *ipv6_icmp_table; 317 318 ipv6_table = net->ipv6.sysctl.hdr->ctl_table_arg; 319 ipv6_route_table = net->ipv6.sysctl.route_hdr->ctl_table_arg; 320 ipv6_icmp_table = net->ipv6.sysctl.icmp_hdr->ctl_table_arg; 321 322 unregister_net_sysctl_table(net->ipv6.sysctl.icmp_hdr); 323 unregister_net_sysctl_table(net->ipv6.sysctl.route_hdr); 324 unregister_net_sysctl_table(net->ipv6.sysctl.hdr); 325 326 kfree(ipv6_table); 327 kfree(ipv6_route_table); 328 kfree(ipv6_icmp_table); 329 } 330 331 static struct pernet_operations ipv6_sysctl_net_ops = { 332 .init = ipv6_sysctl_net_init, 333 .exit = ipv6_sysctl_net_exit, 334 }; 335 336 static struct ctl_table_header *ip6_header; 337 338 int ipv6_sysctl_register(void) 339 { 340 int err = -ENOMEM; 341 342 ip6_header = register_net_sysctl(&init_net, "net/ipv6", ipv6_rotable); 343 if (!ip6_header) 344 goto out; 345 346 err = register_pernet_subsys(&ipv6_sysctl_net_ops); 347 if (err) 348 goto err_pernet; 349 out: 350 return err; 351 352 err_pernet: 353 unregister_net_sysctl_table(ip6_header); 354 goto out; 355 } 356 357 void ipv6_sysctl_unregister(void) 358 { 359 unregister_net_sysctl_table(ip6_header); 360 unregister_pernet_subsys(&ipv6_sysctl_net_ops); 361 } 362