Merge commit 'b1e7e97d3b60469b243b3b2e22c7d8cbd11c7c90'
[unleashed.git] / kernel / net / ip / ip_srcid.c
blob93fffec0f90ca9e9a8aee477a7966830ee0720ca
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
25 * Copyright 2014, OmniTI Computer Consulting, Inc. All rights reserved.
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.
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.
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.
46 * The routines handle both IPv4 and IPv6 with the IPv4 addresses represented
47 * as IPv4-mapped addresses.
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>
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>
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>
89 #include <netinet/igmp_var.h>
90 #include <netinet/ip6.h>
91 #include <netinet/icmp6.h>
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>
106 #include <sys/kmem.h>
108 static uint_t srcid_nextid(ip_stack_t *);
109 static srcid_map_t **srcid_lookup_addr(const in6_addr_t *addr,
110 zoneid_t zoneid, ip_stack_t *);
111 static srcid_map_t **srcid_lookup_id(uint_t id, ip_stack_t *);
115 * Insert/add a new address to the map.
116 * Returns zero if ok; otherwise errno (e.g. for memory allocation failure).
119 ip_srcid_insert(const in6_addr_t *addr, zoneid_t zoneid, ip_stack_t *ipst)
121 srcid_map_t **smpp;
122 #ifdef DEBUG
123 char abuf[INET6_ADDRSTRLEN];
125 ip1dbg(("ip_srcid_insert(%s, %d)\n",
126 inet_ntop(AF_INET6, addr, abuf, sizeof (abuf)), zoneid));
127 #endif
129 rw_enter(&ipst->ips_srcid_lock, RW_WRITER);
130 smpp = srcid_lookup_addr(addr, zoneid, ipst);
131 if (*smpp != NULL) {
132 /* Already present - increment refcount */
133 (*smpp)->sm_refcnt++;
134 ASSERT((*smpp)->sm_refcnt != 0); /* wraparound */
135 rw_exit(&ipst->ips_srcid_lock);
136 return (0);
138 /* Insert new */
139 *smpp = kmem_alloc(sizeof (srcid_map_t), KM_NOSLEEP);
140 if (*smpp == NULL) {
141 rw_exit(&ipst->ips_srcid_lock);
142 return (ENOMEM);
144 (*smpp)->sm_next = NULL;
145 (*smpp)->sm_addr = *addr;
146 (*smpp)->sm_srcid = srcid_nextid(ipst);
147 (*smpp)->sm_refcnt = 1;
148 (*smpp)->sm_zoneid = zoneid;
150 rw_exit(&ipst->ips_srcid_lock);
151 return (0);
155 * Remove an new address from the map.
156 * Returns zero if ok; otherwise errno (e.g. for nonexistent address).
159 ip_srcid_remove(const in6_addr_t *addr, zoneid_t zoneid, ip_stack_t *ipst)
161 srcid_map_t **smpp;
162 srcid_map_t *smp;
163 #ifdef DEBUG
164 char abuf[INET6_ADDRSTRLEN];
166 ip1dbg(("ip_srcid_remove(%s, %d)\n",
167 inet_ntop(AF_INET6, addr, abuf, sizeof (abuf)), zoneid));
168 #endif
170 rw_enter(&ipst->ips_srcid_lock, RW_WRITER);
171 smpp = srcid_lookup_addr(addr, zoneid, ipst);
172 smp = *smpp;
173 if (smp == NULL) {
174 /* Not preset */
175 rw_exit(&ipst->ips_srcid_lock);
176 return (ENOENT);
179 /* Decrement refcount */
180 ASSERT(smp->sm_refcnt != 0);
181 smp->sm_refcnt--;
182 if (smp->sm_refcnt != 0) {
183 rw_exit(&ipst->ips_srcid_lock);
184 return (0);
186 /* Remove entry */
187 *smpp = smp->sm_next;
188 rw_exit(&ipst->ips_srcid_lock);
189 smp->sm_next = NULL;
190 kmem_free(smp, sizeof (srcid_map_t));
191 return (0);
195 * Map from an address to a source id.
196 * If the address is unknown return the unknown id (zero).
198 uint_t
199 ip_srcid_find_addr(const in6_addr_t *addr, zoneid_t zoneid,
200 netstack_t *ns)
202 srcid_map_t **smpp;
203 srcid_map_t *smp;
204 uint_t id;
205 ip_stack_t *ipst = ns->netstack_ip;
207 rw_enter(&ipst->ips_srcid_lock, RW_READER);
208 smpp = srcid_lookup_addr(addr, zoneid, ipst);
209 smp = *smpp;
210 if (smp == NULL) {
211 char abuf[INET6_ADDRSTRLEN];
213 /* Not present - could be broadcast or multicast address */
214 ip1dbg(("ip_srcid_find_addr: unknown %s in zone %d\n",
215 inet_ntop(AF_INET6, addr, abuf, sizeof (abuf)), zoneid));
216 id = 0;
217 } else {
218 ASSERT(smp->sm_refcnt != 0);
219 id = smp->sm_srcid;
221 rw_exit(&ipst->ips_srcid_lock);
222 return (id);
226 * Map from a source id to an address.
227 * If the id is unknown return the unspecified address.
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.
234 * "addr" will not be set if we return B_FALSE.
236 boolean_t
237 ip_srcid_find_id(uint_t id, in6_addr_t *addr, zoneid_t zoneid,
238 boolean_t v4mapped, netstack_t *ns)
240 srcid_map_t **smpp;
241 srcid_map_t *smp;
242 ip_stack_t *ipst = ns->netstack_ip;
243 boolean_t rc;
245 rw_enter(&ipst->ips_srcid_lock, RW_READER);
246 smpp = srcid_lookup_id(id, ipst);
247 smp = *smpp;
248 if (smp == NULL || (smp->sm_zoneid != zoneid && zoneid != ALL_ZONES)) {
249 /* Not preset */
250 ip1dbg(("ip_srcid_find_id: unknown %u or in wrong zone\n", id));
251 *addr = ipv6_all_zeros;
252 rc = B_TRUE;
253 } else {
254 ASSERT(smp->sm_refcnt != 0);
256 * The caller tells us if it expects a v4mapped address.
257 * Use it, along with the property of "addr" to set the rc.
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. */
264 if (rc)
265 *addr = smp->sm_addr;
268 rw_exit(&ipst->ips_srcid_lock);
269 return (rc);
272 /* Assign the next available ID */
273 static uint_t
274 srcid_nextid(ip_stack_t *ipst)
276 uint_t id;
277 srcid_map_t **smpp;
279 ASSERT(rw_owner(&ipst->ips_srcid_lock) == curthread);
281 if (!ipst->ips_srcid_wrapped) {
282 id = ipst->ips_ip_src_id++;
283 if (ipst->ips_ip_src_id == 0)
284 ipst->ips_srcid_wrapped = B_TRUE;
285 return (id);
287 /* Once it wraps we search for an unused ID. */
288 for (id = 0; id < 0xffffffff; id++) {
289 smpp = srcid_lookup_id(id, ipst);
290 if (*smpp == NULL)
291 return (id);
293 panic("srcid_nextid: No free identifiers!");
294 /* NOTREACHED */
298 * Lookup based on address.
299 * Always returns a non-null pointer.
300 * If found then *ptr will be the found object.
301 * Otherwise *ptr will be NULL and can be used to insert a new object.
303 static srcid_map_t **
304 srcid_lookup_addr(const in6_addr_t *addr, zoneid_t zoneid, ip_stack_t *ipst)
306 srcid_map_t **smpp;
308 ASSERT(RW_LOCK_HELD(&ipst->ips_srcid_lock));
309 smpp = &ipst->ips_srcid_head;
310 while (*smpp != NULL) {
311 if (IN6_ARE_ADDR_EQUAL(&(*smpp)->sm_addr, addr) &&
312 (zoneid == (*smpp)->sm_zoneid || zoneid == ALL_ZONES))
313 return (smpp);
314 smpp = &(*smpp)->sm_next;
316 return (smpp);
320 * Lookup based on address.
321 * Always returns a non-null pointer.
322 * If found then *ptr will be the found object.
323 * Otherwise *ptr will be NULL and can be used to insert a new object.
325 static srcid_map_t **
326 srcid_lookup_id(uint_t id, ip_stack_t *ipst)
328 srcid_map_t **smpp;
330 ASSERT(RW_LOCK_HELD(&ipst->ips_srcid_lock));
331 smpp = &ipst->ips_srcid_head;
332 while (*smpp != NULL) {
333 if ((*smpp)->sm_srcid == id)
334 return (smpp);
335 smpp = &(*smpp)->sm_next;
337 return (smpp);