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