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 2009 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 /*
  27  * This is used to support the hidden __sin6_src_id in the sockaddr_in6
  28  * structure which is there to ensure that applications (such as UDP apps)
  29  * which get an address from recvfrom and use that address in a sendto
  30  * or connect will by default use the same source address in the "response"
  31  * as the destination address in the "request" they received.
  32  *
  33  * This is built using some new functions (in IP - doing their own locking
  34  * so they can be called from the transports) to map between integer IDs
  35  * and in6_addr_t.
  36  * The use applies to sockaddr_in6 - whether or not mapped addresses are used.
  37  *
  38  * This file contains the functions used by both IP and the transports
  39  * to implement __sin6_src_id.
  40  * The routines do their own locking since they are called from
  41  * the transports (to map between a source id and an address)
  42  * and from IP proper when IP addresses are added and removed.
  43  *
  44  * The routines handle both IPv4 and IPv6 with the IPv4 addresses represented
  45  * as IPv4-mapped addresses.
  46  */
  47 
  48 #include <sys/types.h>
  49 #include <sys/stream.h>
  50 #include <sys/dlpi.h>
  51 #include <sys/stropts.h>
  52 #include <sys/sysmacros.h>
  53 #include <sys/strsubr.h>
  54 #include <sys/strlog.h>
  55 #define _SUN_TPI_VERSION 2
  56 #include <sys/tihdr.h>
  57 #include <sys/xti_inet.h>
  58 #include <sys/ddi.h>
  59 #include <sys/cmn_err.h>
  60 #include <sys/debug.h>
  61 #include <sys/modctl.h>
  62 #include <sys/atomic.h>
  63 #include <sys/zone.h>
  64 
  65 #include <sys/systm.h>
  66 #include <sys/param.h>
  67 #include <sys/kmem.h>
  68 #include <sys/callb.h>
  69 #include <sys/socket.h>
  70 #include <sys/vtrace.h>
  71 #include <sys/isa_defs.h>
  72 #include <sys/kmem.h>
  73 #include <net/if.h>
  74 #include <net/if_arp.h>
  75 #include <net/route.h>
  76 #include <sys/sockio.h>
  77 #include <netinet/in.h>
  78 #include <net/if_dl.h>
  79 
  80 #include <inet/common.h>
  81 #include <inet/mi.h>
  82 #include <inet/mib2.h>
  83 #include <inet/nd.h>
  84 #include <inet/arp.h>
  85 #include <inet/snmpcom.h>
  86 
  87 #include <netinet/igmp_var.h>
  88 #include <netinet/ip6.h>
  89 #include <netinet/icmp6.h>
  90 
  91 #include <inet/ip.h>
  92 #include <inet/ip6.h>
  93 #include <inet/tcp.h>
  94 #include <inet/ip_multi.h>
  95 #include <inet/ip_if.h>
  96 #include <inet/ip_ire.h>
  97 #include <inet/ip_rts.h>
  98 #include <inet/optcom.h>
  99 #include <inet/ip_ndp.h>
 100 #include <netinet/igmp.h>
 101 #include <netinet/ip_mroute.h>
 102 #include <inet/ipclassifier.h>
 103 
 104 #include <sys/kmem.h>
 105 
 106 static uint_t           srcid_nextid(ip_stack_t *);
 107 static srcid_map_t      **srcid_lookup_addr(const in6_addr_t *addr,
 108     zoneid_t zoneid, ip_stack_t *);
 109 static srcid_map_t      **srcid_lookup_id(uint_t id, ip_stack_t *);
 110 
 111 
 112 /*
 113  * Insert/add a new address to the map.
 114  * Returns zero if ok; otherwise errno (e.g. for memory allocation failure).
 115  */
 116 int
 117 ip_srcid_insert(const in6_addr_t *addr, zoneid_t zoneid, ip_stack_t *ipst)
 118 {
 119         srcid_map_t     **smpp;
 120 #ifdef DEBUG
 121         char            abuf[INET6_ADDRSTRLEN];
 122 
 123         ip1dbg(("ip_srcid_insert(%s, %d)\n",
 124             inet_ntop(AF_INET6, addr, abuf, sizeof (abuf)), zoneid));
 125 #endif
 126 
 127         rw_enter(&ipst->ips_srcid_lock, RW_WRITER);
 128         smpp = srcid_lookup_addr(addr, zoneid, ipst);
 129         if (*smpp != NULL) {
 130                 /* Already present - increment refcount */
 131                 (*smpp)->sm_refcnt++;
 132                 ASSERT((*smpp)->sm_refcnt != 0);     /* wraparound */
 133                 rw_exit(&ipst->ips_srcid_lock);
 134                 return (0);
 135         }
 136         /* Insert new */
 137         *smpp = kmem_alloc(sizeof (srcid_map_t), KM_NOSLEEP);
 138         if (*smpp == NULL) {
 139                 rw_exit(&ipst->ips_srcid_lock);
 140                 return (ENOMEM);
 141         }
 142         (*smpp)->sm_next = NULL;
 143         (*smpp)->sm_addr = *addr;
 144         (*smpp)->sm_srcid = srcid_nextid(ipst);
 145         (*smpp)->sm_refcnt = 1;
 146         (*smpp)->sm_zoneid = zoneid;
 147 
 148         rw_exit(&ipst->ips_srcid_lock);
 149         return (0);
 150 }
 151 
 152 /*
 153  * Remove an new address from the map.
 154  * Returns zero if ok; otherwise errno (e.g. for nonexistent address).
 155  */
 156 int
 157 ip_srcid_remove(const in6_addr_t *addr, zoneid_t zoneid, ip_stack_t *ipst)
 158 {
 159         srcid_map_t     **smpp;
 160         srcid_map_t     *smp;
 161 #ifdef DEBUG
 162         char            abuf[INET6_ADDRSTRLEN];
 163 
 164         ip1dbg(("ip_srcid_remove(%s, %d)\n",
 165             inet_ntop(AF_INET6, addr, abuf, sizeof (abuf)), zoneid));
 166 #endif
 167 
 168         rw_enter(&ipst->ips_srcid_lock, RW_WRITER);
 169         smpp = srcid_lookup_addr(addr, zoneid, ipst);
 170         smp = *smpp;
 171         if (smp == NULL) {
 172                 /* Not preset */
 173                 rw_exit(&ipst->ips_srcid_lock);
 174                 return (ENOENT);
 175         }
 176 
 177         /* Decrement refcount */
 178         ASSERT(smp->sm_refcnt != 0);
 179         smp->sm_refcnt--;
 180         if (smp->sm_refcnt != 0) {
 181                 rw_exit(&ipst->ips_srcid_lock);
 182                 return (0);
 183         }
 184         /* Remove entry */
 185         *smpp = smp->sm_next;
 186         rw_exit(&ipst->ips_srcid_lock);
 187         smp->sm_next = NULL;
 188         kmem_free(smp, sizeof (srcid_map_t));
 189         return (0);
 190 }
 191 
 192 /*
 193  * Map from an address to a source id.
 194  * If the address is unknown return the unknown id (zero).
 195  */
 196 uint_t
 197 ip_srcid_find_addr(const in6_addr_t *addr, zoneid_t zoneid,
 198     netstack_t *ns)
 199 {
 200         srcid_map_t     **smpp;
 201         srcid_map_t     *smp;
 202         uint_t          id;
 203         ip_stack_t      *ipst = ns->netstack_ip;
 204 
 205         rw_enter(&ipst->ips_srcid_lock, RW_READER);
 206         smpp = srcid_lookup_addr(addr, zoneid, ipst);
 207         smp = *smpp;
 208         if (smp == NULL) {
 209                 char            abuf[INET6_ADDRSTRLEN];
 210 
 211                 /* Not present - could be broadcast or multicast address */
 212                 ip1dbg(("ip_srcid_find_addr: unknown %s in zone %d\n",
 213                     inet_ntop(AF_INET6, addr, abuf, sizeof (abuf)), zoneid));
 214                 id = 0;
 215         } else {
 216                 ASSERT(smp->sm_refcnt != 0);
 217                 id = smp->sm_srcid;
 218         }
 219         rw_exit(&ipst->ips_srcid_lock);
 220         return (id);
 221 }
 222 
 223 /*
 224  * Map from a source id to an address.
 225  * If the id is unknown return the unspecified address.
 226  */
 227 void
 228 ip_srcid_find_id(uint_t id, in6_addr_t *addr, zoneid_t zoneid,
 229     netstack_t *ns)
 230 {
 231         srcid_map_t     **smpp;
 232         srcid_map_t     *smp;
 233         ip_stack_t      *ipst = ns->netstack_ip;
 234 
 235         rw_enter(&ipst->ips_srcid_lock, RW_READER);
 236         smpp = srcid_lookup_id(id, ipst);
 237         smp = *smpp;
 238         if (smp == NULL || (smp->sm_zoneid != zoneid && zoneid != ALL_ZONES)) {
 239                 /* Not preset */
 240                 ip1dbg(("ip_srcid_find_id: unknown %u or in wrong zone\n", id));
 241                 *addr = ipv6_all_zeros;
 242         } else {
 243                 ASSERT(smp->sm_refcnt != 0);
 244                 *addr = smp->sm_addr;
 245         }
 246         rw_exit(&ipst->ips_srcid_lock);
 247 }
 248 
 249 /* Assign the next available ID */
 250 static uint_t
 251 srcid_nextid(ip_stack_t *ipst)
 252 {
 253         uint_t id;
 254         srcid_map_t     **smpp;
 255 
 256         ASSERT(rw_owner(&ipst->ips_srcid_lock) == curthread);
 257 
 258         if (!ipst->ips_srcid_wrapped) {
 259                 id = ipst->ips_ip_src_id++;
 260                 if (ipst->ips_ip_src_id == 0)
 261                         ipst->ips_srcid_wrapped = B_TRUE;
 262                 return (id);
 263         }
 264         /* Once it wraps we search for an unused ID. */
 265         for (id = 0; id < 0xffffffff; id++) {
 266                 smpp = srcid_lookup_id(id, ipst);
 267                 if (*smpp == NULL)
 268                         return (id);
 269         }
 270         panic("srcid_nextid: No free identifiers!");
 271         /* NOTREACHED */
 272 }
 273 
 274 /*
 275  * Lookup based on address.
 276  * Always returns a non-null pointer.
 277  * If found then *ptr will be the found object.
 278  * Otherwise *ptr will be NULL and can be used to insert a new object.
 279  */
 280 static srcid_map_t **
 281 srcid_lookup_addr(const in6_addr_t *addr, zoneid_t zoneid, ip_stack_t *ipst)
 282 {
 283         srcid_map_t     **smpp;
 284 
 285         ASSERT(RW_LOCK_HELD(&ipst->ips_srcid_lock));
 286         smpp = &ipst->ips_srcid_head;
 287         while (*smpp != NULL) {
 288                 if (IN6_ARE_ADDR_EQUAL(&(*smpp)->sm_addr, addr) &&
 289                     (zoneid == (*smpp)->sm_zoneid || zoneid == ALL_ZONES))
 290                         return (smpp);
 291                 smpp = &(*smpp)->sm_next;
 292         }
 293         return (smpp);
 294 }
 295 
 296 /*
 297  * Lookup based on address.
 298  * Always returns a non-null pointer.
 299  * If found then *ptr will be the found object.
 300  * Otherwise *ptr will be NULL and can be used to insert a new object.
 301  */
 302 static srcid_map_t **
 303 srcid_lookup_id(uint_t id, ip_stack_t *ipst)
 304 {
 305         srcid_map_t     **smpp;
 306 
 307         ASSERT(RW_LOCK_HELD(&ipst->ips_srcid_lock));
 308         smpp = &ipst->ips_srcid_head;
 309         while (*smpp != NULL) {
 310                 if ((*smpp)->sm_srcid == id)
 311                         return (smpp);
 312                 smpp = &(*smpp)->sm_next;
 313         }
 314         return (smpp);
 315 }