Print this page
    
4596 Callers of ip_srcid_find_id() need to be more careful
    
      
        | Split | 
	Close | 
      
      | Expand all | 
      | Collapse all | 
    
    
          --- old/usr/src/uts/common/inet/ip/ip_srcid.c
          +++ new/usr/src/uts/common/inet/ip/ip_srcid.c
   1    1  /*
   2    2   * CDDL HEADER START
   3    3   *
   4    4   * The contents of this file are subject to the terms of the
   5    5   * Common Development and Distribution License (the "License").
   6    6   * You may not use this file except in compliance with the License.
   7    7   *
   8    8   * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9    9   * or http://www.opensolaris.org/os/licensing.
  10   10   * See the License for the specific language governing permissions
  11   11   * and limitations under the License.
  12   12   *
  13   13   * When distributing Covered Code, include this CDDL HEADER in each
  
    | 
      ↓ open down ↓ | 
    13 lines elided | 
    
      ↑ open up ↑ | 
  
  14   14   * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15   15   * If applicable, add the following below this CDDL HEADER, with the
  16   16   * fields enclosed by brackets "[]" replaced with your own identifying
  17   17   * information: Portions Copyright [yyyy] [name of copyright owner]
  18   18   *
  19   19   * CDDL HEADER END
  20   20   */
  21   21  /*
  22   22   * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  23   23   * Use is subject to license terms.
       24 + *
       25 + * Copyright 2014, OmniTI Computer Consulting, Inc. All rights reserved.
  24   26   */
  25   27  
  26   28  /*
  27   29   * This is used to support the hidden __sin6_src_id in the sockaddr_in6
  28   30   * structure which is there to ensure that applications (such as UDP apps)
  29   31   * which get an address from recvfrom and use that address in a sendto
  30   32   * or connect will by default use the same source address in the "response"
  31   33   * as the destination address in the "request" they received.
  32   34   *
  33   35   * This is built using some new functions (in IP - doing their own locking
  34   36   * so they can be called from the transports) to map between integer IDs
  35   37   * and in6_addr_t.
  36   38   * The use applies to sockaddr_in6 - whether or not mapped addresses are used.
  37   39   *
  38   40   * This file contains the functions used by both IP and the transports
  39   41   * to implement __sin6_src_id.
  40   42   * The routines do their own locking since they are called from
  41   43   * the transports (to map between a source id and an address)
  42   44   * and from IP proper when IP addresses are added and removed.
  43   45   *
  44   46   * The routines handle both IPv4 and IPv6 with the IPv4 addresses represented
  45   47   * as IPv4-mapped addresses.
  46   48   */
  47   49  
  48   50  #include <sys/types.h>
  49   51  #include <sys/stream.h>
  50   52  #include <sys/dlpi.h>
  51   53  #include <sys/stropts.h>
  52   54  #include <sys/sysmacros.h>
  53   55  #include <sys/strsubr.h>
  54   56  #include <sys/strlog.h>
  55   57  #define _SUN_TPI_VERSION 2
  56   58  #include <sys/tihdr.h>
  57   59  #include <sys/xti_inet.h>
  58   60  #include <sys/ddi.h>
  59   61  #include <sys/cmn_err.h>
  60   62  #include <sys/debug.h>
  61   63  #include <sys/modctl.h>
  62   64  #include <sys/atomic.h>
  63   65  #include <sys/zone.h>
  64   66  
  65   67  #include <sys/systm.h>
  66   68  #include <sys/param.h>
  67   69  #include <sys/kmem.h>
  68   70  #include <sys/callb.h>
  69   71  #include <sys/socket.h>
  70   72  #include <sys/vtrace.h>
  71   73  #include <sys/isa_defs.h>
  72   74  #include <sys/kmem.h>
  73   75  #include <net/if.h>
  74   76  #include <net/if_arp.h>
  75   77  #include <net/route.h>
  76   78  #include <sys/sockio.h>
  77   79  #include <netinet/in.h>
  78   80  #include <net/if_dl.h>
  79   81  
  80   82  #include <inet/common.h>
  81   83  #include <inet/mi.h>
  82   84  #include <inet/mib2.h>
  83   85  #include <inet/nd.h>
  84   86  #include <inet/arp.h>
  85   87  #include <inet/snmpcom.h>
  86   88  
  87   89  #include <netinet/igmp_var.h>
  88   90  #include <netinet/ip6.h>
  89   91  #include <netinet/icmp6.h>
  90   92  
  91   93  #include <inet/ip.h>
  92   94  #include <inet/ip6.h>
  93   95  #include <inet/tcp.h>
  94   96  #include <inet/ip_multi.h>
  95   97  #include <inet/ip_if.h>
  96   98  #include <inet/ip_ire.h>
  97   99  #include <inet/ip_rts.h>
  98  100  #include <inet/optcom.h>
  99  101  #include <inet/ip_ndp.h>
 100  102  #include <netinet/igmp.h>
 101  103  #include <netinet/ip_mroute.h>
 102  104  #include <inet/ipclassifier.h>
 103  105  
 104  106  #include <sys/kmem.h>
 105  107  
 106  108  static uint_t           srcid_nextid(ip_stack_t *);
 107  109  static srcid_map_t      **srcid_lookup_addr(const in6_addr_t *addr,
 108  110      zoneid_t zoneid, ip_stack_t *);
 109  111  static srcid_map_t      **srcid_lookup_id(uint_t id, ip_stack_t *);
 110  112  
 111  113  
 112  114  /*
 113  115   * Insert/add a new address to the map.
 114  116   * Returns zero if ok; otherwise errno (e.g. for memory allocation failure).
 115  117   */
 116  118  int
 117  119  ip_srcid_insert(const in6_addr_t *addr, zoneid_t zoneid, ip_stack_t *ipst)
 118  120  {
 119  121          srcid_map_t     **smpp;
 120  122  #ifdef DEBUG
 121  123          char            abuf[INET6_ADDRSTRLEN];
 122  124  
 123  125          ip1dbg(("ip_srcid_insert(%s, %d)\n",
 124  126              inet_ntop(AF_INET6, addr, abuf, sizeof (abuf)), zoneid));
 125  127  #endif
 126  128  
 127  129          rw_enter(&ipst->ips_srcid_lock, RW_WRITER);
 128  130          smpp = srcid_lookup_addr(addr, zoneid, ipst);
 129  131          if (*smpp != NULL) {
 130  132                  /* Already present - increment refcount */
 131  133                  (*smpp)->sm_refcnt++;
 132  134                  ASSERT((*smpp)->sm_refcnt != 0);        /* wraparound */
 133  135                  rw_exit(&ipst->ips_srcid_lock);
 134  136                  return (0);
 135  137          }
 136  138          /* Insert new */
 137  139          *smpp = kmem_alloc(sizeof (srcid_map_t), KM_NOSLEEP);
 138  140          if (*smpp == NULL) {
 139  141                  rw_exit(&ipst->ips_srcid_lock);
 140  142                  return (ENOMEM);
 141  143          }
 142  144          (*smpp)->sm_next = NULL;
 143  145          (*smpp)->sm_addr = *addr;
 144  146          (*smpp)->sm_srcid = srcid_nextid(ipst);
 145  147          (*smpp)->sm_refcnt = 1;
 146  148          (*smpp)->sm_zoneid = zoneid;
 147  149  
 148  150          rw_exit(&ipst->ips_srcid_lock);
 149  151          return (0);
 150  152  }
 151  153  
 152  154  /*
 153  155   * Remove an new address from the map.
 154  156   * Returns zero if ok; otherwise errno (e.g. for nonexistent address).
 155  157   */
 156  158  int
 157  159  ip_srcid_remove(const in6_addr_t *addr, zoneid_t zoneid, ip_stack_t *ipst)
 158  160  {
 159  161          srcid_map_t     **smpp;
 160  162          srcid_map_t     *smp;
 161  163  #ifdef DEBUG
 162  164          char            abuf[INET6_ADDRSTRLEN];
 163  165  
 164  166          ip1dbg(("ip_srcid_remove(%s, %d)\n",
 165  167              inet_ntop(AF_INET6, addr, abuf, sizeof (abuf)), zoneid));
 166  168  #endif
 167  169  
 168  170          rw_enter(&ipst->ips_srcid_lock, RW_WRITER);
 169  171          smpp = srcid_lookup_addr(addr, zoneid, ipst);
 170  172          smp = *smpp;
 171  173          if (smp == NULL) {
 172  174                  /* Not preset */
 173  175                  rw_exit(&ipst->ips_srcid_lock);
 174  176                  return (ENOENT);
 175  177          }
 176  178  
 177  179          /* Decrement refcount */
 178  180          ASSERT(smp->sm_refcnt != 0);
 179  181          smp->sm_refcnt--;
 180  182          if (smp->sm_refcnt != 0) {
 181  183                  rw_exit(&ipst->ips_srcid_lock);
 182  184                  return (0);
 183  185          }
 184  186          /* Remove entry */
 185  187          *smpp = smp->sm_next;
 186  188          rw_exit(&ipst->ips_srcid_lock);
 187  189          smp->sm_next = NULL;
 188  190          kmem_free(smp, sizeof (srcid_map_t));
 189  191          return (0);
 190  192  }
 191  193  
 192  194  /*
 193  195   * Map from an address to a source id.
 194  196   * If the address is unknown return the unknown id (zero).
 195  197   */
 196  198  uint_t
 197  199  ip_srcid_find_addr(const in6_addr_t *addr, zoneid_t zoneid,
 198  200      netstack_t *ns)
 199  201  {
 200  202          srcid_map_t     **smpp;
 201  203          srcid_map_t     *smp;
 202  204          uint_t          id;
 203  205          ip_stack_t      *ipst = ns->netstack_ip;
 204  206  
 205  207          rw_enter(&ipst->ips_srcid_lock, RW_READER);
 206  208          smpp = srcid_lookup_addr(addr, zoneid, ipst);
 207  209          smp = *smpp;
 208  210          if (smp == NULL) {
 209  211                  char            abuf[INET6_ADDRSTRLEN];
 210  212  
 211  213                  /* Not present - could be broadcast or multicast address */
 212  214                  ip1dbg(("ip_srcid_find_addr: unknown %s in zone %d\n",
 213  215                      inet_ntop(AF_INET6, addr, abuf, sizeof (abuf)), zoneid));
 214  216                  id = 0;
 215  217          } else {
  
    | 
      ↓ open down ↓ | 
    182 lines elided | 
    
      ↑ open up ↑ | 
  
 216  218                  ASSERT(smp->sm_refcnt != 0);
 217  219                  id = smp->sm_srcid;
 218  220          }
 219  221          rw_exit(&ipst->ips_srcid_lock);
 220  222          return (id);
 221  223  }
 222  224  
 223  225  /*
 224  226   * Map from a source id to an address.
 225  227   * If the id is unknown return the unspecified address.
      228 + *
      229 + * For known IDs, check if the returned address is v4mapped or not, and
      230 + * return B_TRUE if it matches the desired v4mapped state or not.  This
      231 + * prevents a broken app from requesting (via __sin6_src_id) a v4mapped
      232 + * address for a v6 destination, or vice versa.
      233 + *
      234 + * "addr" will not be set if we return B_FALSE.
 226  235   */
 227      -void
      236 +boolean_t
 228  237  ip_srcid_find_id(uint_t id, in6_addr_t *addr, zoneid_t zoneid,
 229      -    netstack_t *ns)
      238 +    boolean_t v4mapped, netstack_t *ns)
 230  239  {
 231  240          srcid_map_t     **smpp;
 232  241          srcid_map_t     *smp;
 233  242          ip_stack_t      *ipst = ns->netstack_ip;
      243 +        boolean_t       rc;
 234  244  
 235  245          rw_enter(&ipst->ips_srcid_lock, RW_READER);
 236  246          smpp = srcid_lookup_id(id, ipst);
 237  247          smp = *smpp;
 238  248          if (smp == NULL || (smp->sm_zoneid != zoneid && zoneid != ALL_ZONES)) {
 239  249                  /* Not preset */
 240  250                  ip1dbg(("ip_srcid_find_id: unknown %u or in wrong zone\n", id));
 241  251                  *addr = ipv6_all_zeros;
      252 +                rc = B_TRUE;
 242  253          } else {
 243  254                  ASSERT(smp->sm_refcnt != 0);
 244      -                *addr = smp->sm_addr;
      255 +                /*
      256 +                 * The caller tells us if it expects a v4mapped address.
      257 +                 * Use it, along with the property of "addr" to set the rc.
      258 +                 */
      259 +                if (IN6_IS_ADDR_V4MAPPED(&smp->sm_addr))
      260 +                        rc = v4mapped;  /* We want a v4mapped address. */
      261 +                else
      262 +                        rc = !v4mapped; /* We don't want a v4mapped address. */
      263 +
      264 +                if (rc)
      265 +                        *addr = smp->sm_addr;
      266 +
 245  267          }
 246  268          rw_exit(&ipst->ips_srcid_lock);
      269 +        return (rc);
 247  270  }
 248  271  
 249  272  /* Assign the next available ID */
 250  273  static uint_t
 251  274  srcid_nextid(ip_stack_t *ipst)
 252  275  {
 253  276          uint_t id;
 254  277          srcid_map_t     **smpp;
 255  278  
 256  279          ASSERT(rw_owner(&ipst->ips_srcid_lock) == curthread);
 257  280  
 258  281          if (!ipst->ips_srcid_wrapped) {
 259  282                  id = ipst->ips_ip_src_id++;
 260  283                  if (ipst->ips_ip_src_id == 0)
 261  284                          ipst->ips_srcid_wrapped = B_TRUE;
 262  285                  return (id);
 263  286          }
 264  287          /* Once it wraps we search for an unused ID. */
 265  288          for (id = 0; id < 0xffffffff; id++) {
 266  289                  smpp = srcid_lookup_id(id, ipst);
 267  290                  if (*smpp == NULL)
 268  291                          return (id);
 269  292          }
 270  293          panic("srcid_nextid: No free identifiers!");
 271  294          /* NOTREACHED */
 272  295  }
 273  296  
 274  297  /*
 275  298   * Lookup based on address.
 276  299   * Always returns a non-null pointer.
 277  300   * If found then *ptr will be the found object.
 278  301   * Otherwise *ptr will be NULL and can be used to insert a new object.
 279  302   */
 280  303  static srcid_map_t **
 281  304  srcid_lookup_addr(const in6_addr_t *addr, zoneid_t zoneid, ip_stack_t *ipst)
 282  305  {
 283  306          srcid_map_t     **smpp;
 284  307  
 285  308          ASSERT(RW_LOCK_HELD(&ipst->ips_srcid_lock));
 286  309          smpp = &ipst->ips_srcid_head;
 287  310          while (*smpp != NULL) {
 288  311                  if (IN6_ARE_ADDR_EQUAL(&(*smpp)->sm_addr, addr) &&
 289  312                      (zoneid == (*smpp)->sm_zoneid || zoneid == ALL_ZONES))
 290  313                          return (smpp);
 291  314                  smpp = &(*smpp)->sm_next;
 292  315          }
 293  316          return (smpp);
 294  317  }
 295  318  
 296  319  /*
 297  320   * Lookup based on address.
 298  321   * Always returns a non-null pointer.
 299  322   * If found then *ptr will be the found object.
 300  323   * Otherwise *ptr will be NULL and can be used to insert a new object.
 301  324   */
 302  325  static srcid_map_t **
 303  326  srcid_lookup_id(uint_t id, ip_stack_t *ipst)
 304  327  {
 305  328          srcid_map_t     **smpp;
 306  329  
 307  330          ASSERT(RW_LOCK_HELD(&ipst->ips_srcid_lock));
 308  331          smpp = &ipst->ips_srcid_head;
 309  332          while (*smpp != NULL) {
 310  333                  if ((*smpp)->sm_srcid == id)
 311  334                          return (smpp);
 312  335                  smpp = &(*smpp)->sm_next;
 313  336          }
 314  337          return (smpp);
 315  338  }
  
    | 
      ↓ open down ↓ | 
    59 lines elided | 
    
      ↑ open up ↑ | 
  
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX