Home | History | Annotate | Download | only in ip
      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 }
    316