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