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, "?") == 0) {
 246                         continue;
 247                 }
 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 }