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
ip_srcid_insert(const in6_addr_t * addr,zoneid_t zoneid,ip_stack_t * ipst)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
ip_srcid_remove(const in6_addr_t * addr,zoneid_t zoneid,ip_stack_t * ipst)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
ip_srcid_find_addr(const in6_addr_t * addr,zoneid_t zoneid,netstack_t * ns)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
ip_srcid_find_id(uint_t id,in6_addr_t * addr,zoneid_t zoneid,netstack_t * ns)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
srcid_nextid(ip_stack_t * ipst)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 **
srcid_lookup_addr(const in6_addr_t * addr,zoneid_t zoneid,ip_stack_t * ipst)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 **
srcid_lookup_id(uint_t id,ip_stack_t * ipst)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