xref: /illumos-gate/usr/src/uts/common/inet/ip/ip_srcid.c (revision c51cb4bc539e1650eb5bb4f805cc779bfce99c06)
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 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
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 <net/pfkeyv2.h>
107 #include <inet/ipsec_info.h>
108 #include <inet/sadb.h>
109 #include <sys/kmem.h>
110 #include <inet/ipsec_impl.h>
111 #include <inet/tun.h>
112 
113 /* Data structure to represent addresses */
114 struct srcid_map {
115 	struct srcid_map	*sm_next;
116 	in6_addr_t		sm_addr;	/* Local address */
117 	uint_t			sm_srcid;	/* source id */
118 	uint_t			sm_refcnt;	/* > 1 ipif with same addr? */
119 	zoneid_t		sm_zoneid;	/* zone id */
120 };
121 typedef struct srcid_map srcid_map_t;
122 
123 static uint_t		srcid_nextid(ip_stack_t *);
124 static srcid_map_t	**srcid_lookup_addr(const in6_addr_t *addr,
125     zoneid_t zoneid, ip_stack_t *);
126 static srcid_map_t	**srcid_lookup_id(uint_t id, ip_stack_t *);
127 
128 
129 /*
130  * Insert/add a new address to the map.
131  * Returns zero if ok; otherwise errno (e.g. for memory allocation failure).
132  */
133 int
134 ip_srcid_insert(const in6_addr_t *addr, zoneid_t zoneid, ip_stack_t *ipst)
135 {
136 	srcid_map_t	**smpp;
137 #ifdef DEBUG
138 	char		abuf[INET6_ADDRSTRLEN];
139 
140 	ip1dbg(("ip_srcid_insert(%s, %d)\n",
141 	    inet_ntop(AF_INET6, addr, abuf, sizeof (abuf)), zoneid));
142 #endif
143 
144 	rw_enter(&ipst->ips_srcid_lock, RW_WRITER);
145 	smpp = srcid_lookup_addr(addr, zoneid, ipst);
146 	if (*smpp != NULL) {
147 		/* Already present - increment refcount */
148 		(*smpp)->sm_refcnt++;
149 		ASSERT((*smpp)->sm_refcnt != 0);	/* wraparound */
150 		rw_exit(&ipst->ips_srcid_lock);
151 		return (0);
152 	}
153 	/* Insert new */
154 	*smpp = kmem_alloc(sizeof (srcid_map_t), KM_NOSLEEP);
155 	if (*smpp == NULL) {
156 		rw_exit(&ipst->ips_srcid_lock);
157 		return (ENOMEM);
158 	}
159 	(*smpp)->sm_next = NULL;
160 	(*smpp)->sm_addr = *addr;
161 	(*smpp)->sm_srcid = srcid_nextid(ipst);
162 	(*smpp)->sm_refcnt = 1;
163 	(*smpp)->sm_zoneid = zoneid;
164 
165 	rw_exit(&ipst->ips_srcid_lock);
166 	return (0);
167 }
168 
169 /*
170  * Remove an new address from the map.
171  * Returns zero if ok; otherwise errno (e.g. for nonexistent address).
172  */
173 int
174 ip_srcid_remove(const in6_addr_t *addr, zoneid_t zoneid, ip_stack_t *ipst)
175 {
176 	srcid_map_t	**smpp;
177 	srcid_map_t	*smp;
178 #ifdef DEBUG
179 	char		abuf[INET6_ADDRSTRLEN];
180 
181 	ip1dbg(("ip_srcid_remove(%s, %d)\n",
182 	    inet_ntop(AF_INET6, addr, abuf, sizeof (abuf)), zoneid));
183 #endif
184 
185 	rw_enter(&ipst->ips_srcid_lock, RW_WRITER);
186 	smpp = srcid_lookup_addr(addr, zoneid, ipst);
187 	smp = *smpp;
188 	if (smp == NULL) {
189 		/* Not preset */
190 		rw_exit(&ipst->ips_srcid_lock);
191 		return (ENOENT);
192 	}
193 
194 	/* Decrement refcount */
195 	ASSERT(smp->sm_refcnt != 0);
196 	smp->sm_refcnt--;
197 	if (smp->sm_refcnt != 0) {
198 		rw_exit(&ipst->ips_srcid_lock);
199 		return (0);
200 	}
201 	/* Remove entry */
202 	*smpp = smp->sm_next;
203 	rw_exit(&ipst->ips_srcid_lock);
204 	smp->sm_next = NULL;
205 	kmem_free(smp, sizeof (srcid_map_t));
206 	return (0);
207 }
208 
209 /*
210  * Map from an address to a source id.
211  * If the address is unknown return the unknown id (zero).
212  */
213 uint_t
214 ip_srcid_find_addr(const in6_addr_t *addr, zoneid_t zoneid,
215     netstack_t *ns)
216 {
217 	srcid_map_t	**smpp;
218 	srcid_map_t	*smp;
219 	uint_t		id;
220 	ip_stack_t	*ipst = ns->netstack_ip;
221 
222 	rw_enter(&ipst->ips_srcid_lock, RW_READER);
223 	smpp = srcid_lookup_addr(addr, zoneid, ipst);
224 	smp = *smpp;
225 	if (smp == NULL) {
226 		char		abuf[INET6_ADDRSTRLEN];
227 
228 		/* Not present - could be broadcast or multicast address */
229 		ip1dbg(("ip_srcid_find_addr: unknown %s in zone %d\n",
230 		    inet_ntop(AF_INET6, addr, abuf, sizeof (abuf)), zoneid));
231 		id = 0;
232 	} else {
233 		ASSERT(smp->sm_refcnt != 0);
234 		id = smp->sm_srcid;
235 	}
236 	rw_exit(&ipst->ips_srcid_lock);
237 	return (id);
238 }
239 
240 /*
241  * Map from a source id to an address.
242  * If the id is unknown return the unspecified address.
243  */
244 void
245 ip_srcid_find_id(uint_t id, in6_addr_t *addr, zoneid_t zoneid,
246     netstack_t *ns)
247 {
248 	srcid_map_t	**smpp;
249 	srcid_map_t	*smp;
250 	ip_stack_t	*ipst = ns->netstack_ip;
251 
252 	rw_enter(&ipst->ips_srcid_lock, RW_READER);
253 	smpp = srcid_lookup_id(id, ipst);
254 	smp = *smpp;
255 	if (smp == NULL || smp->sm_zoneid != zoneid) {
256 		/* Not preset */
257 		ip1dbg(("ip_srcid_find_id: unknown %u or in wrong zone\n", id));
258 		*addr = ipv6_all_zeros;
259 	} else {
260 		ASSERT(smp->sm_refcnt != 0);
261 		*addr = smp->sm_addr;
262 	}
263 	rw_exit(&ipst->ips_srcid_lock);
264 }
265 
266 /*
267  * ndd report function
268  */
269 /*ARGSUSED*/
270 int
271 ip_srcid_report(queue_t *q, mblk_t *mp, caddr_t arg, cred_t *ioc_cr)
272 {
273 	srcid_map_t	*smp;
274 	char		abuf[INET6_ADDRSTRLEN];
275 	zoneid_t	zoneid;
276 	ip_stack_t	*ipst;
277 
278 	if (CONN_Q(q)) {
279 		ipst = CONNQ_TO_IPST(q);
280 		zoneid = Q_TO_CONN(q)->conn_zoneid;
281 	} else {
282 		ipst = ILLQ_TO_IPST(q);
283 		zoneid =  ((ill_t *)q->q_ptr)->ill_zoneid;
284 	}
285 	(void) mi_mpprintf(mp,
286 	    "addr                                           "
287 	    "id     zone refcnt");
288 	rw_enter(&ipst->ips_srcid_lock, RW_READER);
289 	for (smp = ipst->ips_srcid_head; smp != NULL; smp = smp->sm_next) {
290 		if (zoneid != GLOBAL_ZONEID && zoneid != smp->sm_zoneid)
291 			continue;
292 		(void) mi_mpprintf(mp, "%46s %5u %5d %5u",
293 		    inet_ntop(AF_INET6, &smp->sm_addr, abuf, sizeof (abuf)),
294 		    smp->sm_srcid, smp->sm_zoneid, smp->sm_refcnt);
295 	}
296 	rw_exit(&ipst->ips_srcid_lock);
297 	return (0);
298 }
299 
300 /* Assign the next available ID */
301 static uint_t
302 srcid_nextid(ip_stack_t *ipst)
303 {
304 	uint_t id;
305 	srcid_map_t	**smpp;
306 
307 	ASSERT(rw_owner(&ipst->ips_srcid_lock) == curthread);
308 
309 	if (!ipst->ips_srcid_wrapped) {
310 		id = ipst->ips_ip_src_id++;
311 		if (ipst->ips_ip_src_id == 0)
312 			ipst->ips_srcid_wrapped = B_TRUE;
313 		return (id);
314 	}
315 	/* Once it wraps we search for an unused ID. */
316 	for (id = 0; id < 0xffffffff; id++) {
317 		smpp = srcid_lookup_id(id, ipst);
318 		if (*smpp == NULL)
319 			return (id);
320 	}
321 	panic("srcid_nextid: No free identifiers!");
322 	/* NOTREACHED */
323 }
324 
325 /*
326  * Lookup based on address.
327  * Always returns a non-null pointer.
328  * If found then *ptr will be the found object.
329  * Otherwise *ptr will be NULL and can be used to insert a new object.
330  */
331 static srcid_map_t **
332 srcid_lookup_addr(const in6_addr_t *addr, zoneid_t zoneid, ip_stack_t *ipst)
333 {
334 	srcid_map_t	**smpp;
335 
336 	ASSERT(RW_LOCK_HELD(&ipst->ips_srcid_lock));
337 	smpp = &ipst->ips_srcid_head;
338 	while (*smpp != NULL) {
339 		if (IN6_ARE_ADDR_EQUAL(&(*smpp)->sm_addr, addr) &&
340 		    zoneid == (*smpp)->sm_zoneid)
341 			return (smpp);
342 		smpp = &(*smpp)->sm_next;
343 	}
344 	return (smpp);
345 }
346 
347 /*
348  * Lookup based on address.
349  * Always returns a non-null pointer.
350  * If found then *ptr will be the found object.
351  * Otherwise *ptr will be NULL and can be used to insert a new object.
352  */
353 static srcid_map_t **
354 srcid_lookup_id(uint_t id, ip_stack_t *ipst)
355 {
356 	srcid_map_t	**smpp;
357 
358 	ASSERT(RW_LOCK_HELD(&ipst->ips_srcid_lock));
359 	smpp = &ipst->ips_srcid_head;
360 	while (*smpp != NULL) {
361 		if ((*smpp)->sm_srcid == id)
362 			return (smpp);
363 		smpp = &(*smpp)->sm_next;
364 	}
365 	return (smpp);
366 }
367