sysctl_net_ipv6.c (8993B)
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 26static int flowlabel_reflect_max = 0x7; 27static int auto_flowlabels_max = IP6_AUTO_FLOW_LABEL_MAX; 28static u32 rt6_multipath_hash_fields_all_mask = 29 FIB_MULTIPATH_HASH_FIELD_ALL_MASK; 30static u32 ioam6_id_max = IOAM6_DEFAULT_ID; 31static u64 ioam6_id_wide_max = IOAM6_DEFAULT_ID_WIDE; 32 33static int proc_rt6_multipath_hash_policy(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 48static int 49proc_rt6_multipath_hash_fields(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 64static 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 219static struct ctl_table ipv6_rotable[] = { 220 { 221 .procname = "mld_max_msf", 222 .data = &sysctl_mld_max_msf, 223 .maxlen = sizeof(int), 224 .mode = 0644, 225 .proc_handler = proc_dointvec 226 }, 227 { 228 .procname = "mld_qrv", 229 .data = &sysctl_mld_qrv, 230 .maxlen = sizeof(int), 231 .mode = 0644, 232 .proc_handler = proc_dointvec_minmax, 233 .extra1 = SYSCTL_ONE 234 }, 235#ifdef CONFIG_NETLABEL 236 { 237 .procname = "calipso_cache_enable", 238 .data = &calipso_cache_enabled, 239 .maxlen = sizeof(int), 240 .mode = 0644, 241 .proc_handler = proc_dointvec, 242 }, 243 { 244 .procname = "calipso_cache_bucket_size", 245 .data = &calipso_cache_bucketsize, 246 .maxlen = sizeof(int), 247 .mode = 0644, 248 .proc_handler = proc_dointvec, 249 }, 250#endif /* CONFIG_NETLABEL */ 251 { } 252}; 253 254static int __net_init ipv6_sysctl_net_init(struct net *net) 255{ 256 struct ctl_table *ipv6_table; 257 struct ctl_table *ipv6_route_table; 258 struct ctl_table *ipv6_icmp_table; 259 int err, i; 260 261 err = -ENOMEM; 262 ipv6_table = kmemdup(ipv6_table_template, sizeof(ipv6_table_template), 263 GFP_KERNEL); 264 if (!ipv6_table) 265 goto out; 266 /* Update the variables to point into the current struct net */ 267 for (i = 0; i < ARRAY_SIZE(ipv6_table_template) - 1; i++) 268 ipv6_table[i].data += (void *)net - (void *)&init_net; 269 270 ipv6_route_table = ipv6_route_sysctl_init(net); 271 if (!ipv6_route_table) 272 goto out_ipv6_table; 273 274 ipv6_icmp_table = ipv6_icmp_sysctl_init(net); 275 if (!ipv6_icmp_table) 276 goto out_ipv6_route_table; 277 278 net->ipv6.sysctl.hdr = register_net_sysctl(net, "net/ipv6", ipv6_table); 279 if (!net->ipv6.sysctl.hdr) 280 goto out_ipv6_icmp_table; 281 282 net->ipv6.sysctl.route_hdr = 283 register_net_sysctl(net, "net/ipv6/route", ipv6_route_table); 284 if (!net->ipv6.sysctl.route_hdr) 285 goto out_unregister_ipv6_table; 286 287 net->ipv6.sysctl.icmp_hdr = 288 register_net_sysctl(net, "net/ipv6/icmp", ipv6_icmp_table); 289 if (!net->ipv6.sysctl.icmp_hdr) 290 goto out_unregister_route_table; 291 292 err = 0; 293out: 294 return err; 295out_unregister_route_table: 296 unregister_net_sysctl_table(net->ipv6.sysctl.route_hdr); 297out_unregister_ipv6_table: 298 unregister_net_sysctl_table(net->ipv6.sysctl.hdr); 299out_ipv6_icmp_table: 300 kfree(ipv6_icmp_table); 301out_ipv6_route_table: 302 kfree(ipv6_route_table); 303out_ipv6_table: 304 kfree(ipv6_table); 305 goto out; 306} 307 308static void __net_exit ipv6_sysctl_net_exit(struct net *net) 309{ 310 struct ctl_table *ipv6_table; 311 struct ctl_table *ipv6_route_table; 312 struct ctl_table *ipv6_icmp_table; 313 314 ipv6_table = net->ipv6.sysctl.hdr->ctl_table_arg; 315 ipv6_route_table = net->ipv6.sysctl.route_hdr->ctl_table_arg; 316 ipv6_icmp_table = net->ipv6.sysctl.icmp_hdr->ctl_table_arg; 317 318 unregister_net_sysctl_table(net->ipv6.sysctl.icmp_hdr); 319 unregister_net_sysctl_table(net->ipv6.sysctl.route_hdr); 320 unregister_net_sysctl_table(net->ipv6.sysctl.hdr); 321 322 kfree(ipv6_table); 323 kfree(ipv6_route_table); 324 kfree(ipv6_icmp_table); 325} 326 327static struct pernet_operations ipv6_sysctl_net_ops = { 328 .init = ipv6_sysctl_net_init, 329 .exit = ipv6_sysctl_net_exit, 330}; 331 332static struct ctl_table_header *ip6_header; 333 334int ipv6_sysctl_register(void) 335{ 336 int err = -ENOMEM; 337 338 ip6_header = register_net_sysctl(&init_net, "net/ipv6", ipv6_rotable); 339 if (!ip6_header) 340 goto out; 341 342 err = register_pernet_subsys(&ipv6_sysctl_net_ops); 343 if (err) 344 goto err_pernet; 345out: 346 return err; 347 348err_pernet: 349 unregister_net_sysctl_table(ip6_header); 350 goto out; 351} 352 353void ipv6_sysctl_unregister(void) 354{ 355 unregister_net_sysctl_table(ip6_header); 356 unregister_pernet_subsys(&ipv6_sysctl_net_ops); 357}