xref: /onnv-gate/usr/src/uts/common/inet/ip/ip_dce.c (revision 13123:9efd3d43accd)
111042SErik.Nordmark@Sun.COM /*
211042SErik.Nordmark@Sun.COM  * CDDL HEADER START
311042SErik.Nordmark@Sun.COM  *
411042SErik.Nordmark@Sun.COM  * The contents of this file are subject to the terms of the
511042SErik.Nordmark@Sun.COM  * Common Development and Distribution License (the "License").
611042SErik.Nordmark@Sun.COM  * You may not use this file except in compliance with the License.
711042SErik.Nordmark@Sun.COM  *
811042SErik.Nordmark@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
911042SErik.Nordmark@Sun.COM  * or http://www.opensolaris.org/os/licensing.
1011042SErik.Nordmark@Sun.COM  * See the License for the specific language governing permissions
1111042SErik.Nordmark@Sun.COM  * and limitations under the License.
1211042SErik.Nordmark@Sun.COM  *
1311042SErik.Nordmark@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
1411042SErik.Nordmark@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1511042SErik.Nordmark@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
1611042SErik.Nordmark@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
1711042SErik.Nordmark@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
1811042SErik.Nordmark@Sun.COM  *
1911042SErik.Nordmark@Sun.COM  * CDDL HEADER END
2011042SErik.Nordmark@Sun.COM  */
2111042SErik.Nordmark@Sun.COM 
2211042SErik.Nordmark@Sun.COM /*
23*13123SErik.Nordmark@Sun.COM  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
2411042SErik.Nordmark@Sun.COM  */
2511042SErik.Nordmark@Sun.COM 
2611042SErik.Nordmark@Sun.COM #include <sys/types.h>
2711042SErik.Nordmark@Sun.COM #include <sys/stream.h>
2811042SErik.Nordmark@Sun.COM #include <sys/strsun.h>
2911042SErik.Nordmark@Sun.COM #include <sys/zone.h>
3011042SErik.Nordmark@Sun.COM #include <sys/ddi.h>
3111042SErik.Nordmark@Sun.COM #include <sys/sunddi.h>
3211042SErik.Nordmark@Sun.COM #include <sys/cmn_err.h>
3311042SErik.Nordmark@Sun.COM #include <sys/debug.h>
3411042SErik.Nordmark@Sun.COM #include <sys/atomic.h>
3511042SErik.Nordmark@Sun.COM #define	_SUN_TPI_VERSION 2
3611042SErik.Nordmark@Sun.COM #include <sys/tihdr.h>
3711042SErik.Nordmark@Sun.COM 
3811042SErik.Nordmark@Sun.COM #include <inet/common.h>
3911042SErik.Nordmark@Sun.COM #include <inet/mi.h>
4011042SErik.Nordmark@Sun.COM #include <inet/mib2.h>
4111042SErik.Nordmark@Sun.COM #include <inet/snmpcom.h>
4211042SErik.Nordmark@Sun.COM 
4311042SErik.Nordmark@Sun.COM #include <netinet/ip6.h>
4411042SErik.Nordmark@Sun.COM #include <netinet/icmp6.h>
4511042SErik.Nordmark@Sun.COM 
4611042SErik.Nordmark@Sun.COM #include <inet/ip.h>
4711042SErik.Nordmark@Sun.COM #include <inet/ip_impl.h>
4811042SErik.Nordmark@Sun.COM #include <inet/ip6.h>
4911042SErik.Nordmark@Sun.COM #include <inet/ip6_asp.h>
5011042SErik.Nordmark@Sun.COM #include <inet/ip_multi.h>
5111042SErik.Nordmark@Sun.COM #include <inet/ip_if.h>
5211042SErik.Nordmark@Sun.COM #include <inet/ip_ire.h>
5311042SErik.Nordmark@Sun.COM #include <inet/ip_ftable.h>
5411042SErik.Nordmark@Sun.COM #include <inet/ip_rts.h>
5511042SErik.Nordmark@Sun.COM #include <inet/ip_ndp.h>
5611042SErik.Nordmark@Sun.COM #include <inet/ipclassifier.h>
5711042SErik.Nordmark@Sun.COM #include <inet/ip_listutils.h>
5811042SErik.Nordmark@Sun.COM 
5911042SErik.Nordmark@Sun.COM #include <sys/sunddi.h>
6011042SErik.Nordmark@Sun.COM 
6111042SErik.Nordmark@Sun.COM /*
6211042SErik.Nordmark@Sun.COM  * Routines for handling destination cache entries.
6311042SErik.Nordmark@Sun.COM  * There is always one DCEF_DEFAULT for each ip_stack_t created at init time.
6411042SErik.Nordmark@Sun.COM  * That entry holds both the IP ident value and the dce generation number.
6511042SErik.Nordmark@Sun.COM  *
6611042SErik.Nordmark@Sun.COM  * Any time a DCE is changed significantly (different path MTU, but NOT
6711042SErik.Nordmark@Sun.COM  * different ULP info!), the dce_generation number is increased.
6811042SErik.Nordmark@Sun.COM  * Also, when a new DCE is created, the dce_generation number in the default
6911042SErik.Nordmark@Sun.COM  * DCE is bumped. That allows the dce_t information to be cached efficiently
7011042SErik.Nordmark@Sun.COM  * as long as the entity caching the dce_t also caches the dce_generation,
7111042SErik.Nordmark@Sun.COM  * and compares the cached generation to detect any changes.
7211042SErik.Nordmark@Sun.COM  * Furthermore, when a DCE is deleted, if there are any outstanding references
7311042SErik.Nordmark@Sun.COM  * to the DCE it will be marked as condemned. The condemned mark is
7411042SErik.Nordmark@Sun.COM  * a designated generation number which is never otherwise used, hence
7511042SErik.Nordmark@Sun.COM  * the single comparison with the generation number captures that as well.
7611042SErik.Nordmark@Sun.COM  *
7711042SErik.Nordmark@Sun.COM  * An example of code which caches is as follows:
7811042SErik.Nordmark@Sun.COM  *
7911042SErik.Nordmark@Sun.COM  *	if (mystruct->my_dce_generation != mystruct->my_dce->dce_generation) {
8011042SErik.Nordmark@Sun.COM  *		The DCE has changed
8111042SErik.Nordmark@Sun.COM  *		mystruct->my_dce = dce_lookup_pkt(mp, ixa,
8211042SErik.Nordmark@Sun.COM  *		    &mystruct->my_dce_generation);
8311042SErik.Nordmark@Sun.COM  *		Not needed in practice, since we have the default DCE:
8411042SErik.Nordmark@Sun.COM  *		if (DCE_IS_CONDEMNED(mystruct->my_dce))
8511042SErik.Nordmark@Sun.COM  *			return failure;
8611042SErik.Nordmark@Sun.COM  *	}
8711042SErik.Nordmark@Sun.COM  *
8811042SErik.Nordmark@Sun.COM  * Note that for IPv6 link-local addresses we record the ifindex since the
8911042SErik.Nordmark@Sun.COM  * link-locals are not globally unique.
9011042SErik.Nordmark@Sun.COM  */
9111042SErik.Nordmark@Sun.COM 
9211042SErik.Nordmark@Sun.COM /*
9311042SErik.Nordmark@Sun.COM  * Hash bucket structure for DCEs
9411042SErik.Nordmark@Sun.COM  */
9511042SErik.Nordmark@Sun.COM typedef struct dcb_s {
9611042SErik.Nordmark@Sun.COM 	krwlock_t	dcb_lock;
9711042SErik.Nordmark@Sun.COM 	uint32_t	dcb_cnt;
9811042SErik.Nordmark@Sun.COM 	dce_t		*dcb_dce;
9911042SErik.Nordmark@Sun.COM } dcb_t;
10011042SErik.Nordmark@Sun.COM 
10111042SErik.Nordmark@Sun.COM static void	dce_delete_locked(dcb_t *, dce_t *);
10211042SErik.Nordmark@Sun.COM static void	dce_make_condemned(dce_t *);
10311042SErik.Nordmark@Sun.COM 
10411042SErik.Nordmark@Sun.COM static kmem_cache_t *dce_cache;
10511042SErik.Nordmark@Sun.COM 
10611042SErik.Nordmark@Sun.COM 
10711042SErik.Nordmark@Sun.COM /* Operates on a uint64_t */
10811042SErik.Nordmark@Sun.COM #define	RANDOM_HASH(p) ((p) ^ ((p)>>16) ^ ((p)>>32) ^ ((p)>>48))
10911042SErik.Nordmark@Sun.COM 
11011042SErik.Nordmark@Sun.COM /*
11111042SErik.Nordmark@Sun.COM  * Reclaim a fraction of dce's in the dcb.
11211042SErik.Nordmark@Sun.COM  * For now we have a higher probability to delete DCEs without DCE_PMTU.
11311042SErik.Nordmark@Sun.COM  */
11411042SErik.Nordmark@Sun.COM static void
dcb_reclaim(dcb_t * dcb,ip_stack_t * ipst,uint_t fraction)11511042SErik.Nordmark@Sun.COM dcb_reclaim(dcb_t *dcb, ip_stack_t *ipst, uint_t fraction)
11611042SErik.Nordmark@Sun.COM {
11711042SErik.Nordmark@Sun.COM 	uint_t	fraction_pmtu = fraction*4;
11811042SErik.Nordmark@Sun.COM 	uint_t	hash;
11911042SErik.Nordmark@Sun.COM 	dce_t	*dce, *nextdce;
12011042SErik.Nordmark@Sun.COM 
12111042SErik.Nordmark@Sun.COM 	rw_enter(&dcb->dcb_lock, RW_WRITER);
12211042SErik.Nordmark@Sun.COM 	for (dce = dcb->dcb_dce; dce != NULL; dce = nextdce) {
12311042SErik.Nordmark@Sun.COM 		nextdce = dce->dce_next;
12411042SErik.Nordmark@Sun.COM 		/* Clear DCEF_PMTU if the pmtu is too old */
12511042SErik.Nordmark@Sun.COM 		mutex_enter(&dce->dce_lock);
12611042SErik.Nordmark@Sun.COM 		if ((dce->dce_flags & DCEF_PMTU) &&
12711066Srafael.vanoni@sun.com 		    TICK_TO_SEC(ddi_get_lbolt64()) - dce->dce_last_change_time >
12811042SErik.Nordmark@Sun.COM 		    ipst->ips_ip_pathmtu_interval) {
12911042SErik.Nordmark@Sun.COM 			dce->dce_flags &= ~DCEF_PMTU;
13011042SErik.Nordmark@Sun.COM 			mutex_exit(&dce->dce_lock);
13111042SErik.Nordmark@Sun.COM 			dce_increment_generation(dce);
13211042SErik.Nordmark@Sun.COM 		} else {
13311042SErik.Nordmark@Sun.COM 			mutex_exit(&dce->dce_lock);
13411042SErik.Nordmark@Sun.COM 		}
13511042SErik.Nordmark@Sun.COM 		hash = RANDOM_HASH((uint64_t)(uintptr_t)dce);
13611042SErik.Nordmark@Sun.COM 		if (dce->dce_flags & DCEF_PMTU) {
13711042SErik.Nordmark@Sun.COM 			if (hash % fraction_pmtu != 0)
13811042SErik.Nordmark@Sun.COM 				continue;
13911042SErik.Nordmark@Sun.COM 		} else {
14011042SErik.Nordmark@Sun.COM 			if (hash % fraction != 0)
14111042SErik.Nordmark@Sun.COM 				continue;
14211042SErik.Nordmark@Sun.COM 		}
14311042SErik.Nordmark@Sun.COM 
14411042SErik.Nordmark@Sun.COM 		IP_STAT(ipst, ip_dce_reclaim_deleted);
14511042SErik.Nordmark@Sun.COM 		dce_delete_locked(dcb, dce);
14611042SErik.Nordmark@Sun.COM 		dce_refrele(dce);
14711042SErik.Nordmark@Sun.COM 	}
14811042SErik.Nordmark@Sun.COM 	rw_exit(&dcb->dcb_lock);
14911042SErik.Nordmark@Sun.COM }
15011042SErik.Nordmark@Sun.COM 
15111042SErik.Nordmark@Sun.COM /*
15211042SErik.Nordmark@Sun.COM  * kmem_cache callback to free up memory.
15311042SErik.Nordmark@Sun.COM  *
15411042SErik.Nordmark@Sun.COM  */
15511042SErik.Nordmark@Sun.COM static void
ip_dce_reclaim_stack(ip_stack_t * ipst)15611042SErik.Nordmark@Sun.COM ip_dce_reclaim_stack(ip_stack_t *ipst)
15711042SErik.Nordmark@Sun.COM {
15811042SErik.Nordmark@Sun.COM 	int	i;
15911042SErik.Nordmark@Sun.COM 
16011042SErik.Nordmark@Sun.COM 	IP_STAT(ipst, ip_dce_reclaim_calls);
16111042SErik.Nordmark@Sun.COM 	for (i = 0; i < ipst->ips_dce_hashsize; i++) {
16211042SErik.Nordmark@Sun.COM 		dcb_reclaim(&ipst->ips_dce_hash_v4[i], ipst,
16311042SErik.Nordmark@Sun.COM 		    ipst->ips_ip_dce_reclaim_fraction);
16411042SErik.Nordmark@Sun.COM 
16511042SErik.Nordmark@Sun.COM 		dcb_reclaim(&ipst->ips_dce_hash_v6[i], ipst,
16611042SErik.Nordmark@Sun.COM 		    ipst->ips_ip_dce_reclaim_fraction);
16711042SErik.Nordmark@Sun.COM 	}
16811042SErik.Nordmark@Sun.COM 
16911042SErik.Nordmark@Sun.COM 	/*
17011042SErik.Nordmark@Sun.COM 	 * Walk all CONNs that can have a reference on an ire, nce or dce.
17111042SErik.Nordmark@Sun.COM 	 * Get them to update any stale references to drop any refholds they
17211042SErik.Nordmark@Sun.COM 	 * have.
17311042SErik.Nordmark@Sun.COM 	 */
17411042SErik.Nordmark@Sun.COM 	ipcl_walk(conn_ixa_cleanup, (void *)B_FALSE, ipst);
17511042SErik.Nordmark@Sun.COM }
17611042SErik.Nordmark@Sun.COM 
17711042SErik.Nordmark@Sun.COM /*
17811042SErik.Nordmark@Sun.COM  * Called by the memory allocator subsystem directly, when the system
17911042SErik.Nordmark@Sun.COM  * is running low on memory.
18011042SErik.Nordmark@Sun.COM  */
18111042SErik.Nordmark@Sun.COM /* ARGSUSED */
18211042SErik.Nordmark@Sun.COM void
ip_dce_reclaim(void * args)18311042SErik.Nordmark@Sun.COM ip_dce_reclaim(void *args)
18411042SErik.Nordmark@Sun.COM {
18511042SErik.Nordmark@Sun.COM 	netstack_handle_t nh;
18611042SErik.Nordmark@Sun.COM 	netstack_t *ns;
18711769SKacheong.Poon@Sun.COM 	ip_stack_t *ipst;
18811042SErik.Nordmark@Sun.COM 
18911042SErik.Nordmark@Sun.COM 	netstack_next_init(&nh);
19011042SErik.Nordmark@Sun.COM 	while ((ns = netstack_next(&nh)) != NULL) {
19111769SKacheong.Poon@Sun.COM 		/*
19211769SKacheong.Poon@Sun.COM 		 * netstack_next() can return a netstack_t with a NULL
19311769SKacheong.Poon@Sun.COM 		 * netstack_ip at boot time.
19411769SKacheong.Poon@Sun.COM 		 */
19511769SKacheong.Poon@Sun.COM 		if ((ipst = ns->netstack_ip) == NULL) {
19611769SKacheong.Poon@Sun.COM 			netstack_rele(ns);
19711769SKacheong.Poon@Sun.COM 			continue;
19811769SKacheong.Poon@Sun.COM 		}
19911769SKacheong.Poon@Sun.COM 		ip_dce_reclaim_stack(ipst);
20011042SErik.Nordmark@Sun.COM 		netstack_rele(ns);
20111042SErik.Nordmark@Sun.COM 	}
20211042SErik.Nordmark@Sun.COM 	netstack_next_fini(&nh);
20311042SErik.Nordmark@Sun.COM }
20411042SErik.Nordmark@Sun.COM 
20511042SErik.Nordmark@Sun.COM void
dce_g_init(void)20611042SErik.Nordmark@Sun.COM dce_g_init(void)
20711042SErik.Nordmark@Sun.COM {
20811042SErik.Nordmark@Sun.COM 	dce_cache = kmem_cache_create("dce_cache",
20911042SErik.Nordmark@Sun.COM 	    sizeof (dce_t), 0, NULL, NULL, ip_dce_reclaim, NULL, NULL, 0);
21011042SErik.Nordmark@Sun.COM }
21111042SErik.Nordmark@Sun.COM 
21211042SErik.Nordmark@Sun.COM void
dce_g_destroy(void)21311042SErik.Nordmark@Sun.COM dce_g_destroy(void)
21411042SErik.Nordmark@Sun.COM {
21511042SErik.Nordmark@Sun.COM 	kmem_cache_destroy(dce_cache);
21611042SErik.Nordmark@Sun.COM }
21711042SErik.Nordmark@Sun.COM 
21811042SErik.Nordmark@Sun.COM 
21911042SErik.Nordmark@Sun.COM /*
22011042SErik.Nordmark@Sun.COM  * Allocate a default DCE and a hash table for per-IP address DCEs
22111042SErik.Nordmark@Sun.COM  */
22211042SErik.Nordmark@Sun.COM void
dce_stack_init(ip_stack_t * ipst)22311042SErik.Nordmark@Sun.COM dce_stack_init(ip_stack_t *ipst)
22411042SErik.Nordmark@Sun.COM {
22511042SErik.Nordmark@Sun.COM 	int	i;
22611042SErik.Nordmark@Sun.COM 
22711042SErik.Nordmark@Sun.COM 	ipst->ips_dce_default = kmem_cache_alloc(dce_cache, KM_SLEEP);
22811042SErik.Nordmark@Sun.COM 	bzero(ipst->ips_dce_default, sizeof (dce_t));
22911042SErik.Nordmark@Sun.COM 	ipst->ips_dce_default->dce_flags = DCEF_DEFAULT;
23011042SErik.Nordmark@Sun.COM 	ipst->ips_dce_default->dce_generation = DCE_GENERATION_INITIAL;
23111066Srafael.vanoni@sun.com 	ipst->ips_dce_default->dce_last_change_time =
23211066Srafael.vanoni@sun.com 	    TICK_TO_SEC(ddi_get_lbolt64());
23311042SErik.Nordmark@Sun.COM 	ipst->ips_dce_default->dce_refcnt = 1;	/* Should never go away */
23411042SErik.Nordmark@Sun.COM 	ipst->ips_dce_default->dce_ipst = ipst;
23511042SErik.Nordmark@Sun.COM 
23611042SErik.Nordmark@Sun.COM 	/* This must be a power of two since we are using IRE_ADDR_HASH macro */
23711042SErik.Nordmark@Sun.COM 	ipst->ips_dce_hashsize = 256;
23811042SErik.Nordmark@Sun.COM 	ipst->ips_dce_hash_v4 = kmem_zalloc(ipst->ips_dce_hashsize *
23911042SErik.Nordmark@Sun.COM 	    sizeof (dcb_t), KM_SLEEP);
24011042SErik.Nordmark@Sun.COM 	ipst->ips_dce_hash_v6 = kmem_zalloc(ipst->ips_dce_hashsize *
24111042SErik.Nordmark@Sun.COM 	    sizeof (dcb_t), KM_SLEEP);
24211042SErik.Nordmark@Sun.COM 	for (i = 0; i < ipst->ips_dce_hashsize; i++) {
24311042SErik.Nordmark@Sun.COM 		rw_init(&ipst->ips_dce_hash_v4[i].dcb_lock, NULL, RW_DEFAULT,
24411042SErik.Nordmark@Sun.COM 		    NULL);
24511042SErik.Nordmark@Sun.COM 		rw_init(&ipst->ips_dce_hash_v6[i].dcb_lock, NULL, RW_DEFAULT,
24611042SErik.Nordmark@Sun.COM 		    NULL);
24711042SErik.Nordmark@Sun.COM 	}
24811042SErik.Nordmark@Sun.COM }
24911042SErik.Nordmark@Sun.COM 
25011042SErik.Nordmark@Sun.COM void
dce_stack_destroy(ip_stack_t * ipst)25111042SErik.Nordmark@Sun.COM dce_stack_destroy(ip_stack_t *ipst)
25211042SErik.Nordmark@Sun.COM {
25311042SErik.Nordmark@Sun.COM 	int i;
25411042SErik.Nordmark@Sun.COM 	for (i = 0; i < ipst->ips_dce_hashsize; i++) {
25511042SErik.Nordmark@Sun.COM 		rw_destroy(&ipst->ips_dce_hash_v4[i].dcb_lock);
25611042SErik.Nordmark@Sun.COM 		rw_destroy(&ipst->ips_dce_hash_v6[i].dcb_lock);
25711042SErik.Nordmark@Sun.COM 	}
25811042SErik.Nordmark@Sun.COM 	kmem_free(ipst->ips_dce_hash_v4,
25911042SErik.Nordmark@Sun.COM 	    ipst->ips_dce_hashsize * sizeof (dcb_t));
26011042SErik.Nordmark@Sun.COM 	ipst->ips_dce_hash_v4 = NULL;
26111042SErik.Nordmark@Sun.COM 	kmem_free(ipst->ips_dce_hash_v6,
26211042SErik.Nordmark@Sun.COM 	    ipst->ips_dce_hashsize * sizeof (dcb_t));
26311042SErik.Nordmark@Sun.COM 	ipst->ips_dce_hash_v6 = NULL;
26411042SErik.Nordmark@Sun.COM 	ipst->ips_dce_hashsize = 0;
26511042SErik.Nordmark@Sun.COM 
26611042SErik.Nordmark@Sun.COM 	ASSERT(ipst->ips_dce_default->dce_refcnt == 1);
26711042SErik.Nordmark@Sun.COM 	kmem_cache_free(dce_cache, ipst->ips_dce_default);
26811042SErik.Nordmark@Sun.COM 	ipst->ips_dce_default = NULL;
26911042SErik.Nordmark@Sun.COM }
27011042SErik.Nordmark@Sun.COM 
27111042SErik.Nordmark@Sun.COM /* When any DCE is good enough */
27211042SErik.Nordmark@Sun.COM dce_t *
dce_get_default(ip_stack_t * ipst)27311042SErik.Nordmark@Sun.COM dce_get_default(ip_stack_t *ipst)
27411042SErik.Nordmark@Sun.COM {
27511042SErik.Nordmark@Sun.COM 	dce_t		*dce;
27611042SErik.Nordmark@Sun.COM 
27711042SErik.Nordmark@Sun.COM 	dce = ipst->ips_dce_default;
27811042SErik.Nordmark@Sun.COM 	dce_refhold(dce);
27911042SErik.Nordmark@Sun.COM 	return (dce);
28011042SErik.Nordmark@Sun.COM }
28111042SErik.Nordmark@Sun.COM 
28211042SErik.Nordmark@Sun.COM /*
28311042SErik.Nordmark@Sun.COM  * Generic for IPv4 and IPv6.
28411042SErik.Nordmark@Sun.COM  *
28511042SErik.Nordmark@Sun.COM  * Used by callers that need to cache e.g., the datapath
28611042SErik.Nordmark@Sun.COM  * Returns the generation number in the last argument.
28711042SErik.Nordmark@Sun.COM  */
28811042SErik.Nordmark@Sun.COM dce_t *
dce_lookup_pkt(mblk_t * mp,ip_xmit_attr_t * ixa,uint_t * generationp)28911042SErik.Nordmark@Sun.COM dce_lookup_pkt(mblk_t *mp, ip_xmit_attr_t *ixa, uint_t *generationp)
29011042SErik.Nordmark@Sun.COM {
29111042SErik.Nordmark@Sun.COM 	if (ixa->ixa_flags & IXAF_IS_IPV4) {
29211042SErik.Nordmark@Sun.COM 		/*
29311042SErik.Nordmark@Sun.COM 		 * If we have a source route we need to look for the final
29411042SErik.Nordmark@Sun.COM 		 * destination in the source route option.
29511042SErik.Nordmark@Sun.COM 		 */
29611042SErik.Nordmark@Sun.COM 		ipaddr_t final_dst;
29711042SErik.Nordmark@Sun.COM 		ipha_t *ipha = (ipha_t *)mp->b_rptr;
29811042SErik.Nordmark@Sun.COM 
29911042SErik.Nordmark@Sun.COM 		final_dst = ip_get_dst(ipha);
30011042SErik.Nordmark@Sun.COM 		return (dce_lookup_v4(final_dst, ixa->ixa_ipst, generationp));
30111042SErik.Nordmark@Sun.COM 	} else {
30211042SErik.Nordmark@Sun.COM 		uint_t ifindex;
30311042SErik.Nordmark@Sun.COM 		/*
30411042SErik.Nordmark@Sun.COM 		 * If we have a routing header we need to look for the final
30511042SErik.Nordmark@Sun.COM 		 * destination in the routing extension header.
30611042SErik.Nordmark@Sun.COM 		 */
30711042SErik.Nordmark@Sun.COM 		in6_addr_t final_dst;
30811042SErik.Nordmark@Sun.COM 		ip6_t *ip6h = (ip6_t *)mp->b_rptr;
30911042SErik.Nordmark@Sun.COM 
31011042SErik.Nordmark@Sun.COM 		final_dst = ip_get_dst_v6(ip6h, mp, NULL);
31111042SErik.Nordmark@Sun.COM 		ifindex = 0;
31211042SErik.Nordmark@Sun.COM 		if (IN6_IS_ADDR_LINKSCOPE(&final_dst) && ixa->ixa_nce != NULL) {
31311042SErik.Nordmark@Sun.COM 			ifindex = ixa->ixa_nce->nce_common->ncec_ill->
31411042SErik.Nordmark@Sun.COM 			    ill_phyint->phyint_ifindex;
31511042SErik.Nordmark@Sun.COM 		}
31611042SErik.Nordmark@Sun.COM 		return (dce_lookup_v6(&final_dst, ifindex, ixa->ixa_ipst,
31711042SErik.Nordmark@Sun.COM 		    generationp));
31811042SErik.Nordmark@Sun.COM 	}
31911042SErik.Nordmark@Sun.COM }
32011042SErik.Nordmark@Sun.COM 
32111042SErik.Nordmark@Sun.COM /*
32211042SErik.Nordmark@Sun.COM  * Used by callers that need to cache e.g., the datapath
32311042SErik.Nordmark@Sun.COM  * Returns the generation number in the last argument.
32411042SErik.Nordmark@Sun.COM  */
32511042SErik.Nordmark@Sun.COM dce_t *
dce_lookup_v4(ipaddr_t dst,ip_stack_t * ipst,uint_t * generationp)32611042SErik.Nordmark@Sun.COM dce_lookup_v4(ipaddr_t dst, ip_stack_t *ipst, uint_t *generationp)
32711042SErik.Nordmark@Sun.COM {
32811042SErik.Nordmark@Sun.COM 	uint_t		hash;
32911042SErik.Nordmark@Sun.COM 	dcb_t		*dcb;
33011042SErik.Nordmark@Sun.COM 	dce_t		*dce;
33111042SErik.Nordmark@Sun.COM 
33211042SErik.Nordmark@Sun.COM 	/* Set *generationp before dropping the lock(s) that allow additions */
33311042SErik.Nordmark@Sun.COM 	if (generationp != NULL)
33411042SErik.Nordmark@Sun.COM 		*generationp = ipst->ips_dce_default->dce_generation;
33511042SErik.Nordmark@Sun.COM 
33611042SErik.Nordmark@Sun.COM 	hash = IRE_ADDR_HASH(dst, ipst->ips_dce_hashsize);
33711042SErik.Nordmark@Sun.COM 	dcb = &ipst->ips_dce_hash_v4[hash];
33811042SErik.Nordmark@Sun.COM 	rw_enter(&dcb->dcb_lock, RW_READER);
33911042SErik.Nordmark@Sun.COM 	for (dce = dcb->dcb_dce; dce != NULL; dce = dce->dce_next) {
34011042SErik.Nordmark@Sun.COM 		if (dce->dce_v4addr == dst) {
34111042SErik.Nordmark@Sun.COM 			mutex_enter(&dce->dce_lock);
34211042SErik.Nordmark@Sun.COM 			if (!DCE_IS_CONDEMNED(dce)) {
34311042SErik.Nordmark@Sun.COM 				dce_refhold(dce);
34411042SErik.Nordmark@Sun.COM 				if (generationp != NULL)
34511042SErik.Nordmark@Sun.COM 					*generationp = dce->dce_generation;
34611042SErik.Nordmark@Sun.COM 				mutex_exit(&dce->dce_lock);
34711042SErik.Nordmark@Sun.COM 				rw_exit(&dcb->dcb_lock);
34811042SErik.Nordmark@Sun.COM 				return (dce);
34911042SErik.Nordmark@Sun.COM 			}
35011042SErik.Nordmark@Sun.COM 			mutex_exit(&dce->dce_lock);
35111042SErik.Nordmark@Sun.COM 		}
35211042SErik.Nordmark@Sun.COM 	}
35311042SErik.Nordmark@Sun.COM 	rw_exit(&dcb->dcb_lock);
35411042SErik.Nordmark@Sun.COM 	/* Not found */
35511042SErik.Nordmark@Sun.COM 	dce = ipst->ips_dce_default;
35611042SErik.Nordmark@Sun.COM 	dce_refhold(dce);
35711042SErik.Nordmark@Sun.COM 	return (dce);
35811042SErik.Nordmark@Sun.COM }
35911042SErik.Nordmark@Sun.COM 
36011042SErik.Nordmark@Sun.COM /*
36111042SErik.Nordmark@Sun.COM  * Used by callers that need to cache e.g., the datapath
36211042SErik.Nordmark@Sun.COM  * Returns the generation number in the last argument.
36311042SErik.Nordmark@Sun.COM  * ifindex should only be set for link-locals
36411042SErik.Nordmark@Sun.COM  */
36511042SErik.Nordmark@Sun.COM dce_t *
dce_lookup_v6(const in6_addr_t * dst,uint_t ifindex,ip_stack_t * ipst,uint_t * generationp)36611042SErik.Nordmark@Sun.COM dce_lookup_v6(const in6_addr_t *dst, uint_t ifindex, ip_stack_t *ipst,
36711042SErik.Nordmark@Sun.COM     uint_t *generationp)
36811042SErik.Nordmark@Sun.COM {
36911042SErik.Nordmark@Sun.COM 	uint_t		hash;
37011042SErik.Nordmark@Sun.COM 	dcb_t		*dcb;
37111042SErik.Nordmark@Sun.COM 	dce_t		*dce;
37211042SErik.Nordmark@Sun.COM 
37311042SErik.Nordmark@Sun.COM 	/* Set *generationp before dropping the lock(s) that allow additions */
37411042SErik.Nordmark@Sun.COM 	if (generationp != NULL)
37511042SErik.Nordmark@Sun.COM 		*generationp = ipst->ips_dce_default->dce_generation;
37611042SErik.Nordmark@Sun.COM 
37711042SErik.Nordmark@Sun.COM 	hash = IRE_ADDR_HASH_V6(*dst, ipst->ips_dce_hashsize);
37811042SErik.Nordmark@Sun.COM 	dcb = &ipst->ips_dce_hash_v6[hash];
37911042SErik.Nordmark@Sun.COM 	rw_enter(&dcb->dcb_lock, RW_READER);
38011042SErik.Nordmark@Sun.COM 	for (dce = dcb->dcb_dce; dce != NULL; dce = dce->dce_next) {
38111042SErik.Nordmark@Sun.COM 		if (IN6_ARE_ADDR_EQUAL(&dce->dce_v6addr, dst) &&
38211042SErik.Nordmark@Sun.COM 		    dce->dce_ifindex == ifindex) {
38311042SErik.Nordmark@Sun.COM 			mutex_enter(&dce->dce_lock);
38411042SErik.Nordmark@Sun.COM 			if (!DCE_IS_CONDEMNED(dce)) {
38511042SErik.Nordmark@Sun.COM 				dce_refhold(dce);
38611042SErik.Nordmark@Sun.COM 				if (generationp != NULL)
38711042SErik.Nordmark@Sun.COM 					*generationp = dce->dce_generation;
38811042SErik.Nordmark@Sun.COM 				mutex_exit(&dce->dce_lock);
38911042SErik.Nordmark@Sun.COM 				rw_exit(&dcb->dcb_lock);
39011042SErik.Nordmark@Sun.COM 				return (dce);
39111042SErik.Nordmark@Sun.COM 			}
39211042SErik.Nordmark@Sun.COM 			mutex_exit(&dce->dce_lock);
39311042SErik.Nordmark@Sun.COM 		}
39411042SErik.Nordmark@Sun.COM 	}
39511042SErik.Nordmark@Sun.COM 	rw_exit(&dcb->dcb_lock);
39611042SErik.Nordmark@Sun.COM 	/* Not found */
39711042SErik.Nordmark@Sun.COM 	dce = ipst->ips_dce_default;
39811042SErik.Nordmark@Sun.COM 	dce_refhold(dce);
39911042SErik.Nordmark@Sun.COM 	return (dce);
40011042SErik.Nordmark@Sun.COM }
40111042SErik.Nordmark@Sun.COM 
40211042SErik.Nordmark@Sun.COM /*
40311042SErik.Nordmark@Sun.COM  * Atomically looks for a non-default DCE, and if not found tries to create one.
40411042SErik.Nordmark@Sun.COM  * If there is no memory it returns NULL.
40511042SErik.Nordmark@Sun.COM  * When an entry is created we increase the generation number on
40611042SErik.Nordmark@Sun.COM  * the default DCE so that conn_ip_output will detect there is a new DCE.
40711042SErik.Nordmark@Sun.COM  */
40811042SErik.Nordmark@Sun.COM dce_t *
dce_lookup_and_add_v4(ipaddr_t dst,ip_stack_t * ipst)40911042SErik.Nordmark@Sun.COM dce_lookup_and_add_v4(ipaddr_t dst, ip_stack_t *ipst)
41011042SErik.Nordmark@Sun.COM {
41111042SErik.Nordmark@Sun.COM 	uint_t		hash;
41211042SErik.Nordmark@Sun.COM 	dcb_t		*dcb;
41311042SErik.Nordmark@Sun.COM 	dce_t		*dce;
41411042SErik.Nordmark@Sun.COM 
41511042SErik.Nordmark@Sun.COM 	hash = IRE_ADDR_HASH(dst, ipst->ips_dce_hashsize);
41611042SErik.Nordmark@Sun.COM 	dcb = &ipst->ips_dce_hash_v4[hash];
41711042SErik.Nordmark@Sun.COM 	rw_enter(&dcb->dcb_lock, RW_WRITER);
41811042SErik.Nordmark@Sun.COM 	for (dce = dcb->dcb_dce; dce != NULL; dce = dce->dce_next) {
41911042SErik.Nordmark@Sun.COM 		if (dce->dce_v4addr == dst) {
42011042SErik.Nordmark@Sun.COM 			mutex_enter(&dce->dce_lock);
42111042SErik.Nordmark@Sun.COM 			if (!DCE_IS_CONDEMNED(dce)) {
42211042SErik.Nordmark@Sun.COM 				dce_refhold(dce);
42311042SErik.Nordmark@Sun.COM 				mutex_exit(&dce->dce_lock);
42411042SErik.Nordmark@Sun.COM 				rw_exit(&dcb->dcb_lock);
42511042SErik.Nordmark@Sun.COM 				return (dce);
42611042SErik.Nordmark@Sun.COM 			}
42711042SErik.Nordmark@Sun.COM 			mutex_exit(&dce->dce_lock);
42811042SErik.Nordmark@Sun.COM 		}
42911042SErik.Nordmark@Sun.COM 	}
43011042SErik.Nordmark@Sun.COM 	dce = kmem_cache_alloc(dce_cache, KM_NOSLEEP);
43111042SErik.Nordmark@Sun.COM 	if (dce == NULL) {
43211042SErik.Nordmark@Sun.COM 		rw_exit(&dcb->dcb_lock);
43311042SErik.Nordmark@Sun.COM 		return (NULL);
43411042SErik.Nordmark@Sun.COM 	}
43511042SErik.Nordmark@Sun.COM 	bzero(dce, sizeof (dce_t));
43611042SErik.Nordmark@Sun.COM 	dce->dce_ipst = ipst;	/* No netstack_hold */
43711042SErik.Nordmark@Sun.COM 	dce->dce_v4addr = dst;
43811042SErik.Nordmark@Sun.COM 	dce->dce_generation = DCE_GENERATION_INITIAL;
43911042SErik.Nordmark@Sun.COM 	dce->dce_ipversion = IPV4_VERSION;
44011066Srafael.vanoni@sun.com 	dce->dce_last_change_time = TICK_TO_SEC(ddi_get_lbolt64());
44111042SErik.Nordmark@Sun.COM 	dce_refhold(dce);	/* For the hash list */
44211042SErik.Nordmark@Sun.COM 
44311042SErik.Nordmark@Sun.COM 	/* Link into list */
44411042SErik.Nordmark@Sun.COM 	if (dcb->dcb_dce != NULL)
44511042SErik.Nordmark@Sun.COM 		dcb->dcb_dce->dce_ptpn = &dce->dce_next;
44611042SErik.Nordmark@Sun.COM 	dce->dce_next = dcb->dcb_dce;
44711042SErik.Nordmark@Sun.COM 	dce->dce_ptpn = &dcb->dcb_dce;
44811042SErik.Nordmark@Sun.COM 	dcb->dcb_dce = dce;
44911042SErik.Nordmark@Sun.COM 	dce->dce_bucket = dcb;
45011042SErik.Nordmark@Sun.COM 	dce_refhold(dce);	/* For the caller */
45111042SErik.Nordmark@Sun.COM 	rw_exit(&dcb->dcb_lock);
45211042SErik.Nordmark@Sun.COM 
45311042SErik.Nordmark@Sun.COM 	/* Initialize dce_ident to be different than for the last packet */
45411042SErik.Nordmark@Sun.COM 	dce->dce_ident = ipst->ips_dce_default->dce_ident + 1;
45511042SErik.Nordmark@Sun.COM 
45611042SErik.Nordmark@Sun.COM 	dce_increment_generation(ipst->ips_dce_default);
45711042SErik.Nordmark@Sun.COM 	return (dce);
45811042SErik.Nordmark@Sun.COM }
45911042SErik.Nordmark@Sun.COM 
46011042SErik.Nordmark@Sun.COM /*
46111042SErik.Nordmark@Sun.COM  * Atomically looks for a non-default DCE, and if not found tries to create one.
46211042SErik.Nordmark@Sun.COM  * If there is no memory it returns NULL.
46311042SErik.Nordmark@Sun.COM  * When an entry is created we increase the generation number on
46411042SErik.Nordmark@Sun.COM  * the default DCE so that conn_ip_output will detect there is a new DCE.
46511042SErik.Nordmark@Sun.COM  * ifindex should only be used with link-local addresses.
46611042SErik.Nordmark@Sun.COM  */
46711042SErik.Nordmark@Sun.COM dce_t *
dce_lookup_and_add_v6(const in6_addr_t * dst,uint_t ifindex,ip_stack_t * ipst)46811042SErik.Nordmark@Sun.COM dce_lookup_and_add_v6(const in6_addr_t *dst, uint_t ifindex, ip_stack_t *ipst)
46911042SErik.Nordmark@Sun.COM {
47011042SErik.Nordmark@Sun.COM 	uint_t		hash;
47111042SErik.Nordmark@Sun.COM 	dcb_t		*dcb;
47211042SErik.Nordmark@Sun.COM 	dce_t		*dce;
47311042SErik.Nordmark@Sun.COM 
47411042SErik.Nordmark@Sun.COM 	/* We should not create entries for link-locals w/o an ifindex */
47511042SErik.Nordmark@Sun.COM 	ASSERT(!(IN6_IS_ADDR_LINKSCOPE(dst)) || ifindex != 0);
47611042SErik.Nordmark@Sun.COM 
47711042SErik.Nordmark@Sun.COM 	hash = IRE_ADDR_HASH_V6(*dst, ipst->ips_dce_hashsize);
47811042SErik.Nordmark@Sun.COM 	dcb = &ipst->ips_dce_hash_v6[hash];
47911042SErik.Nordmark@Sun.COM 	rw_enter(&dcb->dcb_lock, RW_WRITER);
48011042SErik.Nordmark@Sun.COM 	for (dce = dcb->dcb_dce; dce != NULL; dce = dce->dce_next) {
48111042SErik.Nordmark@Sun.COM 		if (IN6_ARE_ADDR_EQUAL(&dce->dce_v6addr, dst) &&
48211042SErik.Nordmark@Sun.COM 		    dce->dce_ifindex == ifindex) {
48311042SErik.Nordmark@Sun.COM 			mutex_enter(&dce->dce_lock);
48411042SErik.Nordmark@Sun.COM 			if (!DCE_IS_CONDEMNED(dce)) {
48511042SErik.Nordmark@Sun.COM 				dce_refhold(dce);
48611042SErik.Nordmark@Sun.COM 				mutex_exit(&dce->dce_lock);
48711042SErik.Nordmark@Sun.COM 				rw_exit(&dcb->dcb_lock);
48811042SErik.Nordmark@Sun.COM 				return (dce);
48911042SErik.Nordmark@Sun.COM 			}
49011042SErik.Nordmark@Sun.COM 			mutex_exit(&dce->dce_lock);
49111042SErik.Nordmark@Sun.COM 		}
49211042SErik.Nordmark@Sun.COM 	}
49311042SErik.Nordmark@Sun.COM 
49411042SErik.Nordmark@Sun.COM 	dce = kmem_cache_alloc(dce_cache, KM_NOSLEEP);
49511042SErik.Nordmark@Sun.COM 	if (dce == NULL) {
49611042SErik.Nordmark@Sun.COM 		rw_exit(&dcb->dcb_lock);
49711042SErik.Nordmark@Sun.COM 		return (NULL);
49811042SErik.Nordmark@Sun.COM 	}
49911042SErik.Nordmark@Sun.COM 	bzero(dce, sizeof (dce_t));
50011042SErik.Nordmark@Sun.COM 	dce->dce_ipst = ipst;	/* No netstack_hold */
50111042SErik.Nordmark@Sun.COM 	dce->dce_v6addr = *dst;
50211042SErik.Nordmark@Sun.COM 	dce->dce_ifindex = ifindex;
50311042SErik.Nordmark@Sun.COM 	dce->dce_generation = DCE_GENERATION_INITIAL;
50411042SErik.Nordmark@Sun.COM 	dce->dce_ipversion = IPV6_VERSION;
50511066Srafael.vanoni@sun.com 	dce->dce_last_change_time = TICK_TO_SEC(ddi_get_lbolt64());
50611042SErik.Nordmark@Sun.COM 	dce_refhold(dce);	/* For the hash list */
50711042SErik.Nordmark@Sun.COM 
50811042SErik.Nordmark@Sun.COM 	/* Link into list */
50911042SErik.Nordmark@Sun.COM 	if (dcb->dcb_dce != NULL)
51011042SErik.Nordmark@Sun.COM 		dcb->dcb_dce->dce_ptpn = &dce->dce_next;
51111042SErik.Nordmark@Sun.COM 	dce->dce_next = dcb->dcb_dce;
51211042SErik.Nordmark@Sun.COM 	dce->dce_ptpn = &dcb->dcb_dce;
51311042SErik.Nordmark@Sun.COM 	dcb->dcb_dce = dce;
51411042SErik.Nordmark@Sun.COM 	dce->dce_bucket = dcb;
51511042SErik.Nordmark@Sun.COM 	atomic_add_32(&dcb->dcb_cnt, 1);
51611042SErik.Nordmark@Sun.COM 	dce_refhold(dce);	/* For the caller */
51711042SErik.Nordmark@Sun.COM 	rw_exit(&dcb->dcb_lock);
51811042SErik.Nordmark@Sun.COM 
51911042SErik.Nordmark@Sun.COM 	/* Initialize dce_ident to be different than for the last packet */
52011042SErik.Nordmark@Sun.COM 	dce->dce_ident = ipst->ips_dce_default->dce_ident + 1;
52111042SErik.Nordmark@Sun.COM 	dce_increment_generation(ipst->ips_dce_default);
52211042SErik.Nordmark@Sun.COM 	return (dce);
52311042SErik.Nordmark@Sun.COM }
52411042SErik.Nordmark@Sun.COM 
52511042SErik.Nordmark@Sun.COM /*
52611042SErik.Nordmark@Sun.COM  * Set/update uinfo. Creates a per-destination dce if none exists.
52711042SErik.Nordmark@Sun.COM  *
52811042SErik.Nordmark@Sun.COM  * Note that we do not bump the generation number here.
52911042SErik.Nordmark@Sun.COM  * New connections will find the new uinfo.
53011042SErik.Nordmark@Sun.COM  *
53111042SErik.Nordmark@Sun.COM  * The only use of this (tcp, sctp using iulp_t) is to set rtt+rtt_sd.
53211042SErik.Nordmark@Sun.COM  */
53311042SErik.Nordmark@Sun.COM static void
dce_setuinfo(dce_t * dce,iulp_t * uinfo)53411042SErik.Nordmark@Sun.COM dce_setuinfo(dce_t *dce, iulp_t *uinfo)
53511042SErik.Nordmark@Sun.COM {
53611042SErik.Nordmark@Sun.COM 	/*
53711042SErik.Nordmark@Sun.COM 	 * Update the round trip time estimate and/or the max frag size
53811042SErik.Nordmark@Sun.COM 	 * and/or the slow start threshold.
53911042SErik.Nordmark@Sun.COM 	 *
54011042SErik.Nordmark@Sun.COM 	 * We serialize multiple advises using dce_lock.
54111042SErik.Nordmark@Sun.COM 	 */
54211042SErik.Nordmark@Sun.COM 	mutex_enter(&dce->dce_lock);
54311042SErik.Nordmark@Sun.COM 	/* Gard against setting to zero */
54411042SErik.Nordmark@Sun.COM 	if (uinfo->iulp_rtt != 0) {
54511042SErik.Nordmark@Sun.COM 		/*
54611042SErik.Nordmark@Sun.COM 		 * If there is no old cached values, initialize them
54711042SErik.Nordmark@Sun.COM 		 * conservatively.  Set them to be (1.5 * new value).
54811042SErik.Nordmark@Sun.COM 		 */
54911042SErik.Nordmark@Sun.COM 		if (dce->dce_uinfo.iulp_rtt != 0) {
55011042SErik.Nordmark@Sun.COM 			dce->dce_uinfo.iulp_rtt = (dce->dce_uinfo.iulp_rtt +
55111042SErik.Nordmark@Sun.COM 			    uinfo->iulp_rtt) >> 1;
55211042SErik.Nordmark@Sun.COM 		} else {
55311042SErik.Nordmark@Sun.COM 			dce->dce_uinfo.iulp_rtt = uinfo->iulp_rtt +
55411042SErik.Nordmark@Sun.COM 			    (uinfo->iulp_rtt >> 1);
55511042SErik.Nordmark@Sun.COM 		}
55611042SErik.Nordmark@Sun.COM 		if (dce->dce_uinfo.iulp_rtt_sd != 0) {
55711042SErik.Nordmark@Sun.COM 			dce->dce_uinfo.iulp_rtt_sd =
55811042SErik.Nordmark@Sun.COM 			    (dce->dce_uinfo.iulp_rtt_sd +
55911042SErik.Nordmark@Sun.COM 			    uinfo->iulp_rtt_sd) >> 1;
56011042SErik.Nordmark@Sun.COM 		} else {
56111042SErik.Nordmark@Sun.COM 			dce->dce_uinfo.iulp_rtt_sd = uinfo->iulp_rtt_sd +
56211042SErik.Nordmark@Sun.COM 			    (uinfo->iulp_rtt_sd >> 1);
56311042SErik.Nordmark@Sun.COM 		}
56411042SErik.Nordmark@Sun.COM 	}
56511042SErik.Nordmark@Sun.COM 	if (uinfo->iulp_mtu != 0) {
56611042SErik.Nordmark@Sun.COM 		if (dce->dce_flags & DCEF_PMTU) {
56711042SErik.Nordmark@Sun.COM 			dce->dce_pmtu = MIN(uinfo->iulp_mtu, dce->dce_pmtu);
56811042SErik.Nordmark@Sun.COM 		} else {
56911042SErik.Nordmark@Sun.COM 			dce->dce_pmtu = MIN(uinfo->iulp_mtu, IP_MAXPACKET);
57011042SErik.Nordmark@Sun.COM 			dce->dce_flags |= DCEF_PMTU;
57111042SErik.Nordmark@Sun.COM 		}
57211066Srafael.vanoni@sun.com 		dce->dce_last_change_time = TICK_TO_SEC(ddi_get_lbolt64());
57311042SErik.Nordmark@Sun.COM 	}
57411042SErik.Nordmark@Sun.COM 	if (uinfo->iulp_ssthresh != 0) {
57511042SErik.Nordmark@Sun.COM 		if (dce->dce_uinfo.iulp_ssthresh != 0)
57611042SErik.Nordmark@Sun.COM 			dce->dce_uinfo.iulp_ssthresh =
57711042SErik.Nordmark@Sun.COM 			    (uinfo->iulp_ssthresh +
57811042SErik.Nordmark@Sun.COM 			    dce->dce_uinfo.iulp_ssthresh) >> 1;
57911042SErik.Nordmark@Sun.COM 		else
58011042SErik.Nordmark@Sun.COM 			dce->dce_uinfo.iulp_ssthresh = uinfo->iulp_ssthresh;
58111042SErik.Nordmark@Sun.COM 	}
58211042SErik.Nordmark@Sun.COM 	/* We have uinfo for sure */
58311042SErik.Nordmark@Sun.COM 	dce->dce_flags |= DCEF_UINFO;
58411042SErik.Nordmark@Sun.COM 	mutex_exit(&dce->dce_lock);
58511042SErik.Nordmark@Sun.COM }
58611042SErik.Nordmark@Sun.COM 
58711042SErik.Nordmark@Sun.COM 
58811042SErik.Nordmark@Sun.COM int
dce_update_uinfo_v4(ipaddr_t dst,iulp_t * uinfo,ip_stack_t * ipst)58911042SErik.Nordmark@Sun.COM dce_update_uinfo_v4(ipaddr_t dst, iulp_t *uinfo, ip_stack_t *ipst)
59011042SErik.Nordmark@Sun.COM {
59111042SErik.Nordmark@Sun.COM 	dce_t *dce;
59211042SErik.Nordmark@Sun.COM 
59311042SErik.Nordmark@Sun.COM 	dce = dce_lookup_and_add_v4(dst, ipst);
59411042SErik.Nordmark@Sun.COM 	if (dce == NULL)
59511042SErik.Nordmark@Sun.COM 		return (ENOMEM);
59611042SErik.Nordmark@Sun.COM 
59711042SErik.Nordmark@Sun.COM 	dce_setuinfo(dce, uinfo);
59811042SErik.Nordmark@Sun.COM 	dce_refrele(dce);
59911042SErik.Nordmark@Sun.COM 	return (0);
60011042SErik.Nordmark@Sun.COM }
60111042SErik.Nordmark@Sun.COM 
60211042SErik.Nordmark@Sun.COM int
dce_update_uinfo_v6(const in6_addr_t * dst,uint_t ifindex,iulp_t * uinfo,ip_stack_t * ipst)60311042SErik.Nordmark@Sun.COM dce_update_uinfo_v6(const in6_addr_t *dst, uint_t ifindex, iulp_t *uinfo,
60411042SErik.Nordmark@Sun.COM     ip_stack_t *ipst)
60511042SErik.Nordmark@Sun.COM {
60611042SErik.Nordmark@Sun.COM 	dce_t *dce;
60711042SErik.Nordmark@Sun.COM 
60811042SErik.Nordmark@Sun.COM 	dce = dce_lookup_and_add_v6(dst, ifindex, ipst);
60911042SErik.Nordmark@Sun.COM 	if (dce == NULL)
61011042SErik.Nordmark@Sun.COM 		return (ENOMEM);
61111042SErik.Nordmark@Sun.COM 
61211042SErik.Nordmark@Sun.COM 	dce_setuinfo(dce, uinfo);
61311042SErik.Nordmark@Sun.COM 	dce_refrele(dce);
61411042SErik.Nordmark@Sun.COM 	return (0);
61511042SErik.Nordmark@Sun.COM }
61611042SErik.Nordmark@Sun.COM 
61711042SErik.Nordmark@Sun.COM /* Common routine for IPv4 and IPv6 */
61811042SErik.Nordmark@Sun.COM int
dce_update_uinfo(const in6_addr_t * dst,uint_t ifindex,iulp_t * uinfo,ip_stack_t * ipst)61911042SErik.Nordmark@Sun.COM dce_update_uinfo(const in6_addr_t *dst, uint_t ifindex, iulp_t *uinfo,
62011042SErik.Nordmark@Sun.COM     ip_stack_t *ipst)
62111042SErik.Nordmark@Sun.COM {
62211042SErik.Nordmark@Sun.COM 	ipaddr_t dst4;
62311042SErik.Nordmark@Sun.COM 
62411042SErik.Nordmark@Sun.COM 	if (IN6_IS_ADDR_V4MAPPED_ANY(dst)) {
62511042SErik.Nordmark@Sun.COM 		IN6_V4MAPPED_TO_IPADDR(dst, dst4);
62611042SErik.Nordmark@Sun.COM 		return (dce_update_uinfo_v4(dst4, uinfo, ipst));
62711042SErik.Nordmark@Sun.COM 	} else {
62811042SErik.Nordmark@Sun.COM 		return (dce_update_uinfo_v6(dst, ifindex, uinfo, ipst));
62911042SErik.Nordmark@Sun.COM 	}
63011042SErik.Nordmark@Sun.COM }
63111042SErik.Nordmark@Sun.COM 
63211042SErik.Nordmark@Sun.COM static void
dce_make_condemned(dce_t * dce)63311042SErik.Nordmark@Sun.COM dce_make_condemned(dce_t *dce)
63411042SErik.Nordmark@Sun.COM {
63511042SErik.Nordmark@Sun.COM 	ip_stack_t	*ipst = dce->dce_ipst;
63611042SErik.Nordmark@Sun.COM 
63711042SErik.Nordmark@Sun.COM 	mutex_enter(&dce->dce_lock);
63811042SErik.Nordmark@Sun.COM 	ASSERT(!DCE_IS_CONDEMNED(dce));
63911042SErik.Nordmark@Sun.COM 	dce->dce_generation = DCE_GENERATION_CONDEMNED;
64011042SErik.Nordmark@Sun.COM 	mutex_exit(&dce->dce_lock);
64111042SErik.Nordmark@Sun.COM 	/* Count how many condemned dces for kmem_cache callback */
64211042SErik.Nordmark@Sun.COM 	atomic_add_32(&ipst->ips_num_dce_condemned, 1);
64311042SErik.Nordmark@Sun.COM }
64411042SErik.Nordmark@Sun.COM 
64511042SErik.Nordmark@Sun.COM /*
64611042SErik.Nordmark@Sun.COM  * Increment the generation avoiding the special condemned value
64711042SErik.Nordmark@Sun.COM  */
64811042SErik.Nordmark@Sun.COM void
dce_increment_generation(dce_t * dce)64911042SErik.Nordmark@Sun.COM dce_increment_generation(dce_t *dce)
65011042SErik.Nordmark@Sun.COM {
65111042SErik.Nordmark@Sun.COM 	uint_t generation;
65211042SErik.Nordmark@Sun.COM 
65311042SErik.Nordmark@Sun.COM 	mutex_enter(&dce->dce_lock);
65411042SErik.Nordmark@Sun.COM 	if (!DCE_IS_CONDEMNED(dce)) {
65511042SErik.Nordmark@Sun.COM 		generation = dce->dce_generation + 1;
65611042SErik.Nordmark@Sun.COM 		if (generation == DCE_GENERATION_CONDEMNED)
65711042SErik.Nordmark@Sun.COM 			generation = DCE_GENERATION_INITIAL;
65811042SErik.Nordmark@Sun.COM 		ASSERT(generation != DCE_GENERATION_VERIFY);
65911042SErik.Nordmark@Sun.COM 		dce->dce_generation = generation;
66011042SErik.Nordmark@Sun.COM 	}
66111042SErik.Nordmark@Sun.COM 	mutex_exit(&dce->dce_lock);
66211042SErik.Nordmark@Sun.COM }
66311042SErik.Nordmark@Sun.COM 
66411042SErik.Nordmark@Sun.COM /*
66511042SErik.Nordmark@Sun.COM  * Increment the generation number on all dces that have a path MTU and
666*13123SErik.Nordmark@Sun.COM  * the default DCE. Used when ill_mtu or ill_mc_mtu changes.
66711042SErik.Nordmark@Sun.COM  */
66811042SErik.Nordmark@Sun.COM void
dce_increment_all_generations(boolean_t isv6,ip_stack_t * ipst)66911042SErik.Nordmark@Sun.COM dce_increment_all_generations(boolean_t isv6, ip_stack_t *ipst)
67011042SErik.Nordmark@Sun.COM {
67111042SErik.Nordmark@Sun.COM 	int		i;
67211042SErik.Nordmark@Sun.COM 	dcb_t		*dcb;
67311042SErik.Nordmark@Sun.COM 	dce_t		*dce;
67411042SErik.Nordmark@Sun.COM 
67511042SErik.Nordmark@Sun.COM 	for (i = 0; i < ipst->ips_dce_hashsize; i++) {
67611042SErik.Nordmark@Sun.COM 		if (isv6)
67711042SErik.Nordmark@Sun.COM 			dcb = &ipst->ips_dce_hash_v6[i];
67811042SErik.Nordmark@Sun.COM 		else
67911042SErik.Nordmark@Sun.COM 			dcb = &ipst->ips_dce_hash_v4[i];
68011042SErik.Nordmark@Sun.COM 		rw_enter(&dcb->dcb_lock, RW_WRITER);
68111042SErik.Nordmark@Sun.COM 		for (dce = dcb->dcb_dce; dce != NULL; dce = dce->dce_next) {
68211042SErik.Nordmark@Sun.COM 			if (DCE_IS_CONDEMNED(dce))
68311042SErik.Nordmark@Sun.COM 				continue;
68411042SErik.Nordmark@Sun.COM 			dce_increment_generation(dce);
68511042SErik.Nordmark@Sun.COM 		}
68611042SErik.Nordmark@Sun.COM 		rw_exit(&dcb->dcb_lock);
68711042SErik.Nordmark@Sun.COM 	}
68811042SErik.Nordmark@Sun.COM 	dce_increment_generation(ipst->ips_dce_default);
68911042SErik.Nordmark@Sun.COM }
69011042SErik.Nordmark@Sun.COM 
69111042SErik.Nordmark@Sun.COM /*
69211042SErik.Nordmark@Sun.COM  * Caller needs to do a dce_refrele since we can't do the
69311042SErik.Nordmark@Sun.COM  * dce_refrele under dcb_lock.
69411042SErik.Nordmark@Sun.COM  */
69511042SErik.Nordmark@Sun.COM static void
dce_delete_locked(dcb_t * dcb,dce_t * dce)69611042SErik.Nordmark@Sun.COM dce_delete_locked(dcb_t *dcb, dce_t *dce)
69711042SErik.Nordmark@Sun.COM {
69811042SErik.Nordmark@Sun.COM 	dce->dce_bucket = NULL;
69911042SErik.Nordmark@Sun.COM 	*dce->dce_ptpn = dce->dce_next;
70011042SErik.Nordmark@Sun.COM 	if (dce->dce_next != NULL)
70111042SErik.Nordmark@Sun.COM 		dce->dce_next->dce_ptpn = dce->dce_ptpn;
70211042SErik.Nordmark@Sun.COM 	dce->dce_ptpn = NULL;
70311042SErik.Nordmark@Sun.COM 	dce->dce_next = NULL;
70411042SErik.Nordmark@Sun.COM 	atomic_add_32(&dcb->dcb_cnt, -1);
70511042SErik.Nordmark@Sun.COM 	dce_make_condemned(dce);
70611042SErik.Nordmark@Sun.COM }
70711042SErik.Nordmark@Sun.COM 
70811042SErik.Nordmark@Sun.COM static void
dce_inactive(dce_t * dce)70911042SErik.Nordmark@Sun.COM dce_inactive(dce_t *dce)
71011042SErik.Nordmark@Sun.COM {
71111042SErik.Nordmark@Sun.COM 	ip_stack_t	*ipst = dce->dce_ipst;
71211042SErik.Nordmark@Sun.COM 
71311042SErik.Nordmark@Sun.COM 	ASSERT(!(dce->dce_flags & DCEF_DEFAULT));
71411042SErik.Nordmark@Sun.COM 	ASSERT(dce->dce_ptpn == NULL);
71511042SErik.Nordmark@Sun.COM 	ASSERT(dce->dce_bucket == NULL);
71611042SErik.Nordmark@Sun.COM 
71711042SErik.Nordmark@Sun.COM 	/* Count how many condemned dces for kmem_cache callback */
71811042SErik.Nordmark@Sun.COM 	if (DCE_IS_CONDEMNED(dce))
71911042SErik.Nordmark@Sun.COM 		atomic_add_32(&ipst->ips_num_dce_condemned, -1);
72011042SErik.Nordmark@Sun.COM 
72111042SErik.Nordmark@Sun.COM 	kmem_cache_free(dce_cache, dce);
72211042SErik.Nordmark@Sun.COM }
72311042SErik.Nordmark@Sun.COM 
72411042SErik.Nordmark@Sun.COM void
dce_refrele(dce_t * dce)72511042SErik.Nordmark@Sun.COM dce_refrele(dce_t *dce)
72611042SErik.Nordmark@Sun.COM {
72711042SErik.Nordmark@Sun.COM 	ASSERT(dce->dce_refcnt != 0);
72811042SErik.Nordmark@Sun.COM 	if (atomic_add_32_nv(&dce->dce_refcnt, -1) == 0)
72911042SErik.Nordmark@Sun.COM 		dce_inactive(dce);
73011042SErik.Nordmark@Sun.COM }
73111042SErik.Nordmark@Sun.COM 
73211042SErik.Nordmark@Sun.COM void
dce_refhold(dce_t * dce)73311042SErik.Nordmark@Sun.COM dce_refhold(dce_t *dce)
73411042SErik.Nordmark@Sun.COM {
73511042SErik.Nordmark@Sun.COM 	atomic_add_32(&dce->dce_refcnt, 1);
73611042SErik.Nordmark@Sun.COM 	ASSERT(dce->dce_refcnt != 0);
73711042SErik.Nordmark@Sun.COM }
73811042SErik.Nordmark@Sun.COM 
73911042SErik.Nordmark@Sun.COM /* No tracing support yet hence the same as the above functions */
74011042SErik.Nordmark@Sun.COM void
dce_refrele_notr(dce_t * dce)74111042SErik.Nordmark@Sun.COM dce_refrele_notr(dce_t *dce)
74211042SErik.Nordmark@Sun.COM {
74311042SErik.Nordmark@Sun.COM 	ASSERT(dce->dce_refcnt != 0);
74411042SErik.Nordmark@Sun.COM 	if (atomic_add_32_nv(&dce->dce_refcnt, -1) == 0)
74511042SErik.Nordmark@Sun.COM 		dce_inactive(dce);
74611042SErik.Nordmark@Sun.COM }
74711042SErik.Nordmark@Sun.COM 
74811042SErik.Nordmark@Sun.COM void
dce_refhold_notr(dce_t * dce)74911042SErik.Nordmark@Sun.COM dce_refhold_notr(dce_t *dce)
75011042SErik.Nordmark@Sun.COM {
75111042SErik.Nordmark@Sun.COM 	atomic_add_32(&dce->dce_refcnt, 1);
75211042SErik.Nordmark@Sun.COM 	ASSERT(dce->dce_refcnt != 0);
75311042SErik.Nordmark@Sun.COM }
75411042SErik.Nordmark@Sun.COM 
75511042SErik.Nordmark@Sun.COM /* Report both the IPv4 and IPv6 DCEs. */
75611042SErik.Nordmark@Sun.COM mblk_t *
ip_snmp_get_mib2_ip_dce(queue_t * q,mblk_t * mpctl,ip_stack_t * ipst)75711042SErik.Nordmark@Sun.COM ip_snmp_get_mib2_ip_dce(queue_t *q, mblk_t *mpctl, ip_stack_t *ipst)
75811042SErik.Nordmark@Sun.COM {
75911042SErik.Nordmark@Sun.COM 	struct opthdr		*optp;
76011042SErik.Nordmark@Sun.COM 	mblk_t			*mp2ctl;
76111042SErik.Nordmark@Sun.COM 	dest_cache_entry_t	dest_cache;
76211042SErik.Nordmark@Sun.COM 	mblk_t			*mp_tail = NULL;
76311042SErik.Nordmark@Sun.COM 	dce_t			*dce;
76411042SErik.Nordmark@Sun.COM 	dcb_t			*dcb;
76511042SErik.Nordmark@Sun.COM 	int			i;
76611042SErik.Nordmark@Sun.COM 	uint64_t		current_time;
76711042SErik.Nordmark@Sun.COM 
76811066Srafael.vanoni@sun.com 	current_time = TICK_TO_SEC(ddi_get_lbolt64());
76911042SErik.Nordmark@Sun.COM 
77011042SErik.Nordmark@Sun.COM 	/*
77111042SErik.Nordmark@Sun.COM 	 * make a copy of the original message
77211042SErik.Nordmark@Sun.COM 	 */
77311042SErik.Nordmark@Sun.COM 	mp2ctl = copymsg(mpctl);
77411042SErik.Nordmark@Sun.COM 
77511042SErik.Nordmark@Sun.COM 	/* First we do IPv4 entries */
77611042SErik.Nordmark@Sun.COM 	optp = (struct opthdr *)&mpctl->b_rptr[
77711042SErik.Nordmark@Sun.COM 	    sizeof (struct T_optmgmt_ack)];
77811042SErik.Nordmark@Sun.COM 	optp->level = MIB2_IP;
77911042SErik.Nordmark@Sun.COM 	optp->name = EXPER_IP_DCE;
78011042SErik.Nordmark@Sun.COM 
78111042SErik.Nordmark@Sun.COM 	for (i = 0; i < ipst->ips_dce_hashsize; i++) {
78211042SErik.Nordmark@Sun.COM 		dcb = &ipst->ips_dce_hash_v4[i];
78311042SErik.Nordmark@Sun.COM 		rw_enter(&dcb->dcb_lock, RW_READER);
78411042SErik.Nordmark@Sun.COM 		for (dce = dcb->dcb_dce; dce != NULL; dce = dce->dce_next) {
78511042SErik.Nordmark@Sun.COM 			dest_cache.DestIpv4Address = dce->dce_v4addr;
78611042SErik.Nordmark@Sun.COM 			dest_cache.DestFlags = dce->dce_flags;
78711042SErik.Nordmark@Sun.COM 			if (dce->dce_flags & DCEF_PMTU)
78811042SErik.Nordmark@Sun.COM 				dest_cache.DestPmtu = dce->dce_pmtu;
78911042SErik.Nordmark@Sun.COM 			else
79011042SErik.Nordmark@Sun.COM 				dest_cache.DestPmtu = 0;
79111042SErik.Nordmark@Sun.COM 			dest_cache.DestIdent = dce->dce_ident;
79211042SErik.Nordmark@Sun.COM 			dest_cache.DestIfindex = 0;
79311042SErik.Nordmark@Sun.COM 			dest_cache.DestAge = current_time -
79411042SErik.Nordmark@Sun.COM 			    dce->dce_last_change_time;
79511042SErik.Nordmark@Sun.COM 			if (!snmp_append_data2(mpctl->b_cont, &mp_tail,
79611042SErik.Nordmark@Sun.COM 			    (char *)&dest_cache, (int)sizeof (dest_cache))) {
79711042SErik.Nordmark@Sun.COM 				ip1dbg(("ip_snmp_get_mib2_ip_dce: "
79811042SErik.Nordmark@Sun.COM 				    "failed to allocate %u bytes\n",
79911042SErik.Nordmark@Sun.COM 				    (uint_t)sizeof (dest_cache)));
80011042SErik.Nordmark@Sun.COM 			}
80111042SErik.Nordmark@Sun.COM 		}
80211042SErik.Nordmark@Sun.COM 		rw_exit(&dcb->dcb_lock);
80311042SErik.Nordmark@Sun.COM 	}
80411042SErik.Nordmark@Sun.COM 	optp->len = (t_uscalar_t)msgdsize(mpctl->b_cont);
80511042SErik.Nordmark@Sun.COM 	ip3dbg(("ip_snmp_get: level %d, name %d, len %d\n",
80611042SErik.Nordmark@Sun.COM 	    (int)optp->level, (int)optp->name, (int)optp->len));
80711042SErik.Nordmark@Sun.COM 	qreply(q, mpctl);
80811042SErik.Nordmark@Sun.COM 
80911042SErik.Nordmark@Sun.COM 	if (mp2ctl == NULL) {
81011042SErik.Nordmark@Sun.COM 		/* Copymsg failed above */
81111042SErik.Nordmark@Sun.COM 		return (NULL);
81211042SErik.Nordmark@Sun.COM 	}
81311042SErik.Nordmark@Sun.COM 
81411042SErik.Nordmark@Sun.COM 	/* Now for IPv6 */
81511042SErik.Nordmark@Sun.COM 	mpctl = mp2ctl;
81611042SErik.Nordmark@Sun.COM 	mp_tail = NULL;
81711042SErik.Nordmark@Sun.COM 	mp2ctl = copymsg(mpctl);
81811042SErik.Nordmark@Sun.COM 	optp = (struct opthdr *)&mpctl->b_rptr[
81911042SErik.Nordmark@Sun.COM 	    sizeof (struct T_optmgmt_ack)];
82011042SErik.Nordmark@Sun.COM 	optp->level = MIB2_IP6;
82111042SErik.Nordmark@Sun.COM 	optp->name = EXPER_IP_DCE;
82211042SErik.Nordmark@Sun.COM 
82311042SErik.Nordmark@Sun.COM 	for (i = 0; i < ipst->ips_dce_hashsize; i++) {
82411042SErik.Nordmark@Sun.COM 		dcb = &ipst->ips_dce_hash_v6[i];
82511042SErik.Nordmark@Sun.COM 		rw_enter(&dcb->dcb_lock, RW_READER);
82611042SErik.Nordmark@Sun.COM 		for (dce = dcb->dcb_dce; dce != NULL; dce = dce->dce_next) {
82711042SErik.Nordmark@Sun.COM 			dest_cache.DestIpv6Address = dce->dce_v6addr;
82811042SErik.Nordmark@Sun.COM 			dest_cache.DestFlags = dce->dce_flags;
82911042SErik.Nordmark@Sun.COM 			if (dce->dce_flags & DCEF_PMTU)
83011042SErik.Nordmark@Sun.COM 				dest_cache.DestPmtu = dce->dce_pmtu;
83111042SErik.Nordmark@Sun.COM 			else
83211042SErik.Nordmark@Sun.COM 				dest_cache.DestPmtu = 0;
83311042SErik.Nordmark@Sun.COM 			dest_cache.DestIdent = dce->dce_ident;
83411042SErik.Nordmark@Sun.COM 			if (IN6_IS_ADDR_LINKSCOPE(&dce->dce_v6addr))
83511042SErik.Nordmark@Sun.COM 				dest_cache.DestIfindex = dce->dce_ifindex;
83611042SErik.Nordmark@Sun.COM 			else
83711042SErik.Nordmark@Sun.COM 				dest_cache.DestIfindex = 0;
83811042SErik.Nordmark@Sun.COM 			dest_cache.DestAge = current_time -
83911042SErik.Nordmark@Sun.COM 			    dce->dce_last_change_time;
84011042SErik.Nordmark@Sun.COM 			if (!snmp_append_data2(mpctl->b_cont, &mp_tail,
84111042SErik.Nordmark@Sun.COM 			    (char *)&dest_cache, (int)sizeof (dest_cache))) {
84211042SErik.Nordmark@Sun.COM 				ip1dbg(("ip_snmp_get_mib2_ip_dce: "
84311042SErik.Nordmark@Sun.COM 				    "failed to allocate %u bytes\n",
84411042SErik.Nordmark@Sun.COM 				    (uint_t)sizeof (dest_cache)));
84511042SErik.Nordmark@Sun.COM 			}
84611042SErik.Nordmark@Sun.COM 		}
84711042SErik.Nordmark@Sun.COM 		rw_exit(&dcb->dcb_lock);
84811042SErik.Nordmark@Sun.COM 	}
84911042SErik.Nordmark@Sun.COM 	optp->len = (t_uscalar_t)msgdsize(mpctl->b_cont);
85011042SErik.Nordmark@Sun.COM 	ip3dbg(("ip_snmp_get: level %d, name %d, len %d\n",
85111042SErik.Nordmark@Sun.COM 	    (int)optp->level, (int)optp->name, (int)optp->len));
85211042SErik.Nordmark@Sun.COM 	qreply(q, mpctl);
85311042SErik.Nordmark@Sun.COM 
85411042SErik.Nordmark@Sun.COM 	return (mp2ctl);
85511042SErik.Nordmark@Sun.COM }
85611042SErik.Nordmark@Sun.COM 
85711042SErik.Nordmark@Sun.COM /*
85811042SErik.Nordmark@Sun.COM  * Remove IPv6 DCEs which refer to an ifindex that is going away.
85911042SErik.Nordmark@Sun.COM  * This is not required for correctness, but it avoids netstat -d
86011042SErik.Nordmark@Sun.COM  * showing stale stuff that will never be used.
86111042SErik.Nordmark@Sun.COM  */
86211042SErik.Nordmark@Sun.COM void
dce_cleanup(uint_t ifindex,ip_stack_t * ipst)86311042SErik.Nordmark@Sun.COM dce_cleanup(uint_t ifindex, ip_stack_t *ipst)
86411042SErik.Nordmark@Sun.COM {
86511042SErik.Nordmark@Sun.COM 	uint_t	i;
86611042SErik.Nordmark@Sun.COM 	dcb_t	*dcb;
86711042SErik.Nordmark@Sun.COM 	dce_t	*dce, *nextdce;
86811042SErik.Nordmark@Sun.COM 
86911042SErik.Nordmark@Sun.COM 	for (i = 0; i < ipst->ips_dce_hashsize; i++) {
87011042SErik.Nordmark@Sun.COM 		dcb = &ipst->ips_dce_hash_v6[i];
87111042SErik.Nordmark@Sun.COM 		rw_enter(&dcb->dcb_lock, RW_WRITER);
87211042SErik.Nordmark@Sun.COM 
87311042SErik.Nordmark@Sun.COM 		for (dce = dcb->dcb_dce; dce != NULL; dce = nextdce) {
87411042SErik.Nordmark@Sun.COM 			nextdce = dce->dce_next;
87511042SErik.Nordmark@Sun.COM 			if (dce->dce_ifindex == ifindex) {
87611042SErik.Nordmark@Sun.COM 				dce_delete_locked(dcb, dce);
87711042SErik.Nordmark@Sun.COM 				dce_refrele(dce);
87811042SErik.Nordmark@Sun.COM 			}
87911042SErik.Nordmark@Sun.COM 		}
88011042SErik.Nordmark@Sun.COM 		rw_exit(&dcb->dcb_lock);
88111042SErik.Nordmark@Sun.COM 	}
88211042SErik.Nordmark@Sun.COM }
883