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*bfabfc35Skp158701 * 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 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 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 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 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 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 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 * 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 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, ¤t->rhc_host)) 26145916cd2Sjpk break; 26245916cd2Sjpk prevp = ¤t->rhc_next; 26345916cd2Sjpk } 26445916cd2Sjpk 26545916cd2Sjpk if (current != NULL) { 26645916cd2Sjpk DTRACE_PROBE(tx__tndb__l2__tnrhdelete_existingrhentry); 26745916cd2Sjpk *prevp = current->rhc_next; 26845916cd2Sjpk mutex_enter(¤t->rhc_lock); 26945916cd2Sjpk current->rhc_next = NULL; 27045916cd2Sjpk current->rhc_invalid = 1; 27145916cd2Sjpk mutex_exit(¤t->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 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*bfabfc35Skp158701 static int 333*bfabfc35Skp158701 tnrh_hash_add(tsol_tnrhc_t *new, short prefix) 33445916cd2Sjpk { 33545916cd2Sjpk tsol_tnrhc_t **rhp; 336*bfabfc35Skp158701 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*bfabfc35Skp158701 if (new->rhc_host.ta_family == AF_INET) { 343*bfabfc35Skp158701 if (prefix < 0 || prefix > IP_ABITS) 34445916cd2Sjpk return (EINVAL); 345*bfabfc35Skp158701 if (tnrhc_table[prefix] == NULL && 346*bfabfc35Skp158701 !tnrhc_init_table(tnrhc_table, prefix, 34745916cd2Sjpk KM_NOSLEEP)) 34845916cd2Sjpk return (ENOMEM); 349*bfabfc35Skp158701 tmpmask = tsol_plen_to_mask(prefix); 350*bfabfc35Skp158701 tnrhc_hash = &tnrhc_table[prefix][ 351*bfabfc35Skp158701 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*bfabfc35Skp158701 new->rhc_host.ta_addr_v4.s_addr) & tmpmask) == 35945916cd2Sjpk 0) 36045916cd2Sjpk break; 36145916cd2Sjpk } 362*bfabfc35Skp158701 } else if (new->rhc_host.ta_family == AF_INET6) { 363*bfabfc35Skp158701 if (prefix < 0 || prefix > IPV6_ABITS) 36445916cd2Sjpk return (EINVAL); 365*bfabfc35Skp158701 if (tnrhc_table_v6[prefix] == NULL && 366*bfabfc35Skp158701 !tnrhc_init_table(tnrhc_table_v6, prefix, 36745916cd2Sjpk KM_NOSLEEP)) 36845916cd2Sjpk return (ENOMEM); 369*bfabfc35Skp158701 tsol_plen_to_mask_v6(prefix, &tmpmask_v6); 370*bfabfc35Skp158701 tnrhc_hash = &tnrhc_table_v6[prefix][ 371*bfabfc35Skp158701 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*bfabfc35Skp158701 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*bfabfc35Skp158701 DTRACE_PROBE1(tx__tndb__l2__tnrhhashadd__invalidaterh, 392*bfabfc35Skp158701 tsol_tnrhc_t *, rh); 39345916cd2Sjpk TNRHC_RELE(rh); 39445916cd2Sjpk } 39545916cd2Sjpk 396*bfabfc35Skp158701 TNRHC_HOLD(new); 397*bfabfc35Skp158701 new->rhc_next = tnrhc_hash->tnrh_list; 398*bfabfc35Skp158701 tnrhc_hash->tnrh_list = new; 399*bfabfc35Skp158701 DTRACE_PROBE1(tx__tndb__l2__tnrhhashadd__addedrh, tsol_tnrhc_t *, new); 400*bfabfc35Skp158701 mutex_exit(&tnrhc_hash->tnrh_lock); 401*bfabfc35Skp158701 402*bfabfc35Skp158701 return (0); 403*bfabfc35Skp158701 } 404*bfabfc35Skp158701 405*bfabfc35Skp158701 /* 406*bfabfc35Skp158701 * Load a remote host entry into kernel cache. 407*bfabfc35Skp158701 * 408*bfabfc35Skp158701 * Return 0 for success, error code for failure. 409*bfabfc35Skp158701 */ 410*bfabfc35Skp158701 int 411*bfabfc35Skp158701 tnrh_load(const tsol_rhent_t *rhent) 412*bfabfc35Skp158701 { 413*bfabfc35Skp158701 tsol_tnrhc_t *new; 414*bfabfc35Skp158701 tsol_tpc_t *tpc; 415*bfabfc35Skp158701 int status; 416*bfabfc35Skp158701 417*bfabfc35Skp158701 /* Find and bump the reference count on the named template */ 418*bfabfc35Skp158701 if ((tpc = tnrhtp_find(rhent->rh_template, tpc_name_hash)) == NULL) { 419*bfabfc35Skp158701 return (EINVAL); 420*bfabfc35Skp158701 } 421*bfabfc35Skp158701 ASSERT(tpc->tpc_tp.host_type == UNLABELED || 422*bfabfc35Skp158701 tpc->tpc_tp.host_type == SUN_CIPSO); 423*bfabfc35Skp158701 424*bfabfc35Skp158701 if ((new = kmem_zalloc(sizeof (*new), KM_NOSLEEP)) == NULL) { 425*bfabfc35Skp158701 TPC_RELE(tpc); 426*bfabfc35Skp158701 return (ENOMEM); 427*bfabfc35Skp158701 } 428*bfabfc35Skp158701 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*bfabfc35Skp158701 /* 437*bfabfc35Skp158701 * tnrh_hash_add handles the tnrh entry ref count for hash 438*bfabfc35Skp158701 * table inclusion. The ref count is incremented and decremented 439*bfabfc35Skp158701 * here to trigger deletion of the new hash table entry in the 440*bfabfc35Skp158701 * event that tnrh_hash_add fails. 441*bfabfc35Skp158701 */ 44245916cd2Sjpk TNRHC_HOLD(new); 443*bfabfc35Skp158701 status = tnrh_hash_add(new, rhent->rh_prefix); 444*bfabfc35Skp158701 TNRHC_RELE(new); 44545916cd2Sjpk 446*bfabfc35Skp158701 return (status); 44745916cd2Sjpk } 44845916cd2Sjpk 44945916cd2Sjpk static int 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 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 } 48945916cd2Sjpk return (name < name_end); 49045916cd2Sjpk } 49145916cd2Sjpk 49245916cd2Sjpk static int 49345916cd2Sjpk tnrh(int cmd, void *buf) 49445916cd2Sjpk { 49545916cd2Sjpk int retv; 49645916cd2Sjpk tsol_rhent_t rhent; 49745916cd2Sjpk 49845916cd2Sjpk /* Make sure user has sufficient privilege */ 49945916cd2Sjpk if (cmd != TNDB_GET && 50045916cd2Sjpk (retv = secpolicy_net_config(CRED(), B_FALSE)) != 0) 50145916cd2Sjpk return (set_errno(retv)); 50245916cd2Sjpk 50345916cd2Sjpk /* 50445916cd2Sjpk * Get arguments 50545916cd2Sjpk */ 50645916cd2Sjpk if (cmd != TNDB_FLUSH && 50745916cd2Sjpk copyin(buf, &rhent, sizeof (rhent)) != 0) { 50845916cd2Sjpk DTRACE_PROBE(tx__tndb__l0__tnrhdelete__copyin); 50945916cd2Sjpk return (set_errno(EFAULT)); 51045916cd2Sjpk } 51145916cd2Sjpk 51245916cd2Sjpk switch (cmd) { 51345916cd2Sjpk case TNDB_LOAD: 51445916cd2Sjpk DTRACE_PROBE(tx__tndb__l2__tnrhdelete__tndbload); 51545916cd2Sjpk if (!template_name_ok(rhent.rh_template)) { 51645916cd2Sjpk retv = EINVAL; 51745916cd2Sjpk } else { 51845916cd2Sjpk retv = tnrh_load(&rhent); 51945916cd2Sjpk } 52045916cd2Sjpk break; 52145916cd2Sjpk 52245916cd2Sjpk case TNDB_DELETE: 52345916cd2Sjpk DTRACE_PROBE(tx__tndb__l2__tnrhdelete__tndbdelete); 52445916cd2Sjpk retv = tnrh_delete(&rhent); 52545916cd2Sjpk break; 52645916cd2Sjpk 52745916cd2Sjpk case TNDB_GET: 52845916cd2Sjpk DTRACE_PROBE(tx__tndb__l4__tnrhdelete__tndbget); 52945916cd2Sjpk if (!template_name_ok(rhent.rh_template)) { 53045916cd2Sjpk retv = EINVAL; 53145916cd2Sjpk break; 53245916cd2Sjpk } 53345916cd2Sjpk 53445916cd2Sjpk retv = tnrh_get(&rhent); 53545916cd2Sjpk if (retv != 0) 53645916cd2Sjpk break; 53745916cd2Sjpk 53845916cd2Sjpk /* 53945916cd2Sjpk * Copy out result 54045916cd2Sjpk */ 54145916cd2Sjpk if (copyout(&rhent, buf, sizeof (rhent)) != 0) { 54245916cd2Sjpk DTRACE_PROBE(tx__tndb__l0__tnrhdelete__copyout); 54345916cd2Sjpk retv = EFAULT; 54445916cd2Sjpk } 54545916cd2Sjpk break; 54645916cd2Sjpk 54745916cd2Sjpk case TNDB_FLUSH: 54845916cd2Sjpk DTRACE_PROBE(tx__tndb__l2__tnrhdelete__flush); 54945916cd2Sjpk flush_rh_table(tnrhc_table, TSOL_MASK_TABLE_SIZE); 55045916cd2Sjpk flush_rh_table(tnrhc_table_v6, TSOL_MASK_TABLE_SIZE_V6); 55145916cd2Sjpk break; 55245916cd2Sjpk 55345916cd2Sjpk default: 55445916cd2Sjpk DTRACE_PROBE1(tx__tndb__l0__tnrhdelete__unknowncmd, 55545916cd2Sjpk int, cmd); 55645916cd2Sjpk retv = EOPNOTSUPP; 55745916cd2Sjpk break; 55845916cd2Sjpk } 55945916cd2Sjpk 56045916cd2Sjpk if (retv != 0) 56145916cd2Sjpk return (set_errno(retv)); 56245916cd2Sjpk else 56345916cd2Sjpk return (retv); 56445916cd2Sjpk } 56545916cd2Sjpk 56645916cd2Sjpk static tsol_tpc_t * 56745916cd2Sjpk tnrhtp_create(const tsol_tpent_t *tpent, int kmflags) 56845916cd2Sjpk { 56945916cd2Sjpk tsol_tpc_t *tpc; 57045916cd2Sjpk mod_hash_val_t hv; 57145916cd2Sjpk 57245916cd2Sjpk /* 57345916cd2Sjpk * We intentionally allocate a new entry before taking the lock on the 57445916cd2Sjpk * entire database. 57545916cd2Sjpk */ 57645916cd2Sjpk if ((tpc = kmem_zalloc(sizeof (*tpc), kmflags)) == NULL) 57745916cd2Sjpk return (NULL); 57845916cd2Sjpk 57945916cd2Sjpk mutex_enter(&tpc_lock); 58045916cd2Sjpk if (mod_hash_find(tpc_name_hash, (mod_hash_key_t)tpent->name, 58145916cd2Sjpk &hv) == 0) { 58245916cd2Sjpk tsol_tpc_t *found_tpc = (tsol_tpc_t *)hv; 58345916cd2Sjpk 58445916cd2Sjpk found_tpc->tpc_invalid = 1; 58545916cd2Sjpk (void) mod_hash_destroy(tpc_name_hash, 58645916cd2Sjpk (mod_hash_key_t)tpent->name); 58745916cd2Sjpk TPC_RELE(found_tpc); 58845916cd2Sjpk } 58945916cd2Sjpk 59045916cd2Sjpk mutex_init(&tpc->tpc_lock, NULL, MUTEX_DEFAULT, NULL); 59145916cd2Sjpk /* tsol_tpent_t is the same on LP64 and ILP32 */ 59245916cd2Sjpk bcopy(tpent, &tpc->tpc_tp, sizeof (tpc->tpc_tp)); 59345916cd2Sjpk (void) mod_hash_insert(tpc_name_hash, (mod_hash_key_t)tpc->tpc_tp.name, 59445916cd2Sjpk (mod_hash_val_t)tpc); 59545916cd2Sjpk TPC_HOLD(tpc); 59645916cd2Sjpk mutex_exit(&tpc_lock); 59745916cd2Sjpk 59845916cd2Sjpk return (tpc); 59945916cd2Sjpk } 60045916cd2Sjpk 60145916cd2Sjpk static int 60245916cd2Sjpk tnrhtp_delete(const char *tname) 60345916cd2Sjpk { 60445916cd2Sjpk tsol_tpc_t *tpc; 60545916cd2Sjpk mod_hash_val_t hv; 60645916cd2Sjpk int retv = ENOENT; 60745916cd2Sjpk 60845916cd2Sjpk mutex_enter(&tpc_lock); 60945916cd2Sjpk if (mod_hash_find(tpc_name_hash, (mod_hash_key_t)tname, &hv) == 0) { 61045916cd2Sjpk tpc = (tsol_tpc_t *)hv; 61145916cd2Sjpk ASSERT(!tpc->tpc_invalid); 61245916cd2Sjpk tpc->tpc_invalid = 1; 61345916cd2Sjpk (void) mod_hash_destroy(tpc_name_hash, 61445916cd2Sjpk (mod_hash_key_t)tpc->tpc_tp.name); 61545916cd2Sjpk TPC_RELE(tpc); 61645916cd2Sjpk retv = 0; 61745916cd2Sjpk } 61845916cd2Sjpk mutex_exit(&tpc_lock); 61945916cd2Sjpk return (retv); 62045916cd2Sjpk } 62145916cd2Sjpk 62245916cd2Sjpk /* ARGSUSED */ 62345916cd2Sjpk static uint_t 62445916cd2Sjpk tpc_delete(mod_hash_key_t key, mod_hash_val_t *val, void *arg) 62545916cd2Sjpk { 62645916cd2Sjpk tsol_tpc_t *tpc = (tsol_tpc_t *)val; 62745916cd2Sjpk 62845916cd2Sjpk ASSERT(!tpc->tpc_invalid); 62945916cd2Sjpk tpc->tpc_invalid = 1; 63045916cd2Sjpk TPC_RELE(tpc); 63145916cd2Sjpk return (MH_WALK_CONTINUE); 63245916cd2Sjpk } 63345916cd2Sjpk 63445916cd2Sjpk static void 63545916cd2Sjpk tnrhtp_flush(void) 63645916cd2Sjpk { 63745916cd2Sjpk mutex_enter(&tpc_lock); 63845916cd2Sjpk mod_hash_walk(tpc_name_hash, tpc_delete, NULL); 63945916cd2Sjpk mod_hash_clear(tpc_name_hash); 64045916cd2Sjpk mutex_exit(&tpc_lock); 64145916cd2Sjpk } 64245916cd2Sjpk 64345916cd2Sjpk static int 64445916cd2Sjpk tnrhtp(int cmd, void *buf) 64545916cd2Sjpk { 64645916cd2Sjpk int retv; 64745916cd2Sjpk int type; 64845916cd2Sjpk tsol_tpent_t rhtpent; 64945916cd2Sjpk tsol_tpc_t *tpc; 65045916cd2Sjpk 65145916cd2Sjpk /* Make sure user has sufficient privilege */ 65245916cd2Sjpk if (cmd != TNDB_GET && 65345916cd2Sjpk (retv = secpolicy_net_config(CRED(), B_FALSE)) != 0) 65445916cd2Sjpk return (set_errno(retv)); 65545916cd2Sjpk 65645916cd2Sjpk /* 65745916cd2Sjpk * Get argument. Note that tsol_tpent_t is the same on LP64 and ILP32, 65845916cd2Sjpk * so no special handling is required. 65945916cd2Sjpk */ 66045916cd2Sjpk if (cmd != TNDB_FLUSH) { 66145916cd2Sjpk if (copyin(buf, &rhtpent, sizeof (rhtpent)) != 0) { 66245916cd2Sjpk DTRACE_PROBE(tx__tndb__l0__tnrhtp__copyin); 66345916cd2Sjpk return (set_errno(EFAULT)); 66445916cd2Sjpk } 66545916cd2Sjpk 66645916cd2Sjpk /* 66745916cd2Sjpk * Don't let the user give us a bogus (unterminated) template 66845916cd2Sjpk * name. 66945916cd2Sjpk */ 67045916cd2Sjpk if (!template_name_ok(rhtpent.name)) 67145916cd2Sjpk return (set_errno(EINVAL)); 67245916cd2Sjpk } 67345916cd2Sjpk 67445916cd2Sjpk switch (cmd) { 67545916cd2Sjpk case TNDB_LOAD: 67645916cd2Sjpk DTRACE_PROBE1(tx__tndb__l2__tnrhtp__tndbload, char *, 67745916cd2Sjpk rhtpent.name); 67845916cd2Sjpk type = rhtpent.host_type; 67945916cd2Sjpk if (type != UNLABELED && type != SUN_CIPSO) { 68045916cd2Sjpk retv = EINVAL; 68145916cd2Sjpk break; 68245916cd2Sjpk } 68345916cd2Sjpk 68445916cd2Sjpk if (tnrhtp_create(&rhtpent, KM_NOSLEEP) == NULL) 68545916cd2Sjpk retv = ENOMEM; 68645916cd2Sjpk else 68745916cd2Sjpk retv = 0; 68845916cd2Sjpk break; 68945916cd2Sjpk 69045916cd2Sjpk case TNDB_GET: 69145916cd2Sjpk DTRACE_PROBE1(tx__tndb__l4__tnrhtp__tndbget, char *, 69245916cd2Sjpk rhtpent.name); 69345916cd2Sjpk tpc = tnrhtp_find(rhtpent.name, tpc_name_hash); 69445916cd2Sjpk if (tpc == NULL) { 69545916cd2Sjpk retv = ENOENT; 69645916cd2Sjpk break; 69745916cd2Sjpk } 69845916cd2Sjpk 69945916cd2Sjpk /* Copy out result */ 70045916cd2Sjpk if (copyout(&tpc->tpc_tp, buf, sizeof (tpc->tpc_tp)) != 0) { 70145916cd2Sjpk DTRACE_PROBE(tx__tndb__l0__tnrhtp__copyout); 70245916cd2Sjpk retv = EFAULT; 70345916cd2Sjpk } else { 70445916cd2Sjpk retv = 0; 70545916cd2Sjpk } 70645916cd2Sjpk TPC_RELE(tpc); 70745916cd2Sjpk break; 70845916cd2Sjpk 70945916cd2Sjpk case TNDB_DELETE: 71045916cd2Sjpk DTRACE_PROBE1(tx__tndb__l4__tnrhtp__tndbdelete, char *, 71145916cd2Sjpk rhtpent.name); 71245916cd2Sjpk retv = tnrhtp_delete(rhtpent.name); 71345916cd2Sjpk break; 71445916cd2Sjpk 71545916cd2Sjpk case TNDB_FLUSH: 71645916cd2Sjpk DTRACE_PROBE(tx__tndb__l4__tnrhtp__flush); 71745916cd2Sjpk tnrhtp_flush(); 71845916cd2Sjpk retv = 0; 71945916cd2Sjpk break; 72045916cd2Sjpk 72145916cd2Sjpk default: 72245916cd2Sjpk DTRACE_PROBE1(tx__tndb__l0__tnrhtp__unknowncmd, int, 72345916cd2Sjpk cmd); 72445916cd2Sjpk retv = EOPNOTSUPP; 72545916cd2Sjpk break; 72645916cd2Sjpk } 72745916cd2Sjpk 72845916cd2Sjpk if (retv != 0) 72945916cd2Sjpk return (set_errno(retv)); 73045916cd2Sjpk else 73145916cd2Sjpk return (retv); 73245916cd2Sjpk } 73345916cd2Sjpk 73445916cd2Sjpk /* 73545916cd2Sjpk * MLP entry ordering logic 73645916cd2Sjpk * 73745916cd2Sjpk * There are two loops in this routine. The first loop finds the entry that 73845916cd2Sjpk * either logically follows the new entry to be inserted, or is the entry that 73945916cd2Sjpk * precedes and overlaps the new entry, or is NULL to mean end-of-list. This 74045916cd2Sjpk * is 'tme.' The second loop scans ahead from that point to find any overlap 74145916cd2Sjpk * on the front or back of this new entry. 74245916cd2Sjpk * 74345916cd2Sjpk * For the first loop, we can have the following cases in the list (note that 74445916cd2Sjpk * the port-portmax range is inclusive): 74545916cd2Sjpk * 74645916cd2Sjpk * port portmax 74745916cd2Sjpk * +--------+ 74845916cd2Sjpk * 1: +------+ ................... precedes; skip to next 74945916cd2Sjpk * 2: +------+ ............. overlaps; stop here if same protocol 75045916cd2Sjpk * 3: +------+ ......... overlaps; stop if same or higher protocol 75145916cd2Sjpk * 4: +-------+ .... overlaps or succeeds; stop here 75245916cd2Sjpk * 75345916cd2Sjpk * For the second loop, we can have the following cases (note that we need not 75445916cd2Sjpk * care about other protocol entries at this point, because we're only looking 75545916cd2Sjpk * for overlap, not an insertion point): 75645916cd2Sjpk * 75745916cd2Sjpk * port portmax 75845916cd2Sjpk * +--------+ 75945916cd2Sjpk * 5: +------+ ............. overlaps; stop if same protocol 76045916cd2Sjpk * 6: +------+ ......... overlaps; stop if same protocol 76145916cd2Sjpk * 7: +-------+ .... overlaps; stop if same protocol 76245916cd2Sjpk * 8: +---+ . follows; search is done 76345916cd2Sjpk * 76445916cd2Sjpk * In other words, this second search needs to consider only whether the entry 76545916cd2Sjpk * has a starting port number that's greater than the end point of the new 76645916cd2Sjpk * entry. All others are overlaps. 76745916cd2Sjpk */ 76845916cd2Sjpk static int 76945916cd2Sjpk mlp_add_del(tsol_mlp_list_t *mlpl, zoneid_t zoneid, uint8_t proto, 77045916cd2Sjpk uint16_t port, uint16_t portmax, boolean_t addflag) 77145916cd2Sjpk { 77245916cd2Sjpk int retv; 77345916cd2Sjpk tsol_mlp_entry_t *tme, *tme2, *newent; 77445916cd2Sjpk 77545916cd2Sjpk if (addflag) { 77645916cd2Sjpk if ((newent = kmem_zalloc(sizeof (*newent), KM_NOSLEEP)) == 77745916cd2Sjpk NULL) 77845916cd2Sjpk return (ENOMEM); 77945916cd2Sjpk } else { 78045916cd2Sjpk newent = NULL; 78145916cd2Sjpk } 78245916cd2Sjpk rw_enter(&mlpl->mlpl_rwlock, RW_WRITER); 78345916cd2Sjpk 78445916cd2Sjpk /* 78545916cd2Sjpk * First loop: find logical insertion point or overlap. Table is kept 78645916cd2Sjpk * in order of port number first, and then, within that, by protocol 78745916cd2Sjpk * number. 78845916cd2Sjpk */ 78945916cd2Sjpk for (tme = mlpl->mlpl_first; tme != NULL; tme = tme->mlpe_next) { 79045916cd2Sjpk /* logically next (case 4) */ 79145916cd2Sjpk if (tme->mlpe_mlp.mlp_port > port) 79245916cd2Sjpk break; 79345916cd2Sjpk /* if this is logically next or overlap, then stop (case 3) */ 79445916cd2Sjpk if (tme->mlpe_mlp.mlp_port == port && 79545916cd2Sjpk tme->mlpe_mlp.mlp_ipp >= proto) 79645916cd2Sjpk break; 79745916cd2Sjpk /* earlier or same port sequence; check for overlap (case 2) */ 79845916cd2Sjpk if (tme->mlpe_mlp.mlp_ipp == proto && 79945916cd2Sjpk tme->mlpe_mlp.mlp_port_upper >= port) 80045916cd2Sjpk break; 80145916cd2Sjpk /* otherwise, loop again (case 1) */ 80245916cd2Sjpk } 80345916cd2Sjpk 80445916cd2Sjpk /* Second loop: scan ahead for overlap */ 80545916cd2Sjpk for (tme2 = tme; tme2 != NULL; tme2 = tme2->mlpe_next) { 80645916cd2Sjpk /* check if entry follows; no overlap (case 8) */ 80745916cd2Sjpk if (tme2->mlpe_mlp.mlp_port > portmax) { 80845916cd2Sjpk tme2 = NULL; 80945916cd2Sjpk break; 81045916cd2Sjpk } 81145916cd2Sjpk /* only exact protocol matches at this point (cases 5-7) */ 81245916cd2Sjpk if (tme2->mlpe_mlp.mlp_ipp == proto) 81345916cd2Sjpk break; 81445916cd2Sjpk } 81545916cd2Sjpk 81645916cd2Sjpk retv = 0; 81745916cd2Sjpk if (addflag) { 81845916cd2Sjpk if (tme2 != NULL) { 81945916cd2Sjpk retv = EEXIST; 82045916cd2Sjpk } else { 82145916cd2Sjpk newent->mlpe_zoneid = zoneid; 82245916cd2Sjpk newent->mlpe_mlp.mlp_ipp = proto; 82345916cd2Sjpk newent->mlpe_mlp.mlp_port = port; 82445916cd2Sjpk newent->mlpe_mlp.mlp_port_upper = portmax; 82545916cd2Sjpk newent->mlpe_next = tme; 82645916cd2Sjpk if (tme == NULL) { 82745916cd2Sjpk tme2 = mlpl->mlpl_last; 82845916cd2Sjpk mlpl->mlpl_last = newent; 82945916cd2Sjpk } else { 83045916cd2Sjpk tme2 = tme->mlpe_prev; 83145916cd2Sjpk tme->mlpe_prev = newent; 83245916cd2Sjpk } 83345916cd2Sjpk newent->mlpe_prev = tme2; 83445916cd2Sjpk if (tme2 == NULL) 83545916cd2Sjpk mlpl->mlpl_first = newent; 83645916cd2Sjpk else 83745916cd2Sjpk tme2->mlpe_next = newent; 83845916cd2Sjpk newent = NULL; 83945916cd2Sjpk } 84045916cd2Sjpk } else { 84145916cd2Sjpk if (tme2 == NULL || tme2->mlpe_mlp.mlp_port != port || 84245916cd2Sjpk tme2->mlpe_mlp.mlp_port_upper != portmax) { 84345916cd2Sjpk retv = ENOENT; 84445916cd2Sjpk } else { 84545916cd2Sjpk if ((tme2 = tme->mlpe_prev) == NULL) 84645916cd2Sjpk mlpl->mlpl_first = tme->mlpe_next; 84745916cd2Sjpk else 84845916cd2Sjpk tme2->mlpe_next = tme->mlpe_next; 84945916cd2Sjpk if ((tme2 = tme->mlpe_next) == NULL) 85045916cd2Sjpk mlpl->mlpl_last = tme->mlpe_prev; 85145916cd2Sjpk else 85245916cd2Sjpk tme2->mlpe_prev = tme->mlpe_prev; 85345916cd2Sjpk newent = tme; 85445916cd2Sjpk } 85545916cd2Sjpk } 85645916cd2Sjpk rw_exit(&mlpl->mlpl_rwlock); 85745916cd2Sjpk 85845916cd2Sjpk if (newent != NULL) 85945916cd2Sjpk kmem_free(newent, sizeof (*newent)); 86045916cd2Sjpk 86145916cd2Sjpk return (retv); 86245916cd2Sjpk } 86345916cd2Sjpk 86445916cd2Sjpk /* 86545916cd2Sjpk * Add or remove an MLP entry from the database so that the classifier can find 86645916cd2Sjpk * it. 86745916cd2Sjpk * 86845916cd2Sjpk * Note: port number is in host byte order. 86945916cd2Sjpk */ 87045916cd2Sjpk int 87145916cd2Sjpk tsol_mlp_anon(zone_t *zone, mlp_type_t mlptype, uchar_t proto, uint16_t port, 87245916cd2Sjpk boolean_t addflag) 87345916cd2Sjpk { 87445916cd2Sjpk int retv = 0; 87545916cd2Sjpk 87645916cd2Sjpk if (mlptype == mlptBoth || mlptype == mlptPrivate) 87745916cd2Sjpk retv = mlp_add_del(&zone->zone_mlps, zone->zone_id, proto, 87845916cd2Sjpk port, port, addflag); 87945916cd2Sjpk if ((retv == 0 || !addflag) && 88045916cd2Sjpk (mlptype == mlptBoth || mlptype == mlptShared)) { 88145916cd2Sjpk retv = mlp_add_del(&shared_mlps, zone->zone_id, proto, port, 88245916cd2Sjpk port, addflag); 88345916cd2Sjpk if (retv != 0 && addflag) 88445916cd2Sjpk (void) mlp_add_del(&zone->zone_mlps, zone->zone_id, 88545916cd2Sjpk proto, port, port, B_FALSE); 88645916cd2Sjpk } 88745916cd2Sjpk return (retv); 88845916cd2Sjpk } 88945916cd2Sjpk 89045916cd2Sjpk static void 89145916cd2Sjpk mlp_flush(tsol_mlp_list_t *mlpl, zoneid_t zoneid) 89245916cd2Sjpk { 89345916cd2Sjpk tsol_mlp_entry_t *tme, *tme2, *tmnext; 89445916cd2Sjpk 89545916cd2Sjpk rw_enter(&mlpl->mlpl_rwlock, RW_WRITER); 89645916cd2Sjpk for (tme = mlpl->mlpl_first; tme != NULL; tme = tmnext) { 89745916cd2Sjpk tmnext = tme->mlpe_next; 89845916cd2Sjpk if (zoneid == ALL_ZONES || tme->mlpe_zoneid == zoneid) { 89945916cd2Sjpk if ((tme2 = tme->mlpe_prev) == NULL) 90045916cd2Sjpk mlpl->mlpl_first = tmnext; 90145916cd2Sjpk else 90245916cd2Sjpk tme2->mlpe_next = tmnext; 90345916cd2Sjpk if (tmnext == NULL) 90445916cd2Sjpk mlpl->mlpl_last = tme2; 90545916cd2Sjpk else 90645916cd2Sjpk tmnext->mlpe_prev = tme2; 90745916cd2Sjpk kmem_free(tme, sizeof (*tme)); 90845916cd2Sjpk } 90945916cd2Sjpk } 91045916cd2Sjpk rw_exit(&mlpl->mlpl_rwlock); 91145916cd2Sjpk } 91245916cd2Sjpk 91345916cd2Sjpk /* 91445916cd2Sjpk * Note: user supplies port numbers in host byte order. 91545916cd2Sjpk */ 91645916cd2Sjpk static int 91745916cd2Sjpk tnmlp(int cmd, void *buf) 91845916cd2Sjpk { 91945916cd2Sjpk int retv; 92045916cd2Sjpk tsol_mlpent_t tsme; 92145916cd2Sjpk zone_t *zone; 92245916cd2Sjpk tsol_mlp_list_t *mlpl; 92345916cd2Sjpk tsol_mlp_entry_t *tme; 92445916cd2Sjpk 92545916cd2Sjpk /* Make sure user has sufficient privilege */ 92645916cd2Sjpk if (cmd != TNDB_GET && 92745916cd2Sjpk (retv = secpolicy_net_config(CRED(), B_FALSE)) != 0) 92845916cd2Sjpk return (set_errno(retv)); 92945916cd2Sjpk 93045916cd2Sjpk /* 93145916cd2Sjpk * Get argument. Note that tsol_mlpent_t is the same on LP64 and 93245916cd2Sjpk * ILP32, so no special handling is required. 93345916cd2Sjpk */ 93445916cd2Sjpk if (copyin(buf, &tsme, sizeof (tsme)) != 0) { 93545916cd2Sjpk DTRACE_PROBE(tx__tndb__l0__tnmlp__copyin); 93645916cd2Sjpk return (set_errno(EFAULT)); 93745916cd2Sjpk } 93845916cd2Sjpk 93945916cd2Sjpk /* MLPs on shared IP addresses */ 94045916cd2Sjpk if (tsme.tsme_flags & TSOL_MEF_SHARED) { 94145916cd2Sjpk zone = NULL; 94245916cd2Sjpk mlpl = &shared_mlps; 94345916cd2Sjpk } else { 94445916cd2Sjpk zone = zone_find_by_id(tsme.tsme_zoneid); 94545916cd2Sjpk if (zone == NULL) 94645916cd2Sjpk return (set_errno(EINVAL)); 94745916cd2Sjpk mlpl = &zone->zone_mlps; 94845916cd2Sjpk } 94945916cd2Sjpk if (tsme.tsme_mlp.mlp_port_upper == 0) 95045916cd2Sjpk tsme.tsme_mlp.mlp_port_upper = tsme.tsme_mlp.mlp_port; 95145916cd2Sjpk 95245916cd2Sjpk switch (cmd) { 95345916cd2Sjpk case TNDB_LOAD: 95445916cd2Sjpk DTRACE_PROBE1(tx__tndb__l2__tnmlp__tndbload, 95545916cd2Sjpk tsol_mlpent_t *, &tsme); 95645916cd2Sjpk if (tsme.tsme_mlp.mlp_ipp == 0 || tsme.tsme_mlp.mlp_port == 0 || 95745916cd2Sjpk tsme.tsme_mlp.mlp_port > tsme.tsme_mlp.mlp_port_upper) { 95845916cd2Sjpk retv = EINVAL; 95945916cd2Sjpk break; 96045916cd2Sjpk } 96145916cd2Sjpk retv = mlp_add_del(mlpl, tsme.tsme_zoneid, 96245916cd2Sjpk tsme.tsme_mlp.mlp_ipp, tsme.tsme_mlp.mlp_port, 96345916cd2Sjpk tsme.tsme_mlp.mlp_port_upper, B_TRUE); 96445916cd2Sjpk break; 96545916cd2Sjpk 96645916cd2Sjpk case TNDB_GET: 96745916cd2Sjpk DTRACE_PROBE1(tx__tndb__l2__tnmlp__tndbget, 96845916cd2Sjpk tsol_mlpent_t *, &tsme); 96945916cd2Sjpk 97045916cd2Sjpk /* 97145916cd2Sjpk * Search for the requested element or, failing that, the one 97245916cd2Sjpk * that's logically next in the sequence. 97345916cd2Sjpk */ 97445916cd2Sjpk rw_enter(&mlpl->mlpl_rwlock, RW_READER); 97545916cd2Sjpk for (tme = mlpl->mlpl_first; tme != NULL; 97645916cd2Sjpk tme = tme->mlpe_next) { 97745916cd2Sjpk if (tsme.tsme_zoneid != ALL_ZONES && 97845916cd2Sjpk tme->mlpe_zoneid != tsme.tsme_zoneid) 97945916cd2Sjpk continue; 98045916cd2Sjpk if (tme->mlpe_mlp.mlp_ipp >= tsme.tsme_mlp.mlp_ipp && 98145916cd2Sjpk tme->mlpe_mlp.mlp_port == tsme.tsme_mlp.mlp_port) 98245916cd2Sjpk break; 98345916cd2Sjpk if (tme->mlpe_mlp.mlp_port > tsme.tsme_mlp.mlp_port) 98445916cd2Sjpk break; 98545916cd2Sjpk } 98645916cd2Sjpk if (tme == NULL) { 98745916cd2Sjpk retv = ENOENT; 98845916cd2Sjpk } else { 98945916cd2Sjpk tsme.tsme_zoneid = tme->mlpe_zoneid; 99045916cd2Sjpk tsme.tsme_mlp = tme->mlpe_mlp; 99145916cd2Sjpk retv = 0; 99245916cd2Sjpk } 99345916cd2Sjpk rw_exit(&mlpl->mlpl_rwlock); 99445916cd2Sjpk break; 99545916cd2Sjpk 99645916cd2Sjpk case TNDB_DELETE: 99745916cd2Sjpk DTRACE_PROBE1(tx__tndb__l4__tnmlp__tndbdelete, 99845916cd2Sjpk tsol_mlpent_t *, &tsme); 99945916cd2Sjpk retv = mlp_add_del(mlpl, tsme.tsme_zoneid, 100045916cd2Sjpk tsme.tsme_mlp.mlp_ipp, tsme.tsme_mlp.mlp_port, 100145916cd2Sjpk tsme.tsme_mlp.mlp_port_upper, B_FALSE); 100245916cd2Sjpk break; 100345916cd2Sjpk 100445916cd2Sjpk case TNDB_FLUSH: 100545916cd2Sjpk DTRACE_PROBE1(tx__tndb__l4__tnmlp__tndbflush, 100645916cd2Sjpk tsol_mlpent_t *, &tsme); 100745916cd2Sjpk mlp_flush(mlpl, ALL_ZONES); 100845916cd2Sjpk mlp_flush(&shared_mlps, tsme.tsme_zoneid); 100945916cd2Sjpk retv = 0; 101045916cd2Sjpk break; 101145916cd2Sjpk 101245916cd2Sjpk default: 101345916cd2Sjpk DTRACE_PROBE1(tx__tndb__l0__tnmlp__unknowncmd, int, 101445916cd2Sjpk cmd); 101545916cd2Sjpk retv = EOPNOTSUPP; 101645916cd2Sjpk break; 101745916cd2Sjpk } 101845916cd2Sjpk 101945916cd2Sjpk if (zone != NULL) 102045916cd2Sjpk zone_rele(zone); 102145916cd2Sjpk 102245916cd2Sjpk if (cmd == TNDB_GET && retv == 0) { 102345916cd2Sjpk /* Copy out result */ 102445916cd2Sjpk if (copyout(&tsme, buf, sizeof (tsme)) != 0) { 102545916cd2Sjpk DTRACE_PROBE(tx__tndb__l0__tnmlp__copyout); 102645916cd2Sjpk retv = EFAULT; 102745916cd2Sjpk } 102845916cd2Sjpk } 102945916cd2Sjpk 103045916cd2Sjpk if (retv != 0) 103145916cd2Sjpk return (set_errno(retv)); 103245916cd2Sjpk else 103345916cd2Sjpk return (retv); 103445916cd2Sjpk } 103545916cd2Sjpk 103645916cd2Sjpk /* 103745916cd2Sjpk * Returns a tnrhc matching the addr address. 103845916cd2Sjpk * The returned rhc's refcnt is incremented. 103945916cd2Sjpk */ 104045916cd2Sjpk tsol_tnrhc_t * 1041*bfabfc35Skp158701 find_rhc(const void *addr, uchar_t version, boolean_t staleok) 104245916cd2Sjpk { 104345916cd2Sjpk tsol_tnrhc_t *rh = NULL; 1044*bfabfc35Skp158701 tsol_tnrhc_t *new; 1045*bfabfc35Skp158701 tsol_tpc_t *tpc; 104645916cd2Sjpk tnrhc_hash_t *tnrhc_hash; 104745916cd2Sjpk ipaddr_t tmpmask; 1048*bfabfc35Skp158701 in_addr_t *in4 = (in_addr_t *)addr; 1049*bfabfc35Skp158701 in6_addr_t *in6 = (in6_addr_t *)addr; 1050*bfabfc35Skp158701 in_addr_t tmpin4; 1051*bfabfc35Skp158701 in6_addr_t tmpmask6; 105245916cd2Sjpk int i; 1053*bfabfc35Skp158701 int prefix; 105445916cd2Sjpk 1055*bfabfc35Skp158701 /* 1056*bfabfc35Skp158701 * An IPv4-mapped IPv6 address is really an IPv4 address 1057*bfabfc35Skp158701 * in IPv6 format. 1058*bfabfc35Skp158701 */ 1059*bfabfc35Skp158701 if (version == IPV6_VERSION && 1060*bfabfc35Skp158701 IN6_IS_ADDR_V4MAPPED(in6)) { 1061*bfabfc35Skp158701 IN6_V4MAPPED_TO_IPADDR(in6, tmpin4); 1062*bfabfc35Skp158701 version = IPV4_VERSION; 1063*bfabfc35Skp158701 in4 = &tmpin4; 1064*bfabfc35Skp158701 } 1065*bfabfc35Skp158701 1066*bfabfc35Skp158701 /* 1067*bfabfc35Skp158701 * Search the tnrh hash table for each prefix length, 1068*bfabfc35Skp158701 * starting at longest prefix length, until a matching 1069*bfabfc35Skp158701 * rhc entry is found. 1070*bfabfc35Skp158701 */ 1071*bfabfc35Skp158701 if (version == IPV4_VERSION) { 107245916cd2Sjpk for (i = (TSOL_MASK_TABLE_SIZE - 1); i >= 0; i--) { 107345916cd2Sjpk 107445916cd2Sjpk if ((tnrhc_table[i]) == NULL) 107545916cd2Sjpk continue; 107645916cd2Sjpk 107745916cd2Sjpk tmpmask = tsol_plen_to_mask(i); 107845916cd2Sjpk tnrhc_hash = &tnrhc_table[i][ 107945916cd2Sjpk TSOL_ADDR_HASH(*in4 & tmpmask, TNRHC_SIZE)]; 108045916cd2Sjpk 108145916cd2Sjpk mutex_enter(&tnrhc_hash->tnrh_lock); 108245916cd2Sjpk for (rh = tnrhc_hash->tnrh_list; rh != NULL; 108345916cd2Sjpk rh = rh->rhc_next) { 108445916cd2Sjpk if ((rh->rhc_host.ta_family == AF_INET) && 1085*bfabfc35Skp158701 ((rh->rhc_host.ta_addr_v4.s_addr & 1086*bfabfc35Skp158701 tmpmask) == (*in4 & tmpmask))) { 1087*bfabfc35Skp158701 prefix = i; 108845916cd2Sjpk TNRHC_HOLD(rh); 1089*bfabfc35Skp158701 break; 109045916cd2Sjpk } 109145916cd2Sjpk } 109245916cd2Sjpk mutex_exit(&tnrhc_hash->tnrh_lock); 1093*bfabfc35Skp158701 if (rh != NULL) 1094*bfabfc35Skp158701 break; 109545916cd2Sjpk } 1096*bfabfc35Skp158701 if (rh == NULL) 1097*bfabfc35Skp158701 DTRACE_PROBE1(tx__tndb__l1__findrhc__norhv4ent, 1098*bfabfc35Skp158701 in_addr_t *, in4); 1099*bfabfc35Skp158701 } else { 110045916cd2Sjpk for (i = (TSOL_MASK_TABLE_SIZE_V6 - 1); i >= 0; i--) { 110145916cd2Sjpk if ((tnrhc_table_v6[i]) == NULL) 110245916cd2Sjpk continue; 110345916cd2Sjpk 1104*bfabfc35Skp158701 tsol_plen_to_mask_v6(i, &tmpmask6); 110545916cd2Sjpk tnrhc_hash = &tnrhc_table_v6[i][ 1106*bfabfc35Skp158701 TSOL_ADDR_MASK_HASH_V6(*in6, tmpmask6, TNRHC_SIZE)]; 110745916cd2Sjpk 110845916cd2Sjpk mutex_enter(&tnrhc_hash->tnrh_lock); 110945916cd2Sjpk for (rh = tnrhc_hash->tnrh_list; rh != NULL; 111045916cd2Sjpk rh = rh->rhc_next) { 111145916cd2Sjpk if ((rh->rhc_host.ta_family == AF_INET6) && 1112*bfabfc35Skp158701 V6_MASK_EQ_2(rh->rhc_host.ta_addr_v6, 1113*bfabfc35Skp158701 tmpmask6, *in6)) { 1114*bfabfc35Skp158701 prefix = i; 111545916cd2Sjpk TNRHC_HOLD(rh); 1116*bfabfc35Skp158701 break; 111745916cd2Sjpk } 111845916cd2Sjpk } 111945916cd2Sjpk mutex_exit(&tnrhc_hash->tnrh_lock); 1120*bfabfc35Skp158701 if (rh != NULL) 1121*bfabfc35Skp158701 break; 1122*bfabfc35Skp158701 } 1123*bfabfc35Skp158701 if (rh == NULL) 1124*bfabfc35Skp158701 DTRACE_PROBE1(tx__tndb__l1__findrhc__norhv6ent, 1125*bfabfc35Skp158701 in6_addr_t *, in6); 112645916cd2Sjpk } 112745916cd2Sjpk 1128*bfabfc35Skp158701 /* 1129*bfabfc35Skp158701 * Does the tnrh entry point to a stale template? 1130*bfabfc35Skp158701 * This can happen any time the user deletes or modifies 1131*bfabfc35Skp158701 * a template that has existing tnrh entries pointing 1132*bfabfc35Skp158701 * to it. Try to find a new version of the template. 1133*bfabfc35Skp158701 * If there is no template, then just give up. 1134*bfabfc35Skp158701 * If the template exists, reload the tnrh entry. 1135*bfabfc35Skp158701 */ 1136*bfabfc35Skp158701 if (rh != NULL && rh->rhc_tpc->tpc_invalid) { 1137*bfabfc35Skp158701 tpc = tnrhtp_find(rh->rhc_tpc->tpc_tp.name, tpc_name_hash); 1138*bfabfc35Skp158701 if (tpc == NULL) { 1139*bfabfc35Skp158701 if (!staleok) { 1140*bfabfc35Skp158701 DTRACE_PROBE2(tx__tndb__l1__findrhc__staletpc, 1141*bfabfc35Skp158701 tsol_tnrhc_t *, rh, tsol_tpc_t *, 1142*bfabfc35Skp158701 rh->rhc_tpc); 1143*bfabfc35Skp158701 TNRHC_RELE(rh); 1144*bfabfc35Skp158701 rh = NULL; 1145*bfabfc35Skp158701 } 1146*bfabfc35Skp158701 } else { 1147*bfabfc35Skp158701 ASSERT(tpc->tpc_tp.host_type == UNLABELED || 1148*bfabfc35Skp158701 tpc->tpc_tp.host_type == SUN_CIPSO); 1149*bfabfc35Skp158701 1150*bfabfc35Skp158701 if ((new = kmem_zalloc(sizeof (*new), 1151*bfabfc35Skp158701 KM_NOSLEEP)) == NULL) { 1152*bfabfc35Skp158701 DTRACE_PROBE(tx__tndb__l1__findrhc__nomem); 1153*bfabfc35Skp158701 TNRHC_RELE(rh); 1154*bfabfc35Skp158701 TPC_RELE(tpc); 115545916cd2Sjpk return (NULL); 115645916cd2Sjpk } 115745916cd2Sjpk 1158*bfabfc35Skp158701 mutex_init(&new->rhc_lock, NULL, MUTEX_DEFAULT, NULL); 1159*bfabfc35Skp158701 new->rhc_host = rh->rhc_host; 1160*bfabfc35Skp158701 new->rhc_tpc = tpc; 1161*bfabfc35Skp158701 new->rhc_isbcast = rh->rhc_isbcast; 1162*bfabfc35Skp158701 new->rhc_local = rh->rhc_local; 1163*bfabfc35Skp158701 TNRHC_RELE(rh); 1164*bfabfc35Skp158701 rh = new; 1165*bfabfc35Skp158701 1166*bfabfc35Skp158701 /* 1167*bfabfc35Skp158701 * This function increments the tnrh entry ref count 1168*bfabfc35Skp158701 * for the pointer returned to the caller. 1169*bfabfc35Skp158701 * tnrh_hash_add increments the tnrh entry ref count 1170*bfabfc35Skp158701 * for the pointer in the hash table. 1171*bfabfc35Skp158701 */ 1172*bfabfc35Skp158701 TNRHC_HOLD(rh); 1173*bfabfc35Skp158701 if (tnrh_hash_add(new, prefix) != 0) { 1174*bfabfc35Skp158701 TNRHC_RELE(rh); 1175*bfabfc35Skp158701 rh = NULL; 1176*bfabfc35Skp158701 } 1177*bfabfc35Skp158701 } 1178*bfabfc35Skp158701 } 1179*bfabfc35Skp158701 return (rh); 1180*bfabfc35Skp158701 } 1181*bfabfc35Skp158701 118245916cd2Sjpk tsol_tpc_t * 118345916cd2Sjpk find_tpc(const void *addr, uchar_t version, boolean_t staleok) 118445916cd2Sjpk { 118545916cd2Sjpk tsol_tpc_t *tpc; 118645916cd2Sjpk tsol_tnrhc_t *rhc; 118745916cd2Sjpk 1188*bfabfc35Skp158701 if ((rhc = find_rhc(addr, version, staleok)) == NULL) 1189*bfabfc35Skp158701 return (NULL); 119045916cd2Sjpk 119145916cd2Sjpk tpc = rhc->rhc_tpc; 119245916cd2Sjpk TPC_HOLD(tpc); 119345916cd2Sjpk TNRHC_RELE(rhc); 119445916cd2Sjpk return (tpc); 119545916cd2Sjpk } 119645916cd2Sjpk 119745916cd2Sjpk /* 119845916cd2Sjpk * create an internal template called "_unlab": 119945916cd2Sjpk * 120045916cd2Sjpk * _unlab;\ 120145916cd2Sjpk * host_type = unlabeled;\ 120245916cd2Sjpk * def_label = ADMIN_LOW[ADMIN_LOW];\ 120345916cd2Sjpk * min_sl = ADMIN_LOW;\ 120445916cd2Sjpk * max_sl = ADMIN_HIGH; 120545916cd2Sjpk */ 120645916cd2Sjpk static void 120745916cd2Sjpk tsol_create_i_tmpls(void) 120845916cd2Sjpk { 120945916cd2Sjpk tsol_tpent_t rhtpent; 121045916cd2Sjpk 121145916cd2Sjpk bzero(&rhtpent, sizeof (rhtpent)); 121245916cd2Sjpk 121345916cd2Sjpk /* create _unlab */ 121445916cd2Sjpk (void) strcpy(rhtpent.name, "_unlab"); 121545916cd2Sjpk 121645916cd2Sjpk rhtpent.host_type = UNLABELED; 121745916cd2Sjpk rhtpent.tp_mask_unl = TSOL_MSK_DEF_LABEL | TSOL_MSK_DEF_CL | 121845916cd2Sjpk TSOL_MSK_SL_RANGE_TSOL; 121945916cd2Sjpk 122045916cd2Sjpk rhtpent.tp_gw_sl_range.lower_bound = *label2bslabel(l_admin_low); 122145916cd2Sjpk rhtpent.tp_def_label = rhtpent.tp_gw_sl_range.lower_bound; 122245916cd2Sjpk rhtpent.tp_gw_sl_range.upper_bound = *label2bslabel(l_admin_high); 122345916cd2Sjpk rhtpent.tp_cipso_doi_unl = default_doi; 122445916cd2Sjpk tpc_unlab = tnrhtp_create(&rhtpent, KM_SLEEP); 122545916cd2Sjpk } 122645916cd2Sjpk 122745916cd2Sjpk /* 122845916cd2Sjpk * set up internal host template, called from kernel only. 122945916cd2Sjpk */ 123045916cd2Sjpk static void 123145916cd2Sjpk tsol_create_i_tnrh(const tnaddr_t *sa) 123245916cd2Sjpk { 123345916cd2Sjpk tsol_tnrhc_t *rh, *new; 123445916cd2Sjpk tnrhc_hash_t *tnrhc_hash; 123545916cd2Sjpk 123645916cd2Sjpk /* Allocate a new entry before taking the lock */ 123745916cd2Sjpk new = kmem_zalloc(sizeof (*new), KM_SLEEP); 123845916cd2Sjpk 123945916cd2Sjpk tnrhc_hash = (sa->ta_family == AF_INET) ? &tnrhc_table[0][0] : 124045916cd2Sjpk &tnrhc_table_v6[0][0]; 124145916cd2Sjpk 124245916cd2Sjpk mutex_enter(&tnrhc_hash->tnrh_lock); 124345916cd2Sjpk rh = tnrhc_hash->tnrh_list; 124445916cd2Sjpk 124545916cd2Sjpk if (rh == NULL) { 124645916cd2Sjpk /* We're keeping the new entry. */ 124745916cd2Sjpk rh = new; 124845916cd2Sjpk new = NULL; 124945916cd2Sjpk rh->rhc_host = *sa; 125045916cd2Sjpk mutex_init(&rh->rhc_lock, NULL, MUTEX_DEFAULT, NULL); 125145916cd2Sjpk TNRHC_HOLD(rh); 125245916cd2Sjpk tnrhc_hash->tnrh_list = rh; 125345916cd2Sjpk } 125445916cd2Sjpk 125545916cd2Sjpk /* 125645916cd2Sjpk * Link the entry to internal_unlab 125745916cd2Sjpk */ 125845916cd2Sjpk if (rh->rhc_tpc != tpc_unlab) { 125945916cd2Sjpk if (rh->rhc_tpc != NULL) 126045916cd2Sjpk TPC_RELE(rh->rhc_tpc); 126145916cd2Sjpk rh->rhc_tpc = tpc_unlab; 126245916cd2Sjpk TPC_HOLD(tpc_unlab); 126345916cd2Sjpk } 126445916cd2Sjpk mutex_exit(&tnrhc_hash->tnrh_lock); 126545916cd2Sjpk if (new != NULL) 126645916cd2Sjpk kmem_free(new, sizeof (*new)); 126745916cd2Sjpk } 126845916cd2Sjpk 126945916cd2Sjpk /* 127045916cd2Sjpk * Returns 0 if the port is known to be SLP. Returns next possible port number 127145916cd2Sjpk * (wrapping through 1) if port is MLP on shared or global. Administrator 127245916cd2Sjpk * should not make all ports MLP. If that's done, then we'll just pretend 127345916cd2Sjpk * everything is SLP to avoid looping forever. 127445916cd2Sjpk * 127545916cd2Sjpk * Note: port is in host byte order. 127645916cd2Sjpk */ 127745916cd2Sjpk in_port_t 127845916cd2Sjpk tsol_next_port(zone_t *zone, in_port_t port, int proto, boolean_t upward) 127945916cd2Sjpk { 128045916cd2Sjpk boolean_t loop; 128145916cd2Sjpk tsol_mlp_entry_t *tme; 128245916cd2Sjpk int newport = port; 128345916cd2Sjpk 128445916cd2Sjpk loop = B_FALSE; 128545916cd2Sjpk for (;;) { 128645916cd2Sjpk if (zone != NULL && zone->zone_mlps.mlpl_first != NULL) { 128745916cd2Sjpk rw_enter(&zone->zone_mlps.mlpl_rwlock, RW_READER); 128845916cd2Sjpk for (tme = zone->zone_mlps.mlpl_first; tme != NULL; 128945916cd2Sjpk tme = tme->mlpe_next) { 129045916cd2Sjpk if (proto == tme->mlpe_mlp.mlp_ipp && 129145916cd2Sjpk newport >= tme->mlpe_mlp.mlp_port && 129245916cd2Sjpk newport <= tme->mlpe_mlp.mlp_port_upper) 129345916cd2Sjpk newport = upward ? 129445916cd2Sjpk tme->mlpe_mlp.mlp_port_upper + 1 : 129545916cd2Sjpk tme->mlpe_mlp.mlp_port - 1; 129645916cd2Sjpk } 129745916cd2Sjpk rw_exit(&zone->zone_mlps.mlpl_rwlock); 129845916cd2Sjpk } 129945916cd2Sjpk if (shared_mlps.mlpl_first != NULL) { 130045916cd2Sjpk rw_enter(&shared_mlps.mlpl_rwlock, RW_READER); 130145916cd2Sjpk for (tme = shared_mlps.mlpl_first; tme != NULL; 130245916cd2Sjpk tme = tme->mlpe_next) { 130345916cd2Sjpk if (proto == tme->mlpe_mlp.mlp_ipp && 130445916cd2Sjpk newport >= tme->mlpe_mlp.mlp_port && 130545916cd2Sjpk newport <= tme->mlpe_mlp.mlp_port_upper) 130645916cd2Sjpk newport = upward ? 130745916cd2Sjpk tme->mlpe_mlp.mlp_port_upper + 1 : 130845916cd2Sjpk tme->mlpe_mlp.mlp_port - 1; 130945916cd2Sjpk } 131045916cd2Sjpk rw_exit(&shared_mlps.mlpl_rwlock); 131145916cd2Sjpk } 131245916cd2Sjpk if (newport <= 65535 && newport > 0) 131345916cd2Sjpk break; 131445916cd2Sjpk if (loop) 131545916cd2Sjpk return (0); 131645916cd2Sjpk loop = B_TRUE; 131745916cd2Sjpk newport = upward ? 1 : 65535; 131845916cd2Sjpk } 131945916cd2Sjpk return (newport == port ? 0 : newport); 132045916cd2Sjpk } 132145916cd2Sjpk 132245916cd2Sjpk /* 132345916cd2Sjpk * tsol_mlp_port_type will check if the given (zone, proto, port) is a 132445916cd2Sjpk * multilevel port. If it is, return the type (shared, private, or both), or 132545916cd2Sjpk * indicate that it's single-level. 132645916cd2Sjpk * 132745916cd2Sjpk * Note: port is given in host byte order, not network byte order. 132845916cd2Sjpk */ 132945916cd2Sjpk mlp_type_t 133045916cd2Sjpk tsol_mlp_port_type(zone_t *zone, uchar_t proto, uint16_t port, 133145916cd2Sjpk mlp_type_t mlptype) 133245916cd2Sjpk { 133345916cd2Sjpk tsol_mlp_entry_t *tme; 133445916cd2Sjpk 133545916cd2Sjpk if (mlptype == mlptBoth || mlptype == mlptPrivate) { 133645916cd2Sjpk tme = NULL; 133745916cd2Sjpk if (zone->zone_mlps.mlpl_first != NULL) { 133845916cd2Sjpk rw_enter(&zone->zone_mlps.mlpl_rwlock, RW_READER); 133945916cd2Sjpk for (tme = zone->zone_mlps.mlpl_first; tme != NULL; 134045916cd2Sjpk tme = tme->mlpe_next) { 134145916cd2Sjpk if (proto == tme->mlpe_mlp.mlp_ipp && 134245916cd2Sjpk port >= tme->mlpe_mlp.mlp_port && 134345916cd2Sjpk port <= tme->mlpe_mlp.mlp_port_upper) 134445916cd2Sjpk break; 134545916cd2Sjpk } 134645916cd2Sjpk rw_exit(&zone->zone_mlps.mlpl_rwlock); 134745916cd2Sjpk } 134845916cd2Sjpk if (tme == NULL) { 134945916cd2Sjpk if (mlptype == mlptBoth) 135045916cd2Sjpk mlptype = mlptShared; 135145916cd2Sjpk else if (mlptype == mlptPrivate) 135245916cd2Sjpk mlptype = mlptSingle; 135345916cd2Sjpk } 135445916cd2Sjpk } 135545916cd2Sjpk if (mlptype == mlptBoth || mlptype == mlptShared) { 135645916cd2Sjpk tme = NULL; 135745916cd2Sjpk if (shared_mlps.mlpl_first != NULL) { 135845916cd2Sjpk rw_enter(&shared_mlps.mlpl_rwlock, RW_READER); 135945916cd2Sjpk for (tme = shared_mlps.mlpl_first; tme != NULL; 136045916cd2Sjpk tme = tme->mlpe_next) { 136145916cd2Sjpk if (proto == tme->mlpe_mlp.mlp_ipp && 136245916cd2Sjpk port >= tme->mlpe_mlp.mlp_port && 136345916cd2Sjpk port <= tme->mlpe_mlp.mlp_port_upper) 136445916cd2Sjpk break; 136545916cd2Sjpk } 136645916cd2Sjpk rw_exit(&shared_mlps.mlpl_rwlock); 136745916cd2Sjpk } 136845916cd2Sjpk if (tme == NULL) { 136945916cd2Sjpk if (mlptype == mlptBoth) 137045916cd2Sjpk mlptype = mlptPrivate; 137145916cd2Sjpk else if (mlptype == mlptShared) 137245916cd2Sjpk mlptype = mlptSingle; 137345916cd2Sjpk } 137445916cd2Sjpk } 137545916cd2Sjpk return (mlptype); 137645916cd2Sjpk } 137745916cd2Sjpk 137845916cd2Sjpk /* 137945916cd2Sjpk * tsol_mlp_findzone will check if the given (proto, port) is a multilevel port 138045916cd2Sjpk * on a shared address. If it is, return the owning zone. 138145916cd2Sjpk * 138245916cd2Sjpk * Note: lport is in network byte order, unlike the other MLP functions, 138345916cd2Sjpk * because the callers of this function are all dealing with packets off the 138445916cd2Sjpk * wire. 138545916cd2Sjpk */ 138645916cd2Sjpk zoneid_t 138745916cd2Sjpk tsol_mlp_findzone(uchar_t proto, uint16_t lport) 138845916cd2Sjpk { 138945916cd2Sjpk tsol_mlp_entry_t *tme; 139045916cd2Sjpk zoneid_t zoneid; 139145916cd2Sjpk uint16_t port; 139245916cd2Sjpk 139345916cd2Sjpk if (shared_mlps.mlpl_first == NULL) 139445916cd2Sjpk return (ALL_ZONES); 139545916cd2Sjpk port = ntohs(lport); 139645916cd2Sjpk rw_enter(&shared_mlps.mlpl_rwlock, RW_READER); 139745916cd2Sjpk for (tme = shared_mlps.mlpl_first; tme != NULL; tme = tme->mlpe_next) { 139845916cd2Sjpk if (proto == tme->mlpe_mlp.mlp_ipp && 139945916cd2Sjpk port >= tme->mlpe_mlp.mlp_port && 140045916cd2Sjpk port <= tme->mlpe_mlp.mlp_port_upper) 140145916cd2Sjpk break; 140245916cd2Sjpk } 140345916cd2Sjpk zoneid = tme == NULL ? ALL_ZONES : tme->mlpe_zoneid; 140445916cd2Sjpk rw_exit(&shared_mlps.mlpl_rwlock); 140545916cd2Sjpk return (zoneid); 140645916cd2Sjpk } 140745916cd2Sjpk 140845916cd2Sjpk /* Debug routine */ 140945916cd2Sjpk void 141045916cd2Sjpk tsol_print_label(const blevel_t *blev, const char *name) 141145916cd2Sjpk { 141245916cd2Sjpk const _blevel_impl_t *bli = (const _blevel_impl_t *)blev; 141345916cd2Sjpk 141445916cd2Sjpk /* We really support only sensitivity labels */ 141545916cd2Sjpk cmn_err(CE_NOTE, "%s %x:%x:%08x%08x%08x%08x%08x%08x%08x%08x", 141645916cd2Sjpk name, bli->id, LCLASS(bli), ntohl(bli->_comps.c1), 141745916cd2Sjpk ntohl(bli->_comps.c2), ntohl(bli->_comps.c3), ntohl(bli->_comps.c4), 141845916cd2Sjpk ntohl(bli->_comps.c5), ntohl(bli->_comps.c6), ntohl(bli->_comps.c7), 141945916cd2Sjpk ntohl(bli->_comps.c8)); 142045916cd2Sjpk } 142145916cd2Sjpk 142245916cd2Sjpk /* 142345916cd2Sjpk * Name: labelsys() 142445916cd2Sjpk * 142545916cd2Sjpk * Normal: Routes TSOL syscalls. 142645916cd2Sjpk * 142745916cd2Sjpk * Output: As defined for each TSOL syscall. 142845916cd2Sjpk * Returns ENOSYS for unrecognized calls. 142945916cd2Sjpk */ 143045916cd2Sjpk /* ARGSUSED */ 143145916cd2Sjpk int 143245916cd2Sjpk labelsys(int op, void *a1, void *a2, void *a3, void *a4, void *a5) 143345916cd2Sjpk { 143445916cd2Sjpk switch (op) { 143545916cd2Sjpk case TSOL_SYSLABELING: 143645916cd2Sjpk return (sys_labeling); 143745916cd2Sjpk case TSOL_TNRH: 143845916cd2Sjpk return (tnrh((int)(uintptr_t)a1, a2)); 143945916cd2Sjpk case TSOL_TNRHTP: 144045916cd2Sjpk return (tnrhtp((int)(uintptr_t)a1, a2)); 144145916cd2Sjpk case TSOL_TNMLP: 144245916cd2Sjpk return (tnmlp((int)(uintptr_t)a1, a2)); 144345916cd2Sjpk case TSOL_GETLABEL: 144445916cd2Sjpk return (getlabel((char *)a1, (bslabel_t *)a2)); 144545916cd2Sjpk case TSOL_FGETLABEL: 144645916cd2Sjpk return (fgetlabel((int)(uintptr_t)a1, (bslabel_t *)a2)); 144745916cd2Sjpk default: 144845916cd2Sjpk return (set_errno(ENOSYS)); 144945916cd2Sjpk } 145045916cd2Sjpk /* NOTREACHED */ 145145916cd2Sjpk } 1452