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 }