1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * CDDL HEADER START 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*0Sstevel@tonic-gate * with the License. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*0Sstevel@tonic-gate * See the License for the specific language governing permissions 12*0Sstevel@tonic-gate * and limitations under the License. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*0Sstevel@tonic-gate * 20*0Sstevel@tonic-gate * CDDL HEADER END 21*0Sstevel@tonic-gate */ 22*0Sstevel@tonic-gate /* 23*0Sstevel@tonic-gate * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24*0Sstevel@tonic-gate * Use is subject to license terms. 25*0Sstevel@tonic-gate */ 26*0Sstevel@tonic-gate 27*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 28*0Sstevel@tonic-gate 29*0Sstevel@tonic-gate #include <sys/types.h> 30*0Sstevel@tonic-gate #include <sys/socket.h> 31*0Sstevel@tonic-gate #include <sys/ksynch.h> 32*0Sstevel@tonic-gate #include <sys/kmem.h> 33*0Sstevel@tonic-gate #include <sys/errno.h> 34*0Sstevel@tonic-gate #include <sys/systm.h> 35*0Sstevel@tonic-gate #include <sys/sysmacros.h> 36*0Sstevel@tonic-gate #include <sys/cmn_err.h> 37*0Sstevel@tonic-gate #include <sys/strsun.h> 38*0Sstevel@tonic-gate #include <sys/zone.h> 39*0Sstevel@tonic-gate #include <netinet/in.h> 40*0Sstevel@tonic-gate #include <inet/common.h> 41*0Sstevel@tonic-gate #include <inet/ip.h> 42*0Sstevel@tonic-gate #include <inet/ip6.h> 43*0Sstevel@tonic-gate #include <inet/ip6_asp.h> 44*0Sstevel@tonic-gate #include <inet/ip_ire.h> 45*0Sstevel@tonic-gate 46*0Sstevel@tonic-gate #define IN6ADDR_MASK128_INIT \ 47*0Sstevel@tonic-gate { 0xffffffffU, 0xffffffffU, 0xffffffffU, 0xffffffffU } 48*0Sstevel@tonic-gate #define IN6ADDR_MASK96_INIT { 0xffffffffU, 0xffffffffU, 0xffffffffU, 0 } 49*0Sstevel@tonic-gate #ifdef _BIG_ENDIAN 50*0Sstevel@tonic-gate #define IN6ADDR_MASK16_INIT { 0xffff0000U, 0, 0, 0 } 51*0Sstevel@tonic-gate #else 52*0Sstevel@tonic-gate #define IN6ADDR_MASK16_INIT { 0x0000ffffU, 0, 0, 0 } 53*0Sstevel@tonic-gate #endif 54*0Sstevel@tonic-gate 55*0Sstevel@tonic-gate 56*0Sstevel@tonic-gate /* 57*0Sstevel@tonic-gate * This table is ordered such that longest prefix matches are hit first 58*0Sstevel@tonic-gate * (longer prefix lengths first). The last entry must be the "default" 59*0Sstevel@tonic-gate * entry (::0/0). 60*0Sstevel@tonic-gate */ 61*0Sstevel@tonic-gate static ip6_asp_t default_ip6_asp_table[] = { 62*0Sstevel@tonic-gate { IN6ADDR_LOOPBACK_INIT, IN6ADDR_MASK128_INIT, 63*0Sstevel@tonic-gate "Loopback", 50 }, 64*0Sstevel@tonic-gate { IN6ADDR_ANY_INIT, IN6ADDR_MASK96_INIT, 65*0Sstevel@tonic-gate "IPv4_Compatible", 20 }, 66*0Sstevel@tonic-gate #ifdef _BIG_ENDIAN 67*0Sstevel@tonic-gate { { 0, 0, 0x0000ffffU, 0 }, IN6ADDR_MASK96_INIT, 68*0Sstevel@tonic-gate "IPv4", 10 }, 69*0Sstevel@tonic-gate { { 0x20020000U, 0, 0, 0 }, IN6ADDR_MASK16_INIT, 70*0Sstevel@tonic-gate "6to4", 30 }, 71*0Sstevel@tonic-gate #else 72*0Sstevel@tonic-gate { { 0, 0, 0xffff0000U, 0 }, IN6ADDR_MASK96_INIT, 73*0Sstevel@tonic-gate "IPv4", 10 }, 74*0Sstevel@tonic-gate { { 0x00000220U, 0, 0, 0 }, IN6ADDR_MASK16_INIT, 75*0Sstevel@tonic-gate "6to4", 30 }, 76*0Sstevel@tonic-gate #endif 77*0Sstevel@tonic-gate { IN6ADDR_ANY_INIT, IN6ADDR_ANY_INIT, 78*0Sstevel@tonic-gate "Default", 40 } 79*0Sstevel@tonic-gate }; 80*0Sstevel@tonic-gate 81*0Sstevel@tonic-gate /* pending binds */ 82*0Sstevel@tonic-gate static mblk_t *ip6_asp_pending_ops = NULL, *ip6_asp_pending_ops_tail = NULL; 83*0Sstevel@tonic-gate 84*0Sstevel@tonic-gate /* Synchronize updates with table usage */ 85*0Sstevel@tonic-gate static mblk_t *ip6_asp_pending_update = NULL; /* pending table updates */ 86*0Sstevel@tonic-gate 87*0Sstevel@tonic-gate static boolean_t ip6_asp_uip = B_FALSE; /* table update in progress */ 88*0Sstevel@tonic-gate static kmutex_t ip6_asp_lock; /* protect all the above */ 89*0Sstevel@tonic-gate static uint32_t ip6_asp_refcnt = 0; /* outstanding references */ 90*0Sstevel@tonic-gate 91*0Sstevel@tonic-gate /* 92*0Sstevel@tonic-gate * The IPv6 Default Address Selection policy table. 93*0Sstevel@tonic-gate * Until someone up above reconfigures the policy table, use the global 94*0Sstevel@tonic-gate * default. The table needs no lock since the only way to alter it is 95*0Sstevel@tonic-gate * through the SIOCSIP6ADDRPOLICY which is exclusive in ip. 96*0Sstevel@tonic-gate */ 97*0Sstevel@tonic-gate static ip6_asp_t *ip6_asp_table = default_ip6_asp_table; 98*0Sstevel@tonic-gate /* The number of policy entries in the table */ 99*0Sstevel@tonic-gate static uint_t ip6_asp_table_count = 100*0Sstevel@tonic-gate sizeof (default_ip6_asp_table) / sizeof (ip6_asp_t); 101*0Sstevel@tonic-gate 102*0Sstevel@tonic-gate static void ip6_asp_copy(ip6_asp_t *, ip6_asp_t *, uint_t); 103*0Sstevel@tonic-gate static void ip6_asp_check_for_updates(); 104*0Sstevel@tonic-gate 105*0Sstevel@tonic-gate void 106*0Sstevel@tonic-gate ip6_asp_init(void) 107*0Sstevel@tonic-gate { 108*0Sstevel@tonic-gate /* Initialize the table lock */ 109*0Sstevel@tonic-gate mutex_init(&ip6_asp_lock, NULL, MUTEX_DEFAULT, NULL); 110*0Sstevel@tonic-gate } 111*0Sstevel@tonic-gate 112*0Sstevel@tonic-gate void 113*0Sstevel@tonic-gate ip6_asp_free(void) 114*0Sstevel@tonic-gate { 115*0Sstevel@tonic-gate if (ip6_asp_table != default_ip6_asp_table) { 116*0Sstevel@tonic-gate kmem_free(ip6_asp_table, 117*0Sstevel@tonic-gate ip6_asp_table_count * sizeof (ip6_asp_t)); 118*0Sstevel@tonic-gate } 119*0Sstevel@tonic-gate mutex_destroy(&ip6_asp_lock); 120*0Sstevel@tonic-gate } 121*0Sstevel@tonic-gate 122*0Sstevel@tonic-gate /* 123*0Sstevel@tonic-gate * Return false if the table is being updated. Else, increment the ref 124*0Sstevel@tonic-gate * count and return true. 125*0Sstevel@tonic-gate */ 126*0Sstevel@tonic-gate boolean_t 127*0Sstevel@tonic-gate ip6_asp_can_lookup() 128*0Sstevel@tonic-gate { 129*0Sstevel@tonic-gate mutex_enter(&ip6_asp_lock); 130*0Sstevel@tonic-gate if (ip6_asp_uip) { 131*0Sstevel@tonic-gate mutex_exit(&ip6_asp_lock); 132*0Sstevel@tonic-gate return (B_FALSE); 133*0Sstevel@tonic-gate } 134*0Sstevel@tonic-gate IP6_ASP_TABLE_REFHOLD(); 135*0Sstevel@tonic-gate mutex_exit(&ip6_asp_lock); 136*0Sstevel@tonic-gate return (B_TRUE); 137*0Sstevel@tonic-gate 138*0Sstevel@tonic-gate } 139*0Sstevel@tonic-gate 140*0Sstevel@tonic-gate void 141*0Sstevel@tonic-gate ip6_asp_pending_op(queue_t *q, mblk_t *mp, aspfunc_t func) 142*0Sstevel@tonic-gate { 143*0Sstevel@tonic-gate 144*0Sstevel@tonic-gate ASSERT((mp->b_prev == NULL) && (mp->b_queue == NULL) && 145*0Sstevel@tonic-gate (mp->b_next == NULL)); 146*0Sstevel@tonic-gate mp->b_queue = (void *)q; 147*0Sstevel@tonic-gate mp->b_prev = (void *)func; 148*0Sstevel@tonic-gate mp->b_next = NULL; 149*0Sstevel@tonic-gate 150*0Sstevel@tonic-gate mutex_enter(&ip6_asp_lock); 151*0Sstevel@tonic-gate if (ip6_asp_pending_ops == NULL) { 152*0Sstevel@tonic-gate ASSERT(ip6_asp_pending_ops_tail == NULL); 153*0Sstevel@tonic-gate ip6_asp_pending_ops = ip6_asp_pending_ops_tail = mp; 154*0Sstevel@tonic-gate } else { 155*0Sstevel@tonic-gate ip6_asp_pending_ops_tail->b_next = mp; 156*0Sstevel@tonic-gate ip6_asp_pending_ops_tail = mp; 157*0Sstevel@tonic-gate } 158*0Sstevel@tonic-gate mutex_exit(&ip6_asp_lock); 159*0Sstevel@tonic-gate } 160*0Sstevel@tonic-gate 161*0Sstevel@tonic-gate static void 162*0Sstevel@tonic-gate ip6_asp_complete_op() 163*0Sstevel@tonic-gate { 164*0Sstevel@tonic-gate mblk_t *mp; 165*0Sstevel@tonic-gate queue_t *q; 166*0Sstevel@tonic-gate aspfunc_t func; 167*0Sstevel@tonic-gate 168*0Sstevel@tonic-gate mutex_enter(&ip6_asp_lock); 169*0Sstevel@tonic-gate while (ip6_asp_pending_ops != NULL) { 170*0Sstevel@tonic-gate mp = ip6_asp_pending_ops; 171*0Sstevel@tonic-gate ip6_asp_pending_ops = mp->b_next; 172*0Sstevel@tonic-gate mp->b_next = NULL; 173*0Sstevel@tonic-gate if (ip6_asp_pending_ops == NULL) 174*0Sstevel@tonic-gate ip6_asp_pending_ops_tail = NULL; 175*0Sstevel@tonic-gate mutex_exit(&ip6_asp_lock); 176*0Sstevel@tonic-gate 177*0Sstevel@tonic-gate q = (queue_t *)mp->b_queue; 178*0Sstevel@tonic-gate func = (aspfunc_t)mp->b_prev; 179*0Sstevel@tonic-gate 180*0Sstevel@tonic-gate mp->b_prev = NULL; 181*0Sstevel@tonic-gate mp->b_queue = NULL; 182*0Sstevel@tonic-gate 183*0Sstevel@tonic-gate 184*0Sstevel@tonic-gate (*func)(NULL, q, mp, NULL); 185*0Sstevel@tonic-gate mutex_enter(&ip6_asp_lock); 186*0Sstevel@tonic-gate } 187*0Sstevel@tonic-gate mutex_exit(&ip6_asp_lock); 188*0Sstevel@tonic-gate } 189*0Sstevel@tonic-gate 190*0Sstevel@tonic-gate /* 191*0Sstevel@tonic-gate * Decrement reference count. When it gets to 0, we check for (pending) 192*0Sstevel@tonic-gate * saved update to the table, if any. 193*0Sstevel@tonic-gate */ 194*0Sstevel@tonic-gate void 195*0Sstevel@tonic-gate ip6_asp_table_refrele() 196*0Sstevel@tonic-gate { 197*0Sstevel@tonic-gate IP6_ASP_TABLE_REFRELE(); 198*0Sstevel@tonic-gate } 199*0Sstevel@tonic-gate 200*0Sstevel@tonic-gate /* 201*0Sstevel@tonic-gate * This function is guaranteed never to return a NULL pointer. It 202*0Sstevel@tonic-gate * will always return information from one of the entries in the 203*0Sstevel@tonic-gate * asp_table (which will never be empty). If a pointer is passed 204*0Sstevel@tonic-gate * in for the precedence, the precedence value will be set; a 205*0Sstevel@tonic-gate * pointer to the label will be returned by the function. 206*0Sstevel@tonic-gate * 207*0Sstevel@tonic-gate * Since the table is only anticipated to have five or six entries 208*0Sstevel@tonic-gate * total, the lookup algorithm hasn't been optimized to anything 209*0Sstevel@tonic-gate * better than O(n). 210*0Sstevel@tonic-gate */ 211*0Sstevel@tonic-gate char * 212*0Sstevel@tonic-gate ip6_asp_lookup(const in6_addr_t *addr, uint32_t *precedence) 213*0Sstevel@tonic-gate { 214*0Sstevel@tonic-gate ip6_asp_t *aspp; 215*0Sstevel@tonic-gate ip6_asp_t *match = NULL; 216*0Sstevel@tonic-gate ip6_asp_t *default_policy; 217*0Sstevel@tonic-gate 218*0Sstevel@tonic-gate aspp = ip6_asp_table; 219*0Sstevel@tonic-gate /* The default entry must always be the last one */ 220*0Sstevel@tonic-gate default_policy = aspp + ip6_asp_table_count - 1; 221*0Sstevel@tonic-gate 222*0Sstevel@tonic-gate while (match == NULL) { 223*0Sstevel@tonic-gate if (aspp == default_policy) { 224*0Sstevel@tonic-gate match = aspp; 225*0Sstevel@tonic-gate } else { 226*0Sstevel@tonic-gate if (V6_MASK_EQ(*addr, aspp->ip6_asp_mask, 227*0Sstevel@tonic-gate aspp->ip6_asp_prefix)) 228*0Sstevel@tonic-gate match = aspp; 229*0Sstevel@tonic-gate else 230*0Sstevel@tonic-gate aspp++; 231*0Sstevel@tonic-gate } 232*0Sstevel@tonic-gate } 233*0Sstevel@tonic-gate 234*0Sstevel@tonic-gate if (precedence != NULL) 235*0Sstevel@tonic-gate *precedence = match->ip6_asp_precedence; 236*0Sstevel@tonic-gate return (match->ip6_asp_label); 237*0Sstevel@tonic-gate } 238*0Sstevel@tonic-gate 239*0Sstevel@tonic-gate /* 240*0Sstevel@tonic-gate * If we had deferred updating the table because of outstanding references, 241*0Sstevel@tonic-gate * do it now. Note, we don't do error checking on the queued IOCTL mblk, since 242*0Sstevel@tonic-gate * ip_sioctl_ip6addrpolicy() has already done it for us. 243*0Sstevel@tonic-gate */ 244*0Sstevel@tonic-gate void 245*0Sstevel@tonic-gate ip6_asp_check_for_updates() 246*0Sstevel@tonic-gate { 247*0Sstevel@tonic-gate ip6_asp_t *table; 248*0Sstevel@tonic-gate size_t table_size; 249*0Sstevel@tonic-gate mblk_t *data_mp, *mp; 250*0Sstevel@tonic-gate struct iocblk *iocp; 251*0Sstevel@tonic-gate 252*0Sstevel@tonic-gate mutex_enter(&ip6_asp_lock); 253*0Sstevel@tonic-gate if (ip6_asp_pending_update == NULL || ip6_asp_refcnt > 0) { 254*0Sstevel@tonic-gate mutex_exit(&ip6_asp_lock); 255*0Sstevel@tonic-gate return; 256*0Sstevel@tonic-gate } 257*0Sstevel@tonic-gate 258*0Sstevel@tonic-gate mp = ip6_asp_pending_update; 259*0Sstevel@tonic-gate ip6_asp_pending_update = NULL; 260*0Sstevel@tonic-gate ASSERT(mp->b_prev != NULL); 261*0Sstevel@tonic-gate 262*0Sstevel@tonic-gate ip6_asp_uip = B_TRUE; 263*0Sstevel@tonic-gate 264*0Sstevel@tonic-gate iocp = (struct iocblk *)mp->b_rptr; 265*0Sstevel@tonic-gate data_mp = mp->b_cont; 266*0Sstevel@tonic-gate if (data_mp == NULL) { 267*0Sstevel@tonic-gate table = NULL; 268*0Sstevel@tonic-gate table_size = iocp->ioc_count; 269*0Sstevel@tonic-gate } else { 270*0Sstevel@tonic-gate table = (ip6_asp_t *)data_mp->b_rptr; 271*0Sstevel@tonic-gate table_size = iocp->ioc_count; 272*0Sstevel@tonic-gate } 273*0Sstevel@tonic-gate 274*0Sstevel@tonic-gate ip6_asp_replace(mp, table, table_size, B_TRUE, 275*0Sstevel@tonic-gate iocp->ioc_flag & IOC_MODELS); 276*0Sstevel@tonic-gate } 277*0Sstevel@tonic-gate 278*0Sstevel@tonic-gate /* 279*0Sstevel@tonic-gate * ip6_asp_replace replaces the contents of the IPv6 address selection 280*0Sstevel@tonic-gate * policy table with those specified in new_table. If new_table is NULL, 281*0Sstevel@tonic-gate * this indicates that the caller wishes ip to use the default policy 282*0Sstevel@tonic-gate * table. The caller is responsible for making sure that there are exactly 283*0Sstevel@tonic-gate * new_count policy entries in new_table. 284*0Sstevel@tonic-gate */ 285*0Sstevel@tonic-gate /*ARGSUSED4*/ 286*0Sstevel@tonic-gate void 287*0Sstevel@tonic-gate ip6_asp_replace(mblk_t *mp, ip6_asp_t *new_table, size_t new_size, 288*0Sstevel@tonic-gate boolean_t locked, model_t datamodel) 289*0Sstevel@tonic-gate { 290*0Sstevel@tonic-gate int ret_val = 0; 291*0Sstevel@tonic-gate ip6_asp_t *tmp_table; 292*0Sstevel@tonic-gate uint_t count; 293*0Sstevel@tonic-gate queue_t *q; 294*0Sstevel@tonic-gate struct iocblk *iocp; 295*0Sstevel@tonic-gate #if defined(_SYSCALL32_IMPL) && _LONG_LONG_ALIGNMENT_32 == 4 296*0Sstevel@tonic-gate size_t ip6_asp_size = SIZEOF_STRUCT(ip6_asp, datamodel); 297*0Sstevel@tonic-gate #else 298*0Sstevel@tonic-gate const size_t ip6_asp_size = sizeof (ip6_asp_t); 299*0Sstevel@tonic-gate #endif 300*0Sstevel@tonic-gate 301*0Sstevel@tonic-gate if (new_size % ip6_asp_size != 0) { 302*0Sstevel@tonic-gate ip1dbg(("ip6_asp_replace: invalid table size\n")); 303*0Sstevel@tonic-gate ret_val = EINVAL; 304*0Sstevel@tonic-gate if (locked) 305*0Sstevel@tonic-gate goto unlock_end; 306*0Sstevel@tonic-gate goto replace_end; 307*0Sstevel@tonic-gate } else { 308*0Sstevel@tonic-gate count = new_size / ip6_asp_size; 309*0Sstevel@tonic-gate } 310*0Sstevel@tonic-gate 311*0Sstevel@tonic-gate 312*0Sstevel@tonic-gate if (!locked) 313*0Sstevel@tonic-gate mutex_enter(&ip6_asp_lock); 314*0Sstevel@tonic-gate /* 315*0Sstevel@tonic-gate * Check if we are in the process of creating any IRE using the 316*0Sstevel@tonic-gate * current information. If so, wait till that is done. 317*0Sstevel@tonic-gate */ 318*0Sstevel@tonic-gate if (!locked && ip6_asp_refcnt > 0) { 319*0Sstevel@tonic-gate /* Save this request for later processing */ 320*0Sstevel@tonic-gate if (ip6_asp_pending_update == NULL) { 321*0Sstevel@tonic-gate ip6_asp_pending_update = mp; 322*0Sstevel@tonic-gate } else { 323*0Sstevel@tonic-gate /* Let's not queue multiple requests for now */ 324*0Sstevel@tonic-gate ip1dbg(("ip6_asp_replace: discarding request\n")); 325*0Sstevel@tonic-gate mutex_exit(&ip6_asp_lock); 326*0Sstevel@tonic-gate ret_val = EAGAIN; 327*0Sstevel@tonic-gate goto replace_end; 328*0Sstevel@tonic-gate } 329*0Sstevel@tonic-gate mutex_exit(&ip6_asp_lock); 330*0Sstevel@tonic-gate return; 331*0Sstevel@tonic-gate } 332*0Sstevel@tonic-gate 333*0Sstevel@tonic-gate /* Prevent lookups till the table have been updated */ 334*0Sstevel@tonic-gate if (!locked) 335*0Sstevel@tonic-gate ip6_asp_uip = B_TRUE; 336*0Sstevel@tonic-gate 337*0Sstevel@tonic-gate ASSERT(ip6_asp_refcnt == 0); 338*0Sstevel@tonic-gate 339*0Sstevel@tonic-gate if (new_table == NULL) { 340*0Sstevel@tonic-gate /* 341*0Sstevel@tonic-gate * This is a special case. The user wants to revert 342*0Sstevel@tonic-gate * back to using the default table. 343*0Sstevel@tonic-gate */ 344*0Sstevel@tonic-gate if (ip6_asp_table == default_ip6_asp_table) 345*0Sstevel@tonic-gate goto unlock_end; 346*0Sstevel@tonic-gate 347*0Sstevel@tonic-gate kmem_free(ip6_asp_table, 348*0Sstevel@tonic-gate ip6_asp_table_count * sizeof (ip6_asp_t)); 349*0Sstevel@tonic-gate ip6_asp_table = default_ip6_asp_table; 350*0Sstevel@tonic-gate ip6_asp_table_count = 351*0Sstevel@tonic-gate sizeof (default_ip6_asp_table) / sizeof (ip6_asp_t); 352*0Sstevel@tonic-gate goto unlock_end; 353*0Sstevel@tonic-gate } 354*0Sstevel@tonic-gate 355*0Sstevel@tonic-gate if (count == 0) { 356*0Sstevel@tonic-gate ret_val = EINVAL; 357*0Sstevel@tonic-gate ip1dbg(("ip6_asp_replace: empty table\n")); 358*0Sstevel@tonic-gate goto unlock_end; 359*0Sstevel@tonic-gate } 360*0Sstevel@tonic-gate 361*0Sstevel@tonic-gate if ((tmp_table = kmem_alloc(count * sizeof (ip6_asp_t), KM_NOSLEEP)) == 362*0Sstevel@tonic-gate NULL) { 363*0Sstevel@tonic-gate ret_val = ENOMEM; 364*0Sstevel@tonic-gate goto unlock_end; 365*0Sstevel@tonic-gate } 366*0Sstevel@tonic-gate 367*0Sstevel@tonic-gate #if defined(_SYSCALL32_IMPL) && _LONG_LONG_ALIGNMENT_32 == 4 368*0Sstevel@tonic-gate 369*0Sstevel@tonic-gate /* 370*0Sstevel@tonic-gate * If 'new_table' -actually- originates from a 32-bit process 371*0Sstevel@tonic-gate * then the nicely aligned ip6_asp_label array will be 372*0Sstevel@tonic-gate * subtlely misaligned on this kernel, because the structure 373*0Sstevel@tonic-gate * is 8 byte aligned in the kernel, but only 4 byte aligned in 374*0Sstevel@tonic-gate * userland. Fix it up here. 375*0Sstevel@tonic-gate * 376*0Sstevel@tonic-gate * XX64 See the notes in ip_sioctl_ip6addrpolicy. Perhaps we could 377*0Sstevel@tonic-gate * do the datamodel transformation (below) there instead of here? 378*0Sstevel@tonic-gate */ 379*0Sstevel@tonic-gate if (datamodel == IOC_ILP32) { 380*0Sstevel@tonic-gate ip6_asp_t *dst; 381*0Sstevel@tonic-gate ip6_asp32_t *src; 382*0Sstevel@tonic-gate int i; 383*0Sstevel@tonic-gate 384*0Sstevel@tonic-gate if ((dst = kmem_zalloc(count * sizeof (*dst), 385*0Sstevel@tonic-gate KM_NOSLEEP)) == NULL) { 386*0Sstevel@tonic-gate kmem_free(tmp_table, count * sizeof (ip6_asp_t)); 387*0Sstevel@tonic-gate ret_val = ENOMEM; 388*0Sstevel@tonic-gate goto unlock_end; 389*0Sstevel@tonic-gate } 390*0Sstevel@tonic-gate 391*0Sstevel@tonic-gate /* 392*0Sstevel@tonic-gate * Copy each element of the table from ip6_asp32_t 393*0Sstevel@tonic-gate * format into ip6_asp_t format. Fortunately, since 394*0Sstevel@tonic-gate * we're just dealing with a trailing structure pad, 395*0Sstevel@tonic-gate * we can do this straightforwardly with a flurry of 396*0Sstevel@tonic-gate * bcopying. 397*0Sstevel@tonic-gate */ 398*0Sstevel@tonic-gate src = (void *)new_table; 399*0Sstevel@tonic-gate for (i = 0; i < count; i++) 400*0Sstevel@tonic-gate bcopy(src + i, dst + i, sizeof (*src)); 401*0Sstevel@tonic-gate 402*0Sstevel@tonic-gate ip6_asp_copy(dst, tmp_table, count); 403*0Sstevel@tonic-gate kmem_free(dst, count * sizeof (*dst)); 404*0Sstevel@tonic-gate } else 405*0Sstevel@tonic-gate #endif 406*0Sstevel@tonic-gate ip6_asp_copy(new_table, tmp_table, count); 407*0Sstevel@tonic-gate 408*0Sstevel@tonic-gate /* Make sure the last entry is the default entry */ 409*0Sstevel@tonic-gate if (!IN6_IS_ADDR_UNSPECIFIED(&tmp_table[count - 1].ip6_asp_prefix) || 410*0Sstevel@tonic-gate !IN6_IS_ADDR_UNSPECIFIED(&tmp_table[count - 1].ip6_asp_mask)) { 411*0Sstevel@tonic-gate ret_val = EINVAL; 412*0Sstevel@tonic-gate kmem_free(tmp_table, count * sizeof (ip6_asp_t)); 413*0Sstevel@tonic-gate ip1dbg(("ip6_asp_replace: bad table: no default entry\n")); 414*0Sstevel@tonic-gate goto unlock_end; 415*0Sstevel@tonic-gate } 416*0Sstevel@tonic-gate if (ip6_asp_table != default_ip6_asp_table) { 417*0Sstevel@tonic-gate kmem_free(ip6_asp_table, 418*0Sstevel@tonic-gate ip6_asp_table_count * sizeof (ip6_asp_t)); 419*0Sstevel@tonic-gate } 420*0Sstevel@tonic-gate ip6_asp_table = tmp_table; 421*0Sstevel@tonic-gate ip6_asp_table_count = count; 422*0Sstevel@tonic-gate 423*0Sstevel@tonic-gate /* 424*0Sstevel@tonic-gate * The user has changed the address selection policy table. IPv6 425*0Sstevel@tonic-gate * source address selection for existing IRE_CACHE and 426*0Sstevel@tonic-gate * IRE_HOST_REDIRECT entries used the old table, so we need to 427*0Sstevel@tonic-gate * clear the cache. 428*0Sstevel@tonic-gate */ 429*0Sstevel@tonic-gate ire_walk_v6(ire_delete_cache_v6, NULL, ALL_ZONES); 430*0Sstevel@tonic-gate 431*0Sstevel@tonic-gate unlock_end: 432*0Sstevel@tonic-gate ip6_asp_uip = B_FALSE; 433*0Sstevel@tonic-gate mutex_exit(&ip6_asp_lock); 434*0Sstevel@tonic-gate 435*0Sstevel@tonic-gate replace_end: 436*0Sstevel@tonic-gate /* Reply to the ioctl */ 437*0Sstevel@tonic-gate q = (queue_t *)mp->b_prev; 438*0Sstevel@tonic-gate mp->b_prev = NULL; 439*0Sstevel@tonic-gate if (q == NULL) { 440*0Sstevel@tonic-gate freemsg(mp); 441*0Sstevel@tonic-gate goto check_binds; 442*0Sstevel@tonic-gate } 443*0Sstevel@tonic-gate iocp = (struct iocblk *)mp->b_rptr; 444*0Sstevel@tonic-gate iocp->ioc_error = ret_val; 445*0Sstevel@tonic-gate iocp->ioc_count = 0; 446*0Sstevel@tonic-gate DB_TYPE(mp) = (iocp->ioc_error == 0) ? M_IOCACK : M_IOCNAK; 447*0Sstevel@tonic-gate qreply(q, mp); 448*0Sstevel@tonic-gate check_binds: 449*0Sstevel@tonic-gate ip6_asp_complete_op(); 450*0Sstevel@tonic-gate } 451*0Sstevel@tonic-gate 452*0Sstevel@tonic-gate /* 453*0Sstevel@tonic-gate * Copies the contents of src_table to dst_table, and sorts the 454*0Sstevel@tonic-gate * entries in decending order of prefix lengths. It assumes that both 455*0Sstevel@tonic-gate * tables are appropriately sized to contain count entries. 456*0Sstevel@tonic-gate */ 457*0Sstevel@tonic-gate static void 458*0Sstevel@tonic-gate ip6_asp_copy(ip6_asp_t *src_table, ip6_asp_t *dst_table, uint_t count) 459*0Sstevel@tonic-gate { 460*0Sstevel@tonic-gate ip6_asp_t *src_ptr, *src_limit, *dst_ptr, *dst_limit, *dp; 461*0Sstevel@tonic-gate 462*0Sstevel@tonic-gate dst_table[0] = src_table[0]; 463*0Sstevel@tonic-gate if (count == 1) 464*0Sstevel@tonic-gate return; 465*0Sstevel@tonic-gate 466*0Sstevel@tonic-gate /* 467*0Sstevel@tonic-gate * Sort the entries in descending order of prefix lengths. 468*0Sstevel@tonic-gate * 469*0Sstevel@tonic-gate * Note: this should be a small table. In 99% of cases, we 470*0Sstevel@tonic-gate * expect the table to have 5 entries. In the remaining 1% 471*0Sstevel@tonic-gate * of cases, we expect the table to have one or two more 472*0Sstevel@tonic-gate * entries. It would be very rare for the table to have 473*0Sstevel@tonic-gate * double-digit entries. 474*0Sstevel@tonic-gate */ 475*0Sstevel@tonic-gate src_limit = src_table + count; 476*0Sstevel@tonic-gate dst_limit = dst_table + 1; 477*0Sstevel@tonic-gate for (src_ptr = src_table + 1; src_ptr != src_limit; 478*0Sstevel@tonic-gate src_ptr++, dst_limit++) { 479*0Sstevel@tonic-gate for (dst_ptr = dst_table; dst_ptr < dst_limit; dst_ptr++) { 480*0Sstevel@tonic-gate if (ip_mask_to_plen_v6(&src_ptr->ip6_asp_mask) > 481*0Sstevel@tonic-gate ip_mask_to_plen_v6(&dst_ptr->ip6_asp_mask)) { 482*0Sstevel@tonic-gate /* 483*0Sstevel@tonic-gate * Make room to insert the source entry 484*0Sstevel@tonic-gate * before dst_ptr by shifting entries to 485*0Sstevel@tonic-gate * the right. 486*0Sstevel@tonic-gate */ 487*0Sstevel@tonic-gate for (dp = dst_limit - 1; dp >= dst_ptr; dp--) 488*0Sstevel@tonic-gate *(dp + 1) = *dp; 489*0Sstevel@tonic-gate break; 490*0Sstevel@tonic-gate } 491*0Sstevel@tonic-gate } 492*0Sstevel@tonic-gate *dst_ptr = *src_ptr; 493*0Sstevel@tonic-gate } 494*0Sstevel@tonic-gate } 495*0Sstevel@tonic-gate 496*0Sstevel@tonic-gate /* 497*0Sstevel@tonic-gate * This function copies as many entries from ip6_asp_table as will fit 498*0Sstevel@tonic-gate * into dtable. The dtable_size parameter is the size of dtable 499*0Sstevel@tonic-gate * in bytes. This function returns the number of entries in 500*0Sstevel@tonic-gate * ip6_asp_table, even if it's not able to fit all of the entries into 501*0Sstevel@tonic-gate * dtable. 502*0Sstevel@tonic-gate */ 503*0Sstevel@tonic-gate int 504*0Sstevel@tonic-gate ip6_asp_get(ip6_asp_t *dtable, size_t dtable_size) 505*0Sstevel@tonic-gate { 506*0Sstevel@tonic-gate uint_t dtable_count; 507*0Sstevel@tonic-gate 508*0Sstevel@tonic-gate if (dtable != NULL) { 509*0Sstevel@tonic-gate if (dtable_size < sizeof (ip6_asp_t)) 510*0Sstevel@tonic-gate return (-1); 511*0Sstevel@tonic-gate 512*0Sstevel@tonic-gate dtable_count = dtable_size / sizeof (ip6_asp_t); 513*0Sstevel@tonic-gate bcopy(ip6_asp_table, dtable, 514*0Sstevel@tonic-gate MIN(ip6_asp_table_count, dtable_count) * 515*0Sstevel@tonic-gate sizeof (ip6_asp_t)); 516*0Sstevel@tonic-gate } 517*0Sstevel@tonic-gate 518*0Sstevel@tonic-gate return (ip6_asp_table_count); 519*0Sstevel@tonic-gate } 520*0Sstevel@tonic-gate 521*0Sstevel@tonic-gate /* 522*0Sstevel@tonic-gate * Compare two labels. Return B_TRUE if they are equal, B_FALSE 523*0Sstevel@tonic-gate * otherwise. 524*0Sstevel@tonic-gate */ 525*0Sstevel@tonic-gate boolean_t 526*0Sstevel@tonic-gate ip6_asp_labelcmp(const char *label1, const char *label2) 527*0Sstevel@tonic-gate { 528*0Sstevel@tonic-gate int64_t *llptr1, *llptr2; 529*0Sstevel@tonic-gate 530*0Sstevel@tonic-gate /* 531*0Sstevel@tonic-gate * The common case, the two labels are actually the same string 532*0Sstevel@tonic-gate * from the policy table. 533*0Sstevel@tonic-gate */ 534*0Sstevel@tonic-gate if (label1 == label2) 535*0Sstevel@tonic-gate return (B_TRUE); 536*0Sstevel@tonic-gate 537*0Sstevel@tonic-gate /* 538*0Sstevel@tonic-gate * Since we know the labels are at most 16 bytes long, compare 539*0Sstevel@tonic-gate * the two strings as two 8-byte long integers. The ip6_asp_t 540*0Sstevel@tonic-gate * structure guarantees that the labels are 8 byte alligned. 541*0Sstevel@tonic-gate */ 542*0Sstevel@tonic-gate llptr1 = (int64_t *)label1; 543*0Sstevel@tonic-gate llptr2 = (int64_t *)label2; 544*0Sstevel@tonic-gate if (llptr1[0] == llptr2[0] && llptr1[1] == llptr2[1]) 545*0Sstevel@tonic-gate return (B_TRUE); 546*0Sstevel@tonic-gate return (B_FALSE); 547*0Sstevel@tonic-gate } 548