xref: /onnv-gate/usr/src/uts/common/inet/ip/ip_dce.c (revision 11042:2d6e217af1b4)
1*11042SErik.Nordmark@Sun.COM /*
2*11042SErik.Nordmark@Sun.COM  * CDDL HEADER START
3*11042SErik.Nordmark@Sun.COM  *
4*11042SErik.Nordmark@Sun.COM  * The contents of this file are subject to the terms of the
5*11042SErik.Nordmark@Sun.COM  * Common Development and Distribution License (the "License").
6*11042SErik.Nordmark@Sun.COM  * You may not use this file except in compliance with the License.
7*11042SErik.Nordmark@Sun.COM  *
8*11042SErik.Nordmark@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*11042SErik.Nordmark@Sun.COM  * or http://www.opensolaris.org/os/licensing.
10*11042SErik.Nordmark@Sun.COM  * See the License for the specific language governing permissions
11*11042SErik.Nordmark@Sun.COM  * and limitations under the License.
12*11042SErik.Nordmark@Sun.COM  *
13*11042SErik.Nordmark@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
14*11042SErik.Nordmark@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*11042SErik.Nordmark@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
16*11042SErik.Nordmark@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
17*11042SErik.Nordmark@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
18*11042SErik.Nordmark@Sun.COM  *
19*11042SErik.Nordmark@Sun.COM  * CDDL HEADER END
20*11042SErik.Nordmark@Sun.COM  */
21*11042SErik.Nordmark@Sun.COM 
22*11042SErik.Nordmark@Sun.COM /*
23*11042SErik.Nordmark@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24*11042SErik.Nordmark@Sun.COM  * Use is subject to license terms.
25*11042SErik.Nordmark@Sun.COM  */
26*11042SErik.Nordmark@Sun.COM 
27*11042SErik.Nordmark@Sun.COM #include <sys/types.h>
28*11042SErik.Nordmark@Sun.COM #include <sys/stream.h>
29*11042SErik.Nordmark@Sun.COM #include <sys/strsun.h>
30*11042SErik.Nordmark@Sun.COM #include <sys/zone.h>
31*11042SErik.Nordmark@Sun.COM #include <sys/ddi.h>
32*11042SErik.Nordmark@Sun.COM #include <sys/sunddi.h>
33*11042SErik.Nordmark@Sun.COM #include <sys/cmn_err.h>
34*11042SErik.Nordmark@Sun.COM #include <sys/debug.h>
35*11042SErik.Nordmark@Sun.COM #include <sys/atomic.h>
36*11042SErik.Nordmark@Sun.COM #define	_SUN_TPI_VERSION 2
37*11042SErik.Nordmark@Sun.COM #include <sys/tihdr.h>
38*11042SErik.Nordmark@Sun.COM 
39*11042SErik.Nordmark@Sun.COM #include <inet/common.h>
40*11042SErik.Nordmark@Sun.COM #include <inet/mi.h>
41*11042SErik.Nordmark@Sun.COM #include <inet/mib2.h>
42*11042SErik.Nordmark@Sun.COM #include <inet/snmpcom.h>
43*11042SErik.Nordmark@Sun.COM 
44*11042SErik.Nordmark@Sun.COM #include <netinet/ip6.h>
45*11042SErik.Nordmark@Sun.COM #include <netinet/icmp6.h>
46*11042SErik.Nordmark@Sun.COM 
47*11042SErik.Nordmark@Sun.COM #include <inet/ip.h>
48*11042SErik.Nordmark@Sun.COM #include <inet/ip_impl.h>
49*11042SErik.Nordmark@Sun.COM #include <inet/ip6.h>
50*11042SErik.Nordmark@Sun.COM #include <inet/ip6_asp.h>
51*11042SErik.Nordmark@Sun.COM #include <inet/ip_multi.h>
52*11042SErik.Nordmark@Sun.COM #include <inet/ip_if.h>
53*11042SErik.Nordmark@Sun.COM #include <inet/ip_ire.h>
54*11042SErik.Nordmark@Sun.COM #include <inet/ip_ftable.h>
55*11042SErik.Nordmark@Sun.COM #include <inet/ip_rts.h>
56*11042SErik.Nordmark@Sun.COM #include <inet/ip_ndp.h>
57*11042SErik.Nordmark@Sun.COM #include <inet/ipclassifier.h>
58*11042SErik.Nordmark@Sun.COM #include <inet/ip_listutils.h>
59*11042SErik.Nordmark@Sun.COM 
60*11042SErik.Nordmark@Sun.COM #include <sys/sunddi.h>
61*11042SErik.Nordmark@Sun.COM 
62*11042SErik.Nordmark@Sun.COM /*
63*11042SErik.Nordmark@Sun.COM  * Routines for handling destination cache entries.
64*11042SErik.Nordmark@Sun.COM  * There is always one DCEF_DEFAULT for each ip_stack_t created at init time.
65*11042SErik.Nordmark@Sun.COM  * That entry holds both the IP ident value and the dce generation number.
66*11042SErik.Nordmark@Sun.COM  *
67*11042SErik.Nordmark@Sun.COM  * Any time a DCE is changed significantly (different path MTU, but NOT
68*11042SErik.Nordmark@Sun.COM  * different ULP info!), the dce_generation number is increased.
69*11042SErik.Nordmark@Sun.COM  * Also, when a new DCE is created, the dce_generation number in the default
70*11042SErik.Nordmark@Sun.COM  * DCE is bumped. That allows the dce_t information to be cached efficiently
71*11042SErik.Nordmark@Sun.COM  * as long as the entity caching the dce_t also caches the dce_generation,
72*11042SErik.Nordmark@Sun.COM  * and compares the cached generation to detect any changes.
73*11042SErik.Nordmark@Sun.COM  * Furthermore, when a DCE is deleted, if there are any outstanding references
74*11042SErik.Nordmark@Sun.COM  * to the DCE it will be marked as condemned. The condemned mark is
75*11042SErik.Nordmark@Sun.COM  * a designated generation number which is never otherwise used, hence
76*11042SErik.Nordmark@Sun.COM  * the single comparison with the generation number captures that as well.
77*11042SErik.Nordmark@Sun.COM  *
78*11042SErik.Nordmark@Sun.COM  * An example of code which caches is as follows:
79*11042SErik.Nordmark@Sun.COM  *
80*11042SErik.Nordmark@Sun.COM  *	if (mystruct->my_dce_generation != mystruct->my_dce->dce_generation) {
81*11042SErik.Nordmark@Sun.COM  *		The DCE has changed
82*11042SErik.Nordmark@Sun.COM  *		mystruct->my_dce = dce_lookup_pkt(mp, ixa,
83*11042SErik.Nordmark@Sun.COM  *		    &mystruct->my_dce_generation);
84*11042SErik.Nordmark@Sun.COM  *		Not needed in practice, since we have the default DCE:
85*11042SErik.Nordmark@Sun.COM  *		if (DCE_IS_CONDEMNED(mystruct->my_dce))
86*11042SErik.Nordmark@Sun.COM  *			return failure;
87*11042SErik.Nordmark@Sun.COM  *	}
88*11042SErik.Nordmark@Sun.COM  *
89*11042SErik.Nordmark@Sun.COM  * Note that for IPv6 link-local addresses we record the ifindex since the
90*11042SErik.Nordmark@Sun.COM  * link-locals are not globally unique.
91*11042SErik.Nordmark@Sun.COM  */
92*11042SErik.Nordmark@Sun.COM 
93*11042SErik.Nordmark@Sun.COM /*
94*11042SErik.Nordmark@Sun.COM  * Hash bucket structure for DCEs
95*11042SErik.Nordmark@Sun.COM  */
96*11042SErik.Nordmark@Sun.COM typedef struct dcb_s {
97*11042SErik.Nordmark@Sun.COM 	krwlock_t	dcb_lock;
98*11042SErik.Nordmark@Sun.COM 	uint32_t	dcb_cnt;
99*11042SErik.Nordmark@Sun.COM 	dce_t		*dcb_dce;
100*11042SErik.Nordmark@Sun.COM } dcb_t;
101*11042SErik.Nordmark@Sun.COM 
102*11042SErik.Nordmark@Sun.COM static void	dce_delete_locked(dcb_t *, dce_t *);
103*11042SErik.Nordmark@Sun.COM static void	dce_make_condemned(dce_t *);
104*11042SErik.Nordmark@Sun.COM 
105*11042SErik.Nordmark@Sun.COM static kmem_cache_t *dce_cache;
106*11042SErik.Nordmark@Sun.COM 
107*11042SErik.Nordmark@Sun.COM 
108*11042SErik.Nordmark@Sun.COM /* Operates on a uint64_t */
109*11042SErik.Nordmark@Sun.COM #define	RANDOM_HASH(p) ((p) ^ ((p)>>16) ^ ((p)>>32) ^ ((p)>>48))
110*11042SErik.Nordmark@Sun.COM 
111*11042SErik.Nordmark@Sun.COM /*
112*11042SErik.Nordmark@Sun.COM  * Reclaim a fraction of dce's in the dcb.
113*11042SErik.Nordmark@Sun.COM  * For now we have a higher probability to delete DCEs without DCE_PMTU.
114*11042SErik.Nordmark@Sun.COM  */
115*11042SErik.Nordmark@Sun.COM static void
116*11042SErik.Nordmark@Sun.COM dcb_reclaim(dcb_t *dcb, ip_stack_t *ipst, uint_t fraction)
117*11042SErik.Nordmark@Sun.COM {
118*11042SErik.Nordmark@Sun.COM 	uint_t	fraction_pmtu = fraction*4;
119*11042SErik.Nordmark@Sun.COM 	uint_t	hash;
120*11042SErik.Nordmark@Sun.COM 	dce_t	*dce, *nextdce;
121*11042SErik.Nordmark@Sun.COM 
122*11042SErik.Nordmark@Sun.COM 	rw_enter(&dcb->dcb_lock, RW_WRITER);
123*11042SErik.Nordmark@Sun.COM 	for (dce = dcb->dcb_dce; dce != NULL; dce = nextdce) {
124*11042SErik.Nordmark@Sun.COM 		nextdce = dce->dce_next;
125*11042SErik.Nordmark@Sun.COM 		/* Clear DCEF_PMTU if the pmtu is too old */
126*11042SErik.Nordmark@Sun.COM 		mutex_enter(&dce->dce_lock);
127*11042SErik.Nordmark@Sun.COM 		if ((dce->dce_flags & DCEF_PMTU) &&
128*11042SErik.Nordmark@Sun.COM 		    TICK_TO_SEC(lbolt64) - dce->dce_last_change_time >
129*11042SErik.Nordmark@Sun.COM 		    ipst->ips_ip_pathmtu_interval) {
130*11042SErik.Nordmark@Sun.COM 			dce->dce_flags &= ~DCEF_PMTU;
131*11042SErik.Nordmark@Sun.COM 			mutex_exit(&dce->dce_lock);
132*11042SErik.Nordmark@Sun.COM 			dce_increment_generation(dce);
133*11042SErik.Nordmark@Sun.COM 		} else {
134*11042SErik.Nordmark@Sun.COM 			mutex_exit(&dce->dce_lock);
135*11042SErik.Nordmark@Sun.COM 		}
136*11042SErik.Nordmark@Sun.COM 		hash = RANDOM_HASH((uint64_t)(uintptr_t)dce);
137*11042SErik.Nordmark@Sun.COM 		if (dce->dce_flags & DCEF_PMTU) {
138*11042SErik.Nordmark@Sun.COM 			if (hash % fraction_pmtu != 0)
139*11042SErik.Nordmark@Sun.COM 				continue;
140*11042SErik.Nordmark@Sun.COM 		} else {
141*11042SErik.Nordmark@Sun.COM 			if (hash % fraction != 0)
142*11042SErik.Nordmark@Sun.COM 				continue;
143*11042SErik.Nordmark@Sun.COM 		}
144*11042SErik.Nordmark@Sun.COM 
145*11042SErik.Nordmark@Sun.COM 		IP_STAT(ipst, ip_dce_reclaim_deleted);
146*11042SErik.Nordmark@Sun.COM 		dce_delete_locked(dcb, dce);
147*11042SErik.Nordmark@Sun.COM 		dce_refrele(dce);
148*11042SErik.Nordmark@Sun.COM 	}
149*11042SErik.Nordmark@Sun.COM 	rw_exit(&dcb->dcb_lock);
150*11042SErik.Nordmark@Sun.COM }
151*11042SErik.Nordmark@Sun.COM 
152*11042SErik.Nordmark@Sun.COM /*
153*11042SErik.Nordmark@Sun.COM  * kmem_cache callback to free up memory.
154*11042SErik.Nordmark@Sun.COM  *
155*11042SErik.Nordmark@Sun.COM  */
156*11042SErik.Nordmark@Sun.COM static void
157*11042SErik.Nordmark@Sun.COM ip_dce_reclaim_stack(ip_stack_t *ipst)
158*11042SErik.Nordmark@Sun.COM {
159*11042SErik.Nordmark@Sun.COM 	int	i;
160*11042SErik.Nordmark@Sun.COM 
161*11042SErik.Nordmark@Sun.COM 	IP_STAT(ipst, ip_dce_reclaim_calls);
162*11042SErik.Nordmark@Sun.COM 	for (i = 0; i < ipst->ips_dce_hashsize; i++) {
163*11042SErik.Nordmark@Sun.COM 		dcb_reclaim(&ipst->ips_dce_hash_v4[i], ipst,
164*11042SErik.Nordmark@Sun.COM 		    ipst->ips_ip_dce_reclaim_fraction);
165*11042SErik.Nordmark@Sun.COM 
166*11042SErik.Nordmark@Sun.COM 		dcb_reclaim(&ipst->ips_dce_hash_v6[i], ipst,
167*11042SErik.Nordmark@Sun.COM 		    ipst->ips_ip_dce_reclaim_fraction);
168*11042SErik.Nordmark@Sun.COM 	}
169*11042SErik.Nordmark@Sun.COM 
170*11042SErik.Nordmark@Sun.COM 	/*
171*11042SErik.Nordmark@Sun.COM 	 * Walk all CONNs that can have a reference on an ire, nce or dce.
172*11042SErik.Nordmark@Sun.COM 	 * Get them to update any stale references to drop any refholds they
173*11042SErik.Nordmark@Sun.COM 	 * have.
174*11042SErik.Nordmark@Sun.COM 	 */
175*11042SErik.Nordmark@Sun.COM 	ipcl_walk(conn_ixa_cleanup, (void *)B_FALSE, ipst);
176*11042SErik.Nordmark@Sun.COM }
177*11042SErik.Nordmark@Sun.COM 
178*11042SErik.Nordmark@Sun.COM /*
179*11042SErik.Nordmark@Sun.COM  * Called by the memory allocator subsystem directly, when the system
180*11042SErik.Nordmark@Sun.COM  * is running low on memory.
181*11042SErik.Nordmark@Sun.COM  */
182*11042SErik.Nordmark@Sun.COM /* ARGSUSED */
183*11042SErik.Nordmark@Sun.COM void
184*11042SErik.Nordmark@Sun.COM ip_dce_reclaim(void *args)
185*11042SErik.Nordmark@Sun.COM {
186*11042SErik.Nordmark@Sun.COM 	netstack_handle_t nh;
187*11042SErik.Nordmark@Sun.COM 	netstack_t *ns;
188*11042SErik.Nordmark@Sun.COM 
189*11042SErik.Nordmark@Sun.COM 	netstack_next_init(&nh);
190*11042SErik.Nordmark@Sun.COM 	while ((ns = netstack_next(&nh)) != NULL) {
191*11042SErik.Nordmark@Sun.COM 		ip_dce_reclaim_stack(ns->netstack_ip);
192*11042SErik.Nordmark@Sun.COM 		netstack_rele(ns);
193*11042SErik.Nordmark@Sun.COM 	}
194*11042SErik.Nordmark@Sun.COM 	netstack_next_fini(&nh);
195*11042SErik.Nordmark@Sun.COM }
196*11042SErik.Nordmark@Sun.COM 
197*11042SErik.Nordmark@Sun.COM void
198*11042SErik.Nordmark@Sun.COM dce_g_init(void)
199*11042SErik.Nordmark@Sun.COM {
200*11042SErik.Nordmark@Sun.COM 	dce_cache = kmem_cache_create("dce_cache",
201*11042SErik.Nordmark@Sun.COM 	    sizeof (dce_t), 0, NULL, NULL, ip_dce_reclaim, NULL, NULL, 0);
202*11042SErik.Nordmark@Sun.COM }
203*11042SErik.Nordmark@Sun.COM 
204*11042SErik.Nordmark@Sun.COM void
205*11042SErik.Nordmark@Sun.COM dce_g_destroy(void)
206*11042SErik.Nordmark@Sun.COM {
207*11042SErik.Nordmark@Sun.COM 	kmem_cache_destroy(dce_cache);
208*11042SErik.Nordmark@Sun.COM }
209*11042SErik.Nordmark@Sun.COM 
210*11042SErik.Nordmark@Sun.COM 
211*11042SErik.Nordmark@Sun.COM /*
212*11042SErik.Nordmark@Sun.COM  * Allocate a default DCE and a hash table for per-IP address DCEs
213*11042SErik.Nordmark@Sun.COM  */
214*11042SErik.Nordmark@Sun.COM void
215*11042SErik.Nordmark@Sun.COM dce_stack_init(ip_stack_t *ipst)
216*11042SErik.Nordmark@Sun.COM {
217*11042SErik.Nordmark@Sun.COM 	int	i;
218*11042SErik.Nordmark@Sun.COM 
219*11042SErik.Nordmark@Sun.COM 	ipst->ips_dce_default = kmem_cache_alloc(dce_cache, KM_SLEEP);
220*11042SErik.Nordmark@Sun.COM 	bzero(ipst->ips_dce_default, sizeof (dce_t));
221*11042SErik.Nordmark@Sun.COM 	ipst->ips_dce_default->dce_flags = DCEF_DEFAULT;
222*11042SErik.Nordmark@Sun.COM 	ipst->ips_dce_default->dce_generation = DCE_GENERATION_INITIAL;
223*11042SErik.Nordmark@Sun.COM 	ipst->ips_dce_default->dce_last_change_time = TICK_TO_SEC(lbolt64);
224*11042SErik.Nordmark@Sun.COM 	ipst->ips_dce_default->dce_refcnt = 1;	/* Should never go away */
225*11042SErik.Nordmark@Sun.COM 	ipst->ips_dce_default->dce_ipst = ipst;
226*11042SErik.Nordmark@Sun.COM 
227*11042SErik.Nordmark@Sun.COM 	/* This must be a power of two since we are using IRE_ADDR_HASH macro */
228*11042SErik.Nordmark@Sun.COM 	ipst->ips_dce_hashsize = 256;
229*11042SErik.Nordmark@Sun.COM 	ipst->ips_dce_hash_v4 = kmem_zalloc(ipst->ips_dce_hashsize *
230*11042SErik.Nordmark@Sun.COM 	    sizeof (dcb_t), KM_SLEEP);
231*11042SErik.Nordmark@Sun.COM 	ipst->ips_dce_hash_v6 = kmem_zalloc(ipst->ips_dce_hashsize *
232*11042SErik.Nordmark@Sun.COM 	    sizeof (dcb_t), KM_SLEEP);
233*11042SErik.Nordmark@Sun.COM 	for (i = 0; i < ipst->ips_dce_hashsize; i++) {
234*11042SErik.Nordmark@Sun.COM 		rw_init(&ipst->ips_dce_hash_v4[i].dcb_lock, NULL, RW_DEFAULT,
235*11042SErik.Nordmark@Sun.COM 		    NULL);
236*11042SErik.Nordmark@Sun.COM 		rw_init(&ipst->ips_dce_hash_v6[i].dcb_lock, NULL, RW_DEFAULT,
237*11042SErik.Nordmark@Sun.COM 		    NULL);
238*11042SErik.Nordmark@Sun.COM 	}
239*11042SErik.Nordmark@Sun.COM }
240*11042SErik.Nordmark@Sun.COM 
241*11042SErik.Nordmark@Sun.COM void
242*11042SErik.Nordmark@Sun.COM dce_stack_destroy(ip_stack_t *ipst)
243*11042SErik.Nordmark@Sun.COM {
244*11042SErik.Nordmark@Sun.COM 	int i;
245*11042SErik.Nordmark@Sun.COM 	for (i = 0; i < ipst->ips_dce_hashsize; i++) {
246*11042SErik.Nordmark@Sun.COM 		rw_destroy(&ipst->ips_dce_hash_v4[i].dcb_lock);
247*11042SErik.Nordmark@Sun.COM 		rw_destroy(&ipst->ips_dce_hash_v6[i].dcb_lock);
248*11042SErik.Nordmark@Sun.COM 	}
249*11042SErik.Nordmark@Sun.COM 	kmem_free(ipst->ips_dce_hash_v4,
250*11042SErik.Nordmark@Sun.COM 	    ipst->ips_dce_hashsize * sizeof (dcb_t));
251*11042SErik.Nordmark@Sun.COM 	ipst->ips_dce_hash_v4 = NULL;
252*11042SErik.Nordmark@Sun.COM 	kmem_free(ipst->ips_dce_hash_v6,
253*11042SErik.Nordmark@Sun.COM 	    ipst->ips_dce_hashsize * sizeof (dcb_t));
254*11042SErik.Nordmark@Sun.COM 	ipst->ips_dce_hash_v6 = NULL;
255*11042SErik.Nordmark@Sun.COM 	ipst->ips_dce_hashsize = 0;
256*11042SErik.Nordmark@Sun.COM 
257*11042SErik.Nordmark@Sun.COM 	ASSERT(ipst->ips_dce_default->dce_refcnt == 1);
258*11042SErik.Nordmark@Sun.COM 	kmem_cache_free(dce_cache, ipst->ips_dce_default);
259*11042SErik.Nordmark@Sun.COM 	ipst->ips_dce_default = NULL;
260*11042SErik.Nordmark@Sun.COM }
261*11042SErik.Nordmark@Sun.COM 
262*11042SErik.Nordmark@Sun.COM /* When any DCE is good enough */
263*11042SErik.Nordmark@Sun.COM dce_t *
264*11042SErik.Nordmark@Sun.COM dce_get_default(ip_stack_t *ipst)
265*11042SErik.Nordmark@Sun.COM {
266*11042SErik.Nordmark@Sun.COM 	dce_t		*dce;
267*11042SErik.Nordmark@Sun.COM 
268*11042SErik.Nordmark@Sun.COM 	dce = ipst->ips_dce_default;
269*11042SErik.Nordmark@Sun.COM 	dce_refhold(dce);
270*11042SErik.Nordmark@Sun.COM 	return (dce);
271*11042SErik.Nordmark@Sun.COM }
272*11042SErik.Nordmark@Sun.COM 
273*11042SErik.Nordmark@Sun.COM /*
274*11042SErik.Nordmark@Sun.COM  * Generic for IPv4 and IPv6.
275*11042SErik.Nordmark@Sun.COM  *
276*11042SErik.Nordmark@Sun.COM  * Used by callers that need to cache e.g., the datapath
277*11042SErik.Nordmark@Sun.COM  * Returns the generation number in the last argument.
278*11042SErik.Nordmark@Sun.COM  */
279*11042SErik.Nordmark@Sun.COM dce_t *
280*11042SErik.Nordmark@Sun.COM dce_lookup_pkt(mblk_t *mp, ip_xmit_attr_t *ixa, uint_t *generationp)
281*11042SErik.Nordmark@Sun.COM {
282*11042SErik.Nordmark@Sun.COM 	if (ixa->ixa_flags & IXAF_IS_IPV4) {
283*11042SErik.Nordmark@Sun.COM 		/*
284*11042SErik.Nordmark@Sun.COM 		 * If we have a source route we need to look for the final
285*11042SErik.Nordmark@Sun.COM 		 * destination in the source route option.
286*11042SErik.Nordmark@Sun.COM 		 */
287*11042SErik.Nordmark@Sun.COM 		ipaddr_t final_dst;
288*11042SErik.Nordmark@Sun.COM 		ipha_t *ipha = (ipha_t *)mp->b_rptr;
289*11042SErik.Nordmark@Sun.COM 
290*11042SErik.Nordmark@Sun.COM 		final_dst = ip_get_dst(ipha);
291*11042SErik.Nordmark@Sun.COM 		return (dce_lookup_v4(final_dst, ixa->ixa_ipst, generationp));
292*11042SErik.Nordmark@Sun.COM 	} else {
293*11042SErik.Nordmark@Sun.COM 		uint_t ifindex;
294*11042SErik.Nordmark@Sun.COM 		/*
295*11042SErik.Nordmark@Sun.COM 		 * If we have a routing header we need to look for the final
296*11042SErik.Nordmark@Sun.COM 		 * destination in the routing extension header.
297*11042SErik.Nordmark@Sun.COM 		 */
298*11042SErik.Nordmark@Sun.COM 		in6_addr_t final_dst;
299*11042SErik.Nordmark@Sun.COM 		ip6_t *ip6h = (ip6_t *)mp->b_rptr;
300*11042SErik.Nordmark@Sun.COM 
301*11042SErik.Nordmark@Sun.COM 		final_dst = ip_get_dst_v6(ip6h, mp, NULL);
302*11042SErik.Nordmark@Sun.COM 		ifindex = 0;
303*11042SErik.Nordmark@Sun.COM 		if (IN6_IS_ADDR_LINKSCOPE(&final_dst) && ixa->ixa_nce != NULL) {
304*11042SErik.Nordmark@Sun.COM 			ifindex = ixa->ixa_nce->nce_common->ncec_ill->
305*11042SErik.Nordmark@Sun.COM 			    ill_phyint->phyint_ifindex;
306*11042SErik.Nordmark@Sun.COM 		}
307*11042SErik.Nordmark@Sun.COM 		return (dce_lookup_v6(&final_dst, ifindex, ixa->ixa_ipst,
308*11042SErik.Nordmark@Sun.COM 		    generationp));
309*11042SErik.Nordmark@Sun.COM 	}
310*11042SErik.Nordmark@Sun.COM }
311*11042SErik.Nordmark@Sun.COM 
312*11042SErik.Nordmark@Sun.COM /*
313*11042SErik.Nordmark@Sun.COM  * Used by callers that need to cache e.g., the datapath
314*11042SErik.Nordmark@Sun.COM  * Returns the generation number in the last argument.
315*11042SErik.Nordmark@Sun.COM  */
316*11042SErik.Nordmark@Sun.COM dce_t *
317*11042SErik.Nordmark@Sun.COM dce_lookup_v4(ipaddr_t dst, ip_stack_t *ipst, uint_t *generationp)
318*11042SErik.Nordmark@Sun.COM {
319*11042SErik.Nordmark@Sun.COM 	uint_t		hash;
320*11042SErik.Nordmark@Sun.COM 	dcb_t		*dcb;
321*11042SErik.Nordmark@Sun.COM 	dce_t		*dce;
322*11042SErik.Nordmark@Sun.COM 
323*11042SErik.Nordmark@Sun.COM 	/* Set *generationp before dropping the lock(s) that allow additions */
324*11042SErik.Nordmark@Sun.COM 	if (generationp != NULL)
325*11042SErik.Nordmark@Sun.COM 		*generationp = ipst->ips_dce_default->dce_generation;
326*11042SErik.Nordmark@Sun.COM 
327*11042SErik.Nordmark@Sun.COM 	hash = IRE_ADDR_HASH(dst, ipst->ips_dce_hashsize);
328*11042SErik.Nordmark@Sun.COM 	dcb = &ipst->ips_dce_hash_v4[hash];
329*11042SErik.Nordmark@Sun.COM 	rw_enter(&dcb->dcb_lock, RW_READER);
330*11042SErik.Nordmark@Sun.COM 	for (dce = dcb->dcb_dce; dce != NULL; dce = dce->dce_next) {
331*11042SErik.Nordmark@Sun.COM 		if (dce->dce_v4addr == dst) {
332*11042SErik.Nordmark@Sun.COM 			mutex_enter(&dce->dce_lock);
333*11042SErik.Nordmark@Sun.COM 			if (!DCE_IS_CONDEMNED(dce)) {
334*11042SErik.Nordmark@Sun.COM 				dce_refhold(dce);
335*11042SErik.Nordmark@Sun.COM 				if (generationp != NULL)
336*11042SErik.Nordmark@Sun.COM 					*generationp = dce->dce_generation;
337*11042SErik.Nordmark@Sun.COM 				mutex_exit(&dce->dce_lock);
338*11042SErik.Nordmark@Sun.COM 				rw_exit(&dcb->dcb_lock);
339*11042SErik.Nordmark@Sun.COM 				return (dce);
340*11042SErik.Nordmark@Sun.COM 			}
341*11042SErik.Nordmark@Sun.COM 			mutex_exit(&dce->dce_lock);
342*11042SErik.Nordmark@Sun.COM 		}
343*11042SErik.Nordmark@Sun.COM 	}
344*11042SErik.Nordmark@Sun.COM 	rw_exit(&dcb->dcb_lock);
345*11042SErik.Nordmark@Sun.COM 	/* Not found */
346*11042SErik.Nordmark@Sun.COM 	dce = ipst->ips_dce_default;
347*11042SErik.Nordmark@Sun.COM 	dce_refhold(dce);
348*11042SErik.Nordmark@Sun.COM 	return (dce);
349*11042SErik.Nordmark@Sun.COM }
350*11042SErik.Nordmark@Sun.COM 
351*11042SErik.Nordmark@Sun.COM /*
352*11042SErik.Nordmark@Sun.COM  * Used by callers that need to cache e.g., the datapath
353*11042SErik.Nordmark@Sun.COM  * Returns the generation number in the last argument.
354*11042SErik.Nordmark@Sun.COM  * ifindex should only be set for link-locals
355*11042SErik.Nordmark@Sun.COM  */
356*11042SErik.Nordmark@Sun.COM dce_t *
357*11042SErik.Nordmark@Sun.COM dce_lookup_v6(const in6_addr_t *dst, uint_t ifindex, ip_stack_t *ipst,
358*11042SErik.Nordmark@Sun.COM     uint_t *generationp)
359*11042SErik.Nordmark@Sun.COM {
360*11042SErik.Nordmark@Sun.COM 	uint_t		hash;
361*11042SErik.Nordmark@Sun.COM 	dcb_t		*dcb;
362*11042SErik.Nordmark@Sun.COM 	dce_t		*dce;
363*11042SErik.Nordmark@Sun.COM 
364*11042SErik.Nordmark@Sun.COM 	/* Set *generationp before dropping the lock(s) that allow additions */
365*11042SErik.Nordmark@Sun.COM 	if (generationp != NULL)
366*11042SErik.Nordmark@Sun.COM 		*generationp = ipst->ips_dce_default->dce_generation;
367*11042SErik.Nordmark@Sun.COM 
368*11042SErik.Nordmark@Sun.COM 	hash = IRE_ADDR_HASH_V6(*dst, ipst->ips_dce_hashsize);
369*11042SErik.Nordmark@Sun.COM 	dcb = &ipst->ips_dce_hash_v6[hash];
370*11042SErik.Nordmark@Sun.COM 	rw_enter(&dcb->dcb_lock, RW_READER);
371*11042SErik.Nordmark@Sun.COM 	for (dce = dcb->dcb_dce; dce != NULL; dce = dce->dce_next) {
372*11042SErik.Nordmark@Sun.COM 		if (IN6_ARE_ADDR_EQUAL(&dce->dce_v6addr, dst) &&
373*11042SErik.Nordmark@Sun.COM 		    dce->dce_ifindex == ifindex) {
374*11042SErik.Nordmark@Sun.COM 			mutex_enter(&dce->dce_lock);
375*11042SErik.Nordmark@Sun.COM 			if (!DCE_IS_CONDEMNED(dce)) {
376*11042SErik.Nordmark@Sun.COM 				dce_refhold(dce);
377*11042SErik.Nordmark@Sun.COM 				if (generationp != NULL)
378*11042SErik.Nordmark@Sun.COM 					*generationp = dce->dce_generation;
379*11042SErik.Nordmark@Sun.COM 				mutex_exit(&dce->dce_lock);
380*11042SErik.Nordmark@Sun.COM 				rw_exit(&dcb->dcb_lock);
381*11042SErik.Nordmark@Sun.COM 				return (dce);
382*11042SErik.Nordmark@Sun.COM 			}
383*11042SErik.Nordmark@Sun.COM 			mutex_exit(&dce->dce_lock);
384*11042SErik.Nordmark@Sun.COM 		}
385*11042SErik.Nordmark@Sun.COM 	}
386*11042SErik.Nordmark@Sun.COM 	rw_exit(&dcb->dcb_lock);
387*11042SErik.Nordmark@Sun.COM 	/* Not found */
388*11042SErik.Nordmark@Sun.COM 	dce = ipst->ips_dce_default;
389*11042SErik.Nordmark@Sun.COM 	dce_refhold(dce);
390*11042SErik.Nordmark@Sun.COM 	return (dce);
391*11042SErik.Nordmark@Sun.COM }
392*11042SErik.Nordmark@Sun.COM 
393*11042SErik.Nordmark@Sun.COM /*
394*11042SErik.Nordmark@Sun.COM  * Atomically looks for a non-default DCE, and if not found tries to create one.
395*11042SErik.Nordmark@Sun.COM  * If there is no memory it returns NULL.
396*11042SErik.Nordmark@Sun.COM  * When an entry is created we increase the generation number on
397*11042SErik.Nordmark@Sun.COM  * the default DCE so that conn_ip_output will detect there is a new DCE.
398*11042SErik.Nordmark@Sun.COM  */
399*11042SErik.Nordmark@Sun.COM dce_t *
400*11042SErik.Nordmark@Sun.COM dce_lookup_and_add_v4(ipaddr_t dst, ip_stack_t *ipst)
401*11042SErik.Nordmark@Sun.COM {
402*11042SErik.Nordmark@Sun.COM 	uint_t		hash;
403*11042SErik.Nordmark@Sun.COM 	dcb_t		*dcb;
404*11042SErik.Nordmark@Sun.COM 	dce_t		*dce;
405*11042SErik.Nordmark@Sun.COM 
406*11042SErik.Nordmark@Sun.COM 	hash = IRE_ADDR_HASH(dst, ipst->ips_dce_hashsize);
407*11042SErik.Nordmark@Sun.COM 	dcb = &ipst->ips_dce_hash_v4[hash];
408*11042SErik.Nordmark@Sun.COM 	rw_enter(&dcb->dcb_lock, RW_WRITER);
409*11042SErik.Nordmark@Sun.COM 	for (dce = dcb->dcb_dce; dce != NULL; dce = dce->dce_next) {
410*11042SErik.Nordmark@Sun.COM 		if (dce->dce_v4addr == dst) {
411*11042SErik.Nordmark@Sun.COM 			mutex_enter(&dce->dce_lock);
412*11042SErik.Nordmark@Sun.COM 			if (!DCE_IS_CONDEMNED(dce)) {
413*11042SErik.Nordmark@Sun.COM 				dce_refhold(dce);
414*11042SErik.Nordmark@Sun.COM 				mutex_exit(&dce->dce_lock);
415*11042SErik.Nordmark@Sun.COM 				rw_exit(&dcb->dcb_lock);
416*11042SErik.Nordmark@Sun.COM 				return (dce);
417*11042SErik.Nordmark@Sun.COM 			}
418*11042SErik.Nordmark@Sun.COM 			mutex_exit(&dce->dce_lock);
419*11042SErik.Nordmark@Sun.COM 		}
420*11042SErik.Nordmark@Sun.COM 	}
421*11042SErik.Nordmark@Sun.COM 	dce = kmem_cache_alloc(dce_cache, KM_NOSLEEP);
422*11042SErik.Nordmark@Sun.COM 	if (dce == NULL) {
423*11042SErik.Nordmark@Sun.COM 		rw_exit(&dcb->dcb_lock);
424*11042SErik.Nordmark@Sun.COM 		return (NULL);
425*11042SErik.Nordmark@Sun.COM 	}
426*11042SErik.Nordmark@Sun.COM 	bzero(dce, sizeof (dce_t));
427*11042SErik.Nordmark@Sun.COM 	dce->dce_ipst = ipst;	/* No netstack_hold */
428*11042SErik.Nordmark@Sun.COM 	dce->dce_v4addr = dst;
429*11042SErik.Nordmark@Sun.COM 	dce->dce_generation = DCE_GENERATION_INITIAL;
430*11042SErik.Nordmark@Sun.COM 	dce->dce_ipversion = IPV4_VERSION;
431*11042SErik.Nordmark@Sun.COM 	dce->dce_last_change_time = TICK_TO_SEC(lbolt64);
432*11042SErik.Nordmark@Sun.COM 	dce_refhold(dce);	/* For the hash list */
433*11042SErik.Nordmark@Sun.COM 
434*11042SErik.Nordmark@Sun.COM 	/* Link into list */
435*11042SErik.Nordmark@Sun.COM 	if (dcb->dcb_dce != NULL)
436*11042SErik.Nordmark@Sun.COM 		dcb->dcb_dce->dce_ptpn = &dce->dce_next;
437*11042SErik.Nordmark@Sun.COM 	dce->dce_next = dcb->dcb_dce;
438*11042SErik.Nordmark@Sun.COM 	dce->dce_ptpn = &dcb->dcb_dce;
439*11042SErik.Nordmark@Sun.COM 	dcb->dcb_dce = dce;
440*11042SErik.Nordmark@Sun.COM 	dce->dce_bucket = dcb;
441*11042SErik.Nordmark@Sun.COM 	dce_refhold(dce);	/* For the caller */
442*11042SErik.Nordmark@Sun.COM 	rw_exit(&dcb->dcb_lock);
443*11042SErik.Nordmark@Sun.COM 
444*11042SErik.Nordmark@Sun.COM 	/* Initialize dce_ident to be different than for the last packet */
445*11042SErik.Nordmark@Sun.COM 	dce->dce_ident = ipst->ips_dce_default->dce_ident + 1;
446*11042SErik.Nordmark@Sun.COM 
447*11042SErik.Nordmark@Sun.COM 	dce_increment_generation(ipst->ips_dce_default);
448*11042SErik.Nordmark@Sun.COM 	return (dce);
449*11042SErik.Nordmark@Sun.COM }
450*11042SErik.Nordmark@Sun.COM 
451*11042SErik.Nordmark@Sun.COM /*
452*11042SErik.Nordmark@Sun.COM  * Atomically looks for a non-default DCE, and if not found tries to create one.
453*11042SErik.Nordmark@Sun.COM  * If there is no memory it returns NULL.
454*11042SErik.Nordmark@Sun.COM  * When an entry is created we increase the generation number on
455*11042SErik.Nordmark@Sun.COM  * the default DCE so that conn_ip_output will detect there is a new DCE.
456*11042SErik.Nordmark@Sun.COM  * ifindex should only be used with link-local addresses.
457*11042SErik.Nordmark@Sun.COM  */
458*11042SErik.Nordmark@Sun.COM dce_t *
459*11042SErik.Nordmark@Sun.COM dce_lookup_and_add_v6(const in6_addr_t *dst, uint_t ifindex, ip_stack_t *ipst)
460*11042SErik.Nordmark@Sun.COM {
461*11042SErik.Nordmark@Sun.COM 	uint_t		hash;
462*11042SErik.Nordmark@Sun.COM 	dcb_t		*dcb;
463*11042SErik.Nordmark@Sun.COM 	dce_t		*dce;
464*11042SErik.Nordmark@Sun.COM 
465*11042SErik.Nordmark@Sun.COM 	/* We should not create entries for link-locals w/o an ifindex */
466*11042SErik.Nordmark@Sun.COM 	ASSERT(!(IN6_IS_ADDR_LINKSCOPE(dst)) || ifindex != 0);
467*11042SErik.Nordmark@Sun.COM 
468*11042SErik.Nordmark@Sun.COM 	hash = IRE_ADDR_HASH_V6(*dst, ipst->ips_dce_hashsize);
469*11042SErik.Nordmark@Sun.COM 	dcb = &ipst->ips_dce_hash_v6[hash];
470*11042SErik.Nordmark@Sun.COM 	rw_enter(&dcb->dcb_lock, RW_WRITER);
471*11042SErik.Nordmark@Sun.COM 	for (dce = dcb->dcb_dce; dce != NULL; dce = dce->dce_next) {
472*11042SErik.Nordmark@Sun.COM 		if (IN6_ARE_ADDR_EQUAL(&dce->dce_v6addr, dst) &&
473*11042SErik.Nordmark@Sun.COM 		    dce->dce_ifindex == ifindex) {
474*11042SErik.Nordmark@Sun.COM 			mutex_enter(&dce->dce_lock);
475*11042SErik.Nordmark@Sun.COM 			if (!DCE_IS_CONDEMNED(dce)) {
476*11042SErik.Nordmark@Sun.COM 				dce_refhold(dce);
477*11042SErik.Nordmark@Sun.COM 				mutex_exit(&dce->dce_lock);
478*11042SErik.Nordmark@Sun.COM 				rw_exit(&dcb->dcb_lock);
479*11042SErik.Nordmark@Sun.COM 				return (dce);
480*11042SErik.Nordmark@Sun.COM 			}
481*11042SErik.Nordmark@Sun.COM 			mutex_exit(&dce->dce_lock);
482*11042SErik.Nordmark@Sun.COM 		}
483*11042SErik.Nordmark@Sun.COM 	}
484*11042SErik.Nordmark@Sun.COM 
485*11042SErik.Nordmark@Sun.COM 	dce = kmem_cache_alloc(dce_cache, KM_NOSLEEP);
486*11042SErik.Nordmark@Sun.COM 	if (dce == NULL) {
487*11042SErik.Nordmark@Sun.COM 		rw_exit(&dcb->dcb_lock);
488*11042SErik.Nordmark@Sun.COM 		return (NULL);
489*11042SErik.Nordmark@Sun.COM 	}
490*11042SErik.Nordmark@Sun.COM 	bzero(dce, sizeof (dce_t));
491*11042SErik.Nordmark@Sun.COM 	dce->dce_ipst = ipst;	/* No netstack_hold */
492*11042SErik.Nordmark@Sun.COM 	dce->dce_v6addr = *dst;
493*11042SErik.Nordmark@Sun.COM 	dce->dce_ifindex = ifindex;
494*11042SErik.Nordmark@Sun.COM 	dce->dce_generation = DCE_GENERATION_INITIAL;
495*11042SErik.Nordmark@Sun.COM 	dce->dce_ipversion = IPV6_VERSION;
496*11042SErik.Nordmark@Sun.COM 	dce->dce_last_change_time = TICK_TO_SEC(lbolt64);
497*11042SErik.Nordmark@Sun.COM 	dce_refhold(dce);	/* For the hash list */
498*11042SErik.Nordmark@Sun.COM 
499*11042SErik.Nordmark@Sun.COM 	/* Link into list */
500*11042SErik.Nordmark@Sun.COM 	if (dcb->dcb_dce != NULL)
501*11042SErik.Nordmark@Sun.COM 		dcb->dcb_dce->dce_ptpn = &dce->dce_next;
502*11042SErik.Nordmark@Sun.COM 	dce->dce_next = dcb->dcb_dce;
503*11042SErik.Nordmark@Sun.COM 	dce->dce_ptpn = &dcb->dcb_dce;
504*11042SErik.Nordmark@Sun.COM 	dcb->dcb_dce = dce;
505*11042SErik.Nordmark@Sun.COM 	dce->dce_bucket = dcb;
506*11042SErik.Nordmark@Sun.COM 	atomic_add_32(&dcb->dcb_cnt, 1);
507*11042SErik.Nordmark@Sun.COM 	dce_refhold(dce);	/* For the caller */
508*11042SErik.Nordmark@Sun.COM 	rw_exit(&dcb->dcb_lock);
509*11042SErik.Nordmark@Sun.COM 
510*11042SErik.Nordmark@Sun.COM 	/* Initialize dce_ident to be different than for the last packet */
511*11042SErik.Nordmark@Sun.COM 	dce->dce_ident = ipst->ips_dce_default->dce_ident + 1;
512*11042SErik.Nordmark@Sun.COM 	dce_increment_generation(ipst->ips_dce_default);
513*11042SErik.Nordmark@Sun.COM 	return (dce);
514*11042SErik.Nordmark@Sun.COM }
515*11042SErik.Nordmark@Sun.COM 
516*11042SErik.Nordmark@Sun.COM /*
517*11042SErik.Nordmark@Sun.COM  * Set/update uinfo. Creates a per-destination dce if none exists.
518*11042SErik.Nordmark@Sun.COM  *
519*11042SErik.Nordmark@Sun.COM  * Note that we do not bump the generation number here.
520*11042SErik.Nordmark@Sun.COM  * New connections will find the new uinfo.
521*11042SErik.Nordmark@Sun.COM  *
522*11042SErik.Nordmark@Sun.COM  * The only use of this (tcp, sctp using iulp_t) is to set rtt+rtt_sd.
523*11042SErik.Nordmark@Sun.COM  */
524*11042SErik.Nordmark@Sun.COM static void
525*11042SErik.Nordmark@Sun.COM dce_setuinfo(dce_t *dce, iulp_t *uinfo)
526*11042SErik.Nordmark@Sun.COM {
527*11042SErik.Nordmark@Sun.COM 	/*
528*11042SErik.Nordmark@Sun.COM 	 * Update the round trip time estimate and/or the max frag size
529*11042SErik.Nordmark@Sun.COM 	 * and/or the slow start threshold.
530*11042SErik.Nordmark@Sun.COM 	 *
531*11042SErik.Nordmark@Sun.COM 	 * We serialize multiple advises using dce_lock.
532*11042SErik.Nordmark@Sun.COM 	 */
533*11042SErik.Nordmark@Sun.COM 	mutex_enter(&dce->dce_lock);
534*11042SErik.Nordmark@Sun.COM 	/* Gard against setting to zero */
535*11042SErik.Nordmark@Sun.COM 	if (uinfo->iulp_rtt != 0) {
536*11042SErik.Nordmark@Sun.COM 		/*
537*11042SErik.Nordmark@Sun.COM 		 * If there is no old cached values, initialize them
538*11042SErik.Nordmark@Sun.COM 		 * conservatively.  Set them to be (1.5 * new value).
539*11042SErik.Nordmark@Sun.COM 		 */
540*11042SErik.Nordmark@Sun.COM 		if (dce->dce_uinfo.iulp_rtt != 0) {
541*11042SErik.Nordmark@Sun.COM 			dce->dce_uinfo.iulp_rtt = (dce->dce_uinfo.iulp_rtt +
542*11042SErik.Nordmark@Sun.COM 			    uinfo->iulp_rtt) >> 1;
543*11042SErik.Nordmark@Sun.COM 		} else {
544*11042SErik.Nordmark@Sun.COM 			dce->dce_uinfo.iulp_rtt = uinfo->iulp_rtt +
545*11042SErik.Nordmark@Sun.COM 			    (uinfo->iulp_rtt >> 1);
546*11042SErik.Nordmark@Sun.COM 		}
547*11042SErik.Nordmark@Sun.COM 		if (dce->dce_uinfo.iulp_rtt_sd != 0) {
548*11042SErik.Nordmark@Sun.COM 			dce->dce_uinfo.iulp_rtt_sd =
549*11042SErik.Nordmark@Sun.COM 			    (dce->dce_uinfo.iulp_rtt_sd +
550*11042SErik.Nordmark@Sun.COM 			    uinfo->iulp_rtt_sd) >> 1;
551*11042SErik.Nordmark@Sun.COM 		} else {
552*11042SErik.Nordmark@Sun.COM 			dce->dce_uinfo.iulp_rtt_sd = uinfo->iulp_rtt_sd +
553*11042SErik.Nordmark@Sun.COM 			    (uinfo->iulp_rtt_sd >> 1);
554*11042SErik.Nordmark@Sun.COM 		}
555*11042SErik.Nordmark@Sun.COM 	}
556*11042SErik.Nordmark@Sun.COM 	if (uinfo->iulp_mtu != 0) {
557*11042SErik.Nordmark@Sun.COM 		if (dce->dce_flags & DCEF_PMTU) {
558*11042SErik.Nordmark@Sun.COM 			dce->dce_pmtu = MIN(uinfo->iulp_mtu, dce->dce_pmtu);
559*11042SErik.Nordmark@Sun.COM 		} else {
560*11042SErik.Nordmark@Sun.COM 			dce->dce_pmtu = MIN(uinfo->iulp_mtu, IP_MAXPACKET);
561*11042SErik.Nordmark@Sun.COM 			dce->dce_flags |= DCEF_PMTU;
562*11042SErik.Nordmark@Sun.COM 		}
563*11042SErik.Nordmark@Sun.COM 		dce->dce_last_change_time = TICK_TO_SEC(lbolt64);
564*11042SErik.Nordmark@Sun.COM 	}
565*11042SErik.Nordmark@Sun.COM 	if (uinfo->iulp_ssthresh != 0) {
566*11042SErik.Nordmark@Sun.COM 		if (dce->dce_uinfo.iulp_ssthresh != 0)
567*11042SErik.Nordmark@Sun.COM 			dce->dce_uinfo.iulp_ssthresh =
568*11042SErik.Nordmark@Sun.COM 			    (uinfo->iulp_ssthresh +
569*11042SErik.Nordmark@Sun.COM 			    dce->dce_uinfo.iulp_ssthresh) >> 1;
570*11042SErik.Nordmark@Sun.COM 		else
571*11042SErik.Nordmark@Sun.COM 			dce->dce_uinfo.iulp_ssthresh = uinfo->iulp_ssthresh;
572*11042SErik.Nordmark@Sun.COM 	}
573*11042SErik.Nordmark@Sun.COM 	/* We have uinfo for sure */
574*11042SErik.Nordmark@Sun.COM 	dce->dce_flags |= DCEF_UINFO;
575*11042SErik.Nordmark@Sun.COM 	mutex_exit(&dce->dce_lock);
576*11042SErik.Nordmark@Sun.COM }
577*11042SErik.Nordmark@Sun.COM 
578*11042SErik.Nordmark@Sun.COM 
579*11042SErik.Nordmark@Sun.COM int
580*11042SErik.Nordmark@Sun.COM dce_update_uinfo_v4(ipaddr_t dst, iulp_t *uinfo, ip_stack_t *ipst)
581*11042SErik.Nordmark@Sun.COM {
582*11042SErik.Nordmark@Sun.COM 	dce_t *dce;
583*11042SErik.Nordmark@Sun.COM 
584*11042SErik.Nordmark@Sun.COM 	dce = dce_lookup_and_add_v4(dst, ipst);
585*11042SErik.Nordmark@Sun.COM 	if (dce == NULL)
586*11042SErik.Nordmark@Sun.COM 		return (ENOMEM);
587*11042SErik.Nordmark@Sun.COM 
588*11042SErik.Nordmark@Sun.COM 	dce_setuinfo(dce, uinfo);
589*11042SErik.Nordmark@Sun.COM 	dce_refrele(dce);
590*11042SErik.Nordmark@Sun.COM 	return (0);
591*11042SErik.Nordmark@Sun.COM }
592*11042SErik.Nordmark@Sun.COM 
593*11042SErik.Nordmark@Sun.COM int
594*11042SErik.Nordmark@Sun.COM dce_update_uinfo_v6(const in6_addr_t *dst, uint_t ifindex, iulp_t *uinfo,
595*11042SErik.Nordmark@Sun.COM     ip_stack_t *ipst)
596*11042SErik.Nordmark@Sun.COM {
597*11042SErik.Nordmark@Sun.COM 	dce_t *dce;
598*11042SErik.Nordmark@Sun.COM 
599*11042SErik.Nordmark@Sun.COM 	dce = dce_lookup_and_add_v6(dst, ifindex, ipst);
600*11042SErik.Nordmark@Sun.COM 	if (dce == NULL)
601*11042SErik.Nordmark@Sun.COM 		return (ENOMEM);
602*11042SErik.Nordmark@Sun.COM 
603*11042SErik.Nordmark@Sun.COM 	dce_setuinfo(dce, uinfo);
604*11042SErik.Nordmark@Sun.COM 	dce_refrele(dce);
605*11042SErik.Nordmark@Sun.COM 	return (0);
606*11042SErik.Nordmark@Sun.COM }
607*11042SErik.Nordmark@Sun.COM 
608*11042SErik.Nordmark@Sun.COM /* Common routine for IPv4 and IPv6 */
609*11042SErik.Nordmark@Sun.COM int
610*11042SErik.Nordmark@Sun.COM dce_update_uinfo(const in6_addr_t *dst, uint_t ifindex, iulp_t *uinfo,
611*11042SErik.Nordmark@Sun.COM     ip_stack_t *ipst)
612*11042SErik.Nordmark@Sun.COM {
613*11042SErik.Nordmark@Sun.COM 	ipaddr_t dst4;
614*11042SErik.Nordmark@Sun.COM 
615*11042SErik.Nordmark@Sun.COM 	if (IN6_IS_ADDR_V4MAPPED_ANY(dst)) {
616*11042SErik.Nordmark@Sun.COM 		IN6_V4MAPPED_TO_IPADDR(dst, dst4);
617*11042SErik.Nordmark@Sun.COM 		return (dce_update_uinfo_v4(dst4, uinfo, ipst));
618*11042SErik.Nordmark@Sun.COM 	} else {
619*11042SErik.Nordmark@Sun.COM 		return (dce_update_uinfo_v6(dst, ifindex, uinfo, ipst));
620*11042SErik.Nordmark@Sun.COM 	}
621*11042SErik.Nordmark@Sun.COM }
622*11042SErik.Nordmark@Sun.COM 
623*11042SErik.Nordmark@Sun.COM static void
624*11042SErik.Nordmark@Sun.COM dce_make_condemned(dce_t *dce)
625*11042SErik.Nordmark@Sun.COM {
626*11042SErik.Nordmark@Sun.COM 	ip_stack_t	*ipst = dce->dce_ipst;
627*11042SErik.Nordmark@Sun.COM 
628*11042SErik.Nordmark@Sun.COM 	mutex_enter(&dce->dce_lock);
629*11042SErik.Nordmark@Sun.COM 	ASSERT(!DCE_IS_CONDEMNED(dce));
630*11042SErik.Nordmark@Sun.COM 	dce->dce_generation = DCE_GENERATION_CONDEMNED;
631*11042SErik.Nordmark@Sun.COM 	mutex_exit(&dce->dce_lock);
632*11042SErik.Nordmark@Sun.COM 	/* Count how many condemned dces for kmem_cache callback */
633*11042SErik.Nordmark@Sun.COM 	atomic_add_32(&ipst->ips_num_dce_condemned, 1);
634*11042SErik.Nordmark@Sun.COM }
635*11042SErik.Nordmark@Sun.COM 
636*11042SErik.Nordmark@Sun.COM /*
637*11042SErik.Nordmark@Sun.COM  * Increment the generation avoiding the special condemned value
638*11042SErik.Nordmark@Sun.COM  */
639*11042SErik.Nordmark@Sun.COM void
640*11042SErik.Nordmark@Sun.COM dce_increment_generation(dce_t *dce)
641*11042SErik.Nordmark@Sun.COM {
642*11042SErik.Nordmark@Sun.COM 	uint_t generation;
643*11042SErik.Nordmark@Sun.COM 
644*11042SErik.Nordmark@Sun.COM 	mutex_enter(&dce->dce_lock);
645*11042SErik.Nordmark@Sun.COM 	if (!DCE_IS_CONDEMNED(dce)) {
646*11042SErik.Nordmark@Sun.COM 		generation = dce->dce_generation + 1;
647*11042SErik.Nordmark@Sun.COM 		if (generation == DCE_GENERATION_CONDEMNED)
648*11042SErik.Nordmark@Sun.COM 			generation = DCE_GENERATION_INITIAL;
649*11042SErik.Nordmark@Sun.COM 		ASSERT(generation != DCE_GENERATION_VERIFY);
650*11042SErik.Nordmark@Sun.COM 		dce->dce_generation = generation;
651*11042SErik.Nordmark@Sun.COM 	}
652*11042SErik.Nordmark@Sun.COM 	mutex_exit(&dce->dce_lock);
653*11042SErik.Nordmark@Sun.COM }
654*11042SErik.Nordmark@Sun.COM 
655*11042SErik.Nordmark@Sun.COM /*
656*11042SErik.Nordmark@Sun.COM  * Increment the generation number on all dces that have a path MTU and
657*11042SErik.Nordmark@Sun.COM  * the default DCE. Used when ill_mtu changes.
658*11042SErik.Nordmark@Sun.COM  */
659*11042SErik.Nordmark@Sun.COM void
660*11042SErik.Nordmark@Sun.COM dce_increment_all_generations(boolean_t isv6, ip_stack_t *ipst)
661*11042SErik.Nordmark@Sun.COM {
662*11042SErik.Nordmark@Sun.COM 	int		i;
663*11042SErik.Nordmark@Sun.COM 	dcb_t		*dcb;
664*11042SErik.Nordmark@Sun.COM 	dce_t		*dce;
665*11042SErik.Nordmark@Sun.COM 
666*11042SErik.Nordmark@Sun.COM 	for (i = 0; i < ipst->ips_dce_hashsize; i++) {
667*11042SErik.Nordmark@Sun.COM 		if (isv6)
668*11042SErik.Nordmark@Sun.COM 			dcb = &ipst->ips_dce_hash_v6[i];
669*11042SErik.Nordmark@Sun.COM 		else
670*11042SErik.Nordmark@Sun.COM 			dcb = &ipst->ips_dce_hash_v4[i];
671*11042SErik.Nordmark@Sun.COM 		rw_enter(&dcb->dcb_lock, RW_WRITER);
672*11042SErik.Nordmark@Sun.COM 		for (dce = dcb->dcb_dce; dce != NULL; dce = dce->dce_next) {
673*11042SErik.Nordmark@Sun.COM 			if (DCE_IS_CONDEMNED(dce))
674*11042SErik.Nordmark@Sun.COM 				continue;
675*11042SErik.Nordmark@Sun.COM 			dce_increment_generation(dce);
676*11042SErik.Nordmark@Sun.COM 		}
677*11042SErik.Nordmark@Sun.COM 		rw_exit(&dcb->dcb_lock);
678*11042SErik.Nordmark@Sun.COM 	}
679*11042SErik.Nordmark@Sun.COM 	dce_increment_generation(ipst->ips_dce_default);
680*11042SErik.Nordmark@Sun.COM }
681*11042SErik.Nordmark@Sun.COM 
682*11042SErik.Nordmark@Sun.COM /*
683*11042SErik.Nordmark@Sun.COM  * Caller needs to do a dce_refrele since we can't do the
684*11042SErik.Nordmark@Sun.COM  * dce_refrele under dcb_lock.
685*11042SErik.Nordmark@Sun.COM  */
686*11042SErik.Nordmark@Sun.COM static void
687*11042SErik.Nordmark@Sun.COM dce_delete_locked(dcb_t *dcb, dce_t *dce)
688*11042SErik.Nordmark@Sun.COM {
689*11042SErik.Nordmark@Sun.COM 	dce->dce_bucket = NULL;
690*11042SErik.Nordmark@Sun.COM 	*dce->dce_ptpn = dce->dce_next;
691*11042SErik.Nordmark@Sun.COM 	if (dce->dce_next != NULL)
692*11042SErik.Nordmark@Sun.COM 		dce->dce_next->dce_ptpn = dce->dce_ptpn;
693*11042SErik.Nordmark@Sun.COM 	dce->dce_ptpn = NULL;
694*11042SErik.Nordmark@Sun.COM 	dce->dce_next = NULL;
695*11042SErik.Nordmark@Sun.COM 	atomic_add_32(&dcb->dcb_cnt, -1);
696*11042SErik.Nordmark@Sun.COM 	dce_make_condemned(dce);
697*11042SErik.Nordmark@Sun.COM }
698*11042SErik.Nordmark@Sun.COM 
699*11042SErik.Nordmark@Sun.COM static void
700*11042SErik.Nordmark@Sun.COM dce_inactive(dce_t *dce)
701*11042SErik.Nordmark@Sun.COM {
702*11042SErik.Nordmark@Sun.COM 	ip_stack_t	*ipst = dce->dce_ipst;
703*11042SErik.Nordmark@Sun.COM 
704*11042SErik.Nordmark@Sun.COM 	ASSERT(!(dce->dce_flags & DCEF_DEFAULT));
705*11042SErik.Nordmark@Sun.COM 	ASSERT(dce->dce_ptpn == NULL);
706*11042SErik.Nordmark@Sun.COM 	ASSERT(dce->dce_bucket == NULL);
707*11042SErik.Nordmark@Sun.COM 
708*11042SErik.Nordmark@Sun.COM 	/* Count how many condemned dces for kmem_cache callback */
709*11042SErik.Nordmark@Sun.COM 	if (DCE_IS_CONDEMNED(dce))
710*11042SErik.Nordmark@Sun.COM 		atomic_add_32(&ipst->ips_num_dce_condemned, -1);
711*11042SErik.Nordmark@Sun.COM 
712*11042SErik.Nordmark@Sun.COM 	kmem_cache_free(dce_cache, dce);
713*11042SErik.Nordmark@Sun.COM }
714*11042SErik.Nordmark@Sun.COM 
715*11042SErik.Nordmark@Sun.COM void
716*11042SErik.Nordmark@Sun.COM dce_refrele(dce_t *dce)
717*11042SErik.Nordmark@Sun.COM {
718*11042SErik.Nordmark@Sun.COM 	ASSERT(dce->dce_refcnt != 0);
719*11042SErik.Nordmark@Sun.COM 	if (atomic_add_32_nv(&dce->dce_refcnt, -1) == 0)
720*11042SErik.Nordmark@Sun.COM 		dce_inactive(dce);
721*11042SErik.Nordmark@Sun.COM }
722*11042SErik.Nordmark@Sun.COM 
723*11042SErik.Nordmark@Sun.COM void
724*11042SErik.Nordmark@Sun.COM dce_refhold(dce_t *dce)
725*11042SErik.Nordmark@Sun.COM {
726*11042SErik.Nordmark@Sun.COM 	atomic_add_32(&dce->dce_refcnt, 1);
727*11042SErik.Nordmark@Sun.COM 	ASSERT(dce->dce_refcnt != 0);
728*11042SErik.Nordmark@Sun.COM }
729*11042SErik.Nordmark@Sun.COM 
730*11042SErik.Nordmark@Sun.COM /* No tracing support yet hence the same as the above functions */
731*11042SErik.Nordmark@Sun.COM void
732*11042SErik.Nordmark@Sun.COM dce_refrele_notr(dce_t *dce)
733*11042SErik.Nordmark@Sun.COM {
734*11042SErik.Nordmark@Sun.COM 	ASSERT(dce->dce_refcnt != 0);
735*11042SErik.Nordmark@Sun.COM 	if (atomic_add_32_nv(&dce->dce_refcnt, -1) == 0)
736*11042SErik.Nordmark@Sun.COM 		dce_inactive(dce);
737*11042SErik.Nordmark@Sun.COM }
738*11042SErik.Nordmark@Sun.COM 
739*11042SErik.Nordmark@Sun.COM void
740*11042SErik.Nordmark@Sun.COM dce_refhold_notr(dce_t *dce)
741*11042SErik.Nordmark@Sun.COM {
742*11042SErik.Nordmark@Sun.COM 	atomic_add_32(&dce->dce_refcnt, 1);
743*11042SErik.Nordmark@Sun.COM 	ASSERT(dce->dce_refcnt != 0);
744*11042SErik.Nordmark@Sun.COM }
745*11042SErik.Nordmark@Sun.COM 
746*11042SErik.Nordmark@Sun.COM /* Report both the IPv4 and IPv6 DCEs. */
747*11042SErik.Nordmark@Sun.COM mblk_t *
748*11042SErik.Nordmark@Sun.COM ip_snmp_get_mib2_ip_dce(queue_t *q, mblk_t *mpctl, ip_stack_t *ipst)
749*11042SErik.Nordmark@Sun.COM {
750*11042SErik.Nordmark@Sun.COM 	struct opthdr		*optp;
751*11042SErik.Nordmark@Sun.COM 	mblk_t			*mp2ctl;
752*11042SErik.Nordmark@Sun.COM 	dest_cache_entry_t	dest_cache;
753*11042SErik.Nordmark@Sun.COM 	mblk_t			*mp_tail = NULL;
754*11042SErik.Nordmark@Sun.COM 	dce_t			*dce;
755*11042SErik.Nordmark@Sun.COM 	dcb_t			*dcb;
756*11042SErik.Nordmark@Sun.COM 	int			i;
757*11042SErik.Nordmark@Sun.COM 	uint64_t		current_time;
758*11042SErik.Nordmark@Sun.COM 
759*11042SErik.Nordmark@Sun.COM 	current_time = TICK_TO_SEC(lbolt64);
760*11042SErik.Nordmark@Sun.COM 
761*11042SErik.Nordmark@Sun.COM 	/*
762*11042SErik.Nordmark@Sun.COM 	 * make a copy of the original message
763*11042SErik.Nordmark@Sun.COM 	 */
764*11042SErik.Nordmark@Sun.COM 	mp2ctl = copymsg(mpctl);
765*11042SErik.Nordmark@Sun.COM 
766*11042SErik.Nordmark@Sun.COM 	/* First we do IPv4 entries */
767*11042SErik.Nordmark@Sun.COM 	optp = (struct opthdr *)&mpctl->b_rptr[
768*11042SErik.Nordmark@Sun.COM 	    sizeof (struct T_optmgmt_ack)];
769*11042SErik.Nordmark@Sun.COM 	optp->level = MIB2_IP;
770*11042SErik.Nordmark@Sun.COM 	optp->name = EXPER_IP_DCE;
771*11042SErik.Nordmark@Sun.COM 
772*11042SErik.Nordmark@Sun.COM 	for (i = 0; i < ipst->ips_dce_hashsize; i++) {
773*11042SErik.Nordmark@Sun.COM 		dcb = &ipst->ips_dce_hash_v4[i];
774*11042SErik.Nordmark@Sun.COM 		rw_enter(&dcb->dcb_lock, RW_READER);
775*11042SErik.Nordmark@Sun.COM 		for (dce = dcb->dcb_dce; dce != NULL; dce = dce->dce_next) {
776*11042SErik.Nordmark@Sun.COM 			dest_cache.DestIpv4Address = dce->dce_v4addr;
777*11042SErik.Nordmark@Sun.COM 			dest_cache.DestFlags = dce->dce_flags;
778*11042SErik.Nordmark@Sun.COM 			if (dce->dce_flags & DCEF_PMTU)
779*11042SErik.Nordmark@Sun.COM 				dest_cache.DestPmtu = dce->dce_pmtu;
780*11042SErik.Nordmark@Sun.COM 			else
781*11042SErik.Nordmark@Sun.COM 				dest_cache.DestPmtu = 0;
782*11042SErik.Nordmark@Sun.COM 			dest_cache.DestIdent = dce->dce_ident;
783*11042SErik.Nordmark@Sun.COM 			dest_cache.DestIfindex = 0;
784*11042SErik.Nordmark@Sun.COM 			dest_cache.DestAge = current_time -
785*11042SErik.Nordmark@Sun.COM 			    dce->dce_last_change_time;
786*11042SErik.Nordmark@Sun.COM 			if (!snmp_append_data2(mpctl->b_cont, &mp_tail,
787*11042SErik.Nordmark@Sun.COM 			    (char *)&dest_cache, (int)sizeof (dest_cache))) {
788*11042SErik.Nordmark@Sun.COM 				ip1dbg(("ip_snmp_get_mib2_ip_dce: "
789*11042SErik.Nordmark@Sun.COM 				    "failed to allocate %u bytes\n",
790*11042SErik.Nordmark@Sun.COM 				    (uint_t)sizeof (dest_cache)));
791*11042SErik.Nordmark@Sun.COM 			}
792*11042SErik.Nordmark@Sun.COM 		}
793*11042SErik.Nordmark@Sun.COM 		rw_exit(&dcb->dcb_lock);
794*11042SErik.Nordmark@Sun.COM 	}
795*11042SErik.Nordmark@Sun.COM 	optp->len = (t_uscalar_t)msgdsize(mpctl->b_cont);
796*11042SErik.Nordmark@Sun.COM 	ip3dbg(("ip_snmp_get: level %d, name %d, len %d\n",
797*11042SErik.Nordmark@Sun.COM 	    (int)optp->level, (int)optp->name, (int)optp->len));
798*11042SErik.Nordmark@Sun.COM 	qreply(q, mpctl);
799*11042SErik.Nordmark@Sun.COM 
800*11042SErik.Nordmark@Sun.COM 	if (mp2ctl == NULL) {
801*11042SErik.Nordmark@Sun.COM 		/* Copymsg failed above */
802*11042SErik.Nordmark@Sun.COM 		return (NULL);
803*11042SErik.Nordmark@Sun.COM 	}
804*11042SErik.Nordmark@Sun.COM 
805*11042SErik.Nordmark@Sun.COM 	/* Now for IPv6 */
806*11042SErik.Nordmark@Sun.COM 	mpctl = mp2ctl;
807*11042SErik.Nordmark@Sun.COM 	mp_tail = NULL;
808*11042SErik.Nordmark@Sun.COM 	mp2ctl = copymsg(mpctl);
809*11042SErik.Nordmark@Sun.COM 	optp = (struct opthdr *)&mpctl->b_rptr[
810*11042SErik.Nordmark@Sun.COM 	    sizeof (struct T_optmgmt_ack)];
811*11042SErik.Nordmark@Sun.COM 	optp->level = MIB2_IP6;
812*11042SErik.Nordmark@Sun.COM 	optp->name = EXPER_IP_DCE;
813*11042SErik.Nordmark@Sun.COM 
814*11042SErik.Nordmark@Sun.COM 	for (i = 0; i < ipst->ips_dce_hashsize; i++) {
815*11042SErik.Nordmark@Sun.COM 		dcb = &ipst->ips_dce_hash_v6[i];
816*11042SErik.Nordmark@Sun.COM 		rw_enter(&dcb->dcb_lock, RW_READER);
817*11042SErik.Nordmark@Sun.COM 		for (dce = dcb->dcb_dce; dce != NULL; dce = dce->dce_next) {
818*11042SErik.Nordmark@Sun.COM 			dest_cache.DestIpv6Address = dce->dce_v6addr;
819*11042SErik.Nordmark@Sun.COM 			dest_cache.DestFlags = dce->dce_flags;
820*11042SErik.Nordmark@Sun.COM 			if (dce->dce_flags & DCEF_PMTU)
821*11042SErik.Nordmark@Sun.COM 				dest_cache.DestPmtu = dce->dce_pmtu;
822*11042SErik.Nordmark@Sun.COM 			else
823*11042SErik.Nordmark@Sun.COM 				dest_cache.DestPmtu = 0;
824*11042SErik.Nordmark@Sun.COM 			dest_cache.DestIdent = dce->dce_ident;
825*11042SErik.Nordmark@Sun.COM 			if (IN6_IS_ADDR_LINKSCOPE(&dce->dce_v6addr))
826*11042SErik.Nordmark@Sun.COM 				dest_cache.DestIfindex = dce->dce_ifindex;
827*11042SErik.Nordmark@Sun.COM 			else
828*11042SErik.Nordmark@Sun.COM 				dest_cache.DestIfindex = 0;
829*11042SErik.Nordmark@Sun.COM 			dest_cache.DestAge = current_time -
830*11042SErik.Nordmark@Sun.COM 			    dce->dce_last_change_time;
831*11042SErik.Nordmark@Sun.COM 			if (!snmp_append_data2(mpctl->b_cont, &mp_tail,
832*11042SErik.Nordmark@Sun.COM 			    (char *)&dest_cache, (int)sizeof (dest_cache))) {
833*11042SErik.Nordmark@Sun.COM 				ip1dbg(("ip_snmp_get_mib2_ip_dce: "
834*11042SErik.Nordmark@Sun.COM 				    "failed to allocate %u bytes\n",
835*11042SErik.Nordmark@Sun.COM 				    (uint_t)sizeof (dest_cache)));
836*11042SErik.Nordmark@Sun.COM 			}
837*11042SErik.Nordmark@Sun.COM 		}
838*11042SErik.Nordmark@Sun.COM 		rw_exit(&dcb->dcb_lock);
839*11042SErik.Nordmark@Sun.COM 	}
840*11042SErik.Nordmark@Sun.COM 	optp->len = (t_uscalar_t)msgdsize(mpctl->b_cont);
841*11042SErik.Nordmark@Sun.COM 	ip3dbg(("ip_snmp_get: level %d, name %d, len %d\n",
842*11042SErik.Nordmark@Sun.COM 	    (int)optp->level, (int)optp->name, (int)optp->len));
843*11042SErik.Nordmark@Sun.COM 	qreply(q, mpctl);
844*11042SErik.Nordmark@Sun.COM 
845*11042SErik.Nordmark@Sun.COM 	return (mp2ctl);
846*11042SErik.Nordmark@Sun.COM }
847*11042SErik.Nordmark@Sun.COM 
848*11042SErik.Nordmark@Sun.COM /*
849*11042SErik.Nordmark@Sun.COM  * Remove IPv6 DCEs which refer to an ifindex that is going away.
850*11042SErik.Nordmark@Sun.COM  * This is not required for correctness, but it avoids netstat -d
851*11042SErik.Nordmark@Sun.COM  * showing stale stuff that will never be used.
852*11042SErik.Nordmark@Sun.COM  */
853*11042SErik.Nordmark@Sun.COM void
854*11042SErik.Nordmark@Sun.COM dce_cleanup(uint_t ifindex, ip_stack_t *ipst)
855*11042SErik.Nordmark@Sun.COM {
856*11042SErik.Nordmark@Sun.COM 	uint_t	i;
857*11042SErik.Nordmark@Sun.COM 	dcb_t	*dcb;
858*11042SErik.Nordmark@Sun.COM 	dce_t	*dce, *nextdce;
859*11042SErik.Nordmark@Sun.COM 
860*11042SErik.Nordmark@Sun.COM 	for (i = 0; i < ipst->ips_dce_hashsize; i++) {
861*11042SErik.Nordmark@Sun.COM 		dcb = &ipst->ips_dce_hash_v6[i];
862*11042SErik.Nordmark@Sun.COM 		rw_enter(&dcb->dcb_lock, RW_WRITER);
863*11042SErik.Nordmark@Sun.COM 
864*11042SErik.Nordmark@Sun.COM 		for (dce = dcb->dcb_dce; dce != NULL; dce = nextdce) {
865*11042SErik.Nordmark@Sun.COM 			nextdce = dce->dce_next;
866*11042SErik.Nordmark@Sun.COM 			if (dce->dce_ifindex == ifindex) {
867*11042SErik.Nordmark@Sun.COM 				dce_delete_locked(dcb, dce);
868*11042SErik.Nordmark@Sun.COM 				dce_refrele(dce);
869*11042SErik.Nordmark@Sun.COM 			}
870*11042SErik.Nordmark@Sun.COM 		}
871*11042SErik.Nordmark@Sun.COM 		rw_exit(&dcb->dcb_lock);
872*11042SErik.Nordmark@Sun.COM 	}
873*11042SErik.Nordmark@Sun.COM }
874