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