1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved. 23 * Copyright (c) 1990 Mentat Inc. 24 */ 25 26 #include <inet/tunables.h> 27 #include <sys/md5.h> 28 #include <inet/common.h> 29 #include <inet/ip.h> 30 #include <inet/ip6.h> 31 #include <netinet/icmp6.h> 32 #include <inet/ip_stack.h> 33 #include <inet/rawip_impl.h> 34 #include <inet/tcp_stack.h> 35 #include <inet/tcp_impl.h> 36 #include <inet/udp_impl.h> 37 #include <inet/sctp/sctp_stack.h> 38 #include <inet/sctp/sctp_impl.h> 39 #include <inet/tunables.h> 40 41 static int 42 prop_perm2const(mod_prop_info_t *pinfo) 43 { 44 if (pinfo->mpi_setf == NULL) 45 return (MOD_PROP_PERM_READ); 46 if (pinfo->mpi_getf == NULL) 47 return (MOD_PROP_PERM_WRITE); 48 return (MOD_PROP_PERM_RW); 49 } 50 51 /* 52 * Modifies the value of the property to default value or to the `pval' 53 * specified by the user. 54 */ 55 /* ARGSUSED */ 56 int 57 mod_set_boolean(void *cbarg, cred_t *cr, mod_prop_info_t *pinfo, 58 const char *ifname, const void* pval, uint_t flags) 59 { 60 char *end; 61 unsigned long new_value; 62 63 if (flags & MOD_PROP_DEFAULT) { 64 pinfo->prop_cur_bval = pinfo->prop_def_bval; 65 return (0); 66 } 67 68 if (ddi_strtoul(pval, &end, 10, &new_value) != 0 || *end != '\0') 69 return (EINVAL); 70 if (new_value != B_TRUE && new_value != B_FALSE) 71 return (EINVAL); 72 pinfo->prop_cur_bval = new_value; 73 return (0); 74 } 75 76 /* 77 * Retrieves property permission, default value, current value or possible 78 * values for those properties whose value type is boolean_t. 79 */ 80 /* ARGSUSED */ 81 int 82 mod_get_boolean(void *cbarg, mod_prop_info_t *pinfo, const char *ifname, 83 void *pval, uint_t psize, uint_t flags) 84 { 85 boolean_t get_def = (flags & MOD_PROP_DEFAULT); 86 boolean_t get_perm = (flags & MOD_PROP_PERM); 87 boolean_t get_range = (flags & MOD_PROP_POSSIBLE); 88 size_t nbytes; 89 90 bzero(pval, psize); 91 if (get_perm) 92 nbytes = snprintf(pval, psize, "%u", prop_perm2const(pinfo)); 93 else if (get_range) 94 nbytes = snprintf(pval, psize, "%u,%u", B_FALSE, B_TRUE); 95 else if (get_def) 96 nbytes = snprintf(pval, psize, "%u", pinfo->prop_def_bval); 97 else 98 nbytes = snprintf(pval, psize, "%u", pinfo->prop_cur_bval); 99 if (nbytes >= psize) 100 return (ENOBUFS); 101 return (0); 102 } 103 104 int 105 mod_uint32_value(const void *pval, mod_prop_info_t *pinfo, uint_t flags, 106 ulong_t *new_value) 107 { 108 char *end; 109 110 if (flags & MOD_PROP_DEFAULT) { 111 *new_value = pinfo->prop_def_uval; 112 return (0); 113 } 114 115 if (ddi_strtoul(pval, &end, 10, (ulong_t *)new_value) != 0 || 116 *end != '\0') 117 return (EINVAL); 118 if (*new_value < pinfo->prop_min_uval || 119 *new_value > pinfo->prop_max_uval) { 120 return (ERANGE); 121 } 122 return (0); 123 } 124 125 /* 126 * Modifies the value of the property to default value or to the `pval' 127 * specified by the user. 128 */ 129 /* ARGSUSED */ 130 int 131 mod_set_uint32(void *cbarg, cred_t *cr, mod_prop_info_t *pinfo, 132 const char *ifname, const void *pval, uint_t flags) 133 { 134 unsigned long new_value; 135 int err; 136 137 if ((err = mod_uint32_value(pval, pinfo, flags, &new_value)) != 0) 138 return (err); 139 pinfo->prop_cur_uval = (uint32_t)new_value; 140 return (0); 141 } 142 143 /* 144 * Rounds up the value to make it multiple of 8. 145 */ 146 /* ARGSUSED */ 147 int 148 mod_set_aligned(void *cbarg, cred_t *cr, mod_prop_info_t *pinfo, 149 const char *ifname, const void* pval, uint_t flags) 150 { 151 int err; 152 153 if ((err = mod_set_uint32(cbarg, cr, pinfo, ifname, pval, flags)) != 0) 154 return (err); 155 156 /* if required, align the value to multiple of 8 */ 157 if (pinfo->prop_cur_uval & 0x7) { 158 pinfo->prop_cur_uval &= ~0x7; 159 pinfo->prop_cur_uval += 0x8; 160 } 161 162 return (0); 163 } 164 165 /* 166 * Retrieves property permission, default value, current value or possible 167 * values for those properties whose value type is uint32_t. 168 */ 169 /* ARGSUSED */ 170 int 171 mod_get_uint32(void *cbarg, mod_prop_info_t *pinfo, const char *ifname, 172 void *pval, uint_t psize, uint_t flags) 173 { 174 boolean_t get_def = (flags & MOD_PROP_DEFAULT); 175 boolean_t get_perm = (flags & MOD_PROP_PERM); 176 boolean_t get_range = (flags & MOD_PROP_POSSIBLE); 177 size_t nbytes; 178 179 bzero(pval, psize); 180 if (get_perm) 181 nbytes = snprintf(pval, psize, "%u", prop_perm2const(pinfo)); 182 else if (get_range) 183 nbytes = snprintf(pval, psize, "%u-%u", 184 pinfo->prop_min_uval, pinfo->prop_max_uval); 185 else if (get_def) 186 nbytes = snprintf(pval, psize, "%u", pinfo->prop_def_uval); 187 else 188 nbytes = snprintf(pval, psize, "%u", pinfo->prop_cur_uval); 189 if (nbytes >= psize) 190 return (ENOBUFS); 191 return (0); 192 } 193 194 /* 195 * Implements /sbin/ndd -get /dev/ip ?, for all the modules. Needed for 196 * backward compatibility with /sbin/ndd. 197 */ 198 /* ARGSUSED */ 199 int 200 mod_get_allprop(void *cbarg, mod_prop_info_t *pinfo, const char *ifname, 201 void *val, uint_t psize, uint_t flags) 202 { 203 char *pval = val; 204 mod_prop_info_t *ptbl, *prop; 205 ip_stack_t *ipst; 206 tcp_stack_t *tcps; 207 sctp_stack_t *sctps; 208 udp_stack_t *us; 209 icmp_stack_t *is; 210 uint_t size; 211 size_t nbytes = 0, tbytes = 0; 212 213 bzero(pval, psize); 214 size = psize; 215 216 switch (pinfo->mpi_proto) { 217 case MOD_PROTO_IP: 218 case MOD_PROTO_IPV4: 219 case MOD_PROTO_IPV6: 220 ipst = (ip_stack_t *)cbarg; 221 ptbl = ipst->ips_propinfo_tbl; 222 break; 223 case MOD_PROTO_RAWIP: 224 is = (icmp_stack_t *)cbarg; 225 ptbl = is->is_propinfo_tbl; 226 break; 227 case MOD_PROTO_TCP: 228 tcps = (tcp_stack_t *)cbarg; 229 ptbl = tcps->tcps_propinfo_tbl; 230 break; 231 case MOD_PROTO_UDP: 232 us = (udp_stack_t *)cbarg; 233 ptbl = us->us_propinfo_tbl; 234 break; 235 case MOD_PROTO_SCTP: 236 sctps = (sctp_stack_t *)cbarg; 237 ptbl = sctps->sctps_propinfo_tbl; 238 break; 239 default: 240 return (EINVAL); 241 } 242 243 for (prop = ptbl; prop->mpi_name != NULL; prop++) { 244 if (prop->mpi_name[0] == '\0' || 245 strcmp(prop->mpi_name, "mtu") == 0 || 246 strcmp(prop->mpi_name, "?") == 0) 247 continue; 248 nbytes = snprintf(pval, size, "%s %d %d", prop->mpi_name, 249 prop->mpi_proto, prop_perm2const(prop)); 250 size -= nbytes + 1; 251 pval += nbytes + 1; 252 tbytes += nbytes + 1; 253 if (tbytes >= psize) { 254 /* Buffer overflow, stop copying information */ 255 return (ENOBUFS); 256 } 257 } 258 return (0); 259 } 260 261 /* 262 * Hold a lock while changing *_epriv_ports to prevent multiple 263 * threads from changing it at the same time. 264 */ 265 /* ARGSUSED */ 266 int 267 mod_set_extra_privports(void *cbarg, cred_t *cr, mod_prop_info_t *pinfo, 268 const char *ifname, const void* val, uint_t flags) 269 { 270 uint_t proto = pinfo->mpi_proto; 271 tcp_stack_t *tcps; 272 sctp_stack_t *sctps; 273 udp_stack_t *us; 274 unsigned long new_value; 275 char *end; 276 kmutex_t *lock; 277 uint_t i, nports; 278 in_port_t *ports; 279 boolean_t def = (flags & MOD_PROP_DEFAULT); 280 const char *pval = val; 281 282 if (!def) { 283 if (ddi_strtoul(pval, &end, 10, &new_value) != 0 || 284 *end != '\0') { 285 return (EINVAL); 286 } 287 288 if (new_value < pinfo->prop_min_uval || 289 new_value > pinfo->prop_max_uval) { 290 return (ERANGE); 291 } 292 } 293 294 switch (proto) { 295 case MOD_PROTO_TCP: 296 tcps = (tcp_stack_t *)cbarg; 297 lock = &tcps->tcps_epriv_port_lock; 298 ports = tcps->tcps_g_epriv_ports; 299 nports = tcps->tcps_g_num_epriv_ports; 300 break; 301 case MOD_PROTO_UDP: 302 us = (udp_stack_t *)cbarg; 303 lock = &us->us_epriv_port_lock; 304 ports = us->us_epriv_ports; 305 nports = us->us_num_epriv_ports; 306 break; 307 case MOD_PROTO_SCTP: 308 sctps = (sctp_stack_t *)cbarg; 309 lock = &sctps->sctps_epriv_port_lock; 310 ports = sctps->sctps_g_epriv_ports; 311 nports = sctps->sctps_g_num_epriv_ports; 312 break; 313 default: 314 return (ENOTSUP); 315 } 316 317 mutex_enter(lock); 318 319 /* if MOD_PROP_DEFAULT is set then reset the ports list to default */ 320 if (def) { 321 for (i = 0; i < nports; i++) 322 ports[i] = 0; 323 ports[0] = ULP_DEF_EPRIV_PORT1; 324 ports[1] = ULP_DEF_EPRIV_PORT2; 325 mutex_exit(lock); 326 return (0); 327 } 328 329 /* Check if the value is already in the list */ 330 for (i = 0; i < nports; i++) { 331 if (new_value == ports[i]) 332 break; 333 } 334 335 if (flags & MOD_PROP_REMOVE) { 336 if (i == nports) { 337 mutex_exit(lock); 338 return (ESRCH); 339 } 340 /* Clear the value */ 341 ports[i] = 0; 342 } else if (flags & MOD_PROP_APPEND) { 343 if (i != nports) { 344 mutex_exit(lock); 345 return (EEXIST); 346 } 347 348 /* Find an empty slot */ 349 for (i = 0; i < nports; i++) { 350 if (ports[i] == 0) 351 break; 352 } 353 if (i == nports) { 354 mutex_exit(lock); 355 return (EOVERFLOW); 356 } 357 /* Set the new value */ 358 ports[i] = (in_port_t)new_value; 359 } else { 360 /* 361 * If the user used 'assignment' modifier. 362 * For eg: 363 * # ipadm set-prop -p extra_priv_ports=3001 tcp 364 * 365 * We clear all the ports and then just add 3001. 366 */ 367 ASSERT(flags == MOD_PROP_ACTIVE); 368 for (i = 0; i < nports; i++) 369 ports[i] = 0; 370 ports[0] = (in_port_t)new_value; 371 } 372 373 mutex_exit(lock); 374 return (0); 375 } 376 377 /* 378 * Note: No locks are held when inspecting *_epriv_ports 379 * but instead the code relies on: 380 * - the fact that the address of the array and its size never changes 381 * - the atomic assignment of the elements of the array 382 */ 383 /* ARGSUSED */ 384 int 385 mod_get_extra_privports(void *cbarg, mod_prop_info_t *pinfo, const char *ifname, 386 void *val, uint_t psize, uint_t flags) 387 { 388 uint_t proto = pinfo->mpi_proto; 389 tcp_stack_t *tcps; 390 sctp_stack_t *sctps; 391 udp_stack_t *us; 392 uint_t i, nports, size; 393 in_port_t *ports; 394 char *pval = val; 395 size_t nbytes = 0, tbytes = 0; 396 boolean_t get_def = (flags & MOD_PROP_DEFAULT); 397 boolean_t get_perm = (flags & MOD_PROP_PERM); 398 boolean_t get_range = (flags & MOD_PROP_POSSIBLE); 399 400 bzero(pval, psize); 401 size = psize; 402 403 if (get_def) { 404 tbytes = snprintf(pval, psize, "%u,%u", ULP_DEF_EPRIV_PORT1, 405 ULP_DEF_EPRIV_PORT2); 406 goto ret; 407 } else if (get_perm) { 408 tbytes = snprintf(pval, psize, "%u", MOD_PROP_PERM_RW); 409 goto ret; 410 } 411 412 switch (proto) { 413 case MOD_PROTO_TCP: 414 tcps = (tcp_stack_t *)cbarg; 415 ports = tcps->tcps_g_epriv_ports; 416 nports = tcps->tcps_g_num_epriv_ports; 417 break; 418 case MOD_PROTO_UDP: 419 us = (udp_stack_t *)cbarg; 420 ports = us->us_epriv_ports; 421 nports = us->us_num_epriv_ports; 422 break; 423 case MOD_PROTO_SCTP: 424 sctps = (sctp_stack_t *)cbarg; 425 ports = sctps->sctps_g_epriv_ports; 426 nports = sctps->sctps_g_num_epriv_ports; 427 break; 428 default: 429 return (ENOTSUP); 430 } 431 432 if (get_range) { 433 tbytes = snprintf(pval, psize, "%u-%u", pinfo->prop_min_uval, 434 pinfo->prop_max_uval); 435 goto ret; 436 } 437 438 for (i = 0; i < nports; i++) { 439 if (ports[i] != 0) { 440 if (psize == size) 441 nbytes = snprintf(pval, size, "%u", ports[i]); 442 else 443 nbytes = snprintf(pval, size, ",%u", ports[i]); 444 size -= nbytes; 445 pval += nbytes; 446 tbytes += nbytes; 447 if (tbytes >= psize) 448 return (ENOBUFS); 449 } 450 } 451 return (0); 452 ret: 453 if (tbytes >= psize) 454 return (ENOBUFS); 455 return (0); 456 } 457