xref: /onnv-gate/usr/src/uts/common/inet/ip/ip6_asp.c (revision 0:68f95e015346)
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