Merge branch 'less_closed'
[unleashed.git] / kernel / os / labelsys.c
blobffccc8b70937bc97615709e2bf818de56810e09f
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #pragma ident "%Z%%M% %I% %E% SMI"
28 #include <sys/systm.h>
29 #include <sys/types.h>
30 #include <sys/stream.h>
31 #include <sys/kmem.h>
32 #include <sys/strsubr.h>
33 #include <sys/cmn_err.h>
34 #include <sys/debug.h>
35 #include <sys/param.h>
36 #include <sys/model.h>
37 #include <sys/errno.h>
38 #include <sys/modhash.h>
40 #include <sys/policy.h>
41 #include <sys/tsol/label.h>
42 #include <sys/tsol/tsyscall.h>
43 #include <sys/tsol/tndb.h>
44 #include <sys/tsol/tnet.h>
45 #include <sys/disp.h>
47 #include <inet/ip.h>
48 #include <inet/ip6.h>
49 #include <sys/sdt.h>
51 static mod_hash_t *tpc_name_hash; /* hash of cache entries by name */
52 static kmutex_t tpc_lock;
54 static tsol_tpc_t *tpc_unlab;
57 * tnrhc_table and tnrhc_table_v6 are similar to the IP forwarding tables
58 * in organization and search. The tnrhc_table[_v6] is an array of 33/129
59 * pointers to the 33/129 tnrhc tables indexed by the prefix length.
60 * A largest prefix match search is done by find_rhc and it walks the
61 * tables from the most specific to the least specific table. Table 0
62 * corresponds to the single entry for 0.0.0.0/0 or ::0/0.
64 tnrhc_hash_t *tnrhc_table[TSOL_MASK_TABLE_SIZE];
65 tnrhc_hash_t *tnrhc_table_v6[TSOL_MASK_TABLE_SIZE_V6];
66 kmutex_t tnrhc_g_lock;
68 static void tsol_create_i_tmpls(void);
70 static void tsol_create_i_tnrh(const tnaddr_t *);
72 /* List of MLPs on valid on shared addresses */
73 static tsol_mlp_list_t shared_mlps;
76 * Convert length for a mask to the mask.
78 static ipaddr_t
79 tsol_plen_to_mask(uint_t masklen)
81 return (masklen == 0 ? 0 : htonl(IP_HOST_MASK << (IP_ABITS - masklen)));
85 * Convert a prefix length to the mask for that prefix.
86 * Returns the argument bitmask.
88 static void
89 tsol_plen_to_mask_v6(uint_t plen, in6_addr_t *bitmask)
91 uint32_t *ptr;
93 ASSERT(plen <= IPV6_ABITS);
95 ptr = (uint32_t *)bitmask;
96 while (plen >= 32) {
97 *ptr++ = 0xffffffffU;
98 plen -= 32;
100 if (plen > 0)
101 *ptr++ = htonl(0xffffffff << (32 - plen));
102 while (ptr < (uint32_t *)(bitmask + 1))
103 *ptr++ = 0;
106 boolean_t
107 tnrhc_init_table(tnrhc_hash_t *table[], short prefix_len, int kmflag)
109 int i;
111 mutex_enter(&tnrhc_g_lock);
113 if (table[prefix_len] == NULL) {
114 table[prefix_len] = (tnrhc_hash_t *)
115 kmem_zalloc(TNRHC_SIZE * sizeof (tnrhc_hash_t), kmflag);
116 if (table[prefix_len] == NULL) {
117 mutex_exit(&tnrhc_g_lock);
118 return (B_FALSE);
120 for (i = 0; i < TNRHC_SIZE; i++) {
121 mutex_init(&table[prefix_len][i].tnrh_lock,
122 NULL, MUTEX_DEFAULT, 0);
125 mutex_exit(&tnrhc_g_lock);
126 return (B_TRUE);
129 void
130 tcache_init(void)
132 tnaddr_t address;
135 * Note: unable to use mod_hash_create_strhash here, since it's
136 * assymetric. It assumes that the user has allocated exactly
137 * strlen(key) + 1 bytes for the key when inserted, and attempts to
138 * kmem_free that memory on a delete.
140 tpc_name_hash = mod_hash_create_extended("tnrhtpc_by_name", 256,
141 mod_hash_null_keydtor, mod_hash_null_valdtor, mod_hash_bystr,
142 NULL, mod_hash_strkey_cmp, KM_SLEEP);
143 mutex_init(&tpc_lock, NULL, MUTEX_DEFAULT, NULL);
145 mutex_init(&tnrhc_g_lock, NULL, MUTEX_DEFAULT, NULL);
147 /* label_init always called before tcache_init */
148 ASSERT(l_admin_low != NULL && l_admin_high != NULL);
150 /* Initialize the zeroth table prior to loading the 0.0.0.0 entry */
151 (void) tnrhc_init_table(tnrhc_table, 0, KM_SLEEP);
152 (void) tnrhc_init_table(tnrhc_table_v6, 0, KM_SLEEP);
154 * create an internal host template called "_unlab"
156 tsol_create_i_tmpls();
159 * create a host entry, 0.0.0.0 = _unlab
161 bzero(&address, sizeof (tnaddr_t));
162 address.ta_family = AF_INET;
163 tsol_create_i_tnrh(&address);
166 * create a host entry, ::0 = _unlab
168 address.ta_family = AF_INET6;
169 tsol_create_i_tnrh(&address);
171 rw_init(&shared_mlps.mlpl_rwlock, NULL, RW_DEFAULT, NULL);
174 /* Called only by the TNRHC_RELE macro when the refcount goes to zero. */
175 void
176 tnrhc_free(tsol_tnrhc_t *tnrhc)
179 * We assert rhc_invalid here to make sure that no new thread could
180 * possibly end up finding this entry. If it could, then the
181 * mutex_destroy would panic.
183 DTRACE_PROBE1(tx__tndb__l3__tnrhcfree, tsol_tnrhc_t *, tnrhc);
184 ASSERT(tnrhc->rhc_next == NULL && tnrhc->rhc_invalid);
185 mutex_exit(&tnrhc->rhc_lock);
186 mutex_destroy(&tnrhc->rhc_lock);
187 if (tnrhc->rhc_tpc != NULL)
188 TPC_RELE(tnrhc->rhc_tpc);
189 kmem_free(tnrhc, sizeof (*tnrhc));
192 /* Called only by the TPC_RELE macro when the refcount goes to zero. */
193 void
194 tpc_free(tsol_tpc_t *tpc)
196 DTRACE_PROBE1(tx__tndb__l3__tpcfree, tsol_tpc_t *, tpc);
197 ASSERT(tpc->tpc_invalid);
198 mutex_exit(&tpc->tpc_lock);
199 mutex_destroy(&tpc->tpc_lock);
200 kmem_free(tpc, sizeof (*tpc));
204 * Find and hold a reference to a template entry by name. Ignores entries that
205 * are being deleted.
207 static tsol_tpc_t *
208 tnrhtp_find(const char *name, mod_hash_t *hash)
210 mod_hash_val_t hv;
211 tsol_tpc_t *tpc = NULL;
213 mutex_enter(&tpc_lock);
214 if (mod_hash_find(hash, (mod_hash_key_t)name, &hv) == 0) {
215 tpc = (tsol_tpc_t *)hv;
216 if (tpc->tpc_invalid)
217 tpc = NULL;
218 else
219 TPC_HOLD(tpc);
221 mutex_exit(&tpc_lock);
222 return (tpc);
225 static int
226 tnrh_delete(const tsol_rhent_t *rhent)
228 tsol_tnrhc_t *current;
229 tsol_tnrhc_t **prevp;
230 ipaddr_t tmpmask;
231 in6_addr_t tmpmask_v6;
232 tnrhc_hash_t *tnrhc_hash;
234 if (rhent->rh_address.ta_family == AF_INET) {
235 if (rhent->rh_prefix < 0 || rhent->rh_prefix > IP_ABITS)
236 return (EINVAL);
237 if (tnrhc_table[rhent->rh_prefix] == NULL)
238 return (ENOENT);
239 tmpmask = tsol_plen_to_mask(rhent->rh_prefix);
240 tnrhc_hash = &tnrhc_table[rhent->rh_prefix][
241 TSOL_ADDR_HASH(rhent->rh_address.ta_addr_v4.s_addr &
242 tmpmask, TNRHC_SIZE)];
243 } else if (rhent->rh_address.ta_family == AF_INET6) {
244 if (rhent->rh_prefix < 0 || rhent->rh_prefix > IPV6_ABITS)
245 return (EINVAL);
246 if (tnrhc_table_v6[rhent->rh_prefix] == NULL)
247 return (ENOENT);
248 tsol_plen_to_mask_v6(rhent->rh_prefix, &tmpmask_v6);
249 tnrhc_hash = &tnrhc_table_v6[rhent->rh_prefix][
250 TSOL_ADDR_MASK_HASH_V6(rhent->rh_address.ta_addr_v6,
251 tmpmask_v6, TNRHC_SIZE)];
252 } else {
253 return (EAFNOSUPPORT);
256 /* search for existing entry */
257 mutex_enter(&tnrhc_hash->tnrh_lock);
258 prevp = &tnrhc_hash->tnrh_list;
259 while ((current = *prevp) != NULL) {
260 if (TNADDR_EQ(&rhent->rh_address, &current->rhc_host))
261 break;
262 prevp = &current->rhc_next;
265 if (current != NULL) {
266 DTRACE_PROBE(tx__tndb__l2__tnrhdelete_existingrhentry);
267 *prevp = current->rhc_next;
268 mutex_enter(&current->rhc_lock);
269 current->rhc_next = NULL;
270 current->rhc_invalid = 1;
271 mutex_exit(&current->rhc_lock);
272 TNRHC_RELE(current);
274 mutex_exit(&tnrhc_hash->tnrh_lock);
275 return (current == NULL ? ENOENT : 0);
279 * Flush all remote host entries from the database.
281 * Note that the htable arrays themselves do not have reference counters, so,
282 * unlike the remote host entries, they cannot be freed.
284 static void
285 flush_rh_table(tnrhc_hash_t **htable, int nbits)
287 tnrhc_hash_t *hent, *hend;
288 tsol_tnrhc_t *rhc, *rhnext;
290 while (--nbits >= 0) {
291 if ((hent = htable[nbits]) == NULL)
292 continue;
293 hend = hent + TNRHC_SIZE;
294 while (hent < hend) {
296 * List walkers hold this lock during the walk. It
297 * protects tnrh_list and rhc_next.
299 mutex_enter(&hent->tnrh_lock);
300 rhnext = hent->tnrh_list;
301 hent->tnrh_list = NULL;
302 mutex_exit(&hent->tnrh_lock);
304 * There may still be users of the rhcs at this point,
305 * but not of the list or its next pointer. Thus, the
306 * only thing that would need to be done under a lock
307 * is setting the invalid bit, but that's atomic
308 * anyway, so no locks needed here.
310 while ((rhc = rhnext) != NULL) {
311 rhnext = rhc->rhc_next;
312 rhc->rhc_next = NULL;
313 rhc->rhc_invalid = 1;
314 TNRHC_RELE(rhc);
316 hent++;
322 * Load a remote host entry into kernel cache. Create a new one if a matching
323 * entry isn't found, otherwise replace the contents of the previous one by
324 * deleting it and recreating it. (Delete and recreate is used to avoid
325 * allowing other threads to see an unstable data structure.)
327 * A "matching" entry is the one whose address matches that of the one
328 * being loaded.
330 * Return 0 for success, error code for failure.
332 static int
333 tnrh_hash_add(tsol_tnrhc_t *new, short prefix)
335 tsol_tnrhc_t **rhp;
336 tsol_tnrhc_t *rh;
337 ipaddr_t tmpmask;
338 in6_addr_t tmpmask_v6;
339 tnrhc_hash_t *tnrhc_hash;
341 /* Find the existing entry, if any, leaving the hash locked */
342 if (new->rhc_host.ta_family == AF_INET) {
343 if (prefix < 0 || prefix > IP_ABITS)
344 return (EINVAL);
345 if (tnrhc_table[prefix] == NULL &&
346 !tnrhc_init_table(tnrhc_table, prefix,
347 KM_NOSLEEP))
348 return (ENOMEM);
349 tmpmask = tsol_plen_to_mask(prefix);
350 tnrhc_hash = &tnrhc_table[prefix][
351 TSOL_ADDR_HASH(new->rhc_host.ta_addr_v4.s_addr &
352 tmpmask, TNRHC_SIZE)];
353 mutex_enter(&tnrhc_hash->tnrh_lock);
354 for (rhp = &tnrhc_hash->tnrh_list; (rh = *rhp) != NULL;
355 rhp = &rh->rhc_next) {
356 ASSERT(rh->rhc_host.ta_family == AF_INET);
357 if (((rh->rhc_host.ta_addr_v4.s_addr ^
358 new->rhc_host.ta_addr_v4.s_addr) & tmpmask) ==
360 break;
362 } else if (new->rhc_host.ta_family == AF_INET6) {
363 if (prefix < 0 || prefix > IPV6_ABITS)
364 return (EINVAL);
365 if (tnrhc_table_v6[prefix] == NULL &&
366 !tnrhc_init_table(tnrhc_table_v6, prefix,
367 KM_NOSLEEP))
368 return (ENOMEM);
369 tsol_plen_to_mask_v6(prefix, &tmpmask_v6);
370 tnrhc_hash = &tnrhc_table_v6[prefix][
371 TSOL_ADDR_MASK_HASH_V6(new->rhc_host.ta_addr_v6,
372 tmpmask_v6, TNRHC_SIZE)];
373 mutex_enter(&tnrhc_hash->tnrh_lock);
374 for (rhp = &tnrhc_hash->tnrh_list; (rh = *rhp) != NULL;
375 rhp = &rh->rhc_next) {
376 ASSERT(rh->rhc_host.ta_family == AF_INET6);
377 if (V6_MASK_EQ_2(rh->rhc_host.ta_addr_v6, tmpmask_v6,
378 new->rhc_host.ta_addr_v6))
379 break;
381 } else {
382 return (EAFNOSUPPORT);
385 /* Clobber the old remote host entry. */
386 if (rh != NULL) {
387 ASSERT(!rh->rhc_invalid);
388 rh->rhc_invalid = 1;
389 *rhp = rh->rhc_next;
390 rh->rhc_next = NULL;
391 DTRACE_PROBE1(tx__tndb__l2__tnrhhashadd__invalidaterh,
392 tsol_tnrhc_t *, rh);
393 TNRHC_RELE(rh);
396 TNRHC_HOLD(new);
397 new->rhc_next = tnrhc_hash->tnrh_list;
398 tnrhc_hash->tnrh_list = new;
399 DTRACE_PROBE1(tx__tndb__l2__tnrhhashadd__addedrh, tsol_tnrhc_t *, new);
400 mutex_exit(&tnrhc_hash->tnrh_lock);
402 return (0);
406 * Load a remote host entry into kernel cache.
408 * Return 0 for success, error code for failure.
411 tnrh_load(const tsol_rhent_t *rhent)
413 tsol_tnrhc_t *new;
414 tsol_tpc_t *tpc;
415 int status;
417 /* Find and bump the reference count on the named template */
418 if ((tpc = tnrhtp_find(rhent->rh_template, tpc_name_hash)) == NULL) {
419 return (EINVAL);
421 ASSERT(tpc->tpc_tp.host_type == UNLABELED ||
422 tpc->tpc_tp.host_type == SUN_CIPSO);
424 if ((new = kmem_zalloc(sizeof (*new), KM_NOSLEEP)) == NULL) {
425 TPC_RELE(tpc);
426 return (ENOMEM);
429 /* Initialize the new entry. */
430 mutex_init(&new->rhc_lock, NULL, MUTEX_DEFAULT, NULL);
431 new->rhc_host = rhent->rh_address;
433 /* The rhc now owns this tpc reference, so no TPC_RELE past here */
434 new->rhc_tpc = tpc;
437 * tnrh_hash_add handles the tnrh entry ref count for hash
438 * table inclusion. The ref count is incremented and decremented
439 * here to trigger deletion of the new hash table entry in the
440 * event that tnrh_hash_add fails.
442 TNRHC_HOLD(new);
443 status = tnrh_hash_add(new, rhent->rh_prefix);
444 TNRHC_RELE(new);
446 return (status);
449 static int
450 tnrh_get(tsol_rhent_t *rhent)
452 tsol_tpc_t *tpc;
454 switch (rhent->rh_address.ta_family) {
455 case AF_INET:
456 tpc = find_tpc(&rhent->rh_address.ta_addr_v4, IPV4_VERSION,
457 B_TRUE);
458 break;
460 case AF_INET6:
461 tpc = find_tpc(&rhent->rh_address.ta_addr_v6, IPV6_VERSION,
462 B_TRUE);
463 break;
465 default:
466 return (EINVAL);
468 if (tpc == NULL)
469 return (ENOENT);
471 DTRACE_PROBE2(tx__tndb__l4__tnrhget__foundtpc, tsol_rhent_t *,
472 rhent, tsol_tpc_t *, tpc);
473 bcopy(tpc->tpc_tp.name, rhent->rh_template,
474 sizeof (rhent->rh_template));
475 TPC_RELE(tpc);
476 return (0);
479 static boolean_t
480 template_name_ok(const char *name)
482 const char *name_end = name + TNTNAMSIZ;
484 while (name < name_end) {
485 if (*name == '\0')
486 break;
487 name++;
489 return (name < name_end);
492 static int
493 tnrh(int cmd, void *buf)
495 int retv;
496 tsol_rhent_t rhent;
498 /* Make sure user has sufficient privilege */
499 if (cmd != TNDB_GET &&
500 (retv = secpolicy_net_config(CRED(), B_FALSE)) != 0)
501 return (set_errno(retv));
504 * Get arguments
506 if (cmd != TNDB_FLUSH &&
507 copyin(buf, &rhent, sizeof (rhent)) != 0) {
508 DTRACE_PROBE(tx__tndb__l0__tnrhdelete__copyin);
509 return (set_errno(EFAULT));
512 switch (cmd) {
513 case TNDB_LOAD:
514 DTRACE_PROBE(tx__tndb__l2__tnrhdelete__tndbload);
515 if (!template_name_ok(rhent.rh_template)) {
516 retv = EINVAL;
517 } else {
518 retv = tnrh_load(&rhent);
520 break;
522 case TNDB_DELETE:
523 DTRACE_PROBE(tx__tndb__l2__tnrhdelete__tndbdelete);
524 retv = tnrh_delete(&rhent);
525 break;
527 case TNDB_GET:
528 DTRACE_PROBE(tx__tndb__l4__tnrhdelete__tndbget);
529 if (!template_name_ok(rhent.rh_template)) {
530 retv = EINVAL;
531 break;
534 retv = tnrh_get(&rhent);
535 if (retv != 0)
536 break;
539 * Copy out result
541 if (copyout(&rhent, buf, sizeof (rhent)) != 0) {
542 DTRACE_PROBE(tx__tndb__l0__tnrhdelete__copyout);
543 retv = EFAULT;
545 break;
547 case TNDB_FLUSH:
548 DTRACE_PROBE(tx__tndb__l2__tnrhdelete__flush);
549 flush_rh_table(tnrhc_table, TSOL_MASK_TABLE_SIZE);
550 flush_rh_table(tnrhc_table_v6, TSOL_MASK_TABLE_SIZE_V6);
551 break;
553 default:
554 DTRACE_PROBE1(tx__tndb__l0__tnrhdelete__unknowncmd,
555 int, cmd);
556 retv = EOPNOTSUPP;
557 break;
560 if (retv != 0)
561 return (set_errno(retv));
562 else
563 return (retv);
566 static tsol_tpc_t *
567 tnrhtp_create(const tsol_tpent_t *tpent, int kmflags)
569 tsol_tpc_t *tpc;
570 mod_hash_val_t hv;
573 * We intentionally allocate a new entry before taking the lock on the
574 * entire database.
576 if ((tpc = kmem_zalloc(sizeof (*tpc), kmflags)) == NULL)
577 return (NULL);
579 mutex_enter(&tpc_lock);
580 if (mod_hash_find(tpc_name_hash, (mod_hash_key_t)tpent->name,
581 &hv) == 0) {
582 tsol_tpc_t *found_tpc = (tsol_tpc_t *)hv;
584 found_tpc->tpc_invalid = 1;
585 (void) mod_hash_destroy(tpc_name_hash,
586 (mod_hash_key_t)tpent->name);
587 TPC_RELE(found_tpc);
590 mutex_init(&tpc->tpc_lock, NULL, MUTEX_DEFAULT, NULL);
591 /* tsol_tpent_t is the same on LP64 and ILP32 */
592 bcopy(tpent, &tpc->tpc_tp, sizeof (tpc->tpc_tp));
593 (void) mod_hash_insert(tpc_name_hash, (mod_hash_key_t)tpc->tpc_tp.name,
594 (mod_hash_val_t)tpc);
595 TPC_HOLD(tpc);
596 mutex_exit(&tpc_lock);
598 return (tpc);
601 static int
602 tnrhtp_delete(const char *tname)
604 tsol_tpc_t *tpc;
605 mod_hash_val_t hv;
606 int retv = ENOENT;
608 mutex_enter(&tpc_lock);
609 if (mod_hash_find(tpc_name_hash, (mod_hash_key_t)tname, &hv) == 0) {
610 tpc = (tsol_tpc_t *)hv;
611 ASSERT(!tpc->tpc_invalid);
612 tpc->tpc_invalid = 1;
613 (void) mod_hash_destroy(tpc_name_hash,
614 (mod_hash_key_t)tpc->tpc_tp.name);
615 TPC_RELE(tpc);
616 retv = 0;
618 mutex_exit(&tpc_lock);
619 return (retv);
622 /* ARGSUSED */
623 static uint_t
624 tpc_delete(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
626 tsol_tpc_t *tpc = (tsol_tpc_t *)val;
628 ASSERT(!tpc->tpc_invalid);
629 tpc->tpc_invalid = 1;
630 TPC_RELE(tpc);
631 return (MH_WALK_CONTINUE);
634 static void
635 tnrhtp_flush(void)
637 mutex_enter(&tpc_lock);
638 mod_hash_walk(tpc_name_hash, tpc_delete, NULL);
639 mod_hash_clear(tpc_name_hash);
640 mutex_exit(&tpc_lock);
643 static int
644 tnrhtp(int cmd, void *buf)
646 int retv;
647 int type;
648 tsol_tpent_t rhtpent;
649 tsol_tpc_t *tpc;
651 /* Make sure user has sufficient privilege */
652 if (cmd != TNDB_GET &&
653 (retv = secpolicy_net_config(CRED(), B_FALSE)) != 0)
654 return (set_errno(retv));
657 * Get argument. Note that tsol_tpent_t is the same on LP64 and ILP32,
658 * so no special handling is required.
660 if (cmd != TNDB_FLUSH) {
661 if (copyin(buf, &rhtpent, sizeof (rhtpent)) != 0) {
662 DTRACE_PROBE(tx__tndb__l0__tnrhtp__copyin);
663 return (set_errno(EFAULT));
667 * Don't let the user give us a bogus (unterminated) template
668 * name.
670 if (!template_name_ok(rhtpent.name))
671 return (set_errno(EINVAL));
674 switch (cmd) {
675 case TNDB_LOAD:
676 DTRACE_PROBE1(tx__tndb__l2__tnrhtp__tndbload, char *,
677 rhtpent.name);
678 type = rhtpent.host_type;
679 if (type != UNLABELED && type != SUN_CIPSO) {
680 retv = EINVAL;
681 break;
684 if (tnrhtp_create(&rhtpent, KM_NOSLEEP) == NULL)
685 retv = ENOMEM;
686 else
687 retv = 0;
688 break;
690 case TNDB_GET:
691 DTRACE_PROBE1(tx__tndb__l4__tnrhtp__tndbget, char *,
692 rhtpent.name);
693 tpc = tnrhtp_find(rhtpent.name, tpc_name_hash);
694 if (tpc == NULL) {
695 retv = ENOENT;
696 break;
699 /* Copy out result */
700 if (copyout(&tpc->tpc_tp, buf, sizeof (tpc->tpc_tp)) != 0) {
701 DTRACE_PROBE(tx__tndb__l0__tnrhtp__copyout);
702 retv = EFAULT;
703 } else {
704 retv = 0;
706 TPC_RELE(tpc);
707 break;
709 case TNDB_DELETE:
710 DTRACE_PROBE1(tx__tndb__l4__tnrhtp__tndbdelete, char *,
711 rhtpent.name);
712 retv = tnrhtp_delete(rhtpent.name);
713 break;
715 case TNDB_FLUSH:
716 DTRACE_PROBE(tx__tndb__l4__tnrhtp__flush);
717 tnrhtp_flush();
718 retv = 0;
719 break;
721 default:
722 DTRACE_PROBE1(tx__tndb__l0__tnrhtp__unknowncmd, int,
723 cmd);
724 retv = EOPNOTSUPP;
725 break;
728 if (retv != 0)
729 return (set_errno(retv));
730 else
731 return (retv);
735 * MLP entry ordering logic
737 * There are two loops in this routine. The first loop finds the entry that
738 * either logically follows the new entry to be inserted, or is the entry that
739 * precedes and overlaps the new entry, or is NULL to mean end-of-list. This
740 * is 'tme.' The second loop scans ahead from that point to find any overlap
741 * on the front or back of this new entry.
743 * For the first loop, we can have the following cases in the list (note that
744 * the port-portmax range is inclusive):
746 * port portmax
747 * +--------+
748 * 1: +------+ ................... precedes; skip to next
749 * 2: +------+ ............. overlaps; stop here if same protocol
750 * 3: +------+ ......... overlaps; stop if same or higher protocol
751 * 4: +-------+ .... overlaps or succeeds; stop here
753 * For the second loop, we can have the following cases (note that we need not
754 * care about other protocol entries at this point, because we're only looking
755 * for overlap, not an insertion point):
757 * port portmax
758 * +--------+
759 * 5: +------+ ............. overlaps; stop if same protocol
760 * 6: +------+ ......... overlaps; stop if same protocol
761 * 7: +-------+ .... overlaps; stop if same protocol
762 * 8: +---+ . follows; search is done
764 * In other words, this second search needs to consider only whether the entry
765 * has a starting port number that's greater than the end point of the new
766 * entry. All others are overlaps.
768 static int
769 mlp_add_del(tsol_mlp_list_t *mlpl, zoneid_t zoneid, uint8_t proto,
770 uint16_t port, uint16_t portmax, boolean_t addflag)
772 int retv;
773 tsol_mlp_entry_t *tme, *tme2, *newent;
775 if (addflag) {
776 if ((newent = kmem_zalloc(sizeof (*newent), KM_NOSLEEP)) ==
777 NULL)
778 return (ENOMEM);
779 } else {
780 newent = NULL;
782 rw_enter(&mlpl->mlpl_rwlock, RW_WRITER);
785 * First loop: find logical insertion point or overlap. Table is kept
786 * in order of port number first, and then, within that, by protocol
787 * number.
789 for (tme = mlpl->mlpl_first; tme != NULL; tme = tme->mlpe_next) {
790 /* logically next (case 4) */
791 if (tme->mlpe_mlp.mlp_port > port)
792 break;
793 /* if this is logically next or overlap, then stop (case 3) */
794 if (tme->mlpe_mlp.mlp_port == port &&
795 tme->mlpe_mlp.mlp_ipp >= proto)
796 break;
797 /* earlier or same port sequence; check for overlap (case 2) */
798 if (tme->mlpe_mlp.mlp_ipp == proto &&
799 tme->mlpe_mlp.mlp_port_upper >= port)
800 break;
801 /* otherwise, loop again (case 1) */
804 /* Second loop: scan ahead for overlap */
805 for (tme2 = tme; tme2 != NULL; tme2 = tme2->mlpe_next) {
806 /* check if entry follows; no overlap (case 8) */
807 if (tme2->mlpe_mlp.mlp_port > portmax) {
808 tme2 = NULL;
809 break;
811 /* only exact protocol matches at this point (cases 5-7) */
812 if (tme2->mlpe_mlp.mlp_ipp == proto)
813 break;
816 retv = 0;
817 if (addflag) {
818 if (tme2 != NULL) {
819 retv = EEXIST;
820 } else {
821 newent->mlpe_zoneid = zoneid;
822 newent->mlpe_mlp.mlp_ipp = proto;
823 newent->mlpe_mlp.mlp_port = port;
824 newent->mlpe_mlp.mlp_port_upper = portmax;
825 newent->mlpe_next = tme;
826 if (tme == NULL) {
827 tme2 = mlpl->mlpl_last;
828 mlpl->mlpl_last = newent;
829 } else {
830 tme2 = tme->mlpe_prev;
831 tme->mlpe_prev = newent;
833 newent->mlpe_prev = tme2;
834 if (tme2 == NULL)
835 mlpl->mlpl_first = newent;
836 else
837 tme2->mlpe_next = newent;
838 newent = NULL;
840 } else {
841 if (tme2 == NULL || tme2->mlpe_mlp.mlp_port != port ||
842 tme2->mlpe_mlp.mlp_port_upper != portmax) {
843 retv = ENOENT;
844 } else {
845 if ((tme2 = tme->mlpe_prev) == NULL)
846 mlpl->mlpl_first = tme->mlpe_next;
847 else
848 tme2->mlpe_next = tme->mlpe_next;
849 if ((tme2 = tme->mlpe_next) == NULL)
850 mlpl->mlpl_last = tme->mlpe_prev;
851 else
852 tme2->mlpe_prev = tme->mlpe_prev;
853 newent = tme;
856 rw_exit(&mlpl->mlpl_rwlock);
858 if (newent != NULL)
859 kmem_free(newent, sizeof (*newent));
861 return (retv);
865 * Add or remove an MLP entry from the database so that the classifier can find
866 * it.
868 * Note: port number is in host byte order.
871 tsol_mlp_anon(zone_t *zone, mlp_type_t mlptype, uchar_t proto, uint16_t port,
872 boolean_t addflag)
874 int retv = 0;
876 if (mlptype == mlptBoth || mlptype == mlptPrivate)
877 retv = mlp_add_del(&zone->zone_mlps, zone->zone_id, proto,
878 port, port, addflag);
879 if ((retv == 0 || !addflag) &&
880 (mlptype == mlptBoth || mlptype == mlptShared)) {
881 retv = mlp_add_del(&shared_mlps, zone->zone_id, proto, port,
882 port, addflag);
883 if (retv != 0 && addflag)
884 (void) mlp_add_del(&zone->zone_mlps, zone->zone_id,
885 proto, port, port, B_FALSE);
887 return (retv);
890 static void
891 mlp_flush(tsol_mlp_list_t *mlpl, zoneid_t zoneid)
893 tsol_mlp_entry_t *tme, *tme2, *tmnext;
895 rw_enter(&mlpl->mlpl_rwlock, RW_WRITER);
896 for (tme = mlpl->mlpl_first; tme != NULL; tme = tmnext) {
897 tmnext = tme->mlpe_next;
898 if (zoneid == ALL_ZONES || tme->mlpe_zoneid == zoneid) {
899 if ((tme2 = tme->mlpe_prev) == NULL)
900 mlpl->mlpl_first = tmnext;
901 else
902 tme2->mlpe_next = tmnext;
903 if (tmnext == NULL)
904 mlpl->mlpl_last = tme2;
905 else
906 tmnext->mlpe_prev = tme2;
907 kmem_free(tme, sizeof (*tme));
910 rw_exit(&mlpl->mlpl_rwlock);
914 * Note: user supplies port numbers in host byte order.
916 static int
917 tnmlp(int cmd, void *buf)
919 int retv;
920 tsol_mlpent_t tsme;
921 zone_t *zone;
922 tsol_mlp_list_t *mlpl;
923 tsol_mlp_entry_t *tme;
925 /* Make sure user has sufficient privilege */
926 if (cmd != TNDB_GET &&
927 (retv = secpolicy_net_config(CRED(), B_FALSE)) != 0)
928 return (set_errno(retv));
931 * Get argument. Note that tsol_mlpent_t is the same on LP64 and
932 * ILP32, so no special handling is required.
934 if (copyin(buf, &tsme, sizeof (tsme)) != 0) {
935 DTRACE_PROBE(tx__tndb__l0__tnmlp__copyin);
936 return (set_errno(EFAULT));
939 /* MLPs on shared IP addresses */
940 if (tsme.tsme_flags & TSOL_MEF_SHARED) {
941 zone = NULL;
942 mlpl = &shared_mlps;
943 } else {
944 zone = zone_find_by_id(tsme.tsme_zoneid);
945 if (zone == NULL)
946 return (set_errno(EINVAL));
947 mlpl = &zone->zone_mlps;
949 if (tsme.tsme_mlp.mlp_port_upper == 0)
950 tsme.tsme_mlp.mlp_port_upper = tsme.tsme_mlp.mlp_port;
952 switch (cmd) {
953 case TNDB_LOAD:
954 DTRACE_PROBE1(tx__tndb__l2__tnmlp__tndbload,
955 tsol_mlpent_t *, &tsme);
956 if (tsme.tsme_mlp.mlp_ipp == 0 || tsme.tsme_mlp.mlp_port == 0 ||
957 tsme.tsme_mlp.mlp_port > tsme.tsme_mlp.mlp_port_upper) {
958 retv = EINVAL;
959 break;
961 retv = mlp_add_del(mlpl, tsme.tsme_zoneid,
962 tsme.tsme_mlp.mlp_ipp, tsme.tsme_mlp.mlp_port,
963 tsme.tsme_mlp.mlp_port_upper, B_TRUE);
964 break;
966 case TNDB_GET:
967 DTRACE_PROBE1(tx__tndb__l2__tnmlp__tndbget,
968 tsol_mlpent_t *, &tsme);
971 * Search for the requested element or, failing that, the one
972 * that's logically next in the sequence.
974 rw_enter(&mlpl->mlpl_rwlock, RW_READER);
975 for (tme = mlpl->mlpl_first; tme != NULL;
976 tme = tme->mlpe_next) {
977 if (tsme.tsme_zoneid != ALL_ZONES &&
978 tme->mlpe_zoneid != tsme.tsme_zoneid)
979 continue;
980 if (tme->mlpe_mlp.mlp_ipp >= tsme.tsme_mlp.mlp_ipp &&
981 tme->mlpe_mlp.mlp_port == tsme.tsme_mlp.mlp_port)
982 break;
983 if (tme->mlpe_mlp.mlp_port > tsme.tsme_mlp.mlp_port)
984 break;
986 if (tme == NULL) {
987 retv = ENOENT;
988 } else {
989 tsme.tsme_zoneid = tme->mlpe_zoneid;
990 tsme.tsme_mlp = tme->mlpe_mlp;
991 retv = 0;
993 rw_exit(&mlpl->mlpl_rwlock);
994 break;
996 case TNDB_DELETE:
997 DTRACE_PROBE1(tx__tndb__l4__tnmlp__tndbdelete,
998 tsol_mlpent_t *, &tsme);
999 retv = mlp_add_del(mlpl, tsme.tsme_zoneid,
1000 tsme.tsme_mlp.mlp_ipp, tsme.tsme_mlp.mlp_port,
1001 tsme.tsme_mlp.mlp_port_upper, B_FALSE);
1002 break;
1004 case TNDB_FLUSH:
1005 DTRACE_PROBE1(tx__tndb__l4__tnmlp__tndbflush,
1006 tsol_mlpent_t *, &tsme);
1007 mlp_flush(mlpl, ALL_ZONES);
1008 mlp_flush(&shared_mlps, tsme.tsme_zoneid);
1009 retv = 0;
1010 break;
1012 default:
1013 DTRACE_PROBE1(tx__tndb__l0__tnmlp__unknowncmd, int,
1014 cmd);
1015 retv = EOPNOTSUPP;
1016 break;
1019 if (zone != NULL)
1020 zone_rele(zone);
1022 if (cmd == TNDB_GET && retv == 0) {
1023 /* Copy out result */
1024 if (copyout(&tsme, buf, sizeof (tsme)) != 0) {
1025 DTRACE_PROBE(tx__tndb__l0__tnmlp__copyout);
1026 retv = EFAULT;
1030 if (retv != 0)
1031 return (set_errno(retv));
1032 else
1033 return (retv);
1037 * Returns a tnrhc matching the addr address.
1038 * The returned rhc's refcnt is incremented.
1040 tsol_tnrhc_t *
1041 find_rhc(const void *addr, uchar_t version, boolean_t staleok)
1043 tsol_tnrhc_t *rh = NULL;
1044 tsol_tnrhc_t *new;
1045 tsol_tpc_t *tpc;
1046 tnrhc_hash_t *tnrhc_hash;
1047 ipaddr_t tmpmask;
1048 in_addr_t *in4 = (in_addr_t *)addr;
1049 in6_addr_t *in6 = (in6_addr_t *)addr;
1050 in_addr_t tmpin4;
1051 in6_addr_t tmpmask6;
1052 int i;
1053 int prefix;
1056 * An IPv4-mapped IPv6 address is really an IPv4 address
1057 * in IPv6 format.
1059 if (version == IPV6_VERSION &&
1060 IN6_IS_ADDR_V4MAPPED(in6)) {
1061 IN6_V4MAPPED_TO_IPADDR(in6, tmpin4);
1062 version = IPV4_VERSION;
1063 in4 = &tmpin4;
1067 * Search the tnrh hash table for each prefix length,
1068 * starting at longest prefix length, until a matching
1069 * rhc entry is found.
1071 if (version == IPV4_VERSION) {
1072 for (i = (TSOL_MASK_TABLE_SIZE - 1); i >= 0; i--) {
1074 if ((tnrhc_table[i]) == NULL)
1075 continue;
1077 tmpmask = tsol_plen_to_mask(i);
1078 tnrhc_hash = &tnrhc_table[i][
1079 TSOL_ADDR_HASH(*in4 & tmpmask, TNRHC_SIZE)];
1081 mutex_enter(&tnrhc_hash->tnrh_lock);
1082 for (rh = tnrhc_hash->tnrh_list; rh != NULL;
1083 rh = rh->rhc_next) {
1084 if ((rh->rhc_host.ta_family == AF_INET) &&
1085 ((rh->rhc_host.ta_addr_v4.s_addr &
1086 tmpmask) == (*in4 & tmpmask))) {
1087 prefix = i;
1088 TNRHC_HOLD(rh);
1089 break;
1092 mutex_exit(&tnrhc_hash->tnrh_lock);
1093 if (rh != NULL)
1094 break;
1096 if (rh == NULL)
1097 DTRACE_PROBE1(tx__tndb__l1__findrhc__norhv4ent,
1098 in_addr_t *, in4);
1099 } else {
1100 for (i = (TSOL_MASK_TABLE_SIZE_V6 - 1); i >= 0; i--) {
1101 if ((tnrhc_table_v6[i]) == NULL)
1102 continue;
1104 tsol_plen_to_mask_v6(i, &tmpmask6);
1105 tnrhc_hash = &tnrhc_table_v6[i][
1106 TSOL_ADDR_MASK_HASH_V6(*in6, tmpmask6, TNRHC_SIZE)];
1108 mutex_enter(&tnrhc_hash->tnrh_lock);
1109 for (rh = tnrhc_hash->tnrh_list; rh != NULL;
1110 rh = rh->rhc_next) {
1111 if ((rh->rhc_host.ta_family == AF_INET6) &&
1112 V6_MASK_EQ_2(rh->rhc_host.ta_addr_v6,
1113 tmpmask6, *in6)) {
1114 prefix = i;
1115 TNRHC_HOLD(rh);
1116 break;
1119 mutex_exit(&tnrhc_hash->tnrh_lock);
1120 if (rh != NULL)
1121 break;
1123 if (rh == NULL)
1124 DTRACE_PROBE1(tx__tndb__l1__findrhc__norhv6ent,
1125 in6_addr_t *, in6);
1129 * Does the tnrh entry point to a stale template?
1130 * This can happen any time the user deletes or modifies
1131 * a template that has existing tnrh entries pointing
1132 * to it. Try to find a new version of the template.
1133 * If there is no template, then just give up.
1134 * If the template exists, reload the tnrh entry.
1136 if (rh != NULL && rh->rhc_tpc->tpc_invalid) {
1137 tpc = tnrhtp_find(rh->rhc_tpc->tpc_tp.name, tpc_name_hash);
1138 if (tpc == NULL) {
1139 if (!staleok) {
1140 DTRACE_PROBE2(tx__tndb__l1__findrhc__staletpc,
1141 tsol_tnrhc_t *, rh, tsol_tpc_t *,
1142 rh->rhc_tpc);
1143 TNRHC_RELE(rh);
1144 rh = NULL;
1146 } else {
1147 ASSERT(tpc->tpc_tp.host_type == UNLABELED ||
1148 tpc->tpc_tp.host_type == SUN_CIPSO);
1150 if ((new = kmem_zalloc(sizeof (*new),
1151 KM_NOSLEEP)) == NULL) {
1152 DTRACE_PROBE(tx__tndb__l1__findrhc__nomem);
1153 TNRHC_RELE(rh);
1154 TPC_RELE(tpc);
1155 return (NULL);
1158 mutex_init(&new->rhc_lock, NULL, MUTEX_DEFAULT, NULL);
1159 new->rhc_host = rh->rhc_host;
1160 new->rhc_tpc = tpc;
1161 new->rhc_isbcast = rh->rhc_isbcast;
1162 new->rhc_local = rh->rhc_local;
1163 TNRHC_RELE(rh);
1164 rh = new;
1167 * This function increments the tnrh entry ref count
1168 * for the pointer returned to the caller.
1169 * tnrh_hash_add increments the tnrh entry ref count
1170 * for the pointer in the hash table.
1172 TNRHC_HOLD(rh);
1173 if (tnrh_hash_add(new, prefix) != 0) {
1174 TNRHC_RELE(rh);
1175 rh = NULL;
1179 return (rh);
1182 tsol_tpc_t *
1183 find_tpc(const void *addr, uchar_t version, boolean_t staleok)
1185 tsol_tpc_t *tpc;
1186 tsol_tnrhc_t *rhc;
1188 if ((rhc = find_rhc(addr, version, staleok)) == NULL)
1189 return (NULL);
1191 tpc = rhc->rhc_tpc;
1192 TPC_HOLD(tpc);
1193 TNRHC_RELE(rhc);
1194 return (tpc);
1198 * create an internal template called "_unlab":
1200 * _unlab;\
1201 * host_type = unlabeled;\
1202 * def_label = ADMIN_LOW[ADMIN_LOW];\
1203 * min_sl = ADMIN_LOW;\
1204 * max_sl = ADMIN_HIGH;
1206 static void
1207 tsol_create_i_tmpls(void)
1209 tsol_tpent_t rhtpent;
1211 bzero(&rhtpent, sizeof (rhtpent));
1213 /* create _unlab */
1214 (void) strcpy(rhtpent.name, "_unlab");
1216 rhtpent.host_type = UNLABELED;
1217 rhtpent.tp_mask_unl = TSOL_MSK_DEF_LABEL | TSOL_MSK_DEF_CL |
1218 TSOL_MSK_SL_RANGE_TSOL;
1220 rhtpent.tp_gw_sl_range.lower_bound = *label2bslabel(l_admin_low);
1221 rhtpent.tp_def_label = rhtpent.tp_gw_sl_range.lower_bound;
1222 rhtpent.tp_gw_sl_range.upper_bound = *label2bslabel(l_admin_high);
1223 rhtpent.tp_cipso_doi_unl = default_doi;
1224 tpc_unlab = tnrhtp_create(&rhtpent, KM_SLEEP);
1228 * set up internal host template, called from kernel only.
1230 static void
1231 tsol_create_i_tnrh(const tnaddr_t *sa)
1233 tsol_tnrhc_t *rh, *new;
1234 tnrhc_hash_t *tnrhc_hash;
1236 /* Allocate a new entry before taking the lock */
1237 new = kmem_zalloc(sizeof (*new), KM_SLEEP);
1239 tnrhc_hash = (sa->ta_family == AF_INET) ? &tnrhc_table[0][0] :
1240 &tnrhc_table_v6[0][0];
1242 mutex_enter(&tnrhc_hash->tnrh_lock);
1243 rh = tnrhc_hash->tnrh_list;
1245 if (rh == NULL) {
1246 /* We're keeping the new entry. */
1247 rh = new;
1248 new = NULL;
1249 rh->rhc_host = *sa;
1250 mutex_init(&rh->rhc_lock, NULL, MUTEX_DEFAULT, NULL);
1251 TNRHC_HOLD(rh);
1252 tnrhc_hash->tnrh_list = rh;
1256 * Link the entry to internal_unlab
1258 if (rh->rhc_tpc != tpc_unlab) {
1259 if (rh->rhc_tpc != NULL)
1260 TPC_RELE(rh->rhc_tpc);
1261 rh->rhc_tpc = tpc_unlab;
1262 TPC_HOLD(tpc_unlab);
1264 mutex_exit(&tnrhc_hash->tnrh_lock);
1265 if (new != NULL)
1266 kmem_free(new, sizeof (*new));
1270 * Returns 0 if the port is known to be SLP. Returns next possible port number
1271 * (wrapping through 1) if port is MLP on shared or global. Administrator
1272 * should not make all ports MLP. If that's done, then we'll just pretend
1273 * everything is SLP to avoid looping forever.
1275 * Note: port is in host byte order.
1277 in_port_t
1278 tsol_next_port(zone_t *zone, in_port_t port, int proto, boolean_t upward)
1280 boolean_t loop;
1281 tsol_mlp_entry_t *tme;
1282 int newport = port;
1284 loop = B_FALSE;
1285 for (;;) {
1286 if (zone != NULL && zone->zone_mlps.mlpl_first != NULL) {
1287 rw_enter(&zone->zone_mlps.mlpl_rwlock, RW_READER);
1288 for (tme = zone->zone_mlps.mlpl_first; tme != NULL;
1289 tme = tme->mlpe_next) {
1290 if (proto == tme->mlpe_mlp.mlp_ipp &&
1291 newport >= tme->mlpe_mlp.mlp_port &&
1292 newport <= tme->mlpe_mlp.mlp_port_upper)
1293 newport = upward ?
1294 tme->mlpe_mlp.mlp_port_upper + 1 :
1295 tme->mlpe_mlp.mlp_port - 1;
1297 rw_exit(&zone->zone_mlps.mlpl_rwlock);
1299 if (shared_mlps.mlpl_first != NULL) {
1300 rw_enter(&shared_mlps.mlpl_rwlock, RW_READER);
1301 for (tme = shared_mlps.mlpl_first; tme != NULL;
1302 tme = tme->mlpe_next) {
1303 if (proto == tme->mlpe_mlp.mlp_ipp &&
1304 newport >= tme->mlpe_mlp.mlp_port &&
1305 newport <= tme->mlpe_mlp.mlp_port_upper)
1306 newport = upward ?
1307 tme->mlpe_mlp.mlp_port_upper + 1 :
1308 tme->mlpe_mlp.mlp_port - 1;
1310 rw_exit(&shared_mlps.mlpl_rwlock);
1312 if (newport <= 65535 && newport > 0)
1313 break;
1314 if (loop)
1315 return (0);
1316 loop = B_TRUE;
1317 newport = upward ? 1 : 65535;
1319 return (newport == port ? 0 : newport);
1323 * tsol_mlp_port_type will check if the given (zone, proto, port) is a
1324 * multilevel port. If it is, return the type (shared, private, or both), or
1325 * indicate that it's single-level.
1327 * Note: port is given in host byte order, not network byte order.
1329 mlp_type_t
1330 tsol_mlp_port_type(zone_t *zone, uchar_t proto, uint16_t port,
1331 mlp_type_t mlptype)
1333 tsol_mlp_entry_t *tme;
1335 if (mlptype == mlptBoth || mlptype == mlptPrivate) {
1336 tme = NULL;
1337 if (zone->zone_mlps.mlpl_first != NULL) {
1338 rw_enter(&zone->zone_mlps.mlpl_rwlock, RW_READER);
1339 for (tme = zone->zone_mlps.mlpl_first; tme != NULL;
1340 tme = tme->mlpe_next) {
1341 if (proto == tme->mlpe_mlp.mlp_ipp &&
1342 port >= tme->mlpe_mlp.mlp_port &&
1343 port <= tme->mlpe_mlp.mlp_port_upper)
1344 break;
1346 rw_exit(&zone->zone_mlps.mlpl_rwlock);
1348 if (tme == NULL) {
1349 if (mlptype == mlptBoth)
1350 mlptype = mlptShared;
1351 else if (mlptype == mlptPrivate)
1352 mlptype = mlptSingle;
1355 if (mlptype == mlptBoth || mlptype == mlptShared) {
1356 tme = NULL;
1357 if (shared_mlps.mlpl_first != NULL) {
1358 rw_enter(&shared_mlps.mlpl_rwlock, RW_READER);
1359 for (tme = shared_mlps.mlpl_first; tme != NULL;
1360 tme = tme->mlpe_next) {
1361 if (proto == tme->mlpe_mlp.mlp_ipp &&
1362 port >= tme->mlpe_mlp.mlp_port &&
1363 port <= tme->mlpe_mlp.mlp_port_upper)
1364 break;
1366 rw_exit(&shared_mlps.mlpl_rwlock);
1368 if (tme == NULL) {
1369 if (mlptype == mlptBoth)
1370 mlptype = mlptPrivate;
1371 else if (mlptype == mlptShared)
1372 mlptype = mlptSingle;
1375 return (mlptype);
1379 * tsol_mlp_findzone will check if the given (proto, port) is a multilevel port
1380 * on a shared address. If it is, return the owning zone.
1382 * Note: lport is in network byte order, unlike the other MLP functions,
1383 * because the callers of this function are all dealing with packets off the
1384 * wire.
1386 zoneid_t
1387 tsol_mlp_findzone(uchar_t proto, uint16_t lport)
1389 tsol_mlp_entry_t *tme;
1390 zoneid_t zoneid;
1391 uint16_t port;
1393 if (shared_mlps.mlpl_first == NULL)
1394 return (ALL_ZONES);
1395 port = ntohs(lport);
1396 rw_enter(&shared_mlps.mlpl_rwlock, RW_READER);
1397 for (tme = shared_mlps.mlpl_first; tme != NULL; tme = tme->mlpe_next) {
1398 if (proto == tme->mlpe_mlp.mlp_ipp &&
1399 port >= tme->mlpe_mlp.mlp_port &&
1400 port <= tme->mlpe_mlp.mlp_port_upper)
1401 break;
1403 zoneid = tme == NULL ? ALL_ZONES : tme->mlpe_zoneid;
1404 rw_exit(&shared_mlps.mlpl_rwlock);
1405 return (zoneid);
1408 /* Debug routine */
1409 void
1410 tsol_print_label(const blevel_t *blev, const char *name)
1412 const _blevel_impl_t *bli = (const _blevel_impl_t *)blev;
1414 /* We really support only sensitivity labels */
1415 cmn_err(CE_NOTE, "%s %x:%x:%08x%08x%08x%08x%08x%08x%08x%08x",
1416 name, bli->id, LCLASS(bli), ntohl(bli->_comps.c1),
1417 ntohl(bli->_comps.c2), ntohl(bli->_comps.c3), ntohl(bli->_comps.c4),
1418 ntohl(bli->_comps.c5), ntohl(bli->_comps.c6), ntohl(bli->_comps.c7),
1419 ntohl(bli->_comps.c8));
1423 * Name: labelsys()
1425 * Normal: Routes TSOL syscalls.
1427 * Output: As defined for each TSOL syscall.
1428 * Returns ENOSYS for unrecognized calls.
1430 /* ARGSUSED */
1432 labelsys(int op, void *a1, void *a2, void *a3, void *a4, void *a5)
1434 switch (op) {
1435 case TSOL_SYSLABELING:
1436 return (sys_labeling);
1437 case TSOL_TNRH:
1438 return (tnrh((int)(uintptr_t)a1, a2));
1439 case TSOL_TNRHTP:
1440 return (tnrhtp((int)(uintptr_t)a1, a2));
1441 case TSOL_TNMLP:
1442 return (tnmlp((int)(uintptr_t)a1, a2));
1443 case TSOL_GETLABEL:
1444 return (getlabel((char *)a1, (bslabel_t *)a2));
1445 case TSOL_FGETLABEL:
1446 return (fgetlabel((int)(uintptr_t)a1, (bslabel_t *)a2));
1447 default:
1448 return (set_errno(ENOSYS));
1450 /* NOTREACHED */