17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5f4b3ec61Sdh155122 * Common Development and Distribution License (the "License"). 6f4b3ec61Sdh155122 * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 22*bd670b35SErik Nordmark * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate #include <sys/types.h> 277c478bd9Sstevel@tonic-gate #include <sys/socket.h> 287c478bd9Sstevel@tonic-gate #include <sys/ksynch.h> 297c478bd9Sstevel@tonic-gate #include <sys/kmem.h> 307c478bd9Sstevel@tonic-gate #include <sys/errno.h> 317c478bd9Sstevel@tonic-gate #include <sys/systm.h> 327c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h> 337c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h> 347c478bd9Sstevel@tonic-gate #include <sys/strsun.h> 357c478bd9Sstevel@tonic-gate #include <sys/zone.h> 367c478bd9Sstevel@tonic-gate #include <netinet/in.h> 377c478bd9Sstevel@tonic-gate #include <inet/common.h> 387c478bd9Sstevel@tonic-gate #include <inet/ip.h> 397c478bd9Sstevel@tonic-gate #include <inet/ip6.h> 407c478bd9Sstevel@tonic-gate #include <inet/ip6_asp.h> 417c478bd9Sstevel@tonic-gate #include <inet/ip_ire.h> 42*bd670b35SErik Nordmark #include <inet/ip_if.h> 43f4b3ec61Sdh155122 #include <inet/ipclassifier.h> 447c478bd9Sstevel@tonic-gate 457c478bd9Sstevel@tonic-gate #define IN6ADDR_MASK128_INIT \ 467c478bd9Sstevel@tonic-gate { 0xffffffffU, 0xffffffffU, 0xffffffffU, 0xffffffffU } 477c478bd9Sstevel@tonic-gate #define IN6ADDR_MASK96_INIT { 0xffffffffU, 0xffffffffU, 0xffffffffU, 0 } 487c478bd9Sstevel@tonic-gate #ifdef _BIG_ENDIAN 497c478bd9Sstevel@tonic-gate #define IN6ADDR_MASK16_INIT { 0xffff0000U, 0, 0, 0 } 507c478bd9Sstevel@tonic-gate #else 517c478bd9Sstevel@tonic-gate #define IN6ADDR_MASK16_INIT { 0x0000ffffU, 0, 0, 0 } 527c478bd9Sstevel@tonic-gate #endif 537c478bd9Sstevel@tonic-gate 547c478bd9Sstevel@tonic-gate 557c478bd9Sstevel@tonic-gate /* 567c478bd9Sstevel@tonic-gate * This table is ordered such that longest prefix matches are hit first 577c478bd9Sstevel@tonic-gate * (longer prefix lengths first). The last entry must be the "default" 587c478bd9Sstevel@tonic-gate * entry (::0/0). 597c478bd9Sstevel@tonic-gate */ 607c478bd9Sstevel@tonic-gate static ip6_asp_t default_ip6_asp_table[] = { 617c478bd9Sstevel@tonic-gate { IN6ADDR_LOOPBACK_INIT, IN6ADDR_MASK128_INIT, 627c478bd9Sstevel@tonic-gate "Loopback", 50 }, 637c478bd9Sstevel@tonic-gate { IN6ADDR_ANY_INIT, IN6ADDR_MASK96_INIT, 647c478bd9Sstevel@tonic-gate "IPv4_Compatible", 20 }, 657c478bd9Sstevel@tonic-gate #ifdef _BIG_ENDIAN 667c478bd9Sstevel@tonic-gate { { 0, 0, 0x0000ffffU, 0 }, IN6ADDR_MASK96_INIT, 677c478bd9Sstevel@tonic-gate "IPv4", 10 }, 687c478bd9Sstevel@tonic-gate { { 0x20020000U, 0, 0, 0 }, IN6ADDR_MASK16_INIT, 697c478bd9Sstevel@tonic-gate "6to4", 30 }, 707c478bd9Sstevel@tonic-gate #else 717c478bd9Sstevel@tonic-gate { { 0, 0, 0xffff0000U, 0 }, IN6ADDR_MASK96_INIT, 727c478bd9Sstevel@tonic-gate "IPv4", 10 }, 737c478bd9Sstevel@tonic-gate { { 0x00000220U, 0, 0, 0 }, IN6ADDR_MASK16_INIT, 747c478bd9Sstevel@tonic-gate "6to4", 30 }, 757c478bd9Sstevel@tonic-gate #endif 767c478bd9Sstevel@tonic-gate { IN6ADDR_ANY_INIT, IN6ADDR_ANY_INIT, 777c478bd9Sstevel@tonic-gate "Default", 40 } 787c478bd9Sstevel@tonic-gate }; 797c478bd9Sstevel@tonic-gate 807c478bd9Sstevel@tonic-gate /* 817c478bd9Sstevel@tonic-gate * The IPv6 Default Address Selection policy table. 827c478bd9Sstevel@tonic-gate * Until someone up above reconfigures the policy table, use the global 837c478bd9Sstevel@tonic-gate * default. The table needs no lock since the only way to alter it is 847c478bd9Sstevel@tonic-gate * through the SIOCSIP6ADDRPOLICY which is exclusive in ip. 857c478bd9Sstevel@tonic-gate */ 867c478bd9Sstevel@tonic-gate static void ip6_asp_copy(ip6_asp_t *, ip6_asp_t *, uint_t); 87f4b3ec61Sdh155122 static void ip6_asp_check_for_updates(ip_stack_t *); 887c478bd9Sstevel@tonic-gate 897c478bd9Sstevel@tonic-gate void 90f4b3ec61Sdh155122 ip6_asp_init(ip_stack_t *ipst) 917c478bd9Sstevel@tonic-gate { 927c478bd9Sstevel@tonic-gate /* Initialize the table lock */ 93f4b3ec61Sdh155122 mutex_init(&ipst->ips_ip6_asp_lock, NULL, MUTEX_DEFAULT, NULL); 94f4b3ec61Sdh155122 95f4b3ec61Sdh155122 ipst->ips_ip6_asp_table = default_ip6_asp_table; 96f4b3ec61Sdh155122 97f4b3ec61Sdh155122 ipst->ips_ip6_asp_table_count = 98f4b3ec61Sdh155122 sizeof (default_ip6_asp_table) / sizeof (ip6_asp_t); 997c478bd9Sstevel@tonic-gate } 1007c478bd9Sstevel@tonic-gate 1017c478bd9Sstevel@tonic-gate void 102f4b3ec61Sdh155122 ip6_asp_free(ip_stack_t *ipst) 1037c478bd9Sstevel@tonic-gate { 104f4b3ec61Sdh155122 if (ipst->ips_ip6_asp_table != default_ip6_asp_table) { 105f4b3ec61Sdh155122 kmem_free(ipst->ips_ip6_asp_table, 106f4b3ec61Sdh155122 ipst->ips_ip6_asp_table_count * sizeof (ip6_asp_t)); 107f4b3ec61Sdh155122 ipst->ips_ip6_asp_table = NULL; 1087c478bd9Sstevel@tonic-gate } 109f4b3ec61Sdh155122 mutex_destroy(&ipst->ips_ip6_asp_lock); 1107c478bd9Sstevel@tonic-gate } 1117c478bd9Sstevel@tonic-gate 1127c478bd9Sstevel@tonic-gate /* 1137c478bd9Sstevel@tonic-gate * Return false if the table is being updated. Else, increment the ref 1147c478bd9Sstevel@tonic-gate * count and return true. 1157c478bd9Sstevel@tonic-gate */ 1167c478bd9Sstevel@tonic-gate boolean_t 117f4b3ec61Sdh155122 ip6_asp_can_lookup(ip_stack_t *ipst) 1187c478bd9Sstevel@tonic-gate { 119f4b3ec61Sdh155122 mutex_enter(&ipst->ips_ip6_asp_lock); 120f4b3ec61Sdh155122 if (ipst->ips_ip6_asp_uip) { 121f4b3ec61Sdh155122 mutex_exit(&ipst->ips_ip6_asp_lock); 1227c478bd9Sstevel@tonic-gate return (B_FALSE); 1237c478bd9Sstevel@tonic-gate } 124f4b3ec61Sdh155122 IP6_ASP_TABLE_REFHOLD(ipst); 125f4b3ec61Sdh155122 mutex_exit(&ipst->ips_ip6_asp_lock); 1267c478bd9Sstevel@tonic-gate return (B_TRUE); 1277c478bd9Sstevel@tonic-gate 1287c478bd9Sstevel@tonic-gate } 1297c478bd9Sstevel@tonic-gate 1307c478bd9Sstevel@tonic-gate void 1317c478bd9Sstevel@tonic-gate ip6_asp_pending_op(queue_t *q, mblk_t *mp, aspfunc_t func) 1327c478bd9Sstevel@tonic-gate { 133f4b3ec61Sdh155122 conn_t *connp = Q_TO_CONN(q); 134f4b3ec61Sdh155122 ip_stack_t *ipst = connp->conn_netstack->netstack_ip; 1357c478bd9Sstevel@tonic-gate 1367c478bd9Sstevel@tonic-gate ASSERT((mp->b_prev == NULL) && (mp->b_queue == NULL) && 1377c478bd9Sstevel@tonic-gate (mp->b_next == NULL)); 1387c478bd9Sstevel@tonic-gate mp->b_queue = (void *)q; 1397c478bd9Sstevel@tonic-gate mp->b_prev = (void *)func; 1407c478bd9Sstevel@tonic-gate mp->b_next = NULL; 1417c478bd9Sstevel@tonic-gate 142f4b3ec61Sdh155122 mutex_enter(&ipst->ips_ip6_asp_lock); 143f4b3ec61Sdh155122 if (ipst->ips_ip6_asp_pending_ops == NULL) { 144f4b3ec61Sdh155122 ASSERT(ipst->ips_ip6_asp_pending_ops_tail == NULL); 145f4b3ec61Sdh155122 ipst->ips_ip6_asp_pending_ops = 146f4b3ec61Sdh155122 ipst->ips_ip6_asp_pending_ops_tail = mp; 1477c478bd9Sstevel@tonic-gate } else { 148f4b3ec61Sdh155122 ipst->ips_ip6_asp_pending_ops_tail->b_next = mp; 149f4b3ec61Sdh155122 ipst->ips_ip6_asp_pending_ops_tail = mp; 1507c478bd9Sstevel@tonic-gate } 151f4b3ec61Sdh155122 mutex_exit(&ipst->ips_ip6_asp_lock); 1527c478bd9Sstevel@tonic-gate } 1537c478bd9Sstevel@tonic-gate 1547c478bd9Sstevel@tonic-gate static void 155f4b3ec61Sdh155122 ip6_asp_complete_op(ip_stack_t *ipst) 1567c478bd9Sstevel@tonic-gate { 1577c478bd9Sstevel@tonic-gate mblk_t *mp; 1587c478bd9Sstevel@tonic-gate queue_t *q; 1597c478bd9Sstevel@tonic-gate aspfunc_t func; 1607c478bd9Sstevel@tonic-gate 161f4b3ec61Sdh155122 mutex_enter(&ipst->ips_ip6_asp_lock); 162f4b3ec61Sdh155122 while (ipst->ips_ip6_asp_pending_ops != NULL) { 163f4b3ec61Sdh155122 mp = ipst->ips_ip6_asp_pending_ops; 164f4b3ec61Sdh155122 ipst->ips_ip6_asp_pending_ops = mp->b_next; 1657c478bd9Sstevel@tonic-gate mp->b_next = NULL; 166f4b3ec61Sdh155122 if (ipst->ips_ip6_asp_pending_ops == NULL) 167f4b3ec61Sdh155122 ipst->ips_ip6_asp_pending_ops_tail = NULL; 168f4b3ec61Sdh155122 mutex_exit(&ipst->ips_ip6_asp_lock); 1697c478bd9Sstevel@tonic-gate 1707c478bd9Sstevel@tonic-gate q = (queue_t *)mp->b_queue; 1717c478bd9Sstevel@tonic-gate func = (aspfunc_t)mp->b_prev; 1727c478bd9Sstevel@tonic-gate 1737c478bd9Sstevel@tonic-gate mp->b_prev = NULL; 1747c478bd9Sstevel@tonic-gate mp->b_queue = NULL; 1757c478bd9Sstevel@tonic-gate 1767c478bd9Sstevel@tonic-gate 1777c478bd9Sstevel@tonic-gate (*func)(NULL, q, mp, NULL); 178f4b3ec61Sdh155122 mutex_enter(&ipst->ips_ip6_asp_lock); 1797c478bd9Sstevel@tonic-gate } 180f4b3ec61Sdh155122 mutex_exit(&ipst->ips_ip6_asp_lock); 1817c478bd9Sstevel@tonic-gate } 1827c478bd9Sstevel@tonic-gate 1837c478bd9Sstevel@tonic-gate /* 1847c478bd9Sstevel@tonic-gate * Decrement reference count. When it gets to 0, we check for (pending) 1857c478bd9Sstevel@tonic-gate * saved update to the table, if any. 1867c478bd9Sstevel@tonic-gate */ 1877c478bd9Sstevel@tonic-gate void 188f4b3ec61Sdh155122 ip6_asp_table_refrele(ip_stack_t *ipst) 1897c478bd9Sstevel@tonic-gate { 190f4b3ec61Sdh155122 IP6_ASP_TABLE_REFRELE(ipst); 1917c478bd9Sstevel@tonic-gate } 1927c478bd9Sstevel@tonic-gate 1937c478bd9Sstevel@tonic-gate /* 1947c478bd9Sstevel@tonic-gate * This function is guaranteed never to return a NULL pointer. It 1957c478bd9Sstevel@tonic-gate * will always return information from one of the entries in the 1967c478bd9Sstevel@tonic-gate * asp_table (which will never be empty). If a pointer is passed 1977c478bd9Sstevel@tonic-gate * in for the precedence, the precedence value will be set; a 1987c478bd9Sstevel@tonic-gate * pointer to the label will be returned by the function. 1997c478bd9Sstevel@tonic-gate * 2007c478bd9Sstevel@tonic-gate * Since the table is only anticipated to have five or six entries 2017c478bd9Sstevel@tonic-gate * total, the lookup algorithm hasn't been optimized to anything 2027c478bd9Sstevel@tonic-gate * better than O(n). 2037c478bd9Sstevel@tonic-gate */ 2047c478bd9Sstevel@tonic-gate char * 205f4b3ec61Sdh155122 ip6_asp_lookup(const in6_addr_t *addr, uint32_t *precedence, ip_stack_t *ipst) 2067c478bd9Sstevel@tonic-gate { 2077c478bd9Sstevel@tonic-gate ip6_asp_t *aspp; 2087c478bd9Sstevel@tonic-gate ip6_asp_t *match = NULL; 2097c478bd9Sstevel@tonic-gate ip6_asp_t *default_policy; 2107c478bd9Sstevel@tonic-gate 211f4b3ec61Sdh155122 aspp = ipst->ips_ip6_asp_table; 2127c478bd9Sstevel@tonic-gate /* The default entry must always be the last one */ 213f4b3ec61Sdh155122 default_policy = aspp + ipst->ips_ip6_asp_table_count - 1; 2147c478bd9Sstevel@tonic-gate 2157c478bd9Sstevel@tonic-gate while (match == NULL) { 2167c478bd9Sstevel@tonic-gate if (aspp == default_policy) { 2177c478bd9Sstevel@tonic-gate match = aspp; 2187c478bd9Sstevel@tonic-gate } else { 2197c478bd9Sstevel@tonic-gate if (V6_MASK_EQ(*addr, aspp->ip6_asp_mask, 2207c478bd9Sstevel@tonic-gate aspp->ip6_asp_prefix)) 2217c478bd9Sstevel@tonic-gate match = aspp; 2227c478bd9Sstevel@tonic-gate else 2237c478bd9Sstevel@tonic-gate aspp++; 2247c478bd9Sstevel@tonic-gate } 2257c478bd9Sstevel@tonic-gate } 2267c478bd9Sstevel@tonic-gate 2277c478bd9Sstevel@tonic-gate if (precedence != NULL) 2287c478bd9Sstevel@tonic-gate *precedence = match->ip6_asp_precedence; 2297c478bd9Sstevel@tonic-gate return (match->ip6_asp_label); 2307c478bd9Sstevel@tonic-gate } 2317c478bd9Sstevel@tonic-gate 2327c478bd9Sstevel@tonic-gate /* 2337c478bd9Sstevel@tonic-gate * If we had deferred updating the table because of outstanding references, 2347c478bd9Sstevel@tonic-gate * do it now. Note, we don't do error checking on the queued IOCTL mblk, since 2357c478bd9Sstevel@tonic-gate * ip_sioctl_ip6addrpolicy() has already done it for us. 2367c478bd9Sstevel@tonic-gate */ 2377c478bd9Sstevel@tonic-gate void 238f4b3ec61Sdh155122 ip6_asp_check_for_updates(ip_stack_t *ipst) 2397c478bd9Sstevel@tonic-gate { 2407c478bd9Sstevel@tonic-gate ip6_asp_t *table; 2417c478bd9Sstevel@tonic-gate size_t table_size; 2427c478bd9Sstevel@tonic-gate mblk_t *data_mp, *mp; 2437c478bd9Sstevel@tonic-gate struct iocblk *iocp; 2447c478bd9Sstevel@tonic-gate 245f4b3ec61Sdh155122 mutex_enter(&ipst->ips_ip6_asp_lock); 246f4b3ec61Sdh155122 if (ipst->ips_ip6_asp_pending_update == NULL || 247f4b3ec61Sdh155122 ipst->ips_ip6_asp_refcnt > 0) { 248f4b3ec61Sdh155122 mutex_exit(&ipst->ips_ip6_asp_lock); 2497c478bd9Sstevel@tonic-gate return; 2507c478bd9Sstevel@tonic-gate } 2517c478bd9Sstevel@tonic-gate 252f4b3ec61Sdh155122 mp = ipst->ips_ip6_asp_pending_update; 253f4b3ec61Sdh155122 ipst->ips_ip6_asp_pending_update = NULL; 2547c478bd9Sstevel@tonic-gate ASSERT(mp->b_prev != NULL); 2557c478bd9Sstevel@tonic-gate 256f4b3ec61Sdh155122 ipst->ips_ip6_asp_uip = B_TRUE; 2577c478bd9Sstevel@tonic-gate 2587c478bd9Sstevel@tonic-gate iocp = (struct iocblk *)mp->b_rptr; 2597c478bd9Sstevel@tonic-gate data_mp = mp->b_cont; 2607c478bd9Sstevel@tonic-gate if (data_mp == NULL) { 2617c478bd9Sstevel@tonic-gate table = NULL; 2627c478bd9Sstevel@tonic-gate table_size = iocp->ioc_count; 2637c478bd9Sstevel@tonic-gate } else { 2647c478bd9Sstevel@tonic-gate table = (ip6_asp_t *)data_mp->b_rptr; 2657c478bd9Sstevel@tonic-gate table_size = iocp->ioc_count; 2667c478bd9Sstevel@tonic-gate } 2677c478bd9Sstevel@tonic-gate 268f4b3ec61Sdh155122 ip6_asp_replace(mp, table, table_size, B_TRUE, ipst, 2697c478bd9Sstevel@tonic-gate iocp->ioc_flag & IOC_MODELS); 2707c478bd9Sstevel@tonic-gate } 2717c478bd9Sstevel@tonic-gate 2727c478bd9Sstevel@tonic-gate /* 2737c478bd9Sstevel@tonic-gate * ip6_asp_replace replaces the contents of the IPv6 address selection 2747c478bd9Sstevel@tonic-gate * policy table with those specified in new_table. If new_table is NULL, 2757c478bd9Sstevel@tonic-gate * this indicates that the caller wishes ip to use the default policy 2767c478bd9Sstevel@tonic-gate * table. The caller is responsible for making sure that there are exactly 2777c478bd9Sstevel@tonic-gate * new_count policy entries in new_table. 2787c478bd9Sstevel@tonic-gate */ 279f4b3ec61Sdh155122 /*ARGSUSED5*/ 2807c478bd9Sstevel@tonic-gate void 2817c478bd9Sstevel@tonic-gate ip6_asp_replace(mblk_t *mp, ip6_asp_t *new_table, size_t new_size, 282f4b3ec61Sdh155122 boolean_t locked, ip_stack_t *ipst, model_t datamodel) 2837c478bd9Sstevel@tonic-gate { 2847c478bd9Sstevel@tonic-gate int ret_val = 0; 2857c478bd9Sstevel@tonic-gate ip6_asp_t *tmp_table; 2867c478bd9Sstevel@tonic-gate uint_t count; 2877c478bd9Sstevel@tonic-gate queue_t *q; 2887c478bd9Sstevel@tonic-gate struct iocblk *iocp; 2897c478bd9Sstevel@tonic-gate #if defined(_SYSCALL32_IMPL) && _LONG_LONG_ALIGNMENT_32 == 4 2907c478bd9Sstevel@tonic-gate size_t ip6_asp_size = SIZEOF_STRUCT(ip6_asp, datamodel); 2917c478bd9Sstevel@tonic-gate #else 2927c478bd9Sstevel@tonic-gate const size_t ip6_asp_size = sizeof (ip6_asp_t); 2937c478bd9Sstevel@tonic-gate #endif 2947c478bd9Sstevel@tonic-gate 2957c478bd9Sstevel@tonic-gate if (new_size % ip6_asp_size != 0) { 2967c478bd9Sstevel@tonic-gate ip1dbg(("ip6_asp_replace: invalid table size\n")); 2977c478bd9Sstevel@tonic-gate ret_val = EINVAL; 2987c478bd9Sstevel@tonic-gate if (locked) 2997c478bd9Sstevel@tonic-gate goto unlock_end; 3007c478bd9Sstevel@tonic-gate goto replace_end; 3017c478bd9Sstevel@tonic-gate } else { 3027c478bd9Sstevel@tonic-gate count = new_size / ip6_asp_size; 3037c478bd9Sstevel@tonic-gate } 3047c478bd9Sstevel@tonic-gate 3057c478bd9Sstevel@tonic-gate 3067c478bd9Sstevel@tonic-gate if (!locked) 307f4b3ec61Sdh155122 mutex_enter(&ipst->ips_ip6_asp_lock); 3087c478bd9Sstevel@tonic-gate /* 3097c478bd9Sstevel@tonic-gate * Check if we are in the process of creating any IRE using the 3107c478bd9Sstevel@tonic-gate * current information. If so, wait till that is done. 3117c478bd9Sstevel@tonic-gate */ 312f4b3ec61Sdh155122 if (!locked && ipst->ips_ip6_asp_refcnt > 0) { 3137c478bd9Sstevel@tonic-gate /* Save this request for later processing */ 314f4b3ec61Sdh155122 if (ipst->ips_ip6_asp_pending_update == NULL) { 315f4b3ec61Sdh155122 ipst->ips_ip6_asp_pending_update = mp; 3167c478bd9Sstevel@tonic-gate } else { 3177c478bd9Sstevel@tonic-gate /* Let's not queue multiple requests for now */ 3187c478bd9Sstevel@tonic-gate ip1dbg(("ip6_asp_replace: discarding request\n")); 319f4b3ec61Sdh155122 mutex_exit(&ipst->ips_ip6_asp_lock); 3207c478bd9Sstevel@tonic-gate ret_val = EAGAIN; 3217c478bd9Sstevel@tonic-gate goto replace_end; 3227c478bd9Sstevel@tonic-gate } 323f4b3ec61Sdh155122 mutex_exit(&ipst->ips_ip6_asp_lock); 3247c478bd9Sstevel@tonic-gate return; 3257c478bd9Sstevel@tonic-gate } 3267c478bd9Sstevel@tonic-gate 3277c478bd9Sstevel@tonic-gate /* Prevent lookups till the table have been updated */ 3287c478bd9Sstevel@tonic-gate if (!locked) 329f4b3ec61Sdh155122 ipst->ips_ip6_asp_uip = B_TRUE; 3307c478bd9Sstevel@tonic-gate 331f4b3ec61Sdh155122 ASSERT(ipst->ips_ip6_asp_refcnt == 0); 3327c478bd9Sstevel@tonic-gate 3337c478bd9Sstevel@tonic-gate if (new_table == NULL) { 3347c478bd9Sstevel@tonic-gate /* 3357c478bd9Sstevel@tonic-gate * This is a special case. The user wants to revert 3367c478bd9Sstevel@tonic-gate * back to using the default table. 3377c478bd9Sstevel@tonic-gate */ 338f4b3ec61Sdh155122 if (ipst->ips_ip6_asp_table == default_ip6_asp_table) 3397c478bd9Sstevel@tonic-gate goto unlock_end; 3407c478bd9Sstevel@tonic-gate 341f4b3ec61Sdh155122 kmem_free(ipst->ips_ip6_asp_table, 342f4b3ec61Sdh155122 ipst->ips_ip6_asp_table_count * sizeof (ip6_asp_t)); 343f4b3ec61Sdh155122 ipst->ips_ip6_asp_table = default_ip6_asp_table; 344f4b3ec61Sdh155122 ipst->ips_ip6_asp_table_count = 3457c478bd9Sstevel@tonic-gate sizeof (default_ip6_asp_table) / sizeof (ip6_asp_t); 3467c478bd9Sstevel@tonic-gate goto unlock_end; 3477c478bd9Sstevel@tonic-gate } 3487c478bd9Sstevel@tonic-gate 3497c478bd9Sstevel@tonic-gate if (count == 0) { 3507c478bd9Sstevel@tonic-gate ret_val = EINVAL; 3517c478bd9Sstevel@tonic-gate ip1dbg(("ip6_asp_replace: empty table\n")); 3527c478bd9Sstevel@tonic-gate goto unlock_end; 3537c478bd9Sstevel@tonic-gate } 3547c478bd9Sstevel@tonic-gate 3557c478bd9Sstevel@tonic-gate if ((tmp_table = kmem_alloc(count * sizeof (ip6_asp_t), KM_NOSLEEP)) == 3567c478bd9Sstevel@tonic-gate NULL) { 3577c478bd9Sstevel@tonic-gate ret_val = ENOMEM; 3587c478bd9Sstevel@tonic-gate goto unlock_end; 3597c478bd9Sstevel@tonic-gate } 3607c478bd9Sstevel@tonic-gate 3617c478bd9Sstevel@tonic-gate #if defined(_SYSCALL32_IMPL) && _LONG_LONG_ALIGNMENT_32 == 4 3627c478bd9Sstevel@tonic-gate 3637c478bd9Sstevel@tonic-gate /* 3647c478bd9Sstevel@tonic-gate * If 'new_table' -actually- originates from a 32-bit process 3657c478bd9Sstevel@tonic-gate * then the nicely aligned ip6_asp_label array will be 3667c478bd9Sstevel@tonic-gate * subtlely misaligned on this kernel, because the structure 3677c478bd9Sstevel@tonic-gate * is 8 byte aligned in the kernel, but only 4 byte aligned in 3687c478bd9Sstevel@tonic-gate * userland. Fix it up here. 3697c478bd9Sstevel@tonic-gate * 3707c478bd9Sstevel@tonic-gate * XX64 See the notes in ip_sioctl_ip6addrpolicy. Perhaps we could 3717c478bd9Sstevel@tonic-gate * do the datamodel transformation (below) there instead of here? 3727c478bd9Sstevel@tonic-gate */ 3737c478bd9Sstevel@tonic-gate if (datamodel == IOC_ILP32) { 3747c478bd9Sstevel@tonic-gate ip6_asp_t *dst; 3757c478bd9Sstevel@tonic-gate ip6_asp32_t *src; 3767c478bd9Sstevel@tonic-gate int i; 3777c478bd9Sstevel@tonic-gate 3787c478bd9Sstevel@tonic-gate if ((dst = kmem_zalloc(count * sizeof (*dst), 3797c478bd9Sstevel@tonic-gate KM_NOSLEEP)) == NULL) { 3807c478bd9Sstevel@tonic-gate kmem_free(tmp_table, count * sizeof (ip6_asp_t)); 3817c478bd9Sstevel@tonic-gate ret_val = ENOMEM; 3827c478bd9Sstevel@tonic-gate goto unlock_end; 3837c478bd9Sstevel@tonic-gate } 3847c478bd9Sstevel@tonic-gate 3857c478bd9Sstevel@tonic-gate /* 3867c478bd9Sstevel@tonic-gate * Copy each element of the table from ip6_asp32_t 3877c478bd9Sstevel@tonic-gate * format into ip6_asp_t format. Fortunately, since 3887c478bd9Sstevel@tonic-gate * we're just dealing with a trailing structure pad, 3897c478bd9Sstevel@tonic-gate * we can do this straightforwardly with a flurry of 3907c478bd9Sstevel@tonic-gate * bcopying. 3917c478bd9Sstevel@tonic-gate */ 3927c478bd9Sstevel@tonic-gate src = (void *)new_table; 3937c478bd9Sstevel@tonic-gate for (i = 0; i < count; i++) 3947c478bd9Sstevel@tonic-gate bcopy(src + i, dst + i, sizeof (*src)); 3957c478bd9Sstevel@tonic-gate 3967c478bd9Sstevel@tonic-gate ip6_asp_copy(dst, tmp_table, count); 3977c478bd9Sstevel@tonic-gate kmem_free(dst, count * sizeof (*dst)); 3987c478bd9Sstevel@tonic-gate } else 3997c478bd9Sstevel@tonic-gate #endif 4007c478bd9Sstevel@tonic-gate ip6_asp_copy(new_table, tmp_table, count); 4017c478bd9Sstevel@tonic-gate 4027c478bd9Sstevel@tonic-gate /* Make sure the last entry is the default entry */ 4037c478bd9Sstevel@tonic-gate if (!IN6_IS_ADDR_UNSPECIFIED(&tmp_table[count - 1].ip6_asp_prefix) || 4047c478bd9Sstevel@tonic-gate !IN6_IS_ADDR_UNSPECIFIED(&tmp_table[count - 1].ip6_asp_mask)) { 4057c478bd9Sstevel@tonic-gate ret_val = EINVAL; 4067c478bd9Sstevel@tonic-gate kmem_free(tmp_table, count * sizeof (ip6_asp_t)); 4077c478bd9Sstevel@tonic-gate ip1dbg(("ip6_asp_replace: bad table: no default entry\n")); 4087c478bd9Sstevel@tonic-gate goto unlock_end; 4097c478bd9Sstevel@tonic-gate } 410f4b3ec61Sdh155122 if (ipst->ips_ip6_asp_table != default_ip6_asp_table) { 411f4b3ec61Sdh155122 kmem_free(ipst->ips_ip6_asp_table, 412f4b3ec61Sdh155122 ipst->ips_ip6_asp_table_count * sizeof (ip6_asp_t)); 4137c478bd9Sstevel@tonic-gate } 414f4b3ec61Sdh155122 ipst->ips_ip6_asp_table = tmp_table; 415f4b3ec61Sdh155122 ipst->ips_ip6_asp_table_count = count; 4167c478bd9Sstevel@tonic-gate 4177c478bd9Sstevel@tonic-gate unlock_end: 418f4b3ec61Sdh155122 ipst->ips_ip6_asp_uip = B_FALSE; 419f4b3ec61Sdh155122 mutex_exit(&ipst->ips_ip6_asp_lock); 4207c478bd9Sstevel@tonic-gate 421*bd670b35SErik Nordmark /* Let conn_ixa caching know that source address selection changed */ 422*bd670b35SErik Nordmark ip_update_source_selection(ipst); 423*bd670b35SErik Nordmark 4247c478bd9Sstevel@tonic-gate replace_end: 4257c478bd9Sstevel@tonic-gate /* Reply to the ioctl */ 4267c478bd9Sstevel@tonic-gate q = (queue_t *)mp->b_prev; 4277c478bd9Sstevel@tonic-gate mp->b_prev = NULL; 4287c478bd9Sstevel@tonic-gate if (q == NULL) { 4297c478bd9Sstevel@tonic-gate freemsg(mp); 4307c478bd9Sstevel@tonic-gate goto check_binds; 4317c478bd9Sstevel@tonic-gate } 4327c478bd9Sstevel@tonic-gate iocp = (struct iocblk *)mp->b_rptr; 4337c478bd9Sstevel@tonic-gate iocp->ioc_error = ret_val; 4347c478bd9Sstevel@tonic-gate iocp->ioc_count = 0; 4357c478bd9Sstevel@tonic-gate DB_TYPE(mp) = (iocp->ioc_error == 0) ? M_IOCACK : M_IOCNAK; 4367c478bd9Sstevel@tonic-gate qreply(q, mp); 4377c478bd9Sstevel@tonic-gate check_binds: 438f4b3ec61Sdh155122 ip6_asp_complete_op(ipst); 4397c478bd9Sstevel@tonic-gate } 4407c478bd9Sstevel@tonic-gate 4417c478bd9Sstevel@tonic-gate /* 4427c478bd9Sstevel@tonic-gate * Copies the contents of src_table to dst_table, and sorts the 4437c478bd9Sstevel@tonic-gate * entries in decending order of prefix lengths. It assumes that both 4447c478bd9Sstevel@tonic-gate * tables are appropriately sized to contain count entries. 4457c478bd9Sstevel@tonic-gate */ 4467c478bd9Sstevel@tonic-gate static void 4477c478bd9Sstevel@tonic-gate ip6_asp_copy(ip6_asp_t *src_table, ip6_asp_t *dst_table, uint_t count) 4487c478bd9Sstevel@tonic-gate { 4497c478bd9Sstevel@tonic-gate ip6_asp_t *src_ptr, *src_limit, *dst_ptr, *dst_limit, *dp; 4507c478bd9Sstevel@tonic-gate 4517c478bd9Sstevel@tonic-gate dst_table[0] = src_table[0]; 4527c478bd9Sstevel@tonic-gate if (count == 1) 4537c478bd9Sstevel@tonic-gate return; 4547c478bd9Sstevel@tonic-gate 4557c478bd9Sstevel@tonic-gate /* 4567c478bd9Sstevel@tonic-gate * Sort the entries in descending order of prefix lengths. 4577c478bd9Sstevel@tonic-gate * 4587c478bd9Sstevel@tonic-gate * Note: this should be a small table. In 99% of cases, we 4597c478bd9Sstevel@tonic-gate * expect the table to have 5 entries. In the remaining 1% 4607c478bd9Sstevel@tonic-gate * of cases, we expect the table to have one or two more 4617c478bd9Sstevel@tonic-gate * entries. It would be very rare for the table to have 4627c478bd9Sstevel@tonic-gate * double-digit entries. 4637c478bd9Sstevel@tonic-gate */ 4647c478bd9Sstevel@tonic-gate src_limit = src_table + count; 4657c478bd9Sstevel@tonic-gate dst_limit = dst_table + 1; 4667c478bd9Sstevel@tonic-gate for (src_ptr = src_table + 1; src_ptr != src_limit; 4677c478bd9Sstevel@tonic-gate src_ptr++, dst_limit++) { 4687c478bd9Sstevel@tonic-gate for (dst_ptr = dst_table; dst_ptr < dst_limit; dst_ptr++) { 4697c478bd9Sstevel@tonic-gate if (ip_mask_to_plen_v6(&src_ptr->ip6_asp_mask) > 4707c478bd9Sstevel@tonic-gate ip_mask_to_plen_v6(&dst_ptr->ip6_asp_mask)) { 4717c478bd9Sstevel@tonic-gate /* 4727c478bd9Sstevel@tonic-gate * Make room to insert the source entry 4737c478bd9Sstevel@tonic-gate * before dst_ptr by shifting entries to 4747c478bd9Sstevel@tonic-gate * the right. 4757c478bd9Sstevel@tonic-gate */ 4767c478bd9Sstevel@tonic-gate for (dp = dst_limit - 1; dp >= dst_ptr; dp--) 4777c478bd9Sstevel@tonic-gate *(dp + 1) = *dp; 4787c478bd9Sstevel@tonic-gate break; 4797c478bd9Sstevel@tonic-gate } 4807c478bd9Sstevel@tonic-gate } 4817c478bd9Sstevel@tonic-gate *dst_ptr = *src_ptr; 4827c478bd9Sstevel@tonic-gate } 4837c478bd9Sstevel@tonic-gate } 4847c478bd9Sstevel@tonic-gate 4857c478bd9Sstevel@tonic-gate /* 4867c478bd9Sstevel@tonic-gate * This function copies as many entries from ip6_asp_table as will fit 4877c478bd9Sstevel@tonic-gate * into dtable. The dtable_size parameter is the size of dtable 4887c478bd9Sstevel@tonic-gate * in bytes. This function returns the number of entries in 4897c478bd9Sstevel@tonic-gate * ip6_asp_table, even if it's not able to fit all of the entries into 4907c478bd9Sstevel@tonic-gate * dtable. 4917c478bd9Sstevel@tonic-gate */ 4927c478bd9Sstevel@tonic-gate int 493f4b3ec61Sdh155122 ip6_asp_get(ip6_asp_t *dtable, size_t dtable_size, ip_stack_t *ipst) 4947c478bd9Sstevel@tonic-gate { 4957c478bd9Sstevel@tonic-gate uint_t dtable_count; 4967c478bd9Sstevel@tonic-gate 4977c478bd9Sstevel@tonic-gate if (dtable != NULL) { 4987c478bd9Sstevel@tonic-gate if (dtable_size < sizeof (ip6_asp_t)) 4997c478bd9Sstevel@tonic-gate return (-1); 5007c478bd9Sstevel@tonic-gate 5017c478bd9Sstevel@tonic-gate dtable_count = dtable_size / sizeof (ip6_asp_t); 502f4b3ec61Sdh155122 bcopy(ipst->ips_ip6_asp_table, dtable, 503f4b3ec61Sdh155122 MIN(ipst->ips_ip6_asp_table_count, dtable_count) * 5047c478bd9Sstevel@tonic-gate sizeof (ip6_asp_t)); 5057c478bd9Sstevel@tonic-gate } 5067c478bd9Sstevel@tonic-gate 507f4b3ec61Sdh155122 return (ipst->ips_ip6_asp_table_count); 5087c478bd9Sstevel@tonic-gate } 5097c478bd9Sstevel@tonic-gate 5107c478bd9Sstevel@tonic-gate /* 5117c478bd9Sstevel@tonic-gate * Compare two labels. Return B_TRUE if they are equal, B_FALSE 5127c478bd9Sstevel@tonic-gate * otherwise. 5137c478bd9Sstevel@tonic-gate */ 5147c478bd9Sstevel@tonic-gate boolean_t 5157c478bd9Sstevel@tonic-gate ip6_asp_labelcmp(const char *label1, const char *label2) 5167c478bd9Sstevel@tonic-gate { 5177c478bd9Sstevel@tonic-gate int64_t *llptr1, *llptr2; 5187c478bd9Sstevel@tonic-gate 5197c478bd9Sstevel@tonic-gate /* 5207c478bd9Sstevel@tonic-gate * The common case, the two labels are actually the same string 5217c478bd9Sstevel@tonic-gate * from the policy table. 5227c478bd9Sstevel@tonic-gate */ 5237c478bd9Sstevel@tonic-gate if (label1 == label2) 5247c478bd9Sstevel@tonic-gate return (B_TRUE); 5257c478bd9Sstevel@tonic-gate 5267c478bd9Sstevel@tonic-gate /* 5277c478bd9Sstevel@tonic-gate * Since we know the labels are at most 16 bytes long, compare 5287c478bd9Sstevel@tonic-gate * the two strings as two 8-byte long integers. The ip6_asp_t 5297c478bd9Sstevel@tonic-gate * structure guarantees that the labels are 8 byte alligned. 5307c478bd9Sstevel@tonic-gate */ 5317c478bd9Sstevel@tonic-gate llptr1 = (int64_t *)label1; 5327c478bd9Sstevel@tonic-gate llptr2 = (int64_t *)label2; 5337c478bd9Sstevel@tonic-gate if (llptr1[0] == llptr2[0] && llptr1[1] == llptr2[1]) 5347c478bd9Sstevel@tonic-gate return (B_TRUE); 5357c478bd9Sstevel@tonic-gate return (B_FALSE); 5367c478bd9Sstevel@tonic-gate } 537