xref: /onnv-gate/usr/src/uts/common/inet/ip/ip_srcid.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate /*
30*0Sstevel@tonic-gate  * This is used to support the hidden __sin6_src_id in the sockaddr_in6
31*0Sstevel@tonic-gate  * structure which is there to ensure that applications (such as UDP apps)
32*0Sstevel@tonic-gate  * which get an address from recvfrom and use that address in a sendto
33*0Sstevel@tonic-gate  * or connect will by default use the same source address in the "response"
34*0Sstevel@tonic-gate  * as the destination address in the "request" they received.
35*0Sstevel@tonic-gate  *
36*0Sstevel@tonic-gate  * This is built using some new functions (in IP - doing their own locking
37*0Sstevel@tonic-gate  * so they can be called from the transports) to map between integer IDs
38*0Sstevel@tonic-gate  * and in6_addr_t.
39*0Sstevel@tonic-gate  * The use applies to sockaddr_in6 - whether or not mapped addresses are used.
40*0Sstevel@tonic-gate  *
41*0Sstevel@tonic-gate  * This file contains the functions used by both IP and the transports
42*0Sstevel@tonic-gate  * to implement __sin6_src_id.
43*0Sstevel@tonic-gate  * The routines do their own locking since they are called from
44*0Sstevel@tonic-gate  * the transports (to map between a source id and an address)
45*0Sstevel@tonic-gate  * and from IP proper when IP addresses are added and removed.
46*0Sstevel@tonic-gate  *
47*0Sstevel@tonic-gate  * The routines handle both IPv4 and IPv6 with the IPv4 addresses represented
48*0Sstevel@tonic-gate  * as IPv4-mapped addresses.
49*0Sstevel@tonic-gate  */
50*0Sstevel@tonic-gate 
51*0Sstevel@tonic-gate #include <sys/types.h>
52*0Sstevel@tonic-gate #include <sys/stream.h>
53*0Sstevel@tonic-gate #include <sys/dlpi.h>
54*0Sstevel@tonic-gate #include <sys/stropts.h>
55*0Sstevel@tonic-gate #include <sys/sysmacros.h>
56*0Sstevel@tonic-gate #include <sys/strsubr.h>
57*0Sstevel@tonic-gate #include <sys/strlog.h>
58*0Sstevel@tonic-gate #define	_SUN_TPI_VERSION 2
59*0Sstevel@tonic-gate #include <sys/tihdr.h>
60*0Sstevel@tonic-gate #include <sys/xti_inet.h>
61*0Sstevel@tonic-gate #include <sys/ddi.h>
62*0Sstevel@tonic-gate #include <sys/cmn_err.h>
63*0Sstevel@tonic-gate #include <sys/debug.h>
64*0Sstevel@tonic-gate #include <sys/modctl.h>
65*0Sstevel@tonic-gate #include <sys/atomic.h>
66*0Sstevel@tonic-gate #include <sys/zone.h>
67*0Sstevel@tonic-gate 
68*0Sstevel@tonic-gate #include <sys/systm.h>
69*0Sstevel@tonic-gate #include <sys/param.h>
70*0Sstevel@tonic-gate #include <sys/kmem.h>
71*0Sstevel@tonic-gate #include <sys/callb.h>
72*0Sstevel@tonic-gate #include <sys/socket.h>
73*0Sstevel@tonic-gate #include <sys/vtrace.h>
74*0Sstevel@tonic-gate #include <sys/isa_defs.h>
75*0Sstevel@tonic-gate #include <sys/kmem.h>
76*0Sstevel@tonic-gate #include <net/if.h>
77*0Sstevel@tonic-gate #include <net/if_arp.h>
78*0Sstevel@tonic-gate #include <net/route.h>
79*0Sstevel@tonic-gate #include <sys/sockio.h>
80*0Sstevel@tonic-gate #include <netinet/in.h>
81*0Sstevel@tonic-gate #include <net/if_dl.h>
82*0Sstevel@tonic-gate 
83*0Sstevel@tonic-gate #include <inet/common.h>
84*0Sstevel@tonic-gate #include <inet/mi.h>
85*0Sstevel@tonic-gate #include <inet/mib2.h>
86*0Sstevel@tonic-gate #include <inet/nd.h>
87*0Sstevel@tonic-gate #include <inet/arp.h>
88*0Sstevel@tonic-gate #include <inet/snmpcom.h>
89*0Sstevel@tonic-gate 
90*0Sstevel@tonic-gate #include <netinet/igmp_var.h>
91*0Sstevel@tonic-gate #include <netinet/ip6.h>
92*0Sstevel@tonic-gate #include <netinet/icmp6.h>
93*0Sstevel@tonic-gate 
94*0Sstevel@tonic-gate #include <inet/ip.h>
95*0Sstevel@tonic-gate #include <inet/ip6.h>
96*0Sstevel@tonic-gate #include <inet/tcp.h>
97*0Sstevel@tonic-gate #include <inet/ip_multi.h>
98*0Sstevel@tonic-gate #include <inet/ip_if.h>
99*0Sstevel@tonic-gate #include <inet/ip_ire.h>
100*0Sstevel@tonic-gate #include <inet/ip_rts.h>
101*0Sstevel@tonic-gate #include <inet/optcom.h>
102*0Sstevel@tonic-gate #include <inet/ip_ndp.h>
103*0Sstevel@tonic-gate #include <netinet/igmp.h>
104*0Sstevel@tonic-gate #include <netinet/ip_mroute.h>
105*0Sstevel@tonic-gate #include <inet/ipclassifier.h>
106*0Sstevel@tonic-gate 
107*0Sstevel@tonic-gate #include <net/pfkeyv2.h>
108*0Sstevel@tonic-gate #include <inet/ipsec_info.h>
109*0Sstevel@tonic-gate #include <inet/sadb.h>
110*0Sstevel@tonic-gate #include <sys/kmem.h>
111*0Sstevel@tonic-gate #include <inet/ipsec_impl.h>
112*0Sstevel@tonic-gate #include <inet/tun.h>
113*0Sstevel@tonic-gate 
114*0Sstevel@tonic-gate /* Data structure to represent addresses */
115*0Sstevel@tonic-gate struct srcid_map {
116*0Sstevel@tonic-gate 	struct srcid_map	*sm_next;
117*0Sstevel@tonic-gate 	in6_addr_t		sm_addr;	/* Local address */
118*0Sstevel@tonic-gate 	uint_t			sm_srcid;	/* source id */
119*0Sstevel@tonic-gate 	uint_t			sm_refcnt;	/* > 1 ipif with same addr? */
120*0Sstevel@tonic-gate 	zoneid_t		sm_zoneid;	/* zone id */
121*0Sstevel@tonic-gate };
122*0Sstevel@tonic-gate typedef struct srcid_map srcid_map_t;
123*0Sstevel@tonic-gate 
124*0Sstevel@tonic-gate static uint_t		srcid_nextid(void);
125*0Sstevel@tonic-gate static srcid_map_t	**srcid_lookup_addr(const in6_addr_t *addr,
126*0Sstevel@tonic-gate     zoneid_t zoneid);
127*0Sstevel@tonic-gate static srcid_map_t	**srcid_lookup_id(uint_t id);
128*0Sstevel@tonic-gate 
129*0Sstevel@tonic-gate 
130*0Sstevel@tonic-gate /*
131*0Sstevel@tonic-gate  * ID used to assign next free one.
132*0Sstevel@tonic-gate  * Increases by one. Once it wraps we search for an unused ID.
133*0Sstevel@tonic-gate  */
134*0Sstevel@tonic-gate static uint_t		ip_src_id = 1;
135*0Sstevel@tonic-gate static boolean_t	srcid_wrapped = B_FALSE;
136*0Sstevel@tonic-gate 
137*0Sstevel@tonic-gate static srcid_map_t	*srcid_head;
138*0Sstevel@tonic-gate krwlock_t		srcid_lock;
139*0Sstevel@tonic-gate 
140*0Sstevel@tonic-gate /*
141*0Sstevel@tonic-gate  * Insert/add a new address to the map.
142*0Sstevel@tonic-gate  * Returns zero if ok; otherwise errno (e.g. for memory allocation failure).
143*0Sstevel@tonic-gate  */
144*0Sstevel@tonic-gate int
145*0Sstevel@tonic-gate ip_srcid_insert(const in6_addr_t *addr, zoneid_t zoneid)
146*0Sstevel@tonic-gate {
147*0Sstevel@tonic-gate 	srcid_map_t	**smpp;
148*0Sstevel@tonic-gate #ifdef DEBUG
149*0Sstevel@tonic-gate 	char		abuf[INET6_ADDRSTRLEN];
150*0Sstevel@tonic-gate 
151*0Sstevel@tonic-gate 	ip1dbg(("ip_srcid_insert(%s, %d)\n",
152*0Sstevel@tonic-gate 	    inet_ntop(AF_INET6, addr, abuf, sizeof (abuf)), zoneid));
153*0Sstevel@tonic-gate #endif
154*0Sstevel@tonic-gate 
155*0Sstevel@tonic-gate 	rw_enter(&srcid_lock, RW_WRITER);
156*0Sstevel@tonic-gate 	smpp = srcid_lookup_addr(addr, zoneid);
157*0Sstevel@tonic-gate 	if (*smpp != NULL) {
158*0Sstevel@tonic-gate 		/* Already present - increment refcount */
159*0Sstevel@tonic-gate 		(*smpp)->sm_refcnt++;
160*0Sstevel@tonic-gate 		ASSERT((*smpp)->sm_refcnt != 0);	/* wraparound */
161*0Sstevel@tonic-gate 		rw_exit(&srcid_lock);
162*0Sstevel@tonic-gate 		return (0);
163*0Sstevel@tonic-gate 	}
164*0Sstevel@tonic-gate 	/* Insert new */
165*0Sstevel@tonic-gate 	*smpp = kmem_alloc(sizeof (srcid_map_t), KM_NOSLEEP);
166*0Sstevel@tonic-gate 	if (*smpp == NULL) {
167*0Sstevel@tonic-gate 		rw_exit(&srcid_lock);
168*0Sstevel@tonic-gate 		return (ENOMEM);
169*0Sstevel@tonic-gate 	}
170*0Sstevel@tonic-gate 	(*smpp)->sm_next = NULL;
171*0Sstevel@tonic-gate 	(*smpp)->sm_addr = *addr;
172*0Sstevel@tonic-gate 	(*smpp)->sm_srcid = srcid_nextid();
173*0Sstevel@tonic-gate 	(*smpp)->sm_refcnt = 1;
174*0Sstevel@tonic-gate 	(*smpp)->sm_zoneid = zoneid;
175*0Sstevel@tonic-gate 
176*0Sstevel@tonic-gate 	rw_exit(&srcid_lock);
177*0Sstevel@tonic-gate 	return (0);
178*0Sstevel@tonic-gate }
179*0Sstevel@tonic-gate 
180*0Sstevel@tonic-gate /*
181*0Sstevel@tonic-gate  * Remove an new address from the map.
182*0Sstevel@tonic-gate  * Returns zero if ok; otherwise errno (e.g. for nonexistent address).
183*0Sstevel@tonic-gate  */
184*0Sstevel@tonic-gate int
185*0Sstevel@tonic-gate ip_srcid_remove(const in6_addr_t *addr, zoneid_t zoneid)
186*0Sstevel@tonic-gate {
187*0Sstevel@tonic-gate 	srcid_map_t	**smpp;
188*0Sstevel@tonic-gate 	srcid_map_t	*smp;
189*0Sstevel@tonic-gate #ifdef DEBUG
190*0Sstevel@tonic-gate 	char		abuf[INET6_ADDRSTRLEN];
191*0Sstevel@tonic-gate 
192*0Sstevel@tonic-gate 	ip1dbg(("ip_srcid_remove(%s, %d)\n",
193*0Sstevel@tonic-gate 	    inet_ntop(AF_INET6, addr, abuf, sizeof (abuf)), zoneid));
194*0Sstevel@tonic-gate #endif
195*0Sstevel@tonic-gate 
196*0Sstevel@tonic-gate 	rw_enter(&srcid_lock, RW_WRITER);
197*0Sstevel@tonic-gate 	smpp = srcid_lookup_addr(addr, zoneid);
198*0Sstevel@tonic-gate 	smp = *smpp;
199*0Sstevel@tonic-gate 	if (smp == NULL) {
200*0Sstevel@tonic-gate 		/* Not preset */
201*0Sstevel@tonic-gate 		rw_exit(&srcid_lock);
202*0Sstevel@tonic-gate 		return (ENOENT);
203*0Sstevel@tonic-gate 	}
204*0Sstevel@tonic-gate 
205*0Sstevel@tonic-gate 	/* Decrement refcount */
206*0Sstevel@tonic-gate 	ASSERT(smp->sm_refcnt != 0);
207*0Sstevel@tonic-gate 	smp->sm_refcnt--;
208*0Sstevel@tonic-gate 	if (smp->sm_refcnt != 0) {
209*0Sstevel@tonic-gate 		rw_exit(&srcid_lock);
210*0Sstevel@tonic-gate 		return (0);
211*0Sstevel@tonic-gate 	}
212*0Sstevel@tonic-gate 	/* Remove entry */
213*0Sstevel@tonic-gate 	*smpp = smp->sm_next;
214*0Sstevel@tonic-gate 	rw_exit(&srcid_lock);
215*0Sstevel@tonic-gate 	smp->sm_next = NULL;
216*0Sstevel@tonic-gate 	kmem_free(smp, sizeof (srcid_map_t));
217*0Sstevel@tonic-gate 	return (0);
218*0Sstevel@tonic-gate }
219*0Sstevel@tonic-gate 
220*0Sstevel@tonic-gate /*
221*0Sstevel@tonic-gate  * Map from an address to a source id.
222*0Sstevel@tonic-gate  * If the address is unknown return the unknown id (zero).
223*0Sstevel@tonic-gate  */
224*0Sstevel@tonic-gate uint_t
225*0Sstevel@tonic-gate ip_srcid_find_addr(const in6_addr_t *addr, zoneid_t zoneid)
226*0Sstevel@tonic-gate {
227*0Sstevel@tonic-gate 	srcid_map_t	**smpp;
228*0Sstevel@tonic-gate 	srcid_map_t	*smp;
229*0Sstevel@tonic-gate 	uint_t		id;
230*0Sstevel@tonic-gate 
231*0Sstevel@tonic-gate 	rw_enter(&srcid_lock, RW_READER);
232*0Sstevel@tonic-gate 	smpp = srcid_lookup_addr(addr, zoneid);
233*0Sstevel@tonic-gate 	smp = *smpp;
234*0Sstevel@tonic-gate 	if (smp == NULL) {
235*0Sstevel@tonic-gate 		char		abuf[INET6_ADDRSTRLEN];
236*0Sstevel@tonic-gate 
237*0Sstevel@tonic-gate 		/* Not present - could be broadcast or multicast address */
238*0Sstevel@tonic-gate 		ip1dbg(("ip_srcid_find_addr: unknown %s in zone %d\n",
239*0Sstevel@tonic-gate 		    inet_ntop(AF_INET6, addr, abuf, sizeof (abuf)), zoneid));
240*0Sstevel@tonic-gate 		id = 0;
241*0Sstevel@tonic-gate 	} else {
242*0Sstevel@tonic-gate 		ASSERT(smp->sm_refcnt != 0);
243*0Sstevel@tonic-gate 		id = smp->sm_srcid;
244*0Sstevel@tonic-gate 	}
245*0Sstevel@tonic-gate 	rw_exit(&srcid_lock);
246*0Sstevel@tonic-gate 	return (id);
247*0Sstevel@tonic-gate }
248*0Sstevel@tonic-gate 
249*0Sstevel@tonic-gate /*
250*0Sstevel@tonic-gate  * Map from a source id to an address.
251*0Sstevel@tonic-gate  * If the id is unknown return the unspecified address.
252*0Sstevel@tonic-gate  */
253*0Sstevel@tonic-gate void
254*0Sstevel@tonic-gate ip_srcid_find_id(uint_t id, in6_addr_t *addr, zoneid_t zoneid)
255*0Sstevel@tonic-gate {
256*0Sstevel@tonic-gate 	srcid_map_t	**smpp;
257*0Sstevel@tonic-gate 	srcid_map_t	*smp;
258*0Sstevel@tonic-gate 
259*0Sstevel@tonic-gate 	rw_enter(&srcid_lock, RW_READER);
260*0Sstevel@tonic-gate 	smpp = srcid_lookup_id(id);
261*0Sstevel@tonic-gate 	smp = *smpp;
262*0Sstevel@tonic-gate 	if (smp == NULL || smp->sm_zoneid != zoneid) {
263*0Sstevel@tonic-gate 		/* Not preset */
264*0Sstevel@tonic-gate 		ip1dbg(("ip_srcid_find_id: unknown %u or in wrong zone\n", id));
265*0Sstevel@tonic-gate 		*addr = ipv6_all_zeros;
266*0Sstevel@tonic-gate 	} else {
267*0Sstevel@tonic-gate 		ASSERT(smp->sm_refcnt != 0);
268*0Sstevel@tonic-gate 		*addr = smp->sm_addr;
269*0Sstevel@tonic-gate 	}
270*0Sstevel@tonic-gate 	rw_exit(&srcid_lock);
271*0Sstevel@tonic-gate }
272*0Sstevel@tonic-gate 
273*0Sstevel@tonic-gate /*
274*0Sstevel@tonic-gate  * ndd report function
275*0Sstevel@tonic-gate  */
276*0Sstevel@tonic-gate /*ARGSUSED*/
277*0Sstevel@tonic-gate int
278*0Sstevel@tonic-gate ip_srcid_report(queue_t *q, mblk_t *mp, caddr_t arg, cred_t *ioc_cr)
279*0Sstevel@tonic-gate {
280*0Sstevel@tonic-gate 	srcid_map_t	*smp;
281*0Sstevel@tonic-gate 	char		abuf[INET6_ADDRSTRLEN];
282*0Sstevel@tonic-gate 	zoneid_t	zoneid;
283*0Sstevel@tonic-gate 
284*0Sstevel@tonic-gate 	zoneid = Q_TO_CONN(q)->conn_zoneid;
285*0Sstevel@tonic-gate 	(void) mi_mpprintf(mp,
286*0Sstevel@tonic-gate 	    "addr                                           "
287*0Sstevel@tonic-gate 	    "id     zone refcnt");
288*0Sstevel@tonic-gate 	rw_enter(&srcid_lock, RW_READER);
289*0Sstevel@tonic-gate 	for (smp = srcid_head; smp != NULL; smp = smp->sm_next) {
290*0Sstevel@tonic-gate 		if (zoneid != GLOBAL_ZONEID && zoneid != smp->sm_zoneid)
291*0Sstevel@tonic-gate 			continue;
292*0Sstevel@tonic-gate 		(void) mi_mpprintf(mp, "%46s %5u %5d %5u",
293*0Sstevel@tonic-gate 		    inet_ntop(AF_INET6, &smp->sm_addr, abuf, sizeof (abuf)),
294*0Sstevel@tonic-gate 		    smp->sm_srcid, smp->sm_zoneid, smp->sm_refcnt);
295*0Sstevel@tonic-gate 	}
296*0Sstevel@tonic-gate 	rw_exit(&srcid_lock);
297*0Sstevel@tonic-gate 	return (0);
298*0Sstevel@tonic-gate }
299*0Sstevel@tonic-gate 
300*0Sstevel@tonic-gate /* Assign the next available ID */
301*0Sstevel@tonic-gate static uint_t
302*0Sstevel@tonic-gate srcid_nextid(void)
303*0Sstevel@tonic-gate {
304*0Sstevel@tonic-gate 	uint_t id;
305*0Sstevel@tonic-gate 	srcid_map_t	**smpp;
306*0Sstevel@tonic-gate 
307*0Sstevel@tonic-gate 	ASSERT(rw_owner(&srcid_lock) == curthread);
308*0Sstevel@tonic-gate 
309*0Sstevel@tonic-gate 	if (!srcid_wrapped) {
310*0Sstevel@tonic-gate 		id = ip_src_id++;
311*0Sstevel@tonic-gate 		if (ip_src_id == 0)
312*0Sstevel@tonic-gate 			srcid_wrapped = B_TRUE;
313*0Sstevel@tonic-gate 		return (id);
314*0Sstevel@tonic-gate 	}
315*0Sstevel@tonic-gate 	/* Once it wraps we search for an unused ID. */
316*0Sstevel@tonic-gate 	for (id = 0; id < 0xffffffff; id++) {
317*0Sstevel@tonic-gate 		smpp = srcid_lookup_id(id);
318*0Sstevel@tonic-gate 		if (*smpp == NULL)
319*0Sstevel@tonic-gate 			return (id);
320*0Sstevel@tonic-gate 	}
321*0Sstevel@tonic-gate 	panic("srcid_nextid: No free identifiers!");
322*0Sstevel@tonic-gate 	/* NOTREACHED */
323*0Sstevel@tonic-gate }
324*0Sstevel@tonic-gate 
325*0Sstevel@tonic-gate /*
326*0Sstevel@tonic-gate  * Lookup based on address.
327*0Sstevel@tonic-gate  * Always returns a non-null pointer.
328*0Sstevel@tonic-gate  * If found then *ptr will be the found object.
329*0Sstevel@tonic-gate  * Otherwise *ptr will be NULL and can be used to insert a new object.
330*0Sstevel@tonic-gate  */
331*0Sstevel@tonic-gate static srcid_map_t **
332*0Sstevel@tonic-gate srcid_lookup_addr(const in6_addr_t *addr, zoneid_t zoneid)
333*0Sstevel@tonic-gate {
334*0Sstevel@tonic-gate 	srcid_map_t	**smpp;
335*0Sstevel@tonic-gate 
336*0Sstevel@tonic-gate 	ASSERT(RW_LOCK_HELD(&srcid_lock));
337*0Sstevel@tonic-gate 	smpp = &srcid_head;
338*0Sstevel@tonic-gate 	while (*smpp != NULL) {
339*0Sstevel@tonic-gate 		if (IN6_ARE_ADDR_EQUAL(&(*smpp)->sm_addr, addr) &&
340*0Sstevel@tonic-gate 		    zoneid == (*smpp)->sm_zoneid)
341*0Sstevel@tonic-gate 			return (smpp);
342*0Sstevel@tonic-gate 		smpp = &(*smpp)->sm_next;
343*0Sstevel@tonic-gate 	}
344*0Sstevel@tonic-gate 	return (smpp);
345*0Sstevel@tonic-gate }
346*0Sstevel@tonic-gate 
347*0Sstevel@tonic-gate /*
348*0Sstevel@tonic-gate  * Lookup based on address.
349*0Sstevel@tonic-gate  * Always returns a non-null pointer.
350*0Sstevel@tonic-gate  * If found then *ptr will be the found object.
351*0Sstevel@tonic-gate  * Otherwise *ptr will be NULL and can be used to insert a new object.
352*0Sstevel@tonic-gate  */
353*0Sstevel@tonic-gate static srcid_map_t **
354*0Sstevel@tonic-gate srcid_lookup_id(uint_t id)
355*0Sstevel@tonic-gate {
356*0Sstevel@tonic-gate 	srcid_map_t	**smpp;
357*0Sstevel@tonic-gate 
358*0Sstevel@tonic-gate 	ASSERT(RW_LOCK_HELD(&srcid_lock));
359*0Sstevel@tonic-gate 	smpp = &srcid_head;
360*0Sstevel@tonic-gate 	while (*smpp != NULL) {
361*0Sstevel@tonic-gate 		if ((*smpp)->sm_srcid == id)
362*0Sstevel@tonic-gate 			return (smpp);
363*0Sstevel@tonic-gate 		smpp = &(*smpp)->sm_next;
364*0Sstevel@tonic-gate 	}
365*0Sstevel@tonic-gate 	return (smpp);
366*0Sstevel@tonic-gate }
367