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