xref: /onnv-gate/usr/src/uts/common/os/labelsys.c (revision 3292:f1a92dc5bc08)
11676Sjpk /*
21676Sjpk  * CDDL HEADER START
31676Sjpk  *
41676Sjpk  * The contents of this file are subject to the terms of the
51676Sjpk  * Common Development and Distribution License (the "License").
61676Sjpk  * You may not use this file except in compliance with the License.
71676Sjpk  *
81676Sjpk  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91676Sjpk  * or http://www.opensolaris.org/os/licensing.
101676Sjpk  * See the License for the specific language governing permissions
111676Sjpk  * and limitations under the License.
121676Sjpk  *
131676Sjpk  * When distributing Covered Code, include this CDDL HEADER in each
141676Sjpk  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151676Sjpk  * If applicable, add the following below this CDDL HEADER, with the
161676Sjpk  * fields enclosed by brackets "[]" replaced with your own identifying
171676Sjpk  * information: Portions Copyright [yyyy] [name of copyright owner]
181676Sjpk  *
191676Sjpk  * CDDL HEADER END
201676Sjpk  */
211676Sjpk /*
221676Sjpk  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
231676Sjpk  * Use is subject to license terms.
241676Sjpk  */
251676Sjpk 
261676Sjpk #pragma ident	"%Z%%M%	%I%	%E% SMI"
271676Sjpk 
281676Sjpk #include <sys/systm.h>
291676Sjpk #include <sys/types.h>
301676Sjpk #include <sys/stream.h>
311676Sjpk #include <sys/kmem.h>
321676Sjpk #include <sys/strsubr.h>
331676Sjpk #include <sys/cmn_err.h>
341676Sjpk #include <sys/debug.h>
351676Sjpk #include <sys/param.h>
361676Sjpk #include <sys/model.h>
371676Sjpk #include <sys/errno.h>
381676Sjpk #include <sys/modhash.h>
391676Sjpk 
401676Sjpk #include <sys/policy.h>
411676Sjpk #include <sys/tsol/label.h>
421676Sjpk #include <sys/tsol/tsyscall.h>
431676Sjpk #include <sys/tsol/tndb.h>
441676Sjpk #include <sys/tsol/tnet.h>
451676Sjpk #include <sys/disp.h>
461676Sjpk 
471676Sjpk #include <inet/ip.h>
481676Sjpk #include <inet/ip6.h>
491676Sjpk #include <sys/sdt.h>
501676Sjpk 
511676Sjpk static mod_hash_t *tpc_name_hash;	/* hash of cache entries by name */
521676Sjpk static kmutex_t tpc_lock;
531676Sjpk 
541676Sjpk static tsol_tpc_t *tpc_unlab;
551676Sjpk 
561676Sjpk /*
571676Sjpk  * tnrhc_table and tnrhc_table_v6 are similar to the IP forwarding tables
581676Sjpk  * in organization and search. The tnrhc_table[_v6] is an array of 33/129
591676Sjpk  * pointers to the 33/129 tnrhc tables indexed by the prefix length.
60*3292Skp158701  * A largest prefix match search is done by find_rhc and it walks the
611676Sjpk  * tables from the most specific to the least specific table. Table 0
621676Sjpk  * corresponds to the single entry for 0.0.0.0/0 or ::0/0.
631676Sjpk  */
641676Sjpk tnrhc_hash_t *tnrhc_table[TSOL_MASK_TABLE_SIZE];
651676Sjpk tnrhc_hash_t *tnrhc_table_v6[TSOL_MASK_TABLE_SIZE_V6];
661676Sjpk kmutex_t tnrhc_g_lock;
671676Sjpk 
681676Sjpk static void tsol_create_i_tmpls(void);
691676Sjpk 
701676Sjpk static void tsol_create_i_tnrh(const tnaddr_t *);
711676Sjpk 
721676Sjpk /* List of MLPs on valid on shared addresses */
731676Sjpk static tsol_mlp_list_t shared_mlps;
741676Sjpk 
751676Sjpk /*
761676Sjpk  * Convert length for a mask to the mask.
771676Sjpk  */
781676Sjpk static ipaddr_t
tsol_plen_to_mask(uint_t masklen)791676Sjpk tsol_plen_to_mask(uint_t masklen)
801676Sjpk {
811676Sjpk 	return (masklen == 0 ? 0 : htonl(IP_HOST_MASK << (IP_ABITS - masklen)));
821676Sjpk }
831676Sjpk 
841676Sjpk /*
851676Sjpk  * Convert a prefix length to the mask for that prefix.
861676Sjpk  * Returns the argument bitmask.
871676Sjpk  */
881676Sjpk static void
tsol_plen_to_mask_v6(uint_t plen,in6_addr_t * bitmask)891676Sjpk tsol_plen_to_mask_v6(uint_t plen, in6_addr_t *bitmask)
901676Sjpk {
911676Sjpk 	uint32_t *ptr;
921676Sjpk 
931676Sjpk 	ASSERT(plen <= IPV6_ABITS);
941676Sjpk 
951676Sjpk 	ptr = (uint32_t *)bitmask;
961676Sjpk 	while (plen >= 32) {
971676Sjpk 		*ptr++ = 0xffffffffU;
981676Sjpk 		plen -= 32;
991676Sjpk 	}
1001676Sjpk 	if (plen > 0)
1011676Sjpk 		*ptr++ = htonl(0xffffffff << (32 - plen));
1021676Sjpk 	while (ptr < (uint32_t *)(bitmask + 1))
1031676Sjpk 		*ptr++ = 0;
1041676Sjpk }
1051676Sjpk 
1061676Sjpk boolean_t
tnrhc_init_table(tnrhc_hash_t * table[],short prefix_len,int kmflag)1071676Sjpk tnrhc_init_table(tnrhc_hash_t *table[], short prefix_len, int kmflag)
1081676Sjpk {
1091676Sjpk 	int	i;
1101676Sjpk 
1111676Sjpk 	mutex_enter(&tnrhc_g_lock);
1121676Sjpk 
1131676Sjpk 	if (table[prefix_len] == NULL) {
1141676Sjpk 		table[prefix_len] = (tnrhc_hash_t *)
1151676Sjpk 		    kmem_zalloc(TNRHC_SIZE * sizeof (tnrhc_hash_t), kmflag);
1161676Sjpk 		if (table[prefix_len] == NULL) {
1171676Sjpk 			mutex_exit(&tnrhc_g_lock);
1181676Sjpk 			return (B_FALSE);
1191676Sjpk 		}
1201676Sjpk 		for (i = 0; i < TNRHC_SIZE; i++) {
1211676Sjpk 			mutex_init(&table[prefix_len][i].tnrh_lock,
1221676Sjpk 			    NULL, MUTEX_DEFAULT, 0);
1231676Sjpk 		}
1241676Sjpk 	}
1251676Sjpk 	mutex_exit(&tnrhc_g_lock);
1261676Sjpk 	return (B_TRUE);
1271676Sjpk }
1281676Sjpk 
1291676Sjpk void
tcache_init(void)1301676Sjpk tcache_init(void)
1311676Sjpk {
1321676Sjpk 	tnaddr_t address;
1331676Sjpk 
1341676Sjpk 	/*
1351676Sjpk 	 * Note: unable to use mod_hash_create_strhash here, since it's
1361676Sjpk 	 * assymetric.  It assumes that the user has allocated exactly
1371676Sjpk 	 * strlen(key) + 1 bytes for the key when inserted, and attempts to
1381676Sjpk 	 * kmem_free that memory on a delete.
1391676Sjpk 	 */
1401676Sjpk 	tpc_name_hash = mod_hash_create_extended("tnrhtpc_by_name", 256,
1411676Sjpk 	    mod_hash_null_keydtor,  mod_hash_null_valdtor, mod_hash_bystr,
1421676Sjpk 	    NULL, mod_hash_strkey_cmp, KM_SLEEP);
1431676Sjpk 	mutex_init(&tpc_lock, NULL, MUTEX_DEFAULT, NULL);
1441676Sjpk 
1451676Sjpk 	mutex_init(&tnrhc_g_lock, NULL, MUTEX_DEFAULT, NULL);
1461676Sjpk 
1471676Sjpk 	/* label_init always called before tcache_init */
1481676Sjpk 	ASSERT(l_admin_low != NULL && l_admin_high != NULL);
1491676Sjpk 
1501676Sjpk 	/* Initialize the zeroth table prior to loading the 0.0.0.0 entry */
1511676Sjpk 	(void) tnrhc_init_table(tnrhc_table, 0, KM_SLEEP);
1521676Sjpk 	(void) tnrhc_init_table(tnrhc_table_v6, 0, KM_SLEEP);
1531676Sjpk 	/*
1541676Sjpk 	 * create an internal host template called "_unlab"
1551676Sjpk 	 */
1561676Sjpk 	tsol_create_i_tmpls();
1571676Sjpk 
1581676Sjpk 	/*
1591676Sjpk 	 * create a host entry, 0.0.0.0 = _unlab
1601676Sjpk 	 */
1611676Sjpk 	bzero(&address, sizeof (tnaddr_t));
1621676Sjpk 	address.ta_family = AF_INET;
1631676Sjpk 	tsol_create_i_tnrh(&address);
1641676Sjpk 
1651676Sjpk 	/*
1661676Sjpk 	 * create a host entry, ::0 = _unlab
1671676Sjpk 	 */
1681676Sjpk 	address.ta_family = AF_INET6;
1691676Sjpk 	tsol_create_i_tnrh(&address);
1701676Sjpk 
1711676Sjpk 	rw_init(&shared_mlps.mlpl_rwlock, NULL, RW_DEFAULT, NULL);
1721676Sjpk }
1731676Sjpk 
1741676Sjpk /* Called only by the TNRHC_RELE macro when the refcount goes to zero. */
1751676Sjpk void
tnrhc_free(tsol_tnrhc_t * tnrhc)1761676Sjpk tnrhc_free(tsol_tnrhc_t *tnrhc)
1771676Sjpk {
1781676Sjpk 	/*
1791676Sjpk 	 * We assert rhc_invalid here to make sure that no new thread could
1801676Sjpk 	 * possibly end up finding this entry.  If it could, then the
1811676Sjpk 	 * mutex_destroy would panic.
1821676Sjpk 	 */
1831676Sjpk 	DTRACE_PROBE1(tx__tndb__l3__tnrhcfree, tsol_tnrhc_t *, tnrhc);
1841676Sjpk 	ASSERT(tnrhc->rhc_next == NULL && tnrhc->rhc_invalid);
1851676Sjpk 	mutex_exit(&tnrhc->rhc_lock);
1861676Sjpk 	mutex_destroy(&tnrhc->rhc_lock);
1871676Sjpk 	if (tnrhc->rhc_tpc != NULL)
1881676Sjpk 		TPC_RELE(tnrhc->rhc_tpc);
1891676Sjpk 	kmem_free(tnrhc, sizeof (*tnrhc));
1901676Sjpk }
1911676Sjpk 
1921676Sjpk /* Called only by the TPC_RELE macro when the refcount goes to zero. */
1931676Sjpk void
tpc_free(tsol_tpc_t * tpc)1941676Sjpk tpc_free(tsol_tpc_t *tpc)
1951676Sjpk {
1961676Sjpk 	DTRACE_PROBE1(tx__tndb__l3__tpcfree, tsol_tpc_t *, tpc);
1971676Sjpk 	ASSERT(tpc->tpc_invalid);
1981676Sjpk 	mutex_exit(&tpc->tpc_lock);
1991676Sjpk 	mutex_destroy(&tpc->tpc_lock);
2001676Sjpk 	kmem_free(tpc, sizeof (*tpc));
2011676Sjpk }
2021676Sjpk 
2031676Sjpk /*
2041676Sjpk  * Find and hold a reference to a template entry by name.  Ignores entries that
2051676Sjpk  * are being deleted.
2061676Sjpk  */
2071676Sjpk static tsol_tpc_t *
tnrhtp_find(const char * name,mod_hash_t * hash)2081676Sjpk tnrhtp_find(const char *name, mod_hash_t *hash)
2091676Sjpk {
2101676Sjpk 	mod_hash_val_t hv;
2111676Sjpk 	tsol_tpc_t *tpc = NULL;
2121676Sjpk 
2131676Sjpk 	mutex_enter(&tpc_lock);
2141676Sjpk 	if (mod_hash_find(hash, (mod_hash_key_t)name, &hv) == 0) {
2151676Sjpk 		tpc = (tsol_tpc_t *)hv;
2161676Sjpk 		if (tpc->tpc_invalid)
2171676Sjpk 			tpc = NULL;
2181676Sjpk 		else
2191676Sjpk 			TPC_HOLD(tpc);
2201676Sjpk 	}
2211676Sjpk 	mutex_exit(&tpc_lock);
2221676Sjpk 	return (tpc);
2231676Sjpk }
2241676Sjpk 
2251676Sjpk static int
tnrh_delete(const tsol_rhent_t * rhent)2261676Sjpk tnrh_delete(const tsol_rhent_t *rhent)
2271676Sjpk {
2281676Sjpk 	tsol_tnrhc_t *current;
2291676Sjpk 	tsol_tnrhc_t **prevp;
2301676Sjpk 	ipaddr_t tmpmask;
2311676Sjpk 	in6_addr_t tmpmask_v6;
2321676Sjpk 	tnrhc_hash_t *tnrhc_hash;
2331676Sjpk 
2341676Sjpk 	if (rhent->rh_address.ta_family == AF_INET) {
2351676Sjpk 		if (rhent->rh_prefix < 0 || rhent->rh_prefix > IP_ABITS)
2361676Sjpk 			return (EINVAL);
2371676Sjpk 		if (tnrhc_table[rhent->rh_prefix] == NULL)
2381676Sjpk 			return (ENOENT);
2391676Sjpk 		tmpmask = tsol_plen_to_mask(rhent->rh_prefix);
2401676Sjpk 		tnrhc_hash = &tnrhc_table[rhent->rh_prefix][
2411676Sjpk 		    TSOL_ADDR_HASH(rhent->rh_address.ta_addr_v4.s_addr &
2421676Sjpk 		    tmpmask, TNRHC_SIZE)];
2431676Sjpk 	} else if (rhent->rh_address.ta_family == AF_INET6) {
2441676Sjpk 		if (rhent->rh_prefix < 0 || rhent->rh_prefix > IPV6_ABITS)
2451676Sjpk 			return (EINVAL);
2461676Sjpk 		if (tnrhc_table_v6[rhent->rh_prefix] == NULL)
2471676Sjpk 			return (ENOENT);
2481676Sjpk 		tsol_plen_to_mask_v6(rhent->rh_prefix, &tmpmask_v6);
2491676Sjpk 		tnrhc_hash = &tnrhc_table_v6[rhent->rh_prefix][
2501676Sjpk 		    TSOL_ADDR_MASK_HASH_V6(rhent->rh_address.ta_addr_v6,
2511676Sjpk 		    tmpmask_v6, TNRHC_SIZE)];
2521676Sjpk 	} else {
2531676Sjpk 		return (EAFNOSUPPORT);
2541676Sjpk 	}
2551676Sjpk 
2561676Sjpk 	/* search for existing entry */
2571676Sjpk 	mutex_enter(&tnrhc_hash->tnrh_lock);
2581676Sjpk 	prevp = &tnrhc_hash->tnrh_list;
2591676Sjpk 	while ((current = *prevp) != NULL) {
2601676Sjpk 		if (TNADDR_EQ(&rhent->rh_address, &current->rhc_host))
2611676Sjpk 			break;
2621676Sjpk 		prevp = &current->rhc_next;
2631676Sjpk 	}
2641676Sjpk 
2651676Sjpk 	if (current != NULL) {
2661676Sjpk 		DTRACE_PROBE(tx__tndb__l2__tnrhdelete_existingrhentry);
2671676Sjpk 		*prevp = current->rhc_next;
2681676Sjpk 		mutex_enter(&current->rhc_lock);
2691676Sjpk 		current->rhc_next = NULL;
2701676Sjpk 		current->rhc_invalid = 1;
2711676Sjpk 		mutex_exit(&current->rhc_lock);
2721676Sjpk 		TNRHC_RELE(current);
2731676Sjpk 	}
2741676Sjpk 	mutex_exit(&tnrhc_hash->tnrh_lock);
2751676Sjpk 	return (current == NULL ? ENOENT : 0);
2761676Sjpk }
2771676Sjpk 
2781676Sjpk /*
2791676Sjpk  * Flush all remote host entries from the database.
2801676Sjpk  *
2811676Sjpk  * Note that the htable arrays themselves do not have reference counters, so,
2821676Sjpk  * unlike the remote host entries, they cannot be freed.
2831676Sjpk  */
2841676Sjpk static void
flush_rh_table(tnrhc_hash_t ** htable,int nbits)2851676Sjpk flush_rh_table(tnrhc_hash_t **htable, int nbits)
2861676Sjpk {
2871676Sjpk 	tnrhc_hash_t *hent, *hend;
2881676Sjpk 	tsol_tnrhc_t *rhc, *rhnext;
2891676Sjpk 
2901676Sjpk 	while (--nbits >= 0) {
2911676Sjpk 		if ((hent = htable[nbits]) == NULL)
2921676Sjpk 			continue;
2931676Sjpk 		hend = hent + TNRHC_SIZE;
2941676Sjpk 		while (hent < hend) {
2951676Sjpk 			/*
2961676Sjpk 			 * List walkers hold this lock during the walk.  It
2971676Sjpk 			 * protects tnrh_list and rhc_next.
2981676Sjpk 			 */
2991676Sjpk 			mutex_enter(&hent->tnrh_lock);
3001676Sjpk 			rhnext = hent->tnrh_list;
3011676Sjpk 			hent->tnrh_list = NULL;
3021676Sjpk 			mutex_exit(&hent->tnrh_lock);
3031676Sjpk 			/*
3041676Sjpk 			 * There may still be users of the rhcs at this point,
3051676Sjpk 			 * but not of the list or its next pointer.  Thus, the
3061676Sjpk 			 * only thing that would need to be done under a lock
3071676Sjpk 			 * is setting the invalid bit, but that's atomic
3081676Sjpk 			 * anyway, so no locks needed here.
3091676Sjpk 			 */
3101676Sjpk 			while ((rhc = rhnext) != NULL) {
3111676Sjpk 				rhnext = rhc->rhc_next;
3121676Sjpk 				rhc->rhc_next = NULL;
3131676Sjpk 				rhc->rhc_invalid = 1;
3141676Sjpk 				TNRHC_RELE(rhc);
3151676Sjpk 			}
3161676Sjpk 			hent++;
3171676Sjpk 		}
3181676Sjpk 	}
3191676Sjpk }
3201676Sjpk 
3211676Sjpk /*
3221676Sjpk  * Load a remote host entry into kernel cache.  Create a new one if a matching
3231676Sjpk  * entry isn't found, otherwise replace the contents of the previous one by
3241676Sjpk  * deleting it and recreating it.  (Delete and recreate is used to avoid
3251676Sjpk  * allowing other threads to see an unstable data structure.)
3261676Sjpk  *
3271676Sjpk  * A "matching" entry is the one whose address matches that of the one
3281676Sjpk  * being loaded.
3291676Sjpk  *
3301676Sjpk  * Return 0 for success, error code for failure.
3311676Sjpk  */
332*3292Skp158701 static int
tnrh_hash_add(tsol_tnrhc_t * new,short prefix)333*3292Skp158701 tnrh_hash_add(tsol_tnrhc_t *new, short prefix)
3341676Sjpk {
3351676Sjpk 	tsol_tnrhc_t **rhp;
336*3292Skp158701 	tsol_tnrhc_t *rh;
3371676Sjpk 	ipaddr_t tmpmask;
3381676Sjpk 	in6_addr_t tmpmask_v6;
3391676Sjpk 	tnrhc_hash_t *tnrhc_hash;
3401676Sjpk 
3411676Sjpk 	/* Find the existing entry, if any, leaving the hash locked */
342*3292Skp158701 	if (new->rhc_host.ta_family == AF_INET) {
343*3292Skp158701 		if (prefix < 0 || prefix > IP_ABITS)
3441676Sjpk 			return (EINVAL);
345*3292Skp158701 		if (tnrhc_table[prefix] == NULL &&
346*3292Skp158701 		    !tnrhc_init_table(tnrhc_table, prefix,
3471676Sjpk 		    KM_NOSLEEP))
3481676Sjpk 			return (ENOMEM);
349*3292Skp158701 		tmpmask = tsol_plen_to_mask(prefix);
350*3292Skp158701 		tnrhc_hash = &tnrhc_table[prefix][
351*3292Skp158701 		    TSOL_ADDR_HASH(new->rhc_host.ta_addr_v4.s_addr &
3521676Sjpk 		    tmpmask, TNRHC_SIZE)];
3531676Sjpk 		mutex_enter(&tnrhc_hash->tnrh_lock);
3541676Sjpk 		for (rhp = &tnrhc_hash->tnrh_list; (rh = *rhp) != NULL;
3551676Sjpk 		    rhp = &rh->rhc_next) {
3561676Sjpk 			ASSERT(rh->rhc_host.ta_family == AF_INET);
3571676Sjpk 			if (((rh->rhc_host.ta_addr_v4.s_addr ^
358*3292Skp158701 			    new->rhc_host.ta_addr_v4.s_addr) & tmpmask) ==
3591676Sjpk 			    0)
3601676Sjpk 				break;
3611676Sjpk 		}
362*3292Skp158701 	} else if (new->rhc_host.ta_family == AF_INET6) {
363*3292Skp158701 		if (prefix < 0 || prefix > IPV6_ABITS)
3641676Sjpk 			return (EINVAL);
365*3292Skp158701 		if (tnrhc_table_v6[prefix] == NULL &&
366*3292Skp158701 		    !tnrhc_init_table(tnrhc_table_v6, prefix,
3671676Sjpk 		    KM_NOSLEEP))
3681676Sjpk 			return (ENOMEM);
369*3292Skp158701 		tsol_plen_to_mask_v6(prefix, &tmpmask_v6);
370*3292Skp158701 		tnrhc_hash = &tnrhc_table_v6[prefix][
371*3292Skp158701 		    TSOL_ADDR_MASK_HASH_V6(new->rhc_host.ta_addr_v6,
3721676Sjpk 		    tmpmask_v6, TNRHC_SIZE)];
3731676Sjpk 		mutex_enter(&tnrhc_hash->tnrh_lock);
3741676Sjpk 		for (rhp = &tnrhc_hash->tnrh_list; (rh = *rhp) != NULL;
3751676Sjpk 		    rhp = &rh->rhc_next) {
3761676Sjpk 			ASSERT(rh->rhc_host.ta_family == AF_INET6);
3771676Sjpk 			if (V6_MASK_EQ_2(rh->rhc_host.ta_addr_v6, tmpmask_v6,
378*3292Skp158701 			    new->rhc_host.ta_addr_v6))
3791676Sjpk 				break;
3801676Sjpk 		}
3811676Sjpk 	} else {
3821676Sjpk 		return (EAFNOSUPPORT);
3831676Sjpk 	}
3841676Sjpk 
3851676Sjpk 	/* Clobber the old remote host entry. */
3861676Sjpk 	if (rh != NULL) {
3871676Sjpk 		ASSERT(!rh->rhc_invalid);
3881676Sjpk 		rh->rhc_invalid = 1;
3891676Sjpk 		*rhp = rh->rhc_next;
3901676Sjpk 		rh->rhc_next = NULL;
391*3292Skp158701 		DTRACE_PROBE1(tx__tndb__l2__tnrhhashadd__invalidaterh,
392*3292Skp158701 		    tsol_tnrhc_t *, rh);
3931676Sjpk 		TNRHC_RELE(rh);
3941676Sjpk 	}
3951676Sjpk 
396*3292Skp158701 	TNRHC_HOLD(new);
397*3292Skp158701 	new->rhc_next = tnrhc_hash->tnrh_list;
398*3292Skp158701 	tnrhc_hash->tnrh_list = new;
399*3292Skp158701 	DTRACE_PROBE1(tx__tndb__l2__tnrhhashadd__addedrh, tsol_tnrhc_t *, new);
400*3292Skp158701 	mutex_exit(&tnrhc_hash->tnrh_lock);
401*3292Skp158701 
402*3292Skp158701 	return (0);
403*3292Skp158701 }
404*3292Skp158701 
405*3292Skp158701 /*
406*3292Skp158701  * Load a remote host entry into kernel cache.
407*3292Skp158701  *
408*3292Skp158701  * Return 0 for success, error code for failure.
409*3292Skp158701  */
410*3292Skp158701 int
tnrh_load(const tsol_rhent_t * rhent)411*3292Skp158701 tnrh_load(const tsol_rhent_t *rhent)
412*3292Skp158701 {
413*3292Skp158701 	tsol_tnrhc_t *new;
414*3292Skp158701 	tsol_tpc_t *tpc;
415*3292Skp158701 	int status;
416*3292Skp158701 
417*3292Skp158701 	/* Find and bump the reference count on the named template */
418*3292Skp158701 	if ((tpc = tnrhtp_find(rhent->rh_template, tpc_name_hash)) == NULL) {
419*3292Skp158701 		return (EINVAL);
420*3292Skp158701 	}
421*3292Skp158701 	ASSERT(tpc->tpc_tp.host_type == UNLABELED ||
422*3292Skp158701 	    tpc->tpc_tp.host_type == SUN_CIPSO);
423*3292Skp158701 
424*3292Skp158701 	if ((new = kmem_zalloc(sizeof (*new), KM_NOSLEEP)) == NULL) {
425*3292Skp158701 		TPC_RELE(tpc);
426*3292Skp158701 		return (ENOMEM);
427*3292Skp158701 	}
428*3292Skp158701 
4291676Sjpk 	/* Initialize the new entry. */
4301676Sjpk 	mutex_init(&new->rhc_lock, NULL, MUTEX_DEFAULT, NULL);
4311676Sjpk 	new->rhc_host = rhent->rh_address;
4321676Sjpk 
4331676Sjpk 	/* The rhc now owns this tpc reference, so no TPC_RELE past here */
4341676Sjpk 	new->rhc_tpc = tpc;
4351676Sjpk 
436*3292Skp158701 	/*
437*3292Skp158701 	 * tnrh_hash_add handles the tnrh entry ref count for hash
438*3292Skp158701 	 * table inclusion. The ref count is incremented and decremented
439*3292Skp158701 	 * here to trigger deletion of the new hash table entry in the
440*3292Skp158701 	 * event that tnrh_hash_add fails.
441*3292Skp158701 	 */
4421676Sjpk 	TNRHC_HOLD(new);
443*3292Skp158701 	status = tnrh_hash_add(new, rhent->rh_prefix);
444*3292Skp158701 	TNRHC_RELE(new);
4451676Sjpk 
446*3292Skp158701 	return (status);
4471676Sjpk }
4481676Sjpk 
4491676Sjpk static int
tnrh_get(tsol_rhent_t * rhent)4501676Sjpk tnrh_get(tsol_rhent_t *rhent)
4511676Sjpk {
4521676Sjpk 	tsol_tpc_t *tpc;
4531676Sjpk 
4541676Sjpk 	switch (rhent->rh_address.ta_family) {
4551676Sjpk 	case AF_INET:
4561676Sjpk 		tpc = find_tpc(&rhent->rh_address.ta_addr_v4, IPV4_VERSION,
4571676Sjpk 		    B_TRUE);
4581676Sjpk 		break;
4591676Sjpk 
4601676Sjpk 	case AF_INET6:
4611676Sjpk 		tpc = find_tpc(&rhent->rh_address.ta_addr_v6, IPV6_VERSION,
4621676Sjpk 		    B_TRUE);
4631676Sjpk 		break;
4641676Sjpk 
4651676Sjpk 	default:
4661676Sjpk 		return (EINVAL);
4671676Sjpk 	}
4681676Sjpk 	if (tpc == NULL)
4691676Sjpk 		return (ENOENT);
4701676Sjpk 
4711676Sjpk 	DTRACE_PROBE2(tx__tndb__l4__tnrhget__foundtpc, tsol_rhent_t *,
4721676Sjpk 	    rhent, tsol_tpc_t *, tpc);
4731676Sjpk 	bcopy(tpc->tpc_tp.name, rhent->rh_template,
4741676Sjpk 	    sizeof (rhent->rh_template));
4751676Sjpk 	TPC_RELE(tpc);
4761676Sjpk 	return (0);
4771676Sjpk }
4781676Sjpk 
4791676Sjpk static boolean_t
template_name_ok(const char * name)4801676Sjpk template_name_ok(const char *name)
4811676Sjpk {
4821676Sjpk 	const char *name_end = name + TNTNAMSIZ;
4831676Sjpk 
4841676Sjpk 	while (name < name_end) {
4851676Sjpk 		if (*name == '\0')
4861676Sjpk 			break;
4871676Sjpk 		name++;
4881676Sjpk 	}
4891676Sjpk 	return (name < name_end);
4901676Sjpk }
4911676Sjpk 
4921676Sjpk static int
tnrh(int cmd,void * buf)4931676Sjpk tnrh(int cmd, void *buf)
4941676Sjpk {
4951676Sjpk 	int retv;
4961676Sjpk 	tsol_rhent_t rhent;
4971676Sjpk 
4981676Sjpk 	/* Make sure user has sufficient privilege */
4991676Sjpk 	if (cmd != TNDB_GET &&
5001676Sjpk 	    (retv = secpolicy_net_config(CRED(), B_FALSE)) != 0)
5011676Sjpk 		return (set_errno(retv));
5021676Sjpk 
5031676Sjpk 	/*
5041676Sjpk 	 * Get arguments
5051676Sjpk 	 */
5061676Sjpk 	if (cmd != TNDB_FLUSH &&
5071676Sjpk 	    copyin(buf, &rhent, sizeof (rhent)) != 0) {
5081676Sjpk 		DTRACE_PROBE(tx__tndb__l0__tnrhdelete__copyin);
5091676Sjpk 		return (set_errno(EFAULT));
5101676Sjpk 	}
5111676Sjpk 
5121676Sjpk 	switch (cmd) {
5131676Sjpk 	case TNDB_LOAD:
5141676Sjpk 		DTRACE_PROBE(tx__tndb__l2__tnrhdelete__tndbload);
5151676Sjpk 		if (!template_name_ok(rhent.rh_template)) {
5161676Sjpk 			retv = EINVAL;
5171676Sjpk 		} else {
5181676Sjpk 			retv = tnrh_load(&rhent);
5191676Sjpk 		}
5201676Sjpk 		break;
5211676Sjpk 
5221676Sjpk 	case TNDB_DELETE:
5231676Sjpk 		DTRACE_PROBE(tx__tndb__l2__tnrhdelete__tndbdelete);
5241676Sjpk 		retv = tnrh_delete(&rhent);
5251676Sjpk 		break;
5261676Sjpk 
5271676Sjpk 	case TNDB_GET:
5281676Sjpk 		DTRACE_PROBE(tx__tndb__l4__tnrhdelete__tndbget);
5291676Sjpk 		if (!template_name_ok(rhent.rh_template)) {
5301676Sjpk 			retv = EINVAL;
5311676Sjpk 			break;
5321676Sjpk 		}
5331676Sjpk 
5341676Sjpk 		retv = tnrh_get(&rhent);
5351676Sjpk 		if (retv != 0)
5361676Sjpk 			break;
5371676Sjpk 
5381676Sjpk 		/*
5391676Sjpk 		 * Copy out result
5401676Sjpk 		 */
5411676Sjpk 		if (copyout(&rhent, buf, sizeof (rhent)) != 0) {
5421676Sjpk 			DTRACE_PROBE(tx__tndb__l0__tnrhdelete__copyout);
5431676Sjpk 			retv = EFAULT;
5441676Sjpk 		}
5451676Sjpk 		break;
5461676Sjpk 
5471676Sjpk 	case TNDB_FLUSH:
5481676Sjpk 		DTRACE_PROBE(tx__tndb__l2__tnrhdelete__flush);
5491676Sjpk 		flush_rh_table(tnrhc_table, TSOL_MASK_TABLE_SIZE);
5501676Sjpk 		flush_rh_table(tnrhc_table_v6, TSOL_MASK_TABLE_SIZE_V6);
5511676Sjpk 		break;
5521676Sjpk 
5531676Sjpk 	default:
5541676Sjpk 		DTRACE_PROBE1(tx__tndb__l0__tnrhdelete__unknowncmd,
5551676Sjpk 		    int, cmd);
5561676Sjpk 		retv = EOPNOTSUPP;
5571676Sjpk 		break;
5581676Sjpk 	}
5591676Sjpk 
5601676Sjpk 	if (retv != 0)
5611676Sjpk 		return (set_errno(retv));
5621676Sjpk 	else
5631676Sjpk 		return (retv);
5641676Sjpk }
5651676Sjpk 
5661676Sjpk static tsol_tpc_t *
tnrhtp_create(const tsol_tpent_t * tpent,int kmflags)5671676Sjpk tnrhtp_create(const tsol_tpent_t *tpent, int kmflags)
5681676Sjpk {
5691676Sjpk 	tsol_tpc_t *tpc;
5701676Sjpk 	mod_hash_val_t hv;
5711676Sjpk 
5721676Sjpk 	/*
5731676Sjpk 	 * We intentionally allocate a new entry before taking the lock on the
5741676Sjpk 	 * entire database.
5751676Sjpk 	 */
5761676Sjpk 	if ((tpc = kmem_zalloc(sizeof (*tpc), kmflags)) == NULL)
5771676Sjpk 		return (NULL);
5781676Sjpk 
5791676Sjpk 	mutex_enter(&tpc_lock);
5801676Sjpk 	if (mod_hash_find(tpc_name_hash, (mod_hash_key_t)tpent->name,
5811676Sjpk 	    &hv) == 0) {
5821676Sjpk 		tsol_tpc_t *found_tpc = (tsol_tpc_t *)hv;
5831676Sjpk 
5841676Sjpk 		found_tpc->tpc_invalid = 1;
5851676Sjpk 		(void) mod_hash_destroy(tpc_name_hash,
5861676Sjpk 		    (mod_hash_key_t)tpent->name);
5871676Sjpk 		TPC_RELE(found_tpc);
5881676Sjpk 	}
5891676Sjpk 
5901676Sjpk 	mutex_init(&tpc->tpc_lock, NULL, MUTEX_DEFAULT, NULL);
5911676Sjpk 	/* tsol_tpent_t is the same on LP64 and ILP32 */
5921676Sjpk 	bcopy(tpent, &tpc->tpc_tp, sizeof (tpc->tpc_tp));
5931676Sjpk 	(void) mod_hash_insert(tpc_name_hash, (mod_hash_key_t)tpc->tpc_tp.name,
5941676Sjpk 	    (mod_hash_val_t)tpc);
5951676Sjpk 	TPC_HOLD(tpc);
5961676Sjpk 	mutex_exit(&tpc_lock);
5971676Sjpk 
5981676Sjpk 	return (tpc);
5991676Sjpk }
6001676Sjpk 
6011676Sjpk static int
tnrhtp_delete(const char * tname)6021676Sjpk tnrhtp_delete(const char *tname)
6031676Sjpk {
6041676Sjpk 	tsol_tpc_t *tpc;
6051676Sjpk 	mod_hash_val_t hv;
6061676Sjpk 	int retv = ENOENT;
6071676Sjpk 
6081676Sjpk 	mutex_enter(&tpc_lock);
6091676Sjpk 	if (mod_hash_find(tpc_name_hash, (mod_hash_key_t)tname, &hv) == 0) {
6101676Sjpk 		tpc = (tsol_tpc_t *)hv;
6111676Sjpk 		ASSERT(!tpc->tpc_invalid);
6121676Sjpk 		tpc->tpc_invalid = 1;
6131676Sjpk 		(void) mod_hash_destroy(tpc_name_hash,
6141676Sjpk 		    (mod_hash_key_t)tpc->tpc_tp.name);
6151676Sjpk 		TPC_RELE(tpc);
6161676Sjpk 		retv = 0;
6171676Sjpk 	}
6181676Sjpk 	mutex_exit(&tpc_lock);
6191676Sjpk 	return (retv);
6201676Sjpk }
6211676Sjpk 
6221676Sjpk /* ARGSUSED */
6231676Sjpk static uint_t
tpc_delete(mod_hash_key_t key,mod_hash_val_t * val,void * arg)6241676Sjpk tpc_delete(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
6251676Sjpk {
6261676Sjpk 	tsol_tpc_t *tpc = (tsol_tpc_t *)val;
6271676Sjpk 
6281676Sjpk 	ASSERT(!tpc->tpc_invalid);
6291676Sjpk 	tpc->tpc_invalid = 1;
6301676Sjpk 	TPC_RELE(tpc);
6311676Sjpk 	return (MH_WALK_CONTINUE);
6321676Sjpk }
6331676Sjpk 
6341676Sjpk static void
tnrhtp_flush(void)6351676Sjpk tnrhtp_flush(void)
6361676Sjpk {
6371676Sjpk 	mutex_enter(&tpc_lock);
6381676Sjpk 	mod_hash_walk(tpc_name_hash, tpc_delete, NULL);
6391676Sjpk 	mod_hash_clear(tpc_name_hash);
6401676Sjpk 	mutex_exit(&tpc_lock);
6411676Sjpk }
6421676Sjpk 
6431676Sjpk static int
tnrhtp(int cmd,void * buf)6441676Sjpk tnrhtp(int cmd, void *buf)
6451676Sjpk {
6461676Sjpk 	int retv;
6471676Sjpk 	int type;
6481676Sjpk 	tsol_tpent_t rhtpent;
6491676Sjpk 	tsol_tpc_t *tpc;
6501676Sjpk 
6511676Sjpk 	/* Make sure user has sufficient privilege */
6521676Sjpk 	if (cmd != TNDB_GET &&
6531676Sjpk 	    (retv = secpolicy_net_config(CRED(), B_FALSE)) != 0)
6541676Sjpk 		return (set_errno(retv));
6551676Sjpk 
6561676Sjpk 	/*
6571676Sjpk 	 * Get argument.  Note that tsol_tpent_t is the same on LP64 and ILP32,
6581676Sjpk 	 * so no special handling is required.
6591676Sjpk 	 */
6601676Sjpk 	if (cmd != TNDB_FLUSH) {
6611676Sjpk 		if (copyin(buf, &rhtpent, sizeof (rhtpent)) != 0) {
6621676Sjpk 			DTRACE_PROBE(tx__tndb__l0__tnrhtp__copyin);
6631676Sjpk 			return (set_errno(EFAULT));
6641676Sjpk 		}
6651676Sjpk 
6661676Sjpk 		/*
6671676Sjpk 		 * Don't let the user give us a bogus (unterminated) template
6681676Sjpk 		 * name.
6691676Sjpk 		 */
6701676Sjpk 		if (!template_name_ok(rhtpent.name))
6711676Sjpk 			return (set_errno(EINVAL));
6721676Sjpk 	}
6731676Sjpk 
6741676Sjpk 	switch (cmd) {
6751676Sjpk 	case TNDB_LOAD:
6761676Sjpk 		DTRACE_PROBE1(tx__tndb__l2__tnrhtp__tndbload, char *,
6771676Sjpk 			rhtpent.name);
6781676Sjpk 		type = rhtpent.host_type;
6791676Sjpk 		if (type != UNLABELED && type != SUN_CIPSO) {
6801676Sjpk 			retv = EINVAL;
6811676Sjpk 			break;
6821676Sjpk 		}
6831676Sjpk 
6841676Sjpk 		if (tnrhtp_create(&rhtpent, KM_NOSLEEP) == NULL)
6851676Sjpk 			retv = ENOMEM;
6861676Sjpk 		else
6871676Sjpk 			retv = 0;
6881676Sjpk 		break;
6891676Sjpk 
6901676Sjpk 	case TNDB_GET:
6911676Sjpk 		DTRACE_PROBE1(tx__tndb__l4__tnrhtp__tndbget, char *,
6921676Sjpk 		    rhtpent.name);
6931676Sjpk 		tpc = tnrhtp_find(rhtpent.name, tpc_name_hash);
6941676Sjpk 		if (tpc == NULL) {
6951676Sjpk 			retv = ENOENT;
6961676Sjpk 			break;
6971676Sjpk 		}
6981676Sjpk 
6991676Sjpk 		/* Copy out result */
7001676Sjpk 		if (copyout(&tpc->tpc_tp, buf, sizeof (tpc->tpc_tp)) != 0) {
7011676Sjpk 			DTRACE_PROBE(tx__tndb__l0__tnrhtp__copyout);
7021676Sjpk 			retv = EFAULT;
7031676Sjpk 		} else {
7041676Sjpk 			retv = 0;
7051676Sjpk 		}
7061676Sjpk 		TPC_RELE(tpc);
7071676Sjpk 		break;
7081676Sjpk 
7091676Sjpk 	case TNDB_DELETE:
7101676Sjpk 		DTRACE_PROBE1(tx__tndb__l4__tnrhtp__tndbdelete, char *,
7111676Sjpk 		    rhtpent.name);
7121676Sjpk 		retv = tnrhtp_delete(rhtpent.name);
7131676Sjpk 		break;
7141676Sjpk 
7151676Sjpk 	case TNDB_FLUSH:
7161676Sjpk 		DTRACE_PROBE(tx__tndb__l4__tnrhtp__flush);
7171676Sjpk 		tnrhtp_flush();
7181676Sjpk 		retv = 0;
7191676Sjpk 		break;
7201676Sjpk 
7211676Sjpk 	default:
7221676Sjpk 		DTRACE_PROBE1(tx__tndb__l0__tnrhtp__unknowncmd, int,
7231676Sjpk 		    cmd);
7241676Sjpk 		retv = EOPNOTSUPP;
7251676Sjpk 		break;
7261676Sjpk 	}
7271676Sjpk 
7281676Sjpk 	if (retv != 0)
7291676Sjpk 		return (set_errno(retv));
7301676Sjpk 	else
7311676Sjpk 		return (retv);
7321676Sjpk }
7331676Sjpk 
7341676Sjpk /*
7351676Sjpk  * MLP entry ordering logic
7361676Sjpk  *
7371676Sjpk  * There are two loops in this routine.  The first loop finds the entry that
7381676Sjpk  * either logically follows the new entry to be inserted, or is the entry that
7391676Sjpk  * precedes and overlaps the new entry, or is NULL to mean end-of-list.  This
7401676Sjpk  * is 'tme.'  The second loop scans ahead from that point to find any overlap
7411676Sjpk  * on the front or back of this new entry.
7421676Sjpk  *
7431676Sjpk  * For the first loop, we can have the following cases in the list (note that
7441676Sjpk  * the port-portmax range is inclusive):
7451676Sjpk  *
7461676Sjpk  *	       port   portmax
7471676Sjpk  *		+--------+
7481676Sjpk  * 1: +------+ ................... precedes; skip to next
7491676Sjpk  * 2:	    +------+ ............. overlaps; stop here if same protocol
7501676Sjpk  * 3:		+------+ ......... overlaps; stop if same or higher protocol
7511676Sjpk  * 4:		    +-------+ .... overlaps or succeeds; stop here
7521676Sjpk  *
7531676Sjpk  * For the second loop, we can have the following cases (note that we need not
7541676Sjpk  * care about other protocol entries at this point, because we're only looking
7551676Sjpk  * for overlap, not an insertion point):
7561676Sjpk  *
7571676Sjpk  *	       port   portmax
7581676Sjpk  *		+--------+
7591676Sjpk  * 5:	    +------+ ............. overlaps; stop if same protocol
7601676Sjpk  * 6:		+------+ ......... overlaps; stop if same protocol
7611676Sjpk  * 7:		    +-------+ .... overlaps; stop if same protocol
7621676Sjpk  * 8:			   +---+ . follows; search is done
7631676Sjpk  *
7641676Sjpk  * In other words, this second search needs to consider only whether the entry
7651676Sjpk  * has a starting port number that's greater than the end point of the new
7661676Sjpk  * entry.  All others are overlaps.
7671676Sjpk  */
7681676Sjpk static int
mlp_add_del(tsol_mlp_list_t * mlpl,zoneid_t zoneid,uint8_t proto,uint16_t port,uint16_t portmax,boolean_t addflag)7691676Sjpk mlp_add_del(tsol_mlp_list_t *mlpl, zoneid_t zoneid, uint8_t proto,
7701676Sjpk     uint16_t port, uint16_t portmax, boolean_t addflag)
7711676Sjpk {
7721676Sjpk 	int retv;
7731676Sjpk 	tsol_mlp_entry_t *tme, *tme2, *newent;
7741676Sjpk 
7751676Sjpk 	if (addflag) {
7761676Sjpk 		if ((newent = kmem_zalloc(sizeof (*newent), KM_NOSLEEP)) ==
7771676Sjpk 		    NULL)
7781676Sjpk 			return (ENOMEM);
7791676Sjpk 	} else {
7801676Sjpk 		newent = NULL;
7811676Sjpk 	}
7821676Sjpk 	rw_enter(&mlpl->mlpl_rwlock, RW_WRITER);
7831676Sjpk 
7841676Sjpk 	/*
7851676Sjpk 	 * First loop: find logical insertion point or overlap.  Table is kept
7861676Sjpk 	 * in order of port number first, and then, within that, by protocol
7871676Sjpk 	 * number.
7881676Sjpk 	 */
7891676Sjpk 	for (tme = mlpl->mlpl_first; tme != NULL; tme = tme->mlpe_next) {
7901676Sjpk 		/* logically next (case 4) */
7911676Sjpk 		if (tme->mlpe_mlp.mlp_port > port)
7921676Sjpk 			break;
7931676Sjpk 		/* if this is logically next or overlap, then stop (case 3) */
7941676Sjpk 		if (tme->mlpe_mlp.mlp_port == port &&
7951676Sjpk 		    tme->mlpe_mlp.mlp_ipp >= proto)
7961676Sjpk 			break;
7971676Sjpk 		/* earlier or same port sequence; check for overlap (case 2) */
7981676Sjpk 		if (tme->mlpe_mlp.mlp_ipp == proto &&
7991676Sjpk 		    tme->mlpe_mlp.mlp_port_upper >= port)
8001676Sjpk 			break;
8011676Sjpk 		/* otherwise, loop again (case 1) */
8021676Sjpk 	}
8031676Sjpk 
8041676Sjpk 	/* Second loop: scan ahead for overlap */
8051676Sjpk 	for (tme2 = tme; tme2 != NULL; tme2 = tme2->mlpe_next) {
8061676Sjpk 		/* check if entry follows; no overlap (case 8) */
8071676Sjpk 		if (tme2->mlpe_mlp.mlp_port > portmax) {
8081676Sjpk 			tme2 = NULL;
8091676Sjpk 			break;
8101676Sjpk 		}
8111676Sjpk 		/* only exact protocol matches at this point (cases 5-7) */
8121676Sjpk 		if (tme2->mlpe_mlp.mlp_ipp == proto)
8131676Sjpk 			break;
8141676Sjpk 	}
8151676Sjpk 
8161676Sjpk 	retv = 0;
8171676Sjpk 	if (addflag) {
8181676Sjpk 		if (tme2 != NULL) {
8191676Sjpk 			retv = EEXIST;
8201676Sjpk 		} else {
8211676Sjpk 			newent->mlpe_zoneid = zoneid;
8221676Sjpk 			newent->mlpe_mlp.mlp_ipp = proto;
8231676Sjpk 			newent->mlpe_mlp.mlp_port = port;
8241676Sjpk 			newent->mlpe_mlp.mlp_port_upper = portmax;
8251676Sjpk 			newent->mlpe_next = tme;
8261676Sjpk 			if (tme == NULL) {
8271676Sjpk 				tme2 = mlpl->mlpl_last;
8281676Sjpk 				mlpl->mlpl_last = newent;
8291676Sjpk 			} else {
8301676Sjpk 				tme2 = tme->mlpe_prev;
8311676Sjpk 				tme->mlpe_prev = newent;
8321676Sjpk 			}
8331676Sjpk 			newent->mlpe_prev = tme2;
8341676Sjpk 			if (tme2 == NULL)
8351676Sjpk 				mlpl->mlpl_first = newent;
8361676Sjpk 			else
8371676Sjpk 				tme2->mlpe_next = newent;
8381676Sjpk 			newent = NULL;
8391676Sjpk 		}
8401676Sjpk 	} else {
8411676Sjpk 		if (tme2 == NULL || tme2->mlpe_mlp.mlp_port != port ||
8421676Sjpk 		    tme2->mlpe_mlp.mlp_port_upper != portmax) {
8431676Sjpk 			retv = ENOENT;
8441676Sjpk 		} else {
8451676Sjpk 			if ((tme2 = tme->mlpe_prev) == NULL)
8461676Sjpk 				mlpl->mlpl_first = tme->mlpe_next;
8471676Sjpk 			else
8481676Sjpk 				tme2->mlpe_next = tme->mlpe_next;
8491676Sjpk 			if ((tme2 = tme->mlpe_next) == NULL)
8501676Sjpk 				mlpl->mlpl_last = tme->mlpe_prev;
8511676Sjpk 			else
8521676Sjpk 				tme2->mlpe_prev = tme->mlpe_prev;
8531676Sjpk 			newent = tme;
8541676Sjpk 		}
8551676Sjpk 	}
8561676Sjpk 	rw_exit(&mlpl->mlpl_rwlock);
8571676Sjpk 
8581676Sjpk 	if (newent != NULL)
8591676Sjpk 		kmem_free(newent, sizeof (*newent));
8601676Sjpk 
8611676Sjpk 	return (retv);
8621676Sjpk }
8631676Sjpk 
8641676Sjpk /*
8651676Sjpk  * Add or remove an MLP entry from the database so that the classifier can find
8661676Sjpk  * it.
8671676Sjpk  *
8681676Sjpk  * Note: port number is in host byte order.
8691676Sjpk  */
8701676Sjpk int
tsol_mlp_anon(zone_t * zone,mlp_type_t mlptype,uchar_t proto,uint16_t port,boolean_t addflag)8711676Sjpk tsol_mlp_anon(zone_t *zone, mlp_type_t mlptype, uchar_t proto, uint16_t port,
8721676Sjpk     boolean_t addflag)
8731676Sjpk {
8741676Sjpk 	int retv = 0;
8751676Sjpk 
8761676Sjpk 	if (mlptype == mlptBoth || mlptype == mlptPrivate)
8771676Sjpk 		retv = mlp_add_del(&zone->zone_mlps, zone->zone_id, proto,
8781676Sjpk 		    port, port, addflag);
8791676Sjpk 	if ((retv == 0 || !addflag) &&
8801676Sjpk 	    (mlptype == mlptBoth || mlptype == mlptShared)) {
8811676Sjpk 		retv = mlp_add_del(&shared_mlps, zone->zone_id, proto, port,
8821676Sjpk 		    port, addflag);
8831676Sjpk 		if (retv != 0 && addflag)
8841676Sjpk 			(void) mlp_add_del(&zone->zone_mlps, zone->zone_id,
8851676Sjpk 			    proto, port, port, B_FALSE);
8861676Sjpk 	}
8871676Sjpk 	return (retv);
8881676Sjpk }
8891676Sjpk 
8901676Sjpk static void
mlp_flush(tsol_mlp_list_t * mlpl,zoneid_t zoneid)8911676Sjpk mlp_flush(tsol_mlp_list_t *mlpl, zoneid_t zoneid)
8921676Sjpk {
8931676Sjpk 	tsol_mlp_entry_t *tme, *tme2, *tmnext;
8941676Sjpk 
8951676Sjpk 	rw_enter(&mlpl->mlpl_rwlock, RW_WRITER);
8961676Sjpk 	for (tme = mlpl->mlpl_first; tme != NULL; tme = tmnext) {
8971676Sjpk 		tmnext = tme->mlpe_next;
8981676Sjpk 		if (zoneid == ALL_ZONES || tme->mlpe_zoneid == zoneid) {
8991676Sjpk 			if ((tme2 = tme->mlpe_prev) == NULL)
9001676Sjpk 				mlpl->mlpl_first = tmnext;
9011676Sjpk 			else
9021676Sjpk 				tme2->mlpe_next = tmnext;
9031676Sjpk 			if (tmnext == NULL)
9041676Sjpk 				mlpl->mlpl_last = tme2;
9051676Sjpk 			else
9061676Sjpk 				tmnext->mlpe_prev = tme2;
9071676Sjpk 			kmem_free(tme, sizeof (*tme));
9081676Sjpk 		}
9091676Sjpk 	}
9101676Sjpk 	rw_exit(&mlpl->mlpl_rwlock);
9111676Sjpk }
9121676Sjpk 
9131676Sjpk /*
9141676Sjpk  * Note: user supplies port numbers in host byte order.
9151676Sjpk  */
9161676Sjpk static int
tnmlp(int cmd,void * buf)9171676Sjpk tnmlp(int cmd, void *buf)
9181676Sjpk {
9191676Sjpk 	int retv;
9201676Sjpk 	tsol_mlpent_t tsme;
9211676Sjpk 	zone_t *zone;
9221676Sjpk 	tsol_mlp_list_t *mlpl;
9231676Sjpk 	tsol_mlp_entry_t *tme;
9241676Sjpk 
9251676Sjpk 	/* Make sure user has sufficient privilege */
9261676Sjpk 	if (cmd != TNDB_GET &&
9271676Sjpk 	    (retv = secpolicy_net_config(CRED(), B_FALSE)) != 0)
9281676Sjpk 		return (set_errno(retv));
9291676Sjpk 
9301676Sjpk 	/*
9311676Sjpk 	 * Get argument.  Note that tsol_mlpent_t is the same on LP64 and
9321676Sjpk 	 * ILP32, so no special handling is required.
9331676Sjpk 	 */
9341676Sjpk 	if (copyin(buf, &tsme, sizeof (tsme)) != 0) {
9351676Sjpk 		DTRACE_PROBE(tx__tndb__l0__tnmlp__copyin);
9361676Sjpk 		return (set_errno(EFAULT));
9371676Sjpk 	}
9381676Sjpk 
9391676Sjpk 	/* MLPs on shared IP addresses */
9401676Sjpk 	if (tsme.tsme_flags & TSOL_MEF_SHARED) {
9411676Sjpk 		zone = NULL;
9421676Sjpk 		mlpl = &shared_mlps;
9431676Sjpk 	} else {
9441676Sjpk 		zone = zone_find_by_id(tsme.tsme_zoneid);
9451676Sjpk 		if (zone == NULL)
9461676Sjpk 			return (set_errno(EINVAL));
9471676Sjpk 		mlpl = &zone->zone_mlps;
9481676Sjpk 	}
9491676Sjpk 	if (tsme.tsme_mlp.mlp_port_upper == 0)
9501676Sjpk 		tsme.tsme_mlp.mlp_port_upper = tsme.tsme_mlp.mlp_port;
9511676Sjpk 
9521676Sjpk 	switch (cmd) {
9531676Sjpk 	case TNDB_LOAD:
9541676Sjpk 		DTRACE_PROBE1(tx__tndb__l2__tnmlp__tndbload,
9551676Sjpk 		    tsol_mlpent_t *, &tsme);
9561676Sjpk 		if (tsme.tsme_mlp.mlp_ipp == 0 || tsme.tsme_mlp.mlp_port == 0 ||
9571676Sjpk 		    tsme.tsme_mlp.mlp_port > tsme.tsme_mlp.mlp_port_upper) {
9581676Sjpk 			retv = EINVAL;
9591676Sjpk 			break;
9601676Sjpk 		}
9611676Sjpk 		retv = mlp_add_del(mlpl, tsme.tsme_zoneid,
9621676Sjpk 		    tsme.tsme_mlp.mlp_ipp, tsme.tsme_mlp.mlp_port,
9631676Sjpk 		    tsme.tsme_mlp.mlp_port_upper, B_TRUE);
9641676Sjpk 		break;
9651676Sjpk 
9661676Sjpk 	case TNDB_GET:
9671676Sjpk 		DTRACE_PROBE1(tx__tndb__l2__tnmlp__tndbget,
9681676Sjpk 		    tsol_mlpent_t *, &tsme);
9691676Sjpk 
9701676Sjpk 		/*
9711676Sjpk 		 * Search for the requested element or, failing that, the one
9721676Sjpk 		 * that's logically next in the sequence.
9731676Sjpk 		 */
9741676Sjpk 		rw_enter(&mlpl->mlpl_rwlock, RW_READER);
9751676Sjpk 		for (tme = mlpl->mlpl_first; tme != NULL;
9761676Sjpk 		    tme = tme->mlpe_next) {
9771676Sjpk 			if (tsme.tsme_zoneid != ALL_ZONES &&
9781676Sjpk 			    tme->mlpe_zoneid != tsme.tsme_zoneid)
9791676Sjpk 				continue;
9801676Sjpk 			if (tme->mlpe_mlp.mlp_ipp >= tsme.tsme_mlp.mlp_ipp &&
9811676Sjpk 			    tme->mlpe_mlp.mlp_port == tsme.tsme_mlp.mlp_port)
9821676Sjpk 				break;
9831676Sjpk 			if (tme->mlpe_mlp.mlp_port > tsme.tsme_mlp.mlp_port)
9841676Sjpk 				break;
9851676Sjpk 		}
9861676Sjpk 		if (tme == NULL) {
9871676Sjpk 			retv = ENOENT;
9881676Sjpk 		} else {
9891676Sjpk 			tsme.tsme_zoneid = tme->mlpe_zoneid;
9901676Sjpk 			tsme.tsme_mlp = tme->mlpe_mlp;
9911676Sjpk 			retv = 0;
9921676Sjpk 		}
9931676Sjpk 		rw_exit(&mlpl->mlpl_rwlock);
9941676Sjpk 		break;
9951676Sjpk 
9961676Sjpk 	case TNDB_DELETE:
9971676Sjpk 		DTRACE_PROBE1(tx__tndb__l4__tnmlp__tndbdelete,
9981676Sjpk 		    tsol_mlpent_t *, &tsme);
9991676Sjpk 		retv = mlp_add_del(mlpl, tsme.tsme_zoneid,
10001676Sjpk 		    tsme.tsme_mlp.mlp_ipp, tsme.tsme_mlp.mlp_port,
10011676Sjpk 		    tsme.tsme_mlp.mlp_port_upper, B_FALSE);
10021676Sjpk 		break;
10031676Sjpk 
10041676Sjpk 	case TNDB_FLUSH:
10051676Sjpk 		DTRACE_PROBE1(tx__tndb__l4__tnmlp__tndbflush,
10061676Sjpk 		    tsol_mlpent_t *, &tsme);
10071676Sjpk 		mlp_flush(mlpl, ALL_ZONES);
10081676Sjpk 		mlp_flush(&shared_mlps, tsme.tsme_zoneid);
10091676Sjpk 		retv = 0;
10101676Sjpk 		break;
10111676Sjpk 
10121676Sjpk 	default:
10131676Sjpk 		DTRACE_PROBE1(tx__tndb__l0__tnmlp__unknowncmd, int,
10141676Sjpk 		    cmd);
10151676Sjpk 		retv = EOPNOTSUPP;
10161676Sjpk 		break;
10171676Sjpk 	}
10181676Sjpk 
10191676Sjpk 	if (zone != NULL)
10201676Sjpk 		zone_rele(zone);
10211676Sjpk 
10221676Sjpk 	if (cmd == TNDB_GET && retv == 0) {
10231676Sjpk 		/* Copy out result */
10241676Sjpk 		if (copyout(&tsme, buf, sizeof (tsme)) != 0) {
10251676Sjpk 			DTRACE_PROBE(tx__tndb__l0__tnmlp__copyout);
10261676Sjpk 			retv = EFAULT;
10271676Sjpk 		}
10281676Sjpk 	}
10291676Sjpk 
10301676Sjpk 	if (retv != 0)
10311676Sjpk 		return (set_errno(retv));
10321676Sjpk 	else
10331676Sjpk 		return (retv);
10341676Sjpk }
10351676Sjpk 
10361676Sjpk /*
10371676Sjpk  * Returns a tnrhc matching the addr address.
10381676Sjpk  * The returned rhc's refcnt is incremented.
10391676Sjpk  */
10401676Sjpk tsol_tnrhc_t *
find_rhc(const void * addr,uchar_t version,boolean_t staleok)1041*3292Skp158701 find_rhc(const void *addr, uchar_t version, boolean_t staleok)
10421676Sjpk {
10431676Sjpk 	tsol_tnrhc_t *rh = NULL;
1044*3292Skp158701 	tsol_tnrhc_t *new;
1045*3292Skp158701 	tsol_tpc_t *tpc;
10461676Sjpk 	tnrhc_hash_t *tnrhc_hash;
10471676Sjpk 	ipaddr_t tmpmask;
1048*3292Skp158701 	in_addr_t *in4 = (in_addr_t *)addr;
1049*3292Skp158701 	in6_addr_t *in6 = (in6_addr_t *)addr;
1050*3292Skp158701 	in_addr_t tmpin4;
1051*3292Skp158701 	in6_addr_t tmpmask6;
10521676Sjpk 	int	i;
1053*3292Skp158701 	int	prefix;
10541676Sjpk 
1055*3292Skp158701 	/*
1056*3292Skp158701 	 * An IPv4-mapped IPv6 address is really an IPv4 address
1057*3292Skp158701 	 * in IPv6 format.
1058*3292Skp158701 	 */
1059*3292Skp158701 	if (version == IPV6_VERSION &&
1060*3292Skp158701 	    IN6_IS_ADDR_V4MAPPED(in6)) {
1061*3292Skp158701 		IN6_V4MAPPED_TO_IPADDR(in6, tmpin4);
1062*3292Skp158701 		version = IPV4_VERSION;
1063*3292Skp158701 		in4 = &tmpin4;
10641676Sjpk 	}
10651676Sjpk 
1066*3292Skp158701 	/*
1067*3292Skp158701 	 * Search the tnrh hash table for each prefix length,
1068*3292Skp158701 	 * starting at longest prefix length, until a matching
1069*3292Skp158701 	 * rhc entry is found.
1070*3292Skp158701 	 */
1071*3292Skp158701 	if (version == IPV4_VERSION) {
1072*3292Skp158701 		for (i = (TSOL_MASK_TABLE_SIZE - 1); i >= 0; i--) {
1073*3292Skp158701 
1074*3292Skp158701 			if ((tnrhc_table[i]) == NULL)
1075*3292Skp158701 				continue;
1076*3292Skp158701 
1077*3292Skp158701 			tmpmask = tsol_plen_to_mask(i);
1078*3292Skp158701 			tnrhc_hash = &tnrhc_table[i][
1079*3292Skp158701 			    TSOL_ADDR_HASH(*in4 & tmpmask, TNRHC_SIZE)];
10801676Sjpk 
1081*3292Skp158701 			mutex_enter(&tnrhc_hash->tnrh_lock);
1082*3292Skp158701 			for (rh = tnrhc_hash->tnrh_list; rh != NULL;
1083*3292Skp158701 			    rh = rh->rhc_next) {
1084*3292Skp158701 				if ((rh->rhc_host.ta_family == AF_INET) &&
1085*3292Skp158701 				    ((rh->rhc_host.ta_addr_v4.s_addr &
1086*3292Skp158701 				    tmpmask) == (*in4 & tmpmask))) {
1087*3292Skp158701 					prefix = i;
1088*3292Skp158701 					TNRHC_HOLD(rh);
1089*3292Skp158701 					break;
1090*3292Skp158701 				}
1091*3292Skp158701 			}
1092*3292Skp158701 			mutex_exit(&tnrhc_hash->tnrh_lock);
1093*3292Skp158701 			if (rh != NULL)
1094*3292Skp158701 				break;
1095*3292Skp158701 		}
1096*3292Skp158701 		if (rh == NULL)
1097*3292Skp158701 			DTRACE_PROBE1(tx__tndb__l1__findrhc__norhv4ent,
1098*3292Skp158701 			    in_addr_t *, in4);
1099*3292Skp158701 	} else {
1100*3292Skp158701 		for (i = (TSOL_MASK_TABLE_SIZE_V6 - 1); i >= 0; i--) {
1101*3292Skp158701 			if ((tnrhc_table_v6[i]) == NULL)
1102*3292Skp158701 				continue;
11031676Sjpk 
1104*3292Skp158701 			tsol_plen_to_mask_v6(i, &tmpmask6);
1105*3292Skp158701 			tnrhc_hash = &tnrhc_table_v6[i][
1106*3292Skp158701 			    TSOL_ADDR_MASK_HASH_V6(*in6, tmpmask6, TNRHC_SIZE)];
11071676Sjpk 
1108*3292Skp158701 			mutex_enter(&tnrhc_hash->tnrh_lock);
1109*3292Skp158701 			for (rh = tnrhc_hash->tnrh_list; rh != NULL;
1110*3292Skp158701 			    rh = rh->rhc_next) {
1111*3292Skp158701 				if ((rh->rhc_host.ta_family == AF_INET6) &&
1112*3292Skp158701 				    V6_MASK_EQ_2(rh->rhc_host.ta_addr_v6,
1113*3292Skp158701 				    tmpmask6, *in6)) {
1114*3292Skp158701 					prefix = i;
1115*3292Skp158701 					TNRHC_HOLD(rh);
1116*3292Skp158701 					break;
1117*3292Skp158701 				}
1118*3292Skp158701 			}
1119*3292Skp158701 			mutex_exit(&tnrhc_hash->tnrh_lock);
1120*3292Skp158701 			if (rh != NULL)
1121*3292Skp158701 				break;
1122*3292Skp158701 		}
1123*3292Skp158701 		if (rh == NULL)
1124*3292Skp158701 			DTRACE_PROBE1(tx__tndb__l1__findrhc__norhv6ent,
1125*3292Skp158701 			    in6_addr_t *, in6);
11261676Sjpk 	}
11271676Sjpk 
1128*3292Skp158701 	/*
1129*3292Skp158701 	 * Does the tnrh entry point to a stale template?
1130*3292Skp158701 	 * This can happen any time the user deletes or modifies
1131*3292Skp158701 	 * a template that has existing tnrh entries pointing
1132*3292Skp158701 	 * to it. Try to find a new version of the template.
1133*3292Skp158701 	 * If there is no template, then just give up.
1134*3292Skp158701 	 * If the template exists, reload the tnrh entry.
1135*3292Skp158701 	 */
1136*3292Skp158701 	if (rh != NULL && rh->rhc_tpc->tpc_invalid) {
1137*3292Skp158701 		tpc = tnrhtp_find(rh->rhc_tpc->tpc_tp.name, tpc_name_hash);
1138*3292Skp158701 		if (tpc == NULL) {
1139*3292Skp158701 			if (!staleok) {
1140*3292Skp158701 				DTRACE_PROBE2(tx__tndb__l1__findrhc__staletpc,
1141*3292Skp158701 				    tsol_tnrhc_t *, rh, tsol_tpc_t *,
1142*3292Skp158701 				    rh->rhc_tpc);
1143*3292Skp158701 				TNRHC_RELE(rh);
1144*3292Skp158701 				rh = NULL;
1145*3292Skp158701 			}
1146*3292Skp158701 		} else {
1147*3292Skp158701 			ASSERT(tpc->tpc_tp.host_type == UNLABELED ||
1148*3292Skp158701 			    tpc->tpc_tp.host_type == SUN_CIPSO);
11491676Sjpk 
1150*3292Skp158701 			if ((new = kmem_zalloc(sizeof (*new),
1151*3292Skp158701 			    KM_NOSLEEP)) == NULL) {
1152*3292Skp158701 				DTRACE_PROBE(tx__tndb__l1__findrhc__nomem);
1153*3292Skp158701 				TNRHC_RELE(rh);
1154*3292Skp158701 				TPC_RELE(tpc);
1155*3292Skp158701 				return (NULL);
1156*3292Skp158701 			}
1157*3292Skp158701 
1158*3292Skp158701 			mutex_init(&new->rhc_lock, NULL, MUTEX_DEFAULT, NULL);
1159*3292Skp158701 			new->rhc_host = rh->rhc_host;
1160*3292Skp158701 			new->rhc_tpc = tpc;
1161*3292Skp158701 			new->rhc_isbcast = rh->rhc_isbcast;
1162*3292Skp158701 			new->rhc_local = rh->rhc_local;
1163*3292Skp158701 			TNRHC_RELE(rh);
1164*3292Skp158701 			rh = new;
1165*3292Skp158701 
1166*3292Skp158701 			/*
1167*3292Skp158701 			 * This function increments the tnrh entry ref count
1168*3292Skp158701 			 * for the pointer returned to the caller.
1169*3292Skp158701 			 * tnrh_hash_add increments the tnrh entry ref count
1170*3292Skp158701 			 * for the pointer in the hash table.
1171*3292Skp158701 			 */
1172*3292Skp158701 			TNRHC_HOLD(rh);
1173*3292Skp158701 			if (tnrh_hash_add(new, prefix) != 0) {
1174*3292Skp158701 				TNRHC_RELE(rh);
1175*3292Skp158701 				rh = NULL;
11761676Sjpk 			}
11771676Sjpk 		}
11781676Sjpk 	}
1179*3292Skp158701 	return (rh);
11801676Sjpk }
11811676Sjpk 
11821676Sjpk tsol_tpc_t *
find_tpc(const void * addr,uchar_t version,boolean_t staleok)11831676Sjpk find_tpc(const void *addr, uchar_t version, boolean_t staleok)
11841676Sjpk {
11851676Sjpk 	tsol_tpc_t *tpc;
11861676Sjpk 	tsol_tnrhc_t *rhc;
11871676Sjpk 
1188*3292Skp158701 	if ((rhc = find_rhc(addr, version, staleok)) == NULL)
1189*3292Skp158701 		return (NULL);
11901676Sjpk 
1191*3292Skp158701 	tpc = rhc->rhc_tpc;
1192*3292Skp158701 	TPC_HOLD(tpc);
1193*3292Skp158701 	TNRHC_RELE(rhc);
1194*3292Skp158701 	return (tpc);
11951676Sjpk }
11961676Sjpk 
11971676Sjpk /*
11981676Sjpk  * create an internal template called "_unlab":
11991676Sjpk  *
12001676Sjpk  * _unlab;\
12011676Sjpk  *	host_type = unlabeled;\
12021676Sjpk  *	def_label = ADMIN_LOW[ADMIN_LOW];\
12031676Sjpk  *	min_sl = ADMIN_LOW;\
12041676Sjpk  *	max_sl = ADMIN_HIGH;
12051676Sjpk  */
12061676Sjpk static void
tsol_create_i_tmpls(void)12071676Sjpk tsol_create_i_tmpls(void)
12081676Sjpk {
12091676Sjpk 	tsol_tpent_t rhtpent;
12101676Sjpk 
12111676Sjpk 	bzero(&rhtpent, sizeof (rhtpent));
12121676Sjpk 
12131676Sjpk 	/* create _unlab */
12141676Sjpk 	(void) strcpy(rhtpent.name, "_unlab");
12151676Sjpk 
12161676Sjpk 	rhtpent.host_type = UNLABELED;
12171676Sjpk 	rhtpent.tp_mask_unl = TSOL_MSK_DEF_LABEL | TSOL_MSK_DEF_CL |
12181676Sjpk 	    TSOL_MSK_SL_RANGE_TSOL;
12191676Sjpk 
12201676Sjpk 	rhtpent.tp_gw_sl_range.lower_bound = *label2bslabel(l_admin_low);
12211676Sjpk 	rhtpent.tp_def_label = rhtpent.tp_gw_sl_range.lower_bound;
12221676Sjpk 	rhtpent.tp_gw_sl_range.upper_bound = *label2bslabel(l_admin_high);
12231676Sjpk 	rhtpent.tp_cipso_doi_unl = default_doi;
12241676Sjpk 	tpc_unlab = tnrhtp_create(&rhtpent, KM_SLEEP);
12251676Sjpk }
12261676Sjpk 
12271676Sjpk /*
12281676Sjpk  * set up internal host template, called from kernel only.
12291676Sjpk  */
12301676Sjpk static void
tsol_create_i_tnrh(const tnaddr_t * sa)12311676Sjpk tsol_create_i_tnrh(const tnaddr_t *sa)
12321676Sjpk {
12331676Sjpk 	tsol_tnrhc_t *rh, *new;
12341676Sjpk 	tnrhc_hash_t *tnrhc_hash;
12351676Sjpk 
12361676Sjpk 	/* Allocate a new entry before taking the lock */
12371676Sjpk 	new = kmem_zalloc(sizeof (*new), KM_SLEEP);
12381676Sjpk 
12391676Sjpk 	tnrhc_hash = (sa->ta_family == AF_INET) ? &tnrhc_table[0][0] :
12401676Sjpk 	    &tnrhc_table_v6[0][0];
12411676Sjpk 
12421676Sjpk 	mutex_enter(&tnrhc_hash->tnrh_lock);
12431676Sjpk 	rh = tnrhc_hash->tnrh_list;
12441676Sjpk 
12451676Sjpk 	if (rh == NULL) {
12461676Sjpk 		/* We're keeping the new entry. */
12471676Sjpk 		rh = new;
12481676Sjpk 		new = NULL;
12491676Sjpk 		rh->rhc_host = *sa;
12501676Sjpk 		mutex_init(&rh->rhc_lock, NULL, MUTEX_DEFAULT, NULL);
12511676Sjpk 		TNRHC_HOLD(rh);
12521676Sjpk 		tnrhc_hash->tnrh_list = rh;
12531676Sjpk 	}
12541676Sjpk 
12551676Sjpk 	/*
12561676Sjpk 	 * Link the entry to internal_unlab
12571676Sjpk 	 */
12581676Sjpk 	if (rh->rhc_tpc != tpc_unlab) {
12591676Sjpk 		if (rh->rhc_tpc != NULL)
12601676Sjpk 			TPC_RELE(rh->rhc_tpc);
12611676Sjpk 		rh->rhc_tpc = tpc_unlab;
12621676Sjpk 		TPC_HOLD(tpc_unlab);
12631676Sjpk 	}
12641676Sjpk 	mutex_exit(&tnrhc_hash->tnrh_lock);
12651676Sjpk 	if (new != NULL)
12661676Sjpk 		kmem_free(new, sizeof (*new));
12671676Sjpk }
12681676Sjpk 
12691676Sjpk /*
12701676Sjpk  * Returns 0 if the port is known to be SLP.  Returns next possible port number
12711676Sjpk  * (wrapping through 1) if port is MLP on shared or global.  Administrator
12721676Sjpk  * should not make all ports MLP.  If that's done, then we'll just pretend
12731676Sjpk  * everything is SLP to avoid looping forever.
12741676Sjpk  *
12751676Sjpk  * Note: port is in host byte order.
12761676Sjpk  */
12771676Sjpk in_port_t
tsol_next_port(zone_t * zone,in_port_t port,int proto,boolean_t upward)12781676Sjpk tsol_next_port(zone_t *zone, in_port_t port, int proto, boolean_t upward)
12791676Sjpk {
12801676Sjpk 	boolean_t loop;
12811676Sjpk 	tsol_mlp_entry_t *tme;
12821676Sjpk 	int newport = port;
12831676Sjpk 
12841676Sjpk 	loop = B_FALSE;
12851676Sjpk 	for (;;) {
12861676Sjpk 		if (zone != NULL && zone->zone_mlps.mlpl_first != NULL) {
12871676Sjpk 			rw_enter(&zone->zone_mlps.mlpl_rwlock, RW_READER);
12881676Sjpk 			for (tme = zone->zone_mlps.mlpl_first; tme != NULL;
12891676Sjpk 			    tme = tme->mlpe_next) {
12901676Sjpk 				if (proto == tme->mlpe_mlp.mlp_ipp &&
12911676Sjpk 				    newport >= tme->mlpe_mlp.mlp_port &&
12921676Sjpk 				    newport <= tme->mlpe_mlp.mlp_port_upper)
12931676Sjpk 					newport = upward ?
12941676Sjpk 					    tme->mlpe_mlp.mlp_port_upper + 1 :
12951676Sjpk 					    tme->mlpe_mlp.mlp_port - 1;
12961676Sjpk 			}
12971676Sjpk 			rw_exit(&zone->zone_mlps.mlpl_rwlock);
12981676Sjpk 		}
12991676Sjpk 		if (shared_mlps.mlpl_first != NULL) {
13001676Sjpk 			rw_enter(&shared_mlps.mlpl_rwlock, RW_READER);
13011676Sjpk 			for (tme = shared_mlps.mlpl_first; tme != NULL;
13021676Sjpk 			    tme = tme->mlpe_next) {
13031676Sjpk 				if (proto == tme->mlpe_mlp.mlp_ipp &&
13041676Sjpk 				    newport >= tme->mlpe_mlp.mlp_port &&
13051676Sjpk 				    newport <= tme->mlpe_mlp.mlp_port_upper)
13061676Sjpk 					newport = upward ?
13071676Sjpk 					    tme->mlpe_mlp.mlp_port_upper + 1 :
13081676Sjpk 					    tme->mlpe_mlp.mlp_port - 1;
13091676Sjpk 			}
13101676Sjpk 			rw_exit(&shared_mlps.mlpl_rwlock);
13111676Sjpk 		}
13121676Sjpk 		if (newport <= 65535 && newport > 0)
13131676Sjpk 			break;
13141676Sjpk 		if (loop)
13151676Sjpk 			return (0);
13161676Sjpk 		loop = B_TRUE;
13171676Sjpk 		newport = upward ? 1 : 65535;
13181676Sjpk 	}
13191676Sjpk 	return (newport == port ? 0 : newport);
13201676Sjpk }
13211676Sjpk 
13221676Sjpk /*
13231676Sjpk  * tsol_mlp_port_type will check if the given (zone, proto, port) is a
13241676Sjpk  * multilevel port.  If it is, return the type (shared, private, or both), or
13251676Sjpk  * indicate that it's single-level.
13261676Sjpk  *
13271676Sjpk  * Note: port is given in host byte order, not network byte order.
13281676Sjpk  */
13291676Sjpk mlp_type_t
tsol_mlp_port_type(zone_t * zone,uchar_t proto,uint16_t port,mlp_type_t mlptype)13301676Sjpk tsol_mlp_port_type(zone_t *zone, uchar_t proto, uint16_t port,
13311676Sjpk     mlp_type_t mlptype)
13321676Sjpk {
13331676Sjpk 	tsol_mlp_entry_t *tme;
13341676Sjpk 
13351676Sjpk 	if (mlptype == mlptBoth || mlptype == mlptPrivate) {
13361676Sjpk 		tme = NULL;
13371676Sjpk 		if (zone->zone_mlps.mlpl_first != NULL) {
13381676Sjpk 			rw_enter(&zone->zone_mlps.mlpl_rwlock, RW_READER);
13391676Sjpk 			for (tme = zone->zone_mlps.mlpl_first; tme != NULL;
13401676Sjpk 			    tme = tme->mlpe_next) {
13411676Sjpk 				if (proto == tme->mlpe_mlp.mlp_ipp &&
13421676Sjpk 				    port >= tme->mlpe_mlp.mlp_port &&
13431676Sjpk 				    port <= tme->mlpe_mlp.mlp_port_upper)
13441676Sjpk 					break;
13451676Sjpk 			}
13461676Sjpk 			rw_exit(&zone->zone_mlps.mlpl_rwlock);
13471676Sjpk 		}
13481676Sjpk 		if (tme == NULL) {
13491676Sjpk 			if (mlptype == mlptBoth)
13501676Sjpk 				mlptype = mlptShared;
13511676Sjpk 			else if (mlptype == mlptPrivate)
13521676Sjpk 				mlptype = mlptSingle;
13531676Sjpk 		}
13541676Sjpk 	}
13551676Sjpk 	if (mlptype == mlptBoth || mlptype == mlptShared) {
13561676Sjpk 		tme = NULL;
13571676Sjpk 		if (shared_mlps.mlpl_first != NULL) {
13581676Sjpk 			rw_enter(&shared_mlps.mlpl_rwlock, RW_READER);
13591676Sjpk 			for (tme = shared_mlps.mlpl_first; tme != NULL;
13601676Sjpk 			    tme = tme->mlpe_next) {
13611676Sjpk 				if (proto == tme->mlpe_mlp.mlp_ipp &&
13621676Sjpk 				    port >= tme->mlpe_mlp.mlp_port &&
13631676Sjpk 				    port <= tme->mlpe_mlp.mlp_port_upper)
13641676Sjpk 					break;
13651676Sjpk 			}
13661676Sjpk 			rw_exit(&shared_mlps.mlpl_rwlock);
13671676Sjpk 		}
13681676Sjpk 		if (tme == NULL) {
13691676Sjpk 			if (mlptype == mlptBoth)
13701676Sjpk 				mlptype = mlptPrivate;
13711676Sjpk 			else if (mlptype == mlptShared)
13721676Sjpk 				mlptype = mlptSingle;
13731676Sjpk 		}
13741676Sjpk 	}
13751676Sjpk 	return (mlptype);
13761676Sjpk }
13771676Sjpk 
13781676Sjpk /*
13791676Sjpk  * tsol_mlp_findzone will check if the given (proto, port) is a multilevel port
13801676Sjpk  * on a shared address.  If it is, return the owning zone.
13811676Sjpk  *
13821676Sjpk  * Note: lport is in network byte order, unlike the other MLP functions,
13831676Sjpk  * because the callers of this function are all dealing with packets off the
13841676Sjpk  * wire.
13851676Sjpk  */
13861676Sjpk zoneid_t
tsol_mlp_findzone(uchar_t proto,uint16_t lport)13871676Sjpk tsol_mlp_findzone(uchar_t proto, uint16_t lport)
13881676Sjpk {
13891676Sjpk 	tsol_mlp_entry_t *tme;
13901676Sjpk 	zoneid_t zoneid;
13911676Sjpk 	uint16_t port;
13921676Sjpk 
13931676Sjpk 	if (shared_mlps.mlpl_first == NULL)
13941676Sjpk 		return (ALL_ZONES);
13951676Sjpk 	port = ntohs(lport);
13961676Sjpk 	rw_enter(&shared_mlps.mlpl_rwlock, RW_READER);
13971676Sjpk 	for (tme = shared_mlps.mlpl_first; tme != NULL; tme = tme->mlpe_next) {
13981676Sjpk 		if (proto == tme->mlpe_mlp.mlp_ipp &&
13991676Sjpk 		    port >= tme->mlpe_mlp.mlp_port &&
14001676Sjpk 		    port <= tme->mlpe_mlp.mlp_port_upper)
14011676Sjpk 			break;
14021676Sjpk 	}
14031676Sjpk 	zoneid = tme == NULL ? ALL_ZONES : tme->mlpe_zoneid;
14041676Sjpk 	rw_exit(&shared_mlps.mlpl_rwlock);
14051676Sjpk 	return (zoneid);
14061676Sjpk }
14071676Sjpk 
14081676Sjpk /* Debug routine */
14091676Sjpk void
tsol_print_label(const blevel_t * blev,const char * name)14101676Sjpk tsol_print_label(const blevel_t *blev, const char *name)
14111676Sjpk {
14121676Sjpk 	const _blevel_impl_t *bli = (const _blevel_impl_t *)blev;
14131676Sjpk 
14141676Sjpk 	/* We really support only sensitivity labels */
14151676Sjpk 	cmn_err(CE_NOTE, "%s %x:%x:%08x%08x%08x%08x%08x%08x%08x%08x",
14161676Sjpk 	    name, bli->id, LCLASS(bli), ntohl(bli->_comps.c1),
14171676Sjpk 	    ntohl(bli->_comps.c2), ntohl(bli->_comps.c3), ntohl(bli->_comps.c4),
14181676Sjpk 	    ntohl(bli->_comps.c5), ntohl(bli->_comps.c6), ntohl(bli->_comps.c7),
14191676Sjpk 	    ntohl(bli->_comps.c8));
14201676Sjpk }
14211676Sjpk 
14221676Sjpk /*
14231676Sjpk  * Name:	labelsys()
14241676Sjpk  *
14251676Sjpk  * Normal:	Routes TSOL syscalls.
14261676Sjpk  *
14271676Sjpk  * Output:	As defined for each TSOL syscall.
14281676Sjpk  *		Returns ENOSYS for unrecognized calls.
14291676Sjpk  */
14301676Sjpk /* ARGSUSED */
14311676Sjpk int
labelsys(int op,void * a1,void * a2,void * a3,void * a4,void * a5)14321676Sjpk labelsys(int op, void *a1, void *a2, void *a3, void *a4, void *a5)
14331676Sjpk {
14341676Sjpk 	switch (op) {
14351676Sjpk 	case TSOL_SYSLABELING:
14361676Sjpk 		return (sys_labeling);
14371676Sjpk 	case TSOL_TNRH:
14381676Sjpk 		return (tnrh((int)(uintptr_t)a1, a2));
14391676Sjpk 	case TSOL_TNRHTP:
14401676Sjpk 		return (tnrhtp((int)(uintptr_t)a1, a2));
14411676Sjpk 	case TSOL_TNMLP:
14421676Sjpk 		return (tnmlp((int)(uintptr_t)a1, a2));
14431676Sjpk 	case TSOL_GETLABEL:
14441676Sjpk 		return (getlabel((char *)a1, (bslabel_t *)a2));
14451676Sjpk 	case TSOL_FGETLABEL:
14461676Sjpk 		return (fgetlabel((int)(uintptr_t)a1, (bslabel_t *)a2));
14471676Sjpk 	default:
14481676Sjpk 		return (set_errno(ENOSYS));
14491676Sjpk 	}
14501676Sjpk 	/* NOTREACHED */
14511676Sjpk }
1452