xref: /titanic_52/usr/src/uts/common/inet/ip/ip6_asp.c (revision bd670b35a010421b6e1a5536c34453a827007c81)
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