xref: /illumos-gate/usr/src/uts/common/os/labelsys.c (revision bfabfc35)
145916cd2Sjpk /*
245916cd2Sjpk  * CDDL HEADER START
345916cd2Sjpk  *
445916cd2Sjpk  * The contents of this file are subject to the terms of the
545916cd2Sjpk  * Common Development and Distribution License (the "License").
645916cd2Sjpk  * You may not use this file except in compliance with the License.
745916cd2Sjpk  *
845916cd2Sjpk  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
945916cd2Sjpk  * or http://www.opensolaris.org/os/licensing.
1045916cd2Sjpk  * See the License for the specific language governing permissions
1145916cd2Sjpk  * and limitations under the License.
1245916cd2Sjpk  *
1345916cd2Sjpk  * When distributing Covered Code, include this CDDL HEADER in each
1445916cd2Sjpk  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1545916cd2Sjpk  * If applicable, add the following below this CDDL HEADER, with the
1645916cd2Sjpk  * fields enclosed by brackets "[]" replaced with your own identifying
1745916cd2Sjpk  * information: Portions Copyright [yyyy] [name of copyright owner]
1845916cd2Sjpk  *
1945916cd2Sjpk  * CDDL HEADER END
2045916cd2Sjpk  */
2145916cd2Sjpk /*
2245916cd2Sjpk  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
2345916cd2Sjpk  * Use is subject to license terms.
2445916cd2Sjpk  */
2545916cd2Sjpk 
2645916cd2Sjpk #pragma ident	"%Z%%M%	%I%	%E% SMI"
2745916cd2Sjpk 
2845916cd2Sjpk #include <sys/systm.h>
2945916cd2Sjpk #include <sys/types.h>
3045916cd2Sjpk #include <sys/stream.h>
3145916cd2Sjpk #include <sys/kmem.h>
3245916cd2Sjpk #include <sys/strsubr.h>
3345916cd2Sjpk #include <sys/cmn_err.h>
3445916cd2Sjpk #include <sys/debug.h>
3545916cd2Sjpk #include <sys/param.h>
3645916cd2Sjpk #include <sys/model.h>
3745916cd2Sjpk #include <sys/errno.h>
3845916cd2Sjpk #include <sys/modhash.h>
3945916cd2Sjpk 
4045916cd2Sjpk #include <sys/policy.h>
4145916cd2Sjpk #include <sys/tsol/label.h>
4245916cd2Sjpk #include <sys/tsol/tsyscall.h>
4345916cd2Sjpk #include <sys/tsol/tndb.h>
4445916cd2Sjpk #include <sys/tsol/tnet.h>
4545916cd2Sjpk #include <sys/disp.h>
4645916cd2Sjpk 
4745916cd2Sjpk #include <inet/ip.h>
4845916cd2Sjpk #include <inet/ip6.h>
4945916cd2Sjpk #include <sys/sdt.h>
5045916cd2Sjpk 
5145916cd2Sjpk static mod_hash_t *tpc_name_hash;	/* hash of cache entries by name */
5245916cd2Sjpk static kmutex_t tpc_lock;
5345916cd2Sjpk 
5445916cd2Sjpk static tsol_tpc_t *tpc_unlab;
5545916cd2Sjpk 
5645916cd2Sjpk /*
5745916cd2Sjpk  * tnrhc_table and tnrhc_table_v6 are similar to the IP forwarding tables
5845916cd2Sjpk  * in organization and search. The tnrhc_table[_v6] is an array of 33/129
5945916cd2Sjpk  * pointers to the 33/129 tnrhc tables indexed by the prefix length.
60*bfabfc35Skp  * A largest prefix match search is done by find_rhc and it walks the
6145916cd2Sjpk  * tables from the most specific to the least specific table. Table 0
6245916cd2Sjpk  * corresponds to the single entry for 0.0.0.0/0 or ::0/0.
6345916cd2Sjpk  */
6445916cd2Sjpk tnrhc_hash_t *tnrhc_table[TSOL_MASK_TABLE_SIZE];
6545916cd2Sjpk tnrhc_hash_t *tnrhc_table_v6[TSOL_MASK_TABLE_SIZE_V6];
6645916cd2Sjpk kmutex_t tnrhc_g_lock;
6745916cd2Sjpk 
6845916cd2Sjpk static void tsol_create_i_tmpls(void);
6945916cd2Sjpk 
7045916cd2Sjpk static void tsol_create_i_tnrh(const tnaddr_t *);
7145916cd2Sjpk 
7245916cd2Sjpk /* List of MLPs on valid on shared addresses */
7345916cd2Sjpk static tsol_mlp_list_t shared_mlps;
7445916cd2Sjpk 
7545916cd2Sjpk /*
7645916cd2Sjpk  * Convert length for a mask to the mask.
7745916cd2Sjpk  */
7845916cd2Sjpk static ipaddr_t
tsol_plen_to_mask(uint_t masklen)7945916cd2Sjpk tsol_plen_to_mask(uint_t masklen)
8045916cd2Sjpk {
8145916cd2Sjpk 	return (masklen == 0 ? 0 : htonl(IP_HOST_MASK << (IP_ABITS - masklen)));
8245916cd2Sjpk }
8345916cd2Sjpk 
8445916cd2Sjpk /*
8545916cd2Sjpk  * Convert a prefix length to the mask for that prefix.
8645916cd2Sjpk  * Returns the argument bitmask.
8745916cd2Sjpk  */
8845916cd2Sjpk static void
tsol_plen_to_mask_v6(uint_t plen,in6_addr_t * bitmask)8945916cd2Sjpk tsol_plen_to_mask_v6(uint_t plen, in6_addr_t *bitmask)
9045916cd2Sjpk {
9145916cd2Sjpk 	uint32_t *ptr;
9245916cd2Sjpk 
9345916cd2Sjpk 	ASSERT(plen <= IPV6_ABITS);
9445916cd2Sjpk 
9545916cd2Sjpk 	ptr = (uint32_t *)bitmask;
9645916cd2Sjpk 	while (plen >= 32) {
9745916cd2Sjpk 		*ptr++ = 0xffffffffU;
9845916cd2Sjpk 		plen -= 32;
9945916cd2Sjpk 	}
10045916cd2Sjpk 	if (plen > 0)
10145916cd2Sjpk 		*ptr++ = htonl(0xffffffff << (32 - plen));
10245916cd2Sjpk 	while (ptr < (uint32_t *)(bitmask + 1))
10345916cd2Sjpk 		*ptr++ = 0;
10445916cd2Sjpk }
10545916cd2Sjpk 
10645916cd2Sjpk boolean_t
tnrhc_init_table(tnrhc_hash_t * table[],short prefix_len,int kmflag)10745916cd2Sjpk tnrhc_init_table(tnrhc_hash_t *table[], short prefix_len, int kmflag)
10845916cd2Sjpk {
10945916cd2Sjpk 	int	i;
11045916cd2Sjpk 
11145916cd2Sjpk 	mutex_enter(&tnrhc_g_lock);
11245916cd2Sjpk 
11345916cd2Sjpk 	if (table[prefix_len] == NULL) {
11445916cd2Sjpk 		table[prefix_len] = (tnrhc_hash_t *)
11545916cd2Sjpk 		    kmem_zalloc(TNRHC_SIZE * sizeof (tnrhc_hash_t), kmflag);
11645916cd2Sjpk 		if (table[prefix_len] == NULL) {
11745916cd2Sjpk 			mutex_exit(&tnrhc_g_lock);
11845916cd2Sjpk 			return (B_FALSE);
11945916cd2Sjpk 		}
12045916cd2Sjpk 		for (i = 0; i < TNRHC_SIZE; i++) {
12145916cd2Sjpk 			mutex_init(&table[prefix_len][i].tnrh_lock,
12245916cd2Sjpk 			    NULL, MUTEX_DEFAULT, 0);
12345916cd2Sjpk 		}
12445916cd2Sjpk 	}
12545916cd2Sjpk 	mutex_exit(&tnrhc_g_lock);
12645916cd2Sjpk 	return (B_TRUE);
12745916cd2Sjpk }
12845916cd2Sjpk 
12945916cd2Sjpk void
tcache_init(void)13045916cd2Sjpk tcache_init(void)
13145916cd2Sjpk {
13245916cd2Sjpk 	tnaddr_t address;
13345916cd2Sjpk 
13445916cd2Sjpk 	/*
13545916cd2Sjpk 	 * Note: unable to use mod_hash_create_strhash here, since it's
13645916cd2Sjpk 	 * assymetric.  It assumes that the user has allocated exactly
13745916cd2Sjpk 	 * strlen(key) + 1 bytes for the key when inserted, and attempts to
13845916cd2Sjpk 	 * kmem_free that memory on a delete.
13945916cd2Sjpk 	 */
14045916cd2Sjpk 	tpc_name_hash = mod_hash_create_extended("tnrhtpc_by_name", 256,
14145916cd2Sjpk 	    mod_hash_null_keydtor,  mod_hash_null_valdtor, mod_hash_bystr,
14245916cd2Sjpk 	    NULL, mod_hash_strkey_cmp, KM_SLEEP);
14345916cd2Sjpk 	mutex_init(&tpc_lock, NULL, MUTEX_DEFAULT, NULL);
14445916cd2Sjpk 
14545916cd2Sjpk 	mutex_init(&tnrhc_g_lock, NULL, MUTEX_DEFAULT, NULL);
14645916cd2Sjpk 
14745916cd2Sjpk 	/* label_init always called before tcache_init */
14845916cd2Sjpk 	ASSERT(l_admin_low != NULL && l_admin_high != NULL);
14945916cd2Sjpk 
15045916cd2Sjpk 	/* Initialize the zeroth table prior to loading the 0.0.0.0 entry */
15145916cd2Sjpk 	(void) tnrhc_init_table(tnrhc_table, 0, KM_SLEEP);
15245916cd2Sjpk 	(void) tnrhc_init_table(tnrhc_table_v6, 0, KM_SLEEP);
15345916cd2Sjpk 	/*
15445916cd2Sjpk 	 * create an internal host template called "_unlab"
15545916cd2Sjpk 	 */
15645916cd2Sjpk 	tsol_create_i_tmpls();
15745916cd2Sjpk 
15845916cd2Sjpk 	/*
15945916cd2Sjpk 	 * create a host entry, 0.0.0.0 = _unlab
16045916cd2Sjpk 	 */
16145916cd2Sjpk 	bzero(&address, sizeof (tnaddr_t));
16245916cd2Sjpk 	address.ta_family = AF_INET;
16345916cd2Sjpk 	tsol_create_i_tnrh(&address);
16445916cd2Sjpk 
16545916cd2Sjpk 	/*
16645916cd2Sjpk 	 * create a host entry, ::0 = _unlab
16745916cd2Sjpk 	 */
16845916cd2Sjpk 	address.ta_family = AF_INET6;
16945916cd2Sjpk 	tsol_create_i_tnrh(&address);
17045916cd2Sjpk 
17145916cd2Sjpk 	rw_init(&shared_mlps.mlpl_rwlock, NULL, RW_DEFAULT, NULL);
17245916cd2Sjpk }
17345916cd2Sjpk 
17445916cd2Sjpk /* Called only by the TNRHC_RELE macro when the refcount goes to zero. */
17545916cd2Sjpk void
tnrhc_free(tsol_tnrhc_t * tnrhc)17645916cd2Sjpk tnrhc_free(tsol_tnrhc_t *tnrhc)
17745916cd2Sjpk {
17845916cd2Sjpk 	/*
17945916cd2Sjpk 	 * We assert rhc_invalid here to make sure that no new thread could
18045916cd2Sjpk 	 * possibly end up finding this entry.  If it could, then the
18145916cd2Sjpk 	 * mutex_destroy would panic.
18245916cd2Sjpk 	 */
18345916cd2Sjpk 	DTRACE_PROBE1(tx__tndb__l3__tnrhcfree, tsol_tnrhc_t *, tnrhc);
18445916cd2Sjpk 	ASSERT(tnrhc->rhc_next == NULL && tnrhc->rhc_invalid);
18545916cd2Sjpk 	mutex_exit(&tnrhc->rhc_lock);
18645916cd2Sjpk 	mutex_destroy(&tnrhc->rhc_lock);
18745916cd2Sjpk 	if (tnrhc->rhc_tpc != NULL)
18845916cd2Sjpk 		TPC_RELE(tnrhc->rhc_tpc);
18945916cd2Sjpk 	kmem_free(tnrhc, sizeof (*tnrhc));
19045916cd2Sjpk }
19145916cd2Sjpk 
19245916cd2Sjpk /* Called only by the TPC_RELE macro when the refcount goes to zero. */
19345916cd2Sjpk void
tpc_free(tsol_tpc_t * tpc)19445916cd2Sjpk tpc_free(tsol_tpc_t *tpc)
19545916cd2Sjpk {
19645916cd2Sjpk 	DTRACE_PROBE1(tx__tndb__l3__tpcfree, tsol_tpc_t *, tpc);
19745916cd2Sjpk 	ASSERT(tpc->tpc_invalid);
19845916cd2Sjpk 	mutex_exit(&tpc->tpc_lock);
19945916cd2Sjpk 	mutex_destroy(&tpc->tpc_lock);
20045916cd2Sjpk 	kmem_free(tpc, sizeof (*tpc));
20145916cd2Sjpk }
20245916cd2Sjpk 
20345916cd2Sjpk /*
20445916cd2Sjpk  * Find and hold a reference to a template entry by name.  Ignores entries that
20545916cd2Sjpk  * are being deleted.
20645916cd2Sjpk  */
20745916cd2Sjpk static tsol_tpc_t *
tnrhtp_find(const char * name,mod_hash_t * hash)20845916cd2Sjpk tnrhtp_find(const char *name, mod_hash_t *hash)
20945916cd2Sjpk {
21045916cd2Sjpk 	mod_hash_val_t hv;
21145916cd2Sjpk 	tsol_tpc_t *tpc = NULL;
21245916cd2Sjpk 
21345916cd2Sjpk 	mutex_enter(&tpc_lock);
21445916cd2Sjpk 	if (mod_hash_find(hash, (mod_hash_key_t)name, &hv) == 0) {
21545916cd2Sjpk 		tpc = (tsol_tpc_t *)hv;
21645916cd2Sjpk 		if (tpc->tpc_invalid)
21745916cd2Sjpk 			tpc = NULL;
21845916cd2Sjpk 		else
21945916cd2Sjpk 			TPC_HOLD(tpc);
22045916cd2Sjpk 	}
22145916cd2Sjpk 	mutex_exit(&tpc_lock);
22245916cd2Sjpk 	return (tpc);
22345916cd2Sjpk }
22445916cd2Sjpk 
22545916cd2Sjpk static int
tnrh_delete(const tsol_rhent_t * rhent)22645916cd2Sjpk tnrh_delete(const tsol_rhent_t *rhent)
22745916cd2Sjpk {
22845916cd2Sjpk 	tsol_tnrhc_t *current;
22945916cd2Sjpk 	tsol_tnrhc_t **prevp;
23045916cd2Sjpk 	ipaddr_t tmpmask;
23145916cd2Sjpk 	in6_addr_t tmpmask_v6;
23245916cd2Sjpk 	tnrhc_hash_t *tnrhc_hash;
23345916cd2Sjpk 
23445916cd2Sjpk 	if (rhent->rh_address.ta_family == AF_INET) {
23545916cd2Sjpk 		if (rhent->rh_prefix < 0 || rhent->rh_prefix > IP_ABITS)
23645916cd2Sjpk 			return (EINVAL);
23745916cd2Sjpk 		if (tnrhc_table[rhent->rh_prefix] == NULL)
23845916cd2Sjpk 			return (ENOENT);
23945916cd2Sjpk 		tmpmask = tsol_plen_to_mask(rhent->rh_prefix);
24045916cd2Sjpk 		tnrhc_hash = &tnrhc_table[rhent->rh_prefix][
24145916cd2Sjpk 		    TSOL_ADDR_HASH(rhent->rh_address.ta_addr_v4.s_addr &
24245916cd2Sjpk 		    tmpmask, TNRHC_SIZE)];
24345916cd2Sjpk 	} else if (rhent->rh_address.ta_family == AF_INET6) {
24445916cd2Sjpk 		if (rhent->rh_prefix < 0 || rhent->rh_prefix > IPV6_ABITS)
24545916cd2Sjpk 			return (EINVAL);
24645916cd2Sjpk 		if (tnrhc_table_v6[rhent->rh_prefix] == NULL)
24745916cd2Sjpk 			return (ENOENT);
24845916cd2Sjpk 		tsol_plen_to_mask_v6(rhent->rh_prefix, &tmpmask_v6);
24945916cd2Sjpk 		tnrhc_hash = &tnrhc_table_v6[rhent->rh_prefix][
25045916cd2Sjpk 		    TSOL_ADDR_MASK_HASH_V6(rhent->rh_address.ta_addr_v6,
25145916cd2Sjpk 		    tmpmask_v6, TNRHC_SIZE)];
25245916cd2Sjpk 	} else {
25345916cd2Sjpk 		return (EAFNOSUPPORT);
25445916cd2Sjpk 	}
25545916cd2Sjpk 
25645916cd2Sjpk 	/* search for existing entry */
25745916cd2Sjpk 	mutex_enter(&tnrhc_hash->tnrh_lock);
25845916cd2Sjpk 	prevp = &tnrhc_hash->tnrh_list;
25945916cd2Sjpk 	while ((current = *prevp) != NULL) {
26045916cd2Sjpk 		if (TNADDR_EQ(&rhent->rh_address, &current->rhc_host))
26145916cd2Sjpk 			break;
26245916cd2Sjpk 		prevp = &current->rhc_next;
26345916cd2Sjpk 	}
26445916cd2Sjpk 
26545916cd2Sjpk 	if (current != NULL) {
26645916cd2Sjpk 		DTRACE_PROBE(tx__tndb__l2__tnrhdelete_existingrhentry);
26745916cd2Sjpk 		*prevp = current->rhc_next;
26845916cd2Sjpk 		mutex_enter(&current->rhc_lock);
26945916cd2Sjpk 		current->rhc_next = NULL;
27045916cd2Sjpk 		current->rhc_invalid = 1;
27145916cd2Sjpk 		mutex_exit(&current->rhc_lock);
27245916cd2Sjpk 		TNRHC_RELE(current);
27345916cd2Sjpk 	}
27445916cd2Sjpk 	mutex_exit(&tnrhc_hash->tnrh_lock);
27545916cd2Sjpk 	return (current == NULL ? ENOENT : 0);
27645916cd2Sjpk }
27745916cd2Sjpk 
27845916cd2Sjpk /*
27945916cd2Sjpk  * Flush all remote host entries from the database.
28045916cd2Sjpk  *
28145916cd2Sjpk  * Note that the htable arrays themselves do not have reference counters, so,
28245916cd2Sjpk  * unlike the remote host entries, they cannot be freed.
28345916cd2Sjpk  */
28445916cd2Sjpk static void
flush_rh_table(tnrhc_hash_t ** htable,int nbits)28545916cd2Sjpk flush_rh_table(tnrhc_hash_t **htable, int nbits)
28645916cd2Sjpk {
28745916cd2Sjpk 	tnrhc_hash_t *hent, *hend;
28845916cd2Sjpk 	tsol_tnrhc_t *rhc, *rhnext;
28945916cd2Sjpk 
29045916cd2Sjpk 	while (--nbits >= 0) {
29145916cd2Sjpk 		if ((hent = htable[nbits]) == NULL)
29245916cd2Sjpk 			continue;
29345916cd2Sjpk 		hend = hent + TNRHC_SIZE;
29445916cd2Sjpk 		while (hent < hend) {
29545916cd2Sjpk 			/*
29645916cd2Sjpk 			 * List walkers hold this lock during the walk.  It
29745916cd2Sjpk 			 * protects tnrh_list and rhc_next.
29845916cd2Sjpk 			 */
29945916cd2Sjpk 			mutex_enter(&hent->tnrh_lock);
30045916cd2Sjpk 			rhnext = hent->tnrh_list;
30145916cd2Sjpk 			hent->tnrh_list = NULL;
30245916cd2Sjpk 			mutex_exit(&hent->tnrh_lock);
30345916cd2Sjpk 			/*
30445916cd2Sjpk 			 * There may still be users of the rhcs at this point,
30545916cd2Sjpk 			 * but not of the list or its next pointer.  Thus, the
30645916cd2Sjpk 			 * only thing that would need to be done under a lock
30745916cd2Sjpk 			 * is setting the invalid bit, but that's atomic
30845916cd2Sjpk 			 * anyway, so no locks needed here.
30945916cd2Sjpk 			 */
31045916cd2Sjpk 			while ((rhc = rhnext) != NULL) {
31145916cd2Sjpk 				rhnext = rhc->rhc_next;
31245916cd2Sjpk 				rhc->rhc_next = NULL;
31345916cd2Sjpk 				rhc->rhc_invalid = 1;
31445916cd2Sjpk 				TNRHC_RELE(rhc);
31545916cd2Sjpk 			}
31645916cd2Sjpk 			hent++;
31745916cd2Sjpk 		}
31845916cd2Sjpk 	}
31945916cd2Sjpk }
32045916cd2Sjpk 
32145916cd2Sjpk /*
32245916cd2Sjpk  * Load a remote host entry into kernel cache.  Create a new one if a matching
32345916cd2Sjpk  * entry isn't found, otherwise replace the contents of the previous one by
32445916cd2Sjpk  * deleting it and recreating it.  (Delete and recreate is used to avoid
32545916cd2Sjpk  * allowing other threads to see an unstable data structure.)
32645916cd2Sjpk  *
32745916cd2Sjpk  * A "matching" entry is the one whose address matches that of the one
32845916cd2Sjpk  * being loaded.
32945916cd2Sjpk  *
33045916cd2Sjpk  * Return 0 for success, error code for failure.
33145916cd2Sjpk  */
332*bfabfc35Skp static int
tnrh_hash_add(tsol_tnrhc_t * new,short prefix)333*bfabfc35Skp tnrh_hash_add(tsol_tnrhc_t *new, short prefix)
33445916cd2Sjpk {
33545916cd2Sjpk 	tsol_tnrhc_t **rhp;
336*bfabfc35Skp 	tsol_tnrhc_t *rh;
33745916cd2Sjpk 	ipaddr_t tmpmask;
33845916cd2Sjpk 	in6_addr_t tmpmask_v6;
33945916cd2Sjpk 	tnrhc_hash_t *tnrhc_hash;
34045916cd2Sjpk 
34145916cd2Sjpk 	/* Find the existing entry, if any, leaving the hash locked */
342*bfabfc35Skp 	if (new->rhc_host.ta_family == AF_INET) {
343*bfabfc35Skp 		if (prefix < 0 || prefix > IP_ABITS)
34445916cd2Sjpk 			return (EINVAL);
345*bfabfc35Skp 		if (tnrhc_table[prefix] == NULL &&
346*bfabfc35Skp 		    !tnrhc_init_table(tnrhc_table, prefix,
34745916cd2Sjpk 		    KM_NOSLEEP))
34845916cd2Sjpk 			return (ENOMEM);
349*bfabfc35Skp 		tmpmask = tsol_plen_to_mask(prefix);
350*bfabfc35Skp 		tnrhc_hash = &tnrhc_table[prefix][
351*bfabfc35Skp 		    TSOL_ADDR_HASH(new->rhc_host.ta_addr_v4.s_addr &
35245916cd2Sjpk 		    tmpmask, TNRHC_SIZE)];
35345916cd2Sjpk 		mutex_enter(&tnrhc_hash->tnrh_lock);
35445916cd2Sjpk 		for (rhp = &tnrhc_hash->tnrh_list; (rh = *rhp) != NULL;
35545916cd2Sjpk 		    rhp = &rh->rhc_next) {
35645916cd2Sjpk 			ASSERT(rh->rhc_host.ta_family == AF_INET);
35745916cd2Sjpk 			if (((rh->rhc_host.ta_addr_v4.s_addr ^
358*bfabfc35Skp 			    new->rhc_host.ta_addr_v4.s_addr) & tmpmask) ==
35945916cd2Sjpk 			    0)
36045916cd2Sjpk 				break;
36145916cd2Sjpk 		}
362*bfabfc35Skp 	} else if (new->rhc_host.ta_family == AF_INET6) {
363*bfabfc35Skp 		if (prefix < 0 || prefix > IPV6_ABITS)
36445916cd2Sjpk 			return (EINVAL);
365*bfabfc35Skp 		if (tnrhc_table_v6[prefix] == NULL &&
366*bfabfc35Skp 		    !tnrhc_init_table(tnrhc_table_v6, prefix,
36745916cd2Sjpk 		    KM_NOSLEEP))
36845916cd2Sjpk 			return (ENOMEM);
369*bfabfc35Skp 		tsol_plen_to_mask_v6(prefix, &tmpmask_v6);
370*bfabfc35Skp 		tnrhc_hash = &tnrhc_table_v6[prefix][
371*bfabfc35Skp 		    TSOL_ADDR_MASK_HASH_V6(new->rhc_host.ta_addr_v6,
37245916cd2Sjpk 		    tmpmask_v6, TNRHC_SIZE)];
37345916cd2Sjpk 		mutex_enter(&tnrhc_hash->tnrh_lock);
37445916cd2Sjpk 		for (rhp = &tnrhc_hash->tnrh_list; (rh = *rhp) != NULL;
37545916cd2Sjpk 		    rhp = &rh->rhc_next) {
37645916cd2Sjpk 			ASSERT(rh->rhc_host.ta_family == AF_INET6);
37745916cd2Sjpk 			if (V6_MASK_EQ_2(rh->rhc_host.ta_addr_v6, tmpmask_v6,
378*bfabfc35Skp 			    new->rhc_host.ta_addr_v6))
37945916cd2Sjpk 				break;
38045916cd2Sjpk 		}
38145916cd2Sjpk 	} else {
38245916cd2Sjpk 		return (EAFNOSUPPORT);
38345916cd2Sjpk 	}
38445916cd2Sjpk 
38545916cd2Sjpk 	/* Clobber the old remote host entry. */
38645916cd2Sjpk 	if (rh != NULL) {
38745916cd2Sjpk 		ASSERT(!rh->rhc_invalid);
38845916cd2Sjpk 		rh->rhc_invalid = 1;
38945916cd2Sjpk 		*rhp = rh->rhc_next;
39045916cd2Sjpk 		rh->rhc_next = NULL;
391*bfabfc35Skp 		DTRACE_PROBE1(tx__tndb__l2__tnrhhashadd__invalidaterh,
392*bfabfc35Skp 		    tsol_tnrhc_t *, rh);
39345916cd2Sjpk 		TNRHC_RELE(rh);
39445916cd2Sjpk 	}
39545916cd2Sjpk 
396*bfabfc35Skp 	TNRHC_HOLD(new);
397*bfabfc35Skp 	new->rhc_next = tnrhc_hash->tnrh_list;
398*bfabfc35Skp 	tnrhc_hash->tnrh_list = new;
399*bfabfc35Skp 	DTRACE_PROBE1(tx__tndb__l2__tnrhhashadd__addedrh, tsol_tnrhc_t *, new);
400*bfabfc35Skp 	mutex_exit(&tnrhc_hash->tnrh_lock);
401*bfabfc35Skp 
402*bfabfc35Skp 	return (0);
403*bfabfc35Skp }
404*bfabfc35Skp 
405*bfabfc35Skp /*
406*bfabfc35Skp  * Load a remote host entry into kernel cache.
407*bfabfc35Skp  *
408*bfabfc35Skp  * Return 0 for success, error code for failure.
409*bfabfc35Skp  */
410*bfabfc35Skp int
tnrh_load(const tsol_rhent_t * rhent)411*bfabfc35Skp tnrh_load(const tsol_rhent_t *rhent)
412*bfabfc35Skp {
413*bfabfc35Skp 	tsol_tnrhc_t *new;
414*bfabfc35Skp 	tsol_tpc_t *tpc;
415*bfabfc35Skp 	int status;
416*bfabfc35Skp 
417*bfabfc35Skp 	/* Find and bump the reference count on the named template */
418*bfabfc35Skp 	if ((tpc = tnrhtp_find(rhent->rh_template, tpc_name_hash)) == NULL) {
419*bfabfc35Skp 		return (EINVAL);
420*bfabfc35Skp 	}
421*bfabfc35Skp 	ASSERT(tpc->tpc_tp.host_type == UNLABELED ||
422*bfabfc35Skp 	    tpc->tpc_tp.host_type == SUN_CIPSO);
423*bfabfc35Skp 
424*bfabfc35Skp 	if ((new = kmem_zalloc(sizeof (*new), KM_NOSLEEP)) == NULL) {
425*bfabfc35Skp 		TPC_RELE(tpc);
426*bfabfc35Skp 		return (ENOMEM);
427*bfabfc35Skp 	}
428*bfabfc35Skp 
42945916cd2Sjpk 	/* Initialize the new entry. */
43045916cd2Sjpk 	mutex_init(&new->rhc_lock, NULL, MUTEX_DEFAULT, NULL);
43145916cd2Sjpk 	new->rhc_host = rhent->rh_address;
43245916cd2Sjpk 
43345916cd2Sjpk 	/* The rhc now owns this tpc reference, so no TPC_RELE past here */
43445916cd2Sjpk 	new->rhc_tpc = tpc;
43545916cd2Sjpk 
436*bfabfc35Skp 	/*
437*bfabfc35Skp 	 * tnrh_hash_add handles the tnrh entry ref count for hash
438*bfabfc35Skp 	 * table inclusion. The ref count is incremented and decremented
439*bfabfc35Skp 	 * here to trigger deletion of the new hash table entry in the
440*bfabfc35Skp 	 * event that tnrh_hash_add fails.
441*bfabfc35Skp 	 */
44245916cd2Sjpk 	TNRHC_HOLD(new);
443*bfabfc35Skp 	status = tnrh_hash_add(new, rhent->rh_prefix);
444*bfabfc35Skp 	TNRHC_RELE(new);
44545916cd2Sjpk 
446*bfabfc35Skp 	return (status);
44745916cd2Sjpk }
44845916cd2Sjpk 
44945916cd2Sjpk static int
tnrh_get(tsol_rhent_t * rhent)45045916cd2Sjpk tnrh_get(tsol_rhent_t *rhent)
45145916cd2Sjpk {
45245916cd2Sjpk 	tsol_tpc_t *tpc;
45345916cd2Sjpk 
45445916cd2Sjpk 	switch (rhent->rh_address.ta_family) {
45545916cd2Sjpk 	case AF_INET:
45645916cd2Sjpk 		tpc = find_tpc(&rhent->rh_address.ta_addr_v4, IPV4_VERSION,
45745916cd2Sjpk 		    B_TRUE);
45845916cd2Sjpk 		break;
45945916cd2Sjpk 
46045916cd2Sjpk 	case AF_INET6:
46145916cd2Sjpk 		tpc = find_tpc(&rhent->rh_address.ta_addr_v6, IPV6_VERSION,
46245916cd2Sjpk 		    B_TRUE);
46345916cd2Sjpk 		break;
46445916cd2Sjpk 
46545916cd2Sjpk 	default:
46645916cd2Sjpk 		return (EINVAL);
46745916cd2Sjpk 	}
46845916cd2Sjpk 	if (tpc == NULL)
46945916cd2Sjpk 		return (ENOENT);
47045916cd2Sjpk 
47145916cd2Sjpk 	DTRACE_PROBE2(tx__tndb__l4__tnrhget__foundtpc, tsol_rhent_t *,
47245916cd2Sjpk 	    rhent, tsol_tpc_t *, tpc);
47345916cd2Sjpk 	bcopy(tpc->tpc_tp.name, rhent->rh_template,
47445916cd2Sjpk 	    sizeof (rhent->rh_template));
47545916cd2Sjpk 	TPC_RELE(tpc);
47645916cd2Sjpk 	return (0);
47745916cd2Sjpk }
47845916cd2Sjpk 
47945916cd2Sjpk static boolean_t
template_name_ok(const char * name)48045916cd2Sjpk template_name_ok(const char *name)
48145916cd2Sjpk {
48245916cd2Sjpk 	const char *name_end = name + TNTNAMSIZ;
48345916cd2Sjpk 
48445916cd2Sjpk 	while (name < name_end) {
48545916cd2Sjpk 		if (*name == '\0')
48645916cd2Sjpk 			break;
48745916cd2Sjpk 		name++;
48845916cd2Sjpk 	}
489