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