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 /*
22de8c4a14SErik Nordmark * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
2345916cd2Sjpk * Use is subject to license terms.
2445916cd2Sjpk */
2545916cd2Sjpk
2645916cd2Sjpk #include <sys/types.h>
2745916cd2Sjpk #include <sys/stream.h>
2845916cd2Sjpk #include <sys/strsubr.h>
2945916cd2Sjpk #include <sys/stropts.h>
3045916cd2Sjpk #include <sys/sunddi.h>
3145916cd2Sjpk #include <sys/cred.h>
3245916cd2Sjpk #include <sys/debug.h>
3345916cd2Sjpk #include <sys/kmem.h>
3445916cd2Sjpk #include <sys/errno.h>
3545916cd2Sjpk #include <sys/disp.h>
3645916cd2Sjpk #include <netinet/in.h>
3745916cd2Sjpk #include <netinet/in_systm.h>
3845916cd2Sjpk #include <netinet/ip.h>
3945916cd2Sjpk #include <netinet/ip_icmp.h>
4045916cd2Sjpk #include <netinet/tcp.h>
4145916cd2Sjpk #include <inet/common.h>
4245916cd2Sjpk #include <inet/ipclassifier.h>
4345916cd2Sjpk #include <inet/ip.h>
4445916cd2Sjpk #include <inet/mib2.h>
4545916cd2Sjpk #include <inet/nd.h>
4645916cd2Sjpk #include <inet/tcp.h>
4745916cd2Sjpk #include <inet/ip_rts.h>
4845916cd2Sjpk #include <inet/ip_ire.h>
4945916cd2Sjpk #include <inet/ip_if.h>
5045916cd2Sjpk #include <sys/modhash.h>
5145916cd2Sjpk
5245916cd2Sjpk #include <sys/tsol/label.h>
5345916cd2Sjpk #include <sys/tsol/label_macro.h>
5445916cd2Sjpk #include <sys/tsol/tnet.h>
5545916cd2Sjpk #include <sys/tsol/tndb.h>
5645916cd2Sjpk #include <sys/strsun.h>
5745916cd2Sjpk
5845916cd2Sjpk /* tunable for strict error-reply behavior (TCP RST and ICMP Unreachable) */
5945916cd2Sjpk int tsol_strict_error;
6045916cd2Sjpk
6145916cd2Sjpk /*
6245916cd2Sjpk * Some notes on the Trusted Solaris IRE gateway security attributes:
6345916cd2Sjpk *
6445916cd2Sjpk * When running in Trusted mode, the routing subsystem determines whether or
6545916cd2Sjpk * not a packet can be delivered to an off-link host (not directly reachable
6645916cd2Sjpk * through an interface) based on the accreditation checks of the packet's
6745916cd2Sjpk * security attributes against those associated with the next-hop gateway.
6845916cd2Sjpk *
6945916cd2Sjpk * The next-hop gateway's security attributes can be derived from two sources
7045916cd2Sjpk * (in order of preference): route-related and the host database. A Trusted
7145916cd2Sjpk * system must be configured with at least the host database containing an
7245916cd2Sjpk * entry for the next-hop gateway, or otherwise no accreditation checks can
7345916cd2Sjpk * be performed, which may result in the inability to send packets to any
7445916cd2Sjpk * off-link destination host.
7545916cd2Sjpk *
7645916cd2Sjpk * The major differences between the two sources are the number and type of
7745916cd2Sjpk * security attributes used for accreditation checks. A host database entry
7845916cd2Sjpk * can contain at most one set of security attributes, specific only to the
7945916cd2Sjpk * next-hop gateway. On contrast, route-related security attributes are made
8045916cd2Sjpk * up of a collection of security attributes for the distant networks, and
8145916cd2Sjpk * are grouped together per next-hop gateway used to reach those networks.
8245916cd2Sjpk * This is the preferred method, and the routing subsystem will fallback to
8345916cd2Sjpk * the host database entry only if there are no route-related attributes
8445916cd2Sjpk * associated with the next-hop gateway.
8545916cd2Sjpk *
8645916cd2Sjpk * In Trusted mode, all of the IRE entries (except LOCAL/LOOPBACK/BROADCAST/
8745916cd2Sjpk * INTERFACE type) are initialized to contain a placeholder to store this
8845916cd2Sjpk * information. The ire_gw_secattr structure gets allocated, initialized
8945916cd2Sjpk * and associated with the IRE during the time of the IRE creation. The
9045916cd2Sjpk * initialization process also includes resolving the host database entry
9145916cd2Sjpk * of the next-hop gateway for fallback purposes. It does not include any
9245916cd2Sjpk * route-related attribute setup, as that process comes separately as part
9345916cd2Sjpk * of the route requests (add/change) made to the routing subsystem.
9445916cd2Sjpk *
9545916cd2Sjpk * The underlying logic which involves associating IREs with the gateway
9645916cd2Sjpk * security attributes are represented by the following data structures:
9745916cd2Sjpk *
9845916cd2Sjpk * tsol_gcdb_t, or "gcdb"
9945916cd2Sjpk *
10045916cd2Sjpk * - This is a system-wide collection of records containing the
10145916cd2Sjpk * currently used route-related security attributes, which are fed
10245916cd2Sjpk * through the routing socket interface, e.g. "route add/change".
10345916cd2Sjpk *
10445916cd2Sjpk * tsol_gc_t, or "gc"
10545916cd2Sjpk *
10645916cd2Sjpk * - This is the gateway credential structure, and it provides for the
10745916cd2Sjpk * only mechanism to access the contents of gcdb. More than one gc
10845916cd2Sjpk * entries may refer to the same gcdb record. gc's in the system are
10945916cd2Sjpk * grouped according to the next-hop gateway address.
11045916cd2Sjpk *
11145916cd2Sjpk * tsol_gcgrp_t, or "gcgrp"
11245916cd2Sjpk *
11345916cd2Sjpk * - Group of gateway credentials, and is unique per next-hop gateway
11445916cd2Sjpk * address. When the group is not empty, i.e. when gcgrp_count is
11545916cd2Sjpk * greater than zero, it contains one or more gc's, each pointing to
11645916cd2Sjpk * a gcdb record which indicates the gateway security attributes
11745916cd2Sjpk * associated with the next-hop gateway.
11845916cd2Sjpk *
11945916cd2Sjpk * The fields of the tsol_ire_gw_secattr_t used from within the IRE are:
12045916cd2Sjpk *
12145916cd2Sjpk * igsa_lock
12245916cd2Sjpk *
12345916cd2Sjpk * - Lock that protects all fields within tsol_ire_gw_secattr_t.
12445916cd2Sjpk *
12545916cd2Sjpk * igsa_rhc
12645916cd2Sjpk *
12745916cd2Sjpk * - Remote host cache database entry of next-hop gateway. This is
12845916cd2Sjpk * used in the case when there are no route-related attributes
12945916cd2Sjpk * configured for the IRE.
13045916cd2Sjpk *
13145916cd2Sjpk * igsa_gc
13245916cd2Sjpk *
13345916cd2Sjpk * - A set of route-related attributes that only get set for prefix
13445916cd2Sjpk * IREs. If this is non-NULL, the prefix IRE has been associated
13545916cd2Sjpk * with a set of gateway security attributes by way of route add/
136bd670b35SErik Nordmark * change functionality.
13745916cd2Sjpk */
13845916cd2Sjpk
13945916cd2Sjpk static kmem_cache_t *ire_gw_secattr_cache;
14045916cd2Sjpk
14145916cd2Sjpk #define GCDB_HASH_SIZE 101
14245916cd2Sjpk #define GCGRP_HASH_SIZE 101
14345916cd2Sjpk
14445916cd2Sjpk #define GCDB_REFRELE(p) { \
14545916cd2Sjpk mutex_enter(&gcdb_lock); \
14645916cd2Sjpk ASSERT((p)->gcdb_refcnt > 0); \
14745916cd2Sjpk if (--((p)->gcdb_refcnt) == 0) \
14845916cd2Sjpk gcdb_inactive(p); \
14945916cd2Sjpk ASSERT(MUTEX_HELD(&gcdb_lock)); \
15045916cd2Sjpk mutex_exit(&gcdb_lock); \
15145916cd2Sjpk }
15245916cd2Sjpk
15345916cd2Sjpk static int gcdb_hash_size = GCDB_HASH_SIZE;
15445916cd2Sjpk static int gcgrp_hash_size = GCGRP_HASH_SIZE;
15545916cd2Sjpk static mod_hash_t *gcdb_hash;
15645916cd2Sjpk static mod_hash_t *gcgrp4_hash;
15745916cd2Sjpk static mod_hash_t *gcgrp6_hash;
15845916cd2Sjpk
15945916cd2Sjpk static kmutex_t gcdb_lock;
16045916cd2Sjpk kmutex_t gcgrp_lock;
16145916cd2Sjpk
16245916cd2Sjpk static uint_t gcdb_hash_by_secattr(void *, mod_hash_key_t);
16345916cd2Sjpk static int gcdb_hash_cmp(mod_hash_key_t, mod_hash_key_t);
16445916cd2Sjpk static tsol_gcdb_t *gcdb_lookup(struct rtsa_s *, boolean_t);
16545916cd2Sjpk static void gcdb_inactive(tsol_gcdb_t *);
16645916cd2Sjpk
16745916cd2Sjpk static uint_t gcgrp_hash_by_addr(void *, mod_hash_key_t);
16845916cd2Sjpk static int gcgrp_hash_cmp(mod_hash_key_t, mod_hash_key_t);
16945916cd2Sjpk
17045916cd2Sjpk static int ire_gw_secattr_constructor(void *, void *, int);
17145916cd2Sjpk static void ire_gw_secattr_destructor(void *, void *);
17245916cd2Sjpk
17345916cd2Sjpk void
tnet_init(void)17445916cd2Sjpk tnet_init(void)
17545916cd2Sjpk {
17645916cd2Sjpk ire_gw_secattr_cache = kmem_cache_create("ire_gw_secattr_cache",
17745916cd2Sjpk sizeof (tsol_ire_gw_secattr_t), 64, ire_gw_secattr_constructor,
17845916cd2Sjpk ire_gw_secattr_destructor, NULL, NULL, NULL, 0);
17945916cd2Sjpk
18045916cd2Sjpk gcdb_hash = mod_hash_create_extended("gcdb_hash",
18145916cd2Sjpk gcdb_hash_size, mod_hash_null_keydtor, mod_hash_null_valdtor,
18245916cd2Sjpk gcdb_hash_by_secattr, NULL, gcdb_hash_cmp, KM_SLEEP);
18345916cd2Sjpk
18445916cd2Sjpk gcgrp4_hash = mod_hash_create_extended("gcgrp4_hash",
18545916cd2Sjpk gcgrp_hash_size, mod_hash_null_keydtor, mod_hash_null_valdtor,
18645916cd2Sjpk gcgrp_hash_by_addr, NULL, gcgrp_hash_cmp, KM_SLEEP);
18745916cd2Sjpk
18845916cd2Sjpk gcgrp6_hash = mod_hash_create_extended("gcgrp6_hash",
18945916cd2Sjpk gcgrp_hash_size, mod_hash_null_keydtor, mod_hash_null_valdtor,
19045916cd2Sjpk gcgrp_hash_by_addr, NULL, gcgrp_hash_cmp, KM_SLEEP);
19145916cd2Sjpk
19245916cd2Sjpk mutex_init(&gcdb_lock, NULL, MUTEX_DEFAULT, NULL);
19345916cd2Sjpk mutex_init(&gcgrp_lock, NULL, MUTEX_DEFAULT, NULL);
19445916cd2Sjpk }
19545916cd2Sjpk
19645916cd2Sjpk void
tnet_fini(void)19745916cd2Sjpk tnet_fini(void)
19845916cd2Sjpk {
19945916cd2Sjpk kmem_cache_destroy(ire_gw_secattr_cache);
20045916cd2Sjpk mod_hash_destroy_hash(gcdb_hash);
20145916cd2Sjpk mod_hash_destroy_hash(gcgrp4_hash);
20245916cd2Sjpk mod_hash_destroy_hash(gcgrp6_hash);
20345916cd2Sjpk mutex_destroy(&gcdb_lock);
20445916cd2Sjpk mutex_destroy(&gcgrp_lock);
20545916cd2Sjpk }
20645916cd2Sjpk
20745916cd2Sjpk /* ARGSUSED */
20845916cd2Sjpk static int
ire_gw_secattr_constructor(void * buf,void * cdrarg,int kmflags)20945916cd2Sjpk ire_gw_secattr_constructor(void *buf, void *cdrarg, int kmflags)
21045916cd2Sjpk {
21145916cd2Sjpk tsol_ire_gw_secattr_t *attrp = buf;
21245916cd2Sjpk
21345916cd2Sjpk mutex_init(&attrp->igsa_lock, NULL, MUTEX_DEFAULT, NULL);
21445916cd2Sjpk
21545916cd2Sjpk attrp->igsa_rhc = NULL;
21645916cd2Sjpk attrp->igsa_gc = NULL;
21745916cd2Sjpk
21845916cd2Sjpk return (0);
21945916cd2Sjpk }
22045916cd2Sjpk
22145916cd2Sjpk /* ARGSUSED */
22245916cd2Sjpk static void
ire_gw_secattr_destructor(void * buf,void * cdrarg)22345916cd2Sjpk ire_gw_secattr_destructor(void *buf, void *cdrarg)
22445916cd2Sjpk {
22545916cd2Sjpk tsol_ire_gw_secattr_t *attrp = (tsol_ire_gw_secattr_t *)buf;
22645916cd2Sjpk
22745916cd2Sjpk mutex_destroy(&attrp->igsa_lock);
22845916cd2Sjpk }
22945916cd2Sjpk
23045916cd2Sjpk tsol_ire_gw_secattr_t *
ire_gw_secattr_alloc(int kmflags)23145916cd2Sjpk ire_gw_secattr_alloc(int kmflags)
23245916cd2Sjpk {
23345916cd2Sjpk return (kmem_cache_alloc(ire_gw_secattr_cache, kmflags));
23445916cd2Sjpk }
23545916cd2Sjpk
23645916cd2Sjpk void
ire_gw_secattr_free(tsol_ire_gw_secattr_t * attrp)23745916cd2Sjpk ire_gw_secattr_free(tsol_ire_gw_secattr_t *attrp)
23845916cd2Sjpk {
23945916cd2Sjpk ASSERT(MUTEX_NOT_HELD(&attrp->igsa_lock));
24045916cd2Sjpk
24145916cd2Sjpk if (attrp->igsa_rhc != NULL) {
24245916cd2Sjpk TNRHC_RELE(attrp->igsa_rhc);
24345916cd2Sjpk attrp->igsa_rhc = NULL;
24445916cd2Sjpk }
24545916cd2Sjpk
24645916cd2Sjpk if (attrp->igsa_gc != NULL) {
24745916cd2Sjpk GC_REFRELE(attrp->igsa_gc);
24845916cd2Sjpk attrp->igsa_gc = NULL;
24945916cd2Sjpk }
25045916cd2Sjpk
25145916cd2Sjpk ASSERT(attrp->igsa_rhc == NULL);
25245916cd2Sjpk ASSERT(attrp->igsa_gc == NULL);
25345916cd2Sjpk
25445916cd2Sjpk kmem_cache_free(ire_gw_secattr_cache, attrp);
25545916cd2Sjpk }
25645916cd2Sjpk
25745916cd2Sjpk /* ARGSUSED */
25845916cd2Sjpk static uint_t
gcdb_hash_by_secattr(void * hash_data,mod_hash_key_t key)25945916cd2Sjpk gcdb_hash_by_secattr(void *hash_data, mod_hash_key_t key)
26045916cd2Sjpk {
26145916cd2Sjpk const struct rtsa_s *rp = (struct rtsa_s *)key;
26245916cd2Sjpk const uint32_t *up, *ue;
26345916cd2Sjpk uint_t hash;
26445916cd2Sjpk int i;
26545916cd2Sjpk
26645916cd2Sjpk ASSERT(rp != NULL);
26745916cd2Sjpk
26845916cd2Sjpk /* See comments in hash_bylabel in zone.c for details */
26945916cd2Sjpk hash = rp->rtsa_doi + (rp->rtsa_doi << 1);
27045916cd2Sjpk up = (const uint32_t *)&rp->rtsa_slrange;
27145916cd2Sjpk ue = up + sizeof (rp->rtsa_slrange) / sizeof (*up);
27245916cd2Sjpk i = 1;
27345916cd2Sjpk while (up < ue) {
27445916cd2Sjpk /* using 2^n + 1, 1 <= n <= 16 as source of many primes */
27545916cd2Sjpk hash += *up + (*up << ((i % 16) + 1));
27645916cd2Sjpk up++;
27745916cd2Sjpk i++;
27845916cd2Sjpk }
27945916cd2Sjpk return (hash);
28045916cd2Sjpk }
28145916cd2Sjpk
28245916cd2Sjpk static int
gcdb_hash_cmp(mod_hash_key_t key1,mod_hash_key_t key2)28345916cd2Sjpk gcdb_hash_cmp(mod_hash_key_t key1, mod_hash_key_t key2)
28445916cd2Sjpk {
28545916cd2Sjpk struct rtsa_s *rp1 = (struct rtsa_s *)key1;
28645916cd2Sjpk struct rtsa_s *rp2 = (struct rtsa_s *)key2;
28745916cd2Sjpk
28845916cd2Sjpk ASSERT(rp1 != NULL && rp2 != NULL);
28945916cd2Sjpk
29045916cd2Sjpk if (blequal(&rp1->rtsa_slrange.lower_bound,
29145916cd2Sjpk &rp2->rtsa_slrange.lower_bound) &&
29245916cd2Sjpk blequal(&rp1->rtsa_slrange.upper_bound,
29345916cd2Sjpk &rp2->rtsa_slrange.upper_bound) &&
29445916cd2Sjpk rp1->rtsa_doi == rp2->rtsa_doi)
29545916cd2Sjpk return (0);
29645916cd2Sjpk
29745916cd2Sjpk /* No match; not found */
29845916cd2Sjpk return (-1);
29945916cd2Sjpk }
30045916cd2Sjpk
30145916cd2Sjpk /* ARGSUSED */
30245916cd2Sjpk static uint_t
gcgrp_hash_by_addr(void * hash_data,mod_hash_key_t key)30345916cd2Sjpk gcgrp_hash_by_addr(void *hash_data, mod_hash_key_t key)
30445916cd2Sjpk {
30545916cd2Sjpk tsol_gcgrp_addr_t *ga = (tsol_gcgrp_addr_t *)key;
30645916cd2Sjpk uint_t idx = 0;
30745916cd2Sjpk uint32_t *ap;
30845916cd2Sjpk
30945916cd2Sjpk ASSERT(ga != NULL);
31045916cd2Sjpk ASSERT(ga->ga_af == AF_INET || ga->ga_af == AF_INET6);
31145916cd2Sjpk
31245916cd2Sjpk ap = (uint32_t *)&ga->ga_addr.s6_addr32[0];
31345916cd2Sjpk idx ^= *ap++;
31445916cd2Sjpk idx ^= *ap++;
31545916cd2Sjpk idx ^= *ap++;
31645916cd2Sjpk idx ^= *ap;
31745916cd2Sjpk
31845916cd2Sjpk return (idx);
31945916cd2Sjpk }
32045916cd2Sjpk
32145916cd2Sjpk static int
gcgrp_hash_cmp(mod_hash_key_t key1,mod_hash_key_t key2)32245916cd2Sjpk gcgrp_hash_cmp(mod_hash_key_t key1, mod_hash_key_t key2)
32345916cd2Sjpk {
32445916cd2Sjpk tsol_gcgrp_addr_t *ga1 = (tsol_gcgrp_addr_t *)key1;
32545916cd2Sjpk tsol_gcgrp_addr_t *ga2 = (tsol_gcgrp_addr_t *)key2;
32645916cd2Sjpk
32745916cd2Sjpk ASSERT(ga1 != NULL && ga2 != NULL);
32845916cd2Sjpk
32945916cd2Sjpk /* Address family must match */
33045916cd2Sjpk if (ga1->ga_af != ga2->ga_af)
33145916cd2Sjpk return (-1);
33245916cd2Sjpk
33345916cd2Sjpk if (ga1->ga_addr.s6_addr32[0] == ga2->ga_addr.s6_addr32[0] &&
33445916cd2Sjpk ga1->ga_addr.s6_addr32[1] == ga2->ga_addr.s6_addr32[1] &&
33545916cd2Sjpk ga1->ga_addr.s6_addr32[2] == ga2->ga_addr.s6_addr32[2] &&
33645916cd2Sjpk ga1->ga_addr.s6_addr32[3] == ga2->ga_addr.s6_addr32[3])
33745916cd2Sjpk return (0);
33845916cd2Sjpk
33945916cd2Sjpk /* No match; not found */
34045916cd2Sjpk return (-1);
34145916cd2Sjpk }
34245916cd2Sjpk
34345916cd2Sjpk #define RTSAFLAGS "\20\11cipso\3doi\2max_sl\1min_sl"
34445916cd2Sjpk
34545916cd2Sjpk int
rtsa_validate(const struct rtsa_s * rp)34645916cd2Sjpk rtsa_validate(const struct rtsa_s *rp)
34745916cd2Sjpk {
34845916cd2Sjpk uint32_t mask = rp->rtsa_mask;
34945916cd2Sjpk
35045916cd2Sjpk /* RTSA_CIPSO must be set, and DOI must not be zero */
35145916cd2Sjpk if ((mask & RTSA_CIPSO) == 0 || rp->rtsa_doi == 0) {
35245916cd2Sjpk DTRACE_PROBE2(tx__gcdb__log__error__rtsa__validate, char *,
35345916cd2Sjpk "rtsa(1) lacks flag or has 0 doi.",
35445916cd2Sjpk rtsa_s *, rp);
35545916cd2Sjpk return (EINVAL);
35645916cd2Sjpk }
35745916cd2Sjpk /*
35845916cd2Sjpk * SL range must be specified, and it must have its
35945916cd2Sjpk * upper bound dominating its lower bound.
36045916cd2Sjpk */
36145916cd2Sjpk if ((mask & RTSA_SLRANGE) != RTSA_SLRANGE ||
36245916cd2Sjpk !bldominates(&rp->rtsa_slrange.upper_bound,
36345916cd2Sjpk &rp->rtsa_slrange.lower_bound)) {
36445916cd2Sjpk DTRACE_PROBE2(tx__gcdb__log__error__rtsa__validate, char *,
36545916cd2Sjpk "rtsa(1) min_sl and max_sl not set or max_sl is "
36645916cd2Sjpk "not dominating.", rtsa_s *, rp);
36745916cd2Sjpk return (EINVAL);
36845916cd2Sjpk }
36945916cd2Sjpk return (0);
37045916cd2Sjpk }
37145916cd2Sjpk
37245916cd2Sjpk /*
37345916cd2Sjpk * A brief explanation of the reference counting scheme:
37445916cd2Sjpk *
37545916cd2Sjpk * Apart from dynamic references due to to reference holds done
37645916cd2Sjpk * actively by threads, we have the following references:
37745916cd2Sjpk *
37845916cd2Sjpk * gcdb_refcnt:
37945916cd2Sjpk * - Every tsol_gc_t pointing to a tsol_gcdb_t contributes a reference
38045916cd2Sjpk * to the gcdb_refcnt.
38145916cd2Sjpk *
38245916cd2Sjpk * gc_refcnt:
38345916cd2Sjpk * - A prefix IRE that points to an igsa_gc contributes a reference
38445916cd2Sjpk * to the gc_refcnt.
38545916cd2Sjpk *
38645916cd2Sjpk * gcgrp_refcnt:
38745916cd2Sjpk * - Every tsol_gc_t in the chain headed by tsol_gcgrp_t contributes
38845916cd2Sjpk * a reference to the gcgrp_refcnt.
38945916cd2Sjpk */
39045916cd2Sjpk static tsol_gcdb_t *
gcdb_lookup(struct rtsa_s * rp,boolean_t alloc)39145916cd2Sjpk gcdb_lookup(struct rtsa_s *rp, boolean_t alloc)
39245916cd2Sjpk {
39345916cd2Sjpk tsol_gcdb_t *gcdb = NULL;
39445916cd2Sjpk
39545916cd2Sjpk if (rtsa_validate(rp) != 0)
39645916cd2Sjpk return (NULL);
39745916cd2Sjpk
39845916cd2Sjpk mutex_enter(&gcdb_lock);
39945916cd2Sjpk /* Find a copy in the cache; otherwise, create one and cache it */
40045916cd2Sjpk if (mod_hash_find(gcdb_hash, (mod_hash_key_t)rp,
40145916cd2Sjpk (mod_hash_val_t *)&gcdb) == 0) {
40245916cd2Sjpk gcdb->gcdb_refcnt++;
40345916cd2Sjpk ASSERT(gcdb->gcdb_refcnt != 0);
40445916cd2Sjpk
40545916cd2Sjpk DTRACE_PROBE2(tx__gcdb__log__info__gcdb__lookup, char *,
40645916cd2Sjpk "gcdb(1) is in gcdb_hash(global)", tsol_gcdb_t *, gcdb);
40745916cd2Sjpk } else if (alloc) {
40845916cd2Sjpk gcdb = kmem_zalloc(sizeof (*gcdb), KM_NOSLEEP);
40945916cd2Sjpk if (gcdb != NULL) {
41045916cd2Sjpk gcdb->gcdb_refcnt = 1;
41145916cd2Sjpk gcdb->gcdb_mask = rp->rtsa_mask;
41245916cd2Sjpk gcdb->gcdb_doi = rp->rtsa_doi;
41345916cd2Sjpk gcdb->gcdb_slrange = rp->rtsa_slrange;
41445916cd2Sjpk
41545916cd2Sjpk if (mod_hash_insert(gcdb_hash,
41645916cd2Sjpk (mod_hash_key_t)&gcdb->gcdb_attr,
41745916cd2Sjpk (mod_hash_val_t)gcdb) != 0) {
41845916cd2Sjpk mutex_exit(&gcdb_lock);
41945916cd2Sjpk kmem_free(gcdb, sizeof (*gcdb));
42045916cd2Sjpk return (NULL);
42145916cd2Sjpk }
42245916cd2Sjpk
42345916cd2Sjpk DTRACE_PROBE2(tx__gcdb__log__info__gcdb__insert, char *,
42445916cd2Sjpk "gcdb(1) inserted in gcdb_hash(global)",
42545916cd2Sjpk tsol_gcdb_t *, gcdb);
42645916cd2Sjpk }
42745916cd2Sjpk }
42845916cd2Sjpk mutex_exit(&gcdb_lock);
42945916cd2Sjpk return (gcdb);
43045916cd2Sjpk }
43145916cd2Sjpk
43245916cd2Sjpk static void
gcdb_inactive(tsol_gcdb_t * gcdb)43345916cd2Sjpk gcdb_inactive(tsol_gcdb_t *gcdb)
43445916cd2Sjpk {
43545916cd2Sjpk ASSERT(MUTEX_HELD(&gcdb_lock));
43645916cd2Sjpk ASSERT(gcdb != NULL && gcdb->gcdb_refcnt == 0);
43745916cd2Sjpk
43845916cd2Sjpk (void) mod_hash_remove(gcdb_hash, (mod_hash_key_t)&gcdb->gcdb_attr,
43945916cd2Sjpk (mod_hash_val_t *)&gcdb);
44045916cd2Sjpk
44145916cd2Sjpk DTRACE_PROBE2(tx__gcdb__log__info__gcdb__remove, char *,
44245916cd2Sjpk "gcdb(1) removed from gcdb_hash(global)",
44345916cd2Sjpk tsol_gcdb_t *, gcdb);
44445916cd2Sjpk kmem_free(gcdb, sizeof (*gcdb));
44545916cd2Sjpk }
44645916cd2Sjpk
44745916cd2Sjpk tsol_gc_t *
gc_create(struct rtsa_s * rp,tsol_gcgrp_t * gcgrp,boolean_t * gcgrp_xtrarefp)44845916cd2Sjpk gc_create(struct rtsa_s *rp, tsol_gcgrp_t *gcgrp, boolean_t *gcgrp_xtrarefp)
44945916cd2Sjpk {
45045916cd2Sjpk tsol_gc_t *gc;
45145916cd2Sjpk tsol_gcdb_t *gcdb;
45245916cd2Sjpk
45345916cd2Sjpk *gcgrp_xtrarefp = B_TRUE;
45445916cd2Sjpk
45545916cd2Sjpk rw_enter(&gcgrp->gcgrp_rwlock, RW_WRITER);
45645916cd2Sjpk if ((gcdb = gcdb_lookup(rp, B_TRUE)) == NULL) {
45745916cd2Sjpk rw_exit(&gcgrp->gcgrp_rwlock);
45845916cd2Sjpk return (NULL);
45945916cd2Sjpk }
46045916cd2Sjpk
46145916cd2Sjpk for (gc = gcgrp->gcgrp_head; gc != NULL; gc = gc->gc_next) {
46245916cd2Sjpk if (gc->gc_db == gcdb) {
46345916cd2Sjpk ASSERT(gc->gc_grp == gcgrp);
46445916cd2Sjpk
46545916cd2Sjpk gc->gc_refcnt++;
46645916cd2Sjpk ASSERT(gc->gc_refcnt != 0);
46745916cd2Sjpk
46845916cd2Sjpk GCDB_REFRELE(gcdb);
46945916cd2Sjpk
47045916cd2Sjpk DTRACE_PROBE3(tx__gcdb__log__info__gc__create,
47145916cd2Sjpk char *, "found gc(1) in gcgrp(2)",
47245916cd2Sjpk tsol_gc_t *, gc, tsol_gcgrp_t *, gcgrp);
47345916cd2Sjpk rw_exit(&gcgrp->gcgrp_rwlock);
47445916cd2Sjpk return (gc);
47545916cd2Sjpk }
47645916cd2Sjpk }
47745916cd2Sjpk
47845916cd2Sjpk gc = kmem_zalloc(sizeof (*gc), KM_NOSLEEP);
47945916cd2Sjpk if (gc != NULL) {
48045916cd2Sjpk if (gcgrp->gcgrp_head == NULL) {
48145916cd2Sjpk gcgrp->gcgrp_head = gcgrp->gcgrp_tail = gc;
48245916cd2Sjpk } else {
48345916cd2Sjpk gcgrp->gcgrp_tail->gc_next = gc;
48445916cd2Sjpk gc->gc_prev = gcgrp->gcgrp_tail;
48545916cd2Sjpk gcgrp->gcgrp_tail = gc;
48645916cd2Sjpk }
48745916cd2Sjpk gcgrp->gcgrp_count++;
48845916cd2Sjpk ASSERT(gcgrp->gcgrp_count != 0);
48945916cd2Sjpk
49045916cd2Sjpk /* caller has incremented gcgrp reference for us */
49145916cd2Sjpk gc->gc_grp = gcgrp;
49245916cd2Sjpk
49345916cd2Sjpk gc->gc_db = gcdb;
49445916cd2Sjpk gc->gc_refcnt = 1;
49545916cd2Sjpk
49645916cd2Sjpk DTRACE_PROBE3(tx__gcdb__log__info__gc__create, char *,
49745916cd2Sjpk "added gc(1) to gcgrp(2)", tsol_gc_t *, gc,
49845916cd2Sjpk tsol_gcgrp_t *, gcgrp);
49945916cd2Sjpk
50045916cd2Sjpk *gcgrp_xtrarefp = B_FALSE;
50145916cd2Sjpk }
50245916cd2Sjpk rw_exit(&gcgrp->gcgrp_rwlock);
50345916cd2Sjpk
50445916cd2Sjpk return (gc);
50545916cd2Sjpk }
50645916cd2Sjpk
50745916cd2Sjpk void
gc_inactive(tsol_gc_t * gc)50845916cd2Sjpk gc_inactive(tsol_gc_t *gc)
50945916cd2Sjpk {
51045916cd2Sjpk tsol_gcgrp_t *gcgrp = gc->gc_grp;
51145916cd2Sjpk
51245916cd2Sjpk ASSERT(gcgrp != NULL);
51345916cd2Sjpk ASSERT(RW_WRITE_HELD(&gcgrp->gcgrp_rwlock));
51445916cd2Sjpk ASSERT(gc->gc_refcnt == 0);
51545916cd2Sjpk
51645916cd2Sjpk if (gc->gc_prev != NULL)
51745916cd2Sjpk gc->gc_prev->gc_next = gc->gc_next;
51845916cd2Sjpk else
51945916cd2Sjpk gcgrp->gcgrp_head = gc->gc_next;
52045916cd2Sjpk if (gc->gc_next != NULL)
52145916cd2Sjpk gc->gc_next->gc_prev = gc->gc_prev;
52245916cd2Sjpk else
52345916cd2Sjpk gcgrp->gcgrp_tail = gc->gc_prev;
52445916cd2Sjpk ASSERT(gcgrp->gcgrp_count > 0);
52545916cd2Sjpk gcgrp->gcgrp_count--;
52645916cd2Sjpk
52745916cd2Sjpk /* drop lock before it's destroyed */
52845916cd2Sjpk rw_exit(&gcgrp->gcgrp_rwlock);
52945916cd2Sjpk
53045916cd2Sjpk DTRACE_PROBE3(tx__gcdb__log__info__gc__remove, char *,
53145916cd2Sjpk "removed inactive gc(1) from gcgrp(2)",
53245916cd2Sjpk tsol_gc_t *, gc, tsol_gcgrp_t *, gcgrp);
53345916cd2Sjpk
53445916cd2Sjpk GCGRP_REFRELE(gcgrp);
53545916cd2Sjpk
53645916cd2Sjpk gc->gc_grp = NULL;
53745916cd2Sjpk gc->gc_prev = gc->gc_next = NULL;
53845916cd2Sjpk
53945916cd2Sjpk if (gc->gc_db != NULL)
54045916cd2Sjpk GCDB_REFRELE(gc->gc_db);
54145916cd2Sjpk
54245916cd2Sjpk kmem_free(gc, sizeof (*gc));
54345916cd2Sjpk }
54445916cd2Sjpk
54545916cd2Sjpk tsol_gcgrp_t *
gcgrp_lookup(tsol_gcgrp_addr_t * ga,boolean_t alloc)54645916cd2Sjpk gcgrp_lookup(tsol_gcgrp_addr_t *ga, boolean_t alloc)
54745916cd2Sjpk {
54845916cd2Sjpk tsol_gcgrp_t *gcgrp = NULL;
54945916cd2Sjpk mod_hash_t *hashp;
55045916cd2Sjpk
55145916cd2Sjpk ASSERT(ga->ga_af == AF_INET || ga->ga_af == AF_INET6);
55245916cd2Sjpk
55345916cd2Sjpk hashp = (ga->ga_af == AF_INET) ? gcgrp4_hash : gcgrp6_hash;
55445916cd2Sjpk
55545916cd2Sjpk mutex_enter(&gcgrp_lock);
55645916cd2Sjpk if (mod_hash_find(hashp, (mod_hash_key_t)ga,
55745916cd2Sjpk (mod_hash_val_t *)&gcgrp) == 0) {
55845916cd2Sjpk gcgrp->gcgrp_refcnt++;
55945916cd2Sjpk ASSERT(gcgrp->gcgrp_refcnt != 0);
56045916cd2Sjpk
56145916cd2Sjpk DTRACE_PROBE3(tx__gcdb__log__info__gcgrp__lookup, char *,
56245916cd2Sjpk "found gcgrp(1) in hash(2)", tsol_gcgrp_t *, gcgrp,
56345916cd2Sjpk mod_hash_t *, hashp);
56445916cd2Sjpk
56545916cd2Sjpk } else if (alloc) {
56645916cd2Sjpk gcgrp = kmem_zalloc(sizeof (*gcgrp), KM_NOSLEEP);
56745916cd2Sjpk if (gcgrp != NULL) {
56845916cd2Sjpk gcgrp->gcgrp_refcnt = 1;
56945916cd2Sjpk rw_init(&gcgrp->gcgrp_rwlock, NULL, RW_DEFAULT, NULL);
57045916cd2Sjpk bcopy(ga, &gcgrp->gcgrp_addr, sizeof (*ga));
57145916cd2Sjpk
57245916cd2Sjpk if (mod_hash_insert(hashp,
57345916cd2Sjpk (mod_hash_key_t)&gcgrp->gcgrp_addr,
57445916cd2Sjpk (mod_hash_val_t)gcgrp) != 0) {
57545916cd2Sjpk mutex_exit(&gcgrp_lock);
57645916cd2Sjpk kmem_free(gcgrp, sizeof (*gcgrp));
57745916cd2Sjpk return (NULL);
57845916cd2Sjpk }
57945916cd2Sjpk
58045916cd2Sjpk DTRACE_PROBE3(tx__gcdb__log__info__gcgrp__insert,
58145916cd2Sjpk char *, "inserted gcgrp(1) in hash(2)",
58245916cd2Sjpk tsol_gcgrp_t *, gcgrp, mod_hash_t *, hashp);
58345916cd2Sjpk }
58445916cd2Sjpk }
58545916cd2Sjpk mutex_exit(&gcgrp_lock);
58645916cd2Sjpk return (gcgrp);
58745916cd2Sjpk }
58845916cd2Sjpk
58945916cd2Sjpk void
gcgrp_inactive(tsol_gcgrp_t * gcgrp)59045916cd2Sjpk gcgrp_inactive(tsol_gcgrp_t *gcgrp)
59145916cd2Sjpk {
59245916cd2Sjpk tsol_gcgrp_addr_t *ga;
59345916cd2Sjpk mod_hash_t *hashp;
59445916cd2Sjpk
59545916cd2Sjpk ASSERT(MUTEX_HELD(&gcgrp_lock));
59645916cd2Sjpk ASSERT(gcgrp != NULL && gcgrp->gcgrp_refcnt == 0);
59745916cd2Sjpk ASSERT(gcgrp->gcgrp_head == NULL && gcgrp->gcgrp_count == 0);
59845916cd2Sjpk
59945916cd2Sjpk ga = &gcgrp->gcgrp_addr;
60045916cd2Sjpk ASSERT(ga->ga_af == AF_INET || ga->ga_af == AF_INET6);
60145916cd2Sjpk
60245916cd2Sjpk hashp = (ga->ga_af == AF_INET) ? gcgrp4_hash : gcgrp6_hash;
60345916cd2Sjpk (void) mod_hash_remove(hashp, (mod_hash_key_t)ga,
60445916cd2Sjpk (mod_hash_val_t *)&gcgrp);
60545916cd2Sjpk rw_destroy(&gcgrp->gcgrp_rwlock);
60645916cd2Sjpk
60745916cd2Sjpk DTRACE_PROBE3(tx__gcdb__log__info__gcgrp__remove, char *,
60845916cd2Sjpk "removed inactive gcgrp(1) from hash(2)",
60945916cd2Sjpk tsol_gcgrp_t *, gcgrp, mod_hash_t *, hashp);
61045916cd2Sjpk
61145916cd2Sjpk kmem_free(gcgrp, sizeof (*gcgrp));
61245916cd2Sjpk }
61345916cd2Sjpk
6145d3b8cb7SBill Sommerfeld
6155d3b8cb7SBill Sommerfeld /*
6165d3b8cb7SBill Sommerfeld * Assign a sensitivity label to inbound traffic which arrived without
6175d3b8cb7SBill Sommerfeld * an explicit on-the-wire label.
6185d3b8cb7SBill Sommerfeld *
6195d3b8cb7SBill Sommerfeld * In the case of CIPSO-type hosts, we assume packets arriving without
6205d3b8cb7SBill Sommerfeld * a label are at the most sensitive label known for the host, most
6215d3b8cb7SBill Sommerfeld * likely involving out-of-band key management traffic (such as IKE,
6225d3b8cb7SBill Sommerfeld * etc.,)
6235d3b8cb7SBill Sommerfeld */
6245d3b8cb7SBill Sommerfeld static boolean_t
tsol_find_unlabeled_label(tsol_tpc_t * rhtp,bslabel_t * sl,uint32_t * doi)6255d3b8cb7SBill Sommerfeld tsol_find_unlabeled_label(tsol_tpc_t *rhtp, bslabel_t *sl, uint32_t *doi)
6265d3b8cb7SBill Sommerfeld {
6275d3b8cb7SBill Sommerfeld *doi = rhtp->tpc_tp.tp_doi;
6285d3b8cb7SBill Sommerfeld switch (rhtp->tpc_tp.host_type) {
6295d3b8cb7SBill Sommerfeld case UNLABELED:
6305d3b8cb7SBill Sommerfeld *sl = rhtp->tpc_tp.tp_def_label;
6315d3b8cb7SBill Sommerfeld break;
6325d3b8cb7SBill Sommerfeld case SUN_CIPSO:
6335d3b8cb7SBill Sommerfeld *sl = rhtp->tpc_tp.tp_sl_range_cipso.upper_bound;
6345d3b8cb7SBill Sommerfeld break;
6355d3b8cb7SBill Sommerfeld default:
6365d3b8cb7SBill Sommerfeld return (B_FALSE);
6375d3b8cb7SBill Sommerfeld }
6385d3b8cb7SBill Sommerfeld setbltype(sl, SUN_SL_ID);
6395d3b8cb7SBill Sommerfeld return (B_TRUE);
6405d3b8cb7SBill Sommerfeld }
6415d3b8cb7SBill Sommerfeld
64245916cd2Sjpk /*
64345916cd2Sjpk * Converts CIPSO option to sensitivity label.
64445916cd2Sjpk * Validity checks based on restrictions defined in
64545916cd2Sjpk * COMMERCIAL IP SECURITY OPTION (CIPSO 2.2) (draft-ietf-cipso-ipsecurity)
64645916cd2Sjpk */
64745916cd2Sjpk static boolean_t
cipso_to_sl(const uchar_t * option,bslabel_t * sl)64845916cd2Sjpk cipso_to_sl(const uchar_t *option, bslabel_t *sl)
64945916cd2Sjpk {
65045916cd2Sjpk const struct cipso_option *co = (const struct cipso_option *)option;
65145916cd2Sjpk const struct cipso_tag_type_1 *tt1;
65245916cd2Sjpk
65345916cd2Sjpk tt1 = (struct cipso_tag_type_1 *)&co->cipso_tag_type[0];
65445916cd2Sjpk if (tt1->tag_type != 1 ||
65545916cd2Sjpk tt1->tag_length < TSOL_TT1_MIN_LENGTH ||
65645916cd2Sjpk tt1->tag_length > TSOL_TT1_MAX_LENGTH ||
65745916cd2Sjpk tt1->tag_length + TSOL_CIPSO_TAG_OFFSET > co->cipso_length)
65845916cd2Sjpk return (B_FALSE);
65945916cd2Sjpk
66045916cd2Sjpk bsllow(sl); /* assumed: sets compartments to all zeroes */
66145916cd2Sjpk LCLASS_SET((_bslabel_impl_t *)sl, tt1->tag_sl);
66245916cd2Sjpk bcopy(tt1->tag_cat, &((_bslabel_impl_t *)sl)->compartments,
66345916cd2Sjpk tt1->tag_length - TSOL_TT1_MIN_LENGTH);
66445916cd2Sjpk return (B_TRUE);
66545916cd2Sjpk }
66645916cd2Sjpk
66745916cd2Sjpk /*
668bd670b35SErik Nordmark * If present, parse the CIPSO label in the incoming packet and
669bd670b35SErik Nordmark * construct a ts_label_t that reflects the CIPSO label and put it in
670bd670b35SErik Nordmark * the ip_recv_attr_t. Later as the packet flows up through the stack any
6715d3b8cb7SBill Sommerfeld * code that needs to examine the packet label can inspect the label
672bd670b35SErik Nordmark * from the ira_tsl. This function is
673bd670b35SErik Nordmark * called right in ip_input for all packets, i.e. locally destined and
674bd670b35SErik Nordmark * to be forwarded packets. The forwarding path needs to examine the label
675bd670b35SErik Nordmark * to determine how to forward the packet.
67645916cd2Sjpk *
677c4e55c13Sken Powell - Sun Microsystem * This routine pulls all message text up into the first mblk.
678c4e55c13Sken Powell - Sun Microsystem * For IPv4, only the first 20 bytes of the IP header are guaranteed
679c4e55c13Sken Powell - Sun Microsystem * to exist. For IPv6, only the IPv6 header is guaranteed to exist.
68045916cd2Sjpk */
68145916cd2Sjpk boolean_t
tsol_get_pkt_label(mblk_t * mp,int version,ip_recv_attr_t * ira)682bd670b35SErik Nordmark tsol_get_pkt_label(mblk_t *mp, int version, ip_recv_attr_t *ira)
68345916cd2Sjpk {
6845d3b8cb7SBill Sommerfeld tsol_tpc_t *src_rhtp = NULL;
68545916cd2Sjpk uchar_t *opt_ptr = NULL;
68645916cd2Sjpk const ipha_t *ipha;
68745916cd2Sjpk bslabel_t sl;
68845916cd2Sjpk uint32_t doi;
68945916cd2Sjpk tsol_ip_label_t label_type;
6905d3b8cb7SBill Sommerfeld uint32_t label_flags = 0; /* flags to set in label */
69145916cd2Sjpk const cipso_option_t *co;
69245916cd2Sjpk const void *src;
69345916cd2Sjpk const ip6_t *ip6h;
694de8c4a14SErik Nordmark cred_t *credp;
6955d3b8cb7SBill Sommerfeld int proto;
69645916cd2Sjpk
69745916cd2Sjpk ASSERT(DB_TYPE(mp) == M_DATA);
69845916cd2Sjpk
6995f9878b0Sken Powell - Sun Microsystem if (mp->b_cont != NULL && !pullupmsg(mp, -1))
7005f9878b0Sken Powell - Sun Microsystem return (B_FALSE);
7015f9878b0Sken Powell - Sun Microsystem
70245916cd2Sjpk if (version == IPV4_VERSION) {
703c4e55c13Sken Powell - Sun Microsystem ASSERT(MBLKL(mp) >= IP_SIMPLE_HDR_LENGTH);
70445916cd2Sjpk ipha = (const ipha_t *)mp->b_rptr;
70545916cd2Sjpk src = &ipha->ipha_src;
706c4e55c13Sken Powell - Sun Microsystem if (!tsol_get_option_v4(mp, &label_type, &opt_ptr))
707c4e55c13Sken Powell - Sun Microsystem return (B_FALSE);
70845916cd2Sjpk } else {
709c4e55c13Sken Powell - Sun Microsystem ASSERT(MBLKL(mp) >= IPV6_HDR_LEN);
71045916cd2Sjpk ip6h = (const ip6_t *)mp->b_rptr;
71145916cd2Sjpk src = &ip6h->ip6_src;
712c4e55c13Sken Powell - Sun Microsystem if (!tsol_get_option_v6(mp, &label_type, &opt_ptr))
713c4e55c13Sken Powell - Sun Microsystem return (B_FALSE);
71445916cd2Sjpk }
71545916cd2Sjpk
71645916cd2Sjpk switch (label_type) {
71745916cd2Sjpk case OPT_CIPSO:
71845916cd2Sjpk /*
71945916cd2Sjpk * Convert the CIPSO label to the internal format
72045916cd2Sjpk * and attach it to the dblk cred.
72145916cd2Sjpk * Validity checks based on restrictions defined in
72245916cd2Sjpk * COMMERCIAL IP SECURITY OPTION (CIPSO 2.2)
72345916cd2Sjpk * (draft-ietf-cipso-ipsecurity)
72445916cd2Sjpk */
72545916cd2Sjpk if (version == IPV6_VERSION && ip6opt_ls == 0)
72645916cd2Sjpk return (B_FALSE);
72745916cd2Sjpk co = (const struct cipso_option *)opt_ptr;
72845916cd2Sjpk if ((co->cipso_length <
72945916cd2Sjpk TSOL_CIPSO_TAG_OFFSET + TSOL_TT1_MIN_LENGTH) ||
73045916cd2Sjpk (co->cipso_length > IP_MAX_OPT_LENGTH))
73145916cd2Sjpk return (B_FALSE);
73245916cd2Sjpk bcopy(co->cipso_doi, &doi, sizeof (doi));
73345916cd2Sjpk doi = ntohl(doi);
73445916cd2Sjpk if (!cipso_to_sl(opt_ptr, &sl))
73545916cd2Sjpk return (B_FALSE);
73645916cd2Sjpk setbltype(&sl, SUN_SL_ID);
7375d3b8cb7SBill Sommerfeld
7385d3b8cb7SBill Sommerfeld /*
7395d3b8cb7SBill Sommerfeld * If the source was unlabeled, then flag as such,
7405d3b8cb7SBill Sommerfeld * (since CIPSO routers may add headers)
7415d3b8cb7SBill Sommerfeld */
7425d3b8cb7SBill Sommerfeld
7435d3b8cb7SBill Sommerfeld if ((src_rhtp = find_tpc(src, version, B_FALSE)) == NULL)
7445d3b8cb7SBill Sommerfeld return (B_FALSE);
7455d3b8cb7SBill Sommerfeld
7465d3b8cb7SBill Sommerfeld if (src_rhtp->tpc_tp.host_type == UNLABELED)
7475d3b8cb7SBill Sommerfeld label_flags = TSLF_UNLABELED;
7485d3b8cb7SBill Sommerfeld
7495d3b8cb7SBill Sommerfeld TPC_RELE(src_rhtp);
7505d3b8cb7SBill Sommerfeld
75145916cd2Sjpk break;
75245916cd2Sjpk
75345916cd2Sjpk case OPT_NONE:
75445916cd2Sjpk /*
7555d3b8cb7SBill Sommerfeld * Handle special cases that may not be labeled, even
75645916cd2Sjpk * though the sending system may otherwise be configured as
75745916cd2Sjpk * labeled.
75845916cd2Sjpk * - IGMP
75945916cd2Sjpk * - IPv4 ICMP Router Discovery
76045916cd2Sjpk * - IPv6 Neighbor Discovery
7615d3b8cb7SBill Sommerfeld * - IPsec ESP
76245916cd2Sjpk */
76345916cd2Sjpk if (version == IPV4_VERSION) {
7645d3b8cb7SBill Sommerfeld proto = ipha->ipha_protocol;
7655d3b8cb7SBill Sommerfeld if (proto == IPPROTO_IGMP)
76645916cd2Sjpk return (B_TRUE);
7675d3b8cb7SBill Sommerfeld if (proto == IPPROTO_ICMP) {
76845916cd2Sjpk const struct icmp *icmp = (const struct icmp *)
76945916cd2Sjpk (mp->b_rptr + IPH_HDR_LENGTH(ipha));
77045916cd2Sjpk
771c4e55c13Sken Powell - Sun Microsystem if ((uchar_t *)icmp + ICMP_MINLEN > mp->b_wptr)
77245916cd2Sjpk return (B_FALSE);
77345916cd2Sjpk if (icmp->icmp_type == ICMP_ROUTERADVERT ||
77445916cd2Sjpk icmp->icmp_type == ICMP_ROUTERSOLICIT)
77545916cd2Sjpk return (B_TRUE);
77645916cd2Sjpk }
77745916cd2Sjpk } else {
7785d3b8cb7SBill Sommerfeld proto = ip6h->ip6_nxt;
7795d3b8cb7SBill Sommerfeld if (proto == IPPROTO_ICMPV6) {
78045916cd2Sjpk const icmp6_t *icmp6 = (const icmp6_t *)
78145916cd2Sjpk (mp->b_rptr + IPV6_HDR_LEN);
78245916cd2Sjpk
78345916cd2Sjpk if ((uchar_t *)icmp6 + ICMP6_MINLEN >
784c4e55c13Sken Powell - Sun Microsystem mp->b_wptr)
78545916cd2Sjpk return (B_FALSE);
78645916cd2Sjpk if (icmp6->icmp6_type >= MLD_LISTENER_QUERY &&
78745916cd2Sjpk icmp6->icmp6_type <= ICMP6_MAX_INFO_TYPE)
78845916cd2Sjpk return (B_TRUE);
78945916cd2Sjpk }
79045916cd2Sjpk }
79145916cd2Sjpk
79245916cd2Sjpk /*
79345916cd2Sjpk * Look up the tnrhtp database and get the implicit label
7945d3b8cb7SBill Sommerfeld * that is associated with the sending host and attach
79545916cd2Sjpk * it to the packet.
79645916cd2Sjpk */
79745916cd2Sjpk if ((src_rhtp = find_tpc(src, version, B_FALSE)) == NULL)
79845916cd2Sjpk return (B_FALSE);
79945916cd2Sjpk
8005d3b8cb7SBill Sommerfeld /*
8015d3b8cb7SBill Sommerfeld * If peer is label-aware, mark as "implicit" rather than
8025d3b8cb7SBill Sommerfeld * "unlabeled" to cause appropriate mac-exempt processing
8035d3b8cb7SBill Sommerfeld * to happen.
8045d3b8cb7SBill Sommerfeld */
8055d3b8cb7SBill Sommerfeld if (src_rhtp->tpc_tp.host_type == SUN_CIPSO)
8065d3b8cb7SBill Sommerfeld label_flags = TSLF_IMPLICIT_IN;
8075d3b8cb7SBill Sommerfeld else if (src_rhtp->tpc_tp.host_type == UNLABELED)
8085d3b8cb7SBill Sommerfeld label_flags = TSLF_UNLABELED;
8095d3b8cb7SBill Sommerfeld else {
8105d3b8cb7SBill Sommerfeld DTRACE_PROBE2(tx__get__pkt__label, char *,
8115d3b8cb7SBill Sommerfeld "template(1) has unknown hosttype",
8125d3b8cb7SBill Sommerfeld tsol_tpc_t *, src_rhtp);
81345916cd2Sjpk }
81445916cd2Sjpk
8155d3b8cb7SBill Sommerfeld
8165d3b8cb7SBill Sommerfeld if (!tsol_find_unlabeled_label(src_rhtp, &sl, &doi)) {
8175d3b8cb7SBill Sommerfeld TPC_RELE(src_rhtp);
8185d3b8cb7SBill Sommerfeld return (B_FALSE);
8195d3b8cb7SBill Sommerfeld }
82045916cd2Sjpk TPC_RELE(src_rhtp);
82145916cd2Sjpk break;
82245916cd2Sjpk
82345916cd2Sjpk default:
82445916cd2Sjpk return (B_FALSE);
82545916cd2Sjpk }
82645916cd2Sjpk
827bd670b35SErik Nordmark if (ira->ira_cred == NULL) {
828de8c4a14SErik Nordmark credp = newcred_from_bslabel(&sl, doi, KM_NOSLEEP);
829bd670b35SErik Nordmark if (credp == NULL)
830bd670b35SErik Nordmark return (B_FALSE);
83145916cd2Sjpk } else {
8320e0e37a8SErik Nordmark credp = copycred_from_bslabel(ira->ira_cred, &sl, doi,
83345916cd2Sjpk KM_NOSLEEP);
8340e0e37a8SErik Nordmark if (credp == NULL)
835bd670b35SErik Nordmark return (B_FALSE);
836bd670b35SErik Nordmark if (ira->ira_free_flags & IRA_FREE_CRED) {
837bd670b35SErik Nordmark crfree(ira->ira_cred);
838bd670b35SErik Nordmark ira->ira_free_flags &= ~IRA_FREE_CRED;
839bd670b35SErik Nordmark ira->ira_cred = NULL;
840bd670b35SErik Nordmark }
84145916cd2Sjpk }
8425d3b8cb7SBill Sommerfeld
843bd670b35SErik Nordmark /*
844bd670b35SErik Nordmark * Put the label in ira_tsl for convinience, while keeping
845bd670b35SErik Nordmark * the cred in ira_cred for getpeerucred which is used to get
846bd670b35SErik Nordmark * labels with TX.
847bd670b35SErik Nordmark * Note: no explicit refcnt/free_flag for ira_tsl. The free_flag
848bd670b35SErik Nordmark * for IRA_FREE_CRED is sufficient for both.
849bd670b35SErik Nordmark */
850bd670b35SErik Nordmark ira->ira_tsl = crgetlabel(credp);
851bd670b35SErik Nordmark ira->ira_cred = credp;
852bd670b35SErik Nordmark ira->ira_free_flags |= IRA_FREE_CRED;
8535d3b8cb7SBill Sommerfeld
854bd670b35SErik Nordmark ira->ira_tsl->tsl_flags |= label_flags;
85545916cd2Sjpk return (B_TRUE);
85645916cd2Sjpk }
85745916cd2Sjpk
85845916cd2Sjpk /*
85945916cd2Sjpk * This routine determines whether the given packet should be accepted locally.
86045916cd2Sjpk * It does a range/set check on the packet's label by looking up the given
86145916cd2Sjpk * address in the remote host database.
86245916cd2Sjpk */
86345916cd2Sjpk boolean_t
tsol_receive_local(const mblk_t * mp,const void * addr,uchar_t version,ip_recv_attr_t * ira,const conn_t * connp)86445916cd2Sjpk tsol_receive_local(const mblk_t *mp, const void *addr, uchar_t version,
865bd670b35SErik Nordmark ip_recv_attr_t *ira, const conn_t *connp)
86645916cd2Sjpk {
86745916cd2Sjpk const cred_t *credp;
86845916cd2Sjpk ts_label_t *plabel, *conn_plabel;
86945916cd2Sjpk tsol_tpc_t *tp;
87045916cd2Sjpk boolean_t retv;
87145916cd2Sjpk const bslabel_t *label, *conn_label;
872bd670b35SErik Nordmark boolean_t shared_addr = (ira->ira_flags & IRAF_TX_SHARED_ADDR);
87345916cd2Sjpk
87445916cd2Sjpk /*
875bd670b35SErik Nordmark * tsol_get_pkt_label intentionally avoids the labeling process for:
876bd670b35SErik Nordmark * - IPv6 router and neighbor discovery as well as redirects.
877bd670b35SErik Nordmark * - MLD packets. (Anything between ICMPv6 code 130 and 138.)
878bd670b35SErik Nordmark * - IGMP packets.
879bd670b35SErik Nordmark * - IPv4 router discovery.
8800e0e37a8SErik Nordmark * In those cases ira_cred is NULL.
88145916cd2Sjpk */
882bd670b35SErik Nordmark credp = ira->ira_cred;
883bd670b35SErik Nordmark if (credp == NULL)
88445916cd2Sjpk return (B_TRUE);
88545916cd2Sjpk
88645916cd2Sjpk /*
88745916cd2Sjpk * If this packet is from the inside (not a remote host) and has the
88845916cd2Sjpk * same zoneid as the selected destination, then no checks are
88945916cd2Sjpk * necessary. Membership in the zone is enough proof. This is
89045916cd2Sjpk * intended to be a hot path through this function.
891bd670b35SErik Nordmark * Note: Using crgetzone here is ok since the peer is local.
89245916cd2Sjpk */
89345916cd2Sjpk if (!crisremote(credp) &&
89445916cd2Sjpk crgetzone(credp) == crgetzone(connp->conn_cred))
89545916cd2Sjpk return (B_TRUE);
89645916cd2Sjpk
897bd670b35SErik Nordmark plabel = ira->ira_tsl;
89845916cd2Sjpk conn_plabel = crgetlabel(connp->conn_cred);
89945916cd2Sjpk ASSERT(plabel != NULL && conn_plabel != NULL);
90045916cd2Sjpk
90145916cd2Sjpk label = label2bslabel(plabel);
902bd670b35SErik Nordmark conn_label = label2bslabel(conn_plabel);
90345916cd2Sjpk
9045d3b8cb7SBill Sommerfeld
9055d3b8cb7SBill Sommerfeld /*
9065d3b8cb7SBill Sommerfeld * Implicitly labeled packets from label-aware sources
9075d3b8cb7SBill Sommerfeld * go only to privileged receivers
9085d3b8cb7SBill Sommerfeld */
9095d3b8cb7SBill Sommerfeld if ((plabel->tsl_flags & TSLF_IMPLICIT_IN) &&
9105d3b8cb7SBill Sommerfeld (connp->conn_mac_mode != CONN_MAC_IMPLICIT)) {
9115d3b8cb7SBill Sommerfeld DTRACE_PROBE3(tx__ip__log__drop__receivelocal__mac_impl,
9125d3b8cb7SBill Sommerfeld char *,
9135d3b8cb7SBill Sommerfeld "implicitly labeled packet mp(1) for conn(2) "
9145d3b8cb7SBill Sommerfeld "which isn't in implicit mac mode",
9155d3b8cb7SBill Sommerfeld mblk_t *, mp, conn_t *, connp);
9165d3b8cb7SBill Sommerfeld
9175d3b8cb7SBill Sommerfeld return (B_FALSE);
9185d3b8cb7SBill Sommerfeld }
9195d3b8cb7SBill Sommerfeld
9205d3b8cb7SBill Sommerfeld
92145916cd2Sjpk /*
92245916cd2Sjpk * MLPs are always validated using the range and set of the local
92345916cd2Sjpk * address, even when the remote host is unlabeled.
92445916cd2Sjpk */
92545916cd2Sjpk if (connp->conn_mlp_type == mlptBoth ||
92645916cd2Sjpk /* LINTED: no consequent */
92745916cd2Sjpk connp->conn_mlp_type == (shared_addr ? mlptShared : mlptPrivate)) {
92845916cd2Sjpk ;
92945916cd2Sjpk
93045916cd2Sjpk /*
93145916cd2Sjpk * If this is a packet from an unlabeled sender, then we must apply
93245916cd2Sjpk * different rules. If the label is equal to the zone's label, then
93345916cd2Sjpk * it's allowed. If it's not equal, but the zone is either the global
93445916cd2Sjpk * zone or the label is dominated by the zone's label, then allow it
93545916cd2Sjpk * as long as it's in the range configured for the destination.
93645916cd2Sjpk */
93745916cd2Sjpk } else if (plabel->tsl_flags & TSLF_UNLABELED) {
93845916cd2Sjpk if (plabel->tsl_doi == conn_plabel->tsl_doi &&
93945916cd2Sjpk blequal(label, conn_label))
94045916cd2Sjpk return (B_TRUE);
94145916cd2Sjpk
9425d3b8cb7SBill Sommerfeld if ((connp->conn_mac_mode == CONN_MAC_DEFAULT) ||
943bd670b35SErik Nordmark (!connp->conn_zone_is_global &&
94445916cd2Sjpk (plabel->tsl_doi != conn_plabel->tsl_doi ||
94545916cd2Sjpk !bldominates(conn_label, label)))) {
94645916cd2Sjpk DTRACE_PROBE3(
94745916cd2Sjpk tx__ip__log__drop__receivelocal__mac_unl,
94845916cd2Sjpk char *,
94945916cd2Sjpk "unlabeled packet mp(1) fails mac for conn(2)",
95045916cd2Sjpk mblk_t *, mp, conn_t *, connp);
95145916cd2Sjpk return (B_FALSE);
95245916cd2Sjpk }
95345916cd2Sjpk
95445916cd2Sjpk /*
955e071b5fbSkp158701 * If this is a packet from a labeled sender, verify the
956e071b5fbSkp158701 * label on the packet matches the connection label.
95745916cd2Sjpk */
958e071b5fbSkp158701 } else {
959e071b5fbSkp158701 if (plabel->tsl_doi != conn_plabel->tsl_doi ||
960e071b5fbSkp158701 !blequal(label, conn_label)) {
96145916cd2Sjpk DTRACE_PROBE3(tx__ip__log__drop__receivelocal__mac__slp,
962e071b5fbSkp158701 char *,
963e071b5fbSkp158701 "packet mp(1) failed label match to SLP conn(2)",
96445916cd2Sjpk mblk_t *, mp, conn_t *, connp);
96545916cd2Sjpk return (B_FALSE);
96645916cd2Sjpk }
967e071b5fbSkp158701 /*
968e071b5fbSkp158701 * No further checks will be needed if this is a zone-
969e071b5fbSkp158701 * specific address because (1) The process for bringing up
970e071b5fbSkp158701 * the interface ensures the zone's label is within the zone-
971e071b5fbSkp158701 * specific address's valid label range; (2) For cases where
972e071b5fbSkp158701 * the conn is bound to the unspecified addresses, ip fanout
973e071b5fbSkp158701 * logic ensures conn's zoneid equals the dest addr's zoneid;
974e071b5fbSkp158701 * (3) Mac-exempt and mlp logic above already handle all
975e071b5fbSkp158701 * cases where the zone label may not be the same as the
976e071b5fbSkp158701 * conn label.
977e071b5fbSkp158701 */
978e071b5fbSkp158701 if (!shared_addr)
979e071b5fbSkp158701 return (B_TRUE);
980e071b5fbSkp158701 }
98145916cd2Sjpk
98245916cd2Sjpk tp = find_tpc(addr, version, B_FALSE);
98345916cd2Sjpk if (tp == NULL) {
98445916cd2Sjpk DTRACE_PROBE3(tx__ip__log__drop__receivelocal__no__tnr,
98545916cd2Sjpk char *, "dropping mp(1), host(2) lacks entry",
98645916cd2Sjpk mblk_t *, mp, void *, addr);
98745916cd2Sjpk return (B_FALSE);
98845916cd2Sjpk }
98945916cd2Sjpk
99045916cd2Sjpk /*
99145916cd2Sjpk * The local host address should not be unlabeled at this point. The
99245916cd2Sjpk * only way this can happen is that the destination isn't unicast. We
99345916cd2Sjpk * assume that the packet should not have had a label, and thus should
99445916cd2Sjpk * have been handled by the TSLF_UNLABELED logic above.
99545916cd2Sjpk */
99645916cd2Sjpk if (tp->tpc_tp.host_type == UNLABELED) {
99745916cd2Sjpk retv = B_FALSE;
99845916cd2Sjpk DTRACE_PROBE3(tx__ip__log__drop__receivelocal__flag, char *,
99945916cd2Sjpk "mp(1) unlabeled source, but tp is not unlabeled.",
100045916cd2Sjpk mblk_t *, mp, tsol_tpc_t *, tp);
100145916cd2Sjpk
100245916cd2Sjpk } else if (tp->tpc_tp.host_type != SUN_CIPSO) {
100345916cd2Sjpk retv = B_FALSE;
100445916cd2Sjpk DTRACE_PROBE3(tx__ip__log__drop__receivelocal__tptype, char *,
100545916cd2Sjpk "delivering mp(1), found unrecognized tpc(2) type.",
100645916cd2Sjpk mblk_t *, mp, tsol_tpc_t *, tp);
100745916cd2Sjpk
100845916cd2Sjpk } else if (plabel->tsl_doi != tp->tpc_tp.tp_doi) {
100945916cd2Sjpk retv = B_FALSE;
101045916cd2Sjpk DTRACE_PROBE3(tx__ip__log__drop__receivelocal__mac, char *,
101145916cd2Sjpk "mp(1) could not be delievered to tp(2), doi mismatch",
101245916cd2Sjpk mblk_t *, mp, tsol_tpc_t *, tp);
101345916cd2Sjpk
101445916cd2Sjpk } else if (!_blinrange(label, &tp->tpc_tp.tp_sl_range_cipso) &&
101545916cd2Sjpk !blinlset(label, tp->tpc_tp.tp_sl_set_cipso)) {
101645916cd2Sjpk retv = B_FALSE;
101745916cd2Sjpk DTRACE_PROBE3(tx__ip__log__drop__receivelocal__mac, char *,
101845916cd2Sjpk "mp(1) could not be delievered to tp(2), bad mac",
101945916cd2Sjpk mblk_t *, mp, tsol_tpc_t *, tp);
102045916cd2Sjpk } else {
102145916cd2Sjpk retv = B_TRUE;
102245916cd2Sjpk }
102345916cd2Sjpk
102445916cd2Sjpk TPC_RELE(tp);
102545916cd2Sjpk
102645916cd2Sjpk return (retv);
102745916cd2Sjpk }
102845916cd2Sjpk
102945916cd2Sjpk boolean_t
tsol_can_accept_raw(mblk_t * mp,ip_recv_attr_t * ira,boolean_t check_host)1030bd670b35SErik Nordmark tsol_can_accept_raw(mblk_t *mp, ip_recv_attr_t *ira, boolean_t check_host)
103145916cd2Sjpk {
103245916cd2Sjpk ts_label_t *plabel = NULL;
103345916cd2Sjpk tsol_tpc_t *src_rhtp, *dst_rhtp;
103445916cd2Sjpk boolean_t retv;
103545916cd2Sjpk
1036bd670b35SErik Nordmark plabel = ira->ira_tsl;
103745916cd2Sjpk
103845916cd2Sjpk /* We are bootstrapping or the internal template was never deleted */
103945916cd2Sjpk if (plabel == NULL)
104045916cd2Sjpk return (B_TRUE);
104145916cd2Sjpk
104245916cd2Sjpk if (IPH_HDR_VERSION(mp->b_rptr) == IPV4_VERSION) {
104345916cd2Sjpk ipha_t *ipha = (ipha_t *)mp->b_rptr;
104445916cd2Sjpk
104545916cd2Sjpk src_rhtp = find_tpc(&ipha->ipha_src, IPV4_VERSION,
104645916cd2Sjpk B_FALSE);
104745916cd2Sjpk if (src_rhtp == NULL)
104845916cd2Sjpk return (B_FALSE);
104945916cd2Sjpk dst_rhtp = find_tpc(&ipha->ipha_dst, IPV4_VERSION,
105045916cd2Sjpk B_FALSE);
105145916cd2Sjpk } else {
105245916cd2Sjpk ip6_t *ip6h = (ip6_t *)mp->b_rptr;
105345916cd2Sjpk
105445916cd2Sjpk src_rhtp = find_tpc(&ip6h->ip6_src, IPV6_VERSION,
105545916cd2Sjpk B_FALSE);
105645916cd2Sjpk if (src_rhtp == NULL)
105745916cd2Sjpk return (B_FALSE);
105845916cd2Sjpk dst_rhtp = find_tpc(&ip6h->ip6_dst, IPV6_VERSION,
105945916cd2Sjpk B_FALSE);
106045916cd2Sjpk }
106145916cd2Sjpk if (dst_rhtp == NULL) {
106245916cd2Sjpk TPC_RELE(src_rhtp);
106345916cd2Sjpk return (B_FALSE);
106445916cd2Sjpk }
106545916cd2Sjpk
106645916cd2Sjpk if (label2doi(plabel) != src_rhtp->tpc_tp.tp_doi) {
106745916cd2Sjpk retv = B_FALSE;
106845916cd2Sjpk
106945916cd2Sjpk /*
107045916cd2Sjpk * Check that the packet's label is in the correct range for labeled
107145916cd2Sjpk * sender, or is equal to the default label for unlabeled sender.
107245916cd2Sjpk */
107345916cd2Sjpk } else if ((src_rhtp->tpc_tp.host_type != UNLABELED &&
107445916cd2Sjpk !_blinrange(label2bslabel(plabel),
107545916cd2Sjpk &src_rhtp->tpc_tp.tp_sl_range_cipso) &&
107645916cd2Sjpk !blinlset(label2bslabel(plabel),
107745916cd2Sjpk src_rhtp->tpc_tp.tp_sl_set_cipso)) ||
107845916cd2Sjpk (src_rhtp->tpc_tp.host_type == UNLABELED &&
107945916cd2Sjpk !blequal(&plabel->tsl_label, &src_rhtp->tpc_tp.tp_def_label))) {
108045916cd2Sjpk retv = B_FALSE;
108145916cd2Sjpk
108245916cd2Sjpk } else if (check_host) {
108345916cd2Sjpk retv = B_TRUE;
108445916cd2Sjpk
108545916cd2Sjpk /*
108645916cd2Sjpk * Until we have SL range in the Zone structure, pass it
108745916cd2Sjpk * when our own address lookup returned an internal entry.
108845916cd2Sjpk */
108945916cd2Sjpk } else switch (dst_rhtp->tpc_tp.host_type) {
109045916cd2Sjpk case UNLABELED:
109145916cd2Sjpk retv = B_TRUE;
109245916cd2Sjpk break;
109345916cd2Sjpk
109445916cd2Sjpk case SUN_CIPSO:
109545916cd2Sjpk retv = _blinrange(label2bslabel(plabel),
109645916cd2Sjpk &dst_rhtp->tpc_tp.tp_sl_range_cipso) ||
109745916cd2Sjpk blinlset(label2bslabel(plabel),
109845916cd2Sjpk dst_rhtp->tpc_tp.tp_sl_set_cipso);
109945916cd2Sjpk break;
110045916cd2Sjpk
110145916cd2Sjpk default:
110245916cd2Sjpk retv = B_FALSE;
110345916cd2Sjpk }
110445916cd2Sjpk TPC_RELE(src_rhtp);
110545916cd2Sjpk TPC_RELE(dst_rhtp);
110645916cd2Sjpk return (retv);
110745916cd2Sjpk }
110845916cd2Sjpk
110945916cd2Sjpk /*
111045916cd2Sjpk * This routine determines whether a response to a failed packet delivery or
111145916cd2Sjpk * connection should be sent back. By default, the policy is to allow such
111245916cd2Sjpk * messages to be sent at all times, as these messages reveal little useful
111345916cd2Sjpk * information and are healthy parts of TCP/IP networking.
111445916cd2Sjpk *
111545916cd2Sjpk * If tsol_strict_error is set, then we do strict tests: if the packet label is
111645916cd2Sjpk * within the label range/set of this host/zone, return B_TRUE; otherwise
111745916cd2Sjpk * return B_FALSE, which causes the packet to be dropped silently.
111845916cd2Sjpk *
111945916cd2Sjpk * Note that tsol_get_pkt_label will cause the packet to drop if the sender is
112045916cd2Sjpk * marked as labeled in the remote host database, but the packet lacks a label.
112145916cd2Sjpk * This means that we don't need to do a lookup on the source; the
112245916cd2Sjpk * TSLF_UNLABELED flag is sufficient.
112345916cd2Sjpk */
112445916cd2Sjpk boolean_t
tsol_can_reply_error(const mblk_t * mp,ip_recv_attr_t * ira)1125bd670b35SErik Nordmark tsol_can_reply_error(const mblk_t *mp, ip_recv_attr_t *ira)
112645916cd2Sjpk {
112745916cd2Sjpk ts_label_t *plabel = NULL;
112845916cd2Sjpk tsol_tpc_t *rhtp;
112945916cd2Sjpk const ipha_t *ipha;
113045916cd2Sjpk const ip6_t *ip6h;
113145916cd2Sjpk boolean_t retv;
113245916cd2Sjpk bslabel_t *pktbs;
113345916cd2Sjpk
113445916cd2Sjpk /* Caller must pull up at least the IP header */
113545916cd2Sjpk ASSERT(MBLKL(mp) >= (IPH_HDR_VERSION(mp->b_rptr) == IPV4_VERSION ?
113645916cd2Sjpk sizeof (*ipha) : sizeof (*ip6h)));
113745916cd2Sjpk
113845916cd2Sjpk if (!tsol_strict_error)
113945916cd2Sjpk return (B_TRUE);
114045916cd2Sjpk
1141bd670b35SErik Nordmark plabel = ira->ira_tsl;
114245916cd2Sjpk
114345916cd2Sjpk /* We are bootstrapping or the internal template was never deleted */
114445916cd2Sjpk if (plabel == NULL)
114545916cd2Sjpk return (B_TRUE);
114645916cd2Sjpk
11475d3b8cb7SBill Sommerfeld if (plabel->tsl_flags & TSLF_IMPLICIT_IN) {
11485d3b8cb7SBill Sommerfeld DTRACE_PROBE3(tx__ip__log__drop__replyerror__unresolved__label,
11495d3b8cb7SBill Sommerfeld char *,
11505d3b8cb7SBill Sommerfeld "cannot send error report for packet mp(1) with "
11515d3b8cb7SBill Sommerfeld "unresolved security label sl(2)",
11525d3b8cb7SBill Sommerfeld mblk_t *, mp, ts_label_t *, plabel);
11535d3b8cb7SBill Sommerfeld return (B_FALSE);
11545d3b8cb7SBill Sommerfeld }
11555d3b8cb7SBill Sommerfeld
11565d3b8cb7SBill Sommerfeld
115745916cd2Sjpk if (IPH_HDR_VERSION(mp->b_rptr) == IPV4_VERSION) {
115845916cd2Sjpk ipha = (const ipha_t *)mp->b_rptr;
115945916cd2Sjpk rhtp = find_tpc(&ipha->ipha_dst, IPV4_VERSION, B_FALSE);
116045916cd2Sjpk } else {
116145916cd2Sjpk ip6h = (const ip6_t *)mp->b_rptr;
116245916cd2Sjpk rhtp = find_tpc(&ip6h->ip6_dst, IPV6_VERSION, B_FALSE);
116345916cd2Sjpk }
116445916cd2Sjpk
116545916cd2Sjpk if (rhtp == NULL || label2doi(plabel) != rhtp->tpc_tp.tp_doi) {
116645916cd2Sjpk retv = B_FALSE;
116745916cd2Sjpk } else {
116845916cd2Sjpk /*
116945916cd2Sjpk * If we're in the midst of forwarding, then the destination
117045916cd2Sjpk * address might not be labeled. In that case, allow unlabeled
117145916cd2Sjpk * packets through only if the default label is the same, and
117245916cd2Sjpk * labeled ones if they dominate.
117345916cd2Sjpk */
117445916cd2Sjpk pktbs = label2bslabel(plabel);
117545916cd2Sjpk switch (rhtp->tpc_tp.host_type) {
117645916cd2Sjpk case UNLABELED:
117745916cd2Sjpk if (plabel->tsl_flags & TSLF_UNLABELED) {
117845916cd2Sjpk retv = blequal(pktbs,
117945916cd2Sjpk &rhtp->tpc_tp.tp_def_label);
118045916cd2Sjpk } else {
118145916cd2Sjpk retv = bldominates(pktbs,
118245916cd2Sjpk &rhtp->tpc_tp.tp_def_label);
118345916cd2Sjpk }
118445916cd2Sjpk break;
118545916cd2Sjpk
118645916cd2Sjpk case SUN_CIPSO:
118745916cd2Sjpk retv = _blinrange(pktbs,
118845916cd2Sjpk &rhtp->tpc_tp.tp_sl_range_cipso) ||
118945916cd2Sjpk blinlset(pktbs, rhtp->tpc_tp.tp_sl_set_cipso);
119045916cd2Sjpk break;
119145916cd2Sjpk
119245916cd2Sjpk default:
119345916cd2Sjpk retv = B_FALSE;
119445916cd2Sjpk break;
119545916cd2Sjpk }
119645916cd2Sjpk }
119745916cd2Sjpk
119845916cd2Sjpk if (rhtp != NULL)
119945916cd2Sjpk TPC_RELE(rhtp);
120045916cd2Sjpk
120145916cd2Sjpk return (retv);
120245916cd2Sjpk }
120345916cd2Sjpk
120445916cd2Sjpk /*
1205bd670b35SErik Nordmark * Finds the zone associated with the receive attributes. Returns GLOBAL_ZONEID
1206bd670b35SErik Nordmark * if the zone cannot be located.
120745916cd2Sjpk *
120845916cd2Sjpk * This is used by the classifier when the packet matches an ALL_ZONES IRE, and
120945916cd2Sjpk * there's no MLP defined.
1210f4b3ec61Sdh155122 *
1211f4b3ec61Sdh155122 * Note that we assume that this is only invoked in the ALL_ZONES case.
1212bd670b35SErik Nordmark * Handling other cases would require handling exclusive IP zones where either
1213f4b3ec61Sdh155122 * this routine or the callers would have to map from
1214f4b3ec61Sdh155122 * the zoneid (zone->zone_id) to what IP uses in conn_zoneid etc.
121545916cd2Sjpk */
121645916cd2Sjpk zoneid_t
tsol_attr_to_zoneid(const ip_recv_attr_t * ira)1217bd670b35SErik Nordmark tsol_attr_to_zoneid(const ip_recv_attr_t *ira)
121845916cd2Sjpk {
121945916cd2Sjpk zone_t *zone;
122045916cd2Sjpk ts_label_t *label;
122145916cd2Sjpk
1222bd670b35SErik Nordmark if ((label = ira->ira_tsl) != NULL) {
122345916cd2Sjpk zone = zone_find_by_label(label);
122445916cd2Sjpk if (zone != NULL) {
122545916cd2Sjpk zoneid_t zoneid = zone->zone_id;
122645916cd2Sjpk
122745916cd2Sjpk zone_rele(zone);
122845916cd2Sjpk return (zoneid);
122945916cd2Sjpk }
123045916cd2Sjpk }
123145916cd2Sjpk return (GLOBAL_ZONEID);
123245916cd2Sjpk }
123345916cd2Sjpk
123445916cd2Sjpk int
tsol_ire_match_gwattr(ire_t * ire,const ts_label_t * tsl)123545916cd2Sjpk tsol_ire_match_gwattr(ire_t *ire, const ts_label_t *tsl)
123645916cd2Sjpk {
123745916cd2Sjpk int error = 0;
123845916cd2Sjpk tsol_ire_gw_secattr_t *attrp = NULL;
123945916cd2Sjpk tsol_tnrhc_t *gw_rhc = NULL;
124045916cd2Sjpk tsol_gcgrp_t *gcgrp = NULL;
124145916cd2Sjpk tsol_gc_t *gc = NULL;
124245916cd2Sjpk in_addr_t ga_addr4;
124345916cd2Sjpk void *paddr = NULL;
124445916cd2Sjpk
124545916cd2Sjpk /* Not in Trusted mode or IRE is local/loopback/broadcast/interface */
124645916cd2Sjpk if (!is_system_labeled() ||
124745916cd2Sjpk (ire->ire_type & (IRE_LOCAL | IRE_LOOPBACK | IRE_BROADCAST |
1248bd670b35SErik Nordmark IRE_IF_ALL | IRE_MULTICAST | IRE_NOROUTE)))
124945916cd2Sjpk goto done;
125045916cd2Sjpk
125145916cd2Sjpk /*
125245916cd2Sjpk * If we don't have a label to compare with, or the IRE does not
125345916cd2Sjpk * contain any gateway security attributes, there's not much that
125445916cd2Sjpk * we can do. We let the former case pass, and the latter fail,
125545916cd2Sjpk * since the IRE doesn't qualify for a match due to the lack of
125645916cd2Sjpk * security attributes.
125745916cd2Sjpk */
125845916cd2Sjpk if (tsl == NULL || ire->ire_gw_secattr == NULL) {
125945916cd2Sjpk if (tsl != NULL) {
12605d3b8cb7SBill Sommerfeld DTRACE_PROBE3(
12615d3b8cb7SBill Sommerfeld tx__ip__log__drop__irematch__nogwsec, char *,
12625d3b8cb7SBill Sommerfeld "ire(1) lacks ire_gw_secattr when matching "
12635d3b8cb7SBill Sommerfeld "label(2)", ire_t *, ire, ts_label_t *, tsl);
126445916cd2Sjpk error = EACCES;
126545916cd2Sjpk }
126645916cd2Sjpk goto done;
126745916cd2Sjpk }
126845916cd2Sjpk
126945916cd2Sjpk attrp = ire->ire_gw_secattr;
127045916cd2Sjpk
127145916cd2Sjpk /*
127245916cd2Sjpk * The possible lock order scenarios related to the tsol gateway
127345916cd2Sjpk * attribute locks are documented at the beginning of ip.c in the
127445916cd2Sjpk * lock order scenario section.
127545916cd2Sjpk */
127645916cd2Sjpk mutex_enter(&attrp->igsa_lock);
127745916cd2Sjpk
127845916cd2Sjpk /*
1279bd670b35SErik Nordmark * We seek the group
128045916cd2Sjpk * structure which contains all security credentials of the gateway.
1281bd670b35SErik Nordmark * An offline IRE is associated with at most one gateway credential.
128245916cd2Sjpk */
1283bd670b35SErik Nordmark if ((gc = attrp->igsa_gc) != NULL) {
128445916cd2Sjpk gcgrp = gc->gc_grp;
128545916cd2Sjpk ASSERT(gcgrp != NULL);
128645916cd2Sjpk rw_enter(&gcgrp->gcgrp_rwlock, RW_READER);
128745916cd2Sjpk GCGRP_REFHOLD(gcgrp);
1288bd670b35SErik Nordmark }
128945916cd2Sjpk
129045916cd2Sjpk if ((gw_rhc = attrp->igsa_rhc) != NULL) {
129145916cd2Sjpk /*
129245916cd2Sjpk * If our cached entry has grown stale, then discard it so we
129345916cd2Sjpk * can get a new one.
129445916cd2Sjpk */
129545916cd2Sjpk if (gw_rhc->rhc_invalid || gw_rhc->rhc_tpc->tpc_invalid) {
129645916cd2Sjpk TNRHC_RELE(gw_rhc);
129745916cd2Sjpk attrp->igsa_rhc = gw_rhc = NULL;
129845916cd2Sjpk } else {
129945916cd2Sjpk TNRHC_HOLD(gw_rhc)
130045916cd2Sjpk }
130145916cd2Sjpk }
130245916cd2Sjpk
130345916cd2Sjpk /* Last attempt at loading the template had failed; try again */
130445916cd2Sjpk if (gw_rhc == NULL) {
130545916cd2Sjpk if (gcgrp != NULL) {
130645916cd2Sjpk tsol_gcgrp_addr_t *ga = &gcgrp->gcgrp_addr;
130745916cd2Sjpk
130845916cd2Sjpk if (ire->ire_ipversion == IPV4_VERSION) {
130945916cd2Sjpk ASSERT(ga->ga_af == AF_INET);
131045916cd2Sjpk IN6_V4MAPPED_TO_IPADDR(&ga->ga_addr, ga_addr4);
131145916cd2Sjpk paddr = &ga_addr4;
131245916cd2Sjpk } else {
131345916cd2Sjpk ASSERT(ga->ga_af == AF_INET6);
131445916cd2Sjpk paddr = &ga->ga_addr;
131545916cd2Sjpk }
1316bd670b35SErik Nordmark } else if (ire->ire_type & IRE_OFFLINK) {
1317bd670b35SErik Nordmark if (ire->ire_ipversion == IPV6_VERSION)
131845916cd2Sjpk paddr = &ire->ire_gateway_addr_v6;
1319bd670b35SErik Nordmark else if (ire->ire_ipversion == IPV4_VERSION)
132045916cd2Sjpk paddr = &ire->ire_gateway_addr;
132145916cd2Sjpk }
132245916cd2Sjpk
132345916cd2Sjpk /* We've found a gateway address to do the template lookup */
132445916cd2Sjpk if (paddr != NULL) {
132545916cd2Sjpk ASSERT(gw_rhc == NULL);
1326bfabfc35Skp158701 gw_rhc = find_rhc(paddr, ire->ire_ipversion, B_FALSE);
132745916cd2Sjpk if (gw_rhc != NULL) {
132845916cd2Sjpk /*
132945916cd2Sjpk * Note that if the lookup above returned an
133045916cd2Sjpk * internal template, we'll use it for the
133145916cd2Sjpk * time being, and do another lookup next
133245916cd2Sjpk * time around.
133345916cd2Sjpk */
133445916cd2Sjpk /* Another thread has loaded the template? */
133545916cd2Sjpk if (attrp->igsa_rhc != NULL) {
133645916cd2Sjpk TNRHC_RELE(gw_rhc)
133745916cd2Sjpk /* reload, it could be different */
133845916cd2Sjpk gw_rhc = attrp->igsa_rhc;
133945916cd2Sjpk } else {
134045916cd2Sjpk attrp->igsa_rhc = gw_rhc;
134145916cd2Sjpk }
134245916cd2Sjpk /*
134345916cd2Sjpk * Hold an extra reference just like we did
134445916cd2Sjpk * above prior to dropping the igsa_lock.
134545916cd2Sjpk */
134645916cd2Sjpk TNRHC_HOLD(gw_rhc)
134745916cd2Sjpk }
134845916cd2Sjpk }
134945916cd2Sjpk }
135045916cd2Sjpk
135145916cd2Sjpk mutex_exit(&attrp->igsa_lock);
135245916cd2Sjpk /* Gateway template not found */
135345916cd2Sjpk if (gw_rhc == NULL) {
135445916cd2Sjpk /*
135545916cd2Sjpk * If destination address is directly reachable through an
135645916cd2Sjpk * interface rather than through a learned route, pass it.
135745916cd2Sjpk */
135845916cd2Sjpk if (paddr != NULL) {
135945916cd2Sjpk DTRACE_PROBE3(
136045916cd2Sjpk tx__ip__log__drop__irematch__nogwtmpl, char *,
136145916cd2Sjpk "ire(1), label(2) off-link with no gw_rhc",
136245916cd2Sjpk ire_t *, ire, ts_label_t *, tsl);
136345916cd2Sjpk error = EINVAL;
136445916cd2Sjpk }
136545916cd2Sjpk goto done;
136645916cd2Sjpk }
136745916cd2Sjpk
136845916cd2Sjpk if (gc != NULL) {
1369bd670b35SErik Nordmark
137045916cd2Sjpk tsol_gcdb_t *gcdb;
137145916cd2Sjpk /*
137245916cd2Sjpk * In the case of IRE_CACHE we've got one or more gateway
137345916cd2Sjpk * security credentials to compare against the passed in label.
137445916cd2Sjpk * Perform label range comparison against each security
137545916cd2Sjpk * credential of the gateway. In the case of a prefix ire
137645916cd2Sjpk * we need to match against the security attributes of
137745916cd2Sjpk * just the route itself, so the loop is executed only once.
137845916cd2Sjpk */
137945916cd2Sjpk ASSERT(gcgrp != NULL);
138045916cd2Sjpk gcdb = gc->gc_db;
1381bd670b35SErik Nordmark if (tsl->tsl_doi != gcdb->gcdb_doi ||
1382bd670b35SErik Nordmark !_blinrange(&tsl->tsl_label, &gcdb->gcdb_slrange)) {
138345916cd2Sjpk DTRACE_PROBE3(
138445916cd2Sjpk tx__ip__log__drop__irematch__nogcmatched,
138545916cd2Sjpk char *, "ire(1), tsl(2): all gc failed match",
138645916cd2Sjpk ire_t *, ire, ts_label_t *, tsl);
138745916cd2Sjpk error = EACCES;
138845916cd2Sjpk }
138945916cd2Sjpk } else {
139045916cd2Sjpk /*
139145916cd2Sjpk * We didn't find any gateway credentials in the IRE
139245916cd2Sjpk * attributes; fall back to the gateway's template for
139345916cd2Sjpk * label range checks, if we are required to do so.
139445916cd2Sjpk */
139545916cd2Sjpk ASSERT(gw_rhc != NULL);
139645916cd2Sjpk switch (gw_rhc->rhc_tpc->tpc_tp.host_type) {
139745916cd2Sjpk case SUN_CIPSO:
1398222c5bceSkp158701 if (tsl->tsl_doi != gw_rhc->rhc_tpc->tpc_tp.tp_doi ||
139945916cd2Sjpk (!_blinrange(&tsl->tsl_label,
1400222c5bceSkp158701 &gw_rhc->rhc_tpc->tpc_tp.tp_sl_range_cipso) &&
140145916cd2Sjpk !blinlset(&tsl->tsl_label,
140245916cd2Sjpk gw_rhc->rhc_tpc->tpc_tp.tp_sl_set_cipso))) {
140345916cd2Sjpk error = EACCES;
140445916cd2Sjpk DTRACE_PROBE4(
140545916cd2Sjpk tx__ip__log__drop__irematch__deftmpl,
140645916cd2Sjpk char *, "ire(1), tsl(2), gw_rhc(3) "
140745916cd2Sjpk "failed match (cipso gw)",
140845916cd2Sjpk ire_t *, ire, ts_label_t *, tsl,
140945916cd2Sjpk tsol_tnrhc_t *, gw_rhc);
141045916cd2Sjpk }
141145916cd2Sjpk break;
141245916cd2Sjpk
141345916cd2Sjpk case UNLABELED:
1414222c5bceSkp158701 if (tsl->tsl_doi != gw_rhc->rhc_tpc->tpc_tp.tp_doi ||
141545916cd2Sjpk (!_blinrange(&tsl->tsl_label,
141645916cd2Sjpk &gw_rhc->rhc_tpc->tpc_tp.tp_gw_sl_range) &&
141745916cd2Sjpk !blinlset(&tsl->tsl_label,
141845916cd2Sjpk gw_rhc->rhc_tpc->tpc_tp.tp_gw_sl_set))) {
141945916cd2Sjpk error = EACCES;
142045916cd2Sjpk DTRACE_PROBE4(
142145916cd2Sjpk tx__ip__log__drop__irematch__deftmpl,
142245916cd2Sjpk char *, "ire(1), tsl(2), gw_rhc(3) "
142345916cd2Sjpk "failed match (unlabeled gw)",
142445916cd2Sjpk ire_t *, ire, ts_label_t *, tsl,
142545916cd2Sjpk tsol_tnrhc_t *, gw_rhc);
142645916cd2Sjpk }
142745916cd2Sjpk break;
142845916cd2Sjpk }
142945916cd2Sjpk }
143045916cd2Sjpk
143145916cd2Sjpk done:
143245916cd2Sjpk
143345916cd2Sjpk if (gcgrp != NULL) {
143445916cd2Sjpk rw_exit(&gcgrp->gcgrp_rwlock);
143545916cd2Sjpk GCGRP_REFRELE(gcgrp);
143645916cd2Sjpk }
143745916cd2Sjpk
143845916cd2Sjpk if (gw_rhc != NULL)
143945916cd2Sjpk TNRHC_RELE(gw_rhc)
144045916cd2Sjpk
144145916cd2Sjpk return (error);
144245916cd2Sjpk }
144345916cd2Sjpk
144445916cd2Sjpk /*
144545916cd2Sjpk * Performs label accreditation checks for packet forwarding.
1446bd670b35SErik Nordmark * Add or remove a CIPSO option as needed.
144745916cd2Sjpk *
144845916cd2Sjpk * Returns a pointer to the modified mblk if allowed for forwarding,
144945916cd2Sjpk * or NULL if the packet must be dropped.
145045916cd2Sjpk */
145145916cd2Sjpk mblk_t *
tsol_ip_forward(ire_t * ire,mblk_t * mp,const ip_recv_attr_t * ira)1452bd670b35SErik Nordmark tsol_ip_forward(ire_t *ire, mblk_t *mp, const ip_recv_attr_t *ira)
145345916cd2Sjpk {
145445916cd2Sjpk tsol_ire_gw_secattr_t *attrp = NULL;
145545916cd2Sjpk ipha_t *ipha;
145645916cd2Sjpk ip6_t *ip6h;
145745916cd2Sjpk const void *pdst;
145845916cd2Sjpk const void *psrc;
145945916cd2Sjpk boolean_t off_link;
146045916cd2Sjpk tsol_tpc_t *dst_rhtp, *gw_rhtp;
146145916cd2Sjpk tsol_ip_label_t label_type;
146245916cd2Sjpk uchar_t *opt_ptr = NULL;
146345916cd2Sjpk ts_label_t *tsl;
146445916cd2Sjpk uint8_t proto;
146545916cd2Sjpk int af, adjust;
146645916cd2Sjpk uint16_t iplen;
1467c793af95Ssangeeta boolean_t need_tpc_rele = B_FALSE;
1468c793af95Ssangeeta ipaddr_t *gw;
1469f4b3ec61Sdh155122 ip_stack_t *ipst = ire->ire_ipst;
1470bd670b35SErik Nordmark int err;
1471bd670b35SErik Nordmark ts_label_t *effective_tsl = NULL;
147245916cd2Sjpk
147345916cd2Sjpk ASSERT(ire != NULL && mp != NULL);
1474bd670b35SErik Nordmark /*
1475bd670b35SErik Nordmark * Note that the ire is the first one found, i.e., an IRE_OFFLINK if
1476bd670b35SErik Nordmark * the destination is offlink.
1477bd670b35SErik Nordmark */
147845916cd2Sjpk
147945916cd2Sjpk af = (ire->ire_ipversion == IPV4_VERSION) ? AF_INET : AF_INET6;
148045916cd2Sjpk
148145916cd2Sjpk if (IPH_HDR_VERSION(mp->b_rptr) == IPV4_VERSION) {
148245916cd2Sjpk ASSERT(ire->ire_ipversion == IPV4_VERSION);
148345916cd2Sjpk ipha = (ipha_t *)mp->b_rptr;
148445916cd2Sjpk psrc = &ipha->ipha_src;
148545916cd2Sjpk pdst = &ipha->ipha_dst;
148645916cd2Sjpk proto = ipha->ipha_protocol;
1487c4e55c13Sken Powell - Sun Microsystem if (!tsol_get_option_v4(mp, &label_type, &opt_ptr))
1488c4e55c13Sken Powell - Sun Microsystem return (NULL);
148945916cd2Sjpk } else {
149045916cd2Sjpk ASSERT(ire->ire_ipversion == IPV6_VERSION);
149145916cd2Sjpk ip6h = (ip6_t *)mp->b_rptr;
149245916cd2Sjpk psrc = &ip6h->ip6_src;
149345916cd2Sjpk pdst = &ip6h->ip6_dst;
149445916cd2Sjpk proto = ip6h->ip6_nxt;
149545916cd2Sjpk
149645916cd2Sjpk if (proto != IPPROTO_TCP && proto != IPPROTO_UDP &&
149745916cd2Sjpk proto != IPPROTO_ICMPV6) {
149845916cd2Sjpk uint8_t *nexthdrp;
149945916cd2Sjpk uint16_t hdr_len;
150045916cd2Sjpk
150145916cd2Sjpk if (!ip_hdr_length_nexthdr_v6(mp, ip6h, &hdr_len,
150245916cd2Sjpk &nexthdrp)) {
150345916cd2Sjpk /* malformed packet; drop it */
150445916cd2Sjpk return (NULL);
150545916cd2Sjpk }
150645916cd2Sjpk proto = *nexthdrp;
150745916cd2Sjpk }
1508c4e55c13Sken Powell - Sun Microsystem if (!tsol_get_option_v6(mp, &label_type, &opt_ptr))
1509c4e55c13Sken Powell - Sun Microsystem return (NULL);
151045916cd2Sjpk }
1511bd670b35SErik Nordmark /*
1512bd670b35SErik Nordmark * off_link is TRUE if destination not directly reachable.
1513bd670b35SErik Nordmark */
1514bd670b35SErik Nordmark off_link = (ire->ire_type & IRE_OFFLINK);
151545916cd2Sjpk
1516bd670b35SErik Nordmark if ((tsl = ira->ira_tsl) == NULL)
151745916cd2Sjpk return (mp);
151845916cd2Sjpk
15195d3b8cb7SBill Sommerfeld if (tsl->tsl_flags & TSLF_IMPLICIT_IN) {
15205d3b8cb7SBill Sommerfeld DTRACE_PROBE3(tx__ip__log__drop__forward__unresolved__label,
15215d3b8cb7SBill Sommerfeld char *,
15225d3b8cb7SBill Sommerfeld "cannot forward packet mp(1) with unresolved "
15235d3b8cb7SBill Sommerfeld "security label sl(2)",
15245d3b8cb7SBill Sommerfeld mblk_t *, mp, ts_label_t *, tsl);
15255d3b8cb7SBill Sommerfeld
15265d3b8cb7SBill Sommerfeld return (NULL);
15275d3b8cb7SBill Sommerfeld }
15285d3b8cb7SBill Sommerfeld
15295d3b8cb7SBill Sommerfeld
153045916cd2Sjpk ASSERT(psrc != NULL && pdst != NULL);
153145916cd2Sjpk dst_rhtp = find_tpc(pdst, ire->ire_ipversion, B_FALSE);
153245916cd2Sjpk
153345916cd2Sjpk if (dst_rhtp == NULL) {
153445916cd2Sjpk /*
153545916cd2Sjpk * Without a template we do not know if forwarding
153645916cd2Sjpk * violates MAC
153745916cd2Sjpk */
153845916cd2Sjpk DTRACE_PROBE3(tx__ip__log__drop__forward__nodst, char *,
153945916cd2Sjpk "mp(1) dropped, no template for destination ip4|6(2)",
154045916cd2Sjpk mblk_t *, mp, void *, pdst);
154145916cd2Sjpk return (NULL);
154245916cd2Sjpk }
154345916cd2Sjpk
154445916cd2Sjpk /*
154545916cd2Sjpk * Gateway template must have existed for off-link destinations,
154645916cd2Sjpk * since tsol_ire_match_gwattr has ensured such condition.
154745916cd2Sjpk */
1548c793af95Ssangeeta if (ire->ire_ipversion == IPV4_VERSION && off_link) {
1549c793af95Ssangeeta /*
1550c793af95Ssangeeta * Surya note: first check if we can get the gw_rhtp from
1551c793af95Ssangeeta * the ire_gw_secattr->igsa_rhc; if this is null, then
1552c793af95Ssangeeta * do a lookup based on the ire_addr (address of gw)
1553c793af95Ssangeeta */
1554c793af95Ssangeeta if (ire->ire_gw_secattr != NULL &&
1555c793af95Ssangeeta ire->ire_gw_secattr->igsa_rhc != NULL) {
1556c793af95Ssangeeta attrp = ire->ire_gw_secattr;
1557c793af95Ssangeeta gw_rhtp = attrp->igsa_rhc->rhc_tpc;
1558c793af95Ssangeeta } else {
1559bd670b35SErik Nordmark gw = &ire->ire_gateway_addr;
1560c793af95Ssangeeta gw_rhtp = find_tpc(gw, ire->ire_ipversion, B_FALSE);
1561c793af95Ssangeeta need_tpc_rele = B_TRUE;
1562c793af95Ssangeeta }
1563c793af95Ssangeeta if (gw_rhtp == NULL) {
1564c793af95Ssangeeta DTRACE_PROBE3(tx__ip__log__drop__forward__nogw, char *,
1565c793af95Ssangeeta "mp(1) dropped, no gateway in ire attributes(2)",
1566c793af95Ssangeeta mblk_t *, mp, tsol_ire_gw_secattr_t *, attrp);
1567c793af95Ssangeeta mp = NULL;
1568c793af95Ssangeeta goto keep_label;
1569c793af95Ssangeeta }
1570c793af95Ssangeeta }
1571c793af95Ssangeeta if (ire->ire_ipversion == IPV6_VERSION &&
1572c793af95Ssangeeta ((attrp = ire->ire_gw_secattr) == NULL || attrp->igsa_rhc == NULL ||
157345916cd2Sjpk (gw_rhtp = attrp->igsa_rhc->rhc_tpc) == NULL) && off_link) {
157445916cd2Sjpk DTRACE_PROBE3(tx__ip__log__drop__forward__nogw, char *,
157545916cd2Sjpk "mp(1) dropped, no gateway in ire attributes(2)",
157645916cd2Sjpk mblk_t *, mp, tsol_ire_gw_secattr_t *, attrp);
157745916cd2Sjpk mp = NULL;
157845916cd2Sjpk goto keep_label;
157945916cd2Sjpk }
158045916cd2Sjpk
158145916cd2Sjpk /*
158245916cd2Sjpk * Check that the label for the packet is acceptable
158345916cd2Sjpk * by destination host; otherwise, drop it.
158445916cd2Sjpk */
158545916cd2Sjpk switch (dst_rhtp->tpc_tp.host_type) {
158645916cd2Sjpk case SUN_CIPSO:
158745916cd2Sjpk if (tsl->tsl_doi != dst_rhtp->tpc_tp.tp_doi ||
158845916cd2Sjpk (!_blinrange(&tsl->tsl_label,
158945916cd2Sjpk &dst_rhtp->tpc_tp.tp_sl_range_cipso) &&
159045916cd2Sjpk !blinlset(&tsl->tsl_label,
159145916cd2Sjpk dst_rhtp->tpc_tp.tp_sl_set_cipso))) {
159245916cd2Sjpk DTRACE_PROBE4(tx__ip__log__drop__forward__mac, char *,
159345916cd2Sjpk "labeled packet mp(1) dropped, label(2) fails "
159445916cd2Sjpk "destination(3) accredation check",
159545916cd2Sjpk mblk_t *, mp, ts_label_t *, tsl,
159645916cd2Sjpk tsol_tpc_t *, dst_rhtp);
159745916cd2Sjpk mp = NULL;
159845916cd2Sjpk goto keep_label;
159945916cd2Sjpk }
160045916cd2Sjpk break;
160145916cd2Sjpk
160245916cd2Sjpk
160345916cd2Sjpk case UNLABELED:
160445916cd2Sjpk if (tsl->tsl_doi != dst_rhtp->tpc_tp.tp_doi ||
160545916cd2Sjpk !blequal(&dst_rhtp->tpc_tp.tp_def_label,
160645916cd2Sjpk &tsl->tsl_label)) {
160745916cd2Sjpk DTRACE_PROBE4(tx__ip__log__drop__forward__mac, char *,
160845916cd2Sjpk "unlabeled packet mp(1) dropped, label(2) fails "
160945916cd2Sjpk "destination(3) accredation check",
161045916cd2Sjpk mblk_t *, mp, ts_label_t *, tsl,
161145916cd2Sjpk tsol_tpc_t *, dst_rhtp);
161245916cd2Sjpk mp = NULL;
161345916cd2Sjpk goto keep_label;
161445916cd2Sjpk }
161545916cd2Sjpk break;
161645916cd2Sjpk }
161745916cd2Sjpk if (label_type == OPT_CIPSO) {
161845916cd2Sjpk /*
161945916cd2Sjpk * We keep the label on any of the following cases:
162045916cd2Sjpk *
162145916cd2Sjpk * 1. The destination is labeled (on/off-link).
162245916cd2Sjpk * 2. The unlabeled destination is off-link,
162345916cd2Sjpk * and the next hop gateway is labeled.
162445916cd2Sjpk */
162545916cd2Sjpk if (dst_rhtp->tpc_tp.host_type != UNLABELED ||
162645916cd2Sjpk (off_link &&
162745916cd2Sjpk gw_rhtp->tpc_tp.host_type != UNLABELED))
162845916cd2Sjpk goto keep_label;
162945916cd2Sjpk
163045916cd2Sjpk /*
163145916cd2Sjpk * Strip off the CIPSO option from the packet because: the
163245916cd2Sjpk * unlabeled destination host is directly reachable through
163345916cd2Sjpk * an interface (on-link); or, the unlabeled destination host
163445916cd2Sjpk * is not directly reachable (off-link), and the next hop
163545916cd2Sjpk * gateway is unlabeled.
163645916cd2Sjpk */
163745916cd2Sjpk adjust = (af == AF_INET) ? tsol_remove_secopt(ipha, MBLKL(mp)) :
163845916cd2Sjpk tsol_remove_secopt_v6(ip6h, MBLKL(mp));
163945916cd2Sjpk
164045916cd2Sjpk ASSERT(adjust <= 0);
164145916cd2Sjpk if (adjust != 0) {
164245916cd2Sjpk
164345916cd2Sjpk /* adjust is negative */
164445916cd2Sjpk ASSERT((mp->b_wptr + adjust) >= mp->b_rptr);
164545916cd2Sjpk mp->b_wptr += adjust;
1646bd670b35SErik Nordmark /*
1647bd670b35SErik Nordmark * Note that caller adjusts ira_pktlen and
1648bd670b35SErik Nordmark * ira_ip_hdr_length
1649bd670b35SErik Nordmark *
1650bd670b35SErik Nordmark * For AF_INET6 note that tsol_remove_secopt_v6
1651bd670b35SErik Nordmark * adjusted ip6_plen.
1652bd670b35SErik Nordmark */
165345916cd2Sjpk if (af == AF_INET) {
165445916cd2Sjpk ipha = (ipha_t *)mp->b_rptr;
165545916cd2Sjpk iplen = ntohs(ipha->ipha_length) + adjust;
165645916cd2Sjpk ipha->ipha_length = htons(iplen);
165745916cd2Sjpk ipha->ipha_hdr_checksum = 0;
165845916cd2Sjpk ipha->ipha_hdr_checksum = ip_csum_hdr(ipha);
165945916cd2Sjpk }
166045916cd2Sjpk DTRACE_PROBE3(tx__ip__log__info__forward__adjust,
166145916cd2Sjpk char *,
166245916cd2Sjpk "mp(1) adjusted(2) for CIPSO option removal",
166345916cd2Sjpk mblk_t *, mp, int, adjust);
166445916cd2Sjpk }
166545916cd2Sjpk goto keep_label;
166645916cd2Sjpk }
166745916cd2Sjpk
166845916cd2Sjpk ASSERT(label_type == OPT_NONE);
166945916cd2Sjpk ASSERT(dst_rhtp != NULL);
167045916cd2Sjpk
167145916cd2Sjpk /*
167245916cd2Sjpk * We need to add CIPSO option if the destination or the next hop
167345916cd2Sjpk * gateway is labeled. Otherwise, pass the packet as is.
167445916cd2Sjpk */
167545916cd2Sjpk if (dst_rhtp->tpc_tp.host_type == UNLABELED &&
167645916cd2Sjpk (!off_link || gw_rhtp->tpc_tp.host_type == UNLABELED))
167745916cd2Sjpk goto keep_label;
167845916cd2Sjpk
1679bd670b35SErik Nordmark /*
1680bd670b35SErik Nordmark * Since we are forwarding packets we use GLOBAL_ZONEID for
1681bd670b35SErik Nordmark * the IRE lookup in tsol_check_label.
1682bd670b35SErik Nordmark * Since mac_exempt is false the zoneid isn't used for anything
1683bd670b35SErik Nordmark * but the IRE lookup, hence we set zone_is_global to false.
1684bd670b35SErik Nordmark */
1685bd670b35SErik Nordmark if (af == AF_INET) {
1686bd670b35SErik Nordmark err = tsol_check_label_v4(tsl, GLOBAL_ZONEID, &mp,
1687bd670b35SErik Nordmark CONN_MAC_DEFAULT, B_FALSE, ipst, &effective_tsl);
1688bd670b35SErik Nordmark } else {
1689bd670b35SErik Nordmark err = tsol_check_label_v6(tsl, GLOBAL_ZONEID, &mp,
1690bd670b35SErik Nordmark CONN_MAC_DEFAULT, B_FALSE, ipst, &effective_tsl);
1691bd670b35SErik Nordmark }
1692bd670b35SErik Nordmark if (err != 0) {
1693bd670b35SErik Nordmark BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsOutDiscards);
1694bd670b35SErik Nordmark ip_drop_output("tsol_check_label", mp, NULL);
1695bd670b35SErik Nordmark freemsg(mp);
169645916cd2Sjpk mp = NULL;
169745916cd2Sjpk goto keep_label;
169845916cd2Sjpk }
169945916cd2Sjpk
1700bd670b35SErik Nordmark /*
1701bd670b35SErik Nordmark * The effective_tsl must never affect the routing decision, hence
1702bd670b35SErik Nordmark * we ignore it here.
1703bd670b35SErik Nordmark */
1704bd670b35SErik Nordmark if (effective_tsl != NULL)
1705bd670b35SErik Nordmark label_rele(effective_tsl);
1706bd670b35SErik Nordmark
170745916cd2Sjpk if (af == AF_INET) {
170845916cd2Sjpk ipha = (ipha_t *)mp->b_rptr;
170945916cd2Sjpk ipha->ipha_hdr_checksum = 0;
171045916cd2Sjpk ipha->ipha_hdr_checksum = ip_csum_hdr(ipha);
171145916cd2Sjpk }
171245916cd2Sjpk
171345916cd2Sjpk keep_label:
171445916cd2Sjpk TPC_RELE(dst_rhtp);
1715c793af95Ssangeeta if (need_tpc_rele && gw_rhtp != NULL)
1716c793af95Ssangeeta TPC_RELE(gw_rhtp);
171745916cd2Sjpk return (mp);
171845916cd2Sjpk }
171945916cd2Sjpk
172045916cd2Sjpk /*
17210ec92a15Swy83408 * Name: tsol_pmtu_adjust()
17220ec92a15Swy83408 *
17230ec92a15Swy83408 * Returns the adjusted mtu after removing security option.
17240ec92a15Swy83408 * Removes/subtracts the option if the packet's cred indicates an unlabeled
17250ec92a15Swy83408 * sender or if pkt_diff indicates this system enlarged the packet.
17260ec92a15Swy83408 */
17270ec92a15Swy83408 uint32_t
tsol_pmtu_adjust(mblk_t * mp,uint32_t mtu,int pkt_diff,int af)17280ec92a15Swy83408 tsol_pmtu_adjust(mblk_t *mp, uint32_t mtu, int pkt_diff, int af)
17290ec92a15Swy83408 {
17300ec92a15Swy83408 int label_adj = 0;
17310ec92a15Swy83408 uint32_t min_mtu = IP_MIN_MTU;
17320ec92a15Swy83408 tsol_tpc_t *src_rhtp;
17330ec92a15Swy83408 void *src;
17340ec92a15Swy83408
17350ec92a15Swy83408 /*
17360ec92a15Swy83408 * Note: label_adj is non-positive, indicating the number of
17370ec92a15Swy83408 * bytes removed by removing the security option from the
17380ec92a15Swy83408 * header.
17390ec92a15Swy83408 */
17400ec92a15Swy83408 if (af == AF_INET6) {
17410ec92a15Swy83408 ip6_t *ip6h;
17420ec92a15Swy83408
17430ec92a15Swy83408 min_mtu = IPV6_MIN_MTU;
17440ec92a15Swy83408 ip6h = (ip6_t *)mp->b_rptr;
17450ec92a15Swy83408 src = &ip6h->ip6_src;
17460ec92a15Swy83408 if ((src_rhtp = find_tpc(src, IPV6_VERSION, B_FALSE)) == NULL)
17470ec92a15Swy83408 return (mtu);
17480ec92a15Swy83408 if (pkt_diff > 0 || src_rhtp->tpc_tp.host_type == UNLABELED) {
17490ec92a15Swy83408 label_adj = tsol_remove_secopt_v6(
17500ec92a15Swy83408 (ip6_t *)mp->b_rptr, MBLKL(mp));
17510ec92a15Swy83408 }
17520ec92a15Swy83408 } else {
17530ec92a15Swy83408 ipha_t *ipha;
17540ec92a15Swy83408
17550ec92a15Swy83408 ASSERT(af == AF_INET);
17560ec92a15Swy83408 ipha = (ipha_t *)mp->b_rptr;
17570ec92a15Swy83408 src = &ipha->ipha_src;
17580ec92a15Swy83408 if ((src_rhtp = find_tpc(src, IPV4_VERSION, B_FALSE)) == NULL)
17590ec92a15Swy83408 return (mtu);
17600ec92a15Swy83408 if (pkt_diff > 0 || src_rhtp->tpc_tp.host_type == UNLABELED)
17610ec92a15Swy83408 label_adj = tsol_remove_secopt(
17620ec92a15Swy83408 (ipha_t *)mp->b_rptr, MBLKL(mp));
17630ec92a15Swy83408 }
17640ec92a15Swy83408 /*
17650ec92a15Swy83408 * Make pkt_diff non-negative and the larger of the bytes
17660ec92a15Swy83408 * previously added (if any) or just removed, since label
17670ec92a15Swy83408 * addition + subtraction may not be completely idempotent.
17680ec92a15Swy83408 */
17690ec92a15Swy83408 if (pkt_diff < -label_adj)
17700ec92a15Swy83408 pkt_diff = -label_adj;
17710ec92a15Swy83408 if (pkt_diff > 0 && pkt_diff < mtu)
17720ec92a15Swy83408 mtu -= pkt_diff;
17730ec92a15Swy83408
17740ec92a15Swy83408 TPC_RELE(src_rhtp);
17750ec92a15Swy83408 return (MAX(mtu, min_mtu));
17760ec92a15Swy83408 }
17770ec92a15Swy83408
17780ec92a15Swy83408 /*
177945916cd2Sjpk * Name: tsol_rtsa_init()
178045916cd2Sjpk *
178145916cd2Sjpk * Normal: Sanity checks on the route security attributes provided by
178245916cd2Sjpk * user. Convert it into a route security parameter list to
178345916cd2Sjpk * be returned to caller.
178445916cd2Sjpk *
178545916cd2Sjpk * Output: EINVAL if bad security attributes in the routing message
178645916cd2Sjpk * ENOMEM if unable to allocate data structures
178745916cd2Sjpk * 0 otherwise.
178845916cd2Sjpk *
178945916cd2Sjpk * Note: On input, cp must point to the end of any addresses in
179045916cd2Sjpk * the rt_msghdr_t structure.
179145916cd2Sjpk */
179245916cd2Sjpk int
tsol_rtsa_init(rt_msghdr_t * rtm,tsol_rtsecattr_t * sp,caddr_t cp)179345916cd2Sjpk tsol_rtsa_init(rt_msghdr_t *rtm, tsol_rtsecattr_t *sp, caddr_t cp)
179445916cd2Sjpk {
179545916cd2Sjpk uint_t sacnt;
179645916cd2Sjpk int err;
179745916cd2Sjpk caddr_t lim;
179845916cd2Sjpk tsol_rtsecattr_t *tp;
179945916cd2Sjpk
180045916cd2Sjpk ASSERT((cp >= (caddr_t)&rtm[1]) && sp != NULL);
180145916cd2Sjpk
180245916cd2Sjpk /*
180345916cd2Sjpk * In theory, we could accept as many security attributes configured
180445916cd2Sjpk * per route destination. However, the current design is limited
180545916cd2Sjpk * such that at most only one set security attributes is allowed to
180645916cd2Sjpk * be associated with a prefix IRE. We therefore assert for now.
180745916cd2Sjpk */
180845916cd2Sjpk /* LINTED */
180945916cd2Sjpk ASSERT(TSOL_RTSA_REQUEST_MAX == 1);
181045916cd2Sjpk
181145916cd2Sjpk sp->rtsa_cnt = 0;
181245916cd2Sjpk lim = (caddr_t)rtm + rtm->rtm_msglen;
181345916cd2Sjpk ASSERT(cp <= lim);
181445916cd2Sjpk
181545916cd2Sjpk if ((lim - cp) < sizeof (rtm_ext_t) ||
181645916cd2Sjpk ((rtm_ext_t *)cp)->rtmex_type != RTMEX_GATEWAY_SECATTR)
181745916cd2Sjpk return (0);
181845916cd2Sjpk
181945916cd2Sjpk if (((rtm_ext_t *)cp)->rtmex_len < sizeof (tsol_rtsecattr_t))
182045916cd2Sjpk return (EINVAL);
182145916cd2Sjpk
182245916cd2Sjpk cp += sizeof (rtm_ext_t);
182345916cd2Sjpk
182445916cd2Sjpk if ((lim - cp) < sizeof (*tp) ||
182545916cd2Sjpk (tp = (tsol_rtsecattr_t *)cp, (sacnt = tp->rtsa_cnt) == 0) ||
182645916cd2Sjpk (lim - cp) < TSOL_RTSECATTR_SIZE(sacnt))
182745916cd2Sjpk return (EINVAL);
182845916cd2Sjpk
182945916cd2Sjpk /*
183045916cd2Sjpk * Trying to add route security attributes when system
183145916cd2Sjpk * labeling service is not available, or when user supllies
183245916cd2Sjpk * more than the maximum number of security attributes
183345916cd2Sjpk * allowed per request.
183445916cd2Sjpk */
183545916cd2Sjpk if ((sacnt > 0 && !is_system_labeled()) ||
183645916cd2Sjpk sacnt > TSOL_RTSA_REQUEST_MAX)
183745916cd2Sjpk return (EINVAL);
183845916cd2Sjpk
183945916cd2Sjpk /* Ensure valid credentials */
184045916cd2Sjpk if ((err = rtsa_validate(&((tsol_rtsecattr_t *)cp)->
184145916cd2Sjpk rtsa_attr[0])) != 0) {
184245916cd2Sjpk cp += sizeof (*sp);
184345916cd2Sjpk return (err);
184445916cd2Sjpk }
184545916cd2Sjpk
184645916cd2Sjpk bcopy(cp, sp, sizeof (*sp));
184745916cd2Sjpk cp += sizeof (*sp);
184845916cd2Sjpk return (0);
184945916cd2Sjpk }
185045916cd2Sjpk
185145916cd2Sjpk int
tsol_ire_init_gwattr(ire_t * ire,uchar_t ipversion,tsol_gc_t * gc)1852bd670b35SErik Nordmark tsol_ire_init_gwattr(ire_t *ire, uchar_t ipversion, tsol_gc_t *gc)
185345916cd2Sjpk {
185445916cd2Sjpk tsol_ire_gw_secattr_t *attrp;
185545916cd2Sjpk boolean_t exists = B_FALSE;
185645916cd2Sjpk in_addr_t ga_addr4;
185745916cd2Sjpk void *paddr = NULL;
1858bd670b35SErik Nordmark tsol_gcgrp_t *gcgrp = NULL;
185945916cd2Sjpk
186045916cd2Sjpk ASSERT(ire != NULL);
186145916cd2Sjpk
186245916cd2Sjpk /*
186345916cd2Sjpk * The only time that attrp can be NULL is when this routine is
186445916cd2Sjpk * called for the first time during the creation/initialization
186545916cd2Sjpk * of the corresponding IRE. It will only get cleared when the
186645916cd2Sjpk * IRE is deleted.
186745916cd2Sjpk */
186845916cd2Sjpk if ((attrp = ire->ire_gw_secattr) == NULL) {
186945916cd2Sjpk attrp = ire_gw_secattr_alloc(KM_NOSLEEP);
187045916cd2Sjpk if (attrp == NULL)
187145916cd2Sjpk return (ENOMEM);
187245916cd2Sjpk ire->ire_gw_secattr = attrp;
187345916cd2Sjpk } else {
187445916cd2Sjpk exists = B_TRUE;
187545916cd2Sjpk mutex_enter(&attrp->igsa_lock);
187645916cd2Sjpk
187745916cd2Sjpk if (attrp->igsa_rhc != NULL) {
187845916cd2Sjpk TNRHC_RELE(attrp->igsa_rhc);
187945916cd2Sjpk attrp->igsa_rhc = NULL;
188045916cd2Sjpk }
188145916cd2Sjpk
188245916cd2Sjpk if (attrp->igsa_gc != NULL)
188345916cd2Sjpk GC_REFRELE(attrp->igsa_gc);
188445916cd2Sjpk }
188545916cd2Sjpk ASSERT(!exists || MUTEX_HELD(&attrp->igsa_lock));
188645916cd2Sjpk
188745916cd2Sjpk /*
188845916cd2Sjpk * References already held by caller and we keep them;
1889bd670b35SErik Nordmark * note that gc may be set to NULL to clear out igsa_gc.
189045916cd2Sjpk */
189145916cd2Sjpk attrp->igsa_gc = gc;
189245916cd2Sjpk
1893bd670b35SErik Nordmark if (gc != NULL) {
189445916cd2Sjpk gcgrp = gc->gc_grp;
189545916cd2Sjpk ASSERT(gcgrp != NULL);
189645916cd2Sjpk }
189745916cd2Sjpk
189845916cd2Sjpk /*
189945916cd2Sjpk * Intialize the template for gateway; we use the gateway's
190045916cd2Sjpk * address found in either the passed in gateway credential
190145916cd2Sjpk * or group pointer, or the ire_gateway_addr{_v6} field.
190245916cd2Sjpk */
190345916cd2Sjpk if (gcgrp != NULL) {
190445916cd2Sjpk tsol_gcgrp_addr_t *ga = &gcgrp->gcgrp_addr;
190545916cd2Sjpk
190645916cd2Sjpk /*
190745916cd2Sjpk * Caller is holding a reference, and that we don't
190845916cd2Sjpk * need to hold any lock to access the address.
190945916cd2Sjpk */
191045916cd2Sjpk if (ipversion == IPV4_VERSION) {
191145916cd2Sjpk ASSERT(ga->ga_af == AF_INET);
191245916cd2Sjpk IN6_V4MAPPED_TO_IPADDR(&ga->ga_addr, ga_addr4);
191345916cd2Sjpk paddr = &ga_addr4;
191445916cd2Sjpk } else {
191545916cd2Sjpk ASSERT(ga->ga_af == AF_INET6);
191645916cd2Sjpk paddr = &ga->ga_addr;
191745916cd2Sjpk }
1918bd670b35SErik Nordmark } else if (ire->ire_type & IRE_OFFLINK) {
1919bd670b35SErik Nordmark if (ipversion == IPV6_VERSION)
192045916cd2Sjpk paddr = &ire->ire_gateway_addr_v6;
1921bd670b35SErik Nordmark else if (ipversion == IPV4_VERSION)
192245916cd2Sjpk paddr = &ire->ire_gateway_addr;
192345916cd2Sjpk }
192445916cd2Sjpk
192545916cd2Sjpk /*
192645916cd2Sjpk * Lookup the gateway template; note that we could get an internal
192745916cd2Sjpk * template here, which we cache anyway. During IRE matching, we'll
192845916cd2Sjpk * try to update this gateway template cache and hopefully get a
192945916cd2Sjpk * real one.
193045916cd2Sjpk */
193145916cd2Sjpk if (paddr != NULL) {
1932bfabfc35Skp158701 attrp->igsa_rhc = find_rhc(paddr, ipversion, B_FALSE);
193345916cd2Sjpk }
193445916cd2Sjpk
193545916cd2Sjpk if (exists)
193645916cd2Sjpk mutex_exit(&attrp->igsa_lock);
193745916cd2Sjpk
193845916cd2Sjpk return (0);
193945916cd2Sjpk }
194045916cd2Sjpk
194145916cd2Sjpk /*
194245916cd2Sjpk * This function figures the type of MLP that we'll be using based on the
194345916cd2Sjpk * address that the user is binding and the zone. If the address is
194445916cd2Sjpk * unspecified, then we're looking at both private and shared. If it's one
194545916cd2Sjpk * of the zone's private addresses, then it's private only. If it's one
19467b564b02SJarrett Lu * of the global addresses, then it's shared only. Multicast addresses are
19477b564b02SJarrett Lu * treated same as unspecified address.
194845916cd2Sjpk *
194945916cd2Sjpk * If we can't figure out what it is, then return mlptSingle. That's actually
195045916cd2Sjpk * an error case.
1951f4b3ec61Sdh155122 *
1952bd670b35SErik Nordmark * The callers are assumed to pass in zone->zone_id and not the zoneid that
1953f4b3ec61Sdh155122 * is stored in a conn_t (since the latter will be GLOBAL_ZONEID in an
1954f4b3ec61Sdh155122 * exclusive stack zone).
195545916cd2Sjpk */
195645916cd2Sjpk mlp_type_t
tsol_mlp_addr_type(zoneid_t zoneid,uchar_t version,const void * addr,ip_stack_t * ipst)1957f4b3ec61Sdh155122 tsol_mlp_addr_type(zoneid_t zoneid, uchar_t version, const void *addr,
1958f4b3ec61Sdh155122 ip_stack_t *ipst)
195945916cd2Sjpk {
196045916cd2Sjpk in_addr_t in4;
196145916cd2Sjpk ire_t *ire;
196245916cd2Sjpk ipif_t *ipif;
196345916cd2Sjpk zoneid_t addrzone;
1964f4b3ec61Sdh155122 zoneid_t ip_zoneid;
196545916cd2Sjpk
196645916cd2Sjpk ASSERT(addr != NULL);
196745916cd2Sjpk
1968f4b3ec61Sdh155122 /*
1969f4b3ec61Sdh155122 * For exclusive stacks we set the zoneid to zero
1970f4b3ec61Sdh155122 * to operate as if in the global zone for IRE and conn_t comparisons.
1971f4b3ec61Sdh155122 */
1972f4b3ec61Sdh155122 if (ipst->ips_netstack->netstack_stackid != GLOBAL_NETSTACKID)
1973f4b3ec61Sdh155122 ip_zoneid = GLOBAL_ZONEID;
1974f4b3ec61Sdh155122 else
1975f4b3ec61Sdh155122 ip_zoneid = zoneid;
1976f4b3ec61Sdh155122
197745916cd2Sjpk if (version == IPV6_VERSION &&
197845916cd2Sjpk IN6_IS_ADDR_V4MAPPED((const in6_addr_t *)addr)) {
197945916cd2Sjpk IN6_V4MAPPED_TO_IPADDR((const in6_addr_t *)addr, in4);
198045916cd2Sjpk addr = &in4;
198145916cd2Sjpk version = IPV4_VERSION;
198245916cd2Sjpk }
198345916cd2Sjpk
1984bd670b35SErik Nordmark /* Check whether the IRE_LOCAL (or ipif) is ALL_ZONES */
198545916cd2Sjpk if (version == IPV4_VERSION) {
198645916cd2Sjpk in4 = *(const in_addr_t *)addr;
19877b564b02SJarrett Lu if ((in4 == INADDR_ANY) || CLASSD(in4)) {
198845916cd2Sjpk return (mlptBoth);
1989f4b3ec61Sdh155122 }
1990bd670b35SErik Nordmark ire = ire_ftable_lookup_v4(in4, 0, 0, IRE_LOCAL|IRE_LOOPBACK,
1991bd670b35SErik Nordmark NULL, ip_zoneid, NULL, MATCH_IRE_TYPE | MATCH_IRE_ZONEONLY,
1992bd670b35SErik Nordmark 0, ipst, NULL);
199345916cd2Sjpk } else {
19947b564b02SJarrett Lu if (IN6_IS_ADDR_UNSPECIFIED((const in6_addr_t *)addr) ||
19957b564b02SJarrett Lu IN6_IS_ADDR_MULTICAST((const in6_addr_t *)addr)) {
199645916cd2Sjpk return (mlptBoth);
1997f4b3ec61Sdh155122 }
1998bd670b35SErik Nordmark ire = ire_ftable_lookup_v6(addr, 0, 0, IRE_LOCAL|IRE_LOOPBACK,
1999bd670b35SErik Nordmark NULL, ip_zoneid, NULL, MATCH_IRE_TYPE | MATCH_IRE_ZONEONLY,
2000bd670b35SErik Nordmark 0, ipst, NULL);
200145916cd2Sjpk }
200245916cd2Sjpk /*
200345916cd2Sjpk * If we can't find the IRE, then we have to behave exactly like
2004bd670b35SErik Nordmark * ip_laddr_verify_{v4,v6}. That means looking up the IPIF so that
2005bd670b35SErik Nordmark * users can bind to addresses on "down" interfaces.
200645916cd2Sjpk *
200745916cd2Sjpk * If we can't find that either, then the bind is going to fail, so
200845916cd2Sjpk * just give up. Note that there's a miniscule chance that the address
200945916cd2Sjpk * is in transition, but we don't bother handling that.
201045916cd2Sjpk */
201145916cd2Sjpk if (ire == NULL) {
201245916cd2Sjpk if (version == IPV4_VERSION)
201345916cd2Sjpk ipif = ipif_lookup_addr(*(const in_addr_t *)addr, NULL,
2014bd670b35SErik Nordmark ip_zoneid, ipst);
201545916cd2Sjpk else
201645916cd2Sjpk ipif = ipif_lookup_addr_v6((const in6_addr_t *)addr,
2017bd670b35SErik Nordmark NULL, ip_zoneid, ipst);
2018f4b3ec61Sdh155122 if (ipif == NULL) {
201945916cd2Sjpk return (mlptSingle);
2020f4b3ec61Sdh155122 }
202145916cd2Sjpk addrzone = ipif->ipif_zoneid;
202245916cd2Sjpk ipif_refrele(ipif);
202345916cd2Sjpk } else {
202445916cd2Sjpk addrzone = ire->ire_zoneid;
202545916cd2Sjpk ire_refrele(ire);
202645916cd2Sjpk }
202745916cd2Sjpk return (addrzone == ALL_ZONES ? mlptShared : mlptPrivate);
202845916cd2Sjpk }
202945916cd2Sjpk
203045916cd2Sjpk /*
203145916cd2Sjpk * Since we are configuring local interfaces, and we know trusted
203245916cd2Sjpk * extension CDE requires local interfaces to be cipso host type in
203345916cd2Sjpk * order to function correctly, we'll associate a cipso template
203445916cd2Sjpk * to each local interface and let the interface come up. Configuring
203545916cd2Sjpk * a local interface to be "unlabeled" host type is a configuration error.
203645916cd2Sjpk * We'll override that error and make the interface host type to be cipso
203745916cd2Sjpk * here.
203845916cd2Sjpk *
203945916cd2Sjpk * The code is optimized for the usual "success" case and unwinds things on
204045916cd2Sjpk * error. We don't want to go to the trouble and expense of formatting the
204145916cd2Sjpk * interface name for the usual case where everything is configured correctly.
204245916cd2Sjpk */
204345916cd2Sjpk boolean_t
tsol_check_interface_address(const ipif_t * ipif)204445916cd2Sjpk tsol_check_interface_address(const ipif_t *ipif)
204545916cd2Sjpk {
204645916cd2Sjpk tsol_tpc_t *tp;
204745916cd2Sjpk char addrbuf[INET6_ADDRSTRLEN];
204845916cd2Sjpk int af;
204945916cd2Sjpk const void *addr;
205045916cd2Sjpk zone_t *zone;
205145916cd2Sjpk ts_label_t *plabel;
205245916cd2Sjpk const bslabel_t *label;
2053*e899e593SGirish Moodalbail char ifname[LIFNAMSIZ];
205445916cd2Sjpk boolean_t retval;
205545916cd2Sjpk tsol_rhent_t rhent;
2056f4b3ec61Sdh155122 netstack_t *ns = ipif->ipif_ill->ill_ipst->ips_netstack;
205745916cd2Sjpk
205845916cd2Sjpk if (IN6_IS_ADDR_V4MAPPED(&ipif->ipif_v6lcl_addr)) {
205945916cd2Sjpk af = AF_INET;
206045916cd2Sjpk addr = &V4_PART_OF_V6(ipif->ipif_v6lcl_addr);
206145916cd2Sjpk } else {
206245916cd2Sjpk af = AF_INET6;
206345916cd2Sjpk addr = &ipif->ipif_v6lcl_addr;
206445916cd2Sjpk }
206545916cd2Sjpk
206645916cd2Sjpk tp = find_tpc(&ipif->ipif_v6lcl_addr, IPV6_VERSION, B_FALSE);
2067f4b3ec61Sdh155122
2068f4b3ec61Sdh155122 /* assumes that ALL_ZONES implies that there is no exclusive stack */
2069f4b3ec61Sdh155122 if (ipif->ipif_zoneid == ALL_ZONES) {
2070f4b3ec61Sdh155122 zone = NULL;
2071f4b3ec61Sdh155122 } else if (ns->netstack_stackid == GLOBAL_NETSTACKID) {
2072f4b3ec61Sdh155122 /* Shared stack case */
2073f4b3ec61Sdh155122 zone = zone_find_by_id(ipif->ipif_zoneid);
2074f4b3ec61Sdh155122 } else {
2075f4b3ec61Sdh155122 /* Exclusive stack case */
2076f4b3ec61Sdh155122 zone = zone_find_by_id(crgetzoneid(ipif->ipif_ill->ill_credp));
2077f4b3ec61Sdh155122 }
207845916cd2Sjpk if (zone != NULL) {
207945916cd2Sjpk plabel = zone->zone_slabel;
208045916cd2Sjpk ASSERT(plabel != NULL);
208145916cd2Sjpk label = label2bslabel(plabel);
208245916cd2Sjpk }
208345916cd2Sjpk
208445916cd2Sjpk /*
208545916cd2Sjpk * If it's CIPSO and an all-zones address, then we're done.
208645916cd2Sjpk * If it's a CIPSO zone specific address, the zone's label
208745916cd2Sjpk * must be in the range or set specified in the template.
208845916cd2Sjpk * When the remote host entry is missing or the template
208945916cd2Sjpk * type is incorrect for this interface, we create a
209045916cd2Sjpk * CIPSO host entry in kernel and allow the interface to be
209145916cd2Sjpk * brought up as CIPSO type.
209245916cd2Sjpk */
209345916cd2Sjpk if (tp != NULL && (
209445916cd2Sjpk /* The all-zones case */
209545916cd2Sjpk (tp->tpc_tp.host_type == SUN_CIPSO &&
209645916cd2Sjpk tp->tpc_tp.tp_doi == default_doi &&
209745916cd2Sjpk ipif->ipif_zoneid == ALL_ZONES) ||
209845916cd2Sjpk /* The local-zone case */
209945916cd2Sjpk (zone != NULL && plabel->tsl_doi == tp->tpc_tp.tp_doi &&
210045916cd2Sjpk ((tp->tpc_tp.host_type == SUN_CIPSO &&
210145916cd2Sjpk (_blinrange(label, &tp->tpc_tp.tp_sl_range_cipso) ||
210245916cd2Sjpk blinlset(label, tp->tpc_tp.tp_sl_set_cipso))))))) {
210345916cd2Sjpk if (zone != NULL)
210445916cd2Sjpk zone_rele(zone);
210545916cd2Sjpk TPC_RELE(tp);
210645916cd2Sjpk return (B_TRUE);
210745916cd2Sjpk }
210845916cd2Sjpk
2109*e899e593SGirish Moodalbail ipif_get_name(ipif, ifname, sizeof (ifname));
211045916cd2Sjpk (void) inet_ntop(af, addr, addrbuf, sizeof (addrbuf));
211145916cd2Sjpk
211245916cd2Sjpk if (tp == NULL) {
211345916cd2Sjpk cmn_err(CE_NOTE, "template entry for %s missing. Default to "
211445916cd2Sjpk "CIPSO type for %s", ifname, addrbuf);
211545916cd2Sjpk retval = B_TRUE;
211645916cd2Sjpk } else if (tp->tpc_tp.host_type == UNLABELED) {
211745916cd2Sjpk cmn_err(CE_NOTE, "template type for %s incorrectly configured. "
211845916cd2Sjpk "Change to CIPSO type for %s", ifname, addrbuf);
211945916cd2Sjpk retval = B_TRUE;
212045916cd2Sjpk } else if (ipif->ipif_zoneid == ALL_ZONES) {
212145916cd2Sjpk if (tp->tpc_tp.host_type != SUN_CIPSO) {
212245916cd2Sjpk cmn_err(CE_NOTE, "%s failed: %s isn't set to CIPSO for "
212345916cd2Sjpk "all-zones. Converted to CIPSO.", ifname, addrbuf);
212445916cd2Sjpk retval = B_TRUE;
212545916cd2Sjpk } else {
212645916cd2Sjpk cmn_err(CE_NOTE, "%s failed: %s has wrong DOI %d "
212745916cd2Sjpk "instead of %d", ifname, addrbuf,
212845916cd2Sjpk tp->tpc_tp.tp_doi, default_doi);
212945916cd2Sjpk retval = B_FALSE;
213045916cd2Sjpk }
213145916cd2Sjpk } else if (zone == NULL) {
213245916cd2Sjpk cmn_err(CE_NOTE, "%s failed: zoneid %d unknown",
213345916cd2Sjpk ifname, ipif->ipif_zoneid);
213445916cd2Sjpk retval = B_FALSE;
213545916cd2Sjpk } else if (plabel->tsl_doi != tp->tpc_tp.tp_doi) {
213645916cd2Sjpk cmn_err(CE_NOTE, "%s failed: zone %s has DOI %d but %s has "
213745916cd2Sjpk "DOI %d", ifname, zone->zone_name, plabel->tsl_doi,
213845916cd2Sjpk addrbuf, tp->tpc_tp.tp_doi);
213945916cd2Sjpk retval = B_FALSE;
214045916cd2Sjpk } else {
214145916cd2Sjpk cmn_err(CE_NOTE, "%s failed: zone %s label incompatible with "
214245916cd2Sjpk "%s", ifname, zone->zone_name, addrbuf);
214345916cd2Sjpk tsol_print_label(label, "zone label");
214445916cd2Sjpk retval = B_FALSE;
214545916cd2Sjpk }
214645916cd2Sjpk
214745916cd2Sjpk if (zone != NULL)
214845916cd2Sjpk zone_rele(zone);
214945916cd2Sjpk if (tp != NULL)
215045916cd2Sjpk TPC_RELE(tp);
215145916cd2Sjpk if (retval) {
215245916cd2Sjpk /*
215345916cd2Sjpk * we've corrected a config error and let the interface
215445916cd2Sjpk * come up as cipso. Need to insert an rhent.
215545916cd2Sjpk */
215645916cd2Sjpk if ((rhent.rh_address.ta_family = af) == AF_INET) {
215745916cd2Sjpk rhent.rh_prefix = 32;
215845916cd2Sjpk rhent.rh_address.ta_addr_v4 = *(struct in_addr *)addr;
215945916cd2Sjpk } else {
216045916cd2Sjpk rhent.rh_prefix = 128;
216145916cd2Sjpk rhent.rh_address.ta_addr_v6 = *(in6_addr_t *)addr;
216245916cd2Sjpk }
216345916cd2Sjpk (void) strcpy(rhent.rh_template, "cipso");
216445916cd2Sjpk if (tnrh_load(&rhent) != 0) {
216545916cd2Sjpk cmn_err(CE_NOTE, "%s failed: Cannot insert CIPSO "
216645916cd2Sjpk "template for local addr %s", ifname, addrbuf);
216745916cd2Sjpk retval = B_FALSE;
216845916cd2Sjpk }
216945916cd2Sjpk }
217045916cd2Sjpk return (retval);
217145916cd2Sjpk }
2172