xref: /illumos-gate/usr/src/uts/common/inet/ip/ip_srcid.c (revision 62403833)
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  * Copyright 2014, OmniTI Computer Consulting, Inc. All rights reserved.
26  */
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 <sys/kmem.h>
107 
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 *);
112 
113 
114 /*
115  * Insert/add a new address to the map.
116  * Returns zero if ok; otherwise errno (e.g. for memory allocation failure).
117  */
118 int
ip_srcid_insert(const in6_addr_t * addr,zoneid_t zoneid,ip_stack_t * ipst)119 ip_srcid_insert(const in6_addr_t *addr, zoneid_t zoneid, ip_stack_t *ipst)
120 {
121 	srcid_map_t	**smpp;
122 #ifdef DEBUG
123 	char		abuf[INET6_ADDRSTRLEN];
124 
125 	ip1dbg(("ip_srcid_insert(%s, %d)\n",
126 	    inet_ntop(AF_INET6, addr, abuf, sizeof (abuf)), zoneid));
127 #endif
128 
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);
137 	}
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);
143 	}
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;
149 
150 	rw_exit(&ipst->ips_srcid_lock);
151 	return (0);
152 }
153 
154 /*
155  * Remove an new address from the map.
156  * Returns zero if ok; otherwise errno (e.g. for nonexistent address).
157  */
158 int
ip_srcid_remove(const in6_addr_t * addr,zoneid_t zoneid,ip_stack_t * ipst)159 ip_srcid_remove(const in6_addr_t *addr, zoneid_t zoneid, ip_stack_t *ipst)
160 {
161 	srcid_map_t	**smpp;
162 	srcid_map_t	*smp;
163 #ifdef DEBUG
164 	char		abuf[INET6_ADDRSTRLEN];
165 
166 	ip1dbg(("ip_srcid_remove(%s, %d)\n",
167 	    inet_ntop(AF_INET6, addr, abuf, sizeof (abuf)), zoneid));
168 #endif
169 
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);
177 	}
178 
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);
185 	}
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);
192 }
193 
194 /*
195  * Map from an address to a source id.
196  * If the address is unknown return the unknown id (zero).
197  */
198 uint_t
ip_srcid_find_addr(const in6_addr_t * addr,zoneid_t zoneid,netstack_t * ns)199 ip_srcid_find_addr(const in6_addr_t *addr, zoneid_t zoneid,
200     netstack_t *ns)
201 {
202 	srcid_map_t	**smpp;
203 	srcid_map_t	*smp;
204 	uint_t		id;
205 	ip_stack_t	*ipst = ns->netstack_ip;
206 
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];
212 
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;
220 	}
221 	rw_exit(&ipst->ips_srcid_lock);
222 	return (id);
223 }
224 
225 /*
226  * Map from a source id to an address.
227  * If the id is unknown return the unspecified address.
228  *
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.
233  *
234  * "addr" will not be set if we return B_FALSE.
235  */
236 boolean_t
ip_srcid_find_id(uint_t id,in6_addr_t * addr,zoneid_t zoneid,boolean_t v4mapped,netstack_t * ns)237 ip_srcid_find_id(uint_t id, in6_addr_t *addr, zoneid_t zoneid,
238     boolean_t v4mapped, netstack_t *ns)
239 {
240 	srcid_map_t	**smpp;
241 	srcid_map_t	*smp;
242 	ip_stack_t	*ipst = ns->netstack_ip;
243 	boolean_t	rc;
244 
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);
255 		/*
256 		 * The caller tells us if it expects a v4mapped address.
257 		 * Use it, along with the property of "addr" to set the rc.
258 		 */
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. */
263 
264 		if (rc)
265 			*addr = smp->sm_addr;
266 
267 	}
268 	rw_exit(&ipst->ips_srcid_lock);
269 	return (rc);
270 }
271 
272 /* Assign the next available ID */
273 static uint_t
srcid_nextid(ip_stack_t * ipst)274 srcid_nextid(ip_stack_t *ipst)
275 {
276 	uint_t id;
277 	srcid_map_t	**smpp;
278 
279 	ASSERT(rw_owner(&ipst->ips_srcid_lock) == curthread);
280 
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);
286 	}
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);
292 	}
293 	panic("srcid_nextid: No free identifiers!");
294 	/* NOTREACHED */
295 }
296 
297 /*
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.
302  */
303 static srcid_map_t **
srcid_lookup_addr(const in6_addr_t * addr,zoneid_t zoneid,ip_stack_t * ipst)304 srcid_lookup_addr(const in6_addr_t *addr, zoneid_t zoneid, ip_stack_t *ipst)
305 {
306 	srcid_map_t	**smpp;
307 
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;
315 	}
316 	return (smpp);
317 }
318 
319 /*
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.
324  */
325 static srcid_map_t **
srcid_lookup_id(uint_t id,ip_stack_t * ipst)326 srcid_lookup_id(uint_t id, ip_stack_t *ipst)
327 {
328 	srcid_map_t	**smpp;
329 
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;
336 	}
337 	return (smpp);
338 }
339