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