xref: /illumos-gate/usr/src/uts/common/os/labelsys.c (revision 45916cd2)
1*45916cd2Sjpk /*
2*45916cd2Sjpk  * CDDL HEADER START
3*45916cd2Sjpk  *
4*45916cd2Sjpk  * The contents of this file are subject to the terms of the
5*45916cd2Sjpk  * Common Development and Distribution License (the "License").
6*45916cd2Sjpk  * You may not use this file except in compliance with the License.
7*45916cd2Sjpk  *
8*45916cd2Sjpk  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*45916cd2Sjpk  * or http://www.opensolaris.org/os/licensing.
10*45916cd2Sjpk  * See the License for the specific language governing permissions
11*45916cd2Sjpk  * and limitations under the License.
12*45916cd2Sjpk  *
13*45916cd2Sjpk  * When distributing Covered Code, include this CDDL HEADER in each
14*45916cd2Sjpk  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*45916cd2Sjpk  * If applicable, add the following below this CDDL HEADER, with the
16*45916cd2Sjpk  * fields enclosed by brackets "[]" replaced with your own identifying
17*45916cd2Sjpk  * information: Portions Copyright [yyyy] [name of copyright owner]
18*45916cd2Sjpk  *
19*45916cd2Sjpk  * CDDL HEADER END
20*45916cd2Sjpk  */
21*45916cd2Sjpk /*
22*45916cd2Sjpk  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23*45916cd2Sjpk  * Use is subject to license terms.
24*45916cd2Sjpk  */
25*45916cd2Sjpk 
26*45916cd2Sjpk #pragma ident	"%Z%%M%	%I%	%E% SMI"
27*45916cd2Sjpk 
28*45916cd2Sjpk #include <sys/systm.h>
29*45916cd2Sjpk #include <sys/types.h>
30*45916cd2Sjpk #include <sys/stream.h>
31*45916cd2Sjpk #include <sys/kmem.h>
32*45916cd2Sjpk #include <sys/strsubr.h>
33*45916cd2Sjpk #include <sys/cmn_err.h>
34*45916cd2Sjpk #include <sys/debug.h>
35*45916cd2Sjpk #include <sys/param.h>
36*45916cd2Sjpk #include <sys/model.h>
37*45916cd2Sjpk #include <sys/errno.h>
38*45916cd2Sjpk #include <sys/modhash.h>
39*45916cd2Sjpk 
40*45916cd2Sjpk #include <sys/policy.h>
41*45916cd2Sjpk #include <sys/tsol/label.h>
42*45916cd2Sjpk #include <sys/tsol/tsyscall.h>
43*45916cd2Sjpk #include <sys/tsol/tndb.h>
44*45916cd2Sjpk #include <sys/tsol/tnet.h>
45*45916cd2Sjpk #include <sys/disp.h>
46*45916cd2Sjpk 
47*45916cd2Sjpk #include <inet/ip.h>
48*45916cd2Sjpk #include <inet/ip6.h>
49*45916cd2Sjpk #include <sys/sdt.h>
50*45916cd2Sjpk 
51*45916cd2Sjpk static mod_hash_t *tpc_name_hash;	/* hash of cache entries by name */
52*45916cd2Sjpk static kmutex_t tpc_lock;
53*45916cd2Sjpk 
54*45916cd2Sjpk static tsol_tpc_t *tpc_unlab;
55*45916cd2Sjpk 
56*45916cd2Sjpk /*
57*45916cd2Sjpk  * tnrhc_table and tnrhc_table_v6 are similar to the IP forwarding tables
58*45916cd2Sjpk  * in organization and search. The tnrhc_table[_v6] is an array of 33/129
59*45916cd2Sjpk  * pointers to the 33/129 tnrhc tables indexed by the prefix length.
60*45916cd2Sjpk  * A largest prefix match search is done by find_rhc_v[46] and it walks the
61*45916cd2Sjpk  * tables from the most specific to the least specific table. Table 0
62*45916cd2Sjpk  * corresponds to the single entry for 0.0.0.0/0 or ::0/0.
63*45916cd2Sjpk  */
64*45916cd2Sjpk tnrhc_hash_t *tnrhc_table[TSOL_MASK_TABLE_SIZE];
65*45916cd2Sjpk tnrhc_hash_t *tnrhc_table_v6[TSOL_MASK_TABLE_SIZE_V6];
66*45916cd2Sjpk kmutex_t tnrhc_g_lock;
67*45916cd2Sjpk 
68*45916cd2Sjpk static void tsol_create_i_tmpls(void);
69*45916cd2Sjpk 
70*45916cd2Sjpk static void tsol_create_i_tnrh(const tnaddr_t *);
71*45916cd2Sjpk 
72*45916cd2Sjpk /* List of MLPs on valid on shared addresses */
73*45916cd2Sjpk static tsol_mlp_list_t shared_mlps;
74*45916cd2Sjpk 
75*45916cd2Sjpk /*
76*45916cd2Sjpk  * Convert length for a mask to the mask.
77*45916cd2Sjpk  */
78*45916cd2Sjpk static ipaddr_t
79*45916cd2Sjpk tsol_plen_to_mask(uint_t masklen)
80*45916cd2Sjpk {
81*45916cd2Sjpk 	return (masklen == 0 ? 0 : htonl(IP_HOST_MASK << (IP_ABITS - masklen)));
82*45916cd2Sjpk }
83*45916cd2Sjpk 
84*45916cd2Sjpk /*
85*45916cd2Sjpk  * Convert a prefix length to the mask for that prefix.
86*45916cd2Sjpk  * Returns the argument bitmask.
87*45916cd2Sjpk  */
88*45916cd2Sjpk static void
89*45916cd2Sjpk tsol_plen_to_mask_v6(uint_t plen, in6_addr_t *bitmask)
90*45916cd2Sjpk {
91*45916cd2Sjpk 	uint32_t *ptr;
92*45916cd2Sjpk 
93*45916cd2Sjpk 	ASSERT(plen <= IPV6_ABITS);
94*45916cd2Sjpk 
95*45916cd2Sjpk 	ptr = (uint32_t *)bitmask;
96*45916cd2Sjpk 	while (plen >= 32) {
97*45916cd2Sjpk 		*ptr++ = 0xffffffffU;
98*45916cd2Sjpk 		plen -= 32;
99*45916cd2Sjpk 	}
100*45916cd2Sjpk 	if (plen > 0)
101*45916cd2Sjpk 		*ptr++ = htonl(0xffffffff << (32 - plen));
102*45916cd2Sjpk 	while (ptr < (uint32_t *)(bitmask + 1))
103*45916cd2Sjpk 		*ptr++ = 0;
104*45916cd2Sjpk }
105*45916cd2Sjpk 
106*45916cd2Sjpk boolean_t
107*45916cd2Sjpk tnrhc_init_table(tnrhc_hash_t *table[], short prefix_len, int kmflag)
108*45916cd2Sjpk {
109*45916cd2Sjpk 	int	i;
110*45916cd2Sjpk 
111*45916cd2Sjpk 	mutex_enter(&tnrhc_g_lock);
112*45916cd2Sjpk 
113*45916cd2Sjpk 	if (table[prefix_len] == NULL) {
114*45916cd2Sjpk 		table[prefix_len] = (tnrhc_hash_t *)
115*45916cd2Sjpk 		    kmem_zalloc(TNRHC_SIZE * sizeof (tnrhc_hash_t), kmflag);
116*45916cd2Sjpk 		if (table[prefix_len] == NULL) {
117*45916cd2Sjpk 			mutex_exit(&tnrhc_g_lock);
118*45916cd2Sjpk 			return (B_FALSE);
119*45916cd2Sjpk 		}
120*45916cd2Sjpk 		for (i = 0; i < TNRHC_SIZE; i++) {
121*45916cd2Sjpk 			mutex_init(&table[prefix_len][i].tnrh_lock,
122*45916cd2Sjpk 			    NULL, MUTEX_DEFAULT, 0);
123*45916cd2Sjpk 		}
124*45916cd2Sjpk 	}
125*45916cd2Sjpk 	mutex_exit(&tnrhc_g_lock);
126*45916cd2Sjpk 	return (B_TRUE);
127*45916cd2Sjpk }
128*45916cd2Sjpk 
129*45916cd2Sjpk void
130*45916cd2Sjpk tcache_init(void)
131*45916cd2Sjpk {
132*45916cd2Sjpk 	tnaddr_t address;
133*45916cd2Sjpk 
134*45916cd2Sjpk 	/*
135*45916cd2Sjpk 	 * Note: unable to use mod_hash_create_strhash here, since it's
136*45916cd2Sjpk 	 * assymetric.  It assumes that the user has allocated exactly
137*45916cd2Sjpk 	 * strlen(key) + 1 bytes for the key when inserted, and attempts to
138*45916cd2Sjpk 	 * kmem_free that memory on a delete.
139*45916cd2Sjpk 	 */
140*45916cd2Sjpk 	tpc_name_hash = mod_hash_create_extended("tnrhtpc_by_name", 256,
141*45916cd2Sjpk 	    mod_hash_null_keydtor,  mod_hash_null_valdtor, mod_hash_bystr,
142*45916cd2Sjpk 	    NULL, mod_hash_strkey_cmp, KM_SLEEP);
143*45916cd2Sjpk 	mutex_init(&tpc_lock, NULL, MUTEX_DEFAULT, NULL);
144*45916cd2Sjpk 
145*45916cd2Sjpk 	mutex_init(&tnrhc_g_lock, NULL, MUTEX_DEFAULT, NULL);
146*45916cd2Sjpk 
147*45916cd2Sjpk 	/* label_init always called before tcache_init */
148*45916cd2Sjpk 	ASSERT(l_admin_low != NULL && l_admin_high != NULL);
149*45916cd2Sjpk 
150*45916cd2Sjpk 	/* Initialize the zeroth table prior to loading the 0.0.0.0 entry */
151*45916cd2Sjpk 	(void) tnrhc_init_table(tnrhc_table, 0, KM_SLEEP);
152*45916cd2Sjpk 	(void) tnrhc_init_table(tnrhc_table_v6, 0, KM_SLEEP);
153*45916cd2Sjpk 	/*
154*45916cd2Sjpk 	 * create an internal host template called "_unlab"
155*45916cd2Sjpk 	 */
156*45916cd2Sjpk 	tsol_create_i_tmpls();
157*45916cd2Sjpk 
158*45916cd2Sjpk 	/*
159*45916cd2Sjpk 	 * create a host entry, 0.0.0.0 = _unlab
160*45916cd2Sjpk 	 */
161*45916cd2Sjpk 	bzero(&address, sizeof (tnaddr_t));
162*45916cd2Sjpk 	address.ta_family = AF_INET;
163*45916cd2Sjpk 	tsol_create_i_tnrh(&address);
164*45916cd2Sjpk 
165*45916cd2Sjpk 	/*
166*45916cd2Sjpk 	 * create a host entry, ::0 = _unlab
167*45916cd2Sjpk 	 */
168*45916cd2Sjpk 	address.ta_family = AF_INET6;
169*45916cd2Sjpk 	tsol_create_i_tnrh(&address);
170*45916cd2Sjpk 
171*45916cd2Sjpk 	rw_init(&shared_mlps.mlpl_rwlock, NULL, RW_DEFAULT, NULL);
172*45916cd2Sjpk }
173*45916cd2Sjpk 
174*45916cd2Sjpk /* Called only by the TNRHC_RELE macro when the refcount goes to zero. */
175*45916cd2Sjpk void
176*45916cd2Sjpk tnrhc_free(tsol_tnrhc_t *tnrhc)
177*45916cd2Sjpk {
178*45916cd2Sjpk 	/*
179*45916cd2Sjpk 	 * We assert rhc_invalid here to make sure that no new thread could
180*45916cd2Sjpk 	 * possibly end up finding this entry.  If it could, then the
181*45916cd2Sjpk 	 * mutex_destroy would panic.
182*45916cd2Sjpk 	 */
183*45916cd2Sjpk 	DTRACE_PROBE1(tx__tndb__l3__tnrhcfree, tsol_tnrhc_t *, tnrhc);
184*45916cd2Sjpk 	ASSERT(tnrhc->rhc_next == NULL && tnrhc->rhc_invalid);
185*45916cd2Sjpk 	mutex_exit(&tnrhc->rhc_lock);
186*45916cd2Sjpk 	mutex_destroy(&tnrhc->rhc_lock);
187*45916cd2Sjpk 	if (tnrhc->rhc_tpc != NULL)
188*45916cd2Sjpk 		TPC_RELE(tnrhc->rhc_tpc);
189*45916cd2Sjpk 	kmem_free(tnrhc, sizeof (*tnrhc));
190*45916cd2Sjpk }
191*45916cd2Sjpk 
192*45916cd2Sjpk /* Called only by the TPC_RELE macro when the refcount goes to zero. */
193*45916cd2Sjpk void
194*45916cd2Sjpk tpc_free(tsol_tpc_t *tpc)
195*45916cd2Sjpk {
196*45916cd2Sjpk 	DTRACE_PROBE1(tx__tndb__l3__tpcfree, tsol_tpc_t *, tpc);
197*45916cd2Sjpk 	ASSERT(tpc->tpc_invalid);
198*45916cd2Sjpk 	mutex_exit(&tpc->tpc_lock);
199*45916cd2Sjpk 	mutex_destroy(&tpc->tpc_lock);
200*45916cd2Sjpk 	kmem_free(tpc, sizeof (*tpc));
201*45916cd2Sjpk }
202*45916cd2Sjpk 
203*45916cd2Sjpk /*
204*45916cd2Sjpk  * Find and hold a reference to a template entry by name.  Ignores entries that
205*45916cd2Sjpk  * are being deleted.
206*45916cd2Sjpk  */
207*45916cd2Sjpk static tsol_tpc_t *
208*45916cd2Sjpk tnrhtp_find(const char *name, mod_hash_t *hash)
209*45916cd2Sjpk {
210*45916cd2Sjpk 	mod_hash_val_t hv;
211*45916cd2Sjpk 	tsol_tpc_t *tpc = NULL;
212*45916cd2Sjpk 
213*45916cd2Sjpk 	mutex_enter(&tpc_lock);
214*45916cd2Sjpk 	if (mod_hash_find(hash, (mod_hash_key_t)name, &hv) == 0) {
215*45916cd2Sjpk 		tpc = (tsol_tpc_t *)hv;
216*45916cd2Sjpk 		if (tpc->tpc_invalid)
217*45916cd2Sjpk 			tpc = NULL;
218*45916cd2Sjpk 		else
219*45916cd2Sjpk 			TPC_HOLD(tpc);
220*45916cd2Sjpk 	}
221*45916cd2Sjpk 	mutex_exit(&tpc_lock);
222*45916cd2Sjpk 	return (tpc);
223*45916cd2Sjpk }
224*45916cd2Sjpk 
225*45916cd2Sjpk static int
226*45916cd2Sjpk tnrh_delete(const tsol_rhent_t *rhent)
227*45916cd2Sjpk {
228*45916cd2Sjpk 	tsol_tnrhc_t *current;
229*45916cd2Sjpk 	tsol_tnrhc_t **prevp;
230*45916cd2Sjpk 	ipaddr_t tmpmask;
231*45916cd2Sjpk 	in6_addr_t tmpmask_v6;
232*45916cd2Sjpk 	tnrhc_hash_t *tnrhc_hash;
233*45916cd2Sjpk 
234*45916cd2Sjpk 	if (rhent->rh_address.ta_family == AF_INET) {
235*45916cd2Sjpk 		if (rhent->rh_prefix < 0 || rhent->rh_prefix > IP_ABITS)
236*45916cd2Sjpk 			return (EINVAL);
237*45916cd2Sjpk 		if (tnrhc_table[rhent->rh_prefix] == NULL)
238*45916cd2Sjpk 			return (ENOENT);
239*45916cd2Sjpk 		tmpmask = tsol_plen_to_mask(rhent->rh_prefix);
240*45916cd2Sjpk 		tnrhc_hash = &tnrhc_table[rhent->rh_prefix][
241*45916cd2Sjpk 		    TSOL_ADDR_HASH(rhent->rh_address.ta_addr_v4.s_addr &
242*45916cd2Sjpk 		    tmpmask, TNRHC_SIZE)];
243*45916cd2Sjpk 	} else if (rhent->rh_address.ta_family == AF_INET6) {
244*45916cd2Sjpk 		if (rhent->rh_prefix < 0 || rhent->rh_prefix > IPV6_ABITS)
245*45916cd2Sjpk 			return (EINVAL);
246*45916cd2Sjpk 		if (tnrhc_table_v6[rhent->rh_prefix] == NULL)
247*45916cd2Sjpk 			return (ENOENT);
248*45916cd2Sjpk 		tsol_plen_to_mask_v6(rhent->rh_prefix, &tmpmask_v6);
249*45916cd2Sjpk 		tnrhc_hash = &tnrhc_table_v6[rhent->rh_prefix][
250*45916cd2Sjpk 		    TSOL_ADDR_MASK_HASH_V6(rhent->rh_address.ta_addr_v6,
251*45916cd2Sjpk 		    tmpmask_v6, TNRHC_SIZE)];
252*45916cd2Sjpk 	} else {
253*45916cd2Sjpk 		return (EAFNOSUPPORT);
254*45916cd2Sjpk 	}
255*45916cd2Sjpk 
256*45916cd2Sjpk 	/* search for existing entry */
257*45916cd2Sjpk 	mutex_enter(&tnrhc_hash->tnrh_lock);
258*45916cd2Sjpk 	prevp = &tnrhc_hash->tnrh_list;
259*45916cd2Sjpk 	while ((current = *prevp) != NULL) {
260*45916cd2Sjpk 		if (TNADDR_EQ(&rhent->rh_address, &current->rhc_host))
261*45916cd2Sjpk 			break;
262*45916cd2Sjpk 		prevp = &current->rhc_next;
263*45916cd2Sjpk 	}
264*45916cd2Sjpk 
265*45916cd2Sjpk 	if (current != NULL) {
266*45916cd2Sjpk 		DTRACE_PROBE(tx__tndb__l2__tnrhdelete_existingrhentry);
267*45916cd2Sjpk 		*prevp = current->rhc_next;
268*45916cd2Sjpk 		mutex_enter(&current->rhc_lock);
269*45916cd2Sjpk 		current->rhc_next = NULL;
270*45916cd2Sjpk 		current->rhc_invalid = 1;
271*45916cd2Sjpk 		mutex_exit(&current->rhc_lock);
272*45916cd2Sjpk 		TNRHC_RELE(current);
273*45916cd2Sjpk 	}
274*45916cd2Sjpk 	mutex_exit(&tnrhc_hash->tnrh_lock);
275*45916cd2Sjpk 	return (current == NULL ? ENOENT : 0);
276*45916cd2Sjpk }
277*45916cd2Sjpk 
278*45916cd2Sjpk /*
279*45916cd2Sjpk  * Flush all remote host entries from the database.
280*45916cd2Sjpk  *
281*45916cd2Sjpk  * Note that the htable arrays themselves do not have reference counters, so,
282*45916cd2Sjpk  * unlike the remote host entries, they cannot be freed.
283*45916cd2Sjpk  */
284*45916cd2Sjpk static void
285*45916cd2Sjpk flush_rh_table(tnrhc_hash_t **htable, int nbits)
286*45916cd2Sjpk {
287*45916cd2Sjpk 	tnrhc_hash_t *hent, *hend;
288*45916cd2Sjpk 	tsol_tnrhc_t *rhc, *rhnext;
289*45916cd2Sjpk 
290*45916cd2Sjpk 	while (--nbits >= 0) {
291*45916cd2Sjpk 		if ((hent = htable[nbits]) == NULL)
292*45916cd2Sjpk 			continue;
293*45916cd2Sjpk 		hend = hent + TNRHC_SIZE;
294*45916cd2Sjpk 		while (hent < hend) {
295*45916cd2Sjpk 			/*
296*45916cd2Sjpk 			 * List walkers hold this lock during the walk.  It
297*45916cd2Sjpk 			 * protects tnrh_list and rhc_next.
298*45916cd2Sjpk 			 */
299*45916cd2Sjpk 			mutex_enter(&hent->tnrh_lock);
300*45916cd2Sjpk 			rhnext = hent->tnrh_list;
301*45916cd2Sjpk 			hent->tnrh_list = NULL;
302*45916cd2Sjpk 			mutex_exit(&hent->tnrh_lock);
303*45916cd2Sjpk 			/*
304*45916cd2Sjpk 			 * There may still be users of the rhcs at this point,
305*45916cd2Sjpk 			 * but not of the list or its next pointer.  Thus, the
306*45916cd2Sjpk 			 * only thing that would need to be done under a lock
307*45916cd2Sjpk 			 * is setting the invalid bit, but that's atomic
308*45916cd2Sjpk 			 * anyway, so no locks needed here.
309*45916cd2Sjpk 			 */
310*45916cd2Sjpk 			while ((rhc = rhnext) != NULL) {
311*45916cd2Sjpk 				rhnext = rhc->rhc_next;
312*45916cd2Sjpk 				rhc->rhc_next = NULL;
313*45916cd2Sjpk 				rhc->rhc_invalid = 1;
314*45916cd2Sjpk 				TNRHC_RELE(rhc);
315*45916cd2Sjpk 			}
316*45916cd2Sjpk 			hent++;
317*45916cd2Sjpk 		}
318*45916cd2Sjpk 	}
319*45916cd2Sjpk }
320*45916cd2Sjpk 
321*45916cd2Sjpk /*
322*45916cd2Sjpk  * Load a remote host entry into kernel cache.  Create a new one if a matching
323*45916cd2Sjpk  * entry isn't found, otherwise replace the contents of the previous one by
324*45916cd2Sjpk  * deleting it and recreating it.  (Delete and recreate is used to avoid
325*45916cd2Sjpk  * allowing other threads to see an unstable data structure.)
326*45916cd2Sjpk  *
327*45916cd2Sjpk  * A "matching" entry is the one whose address matches that of the one
328*45916cd2Sjpk  * being loaded.
329*45916cd2Sjpk  *
330*45916cd2Sjpk  * Return 0 for success, error code for failure.
331*45916cd2Sjpk  */
332*45916cd2Sjpk int
333*45916cd2Sjpk tnrh_load(const tsol_rhent_t *rhent)
334*45916cd2Sjpk {
335*45916cd2Sjpk 	tsol_tnrhc_t **rhp;
336*45916cd2Sjpk 	tsol_tnrhc_t *rh, *new;
337*45916cd2Sjpk 	tsol_tpc_t *tpc;
338*45916cd2Sjpk 	ipaddr_t tmpmask;
339*45916cd2Sjpk 	in6_addr_t tmpmask_v6;
340*45916cd2Sjpk 	tnrhc_hash_t *tnrhc_hash;
341*45916cd2Sjpk 
342*45916cd2Sjpk 	/* Find the existing entry, if any, leaving the hash locked */
343*45916cd2Sjpk 	if (rhent->rh_address.ta_family == AF_INET) {
344*45916cd2Sjpk 		if (rhent->rh_prefix < 0 || rhent->rh_prefix > IP_ABITS)
345*45916cd2Sjpk 			return (EINVAL);
346*45916cd2Sjpk 		if (tnrhc_table[rhent->rh_prefix] == NULL &&
347*45916cd2Sjpk 		    !tnrhc_init_table(tnrhc_table, rhent->rh_prefix,
348*45916cd2Sjpk 		    KM_NOSLEEP))
349*45916cd2Sjpk 			return (ENOMEM);
350*45916cd2Sjpk 		tmpmask = tsol_plen_to_mask(rhent->rh_prefix);
351*45916cd2Sjpk 		tnrhc_hash = &tnrhc_table[rhent->rh_prefix][
352*45916cd2Sjpk 		    TSOL_ADDR_HASH(rhent->rh_address.ta_addr_v4.s_addr &
353*45916cd2Sjpk 		    tmpmask, TNRHC_SIZE)];
354*45916cd2Sjpk 		mutex_enter(&tnrhc_hash->tnrh_lock);
355*45916cd2Sjpk 		for (rhp = &tnrhc_hash->tnrh_list; (rh = *rhp) != NULL;
356*45916cd2Sjpk 		    rhp = &rh->rhc_next) {
357*45916cd2Sjpk 			ASSERT(rh->rhc_host.ta_family == AF_INET);
358*45916cd2Sjpk 			if (((rh->rhc_host.ta_addr_v4.s_addr ^
359*45916cd2Sjpk 			    rhent->rh_address.ta_addr_v4.s_addr) & tmpmask) ==
360*45916cd2Sjpk 			    0)
361*45916cd2Sjpk 				break;
362*45916cd2Sjpk 		}
363*45916cd2Sjpk 	} else if (rhent->rh_address.ta_family == AF_INET6) {
364*45916cd2Sjpk 		if (rhent->rh_prefix < 0 || rhent->rh_prefix > IPV6_ABITS)
365*45916cd2Sjpk 			return (EINVAL);
366*45916cd2Sjpk 		if (tnrhc_table_v6[rhent->rh_prefix] == NULL &&
367*45916cd2Sjpk 		    !tnrhc_init_table(tnrhc_table_v6, rhent->rh_prefix,
368*45916cd2Sjpk 		    KM_NOSLEEP))
369*45916cd2Sjpk 			return (ENOMEM);
370*45916cd2Sjpk 		tsol_plen_to_mask_v6(rhent->rh_prefix, &tmpmask_v6);
371*45916cd2Sjpk 		tnrhc_hash = &tnrhc_table_v6[rhent->rh_prefix][
372*45916cd2Sjpk 		    TSOL_ADDR_MASK_HASH_V6(rhent->rh_address.ta_addr_v6,
373*45916cd2Sjpk 		    tmpmask_v6, TNRHC_SIZE)];
374*45916cd2Sjpk 		mutex_enter(&tnrhc_hash->tnrh_lock);
375*45916cd2Sjpk 		for (rhp = &tnrhc_hash->tnrh_list; (rh = *rhp) != NULL;
376*45916cd2Sjpk 		    rhp = &rh->rhc_next) {
377*45916cd2Sjpk 			ASSERT(rh->rhc_host.ta_family == AF_INET6);
378*45916cd2Sjpk 			if (V6_MASK_EQ_2(rh->rhc_host.ta_addr_v6, tmpmask_v6,
379*45916cd2Sjpk 			    rhent->rh_address.ta_addr_v6))
380*45916cd2Sjpk 				break;
381*45916cd2Sjpk 		}
382*45916cd2Sjpk 	} else {
383*45916cd2Sjpk 		return (EAFNOSUPPORT);
384*45916cd2Sjpk 	}
385*45916cd2Sjpk 
386*45916cd2Sjpk 	if ((new = kmem_zalloc(sizeof (*new), KM_NOSLEEP)) == NULL) {
387*45916cd2Sjpk 		mutex_exit(&tnrhc_hash->tnrh_lock);
388*45916cd2Sjpk 		return (ENOMEM);
389*45916cd2Sjpk 	}
390*45916cd2Sjpk 
391*45916cd2Sjpk 	/* Find and bump the reference count on the named template */
392*45916cd2Sjpk 	if ((tpc = tnrhtp_find(rhent->rh_template, tpc_name_hash)) == NULL) {
393*45916cd2Sjpk 		mutex_exit(&tnrhc_hash->tnrh_lock);
394*45916cd2Sjpk 		kmem_free(new, sizeof (*new));
395*45916cd2Sjpk 		return (EINVAL);
396*45916cd2Sjpk 	}
397*45916cd2Sjpk 
398*45916cd2Sjpk 	/* Clobber the old remote host entry. */
399*45916cd2Sjpk 	if (rh != NULL) {
400*45916cd2Sjpk 		ASSERT(!rh->rhc_invalid);
401*45916cd2Sjpk 		rh->rhc_invalid = 1;
402*45916cd2Sjpk 		*rhp = rh->rhc_next;
403*45916cd2Sjpk 		rh->rhc_next = NULL;
404*45916cd2Sjpk 		TNRHC_RELE(rh);
405*45916cd2Sjpk 	}
406*45916cd2Sjpk 
407*45916cd2Sjpk 	/* Initialize the new entry. */
408*45916cd2Sjpk 	mutex_init(&new->rhc_lock, NULL, MUTEX_DEFAULT, NULL);
409*45916cd2Sjpk 	new->rhc_host = rhent->rh_address;
410*45916cd2Sjpk 
411*45916cd2Sjpk 	/* The rhc now owns this tpc reference, so no TPC_RELE past here */
412*45916cd2Sjpk 	new->rhc_tpc = tpc;
413*45916cd2Sjpk 
414*45916cd2Sjpk 	ASSERT(tpc->tpc_tp.host_type == UNLABELED ||
415*45916cd2Sjpk 	    tpc->tpc_tp.host_type == SUN_CIPSO);
416*45916cd2Sjpk 
417*45916cd2Sjpk 	TNRHC_HOLD(new);
418*45916cd2Sjpk 	new->rhc_next = tnrhc_hash->tnrh_list;
419*45916cd2Sjpk 	tnrhc_hash->tnrh_list = new;
420*45916cd2Sjpk 	DTRACE_PROBE(tx__tndb__l2__tnrhload__addedrh);
421*45916cd2Sjpk 	mutex_exit(&tnrhc_hash->tnrh_lock);
422*45916cd2Sjpk 
423*45916cd2Sjpk 	return (0);
424*45916cd2Sjpk }
425*45916cd2Sjpk 
426*45916cd2Sjpk static int
427*45916cd2Sjpk tnrh_get(tsol_rhent_t *rhent)
428*45916cd2Sjpk {
429*45916cd2Sjpk 	tsol_tpc_t *tpc;
430*45916cd2Sjpk 
431*45916cd2Sjpk 	switch (rhent->rh_address.ta_family) {
432*45916cd2Sjpk 	case AF_INET:
433*45916cd2Sjpk 		tpc = find_tpc(&rhent->rh_address.ta_addr_v4, IPV4_VERSION,
434*45916cd2Sjpk 		    B_TRUE);
435*45916cd2Sjpk 		break;
436*45916cd2Sjpk 
437*45916cd2Sjpk 	case AF_INET6:
438*45916cd2Sjpk 		tpc = find_tpc(&rhent->rh_address.ta_addr_v6, IPV6_VERSION,
439*45916cd2Sjpk 		    B_TRUE);
440*45916cd2Sjpk 		break;
441*45916cd2Sjpk 
442*45916cd2Sjpk 	default:
443*45916cd2Sjpk 		return (EINVAL);
444*45916cd2Sjpk 	}
445*45916cd2Sjpk 	if (tpc == NULL)
446*45916cd2Sjpk 		return (ENOENT);
447*45916cd2Sjpk 
448*45916cd2Sjpk 	DTRACE_PROBE2(tx__tndb__l4__tnrhget__foundtpc, tsol_rhent_t *,
449*45916cd2Sjpk 	    rhent, tsol_tpc_t *, tpc);
450*45916cd2Sjpk 	bcopy(tpc->tpc_tp.name, rhent->rh_template,
451*45916cd2Sjpk 	    sizeof (rhent->rh_template));
452*45916cd2Sjpk 	TPC_RELE(tpc);
453*45916cd2Sjpk 	return (0);
454*45916cd2Sjpk }
455*45916cd2Sjpk 
456*45916cd2Sjpk static boolean_t
457*45916cd2Sjpk template_name_ok(const char *name)
458*45916cd2Sjpk {
459*45916cd2Sjpk 	const char *name_end = name + TNTNAMSIZ;
460*45916cd2Sjpk 
461*45916cd2Sjpk 	while (name < name_end) {
462*45916cd2Sjpk 		if (*name == '\0')
463*45916cd2Sjpk 			break;
464*45916cd2Sjpk 		name++;
465*45916cd2Sjpk 	}
466*45916cd2Sjpk 	return (name < name_end);
467*45916cd2Sjpk }
468*45916cd2Sjpk 
469*45916cd2Sjpk static int
470*45916cd2Sjpk tnrh(int cmd, void *buf)
471*45916cd2Sjpk {
472*45916cd2Sjpk 	int retv;
473*45916cd2Sjpk 	tsol_rhent_t rhent;
474*45916cd2Sjpk 
475*45916cd2Sjpk 	/* Make sure user has sufficient privilege */
476*45916cd2Sjpk 	if (cmd != TNDB_GET &&
477*45916cd2Sjpk 	    (retv = secpolicy_net_config(CRED(), B_FALSE)) != 0)
478*45916cd2Sjpk 		return (set_errno(retv));
479*45916cd2Sjpk 
480*45916cd2Sjpk 	/*
481*45916cd2Sjpk 	 * Get arguments
482*45916cd2Sjpk 	 */
483*45916cd2Sjpk 	if (cmd != TNDB_FLUSH &&
484*45916cd2Sjpk 	    copyin(buf, &rhent, sizeof (rhent)) != 0) {
485*45916cd2Sjpk 		DTRACE_PROBE(tx__tndb__l0__tnrhdelete__copyin);
486*45916cd2Sjpk 		return (set_errno(EFAULT));
487*45916cd2Sjpk 	}
488*45916cd2Sjpk 
489*45916cd2Sjpk 	switch (cmd) {
490*45916cd2Sjpk 	case TNDB_LOAD:
491*45916cd2Sjpk 		DTRACE_PROBE(tx__tndb__l2__tnrhdelete__tndbload);
492*45916cd2Sjpk 		if (!template_name_ok(rhent.rh_template)) {
493*45916cd2Sjpk 			retv = EINVAL;
494*45916cd2Sjpk 		} else {
495*45916cd2Sjpk 			retv = tnrh_load(&rhent);
496*45916cd2Sjpk 		}
497*45916cd2Sjpk 		break;
498*45916cd2Sjpk 
499*45916cd2Sjpk 	case TNDB_DELETE:
500*45916cd2Sjpk 		DTRACE_PROBE(tx__tndb__l2__tnrhdelete__tndbdelete);
501*45916cd2Sjpk 		retv = tnrh_delete(&rhent);
502*45916cd2Sjpk 		break;
503*45916cd2Sjpk 
504*45916cd2Sjpk 	case TNDB_GET:
505*45916cd2Sjpk 		DTRACE_PROBE(tx__tndb__l4__tnrhdelete__tndbget);
506*45916cd2Sjpk 		if (!template_name_ok(rhent.rh_template)) {
507*45916cd2Sjpk 			retv = EINVAL;
508*45916cd2Sjpk 			break;
509*45916cd2Sjpk 		}
510*45916cd2Sjpk 
511*45916cd2Sjpk 		retv = tnrh_get(&rhent);
512*45916cd2Sjpk 		if (retv != 0)
513*45916cd2Sjpk 			break;
514*45916cd2Sjpk 
515*45916cd2Sjpk 		/*
516*45916cd2Sjpk 		 * Copy out result
517*45916cd2Sjpk 		 */
518*45916cd2Sjpk 		if (copyout(&rhent, buf, sizeof (rhent)) != 0) {
519*45916cd2Sjpk 			DTRACE_PROBE(tx__tndb__l0__tnrhdelete__copyout);
520*45916cd2Sjpk 			retv = EFAULT;
521*45916cd2Sjpk 		}
522*45916cd2Sjpk 		break;
523*45916cd2Sjpk 
524*45916cd2Sjpk 	case TNDB_FLUSH:
525*45916cd2Sjpk 		DTRACE_PROBE(tx__tndb__l2__tnrhdelete__flush);
526*45916cd2Sjpk 		flush_rh_table(tnrhc_table, TSOL_MASK_TABLE_SIZE);
527*45916cd2Sjpk 		flush_rh_table(tnrhc_table_v6, TSOL_MASK_TABLE_SIZE_V6);
528*45916cd2Sjpk 		break;
529*45916cd2Sjpk 
530*45916cd2Sjpk 	default:
531*45916cd2Sjpk 		DTRACE_PROBE1(tx__tndb__l0__tnrhdelete__unknowncmd,
532*45916cd2Sjpk 		    int, cmd);
533*45916cd2Sjpk 		retv = EOPNOTSUPP;
534*45916cd2Sjpk 		break;
535*45916cd2Sjpk 	}
536*45916cd2Sjpk 
537*45916cd2Sjpk 	if (retv != 0)
538*45916cd2Sjpk 		return (set_errno(retv));
539*45916cd2Sjpk 	else
540*45916cd2Sjpk 		return (retv);
541*45916cd2Sjpk }
542*45916cd2Sjpk 
543*45916cd2Sjpk static tsol_tpc_t *
544*45916cd2Sjpk tnrhtp_create(const tsol_tpent_t *tpent, int kmflags)
545*45916cd2Sjpk {
546*45916cd2Sjpk 	tsol_tpc_t *tpc;
547*45916cd2Sjpk 	mod_hash_val_t hv;
548*45916cd2Sjpk 
549*45916cd2Sjpk 	/*
550*45916cd2Sjpk 	 * We intentionally allocate a new entry before taking the lock on the
551*45916cd2Sjpk 	 * entire database.
552*45916cd2Sjpk 	 */
553*45916cd2Sjpk 	if ((tpc = kmem_zalloc(sizeof (*tpc), kmflags)) == NULL)
554*45916cd2Sjpk 		return (NULL);
555*45916cd2Sjpk 
556*45916cd2Sjpk 	mutex_enter(&tpc_lock);
557*45916cd2Sjpk 	if (mod_hash_find(tpc_name_hash, (mod_hash_key_t)tpent->name,
558*45916cd2Sjpk 	    &hv) == 0) {
559*45916cd2Sjpk 		tsol_tpc_t *found_tpc = (tsol_tpc_t *)hv;
560*45916cd2Sjpk 
561*45916cd2Sjpk 		found_tpc->tpc_invalid = 1;
562*45916cd2Sjpk 		(void) mod_hash_destroy(tpc_name_hash,
563*45916cd2Sjpk 		    (mod_hash_key_t)tpent->name);
564*45916cd2Sjpk 		TPC_RELE(found_tpc);
565*45916cd2Sjpk 	}
566*45916cd2Sjpk 
567*45916cd2Sjpk 	mutex_init(&tpc->tpc_lock, NULL, MUTEX_DEFAULT, NULL);
568*45916cd2Sjpk 	/* tsol_tpent_t is the same on LP64 and ILP32 */
569*45916cd2Sjpk 	bcopy(tpent, &tpc->tpc_tp, sizeof (tpc->tpc_tp));
570*45916cd2Sjpk 	(void) mod_hash_insert(tpc_name_hash, (mod_hash_key_t)tpc->tpc_tp.name,
571*45916cd2Sjpk 	    (mod_hash_val_t)tpc);
572*45916cd2Sjpk 	TPC_HOLD(tpc);
573*45916cd2Sjpk 	mutex_exit(&tpc_lock);
574*45916cd2Sjpk 
575*45916cd2Sjpk 	return (tpc);
576*45916cd2Sjpk }
577*45916cd2Sjpk 
578*45916cd2Sjpk static int
579*45916cd2Sjpk tnrhtp_delete(const char *tname)
580*45916cd2Sjpk {
581*45916cd2Sjpk 	tsol_tpc_t *tpc;
582*45916cd2Sjpk 	mod_hash_val_t hv;
583*45916cd2Sjpk 	int retv = ENOENT;
584*45916cd2Sjpk 
585*45916cd2Sjpk 	mutex_enter(&tpc_lock);
586*45916cd2Sjpk 	if (mod_hash_find(tpc_name_hash, (mod_hash_key_t)tname, &hv) == 0) {
587*45916cd2Sjpk 		tpc = (tsol_tpc_t *)hv;
588*45916cd2Sjpk 		ASSERT(!tpc->tpc_invalid);
589*45916cd2Sjpk 		tpc->tpc_invalid = 1;
590*45916cd2Sjpk 		(void) mod_hash_destroy(tpc_name_hash,
591*45916cd2Sjpk 		    (mod_hash_key_t)tpc->tpc_tp.name);
592*45916cd2Sjpk 		TPC_RELE(tpc);
593*45916cd2Sjpk 		retv = 0;
594*45916cd2Sjpk 	}
595*45916cd2Sjpk 	mutex_exit(&tpc_lock);
596*45916cd2Sjpk 	return (retv);
597*45916cd2Sjpk }
598*45916cd2Sjpk 
599*45916cd2Sjpk /* ARGSUSED */
600*45916cd2Sjpk static uint_t
601*45916cd2Sjpk tpc_delete(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
602*45916cd2Sjpk {
603*45916cd2Sjpk 	tsol_tpc_t *tpc = (tsol_tpc_t *)val;
604*45916cd2Sjpk 
605*45916cd2Sjpk 	ASSERT(!tpc->tpc_invalid);
606*45916cd2Sjpk 	tpc->tpc_invalid = 1;
607*45916cd2Sjpk 	TPC_RELE(tpc);
608*45916cd2Sjpk 	return (MH_WALK_CONTINUE);
609*45916cd2Sjpk }
610*45916cd2Sjpk 
611*45916cd2Sjpk static void
612*45916cd2Sjpk tnrhtp_flush(void)
613*45916cd2Sjpk {
614*45916cd2Sjpk 	mutex_enter(&tpc_lock);
615*45916cd2Sjpk 	mod_hash_walk(tpc_name_hash, tpc_delete, NULL);
616*45916cd2Sjpk 	mod_hash_clear(tpc_name_hash);
617*45916cd2Sjpk 	mutex_exit(&tpc_lock);
618*45916cd2Sjpk }
619*45916cd2Sjpk 
620*45916cd2Sjpk static int
621*45916cd2Sjpk tnrhtp(int cmd, void *buf)
622*45916cd2Sjpk {
623*45916cd2Sjpk 	int retv;
624*45916cd2Sjpk 	int type;
625*45916cd2Sjpk 	tsol_tpent_t rhtpent;
626*45916cd2Sjpk 	tsol_tpc_t *tpc;
627*45916cd2Sjpk 
628*45916cd2Sjpk 	/* Make sure user has sufficient privilege */
629*45916cd2Sjpk 	if (cmd != TNDB_GET &&
630*45916cd2Sjpk 	    (retv = secpolicy_net_config(CRED(), B_FALSE)) != 0)
631*45916cd2Sjpk 		return (set_errno(retv));
632*45916cd2Sjpk 
633*45916cd2Sjpk 	/*
634*45916cd2Sjpk 	 * Get argument.  Note that tsol_tpent_t is the same on LP64 and ILP32,
635*45916cd2Sjpk 	 * so no special handling is required.
636*45916cd2Sjpk 	 */
637*45916cd2Sjpk 	if (cmd != TNDB_FLUSH) {
638*45916cd2Sjpk 		if (copyin(buf, &rhtpent, sizeof (rhtpent)) != 0) {
639*45916cd2Sjpk 			DTRACE_PROBE(tx__tndb__l0__tnrhtp__copyin);
640*45916cd2Sjpk 			return (set_errno(EFAULT));
641*45916cd2Sjpk 		}
642*45916cd2Sjpk 
643*45916cd2Sjpk 		/*
644*45916cd2Sjpk 		 * Don't let the user give us a bogus (unterminated) template
645*45916cd2Sjpk 		 * name.
646*45916cd2Sjpk 		 */
647*45916cd2Sjpk 		if (!template_name_ok(rhtpent.name))
648*45916cd2Sjpk 			return (set_errno(EINVAL));
649*45916cd2Sjpk 	}
650*45916cd2Sjpk 
651*45916cd2Sjpk 	switch (cmd) {
652*45916cd2Sjpk 	case TNDB_LOAD:
653*45916cd2Sjpk 		DTRACE_PROBE1(tx__tndb__l2__tnrhtp__tndbload, char *,
654*45916cd2Sjpk 			rhtpent.name);
655*45916cd2Sjpk 		type = rhtpent.host_type;
656*45916cd2Sjpk 		if (type != UNLABELED && type != SUN_CIPSO) {
657*45916cd2Sjpk 			retv = EINVAL;
658*45916cd2Sjpk 			break;
659*45916cd2Sjpk 		}
660*45916cd2Sjpk 
661*45916cd2Sjpk 		if (tnrhtp_create(&rhtpent, KM_NOSLEEP) == NULL)
662*45916cd2Sjpk 			retv = ENOMEM;
663*45916cd2Sjpk 		else
664*45916cd2Sjpk 			retv = 0;
665*45916cd2Sjpk 		break;
666*45916cd2Sjpk 
667*45916cd2Sjpk 	case TNDB_GET:
668*45916cd2Sjpk 		DTRACE_PROBE1(tx__tndb__l4__tnrhtp__tndbget, char *,
669*45916cd2Sjpk 		    rhtpent.name);
670*45916cd2Sjpk 		tpc = tnrhtp_find(rhtpent.name, tpc_name_hash);
671*45916cd2Sjpk 		if (tpc == NULL) {
672*45916cd2Sjpk 			retv = ENOENT;
673*45916cd2Sjpk 			break;
674*45916cd2Sjpk 		}
675*45916cd2Sjpk 
676*45916cd2Sjpk 		/* Copy out result */
677*45916cd2Sjpk 		if (copyout(&tpc->tpc_tp, buf, sizeof (tpc->tpc_tp)) != 0) {
678*45916cd2Sjpk 			DTRACE_PROBE(tx__tndb__l0__tnrhtp__copyout);
679*45916cd2Sjpk 			retv = EFAULT;
680*45916cd2Sjpk 		} else {
681*45916cd2Sjpk 			retv = 0;
682*45916cd2Sjpk 		}
683*45916cd2Sjpk 		TPC_RELE(tpc);
684*45916cd2Sjpk 		break;
685*45916cd2Sjpk 
686*45916cd2Sjpk 	case TNDB_DELETE:
687*45916cd2Sjpk 		DTRACE_PROBE1(tx__tndb__l4__tnrhtp__tndbdelete, char *,
688*45916cd2Sjpk 		    rhtpent.name);
689*45916cd2Sjpk 		retv = tnrhtp_delete(rhtpent.name);
690*45916cd2Sjpk 		break;
691*45916cd2Sjpk 
692*45916cd2Sjpk 	case TNDB_FLUSH:
693*45916cd2Sjpk 		DTRACE_PROBE(tx__tndb__l4__tnrhtp__flush);
694*45916cd2Sjpk 		tnrhtp_flush();
695*45916cd2Sjpk 		retv = 0;
696*45916cd2Sjpk 		break;
697*45916cd2Sjpk 
698*45916cd2Sjpk 	default:
699*45916cd2Sjpk 		DTRACE_PROBE1(tx__tndb__l0__tnrhtp__unknowncmd, int,
700*45916cd2Sjpk 		    cmd);
701*45916cd2Sjpk 		retv = EOPNOTSUPP;
702*45916cd2Sjpk 		break;
703*45916cd2Sjpk 	}
704*45916cd2Sjpk 
705*45916cd2Sjpk 	if (retv != 0)
706*45916cd2Sjpk 		return (set_errno(retv));
707*45916cd2Sjpk 	else
708*45916cd2Sjpk 		return (retv);
709*45916cd2Sjpk }
710*45916cd2Sjpk 
711*45916cd2Sjpk /*
712*45916cd2Sjpk  * MLP entry ordering logic
713*45916cd2Sjpk  *
714*45916cd2Sjpk  * There are two loops in this routine.  The first loop finds the entry that
715*45916cd2Sjpk  * either logically follows the new entry to be inserted, or is the entry that
716*45916cd2Sjpk  * precedes and overlaps the new entry, or is NULL to mean end-of-list.  This
717*45916cd2Sjpk  * is 'tme.'  The second loop scans ahead from that point to find any overlap
718*45916cd2Sjpk  * on the front or back of this new entry.
719*45916cd2Sjpk  *
720*45916cd2Sjpk  * For the first loop, we can have the following cases in the list (note that
721*45916cd2Sjpk  * the port-portmax range is inclusive):
722*45916cd2Sjpk  *
723*45916cd2Sjpk  *	       port   portmax
724*45916cd2Sjpk  *		+--------+
725*45916cd2Sjpk  * 1: +------+ ................... precedes; skip to next
726*45916cd2Sjpk  * 2:	    +------+ ............. overlaps; stop here if same protocol
727*45916cd2Sjpk  * 3:		+------+ ......... overlaps; stop if same or higher protocol
728*45916cd2Sjpk  * 4:		    +-------+ .... overlaps or succeeds; stop here
729*45916cd2Sjpk  *
730*45916cd2Sjpk  * For the second loop, we can have the following cases (note that we need not
731*45916cd2Sjpk  * care about other protocol entries at this point, because we're only looking
732*45916cd2Sjpk  * for overlap, not an insertion point):
733*45916cd2Sjpk  *
734*45916cd2Sjpk  *	       port   portmax
735*45916cd2Sjpk  *		+--------+
736*45916cd2Sjpk  * 5:	    +------+ ............. overlaps; stop if same protocol
737*45916cd2Sjpk  * 6:		+------+ ......... overlaps; stop if same protocol
738*45916cd2Sjpk  * 7:		    +-------+ .... overlaps; stop if same protocol
739*45916cd2Sjpk  * 8:			   +---+ . follows; search is done
740*45916cd2Sjpk  *
741*45916cd2Sjpk  * In other words, this second search needs to consider only whether the entry
742*45916cd2Sjpk  * has a starting port number that's greater than the end point of the new
743*45916cd2Sjpk  * entry.  All others are overlaps.
744*45916cd2Sjpk  */
745*45916cd2Sjpk static int
746*45916cd2Sjpk mlp_add_del(tsol_mlp_list_t *mlpl, zoneid_t zoneid, uint8_t proto,
747*45916cd2Sjpk     uint16_t port, uint16_t portmax, boolean_t addflag)
748*45916cd2Sjpk {
749*45916cd2Sjpk 	int retv;
750*45916cd2Sjpk 	tsol_mlp_entry_t *tme, *tme2, *newent;
751*45916cd2Sjpk 
752*45916cd2Sjpk 	if (addflag) {
753*45916cd2Sjpk 		if ((newent = kmem_zalloc(sizeof (*newent), KM_NOSLEEP)) ==
754*45916cd2Sjpk 		    NULL)
755*45916cd2Sjpk 			return (ENOMEM);
756*45916cd2Sjpk 	} else {
757*45916cd2Sjpk 		newent = NULL;
758*45916cd2Sjpk 	}
759*45916cd2Sjpk 	rw_enter(&mlpl->mlpl_rwlock, RW_WRITER);
760*45916cd2Sjpk 
761*45916cd2Sjpk 	/*
762*45916cd2Sjpk 	 * First loop: find logical insertion point or overlap.  Table is kept
763*45916cd2Sjpk 	 * in order of port number first, and then, within that, by protocol
764*45916cd2Sjpk 	 * number.
765*45916cd2Sjpk 	 */
766*45916cd2Sjpk 	for (tme = mlpl->mlpl_first; tme != NULL; tme = tme->mlpe_next) {
767*45916cd2Sjpk 		/* logically next (case 4) */
768*45916cd2Sjpk 		if (tme->mlpe_mlp.mlp_port > port)
769*45916cd2Sjpk 			break;
770*45916cd2Sjpk 		/* if this is logically next or overlap, then stop (case 3) */
771*45916cd2Sjpk 		if (tme->mlpe_mlp.mlp_port == port &&
772*45916cd2Sjpk 		    tme->mlpe_mlp.mlp_ipp >= proto)
773*45916cd2Sjpk 			break;
774*45916cd2Sjpk 		/* earlier or same port sequence; check for overlap (case 2) */
775*45916cd2Sjpk 		if (tme->mlpe_mlp.mlp_ipp == proto &&
776*45916cd2Sjpk 		    tme->mlpe_mlp.mlp_port_upper >= port)
777*45916cd2Sjpk 			break;
778*45916cd2Sjpk 		/* otherwise, loop again (case 1) */
779*45916cd2Sjpk 	}
780*45916cd2Sjpk 
781*45916cd2Sjpk 	/* Second loop: scan ahead for overlap */
782*45916cd2Sjpk 	for (tme2 = tme; tme2 != NULL; tme2 = tme2->mlpe_next) {
783*45916cd2Sjpk 		/* check if entry follows; no overlap (case 8) */
784*45916cd2Sjpk 		if (tme2->mlpe_mlp.mlp_port > portmax) {
785*45916cd2Sjpk 			tme2 = NULL;
786*45916cd2Sjpk 			break;
787*45916cd2Sjpk 		}
788*45916cd2Sjpk 		/* only exact protocol matches at this point (cases 5-7) */
789*45916cd2Sjpk 		if (tme2->mlpe_mlp.mlp_ipp == proto)
790*45916cd2Sjpk 			break;
791*45916cd2Sjpk 	}
792*45916cd2Sjpk 
793*45916cd2Sjpk 	retv = 0;
794*45916cd2Sjpk 	if (addflag) {
795*45916cd2Sjpk 		if (tme2 != NULL) {
796*45916cd2Sjpk 			retv = EEXIST;
797*45916cd2Sjpk 		} else {
798*45916cd2Sjpk 			newent->mlpe_zoneid = zoneid;
799*45916cd2Sjpk 			newent->mlpe_mlp.mlp_ipp = proto;
800*45916cd2Sjpk 			newent->mlpe_mlp.mlp_port = port;
801*45916cd2Sjpk 			newent->mlpe_mlp.mlp_port_upper = portmax;
802*45916cd2Sjpk 			newent->mlpe_next = tme;
803*45916cd2Sjpk 			if (tme == NULL) {
804*45916cd2Sjpk 				tme2 = mlpl->mlpl_last;
805*45916cd2Sjpk 				mlpl->mlpl_last = newent;
806*45916cd2Sjpk 			} else {
807*45916cd2Sjpk 				tme2 = tme->mlpe_prev;
808*45916cd2Sjpk 				tme->mlpe_prev = newent;
809*45916cd2Sjpk 			}
810*45916cd2Sjpk 			newent->mlpe_prev = tme2;
811*45916cd2Sjpk 			if (tme2 == NULL)
812*45916cd2Sjpk 				mlpl->mlpl_first = newent;
813*45916cd2Sjpk 			else
814*45916cd2Sjpk 				tme2->mlpe_next = newent;
815*45916cd2Sjpk 			newent = NULL;
816*45916cd2Sjpk 		}
817*45916cd2Sjpk 	} else {
818*45916cd2Sjpk 		if (tme2 == NULL || tme2->mlpe_mlp.mlp_port != port ||
819*45916cd2Sjpk 		    tme2->mlpe_mlp.mlp_port_upper != portmax) {
820*45916cd2Sjpk 			retv = ENOENT;
821*45916cd2Sjpk 		} else {
822*45916cd2Sjpk 			if ((tme2 = tme->mlpe_prev) == NULL)
823*45916cd2Sjpk 				mlpl->mlpl_first = tme->mlpe_next;
824*45916cd2Sjpk 			else
825*45916cd2Sjpk 				tme2->mlpe_next = tme->mlpe_next;
826*45916cd2Sjpk 			if ((tme2 = tme->mlpe_next) == NULL)
827*45916cd2Sjpk 				mlpl->mlpl_last = tme->mlpe_prev;
828*45916cd2Sjpk 			else
829*45916cd2Sjpk 				tme2->mlpe_prev = tme->mlpe_prev;
830*45916cd2Sjpk 			newent = tme;
831*45916cd2Sjpk 		}
832*45916cd2Sjpk 	}
833*45916cd2Sjpk 	rw_exit(&mlpl->mlpl_rwlock);
834*45916cd2Sjpk 
835*45916cd2Sjpk 	if (newent != NULL)
836*45916cd2Sjpk 		kmem_free(newent, sizeof (*newent));
837*45916cd2Sjpk 
838*45916cd2Sjpk 	return (retv);
839*45916cd2Sjpk }
840*45916cd2Sjpk 
841*45916cd2Sjpk /*
842*45916cd2Sjpk  * Add or remove an MLP entry from the database so that the classifier can find
843*45916cd2Sjpk  * it.
844*45916cd2Sjpk  *
845*45916cd2Sjpk  * Note: port number is in host byte order.
846*45916cd2Sjpk  */
847*45916cd2Sjpk int
848*45916cd2Sjpk tsol_mlp_anon(zone_t *zone, mlp_type_t mlptype, uchar_t proto, uint16_t port,
849*45916cd2Sjpk     boolean_t addflag)
850*45916cd2Sjpk {
851*45916cd2Sjpk 	int retv = 0;
852*45916cd2Sjpk 
853*45916cd2Sjpk 	if (mlptype == mlptBoth || mlptype == mlptPrivate)
854*45916cd2Sjpk 		retv = mlp_add_del(&zone->zone_mlps, zone->zone_id, proto,
855*45916cd2Sjpk 		    port, port, addflag);
856*45916cd2Sjpk 	if ((retv == 0 || !addflag) &&
857*45916cd2Sjpk 	    (mlptype == mlptBoth || mlptype == mlptShared)) {
858*45916cd2Sjpk 		retv = mlp_add_del(&shared_mlps, zone->zone_id, proto, port,
859*45916cd2Sjpk 		    port, addflag);
860*45916cd2Sjpk 		if (retv != 0 && addflag)
861*45916cd2Sjpk 			(void) mlp_add_del(&zone->zone_mlps, zone->zone_id,
862*45916cd2Sjpk 			    proto, port, port, B_FALSE);
863*45916cd2Sjpk 	}
864*45916cd2Sjpk 	return (retv);
865*45916cd2Sjpk }
866*45916cd2Sjpk 
867*45916cd2Sjpk static void
868*45916cd2Sjpk mlp_flush(tsol_mlp_list_t *mlpl, zoneid_t zoneid)
869*45916cd2Sjpk {
870*45916cd2Sjpk 	tsol_mlp_entry_t *tme, *tme2, *tmnext;
871*45916cd2Sjpk 
872*45916cd2Sjpk 	rw_enter(&mlpl->mlpl_rwlock, RW_WRITER);
873*45916cd2Sjpk 	for (tme = mlpl->mlpl_first; tme != NULL; tme = tmnext) {
874*45916cd2Sjpk 		tmnext = tme->mlpe_next;
875*45916cd2Sjpk 		if (zoneid == ALL_ZONES || tme->mlpe_zoneid == zoneid) {
876*45916cd2Sjpk 			if ((tme2 = tme->mlpe_prev) == NULL)
877*45916cd2Sjpk 				mlpl->mlpl_first = tmnext;
878*45916cd2Sjpk 			else
879*45916cd2Sjpk 				tme2->mlpe_next = tmnext;
880*45916cd2Sjpk 			if (tmnext == NULL)
881*45916cd2Sjpk 				mlpl->mlpl_last = tme2;
882*45916cd2Sjpk 			else
883*45916cd2Sjpk 				tmnext->mlpe_prev = tme2;
884*45916cd2Sjpk 			kmem_free(tme, sizeof (*tme));
885*45916cd2Sjpk 		}
886*45916cd2Sjpk 	}
887*45916cd2Sjpk 	rw_exit(&mlpl->mlpl_rwlock);
888*45916cd2Sjpk }
889*45916cd2Sjpk 
890*45916cd2Sjpk /*
891*45916cd2Sjpk  * Note: user supplies port numbers in host byte order.
892*45916cd2Sjpk  */
893*45916cd2Sjpk static int
894*45916cd2Sjpk tnmlp(int cmd, void *buf)
895*45916cd2Sjpk {
896*45916cd2Sjpk 	int retv;
897*45916cd2Sjpk 	tsol_mlpent_t tsme;
898*45916cd2Sjpk 	zone_t *zone;
899*45916cd2Sjpk 	tsol_mlp_list_t *mlpl;
900*45916cd2Sjpk 	tsol_mlp_entry_t *tme;
901*45916cd2Sjpk 
902*45916cd2Sjpk 	/* Make sure user has sufficient privilege */
903*45916cd2Sjpk 	if (cmd != TNDB_GET &&
904*45916cd2Sjpk 	    (retv = secpolicy_net_config(CRED(), B_FALSE)) != 0)
905*45916cd2Sjpk 		return (set_errno(retv));
906*45916cd2Sjpk 
907*45916cd2Sjpk 	/*
908*45916cd2Sjpk 	 * Get argument.  Note that tsol_mlpent_t is the same on LP64 and
909*45916cd2Sjpk 	 * ILP32, so no special handling is required.
910*45916cd2Sjpk 	 */
911*45916cd2Sjpk 	if (copyin(buf, &tsme, sizeof (tsme)) != 0) {
912*45916cd2Sjpk 		DTRACE_PROBE(tx__tndb__l0__tnmlp__copyin);
913*45916cd2Sjpk 		return (set_errno(EFAULT));
914*45916cd2Sjpk 	}
915*45916cd2Sjpk 
916*45916cd2Sjpk 	/* MLPs on shared IP addresses */
917*45916cd2Sjpk 	if (tsme.tsme_flags & TSOL_MEF_SHARED) {
918*45916cd2Sjpk 		zone = NULL;
919*45916cd2Sjpk 		mlpl = &shared_mlps;
920*45916cd2Sjpk 	} else {
921*45916cd2Sjpk 		zone = zone_find_by_id(tsme.tsme_zoneid);
922*45916cd2Sjpk 		if (zone == NULL)
923*45916cd2Sjpk 			return (set_errno(EINVAL));
924*45916cd2Sjpk 		mlpl = &zone->zone_mlps;
925*45916cd2Sjpk 	}
926*45916cd2Sjpk 	if (tsme.tsme_mlp.mlp_port_upper == 0)
927*45916cd2Sjpk 		tsme.tsme_mlp.mlp_port_upper = tsme.tsme_mlp.mlp_port;
928*45916cd2Sjpk 
929*45916cd2Sjpk 	switch (cmd) {
930*45916cd2Sjpk 	case TNDB_LOAD:
931*45916cd2Sjpk 		DTRACE_PROBE1(tx__tndb__l2__tnmlp__tndbload,
932*45916cd2Sjpk 		    tsol_mlpent_t *, &tsme);
933*45916cd2Sjpk 		if (tsme.tsme_mlp.mlp_ipp == 0 || tsme.tsme_mlp.mlp_port == 0 ||
934*45916cd2Sjpk 		    tsme.tsme_mlp.mlp_port > tsme.tsme_mlp.mlp_port_upper) {
935*45916cd2Sjpk 			retv = EINVAL;
936*45916cd2Sjpk 			break;
937*45916cd2Sjpk 		}
938*45916cd2Sjpk 		retv = mlp_add_del(mlpl, tsme.tsme_zoneid,
939*45916cd2Sjpk 		    tsme.tsme_mlp.mlp_ipp, tsme.tsme_mlp.mlp_port,
940*45916cd2Sjpk 		    tsme.tsme_mlp.mlp_port_upper, B_TRUE);
941*45916cd2Sjpk 		break;
942*45916cd2Sjpk 
943*45916cd2Sjpk 	case TNDB_GET:
944*45916cd2Sjpk 		DTRACE_PROBE1(tx__tndb__l2__tnmlp__tndbget,
945*45916cd2Sjpk 		    tsol_mlpent_t *, &tsme);
946*45916cd2Sjpk 
947*45916cd2Sjpk 		/*
948*45916cd2Sjpk 		 * Search for the requested element or, failing that, the one
949*45916cd2Sjpk 		 * that's logically next in the sequence.
950*45916cd2Sjpk 		 */
951*45916cd2Sjpk 		rw_enter(&mlpl->mlpl_rwlock, RW_READER);
952*45916cd2Sjpk 		for (tme = mlpl->mlpl_first; tme != NULL;
953*45916cd2Sjpk 		    tme = tme->mlpe_next) {
954*45916cd2Sjpk 			if (tsme.tsme_zoneid != ALL_ZONES &&
955*45916cd2Sjpk 			    tme->mlpe_zoneid != tsme.tsme_zoneid)
956*45916cd2Sjpk 				continue;
957*45916cd2Sjpk 			if (tme->mlpe_mlp.mlp_ipp >= tsme.tsme_mlp.mlp_ipp &&
958*45916cd2Sjpk 			    tme->mlpe_mlp.mlp_port == tsme.tsme_mlp.mlp_port)
959*45916cd2Sjpk 				break;
960*45916cd2Sjpk 			if (tme->mlpe_mlp.mlp_port > tsme.tsme_mlp.mlp_port)
961*45916cd2Sjpk 				break;
962*45916cd2Sjpk 		}
963*45916cd2Sjpk 		if (tme == NULL) {
964*45916cd2Sjpk 			retv = ENOENT;
965*45916cd2Sjpk 		} else {
966*45916cd2Sjpk 			tsme.tsme_zoneid = tme->mlpe_zoneid;
967*45916cd2Sjpk 			tsme.tsme_mlp = tme->mlpe_mlp;
968*45916cd2Sjpk 			retv = 0;
969*45916cd2Sjpk 		}
970*45916cd2Sjpk 		rw_exit(&mlpl->mlpl_rwlock);
971*45916cd2Sjpk 		break;
972*45916cd2Sjpk 
973*45916cd2Sjpk 	case TNDB_DELETE:
974*45916cd2Sjpk 		DTRACE_PROBE1(tx__tndb__l4__tnmlp__tndbdelete,
975*45916cd2Sjpk 		    tsol_mlpent_t *, &tsme);
976*45916cd2Sjpk 		retv = mlp_add_del(mlpl, tsme.tsme_zoneid,
977*45916cd2Sjpk 		    tsme.tsme_mlp.mlp_ipp, tsme.tsme_mlp.mlp_port,
978*45916cd2Sjpk 		    tsme.tsme_mlp.mlp_port_upper, B_FALSE);
979*45916cd2Sjpk 		break;
980*45916cd2Sjpk 
981*45916cd2Sjpk 	case TNDB_FLUSH:
982*45916cd2Sjpk 		DTRACE_PROBE1(tx__tndb__l4__tnmlp__tndbflush,
983*45916cd2Sjpk 		    tsol_mlpent_t *, &tsme);
984*45916cd2Sjpk 		mlp_flush(mlpl, ALL_ZONES);
985*45916cd2Sjpk 		mlp_flush(&shared_mlps, tsme.tsme_zoneid);
986*45916cd2Sjpk 		retv = 0;
987*45916cd2Sjpk 		break;
988*45916cd2Sjpk 
989*45916cd2Sjpk 	default:
990*45916cd2Sjpk 		DTRACE_PROBE1(tx__tndb__l0__tnmlp__unknowncmd, int,
991*45916cd2Sjpk 		    cmd);
992*45916cd2Sjpk 		retv = EOPNOTSUPP;
993*45916cd2Sjpk 		break;
994*45916cd2Sjpk 	}
995*45916cd2Sjpk 
996*45916cd2Sjpk 	if (zone != NULL)
997*45916cd2Sjpk 		zone_rele(zone);
998*45916cd2Sjpk 
999*45916cd2Sjpk 	if (cmd == TNDB_GET && retv == 0) {
1000*45916cd2Sjpk 		/* Copy out result */
1001*45916cd2Sjpk 		if (copyout(&tsme, buf, sizeof (tsme)) != 0) {
1002*45916cd2Sjpk 			DTRACE_PROBE(tx__tndb__l0__tnmlp__copyout);
1003*45916cd2Sjpk 			retv = EFAULT;
1004*45916cd2Sjpk 		}
1005*45916cd2Sjpk 	}
1006*45916cd2Sjpk 
1007*45916cd2Sjpk 	if (retv != 0)
1008*45916cd2Sjpk 		return (set_errno(retv));
1009*45916cd2Sjpk 	else
1010*45916cd2Sjpk 		return (retv);
1011*45916cd2Sjpk }
1012*45916cd2Sjpk 
1013*45916cd2Sjpk /*
1014*45916cd2Sjpk  * Returns a tnrhc matching the addr address.
1015*45916cd2Sjpk  * The returned rhc's refcnt is incremented.
1016*45916cd2Sjpk  */
1017*45916cd2Sjpk tsol_tnrhc_t *
1018*45916cd2Sjpk find_rhc_v4(const in_addr_t *in4)
1019*45916cd2Sjpk {
1020*45916cd2Sjpk 	tsol_tnrhc_t *rh = NULL;
1021*45916cd2Sjpk 	tnrhc_hash_t *tnrhc_hash;
1022*45916cd2Sjpk 	ipaddr_t tmpmask;
1023*45916cd2Sjpk 	int	i;
1024*45916cd2Sjpk 
1025*45916cd2Sjpk 	for (i = (TSOL_MASK_TABLE_SIZE - 1); i >= 0; i--) {
1026*45916cd2Sjpk 
1027*45916cd2Sjpk 		if ((tnrhc_table[i]) == NULL)
1028*45916cd2Sjpk 			continue;
1029*45916cd2Sjpk 
1030*45916cd2Sjpk 		tmpmask = tsol_plen_to_mask(i);
1031*45916cd2Sjpk 		tnrhc_hash = &tnrhc_table[i][
1032*45916cd2Sjpk 		    TSOL_ADDR_HASH(*in4 & tmpmask, TNRHC_SIZE)];
1033*45916cd2Sjpk 
1034*45916cd2Sjpk 		mutex_enter(&tnrhc_hash->tnrh_lock);
1035*45916cd2Sjpk 		for (rh = tnrhc_hash->tnrh_list; rh != NULL;
1036*45916cd2Sjpk 		    rh = rh->rhc_next) {
1037*45916cd2Sjpk 			if ((rh->rhc_host.ta_family == AF_INET) &&
1038*45916cd2Sjpk 			    ((rh->rhc_host.ta_addr_v4.s_addr & tmpmask) ==
1039*45916cd2Sjpk 			    (*in4 & tmpmask))) {
1040*45916cd2Sjpk 				TNRHC_HOLD(rh);
1041*45916cd2Sjpk 				mutex_exit(&tnrhc_hash->tnrh_lock);
1042*45916cd2Sjpk 				return (rh);
1043*45916cd2Sjpk 			}
1044*45916cd2Sjpk 		}
1045*45916cd2Sjpk 		mutex_exit(&tnrhc_hash->tnrh_lock);
1046*45916cd2Sjpk 	}
1047*45916cd2Sjpk 
1048*45916cd2Sjpk 	return (NULL);
1049*45916cd2Sjpk }
1050*45916cd2Sjpk 
1051*45916cd2Sjpk /*
1052*45916cd2Sjpk  * Returns a tnrhc matching the addr address.
1053*45916cd2Sjpk  * The returned rhc's refcnt is incremented.
1054*45916cd2Sjpk  */
1055*45916cd2Sjpk tsol_tnrhc_t *
1056*45916cd2Sjpk find_rhc_v6(const in6_addr_t *in6)
1057*45916cd2Sjpk {
1058*45916cd2Sjpk 	tsol_tnrhc_t *rh = NULL;
1059*45916cd2Sjpk 	tnrhc_hash_t *tnrhc_hash;
1060*45916cd2Sjpk 	in6_addr_t tmpmask;
1061*45916cd2Sjpk 	int i;
1062*45916cd2Sjpk 
1063*45916cd2Sjpk 	if (IN6_IS_ADDR_V4MAPPED(in6)) {
1064*45916cd2Sjpk 		in_addr_t in4;
1065*45916cd2Sjpk 
1066*45916cd2Sjpk 		IN6_V4MAPPED_TO_IPADDR(in6, in4);
1067*45916cd2Sjpk 		return (find_rhc_v4(&in4));
1068*45916cd2Sjpk 	}
1069*45916cd2Sjpk 
1070*45916cd2Sjpk 	for (i = (TSOL_MASK_TABLE_SIZE_V6 - 1); i >= 0; i--) {
1071*45916cd2Sjpk 		if ((tnrhc_table_v6[i]) == NULL)
1072*45916cd2Sjpk 			continue;
1073*45916cd2Sjpk 
1074*45916cd2Sjpk 		tsol_plen_to_mask_v6(i, &tmpmask);
1075*45916cd2Sjpk 		tnrhc_hash = &tnrhc_table_v6[i][
1076*45916cd2Sjpk 		    TSOL_ADDR_MASK_HASH_V6(*in6, tmpmask, TNRHC_SIZE)];
1077*45916cd2Sjpk 
1078*45916cd2Sjpk 		mutex_enter(&tnrhc_hash->tnrh_lock);
1079*45916cd2Sjpk 		for (rh = tnrhc_hash->tnrh_list; rh != NULL;
1080*45916cd2Sjpk 		    rh = rh->rhc_next) {
1081*45916cd2Sjpk 			if ((rh->rhc_host.ta_family == AF_INET6) &&
1082*45916cd2Sjpk 			    V6_MASK_EQ_2(rh->rhc_host.ta_addr_v6, tmpmask,
1083*45916cd2Sjpk 			    *in6)) {
1084*45916cd2Sjpk 				TNRHC_HOLD(rh);
1085*45916cd2Sjpk 				mutex_exit(&tnrhc_hash->tnrh_lock);
1086*45916cd2Sjpk 				return (rh);
1087*45916cd2Sjpk 			}
1088*45916cd2Sjpk 		}
1089*45916cd2Sjpk 		mutex_exit(&tnrhc_hash->tnrh_lock);
1090*45916cd2Sjpk 	}
1091*45916cd2Sjpk 
1092*45916cd2Sjpk 	return (NULL);
1093*45916cd2Sjpk }
1094*45916cd2Sjpk 
1095*45916cd2Sjpk tsol_tpc_t *
1096*45916cd2Sjpk find_tpc(const void *addr, uchar_t version, boolean_t staleok)
1097*45916cd2Sjpk {
1098*45916cd2Sjpk 	tsol_tpc_t *tpc;
1099*45916cd2Sjpk 	tsol_tnrhc_t *rhc;
1100*45916cd2Sjpk 
1101*45916cd2Sjpk 	if (version == IPV4_VERSION)
1102*45916cd2Sjpk 		rhc = find_rhc_v4(addr);
1103*45916cd2Sjpk 	else
1104*45916cd2Sjpk 		rhc = find_rhc_v6(addr);
1105*45916cd2Sjpk 
1106*45916cd2Sjpk 	if (rhc != NULL) {
1107*45916cd2Sjpk 		tpc = rhc->rhc_tpc;
1108*45916cd2Sjpk 		if (!staleok && tpc->tpc_invalid) {
1109*45916cd2Sjpk 			/*
1110*45916cd2Sjpk 			 * This should not happen unless the user deletes
1111*45916cd2Sjpk 			 * templates without recreating them.  Try to find the
1112*45916cd2Sjpk 			 * new version of template.  If there is none, then
1113*45916cd2Sjpk 			 * just give up.
1114*45916cd2Sjpk 			 */
1115*45916cd2Sjpk 			tpc = tnrhtp_find(tpc->tpc_tp.name, tpc_name_hash);
1116*45916cd2Sjpk 			if (tpc != NULL) {
1117*45916cd2Sjpk 				TPC_RELE(rhc->rhc_tpc);
1118*45916cd2Sjpk 				rhc->rhc_tpc = tpc;
1119*45916cd2Sjpk 			}
1120*45916cd2Sjpk 		}
1121*45916cd2Sjpk 		if (tpc != NULL)
1122*45916cd2Sjpk 			TPC_HOLD(tpc);
1123*45916cd2Sjpk 		TNRHC_RELE(rhc);
1124*45916cd2Sjpk 		return (tpc);
1125*45916cd2Sjpk 	}
1126*45916cd2Sjpk 	DTRACE_PROBE(tx__tndb__l1__findtpc__notemplate);
1127*45916cd2Sjpk 	return (NULL);
1128*45916cd2Sjpk }
1129*45916cd2Sjpk 
1130*45916cd2Sjpk /*
1131*45916cd2Sjpk  * create an internal template called "_unlab":
1132*45916cd2Sjpk  *
1133*45916cd2Sjpk  * _unlab;\
1134*45916cd2Sjpk  *	host_type = unlabeled;\
1135*45916cd2Sjpk  *	def_label = ADMIN_LOW[ADMIN_LOW];\
1136*45916cd2Sjpk  *	min_sl = ADMIN_LOW;\
1137*45916cd2Sjpk  *	max_sl = ADMIN_HIGH;
1138*45916cd2Sjpk  */
1139*45916cd2Sjpk static void
1140*45916cd2Sjpk tsol_create_i_tmpls(void)
1141*45916cd2Sjpk {
1142*45916cd2Sjpk 	tsol_tpent_t rhtpent;
1143*45916cd2Sjpk 
1144*45916cd2Sjpk 	bzero(&rhtpent, sizeof (rhtpent));
1145*45916cd2Sjpk 
1146*45916cd2Sjpk 	/* create _unlab */
1147*45916cd2Sjpk 	(void) strcpy(rhtpent.name, "_unlab");
1148*45916cd2Sjpk 
1149*45916cd2Sjpk 	rhtpent.host_type = UNLABELED;
1150*45916cd2Sjpk 	rhtpent.tp_mask_unl = TSOL_MSK_DEF_LABEL | TSOL_MSK_DEF_CL |
1151*45916cd2Sjpk 	    TSOL_MSK_SL_RANGE_TSOL;
1152*45916cd2Sjpk 
1153*45916cd2Sjpk 	rhtpent.tp_gw_sl_range.lower_bound = *label2bslabel(l_admin_low);
1154*45916cd2Sjpk 	rhtpent.tp_def_label = rhtpent.tp_gw_sl_range.lower_bound;
1155*45916cd2Sjpk 	rhtpent.tp_gw_sl_range.upper_bound = *label2bslabel(l_admin_high);
1156*45916cd2Sjpk 	rhtpent.tp_cipso_doi_unl = default_doi;
1157*45916cd2Sjpk 	tpc_unlab = tnrhtp_create(&rhtpent, KM_SLEEP);
1158*45916cd2Sjpk }
1159*45916cd2Sjpk 
1160*45916cd2Sjpk /*
1161*45916cd2Sjpk  * set up internal host template, called from kernel only.
1162*45916cd2Sjpk  */
1163*45916cd2Sjpk static void
1164*45916cd2Sjpk tsol_create_i_tnrh(const tnaddr_t *sa)
1165*45916cd2Sjpk {
1166*45916cd2Sjpk 	tsol_tnrhc_t *rh, *new;
1167*45916cd2Sjpk 	tnrhc_hash_t *tnrhc_hash;
1168*45916cd2Sjpk 
1169*45916cd2Sjpk 	/* Allocate a new entry before taking the lock */
1170*45916cd2Sjpk 	new = kmem_zalloc(sizeof (*new), KM_SLEEP);
1171*45916cd2Sjpk 
1172*45916cd2Sjpk 	tnrhc_hash = (sa->ta_family == AF_INET) ? &tnrhc_table[0][0] :
1173*45916cd2Sjpk 	    &tnrhc_table_v6[0][0];
1174*45916cd2Sjpk 
1175*45916cd2Sjpk 	mutex_enter(&tnrhc_hash->tnrh_lock);
1176*45916cd2Sjpk 	rh = tnrhc_hash->tnrh_list;
1177*45916cd2Sjpk 
1178*45916cd2Sjpk 	if (rh == NULL) {
1179*45916cd2Sjpk 		/* We're keeping the new entry. */
1180*45916cd2Sjpk 		rh = new;
1181*45916cd2Sjpk 		new = NULL;
1182*45916cd2Sjpk 		rh->rhc_host = *sa;
1183*45916cd2Sjpk 		mutex_init(&rh->rhc_lock, NULL, MUTEX_DEFAULT, NULL);
1184*45916cd2Sjpk 		TNRHC_HOLD(rh);
1185*45916cd2Sjpk 		tnrhc_hash->tnrh_list = rh;
1186*45916cd2Sjpk 	}
1187*45916cd2Sjpk 
1188*45916cd2Sjpk 	/*
1189*45916cd2Sjpk 	 * Link the entry to internal_unlab
1190*45916cd2Sjpk 	 */
1191*45916cd2Sjpk 	if (rh->rhc_tpc != tpc_unlab) {
1192*45916cd2Sjpk 		if (rh->rhc_tpc != NULL)
1193*45916cd2Sjpk 			TPC_RELE(rh->rhc_tpc);
1194*45916cd2Sjpk 		rh->rhc_tpc = tpc_unlab;
1195*45916cd2Sjpk 		TPC_HOLD(tpc_unlab);
1196*45916cd2Sjpk 	}
1197*45916cd2Sjpk 	mutex_exit(&tnrhc_hash->tnrh_lock);
1198*45916cd2Sjpk 	if (new != NULL)
1199*45916cd2Sjpk 		kmem_free(new, sizeof (*new));
1200*45916cd2Sjpk }
1201*45916cd2Sjpk 
1202*45916cd2Sjpk /*
1203*45916cd2Sjpk  * Returns 0 if the port is known to be SLP.  Returns next possible port number
1204*45916cd2Sjpk  * (wrapping through 1) if port is MLP on shared or global.  Administrator
1205*45916cd2Sjpk  * should not make all ports MLP.  If that's done, then we'll just pretend
1206*45916cd2Sjpk  * everything is SLP to avoid looping forever.
1207*45916cd2Sjpk  *
1208*45916cd2Sjpk  * Note: port is in host byte order.
1209*45916cd2Sjpk  */
1210*45916cd2Sjpk in_port_t
1211*45916cd2Sjpk tsol_next_port(zone_t *zone, in_port_t port, int proto, boolean_t upward)
1212*45916cd2Sjpk {
1213*45916cd2Sjpk 	boolean_t loop;
1214*45916cd2Sjpk 	tsol_mlp_entry_t *tme;
1215*45916cd2Sjpk 	int newport = port;
1216*45916cd2Sjpk 
1217*45916cd2Sjpk 	loop = B_FALSE;
1218*45916cd2Sjpk 	for (;;) {
1219*45916cd2Sjpk 		if (zone != NULL && zone->zone_mlps.mlpl_first != NULL) {
1220*45916cd2Sjpk 			rw_enter(&zone->zone_mlps.mlpl_rwlock, RW_READER);
1221*45916cd2Sjpk 			for (tme = zone->zone_mlps.mlpl_first; tme != NULL;
1222*45916cd2Sjpk 			    tme = tme->mlpe_next) {
1223*45916cd2Sjpk 				if (proto == tme->mlpe_mlp.mlp_ipp &&
1224*45916cd2Sjpk 				    newport >= tme->mlpe_mlp.mlp_port &&
1225*45916cd2Sjpk 				    newport <= tme->mlpe_mlp.mlp_port_upper)
1226*45916cd2Sjpk 					newport = upward ?
1227*45916cd2Sjpk 					    tme->mlpe_mlp.mlp_port_upper + 1 :
1228*45916cd2Sjpk 					    tme->mlpe_mlp.mlp_port - 1;
1229*45916cd2Sjpk 			}
1230*45916cd2Sjpk 			rw_exit(&zone->zone_mlps.mlpl_rwlock);
1231*45916cd2Sjpk 		}
1232*45916cd2Sjpk 		if (shared_mlps.mlpl_first != NULL) {
1233*45916cd2Sjpk 			rw_enter(&shared_mlps.mlpl_rwlock, RW_READER);
1234*45916cd2Sjpk 			for (tme = shared_mlps.mlpl_first; tme != NULL;
1235*45916cd2Sjpk 			    tme = tme->mlpe_next) {
1236*45916cd2Sjpk 				if (proto == tme->mlpe_mlp.mlp_ipp &&
1237*45916cd2Sjpk 				    newport >= tme->mlpe_mlp.mlp_port &&
1238*45916cd2Sjpk 				    newport <= tme->mlpe_mlp.mlp_port_upper)
1239*45916cd2Sjpk 					newport = upward ?
1240*45916cd2Sjpk 					    tme->mlpe_mlp.mlp_port_upper + 1 :
1241*45916cd2Sjpk 					    tme->mlpe_mlp.mlp_port - 1;
1242*45916cd2Sjpk 			}
1243*45916cd2Sjpk 			rw_exit(&shared_mlps.mlpl_rwlock);
1244*45916cd2Sjpk 		}
1245*45916cd2Sjpk 		if (newport <= 65535 && newport > 0)
1246*45916cd2Sjpk 			break;
1247*45916cd2Sjpk 		if (loop)
1248*45916cd2Sjpk 			return (0);
1249*45916cd2Sjpk 		loop = B_TRUE;
1250*45916cd2Sjpk 		newport = upward ? 1 : 65535;
1251*45916cd2Sjpk 	}
1252*45916cd2Sjpk 	return (newport == port ? 0 : newport);
1253*45916cd2Sjpk }
1254*45916cd2Sjpk 
1255*45916cd2Sjpk /*
1256*45916cd2Sjpk  * tsol_mlp_port_type will check if the given (zone, proto, port) is a
1257*45916cd2Sjpk  * multilevel port.  If it is, return the type (shared, private, or both), or
1258*45916cd2Sjpk  * indicate that it's single-level.
1259*45916cd2Sjpk  *
1260*45916cd2Sjpk  * Note: port is given in host byte order, not network byte order.
1261*45916cd2Sjpk  */
1262*45916cd2Sjpk mlp_type_t
1263*45916cd2Sjpk tsol_mlp_port_type(zone_t *zone, uchar_t proto, uint16_t port,
1264*45916cd2Sjpk     mlp_type_t mlptype)
1265*45916cd2Sjpk {
1266*45916cd2Sjpk 	tsol_mlp_entry_t *tme;
1267*45916cd2Sjpk 
1268*45916cd2Sjpk 	if (mlptype == mlptBoth || mlptype == mlptPrivate) {
1269*45916cd2Sjpk 		tme = NULL;
1270*45916cd2Sjpk 		if (zone->zone_mlps.mlpl_first != NULL) {
1271*45916cd2Sjpk 			rw_enter(&zone->zone_mlps.mlpl_rwlock, RW_READER);
1272*45916cd2Sjpk 			for (tme = zone->zone_mlps.mlpl_first; tme != NULL;
1273*45916cd2Sjpk 			    tme = tme->mlpe_next) {
1274*45916cd2Sjpk 				if (proto == tme->mlpe_mlp.mlp_ipp &&
1275*45916cd2Sjpk 				    port >= tme->mlpe_mlp.mlp_port &&
1276*45916cd2Sjpk 				    port <= tme->mlpe_mlp.mlp_port_upper)
1277*45916cd2Sjpk 					break;
1278*45916cd2Sjpk 			}
1279*45916cd2Sjpk 			rw_exit(&zone->zone_mlps.mlpl_rwlock);
1280*45916cd2Sjpk 		}
1281*45916cd2Sjpk 		if (tme == NULL) {
1282*45916cd2Sjpk 			if (mlptype == mlptBoth)
1283*45916cd2Sjpk 				mlptype = mlptShared;
1284*45916cd2Sjpk 			else if (mlptype == mlptPrivate)
1285*45916cd2Sjpk 				mlptype = mlptSingle;
1286*45916cd2Sjpk 		}
1287*45916cd2Sjpk 	}
1288*45916cd2Sjpk 	if (mlptype == mlptBoth || mlptype == mlptShared) {
1289*45916cd2Sjpk 		tme = NULL;
1290*45916cd2Sjpk 		if (shared_mlps.mlpl_first != NULL) {
1291*45916cd2Sjpk 			rw_enter(&shared_mlps.mlpl_rwlock, RW_READER);
1292*45916cd2Sjpk 			for (tme = shared_mlps.mlpl_first; tme != NULL;
1293*45916cd2Sjpk 			    tme = tme->mlpe_next) {
1294*45916cd2Sjpk 				if (proto == tme->mlpe_mlp.mlp_ipp &&
1295*45916cd2Sjpk 				    port >= tme->mlpe_mlp.mlp_port &&
1296*45916cd2Sjpk 				    port <= tme->mlpe_mlp.mlp_port_upper)
1297*45916cd2Sjpk 					break;
1298*45916cd2Sjpk 			}
1299*45916cd2Sjpk 			rw_exit(&shared_mlps.mlpl_rwlock);
1300*45916cd2Sjpk 		}
1301*45916cd2Sjpk 		if (tme == NULL) {
1302*45916cd2Sjpk 			if (mlptype == mlptBoth)
1303*45916cd2Sjpk 				mlptype = mlptPrivate;
1304*45916cd2Sjpk 			else if (mlptype == mlptShared)
1305*45916cd2Sjpk 				mlptype = mlptSingle;
1306*45916cd2Sjpk 		}
1307*45916cd2Sjpk 	}
1308*45916cd2Sjpk 	return (mlptype);
1309*45916cd2Sjpk }
1310*45916cd2Sjpk 
1311*45916cd2Sjpk /*
1312*45916cd2Sjpk  * tsol_mlp_findzone will check if the given (proto, port) is a multilevel port
1313*45916cd2Sjpk  * on a shared address.  If it is, return the owning zone.
1314*45916cd2Sjpk  *
1315*45916cd2Sjpk  * Note: lport is in network byte order, unlike the other MLP functions,
1316*45916cd2Sjpk  * because the callers of this function are all dealing with packets off the
1317*45916cd2Sjpk  * wire.
1318*45916cd2Sjpk  */
1319*45916cd2Sjpk zoneid_t
1320*45916cd2Sjpk tsol_mlp_findzone(uchar_t proto, uint16_t lport)
1321*45916cd2Sjpk {
1322*45916cd2Sjpk 	tsol_mlp_entry_t *tme;
1323*45916cd2Sjpk 	zoneid_t zoneid;
1324*45916cd2Sjpk 	uint16_t port;
1325*45916cd2Sjpk 
1326*45916cd2Sjpk 	if (shared_mlps.mlpl_first == NULL)
1327*45916cd2Sjpk 		return (ALL_ZONES);
1328*45916cd2Sjpk 	port = ntohs(lport);
1329*45916cd2Sjpk 	rw_enter(&shared_mlps.mlpl_rwlock, RW_READER);
1330*45916cd2Sjpk 	for (tme = shared_mlps.mlpl_first; tme != NULL; tme = tme->mlpe_next) {
1331*45916cd2Sjpk 		if (proto == tme->mlpe_mlp.mlp_ipp &&
1332*45916cd2Sjpk 		    port >= tme->mlpe_mlp.mlp_port &&
1333*45916cd2Sjpk 		    port <= tme->mlpe_mlp.mlp_port_upper)
1334*45916cd2Sjpk 			break;
1335*45916cd2Sjpk 	}
1336*45916cd2Sjpk 	zoneid = tme == NULL ? ALL_ZONES : tme->mlpe_zoneid;
1337*45916cd2Sjpk 	rw_exit(&shared_mlps.mlpl_rwlock);
1338*45916cd2Sjpk 	return (zoneid);
1339*45916cd2Sjpk }
1340*45916cd2Sjpk 
1341*45916cd2Sjpk /* Debug routine */
1342*45916cd2Sjpk void
1343*45916cd2Sjpk tsol_print_label(const blevel_t *blev, const char *name)
1344*45916cd2Sjpk {
1345*45916cd2Sjpk 	const _blevel_impl_t *bli = (const _blevel_impl_t *)blev;
1346*45916cd2Sjpk 
1347*45916cd2Sjpk 	/* We really support only sensitivity labels */
1348*45916cd2Sjpk 	cmn_err(CE_NOTE, "%s %x:%x:%08x%08x%08x%08x%08x%08x%08x%08x",
1349*45916cd2Sjpk 	    name, bli->id, LCLASS(bli), ntohl(bli->_comps.c1),
1350*45916cd2Sjpk 	    ntohl(bli->_comps.c2), ntohl(bli->_comps.c3), ntohl(bli->_comps.c4),
1351*45916cd2Sjpk 	    ntohl(bli->_comps.c5), ntohl(bli->_comps.c6), ntohl(bli->_comps.c7),
1352*45916cd2Sjpk 	    ntohl(bli->_comps.c8));
1353*45916cd2Sjpk }
1354*45916cd2Sjpk 
1355*45916cd2Sjpk /*
1356*45916cd2Sjpk  * Name:	labelsys()
1357*45916cd2Sjpk  *
1358*45916cd2Sjpk  * Normal:	Routes TSOL syscalls.
1359*45916cd2Sjpk  *
1360*45916cd2Sjpk  * Output:	As defined for each TSOL syscall.
1361*45916cd2Sjpk  *		Returns ENOSYS for unrecognized calls.
1362*45916cd2Sjpk  */
1363*45916cd2Sjpk /* ARGSUSED */
1364*45916cd2Sjpk int
1365*45916cd2Sjpk labelsys(int op, void *a1, void *a2, void *a3, void *a4, void *a5)
1366*45916cd2Sjpk {
1367*45916cd2Sjpk 	switch (op) {
1368*45916cd2Sjpk 	case TSOL_SYSLABELING:
1369*45916cd2Sjpk 		return (sys_labeling);
1370*45916cd2Sjpk 	case TSOL_TNRH:
1371*45916cd2Sjpk 		return (tnrh((int)(uintptr_t)a1, a2));
1372*45916cd2Sjpk 	case TSOL_TNRHTP:
1373*45916cd2Sjpk 		return (tnrhtp((int)(uintptr_t)a1, a2));
1374*45916cd2Sjpk 	case TSOL_TNMLP:
1375*45916cd2Sjpk 		return (tnmlp((int)(uintptr_t)a1, a2));
1376*45916cd2Sjpk 	case TSOL_GETLABEL:
1377*45916cd2Sjpk 		return (getlabel((char *)a1, (bslabel_t *)a2));
1378*45916cd2Sjpk 	case TSOL_FGETLABEL:
1379*45916cd2Sjpk 		return (fgetlabel((int)(uintptr_t)a1, (bslabel_t *)a2));
1380*45916cd2Sjpk 	default:
1381*45916cd2Sjpk 		return (set_errno(ENOSYS));
1382*45916cd2Sjpk 	}
1383*45916cd2Sjpk 	/* NOTREACHED */
1384*45916cd2Sjpk }
1385