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
tsol_plen_to_mask(uint_t masklen)7945916cd2Sjpk tsol_plen_to_mask(uint_t masklen)
8045916cd2Sjpk {
8145916cd2Sjpk return (masklen == 0 ? 0 : htonl(IP_HOST_MASK << (IP_ABITS - masklen)));
8245916cd2Sjpk }
8345916cd2Sjpk
8445916cd2Sjpk /*
8545916cd2Sjpk * Convert a prefix length to the mask for that prefix.
8645916cd2Sjpk * Returns the argument bitmask.
8745916cd2Sjpk */
8845916cd2Sjpk static void
tsol_plen_to_mask_v6(uint_t plen,in6_addr_t * bitmask)8945916cd2Sjpk tsol_plen_to_mask_v6(uint_t plen, in6_addr_t *bitmask)
9045916cd2Sjpk {
9145916cd2Sjpk uint32_t *ptr;
9245916cd2Sjpk
9345916cd2Sjpk ASSERT(plen <= IPV6_ABITS);
9445916cd2Sjpk
9545916cd2Sjpk ptr = (uint32_t *)bitmask;
9645916cd2Sjpk while (plen >= 32) {
9745916cd2Sjpk *ptr++ = 0xffffffffU;
9845916cd2Sjpk plen -= 32;
9945916cd2Sjpk }
10045916cd2Sjpk if (plen > 0)
10145916cd2Sjpk *ptr++ = htonl(0xffffffff << (32 - plen));
10245916cd2Sjpk while (ptr < (uint32_t *)(bitmask + 1))
10345916cd2Sjpk *ptr++ = 0;
10445916cd2Sjpk }
10545916cd2Sjpk
10645916cd2Sjpk boolean_t
tnrhc_init_table(tnrhc_hash_t * table[],short prefix_len,int kmflag)10745916cd2Sjpk tnrhc_init_table(tnrhc_hash_t *table[], short prefix_len, int kmflag)
10845916cd2Sjpk {
10945916cd2Sjpk int i;
11045916cd2Sjpk
11145916cd2Sjpk mutex_enter(&tnrhc_g_lock);
11245916cd2Sjpk
11345916cd2Sjpk if (table[prefix_len] == NULL) {
11445916cd2Sjpk table[prefix_len] = (tnrhc_hash_t *)
11545916cd2Sjpk kmem_zalloc(TNRHC_SIZE * sizeof (tnrhc_hash_t), kmflag);
11645916cd2Sjpk if (table[prefix_len] == NULL) {
11745916cd2Sjpk mutex_exit(&tnrhc_g_lock);
11845916cd2Sjpk return (B_FALSE);
11945916cd2Sjpk }
12045916cd2Sjpk for (i = 0; i < TNRHC_SIZE; i++) {
12145916cd2Sjpk mutex_init(&table[prefix_len][i].tnrh_lock,
12245916cd2Sjpk NULL, MUTEX_DEFAULT, 0);
12345916cd2Sjpk }
12445916cd2Sjpk }
12545916cd2Sjpk mutex_exit(&tnrhc_g_lock);
12645916cd2Sjpk return (B_TRUE);
12745916cd2Sjpk }
12845916cd2Sjpk
12945916cd2Sjpk void
tcache_init(void)13045916cd2Sjpk tcache_init(void)
13145916cd2Sjpk {
13245916cd2Sjpk tnaddr_t address;
13345916cd2Sjpk
13445916cd2Sjpk /*
13545916cd2Sjpk * Note: unable to use mod_hash_create_strhash here, since it's
13645916cd2Sjpk * assymetric. It assumes that the user has allocated exactly
13745916cd2Sjpk * strlen(key) + 1 bytes for the key when inserted, and attempts to
13845916cd2Sjpk * kmem_free that memory on a delete.
13945916cd2Sjpk */
14045916cd2Sjpk tpc_name_hash = mod_hash_create_extended("tnrhtpc_by_name", 256,
14145916cd2Sjpk mod_hash_null_keydtor, mod_hash_null_valdtor, mod_hash_bystr,
14245916cd2Sjpk NULL, mod_hash_strkey_cmp, KM_SLEEP);
14345916cd2Sjpk mutex_init(&tpc_lock, NULL, MUTEX_DEFAULT, NULL);
14445916cd2Sjpk
14545916cd2Sjpk mutex_init(&tnrhc_g_lock, NULL, MUTEX_DEFAULT, NULL);
14645916cd2Sjpk
14745916cd2Sjpk /* label_init always called before tcache_init */
14845916cd2Sjpk ASSERT(l_admin_low != NULL && l_admin_high != NULL);
14945916cd2Sjpk
15045916cd2Sjpk /* Initialize the zeroth table prior to loading the 0.0.0.0 entry */
15145916cd2Sjpk (void) tnrhc_init_table(tnrhc_table, 0, KM_SLEEP);
15245916cd2Sjpk (void) tnrhc_init_table(tnrhc_table_v6, 0, KM_SLEEP);
15345916cd2Sjpk /*
15445916cd2Sjpk * create an internal host template called "_unlab"
15545916cd2Sjpk */
15645916cd2Sjpk tsol_create_i_tmpls();
15745916cd2Sjpk
15845916cd2Sjpk /*
15945916cd2Sjpk * create a host entry, 0.0.0.0 = _unlab
16045916cd2Sjpk */
16145916cd2Sjpk bzero(&address, sizeof (tnaddr_t));
16245916cd2Sjpk address.ta_family = AF_INET;
16345916cd2Sjpk tsol_create_i_tnrh(&address);
16445916cd2Sjpk
16545916cd2Sjpk /*
16645916cd2Sjpk * create a host entry, ::0 = _unlab
16745916cd2Sjpk */
16845916cd2Sjpk address.ta_family = AF_INET6;
16945916cd2Sjpk tsol_create_i_tnrh(&address);
17045916cd2Sjpk
17145916cd2Sjpk rw_init(&shared_mlps.mlpl_rwlock, NULL, RW_DEFAULT, NULL);
17245916cd2Sjpk }
17345916cd2Sjpk
17445916cd2Sjpk /* Called only by the TNRHC_RELE macro when the refcount goes to zero. */
17545916cd2Sjpk void
tnrhc_free(tsol_tnrhc_t * tnrhc)17645916cd2Sjpk tnrhc_free(tsol_tnrhc_t *tnrhc)
17745916cd2Sjpk {
17845916cd2Sjpk /*
17945916cd2Sjpk * We assert rhc_invalid here to make sure that no new thread could
18045916cd2Sjpk * possibly end up finding this entry. If it could, then the
18145916cd2Sjpk * mutex_destroy would panic.
18245916cd2Sjpk */
18345916cd2Sjpk DTRACE_PROBE1(tx__tndb__l3__tnrhcfree, tsol_tnrhc_t *, tnrhc);
18445916cd2Sjpk ASSERT(tnrhc->rhc_next == NULL && tnrhc->rhc_invalid);
18545916cd2Sjpk mutex_exit(&tnrhc->rhc_lock);
18645916cd2Sjpk mutex_destroy(&tnrhc->rhc_lock);
18745916cd2Sjpk if (tnrhc->rhc_tpc != NULL)
18845916cd2Sjpk TPC_RELE(tnrhc->rhc_tpc);
18945916cd2Sjpk kmem_free(tnrhc, sizeof (*tnrhc));
19045916cd2Sjpk }
19145916cd2Sjpk
19245916cd2Sjpk /* Called only by the TPC_RELE macro when the refcount goes to zero. */
19345916cd2Sjpk void
tpc_free(tsol_tpc_t * tpc)19445916cd2Sjpk tpc_free(tsol_tpc_t *tpc)
19545916cd2Sjpk {
19645916cd2Sjpk DTRACE_PROBE1(tx__tndb__l3__tpcfree, tsol_tpc_t *, tpc);
19745916cd2Sjpk ASSERT(tpc->tpc_invalid);
19845916cd2Sjpk mutex_exit(&tpc->tpc_lock);
19945916cd2Sjpk mutex_destroy(&tpc->tpc_lock);
20045916cd2Sjpk kmem_free(tpc, sizeof (*tpc));
20145916cd2Sjpk }
20245916cd2Sjpk
20345916cd2Sjpk /*
20445916cd2Sjpk * Find and hold a reference to a template entry by name. Ignores entries that
20545916cd2Sjpk * are being deleted.
20645916cd2Sjpk */
20745916cd2Sjpk static tsol_tpc_t *
tnrhtp_find(const char * name,mod_hash_t * hash)20845916cd2Sjpk tnrhtp_find(const char *name, mod_hash_t *hash)
20945916cd2Sjpk {
21045916cd2Sjpk mod_hash_val_t hv;
21145916cd2Sjpk tsol_tpc_t *tpc = NULL;
21245916cd2Sjpk
21345916cd2Sjpk mutex_enter(&tpc_lock);
21445916cd2Sjpk if (mod_hash_find(hash, (mod_hash_key_t)name, &hv) == 0) {
21545916cd2Sjpk tpc = (tsol_tpc_t *)hv;
21645916cd2Sjpk if (tpc->tpc_invalid)
21745916cd2Sjpk tpc = NULL;
21845916cd2Sjpk else
21945916cd2Sjpk TPC_HOLD(tpc);
22045916cd2Sjpk }
22145916cd2Sjpk mutex_exit(&tpc_lock);
22245916cd2Sjpk return (tpc);
22345916cd2Sjpk }
22445916cd2Sjpk
22545916cd2Sjpk static int
tnrh_delete(const tsol_rhent_t * rhent)22645916cd2Sjpk tnrh_delete(const tsol_rhent_t *rhent)
22745916cd2Sjpk {
22845916cd2Sjpk tsol_tnrhc_t *current;
22945916cd2Sjpk tsol_tnrhc_t **prevp;
23045916cd2Sjpk ipaddr_t tmpmask;
23145916cd2Sjpk in6_addr_t tmpmask_v6;
23245916cd2Sjpk tnrhc_hash_t *tnrhc_hash;
23345916cd2Sjpk
23445916cd2Sjpk if (rhent->rh_address.ta_family == AF_INET) {
23545916cd2Sjpk if (rhent->rh_prefix < 0 || rhent->rh_prefix > IP_ABITS)
23645916cd2Sjpk return (EINVAL);
23745916cd2Sjpk if (tnrhc_table[rhent->rh_prefix] == NULL)
23845916cd2Sjpk return (ENOENT);
23945916cd2Sjpk tmpmask = tsol_plen_to_mask(rhent->rh_prefix);
24045916cd2Sjpk tnrhc_hash = &tnrhc_table[rhent->rh_prefix][
24145916cd2Sjpk TSOL_ADDR_HASH(rhent->rh_address.ta_addr_v4.s_addr &
24245916cd2Sjpk tmpmask, TNRHC_SIZE)];
24345916cd2Sjpk } else if (rhent->rh_address.ta_family == AF_INET6) {
24445916cd2Sjpk if (rhent->rh_prefix < 0 || rhent->rh_prefix > IPV6_ABITS)
24545916cd2Sjpk return (EINVAL);
24645916cd2Sjpk if (tnrhc_table_v6[rhent->rh_prefix] == NULL)
24745916cd2Sjpk return (ENOENT);
24845916cd2Sjpk tsol_plen_to_mask_v6(rhent->rh_prefix, &tmpmask_v6);
24945916cd2Sjpk tnrhc_hash = &tnrhc_table_v6[rhent->rh_prefix][
25045916cd2Sjpk TSOL_ADDR_MASK_HASH_V6(rhent->rh_address.ta_addr_v6,
25145916cd2Sjpk tmpmask_v6, TNRHC_SIZE)];
25245916cd2Sjpk } else {
25345916cd2Sjpk return (EAFNOSUPPORT);
25445916cd2Sjpk }
25545916cd2Sjpk
25645916cd2Sjpk /* search for existing entry */
25745916cd2Sjpk mutex_enter(&tnrhc_hash->tnrh_lock);
25845916cd2Sjpk prevp = &tnrhc_hash->tnrh_list;
25945916cd2Sjpk while ((current = *prevp) != NULL) {
26045916cd2Sjpk if (TNADDR_EQ(&rhent->rh_address, ¤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
flush_rh_table(tnrhc_hash_t ** htable,int nbits)28545916cd2Sjpk flush_rh_table(tnrhc_hash_t **htable, int nbits)
28645916cd2Sjpk {
28745916cd2Sjpk tnrhc_hash_t *hent, *hend;
28845916cd2Sjpk tsol_tnrhc_t *rhc, *rhnext;
28945916cd2Sjpk
29045916cd2Sjpk while (--nbits >= 0) {
29145916cd2Sjpk if ((hent = htable[nbits]) == NULL)
29245916cd2Sjpk continue;
29345916cd2Sjpk hend = hent + TNRHC_SIZE;
29445916cd2Sjpk while (hent < hend) {
29545916cd2Sjpk /*
29645916cd2Sjpk * List walkers hold this lock during the walk. It
29745916cd2Sjpk * protects tnrh_list and rhc_next.
29845916cd2Sjpk */
29945916cd2Sjpk mutex_enter(&hent->tnrh_lock);
30045916cd2Sjpk rhnext = hent->tnrh_list;
30145916cd2Sjpk hent->tnrh_list = NULL;
30245916cd2Sjpk mutex_exit(&hent->tnrh_lock);
30345916cd2Sjpk /*
30445916cd2Sjpk * There may still be users of the rhcs at this point,
30545916cd2Sjpk * but not of the list or its next pointer. Thus, the
30645916cd2Sjpk * only thing that would need to be done under a lock
30745916cd2Sjpk * is setting the invalid bit, but that's atomic
30845916cd2Sjpk * anyway, so no locks needed here.
30945916cd2Sjpk */
31045916cd2Sjpk while ((rhc = rhnext) != NULL) {
31145916cd2Sjpk rhnext = rhc->rhc_next;
31245916cd2Sjpk rhc->rhc_next = NULL;
31345916cd2Sjpk rhc->rhc_invalid = 1;
31445916cd2Sjpk TNRHC_RELE(rhc);
31545916cd2Sjpk }
31645916cd2Sjpk hent++;
31745916cd2Sjpk }
31845916cd2Sjpk }
31945916cd2Sjpk }
32045916cd2Sjpk
32145916cd2Sjpk /*
32245916cd2Sjpk * Load a remote host entry into kernel cache. Create a new one if a matching
32345916cd2Sjpk * entry isn't found, otherwise replace the contents of the previous one by
32445916cd2Sjpk * deleting it and recreating it. (Delete and recreate is used to avoid
32545916cd2Sjpk * allowing other threads to see an unstable data structure.)
32645916cd2Sjpk *
32745916cd2Sjpk * A "matching" entry is the one whose address matches that of the one
32845916cd2Sjpk * being loaded.
32945916cd2Sjpk *
33045916cd2Sjpk * Return 0 for success, error code for failure.
33145916cd2Sjpk */
332*bfabfc35Skp158701 static int
tnrh_hash_add(tsol_tnrhc_t * new,short prefix)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
tnrh_load(const tsol_rhent_t * rhent)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
tnrh_get(tsol_rhent_t * rhent)45045916cd2Sjpk tnrh_get(tsol_rhent_t *rhent)
45145916cd2Sjpk {
45245916cd2Sjpk tsol_tpc_t *tpc;
45345916cd2Sjpk
45445916cd2Sjpk switch (rhent->rh_address.ta_family) {
45545916cd2Sjpk case AF_INET:
45645916cd2Sjpk tpc = find_tpc(&rhent->rh_address.ta_addr_v4, IPV4_VERSION,
45745916cd2Sjpk B_TRUE);
45845916cd2Sjpk break;
45945916cd2Sjpk
46045916cd2Sjpk case AF_INET6:
46145916cd2Sjpk tpc = find_tpc(&rhent->rh_address.ta_addr_v6, IPV6_VERSION,
46245916cd2Sjpk B_TRUE);
46345916cd2Sjpk break;
46445916cd2Sjpk
46545916cd2Sjpk default:
46645916cd2Sjpk return (EINVAL);
46745916cd2Sjpk }
46845916cd2Sjpk if (tpc == NULL)
46945916cd2Sjpk return (ENOENT);
47045916cd2Sjpk
47145916cd2Sjpk DTRACE_PROBE2(tx__tndb__l4__tnrhget__foundtpc, tsol_rhent_t *,
47245916cd2Sjpk rhent, tsol_tpc_t *, tpc);
47345916cd2Sjpk bcopy(tpc->tpc_tp.name, rhent->rh_template,
47445916cd2Sjpk sizeof (rhent->rh_template));
47545916cd2Sjpk TPC_RELE(tpc);
47645916cd2Sjpk return (0);
47745916cd2Sjpk }
47845916cd2Sjpk
47945916cd2Sjpk static boolean_t
template_name_ok(const char * name)48045916cd2Sjpk template_name_ok(const char *name)
48145916cd2Sjpk {
48245916cd2Sjpk const char *name_end = name + TNTNAMSIZ;
48345916cd2Sjpk
48445916cd2Sjpk while (name < name_end) {
48545916cd2Sjpk if (*name == '\0')
48645916cd2Sjpk break;
48745916cd2Sjpk name++;
48845916cd2Sjpk }
48945916cd2Sjpk return (name < name_end);
49045916cd2Sjpk }
49145916cd2Sjpk
49245916cd2Sjpk static int
tnrh(int cmd,void * buf)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 *
tnrhtp_create(const tsol_tpent_t * tpent,int kmflags)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
tnrhtp_delete(const char * tname)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
tpc_delete(mod_hash_key_t key,mod_hash_val_t * val,void * arg)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
tnrhtp_flush(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
tnrhtp(int cmd,void * buf)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
mlp_add_del(tsol_mlp_list_t * mlpl,zoneid_t zoneid,uint8_t proto,uint16_t port,uint16_t portmax,boolean_t addflag)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
tsol_mlp_anon(zone_t * zone,mlp_type_t mlptype,uchar_t proto,uint16_t port,boolean_t addflag)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
mlp_flush(tsol_mlp_list_t * mlpl,zoneid_t zoneid)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
tnmlp(int cmd,void * buf)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 *
find_rhc(const void * addr,uchar_t version,boolean_t staleok)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 *
find_tpc(const void * addr,uchar_t version,boolean_t staleok)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
tsol_create_i_tmpls(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
tsol_create_i_tnrh(const tnaddr_t * sa)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
tsol_next_port(zone_t * zone,in_port_t port,int proto,boolean_t upward)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
tsol_mlp_port_type(zone_t * zone,uchar_t proto,uint16_t port,mlp_type_t mlptype)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
tsol_mlp_findzone(uchar_t proto,uint16_t lport)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
tsol_print_label(const blevel_t * blev,const char * name)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
labelsys(int op,void * a1,void * a2,void * a3,void * a4,void * a5)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