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 <net/pfkeyv2.h> 105 #include <inet/ipsec_info.h> 106 #include <inet/sadb.h> 107 #include <sys/kmem.h> 108 #include <inet/ipsec_impl.h> 109 #include <inet/tun.h> 110 111 static uint_t srcid_nextid(ip_stack_t *); 112 static srcid_map_t **srcid_lookup_addr(const in6_addr_t *addr, 113 zoneid_t zoneid, ip_stack_t *); 114 static srcid_map_t **srcid_lookup_id(uint_t id, ip_stack_t *); 115 116 117 /* 118 * Insert/add a new address to the map. 119 * Returns zero if ok; otherwise errno (e.g. for memory allocation failure). 120 */ 121 int 122 ip_srcid_insert(const in6_addr_t *addr, zoneid_t zoneid, ip_stack_t *ipst) 123 { 124 srcid_map_t **smpp; 125 #ifdef DEBUG 126 char abuf[INET6_ADDRSTRLEN]; 127 128 ip1dbg(("ip_srcid_insert(%s, %d)\n", 129 inet_ntop(AF_INET6, addr, abuf, sizeof (abuf)), zoneid)); 130 #endif 131 132 rw_enter(&ipst->ips_srcid_lock, RW_WRITER); 133 smpp = srcid_lookup_addr(addr, zoneid, ipst); 134 if (*smpp != NULL) { 135 /* Already present - increment refcount */ 136 (*smpp)->sm_refcnt++; 137 ASSERT((*smpp)->sm_refcnt != 0); /* wraparound */ 138 rw_exit(&ipst->ips_srcid_lock); 139 return (0); 140 } 141 /* Insert new */ 142 *smpp = kmem_alloc(sizeof (srcid_map_t), KM_NOSLEEP); 143 if (*smpp == NULL) { 144 rw_exit(&ipst->ips_srcid_lock); 145 return (ENOMEM); 146 } 147 (*smpp)->sm_next = NULL; 148 (*smpp)->sm_addr = *addr; 149 (*smpp)->sm_srcid = srcid_nextid(ipst); 150 (*smpp)->sm_refcnt = 1; 151 (*smpp)->sm_zoneid = zoneid; 152 153 rw_exit(&ipst->ips_srcid_lock); 154 return (0); 155 } 156 157 /* 158 * Remove an new address from the map. 159 * Returns zero if ok; otherwise errno (e.g. for nonexistent address). 160 */ 161 int 162 ip_srcid_remove(const in6_addr_t *addr, zoneid_t zoneid, ip_stack_t *ipst) 163 { 164 srcid_map_t **smpp; 165 srcid_map_t *smp; 166 #ifdef DEBUG 167 char abuf[INET6_ADDRSTRLEN]; 168 169 ip1dbg(("ip_srcid_remove(%s, %d)\n", 170 inet_ntop(AF_INET6, addr, abuf, sizeof (abuf)), zoneid)); 171 #endif 172 173 rw_enter(&ipst->ips_srcid_lock, RW_WRITER); 174 smpp = srcid_lookup_addr(addr, zoneid, ipst); 175 smp = *smpp; 176 if (smp == NULL) { 177 /* Not preset */ 178 rw_exit(&ipst->ips_srcid_lock); 179 return (ENOENT); 180 } 181 182 /* Decrement refcount */ 183 ASSERT(smp->sm_refcnt != 0); 184 smp->sm_refcnt--; 185 if (smp->sm_refcnt != 0) { 186 rw_exit(&ipst->ips_srcid_lock); 187 return (0); 188 } 189 /* Remove entry */ 190 *smpp = smp->sm_next; 191 rw_exit(&ipst->ips_srcid_lock); 192 smp->sm_next = NULL; 193 kmem_free(smp, sizeof (srcid_map_t)); 194 return (0); 195 } 196 197 /* 198 * Map from an address to a source id. 199 * If the address is unknown return the unknown id (zero). 200 */ 201 uint_t 202 ip_srcid_find_addr(const in6_addr_t *addr, zoneid_t zoneid, 203 netstack_t *ns) 204 { 205 srcid_map_t **smpp; 206 srcid_map_t *smp; 207 uint_t id; 208 ip_stack_t *ipst = ns->netstack_ip; 209 210 rw_enter(&ipst->ips_srcid_lock, RW_READER); 211 smpp = srcid_lookup_addr(addr, zoneid, ipst); 212 smp = *smpp; 213 if (smp == NULL) { 214 char abuf[INET6_ADDRSTRLEN]; 215 216 /* Not present - could be broadcast or multicast address */ 217 ip1dbg(("ip_srcid_find_addr: unknown %s in zone %d\n", 218 inet_ntop(AF_INET6, addr, abuf, sizeof (abuf)), zoneid)); 219 id = 0; 220 } else { 221 ASSERT(smp->sm_refcnt != 0); 222 id = smp->sm_srcid; 223 } 224 rw_exit(&ipst->ips_srcid_lock); 225 return (id); 226 } 227 228 /* 229 * Map from a source id to an address. 230 * If the id is unknown return the unspecified address. 231 */ 232 void 233 ip_srcid_find_id(uint_t id, in6_addr_t *addr, zoneid_t zoneid, 234 netstack_t *ns) 235 { 236 srcid_map_t **smpp; 237 srcid_map_t *smp; 238 ip_stack_t *ipst = ns->netstack_ip; 239 240 rw_enter(&ipst->ips_srcid_lock, RW_READER); 241 smpp = srcid_lookup_id(id, ipst); 242 smp = *smpp; 243 if (smp == NULL || smp->sm_zoneid != zoneid) { 244 /* Not preset */ 245 ip1dbg(("ip_srcid_find_id: unknown %u or in wrong zone\n", id)); 246 *addr = ipv6_all_zeros; 247 } else { 248 ASSERT(smp->sm_refcnt != 0); 249 *addr = smp->sm_addr; 250 } 251 rw_exit(&ipst->ips_srcid_lock); 252 } 253 254 /* Assign the next available ID */ 255 static uint_t 256 srcid_nextid(ip_stack_t *ipst) 257 { 258 uint_t id; 259 srcid_map_t **smpp; 260 261 ASSERT(rw_owner(&ipst->ips_srcid_lock) == curthread); 262 263 if (!ipst->ips_srcid_wrapped) { 264 id = ipst->ips_ip_src_id++; 265 if (ipst->ips_ip_src_id == 0) 266 ipst->ips_srcid_wrapped = B_TRUE; 267 return (id); 268 } 269 /* Once it wraps we search for an unused ID. */ 270 for (id = 0; id < 0xffffffff; id++) { 271 smpp = srcid_lookup_id(id, ipst); 272 if (*smpp == NULL) 273 return (id); 274 } 275 panic("srcid_nextid: No free identifiers!"); 276 /* NOTREACHED */ 277 } 278 279 /* 280 * Lookup based on address. 281 * Always returns a non-null pointer. 282 * If found then *ptr will be the found object. 283 * Otherwise *ptr will be NULL and can be used to insert a new object. 284 */ 285 static srcid_map_t ** 286 srcid_lookup_addr(const in6_addr_t *addr, zoneid_t zoneid, ip_stack_t *ipst) 287 { 288 srcid_map_t **smpp; 289 290 ASSERT(RW_LOCK_HELD(&ipst->ips_srcid_lock)); 291 smpp = &ipst->ips_srcid_head; 292 while (*smpp != NULL) { 293 if (IN6_ARE_ADDR_EQUAL(&(*smpp)->sm_addr, addr) && 294 zoneid == (*smpp)->sm_zoneid) 295 return (smpp); 296 smpp = &(*smpp)->sm_next; 297 } 298 return (smpp); 299 } 300 301 /* 302 * Lookup based on address. 303 * Always returns a non-null pointer. 304 * If found then *ptr will be the found object. 305 * Otherwise *ptr will be NULL and can be used to insert a new object. 306 */ 307 static srcid_map_t ** 308 srcid_lookup_id(uint_t id, ip_stack_t *ipst) 309 { 310 srcid_map_t **smpp; 311 312 ASSERT(RW_LOCK_HELD(&ipst->ips_srcid_lock)); 313 smpp = &ipst->ips_srcid_head; 314 while (*smpp != NULL) { 315 if ((*smpp)->sm_srcid == id) 316 return (smpp); 317 smpp = &(*smpp)->sm_next; 318 } 319 return (smpp); 320 } 321