110616SSebastien.Roy@Sun.COM /*
210616SSebastien.Roy@Sun.COM * CDDL HEADER START
310616SSebastien.Roy@Sun.COM *
410616SSebastien.Roy@Sun.COM * The contents of this file are subject to the terms of the
510616SSebastien.Roy@Sun.COM * Common Development and Distribution License (the "License").
610616SSebastien.Roy@Sun.COM * You may not use this file except in compliance with the License.
710616SSebastien.Roy@Sun.COM *
810616SSebastien.Roy@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
910616SSebastien.Roy@Sun.COM * or http://www.opensolaris.org/os/licensing.
1010616SSebastien.Roy@Sun.COM * See the License for the specific language governing permissions
1110616SSebastien.Roy@Sun.COM * and limitations under the License.
1210616SSebastien.Roy@Sun.COM *
1310616SSebastien.Roy@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each
1410616SSebastien.Roy@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1510616SSebastien.Roy@Sun.COM * If applicable, add the following below this CDDL HEADER, with the
1610616SSebastien.Roy@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying
1710616SSebastien.Roy@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner]
1810616SSebastien.Roy@Sun.COM *
1910616SSebastien.Roy@Sun.COM * CDDL HEADER END
2010616SSebastien.Roy@Sun.COM */
2110616SSebastien.Roy@Sun.COM /*
22*12266SErik.Nordmark@Sun.COM * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
2310616SSebastien.Roy@Sun.COM */
2410616SSebastien.Roy@Sun.COM
2510616SSebastien.Roy@Sun.COM /*
2610616SSebastien.Roy@Sun.COM * iptun - IP Tunneling Driver
2710616SSebastien.Roy@Sun.COM *
2810616SSebastien.Roy@Sun.COM * This module is a GLDv3 driver that implements virtual datalinks over IP
2910616SSebastien.Roy@Sun.COM * (a.k.a, IP tunneling). The datalinks are managed through a dld ioctl
3010616SSebastien.Roy@Sun.COM * interface (see iptun_ctl.c), and registered with GLDv3 using
3110616SSebastien.Roy@Sun.COM * mac_register(). It implements the logic for various forms of IP (IPv4 or
3210616SSebastien.Roy@Sun.COM * IPv6) encapsulation within IP (IPv4 or IPv6) by interacting with the ip
3310616SSebastien.Roy@Sun.COM * module below it. Each virtual IP tunnel datalink has a conn_t associated
3410616SSebastien.Roy@Sun.COM * with it representing the "outer" IP connection.
3510616SSebastien.Roy@Sun.COM *
3610616SSebastien.Roy@Sun.COM * The module implements the following locking semantics:
3710616SSebastien.Roy@Sun.COM *
3810616SSebastien.Roy@Sun.COM * Lookups and deletions in iptun_hash are synchronized using iptun_hash_lock.
3910616SSebastien.Roy@Sun.COM * See comments above iptun_hash_lock for details.
4010616SSebastien.Roy@Sun.COM *
4110616SSebastien.Roy@Sun.COM * No locks are ever held while calling up to GLDv3. The general architecture
4210616SSebastien.Roy@Sun.COM * of GLDv3 requires this, as the mac perimeter (essentially a lock) for a
4310616SSebastien.Roy@Sun.COM * given link will be held while making downcalls (iptun_m_*() callbacks).
4410616SSebastien.Roy@Sun.COM * Because we need to hold locks while handling downcalls, holding these locks
4510616SSebastien.Roy@Sun.COM * while issuing upcalls results in deadlock scenarios. See the block comment
4610616SSebastien.Roy@Sun.COM * above iptun_task_cb() for details on how we safely issue upcalls without
4710616SSebastien.Roy@Sun.COM * holding any locks.
4810616SSebastien.Roy@Sun.COM *
4910616SSebastien.Roy@Sun.COM * The contents of each iptun_t is protected by an iptun_mutex which is held
5010616SSebastien.Roy@Sun.COM * in iptun_enter() (called by iptun_enter_by_linkid()), and exited in
5110616SSebastien.Roy@Sun.COM * iptun_exit().
5210616SSebastien.Roy@Sun.COM *
5310616SSebastien.Roy@Sun.COM * See comments in iptun_delete() and iptun_free() for details on how the
5410616SSebastien.Roy@Sun.COM * iptun_t is deleted safely.
5510616SSebastien.Roy@Sun.COM */
5610616SSebastien.Roy@Sun.COM
5710616SSebastien.Roy@Sun.COM #include <sys/types.h>
5810616SSebastien.Roy@Sun.COM #include <sys/kmem.h>
5910616SSebastien.Roy@Sun.COM #include <sys/errno.h>
6010616SSebastien.Roy@Sun.COM #include <sys/modhash.h>
6110616SSebastien.Roy@Sun.COM #include <sys/list.h>
6210616SSebastien.Roy@Sun.COM #include <sys/strsun.h>
6310616SSebastien.Roy@Sun.COM #include <sys/file.h>
6410616SSebastien.Roy@Sun.COM #include <sys/systm.h>
6510616SSebastien.Roy@Sun.COM #include <sys/tihdr.h>
6610616SSebastien.Roy@Sun.COM #include <sys/param.h>
6710616SSebastien.Roy@Sun.COM #include <sys/mac_provider.h>
6810616SSebastien.Roy@Sun.COM #include <sys/mac_ipv4.h>
6910616SSebastien.Roy@Sun.COM #include <sys/mac_ipv6.h>
7010616SSebastien.Roy@Sun.COM #include <sys/mac_6to4.h>
7110616SSebastien.Roy@Sun.COM #include <sys/tsol/tnet.h>
7210616SSebastien.Roy@Sun.COM #include <sys/sunldi.h>
7310616SSebastien.Roy@Sun.COM #include <netinet/in.h>
7410616SSebastien.Roy@Sun.COM #include <netinet/ip6.h>
7510616SSebastien.Roy@Sun.COM #include <inet/ip.h>
7610616SSebastien.Roy@Sun.COM #include <inet/ip_ire.h>
7710616SSebastien.Roy@Sun.COM #include <inet/ipsec_impl.h>
7811042SErik.Nordmark@Sun.COM #include <sys/tsol/label.h>
7911042SErik.Nordmark@Sun.COM #include <sys/tsol/tnet.h>
8010616SSebastien.Roy@Sun.COM #include <inet/iptun.h>
8110616SSebastien.Roy@Sun.COM #include "iptun_impl.h"
8210616SSebastien.Roy@Sun.COM
8310616SSebastien.Roy@Sun.COM /* Do the tunnel type and address family match? */
8410616SSebastien.Roy@Sun.COM #define IPTUN_ADDR_MATCH(iptun_type, family) \
8510616SSebastien.Roy@Sun.COM ((iptun_type == IPTUN_TYPE_IPV4 && family == AF_INET) || \
8610616SSebastien.Roy@Sun.COM (iptun_type == IPTUN_TYPE_IPV6 && family == AF_INET6) || \
8710616SSebastien.Roy@Sun.COM (iptun_type == IPTUN_TYPE_6TO4 && family == AF_INET))
8810616SSebastien.Roy@Sun.COM
8910616SSebastien.Roy@Sun.COM #define IPTUN_HASH_KEY(key) ((mod_hash_key_t)(uintptr_t)(key))
9010616SSebastien.Roy@Sun.COM
9110616SSebastien.Roy@Sun.COM #define IPTUN_MIN_IPV4_MTU 576 /* ip.h still uses 68 (!) */
9210616SSebastien.Roy@Sun.COM #define IPTUN_MIN_IPV6_MTU IPV6_MIN_MTU
9310616SSebastien.Roy@Sun.COM #define IPTUN_MAX_IPV4_MTU (IP_MAXPACKET - sizeof (ipha_t))
9410616SSebastien.Roy@Sun.COM #define IPTUN_MAX_IPV6_MTU (IP_MAXPACKET - sizeof (ip6_t) - \
9510616SSebastien.Roy@Sun.COM sizeof (iptun_encaplim_t))
9610616SSebastien.Roy@Sun.COM
9710616SSebastien.Roy@Sun.COM #define IPTUN_MIN_HOPLIMIT 1
9810616SSebastien.Roy@Sun.COM #define IPTUN_MAX_HOPLIMIT UINT8_MAX
9910616SSebastien.Roy@Sun.COM
10010616SSebastien.Roy@Sun.COM #define IPTUN_MIN_ENCAPLIMIT 0
10110616SSebastien.Roy@Sun.COM #define IPTUN_MAX_ENCAPLIMIT UINT8_MAX
10210616SSebastien.Roy@Sun.COM
10310616SSebastien.Roy@Sun.COM #define IPTUN_IPSEC_REQ_MASK (IPSEC_PREF_REQUIRED | IPSEC_PREF_NEVER)
10410616SSebastien.Roy@Sun.COM
10510616SSebastien.Roy@Sun.COM static iptun_encaplim_t iptun_encaplim_init = {
10610616SSebastien.Roy@Sun.COM { IPPROTO_NONE, 0 },
10710616SSebastien.Roy@Sun.COM IP6OPT_TUNNEL_LIMIT,
10810616SSebastien.Roy@Sun.COM 1,
10910616SSebastien.Roy@Sun.COM IPTUN_DEFAULT_ENCAPLIMIT, /* filled in with actual value later */
11010616SSebastien.Roy@Sun.COM IP6OPT_PADN,
11110616SSebastien.Roy@Sun.COM 1,
11210616SSebastien.Roy@Sun.COM 0
11310616SSebastien.Roy@Sun.COM };
11410616SSebastien.Roy@Sun.COM
11511042SErik.Nordmark@Sun.COM /*
11611042SErik.Nordmark@Sun.COM * Table containing per-iptun-type information.
11711042SErik.Nordmark@Sun.COM * Since IPv6 can run over all of these we have the IPv6 min as the min MTU.
11811042SErik.Nordmark@Sun.COM */
11910616SSebastien.Roy@Sun.COM static iptun_typeinfo_t iptun_type_table[] = {
12011042SErik.Nordmark@Sun.COM { IPTUN_TYPE_IPV4, MAC_PLUGIN_IDENT_IPV4, IPV4_VERSION,
12111042SErik.Nordmark@Sun.COM IPTUN_MIN_IPV6_MTU, IPTUN_MAX_IPV4_MTU, B_TRUE },
12211042SErik.Nordmark@Sun.COM { IPTUN_TYPE_IPV6, MAC_PLUGIN_IDENT_IPV6, IPV6_VERSION,
12310616SSebastien.Roy@Sun.COM IPTUN_MIN_IPV6_MTU, IPTUN_MAX_IPV6_MTU, B_TRUE },
12411042SErik.Nordmark@Sun.COM { IPTUN_TYPE_6TO4, MAC_PLUGIN_IDENT_6TO4, IPV4_VERSION,
12511042SErik.Nordmark@Sun.COM IPTUN_MIN_IPV6_MTU, IPTUN_MAX_IPV4_MTU, B_FALSE },
12611042SErik.Nordmark@Sun.COM { IPTUN_TYPE_UNKNOWN, NULL, 0, 0, 0, B_FALSE }
12710616SSebastien.Roy@Sun.COM };
12810616SSebastien.Roy@Sun.COM
12910616SSebastien.Roy@Sun.COM /*
13010616SSebastien.Roy@Sun.COM * iptun_hash is an iptun_t lookup table by link ID protected by
13110616SSebastien.Roy@Sun.COM * iptun_hash_lock. While the hash table's integrity is maintained via
13210616SSebastien.Roy@Sun.COM * internal locking in the mod_hash_*() functions, we need additional locking
13310616SSebastien.Roy@Sun.COM * so that an iptun_t cannot be deleted after a hash lookup has returned an
13410616SSebastien.Roy@Sun.COM * iptun_t and before iptun_lock has been entered. As such, we use
13510616SSebastien.Roy@Sun.COM * iptun_hash_lock when doing lookups and removals from iptun_hash.
13610616SSebastien.Roy@Sun.COM */
13710616SSebastien.Roy@Sun.COM mod_hash_t *iptun_hash;
13810616SSebastien.Roy@Sun.COM static kmutex_t iptun_hash_lock;
13910616SSebastien.Roy@Sun.COM
14010616SSebastien.Roy@Sun.COM static uint_t iptun_tunnelcount; /* total for all stacks */
14110616SSebastien.Roy@Sun.COM kmem_cache_t *iptun_cache;
14210616SSebastien.Roy@Sun.COM ddi_taskq_t *iptun_taskq;
14310616SSebastien.Roy@Sun.COM
14410616SSebastien.Roy@Sun.COM typedef enum {
14510616SSebastien.Roy@Sun.COM IPTUN_TASK_MTU_UPDATE, /* tell mac about new tunnel link MTU */
14610616SSebastien.Roy@Sun.COM IPTUN_TASK_LADDR_UPDATE, /* tell mac about new local address */
14710616SSebastien.Roy@Sun.COM IPTUN_TASK_RADDR_UPDATE, /* tell mac about new remote address */
14810616SSebastien.Roy@Sun.COM IPTUN_TASK_LINK_UPDATE, /* tell mac about new link state */
14910616SSebastien.Roy@Sun.COM IPTUN_TASK_PDATA_UPDATE /* tell mac about updated plugin data */
15010616SSebastien.Roy@Sun.COM } iptun_task_t;
15110616SSebastien.Roy@Sun.COM
15210616SSebastien.Roy@Sun.COM typedef struct iptun_task_data_s {
15310616SSebastien.Roy@Sun.COM iptun_task_t itd_task;
15410616SSebastien.Roy@Sun.COM datalink_id_t itd_linkid;
15510616SSebastien.Roy@Sun.COM } iptun_task_data_t;
15610616SSebastien.Roy@Sun.COM
15710616SSebastien.Roy@Sun.COM static void iptun_task_dispatch(iptun_t *, iptun_task_t);
15810616SSebastien.Roy@Sun.COM static int iptun_enter(iptun_t *);
15910616SSebastien.Roy@Sun.COM static void iptun_exit(iptun_t *);
16010616SSebastien.Roy@Sun.COM static void iptun_headergen(iptun_t *, boolean_t);
16110616SSebastien.Roy@Sun.COM static void iptun_drop_pkt(mblk_t *, uint64_t *);
16211042SErik.Nordmark@Sun.COM static void iptun_input(void *, mblk_t *, void *, ip_recv_attr_t *);
16311042SErik.Nordmark@Sun.COM static void iptun_input_icmp(void *, mblk_t *, void *, ip_recv_attr_t *);
16410616SSebastien.Roy@Sun.COM static void iptun_output(iptun_t *, mblk_t *);
16511042SErik.Nordmark@Sun.COM static uint32_t iptun_get_maxmtu(iptun_t *, ip_xmit_attr_t *, uint32_t);
16611042SErik.Nordmark@Sun.COM static uint32_t iptun_update_mtu(iptun_t *, ip_xmit_attr_t *, uint32_t);
16711042SErik.Nordmark@Sun.COM static uint32_t iptun_get_dst_pmtu(iptun_t *, ip_xmit_attr_t *);
16811042SErik.Nordmark@Sun.COM static void iptun_update_dst_pmtu(iptun_t *, ip_xmit_attr_t *);
16910616SSebastien.Roy@Sun.COM static int iptun_setladdr(iptun_t *, const struct sockaddr_storage *);
17010616SSebastien.Roy@Sun.COM
17111042SErik.Nordmark@Sun.COM static void iptun_output_6to4(iptun_t *, mblk_t *);
17211042SErik.Nordmark@Sun.COM static void iptun_output_common(iptun_t *, ip_xmit_attr_t *, mblk_t *);
17311042SErik.Nordmark@Sun.COM static boolean_t iptun_verifyicmp(conn_t *, void *, icmph_t *, icmp6_t *,
17411042SErik.Nordmark@Sun.COM ip_recv_attr_t *);
17511042SErik.Nordmark@Sun.COM
17611042SErik.Nordmark@Sun.COM static void iptun_notify(void *, ip_xmit_attr_t *, ixa_notify_type_t,
17711042SErik.Nordmark@Sun.COM ixa_notify_arg_t);
17811042SErik.Nordmark@Sun.COM
17910616SSebastien.Roy@Sun.COM static mac_callbacks_t iptun_m_callbacks;
18010616SSebastien.Roy@Sun.COM
18110616SSebastien.Roy@Sun.COM static int
iptun_m_getstat(void * arg,uint_t stat,uint64_t * val)18210616SSebastien.Roy@Sun.COM iptun_m_getstat(void *arg, uint_t stat, uint64_t *val)
18310616SSebastien.Roy@Sun.COM {
18410616SSebastien.Roy@Sun.COM iptun_t *iptun = arg;
18510616SSebastien.Roy@Sun.COM int err = 0;
18610616SSebastien.Roy@Sun.COM
18710616SSebastien.Roy@Sun.COM switch (stat) {
18810616SSebastien.Roy@Sun.COM case MAC_STAT_IERRORS:
18910616SSebastien.Roy@Sun.COM *val = iptun->iptun_ierrors;
19010616SSebastien.Roy@Sun.COM break;
19110616SSebastien.Roy@Sun.COM case MAC_STAT_OERRORS:
19210616SSebastien.Roy@Sun.COM *val = iptun->iptun_oerrors;
19310616SSebastien.Roy@Sun.COM break;
19410616SSebastien.Roy@Sun.COM case MAC_STAT_RBYTES:
19510616SSebastien.Roy@Sun.COM *val = iptun->iptun_rbytes;
19610616SSebastien.Roy@Sun.COM break;
19710616SSebastien.Roy@Sun.COM case MAC_STAT_IPACKETS:
19810616SSebastien.Roy@Sun.COM *val = iptun->iptun_ipackets;
19910616SSebastien.Roy@Sun.COM break;
20010616SSebastien.Roy@Sun.COM case MAC_STAT_OBYTES:
20110616SSebastien.Roy@Sun.COM *val = iptun->iptun_obytes;
20210616SSebastien.Roy@Sun.COM break;
20310616SSebastien.Roy@Sun.COM case MAC_STAT_OPACKETS:
20410616SSebastien.Roy@Sun.COM *val = iptun->iptun_opackets;
20510616SSebastien.Roy@Sun.COM break;
20610616SSebastien.Roy@Sun.COM case MAC_STAT_NORCVBUF:
20710616SSebastien.Roy@Sun.COM *val = iptun->iptun_norcvbuf;
20810616SSebastien.Roy@Sun.COM break;
20910616SSebastien.Roy@Sun.COM case MAC_STAT_NOXMTBUF:
21010616SSebastien.Roy@Sun.COM *val = iptun->iptun_noxmtbuf;
21110616SSebastien.Roy@Sun.COM break;
21210616SSebastien.Roy@Sun.COM default:
21310616SSebastien.Roy@Sun.COM err = ENOTSUP;
21410616SSebastien.Roy@Sun.COM }
21510616SSebastien.Roy@Sun.COM
21610616SSebastien.Roy@Sun.COM return (err);
21710616SSebastien.Roy@Sun.COM }
21810616SSebastien.Roy@Sun.COM
21910616SSebastien.Roy@Sun.COM static int
iptun_m_start(void * arg)22010616SSebastien.Roy@Sun.COM iptun_m_start(void *arg)
22110616SSebastien.Roy@Sun.COM {
22210616SSebastien.Roy@Sun.COM iptun_t *iptun = arg;
22310616SSebastien.Roy@Sun.COM int err;
22410616SSebastien.Roy@Sun.COM
22510616SSebastien.Roy@Sun.COM if ((err = iptun_enter(iptun)) == 0) {
22610616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_MAC_STARTED;
22710616SSebastien.Roy@Sun.COM iptun_task_dispatch(iptun, IPTUN_TASK_LINK_UPDATE);
22810616SSebastien.Roy@Sun.COM iptun_exit(iptun);
22910616SSebastien.Roy@Sun.COM }
23010616SSebastien.Roy@Sun.COM return (err);
23110616SSebastien.Roy@Sun.COM }
23210616SSebastien.Roy@Sun.COM
23310616SSebastien.Roy@Sun.COM static void
iptun_m_stop(void * arg)23410616SSebastien.Roy@Sun.COM iptun_m_stop(void *arg)
23510616SSebastien.Roy@Sun.COM {
23610616SSebastien.Roy@Sun.COM iptun_t *iptun = arg;
23710616SSebastien.Roy@Sun.COM
23810616SSebastien.Roy@Sun.COM if (iptun_enter(iptun) == 0) {
23910616SSebastien.Roy@Sun.COM iptun->iptun_flags &= ~IPTUN_MAC_STARTED;
24010616SSebastien.Roy@Sun.COM iptun_task_dispatch(iptun, IPTUN_TASK_LINK_UPDATE);
24110616SSebastien.Roy@Sun.COM iptun_exit(iptun);
24210616SSebastien.Roy@Sun.COM }
24310616SSebastien.Roy@Sun.COM }
24410616SSebastien.Roy@Sun.COM
24510616SSebastien.Roy@Sun.COM /*
24610616SSebastien.Roy@Sun.COM * iptun_m_setpromisc() does nothing and always succeeds. This is because a
24710616SSebastien.Roy@Sun.COM * tunnel data-link only ever receives packets that are destined exclusively
24810616SSebastien.Roy@Sun.COM * for the local address of the tunnel.
24910616SSebastien.Roy@Sun.COM */
25010616SSebastien.Roy@Sun.COM /* ARGSUSED */
25110616SSebastien.Roy@Sun.COM static int
iptun_m_setpromisc(void * arg,boolean_t on)25210616SSebastien.Roy@Sun.COM iptun_m_setpromisc(void *arg, boolean_t on)
25310616SSebastien.Roy@Sun.COM {
25410616SSebastien.Roy@Sun.COM return (0);
25510616SSebastien.Roy@Sun.COM }
25610616SSebastien.Roy@Sun.COM
25710616SSebastien.Roy@Sun.COM /* ARGSUSED */
25810616SSebastien.Roy@Sun.COM static int
iptun_m_multicst(void * arg,boolean_t add,const uint8_t * addrp)25910616SSebastien.Roy@Sun.COM iptun_m_multicst(void *arg, boolean_t add, const uint8_t *addrp)
26010616SSebastien.Roy@Sun.COM {
26110616SSebastien.Roy@Sun.COM return (ENOTSUP);
26210616SSebastien.Roy@Sun.COM }
26310616SSebastien.Roy@Sun.COM
26410616SSebastien.Roy@Sun.COM /*
26510616SSebastien.Roy@Sun.COM * iptun_m_unicst() sets the local address.
26610616SSebastien.Roy@Sun.COM */
26710616SSebastien.Roy@Sun.COM /* ARGSUSED */
26810616SSebastien.Roy@Sun.COM static int
iptun_m_unicst(void * arg,const uint8_t * addrp)26910616SSebastien.Roy@Sun.COM iptun_m_unicst(void *arg, const uint8_t *addrp)
27010616SSebastien.Roy@Sun.COM {
27110616SSebastien.Roy@Sun.COM iptun_t *iptun = arg;
27210616SSebastien.Roy@Sun.COM int err;
27310616SSebastien.Roy@Sun.COM struct sockaddr_storage ss;
27410616SSebastien.Roy@Sun.COM struct sockaddr_in *sin;
27510616SSebastien.Roy@Sun.COM struct sockaddr_in6 *sin6;
27610616SSebastien.Roy@Sun.COM
27710616SSebastien.Roy@Sun.COM if ((err = iptun_enter(iptun)) == 0) {
27810616SSebastien.Roy@Sun.COM switch (iptun->iptun_typeinfo->iti_ipvers) {
27910616SSebastien.Roy@Sun.COM case IPV4_VERSION:
28010616SSebastien.Roy@Sun.COM sin = (struct sockaddr_in *)&ss;
28110616SSebastien.Roy@Sun.COM sin->sin_family = AF_INET;
28210616SSebastien.Roy@Sun.COM bcopy(addrp, &sin->sin_addr, sizeof (in_addr_t));
28310616SSebastien.Roy@Sun.COM break;
28410616SSebastien.Roy@Sun.COM case IPV6_VERSION:
28510616SSebastien.Roy@Sun.COM sin6 = (struct sockaddr_in6 *)&ss;
28610616SSebastien.Roy@Sun.COM sin6->sin6_family = AF_INET6;
28710616SSebastien.Roy@Sun.COM bcopy(addrp, &sin6->sin6_addr, sizeof (in6_addr_t));
28810616SSebastien.Roy@Sun.COM break;
28910616SSebastien.Roy@Sun.COM default:
29010616SSebastien.Roy@Sun.COM ASSERT(0);
29110616SSebastien.Roy@Sun.COM }
29210616SSebastien.Roy@Sun.COM err = iptun_setladdr(iptun, &ss);
29310616SSebastien.Roy@Sun.COM iptun_exit(iptun);
29410616SSebastien.Roy@Sun.COM }
29510616SSebastien.Roy@Sun.COM return (err);
29610616SSebastien.Roy@Sun.COM }
29710616SSebastien.Roy@Sun.COM
29810616SSebastien.Roy@Sun.COM static mblk_t *
iptun_m_tx(void * arg,mblk_t * mpchain)29910616SSebastien.Roy@Sun.COM iptun_m_tx(void *arg, mblk_t *mpchain)
30010616SSebastien.Roy@Sun.COM {
30110616SSebastien.Roy@Sun.COM mblk_t *mp, *nmp;
30210616SSebastien.Roy@Sun.COM iptun_t *iptun = arg;
30310616SSebastien.Roy@Sun.COM
30410616SSebastien.Roy@Sun.COM if (!IS_IPTUN_RUNNING(iptun)) {
30510616SSebastien.Roy@Sun.COM iptun_drop_pkt(mpchain, &iptun->iptun_noxmtbuf);
30610616SSebastien.Roy@Sun.COM return (NULL);
30710616SSebastien.Roy@Sun.COM }
30810616SSebastien.Roy@Sun.COM
30910616SSebastien.Roy@Sun.COM for (mp = mpchain; mp != NULL; mp = nmp) {
31010616SSebastien.Roy@Sun.COM nmp = mp->b_next;
31110616SSebastien.Roy@Sun.COM mp->b_next = NULL;
31210616SSebastien.Roy@Sun.COM iptun_output(iptun, mp);
31310616SSebastien.Roy@Sun.COM }
31410616SSebastien.Roy@Sun.COM
31510616SSebastien.Roy@Sun.COM return (NULL);
31610616SSebastien.Roy@Sun.COM }
31710616SSebastien.Roy@Sun.COM
31810616SSebastien.Roy@Sun.COM /* ARGSUSED */
31910616SSebastien.Roy@Sun.COM static int
iptun_m_setprop(void * barg,const char * pr_name,mac_prop_id_t pr_num,uint_t pr_valsize,const void * pr_val)32010616SSebastien.Roy@Sun.COM iptun_m_setprop(void *barg, const char *pr_name, mac_prop_id_t pr_num,
32110616SSebastien.Roy@Sun.COM uint_t pr_valsize, const void *pr_val)
32210616SSebastien.Roy@Sun.COM {
32310616SSebastien.Roy@Sun.COM iptun_t *iptun = barg;
32410616SSebastien.Roy@Sun.COM uint32_t value = *(uint32_t *)pr_val;
32510616SSebastien.Roy@Sun.COM int err;
32610616SSebastien.Roy@Sun.COM
32710616SSebastien.Roy@Sun.COM /*
32810616SSebastien.Roy@Sun.COM * We need to enter this iptun_t since we'll be modifying the outer
32910616SSebastien.Roy@Sun.COM * header.
33010616SSebastien.Roy@Sun.COM */
33110616SSebastien.Roy@Sun.COM if ((err = iptun_enter(iptun)) != 0)
33210616SSebastien.Roy@Sun.COM return (err);
33310616SSebastien.Roy@Sun.COM
33410616SSebastien.Roy@Sun.COM switch (pr_num) {
33510616SSebastien.Roy@Sun.COM case MAC_PROP_IPTUN_HOPLIMIT:
33610616SSebastien.Roy@Sun.COM if (value < IPTUN_MIN_HOPLIMIT || value > IPTUN_MAX_HOPLIMIT) {
33710616SSebastien.Roy@Sun.COM err = EINVAL;
33810616SSebastien.Roy@Sun.COM break;
33910616SSebastien.Roy@Sun.COM }
34010616SSebastien.Roy@Sun.COM if (value != iptun->iptun_hoplimit) {
34110616SSebastien.Roy@Sun.COM iptun->iptun_hoplimit = (uint8_t)value;
34210616SSebastien.Roy@Sun.COM iptun_headergen(iptun, B_TRUE);
34310616SSebastien.Roy@Sun.COM }
34410616SSebastien.Roy@Sun.COM break;
34510616SSebastien.Roy@Sun.COM case MAC_PROP_IPTUN_ENCAPLIMIT:
34610616SSebastien.Roy@Sun.COM if (iptun->iptun_typeinfo->iti_type != IPTUN_TYPE_IPV6 ||
34710616SSebastien.Roy@Sun.COM value > IPTUN_MAX_ENCAPLIMIT) {
34810616SSebastien.Roy@Sun.COM err = EINVAL;
34910616SSebastien.Roy@Sun.COM break;
35010616SSebastien.Roy@Sun.COM }
35110616SSebastien.Roy@Sun.COM if (value != iptun->iptun_encaplimit) {
35210616SSebastien.Roy@Sun.COM iptun->iptun_encaplimit = (uint8_t)value;
35310616SSebastien.Roy@Sun.COM iptun_headergen(iptun, B_TRUE);
35410616SSebastien.Roy@Sun.COM }
35510616SSebastien.Roy@Sun.COM break;
35610616SSebastien.Roy@Sun.COM case MAC_PROP_MTU: {
35711042SErik.Nordmark@Sun.COM uint32_t maxmtu = iptun_get_maxmtu(iptun, NULL, 0);
35810616SSebastien.Roy@Sun.COM
35910616SSebastien.Roy@Sun.COM if (value < iptun->iptun_typeinfo->iti_minmtu ||
36010616SSebastien.Roy@Sun.COM value > maxmtu) {
36110616SSebastien.Roy@Sun.COM err = EINVAL;
36210616SSebastien.Roy@Sun.COM break;
36310616SSebastien.Roy@Sun.COM }
36410616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_FIXED_MTU;
36510616SSebastien.Roy@Sun.COM if (value != iptun->iptun_mtu) {
36610616SSebastien.Roy@Sun.COM iptun->iptun_mtu = value;
36710616SSebastien.Roy@Sun.COM iptun_task_dispatch(iptun, IPTUN_TASK_MTU_UPDATE);
36810616SSebastien.Roy@Sun.COM }
36910616SSebastien.Roy@Sun.COM break;
37010616SSebastien.Roy@Sun.COM }
37110616SSebastien.Roy@Sun.COM default:
37210616SSebastien.Roy@Sun.COM err = EINVAL;
37310616SSebastien.Roy@Sun.COM }
37410616SSebastien.Roy@Sun.COM iptun_exit(iptun);
37510616SSebastien.Roy@Sun.COM return (err);
37610616SSebastien.Roy@Sun.COM }
37710616SSebastien.Roy@Sun.COM
37810616SSebastien.Roy@Sun.COM /* ARGSUSED */
37910616SSebastien.Roy@Sun.COM static int
iptun_m_getprop(void * barg,const char * pr_name,mac_prop_id_t pr_num,uint_t pr_valsize,void * pr_val)38010616SSebastien.Roy@Sun.COM iptun_m_getprop(void *barg, const char *pr_name, mac_prop_id_t pr_num,
38111878SVenu.Iyer@Sun.COM uint_t pr_valsize, void *pr_val)
38210616SSebastien.Roy@Sun.COM {
38310616SSebastien.Roy@Sun.COM iptun_t *iptun = barg;
38410616SSebastien.Roy@Sun.COM int err;
38510616SSebastien.Roy@Sun.COM
38610616SSebastien.Roy@Sun.COM if ((err = iptun_enter(iptun)) != 0)
38710616SSebastien.Roy@Sun.COM return (err);
38810616SSebastien.Roy@Sun.COM
38911878SVenu.Iyer@Sun.COM switch (pr_num) {
39011878SVenu.Iyer@Sun.COM case MAC_PROP_IPTUN_HOPLIMIT:
39111878SVenu.Iyer@Sun.COM ASSERT(pr_valsize >= sizeof (uint32_t));
39211878SVenu.Iyer@Sun.COM *(uint32_t *)pr_val = iptun->iptun_hoplimit;
39311878SVenu.Iyer@Sun.COM break;
39411878SVenu.Iyer@Sun.COM
39511878SVenu.Iyer@Sun.COM case MAC_PROP_IPTUN_ENCAPLIMIT:
39611878SVenu.Iyer@Sun.COM *(uint32_t *)pr_val = iptun->iptun_encaplimit;
39711878SVenu.Iyer@Sun.COM break;
39811878SVenu.Iyer@Sun.COM default:
39910616SSebastien.Roy@Sun.COM err = ENOTSUP;
40010616SSebastien.Roy@Sun.COM }
40111878SVenu.Iyer@Sun.COM done:
40211878SVenu.Iyer@Sun.COM iptun_exit(iptun);
40311878SVenu.Iyer@Sun.COM return (err);
40411878SVenu.Iyer@Sun.COM }
40511878SVenu.Iyer@Sun.COM
40611878SVenu.Iyer@Sun.COM /* ARGSUSED */
40711878SVenu.Iyer@Sun.COM static void
iptun_m_propinfo(void * barg,const char * pr_name,mac_prop_id_t pr_num,mac_prop_info_handle_t prh)40811878SVenu.Iyer@Sun.COM iptun_m_propinfo(void *barg, const char *pr_name, mac_prop_id_t pr_num,
40911878SVenu.Iyer@Sun.COM mac_prop_info_handle_t prh)
41011878SVenu.Iyer@Sun.COM {
41111878SVenu.Iyer@Sun.COM iptun_t *iptun = barg;
41210616SSebastien.Roy@Sun.COM
41310616SSebastien.Roy@Sun.COM switch (pr_num) {
41410616SSebastien.Roy@Sun.COM case MAC_PROP_IPTUN_HOPLIMIT:
41511878SVenu.Iyer@Sun.COM mac_prop_info_set_range_uint32(prh,
41611878SVenu.Iyer@Sun.COM IPTUN_MIN_HOPLIMIT, IPTUN_MAX_HOPLIMIT);
41711878SVenu.Iyer@Sun.COM mac_prop_info_set_default_uint32(prh, IPTUN_DEFAULT_HOPLIMIT);
41810616SSebastien.Roy@Sun.COM break;
41911878SVenu.Iyer@Sun.COM
42010616SSebastien.Roy@Sun.COM case MAC_PROP_IPTUN_ENCAPLIMIT:
42111878SVenu.Iyer@Sun.COM if (iptun->iptun_typeinfo->iti_type != IPTUN_TYPE_IPV6)
42211878SVenu.Iyer@Sun.COM break;
42311878SVenu.Iyer@Sun.COM mac_prop_info_set_range_uint32(prh,
42411878SVenu.Iyer@Sun.COM IPTUN_MIN_ENCAPLIMIT, IPTUN_MAX_ENCAPLIMIT);
42511878SVenu.Iyer@Sun.COM mac_prop_info_set_default_uint32(prh, IPTUN_DEFAULT_ENCAPLIMIT);
42610616SSebastien.Roy@Sun.COM break;
42711878SVenu.Iyer@Sun.COM case MAC_PROP_MTU:
42811878SVenu.Iyer@Sun.COM mac_prop_info_set_range_uint32(prh,
42911878SVenu.Iyer@Sun.COM iptun->iptun_typeinfo->iti_minmtu,
43011878SVenu.Iyer@Sun.COM iptun_get_maxmtu(iptun, NULL, 0));
43110616SSebastien.Roy@Sun.COM break;
43210616SSebastien.Roy@Sun.COM }
43310616SSebastien.Roy@Sun.COM }
43410616SSebastien.Roy@Sun.COM
43510616SSebastien.Roy@Sun.COM uint_t
iptun_count(void)43610616SSebastien.Roy@Sun.COM iptun_count(void)
43710616SSebastien.Roy@Sun.COM {
43810616SSebastien.Roy@Sun.COM return (iptun_tunnelcount);
43910616SSebastien.Roy@Sun.COM }
44010616SSebastien.Roy@Sun.COM
44110616SSebastien.Roy@Sun.COM /*
44210616SSebastien.Roy@Sun.COM * Enter an iptun_t exclusively. This is essentially just a mutex, but we
44310616SSebastien.Roy@Sun.COM * don't allow iptun_enter() to succeed on a tunnel if it's in the process of
44410616SSebastien.Roy@Sun.COM * being deleted.
44510616SSebastien.Roy@Sun.COM */
44610616SSebastien.Roy@Sun.COM static int
iptun_enter(iptun_t * iptun)44710616SSebastien.Roy@Sun.COM iptun_enter(iptun_t *iptun)
44810616SSebastien.Roy@Sun.COM {
44910616SSebastien.Roy@Sun.COM mutex_enter(&iptun->iptun_lock);
45010616SSebastien.Roy@Sun.COM while (iptun->iptun_flags & IPTUN_DELETE_PENDING)
45110616SSebastien.Roy@Sun.COM cv_wait(&iptun->iptun_enter_cv, &iptun->iptun_lock);
45210616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_CONDEMNED) {
45310616SSebastien.Roy@Sun.COM mutex_exit(&iptun->iptun_lock);
45410616SSebastien.Roy@Sun.COM return (ENOENT);
45510616SSebastien.Roy@Sun.COM }
45610616SSebastien.Roy@Sun.COM return (0);
45710616SSebastien.Roy@Sun.COM }
45810616SSebastien.Roy@Sun.COM
45910616SSebastien.Roy@Sun.COM /*
46010616SSebastien.Roy@Sun.COM * Exit the tunnel entered in iptun_enter().
46110616SSebastien.Roy@Sun.COM */
46210616SSebastien.Roy@Sun.COM static void
iptun_exit(iptun_t * iptun)46310616SSebastien.Roy@Sun.COM iptun_exit(iptun_t *iptun)
46410616SSebastien.Roy@Sun.COM {
46510616SSebastien.Roy@Sun.COM mutex_exit(&iptun->iptun_lock);
46610616SSebastien.Roy@Sun.COM }
46710616SSebastien.Roy@Sun.COM
46810616SSebastien.Roy@Sun.COM /*
46910616SSebastien.Roy@Sun.COM * Enter the IP tunnel instance by datalink ID.
47010616SSebastien.Roy@Sun.COM */
47110616SSebastien.Roy@Sun.COM static int
iptun_enter_by_linkid(datalink_id_t linkid,iptun_t ** iptun)47210616SSebastien.Roy@Sun.COM iptun_enter_by_linkid(datalink_id_t linkid, iptun_t **iptun)
47310616SSebastien.Roy@Sun.COM {
47410616SSebastien.Roy@Sun.COM int err;
47510616SSebastien.Roy@Sun.COM
47610616SSebastien.Roy@Sun.COM mutex_enter(&iptun_hash_lock);
47710616SSebastien.Roy@Sun.COM if (mod_hash_find(iptun_hash, IPTUN_HASH_KEY(linkid),
47810616SSebastien.Roy@Sun.COM (mod_hash_val_t *)iptun) == 0)
47910616SSebastien.Roy@Sun.COM err = iptun_enter(*iptun);
48010616SSebastien.Roy@Sun.COM else
48110616SSebastien.Roy@Sun.COM err = ENOENT;
48210616SSebastien.Roy@Sun.COM if (err != 0)
48310616SSebastien.Roy@Sun.COM *iptun = NULL;
48410616SSebastien.Roy@Sun.COM mutex_exit(&iptun_hash_lock);
48510616SSebastien.Roy@Sun.COM return (err);
48610616SSebastien.Roy@Sun.COM }
48710616SSebastien.Roy@Sun.COM
48810616SSebastien.Roy@Sun.COM /*
48911042SErik.Nordmark@Sun.COM * Handle tasks that were deferred through the iptun_taskq because they require
49011042SErik.Nordmark@Sun.COM * calling up to the mac module, and we can't call up to the mac module while
49111042SErik.Nordmark@Sun.COM * holding locks.
49210616SSebastien.Roy@Sun.COM *
49311042SErik.Nordmark@Sun.COM * This is tricky to get right without introducing race conditions and
49410616SSebastien.Roy@Sun.COM * deadlocks with the mac module, as we cannot issue an upcall while in the
49510616SSebastien.Roy@Sun.COM * iptun_t. The reason is that upcalls may try and enter the mac perimeter,
49610616SSebastien.Roy@Sun.COM * while iptun callbacks (such as iptun_m_setprop()) called from the mac
49710616SSebastien.Roy@Sun.COM * module will already have the perimeter held, and will then try and enter
49810616SSebastien.Roy@Sun.COM * the iptun_t. You can see the lock ordering problem with this; this will
49910616SSebastien.Roy@Sun.COM * deadlock.
50010616SSebastien.Roy@Sun.COM *
50110616SSebastien.Roy@Sun.COM * The safe way to do this is to enter the iptun_t in question and copy the
50210616SSebastien.Roy@Sun.COM * information we need out of it so that we can exit it and know that the
50310616SSebastien.Roy@Sun.COM * information being passed up to the upcalls won't be subject to modification
50410616SSebastien.Roy@Sun.COM * by other threads. The problem now is that we need to exit it prior to
50510616SSebastien.Roy@Sun.COM * issuing the upcall, but once we do this, a thread could come along and
50610616SSebastien.Roy@Sun.COM * delete the iptun_t and thus the mac handle required to issue the upcall.
50710616SSebastien.Roy@Sun.COM * To prevent this, we set the IPTUN_UPCALL_PENDING flag prior to exiting the
50810616SSebastien.Roy@Sun.COM * iptun_t. This flag is the condition associated with iptun_upcall_cv, which
50910616SSebastien.Roy@Sun.COM * iptun_delete() will cv_wait() on. When the upcall completes, we clear
51010616SSebastien.Roy@Sun.COM * IPTUN_UPCALL_PENDING and cv_signal() any potentially waiting
51110616SSebastien.Roy@Sun.COM * iptun_delete(). We can thus still safely use iptun->iptun_mh after having
51210616SSebastien.Roy@Sun.COM * exited the iptun_t.
51310616SSebastien.Roy@Sun.COM */
51410616SSebastien.Roy@Sun.COM static void
iptun_task_cb(void * arg)51510616SSebastien.Roy@Sun.COM iptun_task_cb(void *arg)
51610616SSebastien.Roy@Sun.COM {
51710616SSebastien.Roy@Sun.COM iptun_task_data_t *itd = arg;
51810616SSebastien.Roy@Sun.COM iptun_task_t task = itd->itd_task;
51910616SSebastien.Roy@Sun.COM datalink_id_t linkid = itd->itd_linkid;
52010616SSebastien.Roy@Sun.COM iptun_t *iptun;
52110616SSebastien.Roy@Sun.COM uint32_t mtu;
52210616SSebastien.Roy@Sun.COM iptun_addr_t addr;
52310616SSebastien.Roy@Sun.COM link_state_t linkstate;
52410616SSebastien.Roy@Sun.COM size_t header_size;
52510616SSebastien.Roy@Sun.COM iptun_header_t header;
52610616SSebastien.Roy@Sun.COM
52710616SSebastien.Roy@Sun.COM kmem_free(itd, sizeof (*itd));
52810616SSebastien.Roy@Sun.COM
52910616SSebastien.Roy@Sun.COM /*
53010616SSebastien.Roy@Sun.COM * Note that if the lookup fails, it's because the tunnel was deleted
53110616SSebastien.Roy@Sun.COM * between the time the task was dispatched and now. That isn't an
53210616SSebastien.Roy@Sun.COM * error.
53310616SSebastien.Roy@Sun.COM */
53410616SSebastien.Roy@Sun.COM if (iptun_enter_by_linkid(linkid, &iptun) != 0)
53510616SSebastien.Roy@Sun.COM return;
53610616SSebastien.Roy@Sun.COM
53710616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_UPCALL_PENDING;
53810616SSebastien.Roy@Sun.COM
53910616SSebastien.Roy@Sun.COM switch (task) {
54010616SSebastien.Roy@Sun.COM case IPTUN_TASK_MTU_UPDATE:
54110616SSebastien.Roy@Sun.COM mtu = iptun->iptun_mtu;
54210616SSebastien.Roy@Sun.COM break;
54310616SSebastien.Roy@Sun.COM case IPTUN_TASK_LADDR_UPDATE:
54410616SSebastien.Roy@Sun.COM addr = iptun->iptun_laddr;
54510616SSebastien.Roy@Sun.COM break;
54610616SSebastien.Roy@Sun.COM case IPTUN_TASK_RADDR_UPDATE:
54710616SSebastien.Roy@Sun.COM addr = iptun->iptun_raddr;
54810616SSebastien.Roy@Sun.COM break;
54910616SSebastien.Roy@Sun.COM case IPTUN_TASK_LINK_UPDATE:
55010616SSebastien.Roy@Sun.COM linkstate = IS_IPTUN_RUNNING(iptun) ?
55110616SSebastien.Roy@Sun.COM LINK_STATE_UP : LINK_STATE_DOWN;
55210616SSebastien.Roy@Sun.COM break;
55310616SSebastien.Roy@Sun.COM case IPTUN_TASK_PDATA_UPDATE:
55410616SSebastien.Roy@Sun.COM header_size = iptun->iptun_header_size;
55510616SSebastien.Roy@Sun.COM header = iptun->iptun_header;
55610616SSebastien.Roy@Sun.COM break;
55710616SSebastien.Roy@Sun.COM default:
55810616SSebastien.Roy@Sun.COM ASSERT(0);
55910616SSebastien.Roy@Sun.COM }
56010616SSebastien.Roy@Sun.COM
56110616SSebastien.Roy@Sun.COM iptun_exit(iptun);
56210616SSebastien.Roy@Sun.COM
56310616SSebastien.Roy@Sun.COM switch (task) {
56410616SSebastien.Roy@Sun.COM case IPTUN_TASK_MTU_UPDATE:
56510616SSebastien.Roy@Sun.COM (void) mac_maxsdu_update(iptun->iptun_mh, mtu);
56610616SSebastien.Roy@Sun.COM break;
56710616SSebastien.Roy@Sun.COM case IPTUN_TASK_LADDR_UPDATE:
56810616SSebastien.Roy@Sun.COM mac_unicst_update(iptun->iptun_mh, (uint8_t *)&addr.ia_addr);
56910616SSebastien.Roy@Sun.COM break;
57010616SSebastien.Roy@Sun.COM case IPTUN_TASK_RADDR_UPDATE:
57110616SSebastien.Roy@Sun.COM mac_dst_update(iptun->iptun_mh, (uint8_t *)&addr.ia_addr);
57210616SSebastien.Roy@Sun.COM break;
57310616SSebastien.Roy@Sun.COM case IPTUN_TASK_LINK_UPDATE:
57410616SSebastien.Roy@Sun.COM mac_link_update(iptun->iptun_mh, linkstate);
57510616SSebastien.Roy@Sun.COM break;
57610616SSebastien.Roy@Sun.COM case IPTUN_TASK_PDATA_UPDATE:
57710616SSebastien.Roy@Sun.COM if (mac_pdata_update(iptun->iptun_mh,
57810616SSebastien.Roy@Sun.COM header_size == 0 ? NULL : &header, header_size) != 0)
57910616SSebastien.Roy@Sun.COM atomic_inc_64(&iptun->iptun_taskq_fail);
58010616SSebastien.Roy@Sun.COM break;
58110616SSebastien.Roy@Sun.COM }
58210616SSebastien.Roy@Sun.COM
58310616SSebastien.Roy@Sun.COM mutex_enter(&iptun->iptun_lock);
58410616SSebastien.Roy@Sun.COM iptun->iptun_flags &= ~IPTUN_UPCALL_PENDING;
58510616SSebastien.Roy@Sun.COM cv_signal(&iptun->iptun_upcall_cv);
58610616SSebastien.Roy@Sun.COM mutex_exit(&iptun->iptun_lock);
58710616SSebastien.Roy@Sun.COM }
58810616SSebastien.Roy@Sun.COM
58910616SSebastien.Roy@Sun.COM static void
iptun_task_dispatch(iptun_t * iptun,iptun_task_t iptun_task)59010616SSebastien.Roy@Sun.COM iptun_task_dispatch(iptun_t *iptun, iptun_task_t iptun_task)
59110616SSebastien.Roy@Sun.COM {
59210616SSebastien.Roy@Sun.COM iptun_task_data_t *itd;
59310616SSebastien.Roy@Sun.COM
59410616SSebastien.Roy@Sun.COM itd = kmem_alloc(sizeof (*itd), KM_NOSLEEP);
59510616SSebastien.Roy@Sun.COM if (itd == NULL) {
59610616SSebastien.Roy@Sun.COM atomic_inc_64(&iptun->iptun_taskq_fail);
59710616SSebastien.Roy@Sun.COM return;
59810616SSebastien.Roy@Sun.COM }
59910616SSebastien.Roy@Sun.COM itd->itd_task = iptun_task;
60010616SSebastien.Roy@Sun.COM itd->itd_linkid = iptun->iptun_linkid;
60110616SSebastien.Roy@Sun.COM if (ddi_taskq_dispatch(iptun_taskq, iptun_task_cb, itd, DDI_NOSLEEP)) {
60210616SSebastien.Roy@Sun.COM atomic_inc_64(&iptun->iptun_taskq_fail);
60310616SSebastien.Roy@Sun.COM kmem_free(itd, sizeof (*itd));
60410616SSebastien.Roy@Sun.COM }
60510616SSebastien.Roy@Sun.COM }
60610616SSebastien.Roy@Sun.COM
60710616SSebastien.Roy@Sun.COM /*
60810616SSebastien.Roy@Sun.COM * Convert an iptun_addr_t to sockaddr_storage.
60910616SSebastien.Roy@Sun.COM */
61010616SSebastien.Roy@Sun.COM static void
iptun_getaddr(iptun_addr_t * iptun_addr,struct sockaddr_storage * ss)61110616SSebastien.Roy@Sun.COM iptun_getaddr(iptun_addr_t *iptun_addr, struct sockaddr_storage *ss)
61210616SSebastien.Roy@Sun.COM {
61310616SSebastien.Roy@Sun.COM struct sockaddr_in *sin;
61410616SSebastien.Roy@Sun.COM struct sockaddr_in6 *sin6;
61510616SSebastien.Roy@Sun.COM
61610616SSebastien.Roy@Sun.COM bzero(ss, sizeof (*ss));
61710616SSebastien.Roy@Sun.COM switch (iptun_addr->ia_family) {
61810616SSebastien.Roy@Sun.COM case AF_INET:
61910616SSebastien.Roy@Sun.COM sin = (struct sockaddr_in *)ss;
62010616SSebastien.Roy@Sun.COM sin->sin_addr.s_addr = iptun_addr->ia_addr.iau_addr4;
62110616SSebastien.Roy@Sun.COM break;
62210616SSebastien.Roy@Sun.COM case AF_INET6:
62310616SSebastien.Roy@Sun.COM sin6 = (struct sockaddr_in6 *)ss;
62410616SSebastien.Roy@Sun.COM sin6->sin6_addr = iptun_addr->ia_addr.iau_addr6;
62510616SSebastien.Roy@Sun.COM break;
62610616SSebastien.Roy@Sun.COM default:
62710616SSebastien.Roy@Sun.COM ASSERT(0);
62810616SSebastien.Roy@Sun.COM }
62910616SSebastien.Roy@Sun.COM ss->ss_family = iptun_addr->ia_family;
63010616SSebastien.Roy@Sun.COM }
63110616SSebastien.Roy@Sun.COM
63210616SSebastien.Roy@Sun.COM /*
63310616SSebastien.Roy@Sun.COM * General purpose function to set an IP tunnel source or destination address.
63410616SSebastien.Roy@Sun.COM */
63510616SSebastien.Roy@Sun.COM static int
iptun_setaddr(iptun_type_t iptun_type,iptun_addr_t * iptun_addr,const struct sockaddr_storage * ss)63610616SSebastien.Roy@Sun.COM iptun_setaddr(iptun_type_t iptun_type, iptun_addr_t *iptun_addr,
63710616SSebastien.Roy@Sun.COM const struct sockaddr_storage *ss)
63810616SSebastien.Roy@Sun.COM {
63910616SSebastien.Roy@Sun.COM if (!IPTUN_ADDR_MATCH(iptun_type, ss->ss_family))
64010616SSebastien.Roy@Sun.COM return (EINVAL);
64110616SSebastien.Roy@Sun.COM
64210616SSebastien.Roy@Sun.COM switch (ss->ss_family) {
64310616SSebastien.Roy@Sun.COM case AF_INET: {
64410616SSebastien.Roy@Sun.COM struct sockaddr_in *sin = (struct sockaddr_in *)ss;
64510616SSebastien.Roy@Sun.COM
64610616SSebastien.Roy@Sun.COM if ((sin->sin_addr.s_addr == INADDR_ANY) ||
64710616SSebastien.Roy@Sun.COM (sin->sin_addr.s_addr == INADDR_BROADCAST) ||
64810616SSebastien.Roy@Sun.COM CLASSD(sin->sin_addr.s_addr)) {
64910616SSebastien.Roy@Sun.COM return (EADDRNOTAVAIL);
65010616SSebastien.Roy@Sun.COM }
65110616SSebastien.Roy@Sun.COM iptun_addr->ia_addr.iau_addr4 = sin->sin_addr.s_addr;
65210616SSebastien.Roy@Sun.COM break;
65310616SSebastien.Roy@Sun.COM }
65410616SSebastien.Roy@Sun.COM case AF_INET6: {
65510616SSebastien.Roy@Sun.COM struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ss;
65610616SSebastien.Roy@Sun.COM
65710616SSebastien.Roy@Sun.COM if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) ||
65810616SSebastien.Roy@Sun.COM IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr) ||
65910616SSebastien.Roy@Sun.COM IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
66010616SSebastien.Roy@Sun.COM return (EADDRNOTAVAIL);
66110616SSebastien.Roy@Sun.COM }
66210616SSebastien.Roy@Sun.COM iptun_addr->ia_addr.iau_addr6 = sin6->sin6_addr;
66310616SSebastien.Roy@Sun.COM break;
66410616SSebastien.Roy@Sun.COM }
66510616SSebastien.Roy@Sun.COM default:
66610616SSebastien.Roy@Sun.COM return (EAFNOSUPPORT);
66710616SSebastien.Roy@Sun.COM }
66810616SSebastien.Roy@Sun.COM iptun_addr->ia_family = ss->ss_family;
66910616SSebastien.Roy@Sun.COM return (0);
67010616SSebastien.Roy@Sun.COM }
67110616SSebastien.Roy@Sun.COM
67210616SSebastien.Roy@Sun.COM static int
iptun_setladdr(iptun_t * iptun,const struct sockaddr_storage * laddr)67310616SSebastien.Roy@Sun.COM iptun_setladdr(iptun_t *iptun, const struct sockaddr_storage *laddr)
67410616SSebastien.Roy@Sun.COM {
67510616SSebastien.Roy@Sun.COM return (iptun_setaddr(iptun->iptun_typeinfo->iti_type,
67610616SSebastien.Roy@Sun.COM &iptun->iptun_laddr, laddr));
67710616SSebastien.Roy@Sun.COM }
67810616SSebastien.Roy@Sun.COM
67910616SSebastien.Roy@Sun.COM static int
iptun_setraddr(iptun_t * iptun,const struct sockaddr_storage * raddr)68010616SSebastien.Roy@Sun.COM iptun_setraddr(iptun_t *iptun, const struct sockaddr_storage *raddr)
68110616SSebastien.Roy@Sun.COM {
68210616SSebastien.Roy@Sun.COM if (!(iptun->iptun_typeinfo->iti_hasraddr))
68310616SSebastien.Roy@Sun.COM return (EINVAL);
68410616SSebastien.Roy@Sun.COM return (iptun_setaddr(iptun->iptun_typeinfo->iti_type,
68510616SSebastien.Roy@Sun.COM &iptun->iptun_raddr, raddr));
68610616SSebastien.Roy@Sun.COM }
68710616SSebastien.Roy@Sun.COM
68810616SSebastien.Roy@Sun.COM static boolean_t
iptun_canbind(iptun_t * iptun)68910616SSebastien.Roy@Sun.COM iptun_canbind(iptun_t *iptun)
69010616SSebastien.Roy@Sun.COM {
69110616SSebastien.Roy@Sun.COM /*
69210616SSebastien.Roy@Sun.COM * A tunnel may bind when its source address has been set, and if its
69310616SSebastien.Roy@Sun.COM * tunnel type requires one, also its destination address.
69410616SSebastien.Roy@Sun.COM */
69510616SSebastien.Roy@Sun.COM return ((iptun->iptun_flags & IPTUN_LADDR) &&
69610616SSebastien.Roy@Sun.COM ((iptun->iptun_flags & IPTUN_RADDR) ||
69710616SSebastien.Roy@Sun.COM !(iptun->iptun_typeinfo->iti_hasraddr)));
69810616SSebastien.Roy@Sun.COM }
69910616SSebastien.Roy@Sun.COM
70011042SErik.Nordmark@Sun.COM /*
70111042SErik.Nordmark@Sun.COM * Verify that the local address is valid, and insert in the fanout
70211042SErik.Nordmark@Sun.COM */
70310616SSebastien.Roy@Sun.COM static int
iptun_bind(iptun_t * iptun)70410616SSebastien.Roy@Sun.COM iptun_bind(iptun_t *iptun)
70510616SSebastien.Roy@Sun.COM {
70611042SErik.Nordmark@Sun.COM conn_t *connp = iptun->iptun_connp;
70711042SErik.Nordmark@Sun.COM int error = 0;
70811042SErik.Nordmark@Sun.COM ip_xmit_attr_t *ixa;
709*12266SErik.Nordmark@Sun.COM ip_xmit_attr_t *oldixa;
71011042SErik.Nordmark@Sun.COM iulp_t uinfo;
71111042SErik.Nordmark@Sun.COM ip_stack_t *ipst = connp->conn_netstack->netstack_ip;
71211042SErik.Nordmark@Sun.COM
713*12266SErik.Nordmark@Sun.COM /*
714*12266SErik.Nordmark@Sun.COM * Get an exclusive ixa for this thread.
715*12266SErik.Nordmark@Sun.COM * We defer updating conn_ixa until later to handle any concurrent
716*12266SErik.Nordmark@Sun.COM * conn_ixa_cleanup thread.
717*12266SErik.Nordmark@Sun.COM */
718*12266SErik.Nordmark@Sun.COM ixa = conn_get_ixa(connp, B_FALSE);
71911042SErik.Nordmark@Sun.COM if (ixa == NULL)
72011042SErik.Nordmark@Sun.COM return (ENOMEM);
72111042SErik.Nordmark@Sun.COM
72211042SErik.Nordmark@Sun.COM /* We create PMTU state including for 6to4 */
72311042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_PMTU_DISCOVERY;
72410616SSebastien.Roy@Sun.COM
72510616SSebastien.Roy@Sun.COM ASSERT(iptun_canbind(iptun));
72610616SSebastien.Roy@Sun.COM
72711042SErik.Nordmark@Sun.COM mutex_enter(&connp->conn_lock);
72811042SErik.Nordmark@Sun.COM /*
72911042SErik.Nordmark@Sun.COM * Note that conn_proto can't be set since the upper protocol
73011042SErik.Nordmark@Sun.COM * can be both 41 and 4 when IPv6 and IPv4 are over the same tunnel.
73111042SErik.Nordmark@Sun.COM * ipcl_iptun_classify doesn't use conn_proto.
73211042SErik.Nordmark@Sun.COM */
73311042SErik.Nordmark@Sun.COM connp->conn_ipversion = iptun->iptun_typeinfo->iti_ipvers;
73411042SErik.Nordmark@Sun.COM
73510616SSebastien.Roy@Sun.COM switch (iptun->iptun_typeinfo->iti_type) {
73610616SSebastien.Roy@Sun.COM case IPTUN_TYPE_IPV4:
73711042SErik.Nordmark@Sun.COM IN6_IPADDR_TO_V4MAPPED(iptun->iptun_laddr4,
73811042SErik.Nordmark@Sun.COM &connp->conn_laddr_v6);
73911042SErik.Nordmark@Sun.COM IN6_IPADDR_TO_V4MAPPED(iptun->iptun_raddr4,
74011042SErik.Nordmark@Sun.COM &connp->conn_faddr_v6);
74111042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_IS_IPV4;
74211042SErik.Nordmark@Sun.COM if (ip_laddr_verify_v4(iptun->iptun_laddr4, IPCL_ZONEID(connp),
74311042SErik.Nordmark@Sun.COM ipst, B_FALSE) != IPVL_UNICAST_UP) {
74411042SErik.Nordmark@Sun.COM mutex_exit(&connp->conn_lock);
74511042SErik.Nordmark@Sun.COM error = EADDRNOTAVAIL;
74611042SErik.Nordmark@Sun.COM goto done;
74711042SErik.Nordmark@Sun.COM }
74810616SSebastien.Roy@Sun.COM break;
74910616SSebastien.Roy@Sun.COM case IPTUN_TYPE_IPV6:
75011042SErik.Nordmark@Sun.COM connp->conn_laddr_v6 = iptun->iptun_laddr6;
75111042SErik.Nordmark@Sun.COM connp->conn_faddr_v6 = iptun->iptun_raddr6;
75211042SErik.Nordmark@Sun.COM ixa->ixa_flags &= ~IXAF_IS_IPV4;
75311042SErik.Nordmark@Sun.COM /* We use a zero scopeid for now */
75411042SErik.Nordmark@Sun.COM if (ip_laddr_verify_v6(&iptun->iptun_laddr6, IPCL_ZONEID(connp),
75511042SErik.Nordmark@Sun.COM ipst, B_FALSE, 0) != IPVL_UNICAST_UP) {
75611042SErik.Nordmark@Sun.COM mutex_exit(&connp->conn_lock);
75711042SErik.Nordmark@Sun.COM error = EADDRNOTAVAIL;
75811042SErik.Nordmark@Sun.COM goto done;
75911042SErik.Nordmark@Sun.COM }
76010616SSebastien.Roy@Sun.COM break;
76110616SSebastien.Roy@Sun.COM case IPTUN_TYPE_6TO4:
76211042SErik.Nordmark@Sun.COM IN6_IPADDR_TO_V4MAPPED(iptun->iptun_laddr4,
76311042SErik.Nordmark@Sun.COM &connp->conn_laddr_v6);
76411042SErik.Nordmark@Sun.COM IN6_IPADDR_TO_V4MAPPED(INADDR_ANY, &connp->conn_faddr_v6);
76511042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_IS_IPV4;
76611042SErik.Nordmark@Sun.COM mutex_exit(&connp->conn_lock);
76711042SErik.Nordmark@Sun.COM
76811042SErik.Nordmark@Sun.COM switch (ip_laddr_verify_v4(iptun->iptun_laddr4,
76911042SErik.Nordmark@Sun.COM IPCL_ZONEID(connp), ipst, B_FALSE)) {
77011042SErik.Nordmark@Sun.COM case IPVL_UNICAST_UP:
77111042SErik.Nordmark@Sun.COM case IPVL_UNICAST_DOWN:
77211042SErik.Nordmark@Sun.COM break;
77311042SErik.Nordmark@Sun.COM default:
77411042SErik.Nordmark@Sun.COM error = EADDRNOTAVAIL;
77511042SErik.Nordmark@Sun.COM goto done;
77611042SErik.Nordmark@Sun.COM }
77711042SErik.Nordmark@Sun.COM goto insert;
77810616SSebastien.Roy@Sun.COM }
77910616SSebastien.Roy@Sun.COM
78011042SErik.Nordmark@Sun.COM /* In case previous destination was multirt */
78111042SErik.Nordmark@Sun.COM ip_attr_newdst(ixa);
78211042SErik.Nordmark@Sun.COM
78311042SErik.Nordmark@Sun.COM /*
78411042SErik.Nordmark@Sun.COM * When we set a tunnel's destination address, we do not
78511042SErik.Nordmark@Sun.COM * care if the destination is reachable. Transient routing
78611042SErik.Nordmark@Sun.COM * issues should not inhibit the creation of a tunnel
78711042SErik.Nordmark@Sun.COM * interface, for example. Thus we pass B_FALSE here.
78811042SErik.Nordmark@Sun.COM */
78911042SErik.Nordmark@Sun.COM connp->conn_saddr_v6 = connp->conn_laddr_v6;
79011042SErik.Nordmark@Sun.COM mutex_exit(&connp->conn_lock);
79111042SErik.Nordmark@Sun.COM
79211042SErik.Nordmark@Sun.COM /* As long as the MTU is large we avoid fragmentation */
79311042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_DONTFRAG | IXAF_PMTU_IPV4_DF;
79411042SErik.Nordmark@Sun.COM
79511042SErik.Nordmark@Sun.COM /* We handle IPsec in iptun_output_common */
79611042SErik.Nordmark@Sun.COM error = ip_attr_connect(connp, ixa, &connp->conn_saddr_v6,
79711042SErik.Nordmark@Sun.COM &connp->conn_faddr_v6, &connp->conn_faddr_v6, 0,
79811042SErik.Nordmark@Sun.COM &connp->conn_saddr_v6, &uinfo, 0);
79911042SErik.Nordmark@Sun.COM
80011042SErik.Nordmark@Sun.COM if (error != 0)
80111042SErik.Nordmark@Sun.COM goto done;
80211042SErik.Nordmark@Sun.COM
80311042SErik.Nordmark@Sun.COM /* saddr shouldn't change since it was already set */
80411042SErik.Nordmark@Sun.COM ASSERT(IN6_ARE_ADDR_EQUAL(&connp->conn_laddr_v6,
80511042SErik.Nordmark@Sun.COM &connp->conn_saddr_v6));
80611042SErik.Nordmark@Sun.COM
80711042SErik.Nordmark@Sun.COM /* We set IXAF_VERIFY_PMTU to catch PMTU increases */
80811042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_VERIFY_PMTU;
80911042SErik.Nordmark@Sun.COM ASSERT(uinfo.iulp_mtu != 0);
81011042SErik.Nordmark@Sun.COM
81111042SErik.Nordmark@Sun.COM /*
81211042SErik.Nordmark@Sun.COM * Allow setting new policies.
81311042SErik.Nordmark@Sun.COM * The addresses/ports are already set, thus the IPsec policy calls
81411042SErik.Nordmark@Sun.COM * can handle their passed-in conn's.
81511042SErik.Nordmark@Sun.COM */
81611042SErik.Nordmark@Sun.COM connp->conn_policy_cached = B_FALSE;
81711042SErik.Nordmark@Sun.COM
81811042SErik.Nordmark@Sun.COM insert:
81911042SErik.Nordmark@Sun.COM error = ipcl_conn_insert(connp);
82011042SErik.Nordmark@Sun.COM if (error != 0)
82111042SErik.Nordmark@Sun.COM goto done;
82211042SErik.Nordmark@Sun.COM
823*12266SErik.Nordmark@Sun.COM /* Atomically update v6lastdst and conn_ixa */
824*12266SErik.Nordmark@Sun.COM mutex_enter(&connp->conn_lock);
82511042SErik.Nordmark@Sun.COM /* Record this as the "last" send even though we haven't sent any */
82611042SErik.Nordmark@Sun.COM connp->conn_v6lastdst = connp->conn_faddr_v6;
82711042SErik.Nordmark@Sun.COM
82811042SErik.Nordmark@Sun.COM iptun->iptun_flags |= IPTUN_BOUND;
829*12266SErik.Nordmark@Sun.COM
830*12266SErik.Nordmark@Sun.COM oldixa = conn_replace_ixa(connp, ixa);
831*12266SErik.Nordmark@Sun.COM /* Done with conn_t */
832*12266SErik.Nordmark@Sun.COM mutex_exit(&connp->conn_lock);
833*12266SErik.Nordmark@Sun.COM ixa_refrele(oldixa);
834*12266SErik.Nordmark@Sun.COM
83511042SErik.Nordmark@Sun.COM /*
83611042SErik.Nordmark@Sun.COM * Now that we're bound with ip below us, this is a good
83711042SErik.Nordmark@Sun.COM * time to initialize the destination path MTU and to
83811042SErik.Nordmark@Sun.COM * re-calculate the tunnel's link MTU.
83911042SErik.Nordmark@Sun.COM */
84011042SErik.Nordmark@Sun.COM (void) iptun_update_mtu(iptun, ixa, 0);
84111042SErik.Nordmark@Sun.COM
84211042SErik.Nordmark@Sun.COM if (IS_IPTUN_RUNNING(iptun))
84311042SErik.Nordmark@Sun.COM iptun_task_dispatch(iptun, IPTUN_TASK_LINK_UPDATE);
84411042SErik.Nordmark@Sun.COM
84511042SErik.Nordmark@Sun.COM done:
84611042SErik.Nordmark@Sun.COM ixa_refrele(ixa);
84711042SErik.Nordmark@Sun.COM return (error);
84810616SSebastien.Roy@Sun.COM }
84910616SSebastien.Roy@Sun.COM
85010616SSebastien.Roy@Sun.COM static void
iptun_unbind(iptun_t * iptun)85110616SSebastien.Roy@Sun.COM iptun_unbind(iptun_t *iptun)
85210616SSebastien.Roy@Sun.COM {
85310616SSebastien.Roy@Sun.COM ASSERT(iptun->iptun_flags & IPTUN_BOUND);
85410616SSebastien.Roy@Sun.COM ASSERT(mutex_owned(&iptun->iptun_lock) ||
85510616SSebastien.Roy@Sun.COM (iptun->iptun_flags & IPTUN_CONDEMNED));
85610616SSebastien.Roy@Sun.COM ip_unbind(iptun->iptun_connp);
85710616SSebastien.Roy@Sun.COM iptun->iptun_flags &= ~IPTUN_BOUND;
85810616SSebastien.Roy@Sun.COM if (!(iptun->iptun_flags & IPTUN_CONDEMNED))
85910616SSebastien.Roy@Sun.COM iptun_task_dispatch(iptun, IPTUN_TASK_LINK_UPDATE);
86010616SSebastien.Roy@Sun.COM }
86110616SSebastien.Roy@Sun.COM
86210616SSebastien.Roy@Sun.COM /*
86310616SSebastien.Roy@Sun.COM * Re-generate the template data-link header for a given IP tunnel given the
86410616SSebastien.Roy@Sun.COM * tunnel's current parameters.
86510616SSebastien.Roy@Sun.COM */
86610616SSebastien.Roy@Sun.COM static void
iptun_headergen(iptun_t * iptun,boolean_t update_mac)86710616SSebastien.Roy@Sun.COM iptun_headergen(iptun_t *iptun, boolean_t update_mac)
86810616SSebastien.Roy@Sun.COM {
86910616SSebastien.Roy@Sun.COM switch (iptun->iptun_typeinfo->iti_ipvers) {
87010616SSebastien.Roy@Sun.COM case IPV4_VERSION:
87110616SSebastien.Roy@Sun.COM /*
87210616SSebastien.Roy@Sun.COM * We only need to use a custom IP header if the administrator
87310616SSebastien.Roy@Sun.COM * has supplied a non-default hoplimit.
87410616SSebastien.Roy@Sun.COM */
87510616SSebastien.Roy@Sun.COM if (iptun->iptun_hoplimit == IPTUN_DEFAULT_HOPLIMIT) {
87610616SSebastien.Roy@Sun.COM iptun->iptun_header_size = 0;
87710616SSebastien.Roy@Sun.COM break;
87810616SSebastien.Roy@Sun.COM }
87910616SSebastien.Roy@Sun.COM iptun->iptun_header_size = sizeof (ipha_t);
88010616SSebastien.Roy@Sun.COM iptun->iptun_header4.ipha_version_and_hdr_length =
88110616SSebastien.Roy@Sun.COM IP_SIMPLE_HDR_VERSION;
88210616SSebastien.Roy@Sun.COM iptun->iptun_header4.ipha_fragment_offset_and_flags =
88310616SSebastien.Roy@Sun.COM htons(IPH_DF);
88410616SSebastien.Roy@Sun.COM iptun->iptun_header4.ipha_ttl = iptun->iptun_hoplimit;
88510616SSebastien.Roy@Sun.COM break;
88610616SSebastien.Roy@Sun.COM case IPV6_VERSION: {
88710616SSebastien.Roy@Sun.COM ip6_t *ip6hp = &iptun->iptun_header6.it6h_ip6h;
88810616SSebastien.Roy@Sun.COM
88910616SSebastien.Roy@Sun.COM /*
89010616SSebastien.Roy@Sun.COM * We only need to use a custom IPv6 header if either the
89110616SSebastien.Roy@Sun.COM * administrator has supplied a non-default hoplimit, or we
89210616SSebastien.Roy@Sun.COM * need to include an encapsulation limit option in the outer
89310616SSebastien.Roy@Sun.COM * header.
89410616SSebastien.Roy@Sun.COM */
89510616SSebastien.Roy@Sun.COM if (iptun->iptun_hoplimit == IPTUN_DEFAULT_HOPLIMIT &&
89610616SSebastien.Roy@Sun.COM iptun->iptun_encaplimit == 0) {
89710616SSebastien.Roy@Sun.COM iptun->iptun_header_size = 0;
89810616SSebastien.Roy@Sun.COM break;
89910616SSebastien.Roy@Sun.COM }
90010616SSebastien.Roy@Sun.COM
90110616SSebastien.Roy@Sun.COM (void) memset(ip6hp, 0, sizeof (*ip6hp));
90210616SSebastien.Roy@Sun.COM if (iptun->iptun_encaplimit == 0) {
90310616SSebastien.Roy@Sun.COM iptun->iptun_header_size = sizeof (ip6_t);
90410616SSebastien.Roy@Sun.COM ip6hp->ip6_nxt = IPPROTO_NONE;
90510616SSebastien.Roy@Sun.COM } else {
90610616SSebastien.Roy@Sun.COM iptun_encaplim_t *iel;
90710616SSebastien.Roy@Sun.COM
90810616SSebastien.Roy@Sun.COM iptun->iptun_header_size = sizeof (iptun_ipv6hdrs_t);
90910616SSebastien.Roy@Sun.COM /*
91010616SSebastien.Roy@Sun.COM * The mac_ipv6 plugin requires ip6_plen to be in host
91110616SSebastien.Roy@Sun.COM * byte order and reflect the extension headers
91210616SSebastien.Roy@Sun.COM * present in the template. The actual network byte
91310616SSebastien.Roy@Sun.COM * order ip6_plen will be set on a per-packet basis on
91410616SSebastien.Roy@Sun.COM * transmit.
91510616SSebastien.Roy@Sun.COM */
91610616SSebastien.Roy@Sun.COM ip6hp->ip6_plen = sizeof (*iel);
91710616SSebastien.Roy@Sun.COM ip6hp->ip6_nxt = IPPROTO_DSTOPTS;
91810616SSebastien.Roy@Sun.COM iel = &iptun->iptun_header6.it6h_encaplim;
91910616SSebastien.Roy@Sun.COM *iel = iptun_encaplim_init;
92010616SSebastien.Roy@Sun.COM iel->iel_telopt.ip6ot_encap_limit =
92110616SSebastien.Roy@Sun.COM iptun->iptun_encaplimit;
92210616SSebastien.Roy@Sun.COM }
92310616SSebastien.Roy@Sun.COM
92410616SSebastien.Roy@Sun.COM ip6hp->ip6_hlim = iptun->iptun_hoplimit;
92510616SSebastien.Roy@Sun.COM break;
92610616SSebastien.Roy@Sun.COM }
92710616SSebastien.Roy@Sun.COM }
92810616SSebastien.Roy@Sun.COM
92910616SSebastien.Roy@Sun.COM if (update_mac)
93010616SSebastien.Roy@Sun.COM iptun_task_dispatch(iptun, IPTUN_TASK_PDATA_UPDATE);
93110616SSebastien.Roy@Sun.COM }
93210616SSebastien.Roy@Sun.COM
93310616SSebastien.Roy@Sun.COM /*
93410616SSebastien.Roy@Sun.COM * Insert inbound and outbound IPv4 and IPv6 policy into the given policy
93510616SSebastien.Roy@Sun.COM * head.
93610616SSebastien.Roy@Sun.COM */
93710616SSebastien.Roy@Sun.COM static boolean_t
iptun_insert_simple_policies(ipsec_policy_head_t * ph,ipsec_act_t * actp,uint_t n,netstack_t * ns)93810616SSebastien.Roy@Sun.COM iptun_insert_simple_policies(ipsec_policy_head_t *ph, ipsec_act_t *actp,
93910616SSebastien.Roy@Sun.COM uint_t n, netstack_t *ns)
94010616SSebastien.Roy@Sun.COM {
94110616SSebastien.Roy@Sun.COM int f = IPSEC_AF_V4;
94210616SSebastien.Roy@Sun.COM
94310616SSebastien.Roy@Sun.COM if (!ipsec_polhead_insert(ph, actp, n, f, IPSEC_TYPE_INBOUND, ns) ||
94410616SSebastien.Roy@Sun.COM !ipsec_polhead_insert(ph, actp, n, f, IPSEC_TYPE_OUTBOUND, ns))
94510616SSebastien.Roy@Sun.COM return (B_FALSE);
94610616SSebastien.Roy@Sun.COM
94710616SSebastien.Roy@Sun.COM f = IPSEC_AF_V6;
94810616SSebastien.Roy@Sun.COM return (ipsec_polhead_insert(ph, actp, n, f, IPSEC_TYPE_INBOUND, ns) &&
94910616SSebastien.Roy@Sun.COM ipsec_polhead_insert(ph, actp, n, f, IPSEC_TYPE_OUTBOUND, ns));
95010616SSebastien.Roy@Sun.COM }
95110616SSebastien.Roy@Sun.COM
95210616SSebastien.Roy@Sun.COM /*
95310616SSebastien.Roy@Sun.COM * Used to set IPsec policy when policy is set through the IPTUN_CREATE or
95410616SSebastien.Roy@Sun.COM * IPTUN_MODIFY ioctls.
95510616SSebastien.Roy@Sun.COM */
95610616SSebastien.Roy@Sun.COM static int
iptun_set_sec_simple(iptun_t * iptun,const ipsec_req_t * ipsr)95710616SSebastien.Roy@Sun.COM iptun_set_sec_simple(iptun_t *iptun, const ipsec_req_t *ipsr)
95810616SSebastien.Roy@Sun.COM {
95910616SSebastien.Roy@Sun.COM int rc = 0;
96010616SSebastien.Roy@Sun.COM uint_t nact;
96110616SSebastien.Roy@Sun.COM ipsec_act_t *actp = NULL;
96210616SSebastien.Roy@Sun.COM boolean_t clear_all, old_policy = B_FALSE;
96310616SSebastien.Roy@Sun.COM ipsec_tun_pol_t *itp;
96410616SSebastien.Roy@Sun.COM char name[MAXLINKNAMELEN];
96510616SSebastien.Roy@Sun.COM uint64_t gen;
96610616SSebastien.Roy@Sun.COM netstack_t *ns = iptun->iptun_ns;
96710616SSebastien.Roy@Sun.COM
96810616SSebastien.Roy@Sun.COM /* Can't specify self-encap on a tunnel. */
96910616SSebastien.Roy@Sun.COM if (ipsr->ipsr_self_encap_req != 0)
97010616SSebastien.Roy@Sun.COM return (EINVAL);
97110616SSebastien.Roy@Sun.COM
97210616SSebastien.Roy@Sun.COM /*
97310616SSebastien.Roy@Sun.COM * If it's a "clear-all" entry, unset the security flags and resume
97410616SSebastien.Roy@Sun.COM * normal cleartext (or inherit-from-global) policy.
97510616SSebastien.Roy@Sun.COM */
97610616SSebastien.Roy@Sun.COM clear_all = ((ipsr->ipsr_ah_req & IPTUN_IPSEC_REQ_MASK) == 0 &&
97710616SSebastien.Roy@Sun.COM (ipsr->ipsr_esp_req & IPTUN_IPSEC_REQ_MASK) == 0);
97810616SSebastien.Roy@Sun.COM
97910616SSebastien.Roy@Sun.COM ASSERT(mutex_owned(&iptun->iptun_lock));
98010616SSebastien.Roy@Sun.COM itp = iptun->iptun_itp;
98110616SSebastien.Roy@Sun.COM if (itp == NULL) {
98210616SSebastien.Roy@Sun.COM if (clear_all)
98310616SSebastien.Roy@Sun.COM goto bail;
98410616SSebastien.Roy@Sun.COM if ((rc = dls_mgmt_get_linkinfo(iptun->iptun_linkid, name, NULL,
98510616SSebastien.Roy@Sun.COM NULL, NULL)) != 0)
98610616SSebastien.Roy@Sun.COM goto bail;
98710616SSebastien.Roy@Sun.COM ASSERT(name[0] != '\0');
98810616SSebastien.Roy@Sun.COM if ((itp = create_tunnel_policy(name, &rc, &gen, ns)) == NULL)
98910616SSebastien.Roy@Sun.COM goto bail;
99010616SSebastien.Roy@Sun.COM iptun->iptun_itp = itp;
99110616SSebastien.Roy@Sun.COM }
99210616SSebastien.Roy@Sun.COM
99310616SSebastien.Roy@Sun.COM /* Allocate the actvec now, before holding itp or polhead locks. */
99410616SSebastien.Roy@Sun.COM ipsec_actvec_from_req(ipsr, &actp, &nact, ns);
99510616SSebastien.Roy@Sun.COM if (actp == NULL) {
99610616SSebastien.Roy@Sun.COM rc = ENOMEM;
99710616SSebastien.Roy@Sun.COM goto bail;
99810616SSebastien.Roy@Sun.COM }
99910616SSebastien.Roy@Sun.COM
100010616SSebastien.Roy@Sun.COM /*
100110616SSebastien.Roy@Sun.COM * Just write on the active polhead. Save the primary/secondary stuff
100210616SSebastien.Roy@Sun.COM * for spdsock operations.
100310616SSebastien.Roy@Sun.COM *
100410616SSebastien.Roy@Sun.COM * Mutex because we need to write to the polhead AND flags atomically.
100510616SSebastien.Roy@Sun.COM * Other threads will acquire the polhead lock as a reader if the
100610616SSebastien.Roy@Sun.COM * (unprotected) flag is set.
100710616SSebastien.Roy@Sun.COM */
100810616SSebastien.Roy@Sun.COM mutex_enter(&itp->itp_lock);
100910616SSebastien.Roy@Sun.COM if (itp->itp_flags & ITPF_P_TUNNEL) {
101010616SSebastien.Roy@Sun.COM /* Oops, we lost a race. Let's get out of here. */
101110616SSebastien.Roy@Sun.COM rc = EBUSY;
101210616SSebastien.Roy@Sun.COM goto mutex_bail;
101310616SSebastien.Roy@Sun.COM }
101410616SSebastien.Roy@Sun.COM old_policy = ((itp->itp_flags & ITPF_P_ACTIVE) != 0);
101510616SSebastien.Roy@Sun.COM
101610616SSebastien.Roy@Sun.COM if (old_policy) {
101710616SSebastien.Roy@Sun.COM ITPF_CLONE(itp->itp_flags);
101810616SSebastien.Roy@Sun.COM rc = ipsec_copy_polhead(itp->itp_policy, itp->itp_inactive, ns);
101910616SSebastien.Roy@Sun.COM if (rc != 0) {
102010616SSebastien.Roy@Sun.COM /* inactive has already been cleared. */
102110616SSebastien.Roy@Sun.COM itp->itp_flags &= ~ITPF_IFLAGS;
102210616SSebastien.Roy@Sun.COM goto mutex_bail;
102310616SSebastien.Roy@Sun.COM }
102410616SSebastien.Roy@Sun.COM rw_enter(&itp->itp_policy->iph_lock, RW_WRITER);
102510616SSebastien.Roy@Sun.COM ipsec_polhead_flush(itp->itp_policy, ns);
102610616SSebastien.Roy@Sun.COM } else {
102710616SSebastien.Roy@Sun.COM /* Else assume itp->itp_policy is already flushed. */
102810616SSebastien.Roy@Sun.COM rw_enter(&itp->itp_policy->iph_lock, RW_WRITER);
102910616SSebastien.Roy@Sun.COM }
103010616SSebastien.Roy@Sun.COM
103110616SSebastien.Roy@Sun.COM if (clear_all) {
103210616SSebastien.Roy@Sun.COM ASSERT(avl_numnodes(&itp->itp_policy->iph_rulebyid) == 0);
103310616SSebastien.Roy@Sun.COM itp->itp_flags &= ~ITPF_PFLAGS;
103410616SSebastien.Roy@Sun.COM rw_exit(&itp->itp_policy->iph_lock);
103510616SSebastien.Roy@Sun.COM old_policy = B_FALSE; /* Clear out the inactive one too. */
103610616SSebastien.Roy@Sun.COM goto recover_bail;
103710616SSebastien.Roy@Sun.COM }
103810616SSebastien.Roy@Sun.COM
103910616SSebastien.Roy@Sun.COM if (iptun_insert_simple_policies(itp->itp_policy, actp, nact, ns)) {
104010616SSebastien.Roy@Sun.COM rw_exit(&itp->itp_policy->iph_lock);
104110616SSebastien.Roy@Sun.COM /*
104210616SSebastien.Roy@Sun.COM * Adjust MTU and make sure the DL side knows what's up.
104310616SSebastien.Roy@Sun.COM */
104410616SSebastien.Roy@Sun.COM itp->itp_flags = ITPF_P_ACTIVE;
104511042SErik.Nordmark@Sun.COM (void) iptun_update_mtu(iptun, NULL, 0);
104610616SSebastien.Roy@Sun.COM old_policy = B_FALSE; /* Blank out inactive - we succeeded */
104710616SSebastien.Roy@Sun.COM } else {
104810616SSebastien.Roy@Sun.COM rw_exit(&itp->itp_policy->iph_lock);
104910616SSebastien.Roy@Sun.COM rc = ENOMEM;
105010616SSebastien.Roy@Sun.COM }
105110616SSebastien.Roy@Sun.COM
105210616SSebastien.Roy@Sun.COM recover_bail:
105310616SSebastien.Roy@Sun.COM if (old_policy) {
105410616SSebastien.Roy@Sun.COM /* Recover policy in in active polhead. */
105510616SSebastien.Roy@Sun.COM ipsec_swap_policy(itp->itp_policy, itp->itp_inactive, ns);
105610616SSebastien.Roy@Sun.COM ITPF_SWAP(itp->itp_flags);
105710616SSebastien.Roy@Sun.COM }
105810616SSebastien.Roy@Sun.COM
105910616SSebastien.Roy@Sun.COM /* Clear policy in inactive polhead. */
106010616SSebastien.Roy@Sun.COM itp->itp_flags &= ~ITPF_IFLAGS;
106110616SSebastien.Roy@Sun.COM rw_enter(&itp->itp_inactive->iph_lock, RW_WRITER);
106210616SSebastien.Roy@Sun.COM ipsec_polhead_flush(itp->itp_inactive, ns);
106310616SSebastien.Roy@Sun.COM rw_exit(&itp->itp_inactive->iph_lock);
106410616SSebastien.Roy@Sun.COM
106510616SSebastien.Roy@Sun.COM mutex_bail:
106610616SSebastien.Roy@Sun.COM mutex_exit(&itp->itp_lock);
106710616SSebastien.Roy@Sun.COM
106810616SSebastien.Roy@Sun.COM bail:
106910616SSebastien.Roy@Sun.COM if (actp != NULL)
107010616SSebastien.Roy@Sun.COM ipsec_actvec_free(actp, nact);
107110616SSebastien.Roy@Sun.COM
107210616SSebastien.Roy@Sun.COM return (rc);
107310616SSebastien.Roy@Sun.COM }
107410616SSebastien.Roy@Sun.COM
107510616SSebastien.Roy@Sun.COM static iptun_typeinfo_t *
iptun_gettypeinfo(iptun_type_t type)107610616SSebastien.Roy@Sun.COM iptun_gettypeinfo(iptun_type_t type)
107710616SSebastien.Roy@Sun.COM {
107810616SSebastien.Roy@Sun.COM int i;
107910616SSebastien.Roy@Sun.COM
108010616SSebastien.Roy@Sun.COM for (i = 0; iptun_type_table[i].iti_type != IPTUN_TYPE_UNKNOWN; i++) {
108110616SSebastien.Roy@Sun.COM if (iptun_type_table[i].iti_type == type)
108210616SSebastien.Roy@Sun.COM break;
108310616SSebastien.Roy@Sun.COM }
108410616SSebastien.Roy@Sun.COM return (&iptun_type_table[i]);
108510616SSebastien.Roy@Sun.COM }
108610616SSebastien.Roy@Sun.COM
108710616SSebastien.Roy@Sun.COM /*
108810616SSebastien.Roy@Sun.COM * Set the parameters included in ik on the tunnel iptun. Parameters that can
108910616SSebastien.Roy@Sun.COM * only be set at creation time are set in iptun_create().
109010616SSebastien.Roy@Sun.COM */
109110616SSebastien.Roy@Sun.COM static int
iptun_setparams(iptun_t * iptun,const iptun_kparams_t * ik)109210616SSebastien.Roy@Sun.COM iptun_setparams(iptun_t *iptun, const iptun_kparams_t *ik)
109310616SSebastien.Roy@Sun.COM {
109410616SSebastien.Roy@Sun.COM int err = 0;
109510616SSebastien.Roy@Sun.COM netstack_t *ns = iptun->iptun_ns;
109610616SSebastien.Roy@Sun.COM iptun_addr_t orig_laddr, orig_raddr;
109710616SSebastien.Roy@Sun.COM uint_t orig_flags = iptun->iptun_flags;
109810616SSebastien.Roy@Sun.COM
109910616SSebastien.Roy@Sun.COM if (ik->iptun_kparam_flags & IPTUN_KPARAM_LADDR) {
110010616SSebastien.Roy@Sun.COM if (orig_flags & IPTUN_LADDR)
110110616SSebastien.Roy@Sun.COM orig_laddr = iptun->iptun_laddr;
110210616SSebastien.Roy@Sun.COM if ((err = iptun_setladdr(iptun, &ik->iptun_kparam_laddr)) != 0)
110310616SSebastien.Roy@Sun.COM return (err);
110410616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_LADDR;
110510616SSebastien.Roy@Sun.COM }
110610616SSebastien.Roy@Sun.COM
110710616SSebastien.Roy@Sun.COM if (ik->iptun_kparam_flags & IPTUN_KPARAM_RADDR) {
110810616SSebastien.Roy@Sun.COM if (orig_flags & IPTUN_RADDR)
110910616SSebastien.Roy@Sun.COM orig_raddr = iptun->iptun_raddr;
111010616SSebastien.Roy@Sun.COM if ((err = iptun_setraddr(iptun, &ik->iptun_kparam_raddr)) != 0)
111110616SSebastien.Roy@Sun.COM goto done;
111210616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_RADDR;
111310616SSebastien.Roy@Sun.COM }
111410616SSebastien.Roy@Sun.COM
111510616SSebastien.Roy@Sun.COM if (ik->iptun_kparam_flags & IPTUN_KPARAM_SECINFO) {
111610616SSebastien.Roy@Sun.COM /*
111710616SSebastien.Roy@Sun.COM * Set IPsec policy originating from the ifconfig(1M) command
111810616SSebastien.Roy@Sun.COM * line. This is traditionally called "simple" policy because
111910616SSebastien.Roy@Sun.COM * the ipsec_req_t (iptun_kparam_secinfo) can only describe a
112010616SSebastien.Roy@Sun.COM * simple policy of "do ESP on everything" and/or "do AH on
112110616SSebastien.Roy@Sun.COM * everything" (as opposed to the rich policy that can be
112210616SSebastien.Roy@Sun.COM * defined with ipsecconf(1M)).
112310616SSebastien.Roy@Sun.COM */
112410616SSebastien.Roy@Sun.COM if (iptun->iptun_typeinfo->iti_type == IPTUN_TYPE_6TO4) {
112510616SSebastien.Roy@Sun.COM /*
112610616SSebastien.Roy@Sun.COM * Can't set security properties for automatic
112710616SSebastien.Roy@Sun.COM * tunnels.
112810616SSebastien.Roy@Sun.COM */
112910616SSebastien.Roy@Sun.COM err = EINVAL;
113010616SSebastien.Roy@Sun.COM goto done;
113110616SSebastien.Roy@Sun.COM }
113210616SSebastien.Roy@Sun.COM
113310616SSebastien.Roy@Sun.COM if (!ipsec_loaded(ns->netstack_ipsec)) {
113410616SSebastien.Roy@Sun.COM /* If IPsec can be loaded, try and load it now. */
113510616SSebastien.Roy@Sun.COM if (ipsec_failed(ns->netstack_ipsec)) {
113610616SSebastien.Roy@Sun.COM err = EPROTONOSUPPORT;
113710616SSebastien.Roy@Sun.COM goto done;
113810616SSebastien.Roy@Sun.COM }
113910616SSebastien.Roy@Sun.COM ipsec_loader_loadnow(ns->netstack_ipsec);
114010616SSebastien.Roy@Sun.COM /*
114110616SSebastien.Roy@Sun.COM * ipsec_loader_loadnow() returns while IPsec is
114210616SSebastien.Roy@Sun.COM * loaded asynchronously. While a method exists to
114310616SSebastien.Roy@Sun.COM * wait for IPsec to load (ipsec_loader_wait()), it
114410616SSebastien.Roy@Sun.COM * requires use of a STREAMS queue to do a qwait().
114510616SSebastien.Roy@Sun.COM * We're not in STREAMS context here, and so we can't
114610616SSebastien.Roy@Sun.COM * use it. This is not a problem in practice because
114710616SSebastien.Roy@Sun.COM * in the vast majority of cases, key management and
114810616SSebastien.Roy@Sun.COM * global policy will have loaded before any tunnels
114910616SSebastien.Roy@Sun.COM * are plumbed, and so IPsec will already have been
115010616SSebastien.Roy@Sun.COM * loaded.
115110616SSebastien.Roy@Sun.COM */
115210616SSebastien.Roy@Sun.COM err = EAGAIN;
115310616SSebastien.Roy@Sun.COM goto done;
115410616SSebastien.Roy@Sun.COM }
115510616SSebastien.Roy@Sun.COM
115610616SSebastien.Roy@Sun.COM err = iptun_set_sec_simple(iptun, &ik->iptun_kparam_secinfo);
115710616SSebastien.Roy@Sun.COM if (err == 0) {
115810616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_SIMPLE_POLICY;
115910616SSebastien.Roy@Sun.COM iptun->iptun_simple_policy = ik->iptun_kparam_secinfo;
116010616SSebastien.Roy@Sun.COM }
116110616SSebastien.Roy@Sun.COM }
116210616SSebastien.Roy@Sun.COM done:
116310616SSebastien.Roy@Sun.COM if (err != 0) {
116410616SSebastien.Roy@Sun.COM /* Restore original source and destination. */
116510616SSebastien.Roy@Sun.COM if (ik->iptun_kparam_flags & IPTUN_KPARAM_LADDR &&
116610616SSebastien.Roy@Sun.COM (orig_flags & IPTUN_LADDR))
116710616SSebastien.Roy@Sun.COM iptun->iptun_laddr = orig_laddr;
116810616SSebastien.Roy@Sun.COM if ((ik->iptun_kparam_flags & IPTUN_KPARAM_RADDR) &&
116910616SSebastien.Roy@Sun.COM (orig_flags & IPTUN_RADDR))
117010616SSebastien.Roy@Sun.COM iptun->iptun_raddr = orig_raddr;
117110616SSebastien.Roy@Sun.COM iptun->iptun_flags = orig_flags;
117210616SSebastien.Roy@Sun.COM }
117310616SSebastien.Roy@Sun.COM return (err);
117410616SSebastien.Roy@Sun.COM }
117510616SSebastien.Roy@Sun.COM
117610616SSebastien.Roy@Sun.COM static int
iptun_register(iptun_t * iptun)117710616SSebastien.Roy@Sun.COM iptun_register(iptun_t *iptun)
117810616SSebastien.Roy@Sun.COM {
117910616SSebastien.Roy@Sun.COM mac_register_t *mac;
118010616SSebastien.Roy@Sun.COM int err;
118110616SSebastien.Roy@Sun.COM
118210616SSebastien.Roy@Sun.COM ASSERT(!(iptun->iptun_flags & IPTUN_MAC_REGISTERED));
118310616SSebastien.Roy@Sun.COM
118410616SSebastien.Roy@Sun.COM if ((mac = mac_alloc(MAC_VERSION)) == NULL)
118510616SSebastien.Roy@Sun.COM return (EINVAL);
118610616SSebastien.Roy@Sun.COM
118710616SSebastien.Roy@Sun.COM mac->m_type_ident = iptun->iptun_typeinfo->iti_ident;
118810616SSebastien.Roy@Sun.COM mac->m_driver = iptun;
118910616SSebastien.Roy@Sun.COM mac->m_dip = iptun_dip;
119010616SSebastien.Roy@Sun.COM mac->m_instance = (uint_t)-1;
119110616SSebastien.Roy@Sun.COM mac->m_src_addr = (uint8_t *)&iptun->iptun_laddr.ia_addr;
119210616SSebastien.Roy@Sun.COM mac->m_dst_addr = iptun->iptun_typeinfo->iti_hasraddr ?
119310616SSebastien.Roy@Sun.COM (uint8_t *)&iptun->iptun_raddr.ia_addr : NULL;
119410616SSebastien.Roy@Sun.COM mac->m_callbacks = &iptun_m_callbacks;
119510616SSebastien.Roy@Sun.COM mac->m_min_sdu = iptun->iptun_typeinfo->iti_minmtu;
119610616SSebastien.Roy@Sun.COM mac->m_max_sdu = iptun->iptun_mtu;
119710616SSebastien.Roy@Sun.COM if (iptun->iptun_header_size != 0) {
119810616SSebastien.Roy@Sun.COM mac->m_pdata = &iptun->iptun_header;
119910616SSebastien.Roy@Sun.COM mac->m_pdata_size = iptun->iptun_header_size;
120010616SSebastien.Roy@Sun.COM }
120110616SSebastien.Roy@Sun.COM if ((err = mac_register(mac, &iptun->iptun_mh)) == 0)
120210616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_MAC_REGISTERED;
120310616SSebastien.Roy@Sun.COM mac_free(mac);
120410616SSebastien.Roy@Sun.COM return (err);
120510616SSebastien.Roy@Sun.COM }
120610616SSebastien.Roy@Sun.COM
120710616SSebastien.Roy@Sun.COM static int
iptun_unregister(iptun_t * iptun)120810616SSebastien.Roy@Sun.COM iptun_unregister(iptun_t *iptun)
120910616SSebastien.Roy@Sun.COM {
121010616SSebastien.Roy@Sun.COM int err;
121110616SSebastien.Roy@Sun.COM
121210616SSebastien.Roy@Sun.COM ASSERT(iptun->iptun_flags & IPTUN_MAC_REGISTERED);
121310616SSebastien.Roy@Sun.COM if ((err = mac_unregister(iptun->iptun_mh)) == 0)
121410616SSebastien.Roy@Sun.COM iptun->iptun_flags &= ~IPTUN_MAC_REGISTERED;
121510616SSebastien.Roy@Sun.COM return (err);
121610616SSebastien.Roy@Sun.COM }
121710616SSebastien.Roy@Sun.COM
121810616SSebastien.Roy@Sun.COM static conn_t *
iptun_conn_create(iptun_t * iptun,netstack_t * ns,cred_t * credp)121910616SSebastien.Roy@Sun.COM iptun_conn_create(iptun_t *iptun, netstack_t *ns, cred_t *credp)
122010616SSebastien.Roy@Sun.COM {
122110616SSebastien.Roy@Sun.COM conn_t *connp;
122210616SSebastien.Roy@Sun.COM
122310616SSebastien.Roy@Sun.COM if ((connp = ipcl_conn_create(IPCL_IPCCONN, KM_NOSLEEP, ns)) == NULL)
122410616SSebastien.Roy@Sun.COM return (NULL);
122510616SSebastien.Roy@Sun.COM
122610616SSebastien.Roy@Sun.COM connp->conn_flags |= IPCL_IPTUN;
122710616SSebastien.Roy@Sun.COM connp->conn_iptun = iptun;
122810616SSebastien.Roy@Sun.COM connp->conn_recv = iptun_input;
122911042SErik.Nordmark@Sun.COM connp->conn_recvicmp = iptun_input_icmp;
123011042SErik.Nordmark@Sun.COM connp->conn_verifyicmp = iptun_verifyicmp;
123111042SErik.Nordmark@Sun.COM
123211042SErik.Nordmark@Sun.COM /*
123311042SErik.Nordmark@Sun.COM * Register iptun_notify to listen to capability changes detected by IP.
123411042SErik.Nordmark@Sun.COM * This upcall is made in the context of the call to conn_ip_output.
123511042SErik.Nordmark@Sun.COM */
123611042SErik.Nordmark@Sun.COM connp->conn_ixa->ixa_notify = iptun_notify;
123711042SErik.Nordmark@Sun.COM connp->conn_ixa->ixa_notify_cookie = iptun;
123811042SErik.Nordmark@Sun.COM
123910616SSebastien.Roy@Sun.COM /*
124010616SSebastien.Roy@Sun.COM * For exclusive stacks we set conn_zoneid to GLOBAL_ZONEID as is done
124110616SSebastien.Roy@Sun.COM * for all other conn_t's.
124210616SSebastien.Roy@Sun.COM *
124310616SSebastien.Roy@Sun.COM * Note that there's an important distinction between iptun_zoneid and
124410616SSebastien.Roy@Sun.COM * conn_zoneid. The conn_zoneid is set to GLOBAL_ZONEID in non-global
124510616SSebastien.Roy@Sun.COM * exclusive stack zones to make the ip module believe that the
124610616SSebastien.Roy@Sun.COM * non-global zone is actually a global zone. Therefore, when
124710616SSebastien.Roy@Sun.COM * interacting with the ip module, we must always use conn_zoneid.
124810616SSebastien.Roy@Sun.COM */
124910616SSebastien.Roy@Sun.COM connp->conn_zoneid = (ns->netstack_stackid == GLOBAL_NETSTACKID) ?
125010616SSebastien.Roy@Sun.COM crgetzoneid(credp) : GLOBAL_ZONEID;
125110616SSebastien.Roy@Sun.COM connp->conn_cred = credp;
125210616SSebastien.Roy@Sun.COM /* crfree() is done in ipcl_conn_destroy(), called by CONN_DEC_REF() */
125310616SSebastien.Roy@Sun.COM crhold(connp->conn_cred);
125411042SErik.Nordmark@Sun.COM connp->conn_cpid = NOPID;
125511042SErik.Nordmark@Sun.COM
125611042SErik.Nordmark@Sun.COM /* conn_allzones can not be set this early, hence no IPCL_ZONEID */
125711042SErik.Nordmark@Sun.COM connp->conn_ixa->ixa_zoneid = connp->conn_zoneid;
125810616SSebastien.Roy@Sun.COM ASSERT(connp->conn_ref == 1);
125910616SSebastien.Roy@Sun.COM
126011042SErik.Nordmark@Sun.COM /* Cache things in ixa without an extra refhold */
126111849SErik.Nordmark@Sun.COM ASSERT(!(connp->conn_ixa->ixa_free_flags & IXA_FREE_CRED));
126211042SErik.Nordmark@Sun.COM connp->conn_ixa->ixa_cred = connp->conn_cred;
126311042SErik.Nordmark@Sun.COM connp->conn_ixa->ixa_cpid = connp->conn_cpid;
126411042SErik.Nordmark@Sun.COM if (is_system_labeled())
126511042SErik.Nordmark@Sun.COM connp->conn_ixa->ixa_tsl = crgetlabel(connp->conn_cred);
126611042SErik.Nordmark@Sun.COM
126711042SErik.Nordmark@Sun.COM /*
126811042SErik.Nordmark@Sun.COM * Have conn_ip_output drop packets should our outer source
126911042SErik.Nordmark@Sun.COM * go invalid
127011042SErik.Nordmark@Sun.COM */
127111042SErik.Nordmark@Sun.COM connp->conn_ixa->ixa_flags |= IXAF_VERIFY_SOURCE;
127211042SErik.Nordmark@Sun.COM
127311042SErik.Nordmark@Sun.COM switch (iptun->iptun_typeinfo->iti_ipvers) {
127411042SErik.Nordmark@Sun.COM case IPV4_VERSION:
127511042SErik.Nordmark@Sun.COM connp->conn_family = AF_INET6;
127611042SErik.Nordmark@Sun.COM break;
127711042SErik.Nordmark@Sun.COM case IPV6_VERSION:
127811042SErik.Nordmark@Sun.COM connp->conn_family = AF_INET;
127911042SErik.Nordmark@Sun.COM break;
128011042SErik.Nordmark@Sun.COM }
128110616SSebastien.Roy@Sun.COM mutex_enter(&connp->conn_lock);
128210616SSebastien.Roy@Sun.COM connp->conn_state_flags &= ~CONN_INCIPIENT;
128310616SSebastien.Roy@Sun.COM mutex_exit(&connp->conn_lock);
128410616SSebastien.Roy@Sun.COM return (connp);
128510616SSebastien.Roy@Sun.COM }
128610616SSebastien.Roy@Sun.COM
128710616SSebastien.Roy@Sun.COM static void
iptun_conn_destroy(conn_t * connp)128810616SSebastien.Roy@Sun.COM iptun_conn_destroy(conn_t *connp)
128910616SSebastien.Roy@Sun.COM {
129010616SSebastien.Roy@Sun.COM ip_quiesce_conn(connp);
129110616SSebastien.Roy@Sun.COM connp->conn_iptun = NULL;
129210616SSebastien.Roy@Sun.COM ASSERT(connp->conn_ref == 1);
129310616SSebastien.Roy@Sun.COM CONN_DEC_REF(connp);
129410616SSebastien.Roy@Sun.COM }
129510616SSebastien.Roy@Sun.COM
129610616SSebastien.Roy@Sun.COM static iptun_t *
iptun_alloc(void)129710616SSebastien.Roy@Sun.COM iptun_alloc(void)
129810616SSebastien.Roy@Sun.COM {
129910616SSebastien.Roy@Sun.COM iptun_t *iptun;
130010616SSebastien.Roy@Sun.COM
130110616SSebastien.Roy@Sun.COM if ((iptun = kmem_cache_alloc(iptun_cache, KM_NOSLEEP)) != NULL) {
130210616SSebastien.Roy@Sun.COM bzero(iptun, sizeof (*iptun));
130310616SSebastien.Roy@Sun.COM atomic_inc_32(&iptun_tunnelcount);
130410616SSebastien.Roy@Sun.COM }
130510616SSebastien.Roy@Sun.COM return (iptun);
130610616SSebastien.Roy@Sun.COM }
130710616SSebastien.Roy@Sun.COM
130810616SSebastien.Roy@Sun.COM static void
iptun_free(iptun_t * iptun)130910616SSebastien.Roy@Sun.COM iptun_free(iptun_t *iptun)
131010616SSebastien.Roy@Sun.COM {
131110616SSebastien.Roy@Sun.COM ASSERT(iptun->iptun_flags & IPTUN_CONDEMNED);
131210616SSebastien.Roy@Sun.COM
131310616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_HASH_INSERTED) {
131410616SSebastien.Roy@Sun.COM iptun_stack_t *iptuns = iptun->iptun_iptuns;
131510616SSebastien.Roy@Sun.COM
131610616SSebastien.Roy@Sun.COM mutex_enter(&iptun_hash_lock);
131710616SSebastien.Roy@Sun.COM VERIFY(mod_hash_remove(iptun_hash,
131810616SSebastien.Roy@Sun.COM IPTUN_HASH_KEY(iptun->iptun_linkid),
131910616SSebastien.Roy@Sun.COM (mod_hash_val_t *)&iptun) == 0);
132010616SSebastien.Roy@Sun.COM mutex_exit(&iptun_hash_lock);
132110616SSebastien.Roy@Sun.COM iptun->iptun_flags &= ~IPTUN_HASH_INSERTED;
132210616SSebastien.Roy@Sun.COM mutex_enter(&iptuns->iptuns_lock);
132310616SSebastien.Roy@Sun.COM list_remove(&iptuns->iptuns_iptunlist, iptun);
132410616SSebastien.Roy@Sun.COM mutex_exit(&iptuns->iptuns_lock);
132510616SSebastien.Roy@Sun.COM }
132610616SSebastien.Roy@Sun.COM
132710616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_BOUND)
132810616SSebastien.Roy@Sun.COM iptun_unbind(iptun);
132910616SSebastien.Roy@Sun.COM
133010616SSebastien.Roy@Sun.COM /*
133110616SSebastien.Roy@Sun.COM * After iptun_unregister(), there will be no threads executing a
133210616SSebastien.Roy@Sun.COM * downcall from the mac module, including in the tx datapath.
133310616SSebastien.Roy@Sun.COM */
133410616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_MAC_REGISTERED)
133510616SSebastien.Roy@Sun.COM VERIFY(iptun_unregister(iptun) == 0);
133610616SSebastien.Roy@Sun.COM
133710616SSebastien.Roy@Sun.COM if (iptun->iptun_itp != NULL) {
133810616SSebastien.Roy@Sun.COM /*
133910616SSebastien.Roy@Sun.COM * Remove from the AVL tree, AND release the reference iptun_t
134010616SSebastien.Roy@Sun.COM * itself holds on the ITP.
134110616SSebastien.Roy@Sun.COM */
134210616SSebastien.Roy@Sun.COM itp_unlink(iptun->iptun_itp, iptun->iptun_ns);
134310616SSebastien.Roy@Sun.COM ITP_REFRELE(iptun->iptun_itp, iptun->iptun_ns);
134410616SSebastien.Roy@Sun.COM iptun->iptun_itp = NULL;
134510616SSebastien.Roy@Sun.COM iptun->iptun_flags &= ~IPTUN_SIMPLE_POLICY;
134610616SSebastien.Roy@Sun.COM }
134710616SSebastien.Roy@Sun.COM
134810616SSebastien.Roy@Sun.COM /*
134910616SSebastien.Roy@Sun.COM * After ipcl_conn_destroy(), there will be no threads executing an
135010616SSebastien.Roy@Sun.COM * upcall from ip (i.e., iptun_input()), and it is then safe to free
135110616SSebastien.Roy@Sun.COM * the iptun_t.
135210616SSebastien.Roy@Sun.COM */
135310616SSebastien.Roy@Sun.COM if (iptun->iptun_connp != NULL) {
135410616SSebastien.Roy@Sun.COM iptun_conn_destroy(iptun->iptun_connp);
135510616SSebastien.Roy@Sun.COM iptun->iptun_connp = NULL;
135610616SSebastien.Roy@Sun.COM }
135710616SSebastien.Roy@Sun.COM
135810616SSebastien.Roy@Sun.COM kmem_cache_free(iptun_cache, iptun);
135910616SSebastien.Roy@Sun.COM atomic_dec_32(&iptun_tunnelcount);
136010616SSebastien.Roy@Sun.COM }
136110616SSebastien.Roy@Sun.COM
136210616SSebastien.Roy@Sun.COM int
iptun_create(iptun_kparams_t * ik,cred_t * credp)136310616SSebastien.Roy@Sun.COM iptun_create(iptun_kparams_t *ik, cred_t *credp)
136410616SSebastien.Roy@Sun.COM {
136510616SSebastien.Roy@Sun.COM iptun_t *iptun = NULL;
136610616SSebastien.Roy@Sun.COM int err = 0, mherr;
136710616SSebastien.Roy@Sun.COM char linkname[MAXLINKNAMELEN];
136810616SSebastien.Roy@Sun.COM ipsec_tun_pol_t *itp;
136910616SSebastien.Roy@Sun.COM netstack_t *ns = NULL;
137010616SSebastien.Roy@Sun.COM iptun_stack_t *iptuns;
137110616SSebastien.Roy@Sun.COM datalink_id_t tmpid;
137210616SSebastien.Roy@Sun.COM zoneid_t zoneid = crgetzoneid(credp);
137310616SSebastien.Roy@Sun.COM boolean_t link_created = B_FALSE;
137410616SSebastien.Roy@Sun.COM
137510616SSebastien.Roy@Sun.COM /* The tunnel type is mandatory */
137610616SSebastien.Roy@Sun.COM if (!(ik->iptun_kparam_flags & IPTUN_KPARAM_TYPE))
137710616SSebastien.Roy@Sun.COM return (EINVAL);
137810616SSebastien.Roy@Sun.COM
137910616SSebastien.Roy@Sun.COM /*
138010616SSebastien.Roy@Sun.COM * Is the linkid that the caller wishes to associate with this new
138110616SSebastien.Roy@Sun.COM * tunnel assigned to this zone?
138210616SSebastien.Roy@Sun.COM */
138310616SSebastien.Roy@Sun.COM if (zone_check_datalink(&zoneid, ik->iptun_kparam_linkid) != 0) {
138410616SSebastien.Roy@Sun.COM if (zoneid != GLOBAL_ZONEID)
138510616SSebastien.Roy@Sun.COM return (EINVAL);
138610616SSebastien.Roy@Sun.COM } else if (zoneid == GLOBAL_ZONEID) {
138710616SSebastien.Roy@Sun.COM return (EINVAL);
138810616SSebastien.Roy@Sun.COM }
138910616SSebastien.Roy@Sun.COM
139010616SSebastien.Roy@Sun.COM /*
139110616SSebastien.Roy@Sun.COM * Make sure that we're not trying to create a tunnel that has already
139210616SSebastien.Roy@Sun.COM * been created.
139310616SSebastien.Roy@Sun.COM */
139410616SSebastien.Roy@Sun.COM if (iptun_enter_by_linkid(ik->iptun_kparam_linkid, &iptun) == 0) {
139510616SSebastien.Roy@Sun.COM iptun_exit(iptun);
139610616SSebastien.Roy@Sun.COM iptun = NULL;
139710616SSebastien.Roy@Sun.COM err = EEXIST;
139810616SSebastien.Roy@Sun.COM goto done;
139910616SSebastien.Roy@Sun.COM }
140010616SSebastien.Roy@Sun.COM
140110616SSebastien.Roy@Sun.COM ns = netstack_find_by_cred(credp);
140210616SSebastien.Roy@Sun.COM iptuns = ns->netstack_iptun;
140310616SSebastien.Roy@Sun.COM
140410616SSebastien.Roy@Sun.COM if ((iptun = iptun_alloc()) == NULL) {
140510616SSebastien.Roy@Sun.COM err = ENOMEM;
140610616SSebastien.Roy@Sun.COM goto done;
140710616SSebastien.Roy@Sun.COM }
140810616SSebastien.Roy@Sun.COM
140910616SSebastien.Roy@Sun.COM iptun->iptun_linkid = ik->iptun_kparam_linkid;
141010616SSebastien.Roy@Sun.COM iptun->iptun_zoneid = zoneid;
141110616SSebastien.Roy@Sun.COM iptun->iptun_ns = ns;
141210616SSebastien.Roy@Sun.COM
141310616SSebastien.Roy@Sun.COM iptun->iptun_typeinfo = iptun_gettypeinfo(ik->iptun_kparam_type);
141410616SSebastien.Roy@Sun.COM if (iptun->iptun_typeinfo->iti_type == IPTUN_TYPE_UNKNOWN) {
141510616SSebastien.Roy@Sun.COM err = EINVAL;
141610616SSebastien.Roy@Sun.COM goto done;
141710616SSebastien.Roy@Sun.COM }
141810616SSebastien.Roy@Sun.COM
141910616SSebastien.Roy@Sun.COM if (ik->iptun_kparam_flags & IPTUN_KPARAM_IMPLICIT)
142010616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_IMPLICIT;
142110616SSebastien.Roy@Sun.COM
142210616SSebastien.Roy@Sun.COM if ((err = iptun_setparams(iptun, ik)) != 0)
142310616SSebastien.Roy@Sun.COM goto done;
142410616SSebastien.Roy@Sun.COM
142510616SSebastien.Roy@Sun.COM iptun->iptun_hoplimit = IPTUN_DEFAULT_HOPLIMIT;
142610616SSebastien.Roy@Sun.COM if (iptun->iptun_typeinfo->iti_type == IPTUN_TYPE_IPV6)
142710616SSebastien.Roy@Sun.COM iptun->iptun_encaplimit = IPTUN_DEFAULT_ENCAPLIMIT;
142810616SSebastien.Roy@Sun.COM
142910616SSebastien.Roy@Sun.COM iptun_headergen(iptun, B_FALSE);
143010616SSebastien.Roy@Sun.COM
143110616SSebastien.Roy@Sun.COM iptun->iptun_connp = iptun_conn_create(iptun, ns, credp);
143210616SSebastien.Roy@Sun.COM if (iptun->iptun_connp == NULL) {
143310616SSebastien.Roy@Sun.COM err = ENOMEM;
143410616SSebastien.Roy@Sun.COM goto done;
143510616SSebastien.Roy@Sun.COM }
143610616SSebastien.Roy@Sun.COM
143710616SSebastien.Roy@Sun.COM iptun->iptun_mtu = iptun->iptun_typeinfo->iti_maxmtu;
143810616SSebastien.Roy@Sun.COM iptun->iptun_dpmtu = iptun->iptun_mtu;
143910616SSebastien.Roy@Sun.COM
144010616SSebastien.Roy@Sun.COM /*
144110616SSebastien.Roy@Sun.COM * Find an ITP based on linkname. If we have parms already set via
144210616SSebastien.Roy@Sun.COM * the iptun_setparams() call above, it may have created an ITP for
144310616SSebastien.Roy@Sun.COM * us. We always try get_tunnel_policy() for DEBUG correctness
144410616SSebastien.Roy@Sun.COM * checks, and we may wish to refactor this to only check when
144510616SSebastien.Roy@Sun.COM * iptun_itp is NULL.
144610616SSebastien.Roy@Sun.COM */
144710616SSebastien.Roy@Sun.COM if ((err = dls_mgmt_get_linkinfo(iptun->iptun_linkid, linkname, NULL,
144810616SSebastien.Roy@Sun.COM NULL, NULL)) != 0)
144910616SSebastien.Roy@Sun.COM goto done;
145010616SSebastien.Roy@Sun.COM if ((itp = get_tunnel_policy(linkname, ns)) != NULL)
145110616SSebastien.Roy@Sun.COM iptun->iptun_itp = itp;
145210616SSebastien.Roy@Sun.COM
145310616SSebastien.Roy@Sun.COM /*
145410616SSebastien.Roy@Sun.COM * See if we have the necessary IP addresses assigned to this tunnel
145510616SSebastien.Roy@Sun.COM * to try and bind them with ip underneath us. If we're not ready to
145610616SSebastien.Roy@Sun.COM * bind yet, then we'll defer the bind operation until the addresses
145710616SSebastien.Roy@Sun.COM * are modified.
145810616SSebastien.Roy@Sun.COM */
145910616SSebastien.Roy@Sun.COM if (iptun_canbind(iptun) && ((err = iptun_bind(iptun)) != 0))
146010616SSebastien.Roy@Sun.COM goto done;
146110616SSebastien.Roy@Sun.COM
146210616SSebastien.Roy@Sun.COM if ((err = iptun_register(iptun)) != 0)
146310616SSebastien.Roy@Sun.COM goto done;
146410616SSebastien.Roy@Sun.COM
146510616SSebastien.Roy@Sun.COM err = dls_devnet_create(iptun->iptun_mh, iptun->iptun_linkid,
146610616SSebastien.Roy@Sun.COM iptun->iptun_zoneid);
146710616SSebastien.Roy@Sun.COM if (err != 0)
146810616SSebastien.Roy@Sun.COM goto done;
146910616SSebastien.Roy@Sun.COM link_created = B_TRUE;
147010616SSebastien.Roy@Sun.COM
147110616SSebastien.Roy@Sun.COM /*
147210616SSebastien.Roy@Sun.COM * We hash by link-id as that is the key used by all other iptun
147310616SSebastien.Roy@Sun.COM * interfaces (modify, delete, etc.).
147410616SSebastien.Roy@Sun.COM */
147510616SSebastien.Roy@Sun.COM if ((mherr = mod_hash_insert(iptun_hash,
147610616SSebastien.Roy@Sun.COM IPTUN_HASH_KEY(iptun->iptun_linkid), (mod_hash_val_t)iptun)) == 0) {
147710616SSebastien.Roy@Sun.COM mutex_enter(&iptuns->iptuns_lock);
147810616SSebastien.Roy@Sun.COM list_insert_head(&iptuns->iptuns_iptunlist, iptun);
147910616SSebastien.Roy@Sun.COM mutex_exit(&iptuns->iptuns_lock);
148010616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_HASH_INSERTED;
148110616SSebastien.Roy@Sun.COM } else if (mherr == MH_ERR_NOMEM) {
148210616SSebastien.Roy@Sun.COM err = ENOMEM;
148310616SSebastien.Roy@Sun.COM } else if (mherr == MH_ERR_DUPLICATE) {
148410616SSebastien.Roy@Sun.COM err = EEXIST;
148510616SSebastien.Roy@Sun.COM } else {
148610616SSebastien.Roy@Sun.COM err = EINVAL;
148710616SSebastien.Roy@Sun.COM }
148810616SSebastien.Roy@Sun.COM
148910616SSebastien.Roy@Sun.COM done:
149010616SSebastien.Roy@Sun.COM if (iptun == NULL && ns != NULL)
149110616SSebastien.Roy@Sun.COM netstack_rele(ns);
149210616SSebastien.Roy@Sun.COM if (err != 0 && iptun != NULL) {
149310616SSebastien.Roy@Sun.COM if (link_created) {
149410616SSebastien.Roy@Sun.COM (void) dls_devnet_destroy(iptun->iptun_mh, &tmpid,
149510616SSebastien.Roy@Sun.COM B_TRUE);
149610616SSebastien.Roy@Sun.COM }
149710616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_CONDEMNED;
149810616SSebastien.Roy@Sun.COM iptun_free(iptun);
149910616SSebastien.Roy@Sun.COM }
150010616SSebastien.Roy@Sun.COM return (err);
150110616SSebastien.Roy@Sun.COM }
150210616SSebastien.Roy@Sun.COM
150310616SSebastien.Roy@Sun.COM int
iptun_delete(datalink_id_t linkid,cred_t * credp)150410616SSebastien.Roy@Sun.COM iptun_delete(datalink_id_t linkid, cred_t *credp)
150510616SSebastien.Roy@Sun.COM {
150610616SSebastien.Roy@Sun.COM int err;
150710616SSebastien.Roy@Sun.COM iptun_t *iptun = NULL;
150810616SSebastien.Roy@Sun.COM
150910616SSebastien.Roy@Sun.COM if ((err = iptun_enter_by_linkid(linkid, &iptun)) != 0)
151010616SSebastien.Roy@Sun.COM return (err);
151110616SSebastien.Roy@Sun.COM
151210616SSebastien.Roy@Sun.COM /* One cannot delete a tunnel that belongs to another zone. */
151310616SSebastien.Roy@Sun.COM if (iptun->iptun_zoneid != crgetzoneid(credp)) {
151410616SSebastien.Roy@Sun.COM iptun_exit(iptun);
151510616SSebastien.Roy@Sun.COM return (EACCES);
151610616SSebastien.Roy@Sun.COM }
151710616SSebastien.Roy@Sun.COM
151810616SSebastien.Roy@Sun.COM /*
151910616SSebastien.Roy@Sun.COM * We need to exit iptun in order to issue calls up the stack such as
152010616SSebastien.Roy@Sun.COM * dls_devnet_destroy(). If we call up while still in iptun, deadlock
152110616SSebastien.Roy@Sun.COM * with calls coming down the stack is possible. We prevent other
152210616SSebastien.Roy@Sun.COM * threads from entering this iptun after we've exited it by setting
152310616SSebastien.Roy@Sun.COM * the IPTUN_DELETE_PENDING flag. This will cause callers of
152410616SSebastien.Roy@Sun.COM * iptun_enter() to block waiting on iptun_enter_cv. The assumption
152510616SSebastien.Roy@Sun.COM * here is that the functions we're calling while IPTUN_DELETE_PENDING
152610616SSebastien.Roy@Sun.COM * is set dont resuult in an iptun_enter() call, as that would result
152710616SSebastien.Roy@Sun.COM * in deadlock.
152810616SSebastien.Roy@Sun.COM */
152910616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_DELETE_PENDING;
153010616SSebastien.Roy@Sun.COM
153110616SSebastien.Roy@Sun.COM /* Wait for any pending upcall to the mac module to complete. */
153210616SSebastien.Roy@Sun.COM while (iptun->iptun_flags & IPTUN_UPCALL_PENDING)
153310616SSebastien.Roy@Sun.COM cv_wait(&iptun->iptun_upcall_cv, &iptun->iptun_lock);
153410616SSebastien.Roy@Sun.COM
153510616SSebastien.Roy@Sun.COM iptun_exit(iptun);
153610616SSebastien.Roy@Sun.COM
153710616SSebastien.Roy@Sun.COM if ((err = dls_devnet_destroy(iptun->iptun_mh, &linkid, B_TRUE)) == 0) {
153810616SSebastien.Roy@Sun.COM /*
153910616SSebastien.Roy@Sun.COM * mac_disable() will fail with EBUSY if there are references
154010616SSebastien.Roy@Sun.COM * to the iptun MAC. If there are none, then mac_disable()
154110616SSebastien.Roy@Sun.COM * will assure that none can be acquired until the MAC is
154210616SSebastien.Roy@Sun.COM * unregistered.
154310616SSebastien.Roy@Sun.COM *
154410616SSebastien.Roy@Sun.COM * XXX CR 6791335 prevents us from calling mac_disable() prior
154510616SSebastien.Roy@Sun.COM * to dls_devnet_destroy(), so we unfortunately need to
154610616SSebastien.Roy@Sun.COM * attempt to re-create the devnet node if mac_disable()
154710616SSebastien.Roy@Sun.COM * fails.
154810616SSebastien.Roy@Sun.COM */
154910616SSebastien.Roy@Sun.COM if ((err = mac_disable(iptun->iptun_mh)) != 0) {
155010616SSebastien.Roy@Sun.COM (void) dls_devnet_create(iptun->iptun_mh, linkid,
155110616SSebastien.Roy@Sun.COM iptun->iptun_zoneid);
155210616SSebastien.Roy@Sun.COM }
155310616SSebastien.Roy@Sun.COM }
155410616SSebastien.Roy@Sun.COM
155510616SSebastien.Roy@Sun.COM /*
155610616SSebastien.Roy@Sun.COM * Now that we know the fate of this iptun_t, we need to clear
155710616SSebastien.Roy@Sun.COM * IPTUN_DELETE_PENDING, and set IPTUN_CONDEMNED if the iptun_t is
155810616SSebastien.Roy@Sun.COM * slated to be freed. Either way, we need to signal the threads
155910616SSebastien.Roy@Sun.COM * waiting in iptun_enter() so that they can either fail if
156010616SSebastien.Roy@Sun.COM * IPTUN_CONDEMNED is set, or continue if it's not.
156110616SSebastien.Roy@Sun.COM */
156210616SSebastien.Roy@Sun.COM mutex_enter(&iptun->iptun_lock);
156310616SSebastien.Roy@Sun.COM iptun->iptun_flags &= ~IPTUN_DELETE_PENDING;
156410616SSebastien.Roy@Sun.COM if (err == 0)
156510616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_CONDEMNED;
156610616SSebastien.Roy@Sun.COM cv_broadcast(&iptun->iptun_enter_cv);
156710616SSebastien.Roy@Sun.COM mutex_exit(&iptun->iptun_lock);
156810616SSebastien.Roy@Sun.COM
156910616SSebastien.Roy@Sun.COM /*
157010616SSebastien.Roy@Sun.COM * Note that there is no danger in calling iptun_free() after having
157110616SSebastien.Roy@Sun.COM * dropped the iptun_lock since callers of iptun_enter() at this point
157210616SSebastien.Roy@Sun.COM * are doing so from iptun_enter_by_linkid() (mac_disable() got rid of
157310616SSebastien.Roy@Sun.COM * threads entering from mac callbacks which call iptun_enter()
157410616SSebastien.Roy@Sun.COM * directly) which holds iptun_hash_lock, and iptun_free() grabs this
157510616SSebastien.Roy@Sun.COM * lock in order to remove the iptun_t from the hash table.
157610616SSebastien.Roy@Sun.COM */
157710616SSebastien.Roy@Sun.COM if (err == 0)
157810616SSebastien.Roy@Sun.COM iptun_free(iptun);
157910616SSebastien.Roy@Sun.COM
158010616SSebastien.Roy@Sun.COM return (err);
158110616SSebastien.Roy@Sun.COM }
158210616SSebastien.Roy@Sun.COM
158310616SSebastien.Roy@Sun.COM int
iptun_modify(const iptun_kparams_t * ik,cred_t * credp)158410616SSebastien.Roy@Sun.COM iptun_modify(const iptun_kparams_t *ik, cred_t *credp)
158510616SSebastien.Roy@Sun.COM {
158610616SSebastien.Roy@Sun.COM iptun_t *iptun;
158710616SSebastien.Roy@Sun.COM boolean_t laddr_change = B_FALSE, raddr_change = B_FALSE;
158810616SSebastien.Roy@Sun.COM int err;
158910616SSebastien.Roy@Sun.COM
159010616SSebastien.Roy@Sun.COM if ((err = iptun_enter_by_linkid(ik->iptun_kparam_linkid, &iptun)) != 0)
159110616SSebastien.Roy@Sun.COM return (err);
159210616SSebastien.Roy@Sun.COM
159310616SSebastien.Roy@Sun.COM /* One cannot modify a tunnel that belongs to another zone. */
159410616SSebastien.Roy@Sun.COM if (iptun->iptun_zoneid != crgetzoneid(credp)) {
159510616SSebastien.Roy@Sun.COM err = EACCES;
159610616SSebastien.Roy@Sun.COM goto done;
159710616SSebastien.Roy@Sun.COM }
159810616SSebastien.Roy@Sun.COM
159910616SSebastien.Roy@Sun.COM /* The tunnel type cannot be changed */
160010616SSebastien.Roy@Sun.COM if (ik->iptun_kparam_flags & IPTUN_KPARAM_TYPE) {
160110616SSebastien.Roy@Sun.COM err = EINVAL;
160210616SSebastien.Roy@Sun.COM goto done;
160310616SSebastien.Roy@Sun.COM }
160410616SSebastien.Roy@Sun.COM
160510616SSebastien.Roy@Sun.COM if ((err = iptun_setparams(iptun, ik)) != 0)
160610616SSebastien.Roy@Sun.COM goto done;
160710616SSebastien.Roy@Sun.COM iptun_headergen(iptun, B_FALSE);
160810616SSebastien.Roy@Sun.COM
160910616SSebastien.Roy@Sun.COM /*
161010616SSebastien.Roy@Sun.COM * If any of the tunnel's addresses has been modified and the tunnel
161110616SSebastien.Roy@Sun.COM * has the necessary addresses assigned to it, we need to try to bind
161210616SSebastien.Roy@Sun.COM * with ip underneath us. If we're not ready to bind yet, then we'll
161310616SSebastien.Roy@Sun.COM * try again when the addresses are modified later.
161410616SSebastien.Roy@Sun.COM */
161510616SSebastien.Roy@Sun.COM laddr_change = (ik->iptun_kparam_flags & IPTUN_KPARAM_LADDR);
161610616SSebastien.Roy@Sun.COM raddr_change = (ik->iptun_kparam_flags & IPTUN_KPARAM_RADDR);
161710616SSebastien.Roy@Sun.COM if (laddr_change || raddr_change) {
161810616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_BOUND)
161910616SSebastien.Roy@Sun.COM iptun_unbind(iptun);
162010616SSebastien.Roy@Sun.COM if (iptun_canbind(iptun) && (err = iptun_bind(iptun)) != 0) {
162110616SSebastien.Roy@Sun.COM if (laddr_change)
162210616SSebastien.Roy@Sun.COM iptun->iptun_flags &= ~IPTUN_LADDR;
162310616SSebastien.Roy@Sun.COM if (raddr_change)
162410616SSebastien.Roy@Sun.COM iptun->iptun_flags &= ~IPTUN_RADDR;
162510616SSebastien.Roy@Sun.COM goto done;
162610616SSebastien.Roy@Sun.COM }
162710616SSebastien.Roy@Sun.COM }
162810616SSebastien.Roy@Sun.COM
162910616SSebastien.Roy@Sun.COM if (laddr_change)
163010616SSebastien.Roy@Sun.COM iptun_task_dispatch(iptun, IPTUN_TASK_LADDR_UPDATE);
163110616SSebastien.Roy@Sun.COM if (raddr_change)
163210616SSebastien.Roy@Sun.COM iptun_task_dispatch(iptun, IPTUN_TASK_RADDR_UPDATE);
163310616SSebastien.Roy@Sun.COM
163410616SSebastien.Roy@Sun.COM done:
163510616SSebastien.Roy@Sun.COM iptun_exit(iptun);
163610616SSebastien.Roy@Sun.COM return (err);
163710616SSebastien.Roy@Sun.COM }
163810616SSebastien.Roy@Sun.COM
163910616SSebastien.Roy@Sun.COM /* Given an IP tunnel's datalink id, fill in its parameters. */
164010616SSebastien.Roy@Sun.COM int
iptun_info(iptun_kparams_t * ik,cred_t * credp)164110616SSebastien.Roy@Sun.COM iptun_info(iptun_kparams_t *ik, cred_t *credp)
164210616SSebastien.Roy@Sun.COM {
164310616SSebastien.Roy@Sun.COM iptun_t *iptun;
164410616SSebastien.Roy@Sun.COM int err;
164510616SSebastien.Roy@Sun.COM
164610616SSebastien.Roy@Sun.COM /* Is the tunnel link visible from the caller's zone? */
164710616SSebastien.Roy@Sun.COM if (!dls_devnet_islinkvisible(ik->iptun_kparam_linkid,
164810616SSebastien.Roy@Sun.COM crgetzoneid(credp)))
164910616SSebastien.Roy@Sun.COM return (ENOENT);
165010616SSebastien.Roy@Sun.COM
165110616SSebastien.Roy@Sun.COM if ((err = iptun_enter_by_linkid(ik->iptun_kparam_linkid, &iptun)) != 0)
165210616SSebastien.Roy@Sun.COM return (err);
165310616SSebastien.Roy@Sun.COM
165410616SSebastien.Roy@Sun.COM bzero(ik, sizeof (iptun_kparams_t));
165510616SSebastien.Roy@Sun.COM
165610616SSebastien.Roy@Sun.COM ik->iptun_kparam_linkid = iptun->iptun_linkid;
165710616SSebastien.Roy@Sun.COM ik->iptun_kparam_type = iptun->iptun_typeinfo->iti_type;
165810616SSebastien.Roy@Sun.COM ik->iptun_kparam_flags |= IPTUN_KPARAM_TYPE;
165910616SSebastien.Roy@Sun.COM
166010616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_LADDR) {
166110616SSebastien.Roy@Sun.COM iptun_getaddr(&iptun->iptun_laddr, &ik->iptun_kparam_laddr);
166210616SSebastien.Roy@Sun.COM ik->iptun_kparam_flags |= IPTUN_KPARAM_LADDR;
166310616SSebastien.Roy@Sun.COM }
166410616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_RADDR) {
166510616SSebastien.Roy@Sun.COM iptun_getaddr(&iptun->iptun_raddr, &ik->iptun_kparam_raddr);
166610616SSebastien.Roy@Sun.COM ik->iptun_kparam_flags |= IPTUN_KPARAM_RADDR;
166710616SSebastien.Roy@Sun.COM }
166810616SSebastien.Roy@Sun.COM
166910616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_IMPLICIT)
167010616SSebastien.Roy@Sun.COM ik->iptun_kparam_flags |= IPTUN_KPARAM_IMPLICIT;
167110616SSebastien.Roy@Sun.COM
167210616SSebastien.Roy@Sun.COM if (iptun->iptun_itp != NULL) {
167310616SSebastien.Roy@Sun.COM mutex_enter(&iptun->iptun_itp->itp_lock);
167410616SSebastien.Roy@Sun.COM if (iptun->iptun_itp->itp_flags & ITPF_P_ACTIVE) {
167510616SSebastien.Roy@Sun.COM ik->iptun_kparam_flags |= IPTUN_KPARAM_IPSECPOL;
167610616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_SIMPLE_POLICY) {
167710616SSebastien.Roy@Sun.COM ik->iptun_kparam_flags |= IPTUN_KPARAM_SECINFO;
167810616SSebastien.Roy@Sun.COM ik->iptun_kparam_secinfo =
167910616SSebastien.Roy@Sun.COM iptun->iptun_simple_policy;
168010616SSebastien.Roy@Sun.COM }
168110616SSebastien.Roy@Sun.COM }
168210616SSebastien.Roy@Sun.COM mutex_exit(&iptun->iptun_itp->itp_lock);
168310616SSebastien.Roy@Sun.COM }
168410616SSebastien.Roy@Sun.COM
168510616SSebastien.Roy@Sun.COM done:
168610616SSebastien.Roy@Sun.COM iptun_exit(iptun);
168710616SSebastien.Roy@Sun.COM return (err);
168810616SSebastien.Roy@Sun.COM }
168910616SSebastien.Roy@Sun.COM
169010616SSebastien.Roy@Sun.COM int
iptun_set_6to4relay(netstack_t * ns,ipaddr_t relay_addr)169110616SSebastien.Roy@Sun.COM iptun_set_6to4relay(netstack_t *ns, ipaddr_t relay_addr)
169210616SSebastien.Roy@Sun.COM {
169310616SSebastien.Roy@Sun.COM if (relay_addr == INADDR_BROADCAST || CLASSD(relay_addr))
169410616SSebastien.Roy@Sun.COM return (EADDRNOTAVAIL);
169510616SSebastien.Roy@Sun.COM ns->netstack_iptun->iptuns_relay_rtr_addr = relay_addr;
169610616SSebastien.Roy@Sun.COM return (0);
169710616SSebastien.Roy@Sun.COM }
169810616SSebastien.Roy@Sun.COM
169910616SSebastien.Roy@Sun.COM void
iptun_get_6to4relay(netstack_t * ns,ipaddr_t * relay_addr)170010616SSebastien.Roy@Sun.COM iptun_get_6to4relay(netstack_t *ns, ipaddr_t *relay_addr)
170110616SSebastien.Roy@Sun.COM {
170210616SSebastien.Roy@Sun.COM *relay_addr = ns->netstack_iptun->iptuns_relay_rtr_addr;
170310616SSebastien.Roy@Sun.COM }
170410616SSebastien.Roy@Sun.COM
170510616SSebastien.Roy@Sun.COM void
iptun_set_policy(datalink_id_t linkid,ipsec_tun_pol_t * itp)170610616SSebastien.Roy@Sun.COM iptun_set_policy(datalink_id_t linkid, ipsec_tun_pol_t *itp)
170710616SSebastien.Roy@Sun.COM {
170810616SSebastien.Roy@Sun.COM iptun_t *iptun;
170910616SSebastien.Roy@Sun.COM
171010616SSebastien.Roy@Sun.COM if (iptun_enter_by_linkid(linkid, &iptun) != 0)
171110616SSebastien.Roy@Sun.COM return;
171210616SSebastien.Roy@Sun.COM if (iptun->iptun_itp != itp) {
171310616SSebastien.Roy@Sun.COM ASSERT(iptun->iptun_itp == NULL);
171410616SSebastien.Roy@Sun.COM ITP_REFHOLD(itp);
171510616SSebastien.Roy@Sun.COM iptun->iptun_itp = itp;
171610616SSebastien.Roy@Sun.COM }
171711456Sdanmcd@sun.com /*
171811456Sdanmcd@sun.com * IPsec policy means IPsec overhead, which means lower MTU.
171911456Sdanmcd@sun.com * Refresh the MTU for this tunnel.
172011456Sdanmcd@sun.com */
172111456Sdanmcd@sun.com (void) iptun_update_mtu(iptun, NULL, 0);
172210616SSebastien.Roy@Sun.COM iptun_exit(iptun);
172310616SSebastien.Roy@Sun.COM }
172410616SSebastien.Roy@Sun.COM
172510616SSebastien.Roy@Sun.COM /*
172610616SSebastien.Roy@Sun.COM * Obtain the path MTU to the tunnel destination.
172711042SErik.Nordmark@Sun.COM * Can return zero in some cases.
172810616SSebastien.Roy@Sun.COM */
172910616SSebastien.Roy@Sun.COM static uint32_t
iptun_get_dst_pmtu(iptun_t * iptun,ip_xmit_attr_t * ixa)173011042SErik.Nordmark@Sun.COM iptun_get_dst_pmtu(iptun_t *iptun, ip_xmit_attr_t *ixa)
173110616SSebastien.Roy@Sun.COM {
173210616SSebastien.Roy@Sun.COM uint32_t pmtu = 0;
173311042SErik.Nordmark@Sun.COM conn_t *connp = iptun->iptun_connp;
173411042SErik.Nordmark@Sun.COM boolean_t need_rele = B_FALSE;
173510616SSebastien.Roy@Sun.COM
173610616SSebastien.Roy@Sun.COM /*
173711042SErik.Nordmark@Sun.COM * We only obtain the pmtu for tunnels that have a remote tunnel
173811042SErik.Nordmark@Sun.COM * address.
173910616SSebastien.Roy@Sun.COM */
174010616SSebastien.Roy@Sun.COM if (!(iptun->iptun_flags & IPTUN_RADDR))
174110616SSebastien.Roy@Sun.COM return (0);
174210616SSebastien.Roy@Sun.COM
174311042SErik.Nordmark@Sun.COM if (ixa == NULL) {
174411042SErik.Nordmark@Sun.COM ixa = conn_get_ixa(connp, B_FALSE);
174511042SErik.Nordmark@Sun.COM if (ixa == NULL)
174611042SErik.Nordmark@Sun.COM return (0);
174711042SErik.Nordmark@Sun.COM need_rele = B_TRUE;
174811042SErik.Nordmark@Sun.COM }
174911042SErik.Nordmark@Sun.COM /*
175011042SErik.Nordmark@Sun.COM * Guard against ICMP errors before we have sent, as well as against
175111042SErik.Nordmark@Sun.COM * and a thread which held conn_ixa.
175211042SErik.Nordmark@Sun.COM */
175311042SErik.Nordmark@Sun.COM if (ixa->ixa_ire != NULL) {
175411042SErik.Nordmark@Sun.COM pmtu = ip_get_pmtu(ixa);
175511042SErik.Nordmark@Sun.COM
175611042SErik.Nordmark@Sun.COM /*
175711042SErik.Nordmark@Sun.COM * For both IPv4 and IPv6 we can have indication that the outer
175811042SErik.Nordmark@Sun.COM * header needs fragmentation.
175911042SErik.Nordmark@Sun.COM */
176011042SErik.Nordmark@Sun.COM if (ixa->ixa_flags & IXAF_PMTU_TOO_SMALL) {
176111042SErik.Nordmark@Sun.COM /* Must allow fragmentation in ip_output */
176211042SErik.Nordmark@Sun.COM ixa->ixa_flags &= ~IXAF_DONTFRAG;
176311042SErik.Nordmark@Sun.COM } else if (iptun->iptun_typeinfo->iti_type != IPTUN_TYPE_6TO4) {
176411042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_DONTFRAG;
176511042SErik.Nordmark@Sun.COM } else {
176611042SErik.Nordmark@Sun.COM /* ip_get_pmtu might have set this - we don't want it */
176711042SErik.Nordmark@Sun.COM ixa->ixa_flags &= ~IXAF_PMTU_IPV4_DF;
176811042SErik.Nordmark@Sun.COM }
176911042SErik.Nordmark@Sun.COM }
177011042SErik.Nordmark@Sun.COM
177111042SErik.Nordmark@Sun.COM if (need_rele)
177211042SErik.Nordmark@Sun.COM ixa_refrele(ixa);
177311042SErik.Nordmark@Sun.COM return (pmtu);
177411042SErik.Nordmark@Sun.COM }
177511042SErik.Nordmark@Sun.COM
177611042SErik.Nordmark@Sun.COM /*
177711042SErik.Nordmark@Sun.COM * Update the ip_xmit_attr_t to capture the current lower path mtu as known
177811042SErik.Nordmark@Sun.COM * by ip.
177911042SErik.Nordmark@Sun.COM */
178011042SErik.Nordmark@Sun.COM static void
iptun_update_dst_pmtu(iptun_t * iptun,ip_xmit_attr_t * ixa)178111042SErik.Nordmark@Sun.COM iptun_update_dst_pmtu(iptun_t *iptun, ip_xmit_attr_t *ixa)
178211042SErik.Nordmark@Sun.COM {
178311042SErik.Nordmark@Sun.COM uint32_t pmtu;
178411042SErik.Nordmark@Sun.COM conn_t *connp = iptun->iptun_connp;
178511042SErik.Nordmark@Sun.COM boolean_t need_rele = B_FALSE;
178611042SErik.Nordmark@Sun.COM
178711042SErik.Nordmark@Sun.COM /* IXAF_VERIFY_PMTU is not set if we don't have a fixed destination */
178811042SErik.Nordmark@Sun.COM if (!(iptun->iptun_flags & IPTUN_RADDR))
178911042SErik.Nordmark@Sun.COM return;
179011042SErik.Nordmark@Sun.COM
179111042SErik.Nordmark@Sun.COM if (ixa == NULL) {
179211042SErik.Nordmark@Sun.COM ixa = conn_get_ixa(connp, B_FALSE);
179311042SErik.Nordmark@Sun.COM if (ixa == NULL)
179411042SErik.Nordmark@Sun.COM return;
179511042SErik.Nordmark@Sun.COM need_rele = B_TRUE;
179611042SErik.Nordmark@Sun.COM }
179711042SErik.Nordmark@Sun.COM /*
179811042SErik.Nordmark@Sun.COM * Guard against ICMP errors before we have sent, as well as against
179911042SErik.Nordmark@Sun.COM * and a thread which held conn_ixa.
180011042SErik.Nordmark@Sun.COM */
180111042SErik.Nordmark@Sun.COM if (ixa->ixa_ire != NULL) {
180211042SErik.Nordmark@Sun.COM pmtu = ip_get_pmtu(ixa);
180311042SErik.Nordmark@Sun.COM /*
180411042SErik.Nordmark@Sun.COM * Update ixa_fragsize and ixa_pmtu.
180511042SErik.Nordmark@Sun.COM */
180611042SErik.Nordmark@Sun.COM ixa->ixa_fragsize = ixa->ixa_pmtu = pmtu;
180711042SErik.Nordmark@Sun.COM
180811042SErik.Nordmark@Sun.COM /*
180911042SErik.Nordmark@Sun.COM * For both IPv4 and IPv6 we can have indication that the outer
181011042SErik.Nordmark@Sun.COM * header needs fragmentation.
181111042SErik.Nordmark@Sun.COM */
181211042SErik.Nordmark@Sun.COM if (ixa->ixa_flags & IXAF_PMTU_TOO_SMALL) {
181311042SErik.Nordmark@Sun.COM /* Must allow fragmentation in ip_output */
181411042SErik.Nordmark@Sun.COM ixa->ixa_flags &= ~IXAF_DONTFRAG;
181511042SErik.Nordmark@Sun.COM } else if (iptun->iptun_typeinfo->iti_type != IPTUN_TYPE_6TO4) {
181611042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_DONTFRAG;
181711042SErik.Nordmark@Sun.COM } else {
181811042SErik.Nordmark@Sun.COM /* ip_get_pmtu might have set this - we don't want it */
181911042SErik.Nordmark@Sun.COM ixa->ixa_flags &= ~IXAF_PMTU_IPV4_DF;
182011042SErik.Nordmark@Sun.COM }
182111042SErik.Nordmark@Sun.COM }
182211042SErik.Nordmark@Sun.COM
182311042SErik.Nordmark@Sun.COM if (need_rele)
182411042SErik.Nordmark@Sun.COM ixa_refrele(ixa);
182511042SErik.Nordmark@Sun.COM }
182611042SErik.Nordmark@Sun.COM
182711042SErik.Nordmark@Sun.COM /*
182811042SErik.Nordmark@Sun.COM * There is nothing that iptun can verify in addition to IP having
182911042SErik.Nordmark@Sun.COM * verified the IP addresses in the fanout.
183011042SErik.Nordmark@Sun.COM */
183111042SErik.Nordmark@Sun.COM /* ARGSUSED */
183211042SErik.Nordmark@Sun.COM static boolean_t
iptun_verifyicmp(conn_t * connp,void * arg2,icmph_t * icmph,icmp6_t * icmp6,ip_recv_attr_t * ira)183311042SErik.Nordmark@Sun.COM iptun_verifyicmp(conn_t *connp, void *arg2, icmph_t *icmph, icmp6_t *icmp6,
183411042SErik.Nordmark@Sun.COM ip_recv_attr_t *ira)
183511042SErik.Nordmark@Sun.COM {
183611042SErik.Nordmark@Sun.COM return (B_TRUE);
183711042SErik.Nordmark@Sun.COM }
183811042SErik.Nordmark@Sun.COM
183911042SErik.Nordmark@Sun.COM /*
184011042SErik.Nordmark@Sun.COM * Notify function registered with ip_xmit_attr_t.
184111042SErik.Nordmark@Sun.COM */
184211042SErik.Nordmark@Sun.COM static void
iptun_notify(void * arg,ip_xmit_attr_t * ixa,ixa_notify_type_t ntype,ixa_notify_arg_t narg)184311042SErik.Nordmark@Sun.COM iptun_notify(void *arg, ip_xmit_attr_t *ixa, ixa_notify_type_t ntype,
184411042SErik.Nordmark@Sun.COM ixa_notify_arg_t narg)
184511042SErik.Nordmark@Sun.COM {
184611042SErik.Nordmark@Sun.COM iptun_t *iptun = (iptun_t *)arg;
184711042SErik.Nordmark@Sun.COM
184811042SErik.Nordmark@Sun.COM switch (ntype) {
184911042SErik.Nordmark@Sun.COM case IXAN_PMTU:
185011042SErik.Nordmark@Sun.COM (void) iptun_update_mtu(iptun, ixa, narg);
185110616SSebastien.Roy@Sun.COM break;
185210616SSebastien.Roy@Sun.COM }
185310616SSebastien.Roy@Sun.COM }
185410616SSebastien.Roy@Sun.COM
185510616SSebastien.Roy@Sun.COM /*
185610616SSebastien.Roy@Sun.COM * Returns the max of old_ovhd and the overhead associated with pol.
185710616SSebastien.Roy@Sun.COM */
185810616SSebastien.Roy@Sun.COM static uint32_t
iptun_max_policy_overhead(ipsec_policy_t * pol,uint32_t old_ovhd)185910616SSebastien.Roy@Sun.COM iptun_max_policy_overhead(ipsec_policy_t *pol, uint32_t old_ovhd)
186010616SSebastien.Roy@Sun.COM {
186110616SSebastien.Roy@Sun.COM uint32_t new_ovhd = old_ovhd;
186210616SSebastien.Roy@Sun.COM
186310616SSebastien.Roy@Sun.COM while (pol != NULL) {
186410616SSebastien.Roy@Sun.COM new_ovhd = max(new_ovhd,
186510616SSebastien.Roy@Sun.COM ipsec_act_ovhd(&pol->ipsp_act->ipa_act));
186610616SSebastien.Roy@Sun.COM pol = pol->ipsp_hash.hash_next;
186710616SSebastien.Roy@Sun.COM }
186810616SSebastien.Roy@Sun.COM return (new_ovhd);
186910616SSebastien.Roy@Sun.COM }
187010616SSebastien.Roy@Sun.COM
187110616SSebastien.Roy@Sun.COM static uint32_t
iptun_get_ipsec_overhead(iptun_t * iptun)187210616SSebastien.Roy@Sun.COM iptun_get_ipsec_overhead(iptun_t *iptun)
187310616SSebastien.Roy@Sun.COM {
187410616SSebastien.Roy@Sun.COM ipsec_policy_root_t *ipr;
187510616SSebastien.Roy@Sun.COM ipsec_policy_head_t *iph;
187610616SSebastien.Roy@Sun.COM ipsec_policy_t *pol;
187710616SSebastien.Roy@Sun.COM ipsec_selector_t sel;
187810616SSebastien.Roy@Sun.COM int i;
187910616SSebastien.Roy@Sun.COM uint32_t ipsec_ovhd = 0;
188010616SSebastien.Roy@Sun.COM ipsec_tun_pol_t *itp = iptun->iptun_itp;
188110616SSebastien.Roy@Sun.COM netstack_t *ns = iptun->iptun_ns;
188210616SSebastien.Roy@Sun.COM
188310616SSebastien.Roy@Sun.COM if (itp == NULL || !(itp->itp_flags & ITPF_P_ACTIVE)) {
188410616SSebastien.Roy@Sun.COM /*
188510616SSebastien.Roy@Sun.COM * Consult global policy, just in case. This will only work
188610616SSebastien.Roy@Sun.COM * if we have both source and destination addresses to work
188710616SSebastien.Roy@Sun.COM * with.
188810616SSebastien.Roy@Sun.COM */
188910616SSebastien.Roy@Sun.COM if ((iptun->iptun_flags & (IPTUN_LADDR|IPTUN_RADDR)) !=
189010616SSebastien.Roy@Sun.COM (IPTUN_LADDR|IPTUN_RADDR))
189110616SSebastien.Roy@Sun.COM return (0);
189210616SSebastien.Roy@Sun.COM
189310616SSebastien.Roy@Sun.COM iph = ipsec_system_policy(ns);
189410616SSebastien.Roy@Sun.COM bzero(&sel, sizeof (sel));
189510616SSebastien.Roy@Sun.COM sel.ips_isv4 =
189610616SSebastien.Roy@Sun.COM (iptun->iptun_typeinfo->iti_ipvers == IPV4_VERSION);
189710616SSebastien.Roy@Sun.COM switch (iptun->iptun_typeinfo->iti_ipvers) {
189810616SSebastien.Roy@Sun.COM case IPV4_VERSION:
189910616SSebastien.Roy@Sun.COM sel.ips_local_addr_v4 = iptun->iptun_laddr4;
190010616SSebastien.Roy@Sun.COM sel.ips_remote_addr_v4 = iptun->iptun_raddr4;
190110616SSebastien.Roy@Sun.COM break;
190210616SSebastien.Roy@Sun.COM case IPV6_VERSION:
190310616SSebastien.Roy@Sun.COM sel.ips_local_addr_v6 = iptun->iptun_laddr6;
190410616SSebastien.Roy@Sun.COM sel.ips_remote_addr_v6 = iptun->iptun_raddr6;
190510616SSebastien.Roy@Sun.COM break;
190610616SSebastien.Roy@Sun.COM }
190710616SSebastien.Roy@Sun.COM /* Check for both IPv4 and IPv6. */
190810616SSebastien.Roy@Sun.COM sel.ips_protocol = IPPROTO_ENCAP;
190910616SSebastien.Roy@Sun.COM pol = ipsec_find_policy_head(NULL, iph, IPSEC_TYPE_OUTBOUND,
191011042SErik.Nordmark@Sun.COM &sel);
191110616SSebastien.Roy@Sun.COM if (pol != NULL) {
191210616SSebastien.Roy@Sun.COM ipsec_ovhd = ipsec_act_ovhd(&pol->ipsp_act->ipa_act);
191311042SErik.Nordmark@Sun.COM IPPOL_REFRELE(pol);
191410616SSebastien.Roy@Sun.COM }
191510616SSebastien.Roy@Sun.COM sel.ips_protocol = IPPROTO_IPV6;
191610616SSebastien.Roy@Sun.COM pol = ipsec_find_policy_head(NULL, iph, IPSEC_TYPE_OUTBOUND,
191711042SErik.Nordmark@Sun.COM &sel);
191810616SSebastien.Roy@Sun.COM if (pol != NULL) {
191910616SSebastien.Roy@Sun.COM ipsec_ovhd = max(ipsec_ovhd,
192010616SSebastien.Roy@Sun.COM ipsec_act_ovhd(&pol->ipsp_act->ipa_act));
192111042SErik.Nordmark@Sun.COM IPPOL_REFRELE(pol);
192210616SSebastien.Roy@Sun.COM }
192310616SSebastien.Roy@Sun.COM IPPH_REFRELE(iph, ns);
192410616SSebastien.Roy@Sun.COM } else {
192510616SSebastien.Roy@Sun.COM /*
192610616SSebastien.Roy@Sun.COM * Look through all of the possible IPsec actions for the
192710616SSebastien.Roy@Sun.COM * tunnel, and find the largest potential IPsec overhead.
192810616SSebastien.Roy@Sun.COM */
192910616SSebastien.Roy@Sun.COM iph = itp->itp_policy;
193010616SSebastien.Roy@Sun.COM rw_enter(&iph->iph_lock, RW_READER);
193110616SSebastien.Roy@Sun.COM ipr = &(iph->iph_root[IPSEC_TYPE_OUTBOUND]);
193210616SSebastien.Roy@Sun.COM ipsec_ovhd = iptun_max_policy_overhead(
193310616SSebastien.Roy@Sun.COM ipr->ipr_nonhash[IPSEC_AF_V4], 0);
193410616SSebastien.Roy@Sun.COM ipsec_ovhd = iptun_max_policy_overhead(
193510616SSebastien.Roy@Sun.COM ipr->ipr_nonhash[IPSEC_AF_V6], ipsec_ovhd);
193610616SSebastien.Roy@Sun.COM for (i = 0; i < ipr->ipr_nchains; i++) {
193710616SSebastien.Roy@Sun.COM ipsec_ovhd = iptun_max_policy_overhead(
193810616SSebastien.Roy@Sun.COM ipr->ipr_hash[i].hash_head, ipsec_ovhd);
193910616SSebastien.Roy@Sun.COM }
194010616SSebastien.Roy@Sun.COM rw_exit(&iph->iph_lock);
194110616SSebastien.Roy@Sun.COM }
194210616SSebastien.Roy@Sun.COM
194310616SSebastien.Roy@Sun.COM return (ipsec_ovhd);
194410616SSebastien.Roy@Sun.COM }
194510616SSebastien.Roy@Sun.COM
194610616SSebastien.Roy@Sun.COM /*
194711042SErik.Nordmark@Sun.COM * Calculate and return the maximum possible upper MTU for the given tunnel.
194811042SErik.Nordmark@Sun.COM *
194911042SErik.Nordmark@Sun.COM * If new_pmtu is set then we also need to update the lower path MTU information
195011042SErik.Nordmark@Sun.COM * in the ip_xmit_attr_t. That is needed since we set IXAF_VERIFY_PMTU so that
195111042SErik.Nordmark@Sun.COM * we are notified by conn_ip_output() when the path MTU increases.
195210616SSebastien.Roy@Sun.COM */
195310616SSebastien.Roy@Sun.COM static uint32_t
iptun_get_maxmtu(iptun_t * iptun,ip_xmit_attr_t * ixa,uint32_t new_pmtu)195411042SErik.Nordmark@Sun.COM iptun_get_maxmtu(iptun_t *iptun, ip_xmit_attr_t *ixa, uint32_t new_pmtu)
195510616SSebastien.Roy@Sun.COM {
195610616SSebastien.Roy@Sun.COM size_t header_size, ipsec_overhead;
195710616SSebastien.Roy@Sun.COM uint32_t maxmtu, pmtu;
195810616SSebastien.Roy@Sun.COM
195910616SSebastien.Roy@Sun.COM /*
196010616SSebastien.Roy@Sun.COM * Start with the path-MTU to the remote address, which is either
196110616SSebastien.Roy@Sun.COM * provided as the new_pmtu argument, or obtained using
196210616SSebastien.Roy@Sun.COM * iptun_get_dst_pmtu().
196310616SSebastien.Roy@Sun.COM */
196410616SSebastien.Roy@Sun.COM if (new_pmtu != 0) {
196511042SErik.Nordmark@Sun.COM if (iptun->iptun_flags & IPTUN_RADDR)
196610616SSebastien.Roy@Sun.COM iptun->iptun_dpmtu = new_pmtu;
196710616SSebastien.Roy@Sun.COM pmtu = new_pmtu;
196810616SSebastien.Roy@Sun.COM } else if (iptun->iptun_flags & IPTUN_RADDR) {
196911042SErik.Nordmark@Sun.COM if ((pmtu = iptun_get_dst_pmtu(iptun, ixa)) == 0) {
197010616SSebastien.Roy@Sun.COM /*
197110616SSebastien.Roy@Sun.COM * We weren't able to obtain the path-MTU of the
197210616SSebastien.Roy@Sun.COM * destination. Use the previous value.
197310616SSebastien.Roy@Sun.COM */
197410616SSebastien.Roy@Sun.COM pmtu = iptun->iptun_dpmtu;
197510616SSebastien.Roy@Sun.COM } else {
197610616SSebastien.Roy@Sun.COM iptun->iptun_dpmtu = pmtu;
197710616SSebastien.Roy@Sun.COM }
197810616SSebastien.Roy@Sun.COM } else {
197910616SSebastien.Roy@Sun.COM /*
198010616SSebastien.Roy@Sun.COM * We have no path-MTU information to go on, use the maximum
198110616SSebastien.Roy@Sun.COM * possible value.
198210616SSebastien.Roy@Sun.COM */
198310616SSebastien.Roy@Sun.COM pmtu = iptun->iptun_typeinfo->iti_maxmtu;
198410616SSebastien.Roy@Sun.COM }
198510616SSebastien.Roy@Sun.COM
198610616SSebastien.Roy@Sun.COM /*
198710616SSebastien.Roy@Sun.COM * Now calculate tunneling overhead and subtract that from the
198810616SSebastien.Roy@Sun.COM * path-MTU information obtained above.
198910616SSebastien.Roy@Sun.COM */
199010616SSebastien.Roy@Sun.COM if (iptun->iptun_header_size != 0) {
199110616SSebastien.Roy@Sun.COM header_size = iptun->iptun_header_size;
199210616SSebastien.Roy@Sun.COM } else {
199310616SSebastien.Roy@Sun.COM switch (iptun->iptun_typeinfo->iti_ipvers) {
199410616SSebastien.Roy@Sun.COM case IPV4_VERSION:
199510616SSebastien.Roy@Sun.COM header_size = sizeof (ipha_t);
199610934Ssommerfeld@sun.com if (is_system_labeled())
199710934Ssommerfeld@sun.com header_size += IP_MAX_OPT_LENGTH;
199810616SSebastien.Roy@Sun.COM break;
199910616SSebastien.Roy@Sun.COM case IPV6_VERSION:
200010616SSebastien.Roy@Sun.COM header_size = sizeof (iptun_ipv6hdrs_t);
200110616SSebastien.Roy@Sun.COM break;
200210616SSebastien.Roy@Sun.COM }
200310616SSebastien.Roy@Sun.COM }
200410616SSebastien.Roy@Sun.COM
200510616SSebastien.Roy@Sun.COM ipsec_overhead = iptun_get_ipsec_overhead(iptun);
200610616SSebastien.Roy@Sun.COM
200710616SSebastien.Roy@Sun.COM maxmtu = pmtu - (header_size + ipsec_overhead);
200810616SSebastien.Roy@Sun.COM return (max(maxmtu, iptun->iptun_typeinfo->iti_minmtu));
200910616SSebastien.Roy@Sun.COM }
201010616SSebastien.Roy@Sun.COM
201110616SSebastien.Roy@Sun.COM /*
201211042SErik.Nordmark@Sun.COM * Re-calculate the tunnel's MTU as seen from above and notify the MAC layer
201311042SErik.Nordmark@Sun.COM * of any change in MTU. The new_pmtu argument is the new lower path MTU to
201411042SErik.Nordmark@Sun.COM * the tunnel destination to be used in the tunnel MTU calculation. Passing
201511042SErik.Nordmark@Sun.COM * in 0 for new_pmtu causes the lower path MTU to be dynamically updated using
201611042SErik.Nordmark@Sun.COM * ip_get_pmtu().
201710616SSebastien.Roy@Sun.COM *
201810616SSebastien.Roy@Sun.COM * If the calculated tunnel MTU is different than its previous value, then we
201910616SSebastien.Roy@Sun.COM * notify the MAC layer above us of this change using mac_maxsdu_update().
202010616SSebastien.Roy@Sun.COM */
202110616SSebastien.Roy@Sun.COM static uint32_t
iptun_update_mtu(iptun_t * iptun,ip_xmit_attr_t * ixa,uint32_t new_pmtu)202211042SErik.Nordmark@Sun.COM iptun_update_mtu(iptun_t *iptun, ip_xmit_attr_t *ixa, uint32_t new_pmtu)
202310616SSebastien.Roy@Sun.COM {
202410616SSebastien.Roy@Sun.COM uint32_t newmtu;
202510616SSebastien.Roy@Sun.COM
202611042SErik.Nordmark@Sun.COM /* We always update the ixa since we might have set IXAF_VERIFY_PMTU */
202711042SErik.Nordmark@Sun.COM iptun_update_dst_pmtu(iptun, ixa);
202811042SErik.Nordmark@Sun.COM
202910616SSebastien.Roy@Sun.COM /*
203010616SSebastien.Roy@Sun.COM * We return the current MTU without updating it if it was pegged to a
203110616SSebastien.Roy@Sun.COM * static value using the MAC_PROP_MTU link property.
203210616SSebastien.Roy@Sun.COM */
203310616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_FIXED_MTU)
203410616SSebastien.Roy@Sun.COM return (iptun->iptun_mtu);
203510616SSebastien.Roy@Sun.COM
203610616SSebastien.Roy@Sun.COM /* If the MTU isn't fixed, then use the maximum possible value. */
203711042SErik.Nordmark@Sun.COM newmtu = iptun_get_maxmtu(iptun, ixa, new_pmtu);
203810616SSebastien.Roy@Sun.COM /*
203910616SSebastien.Roy@Sun.COM * We only dynamically adjust the tunnel MTU for tunnels with
204010616SSebastien.Roy@Sun.COM * destinations because dynamic MTU calculations are based on the
204110616SSebastien.Roy@Sun.COM * destination path-MTU.
204210616SSebastien.Roy@Sun.COM */
204310616SSebastien.Roy@Sun.COM if ((iptun->iptun_flags & IPTUN_RADDR) && newmtu != iptun->iptun_mtu) {
204410616SSebastien.Roy@Sun.COM iptun->iptun_mtu = newmtu;
204510616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_MAC_REGISTERED)
204610616SSebastien.Roy@Sun.COM iptun_task_dispatch(iptun, IPTUN_TASK_MTU_UPDATE);
204710616SSebastien.Roy@Sun.COM }
204810616SSebastien.Roy@Sun.COM
204910616SSebastien.Roy@Sun.COM return (newmtu);
205010616SSebastien.Roy@Sun.COM }
205110616SSebastien.Roy@Sun.COM
205210616SSebastien.Roy@Sun.COM /*
205310616SSebastien.Roy@Sun.COM * Frees a packet or packet chain and bumps stat for each freed packet.
205410616SSebastien.Roy@Sun.COM */
205510616SSebastien.Roy@Sun.COM static void
iptun_drop_pkt(mblk_t * mp,uint64_t * stat)205610616SSebastien.Roy@Sun.COM iptun_drop_pkt(mblk_t *mp, uint64_t *stat)
205710616SSebastien.Roy@Sun.COM {
205810616SSebastien.Roy@Sun.COM mblk_t *pktmp;
205910616SSebastien.Roy@Sun.COM
206010616SSebastien.Roy@Sun.COM for (pktmp = mp; pktmp != NULL; pktmp = mp) {
206110616SSebastien.Roy@Sun.COM mp = mp->b_next;
206210616SSebastien.Roy@Sun.COM pktmp->b_next = NULL;
206310616SSebastien.Roy@Sun.COM if (stat != NULL)
206410616SSebastien.Roy@Sun.COM atomic_inc_64(stat);
206510616SSebastien.Roy@Sun.COM freemsg(pktmp);
206610616SSebastien.Roy@Sun.COM }
206710616SSebastien.Roy@Sun.COM }
206810616SSebastien.Roy@Sun.COM
206910616SSebastien.Roy@Sun.COM /*
207010616SSebastien.Roy@Sun.COM * Allocate and return a new mblk to hold an IP and ICMP header, and chain the
207110616SSebastien.Roy@Sun.COM * original packet to its b_cont. Returns NULL on failure.
207210616SSebastien.Roy@Sun.COM */
207310616SSebastien.Roy@Sun.COM static mblk_t *
iptun_build_icmperr(size_t hdrs_size,mblk_t * orig_pkt)207410616SSebastien.Roy@Sun.COM iptun_build_icmperr(size_t hdrs_size, mblk_t *orig_pkt)
207510616SSebastien.Roy@Sun.COM {
207610616SSebastien.Roy@Sun.COM mblk_t *icmperr_mp;
207710616SSebastien.Roy@Sun.COM
207811042SErik.Nordmark@Sun.COM if ((icmperr_mp = allocb(hdrs_size, BPRI_MED)) != NULL) {
207910616SSebastien.Roy@Sun.COM icmperr_mp->b_wptr += hdrs_size;
208010616SSebastien.Roy@Sun.COM /* tack on the offending packet */
208110616SSebastien.Roy@Sun.COM icmperr_mp->b_cont = orig_pkt;
208210616SSebastien.Roy@Sun.COM }
208310616SSebastien.Roy@Sun.COM return (icmperr_mp);
208410616SSebastien.Roy@Sun.COM }
208510616SSebastien.Roy@Sun.COM
208610616SSebastien.Roy@Sun.COM /*
208710616SSebastien.Roy@Sun.COM * Transmit an ICMP error. mp->b_rptr points at the packet to be included in
208810616SSebastien.Roy@Sun.COM * the ICMP error.
208910616SSebastien.Roy@Sun.COM */
209010616SSebastien.Roy@Sun.COM static void
iptun_sendicmp_v4(iptun_t * iptun,icmph_t * icmp,ipha_t * orig_ipha,mblk_t * mp,ts_label_t * tsl)209111042SErik.Nordmark@Sun.COM iptun_sendicmp_v4(iptun_t *iptun, icmph_t *icmp, ipha_t *orig_ipha, mblk_t *mp,
209211042SErik.Nordmark@Sun.COM ts_label_t *tsl)
209310616SSebastien.Roy@Sun.COM {
209410616SSebastien.Roy@Sun.COM size_t orig_pktsize, hdrs_size;
209510616SSebastien.Roy@Sun.COM mblk_t *icmperr_mp;
209610616SSebastien.Roy@Sun.COM ipha_t *new_ipha;
209710616SSebastien.Roy@Sun.COM icmph_t *new_icmp;
209811042SErik.Nordmark@Sun.COM ip_xmit_attr_t ixas;
209911042SErik.Nordmark@Sun.COM conn_t *connp = iptun->iptun_connp;
210010616SSebastien.Roy@Sun.COM
210110616SSebastien.Roy@Sun.COM orig_pktsize = msgdsize(mp);
210210616SSebastien.Roy@Sun.COM hdrs_size = sizeof (ipha_t) + sizeof (icmph_t);
210310616SSebastien.Roy@Sun.COM if ((icmperr_mp = iptun_build_icmperr(hdrs_size, mp)) == NULL) {
210410616SSebastien.Roy@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_noxmtbuf);
210510616SSebastien.Roy@Sun.COM return;
210610616SSebastien.Roy@Sun.COM }
210710616SSebastien.Roy@Sun.COM
210810616SSebastien.Roy@Sun.COM new_ipha = (ipha_t *)icmperr_mp->b_rptr;
210910616SSebastien.Roy@Sun.COM new_icmp = (icmph_t *)(new_ipha + 1);
211010616SSebastien.Roy@Sun.COM
211110616SSebastien.Roy@Sun.COM new_ipha->ipha_version_and_hdr_length = IP_SIMPLE_HDR_VERSION;
211210616SSebastien.Roy@Sun.COM new_ipha->ipha_type_of_service = 0;
211310616SSebastien.Roy@Sun.COM new_ipha->ipha_ident = 0;
211410616SSebastien.Roy@Sun.COM new_ipha->ipha_fragment_offset_and_flags = 0;
211510616SSebastien.Roy@Sun.COM new_ipha->ipha_ttl = orig_ipha->ipha_ttl;
211610616SSebastien.Roy@Sun.COM new_ipha->ipha_protocol = IPPROTO_ICMP;
211710616SSebastien.Roy@Sun.COM new_ipha->ipha_src = orig_ipha->ipha_dst;
211810616SSebastien.Roy@Sun.COM new_ipha->ipha_dst = orig_ipha->ipha_src;
211910616SSebastien.Roy@Sun.COM new_ipha->ipha_hdr_checksum = 0; /* will be computed by ip */
212010616SSebastien.Roy@Sun.COM new_ipha->ipha_length = htons(hdrs_size + orig_pktsize);
212110616SSebastien.Roy@Sun.COM
212210616SSebastien.Roy@Sun.COM *new_icmp = *icmp;
212310616SSebastien.Roy@Sun.COM new_icmp->icmph_checksum = 0;
212410616SSebastien.Roy@Sun.COM new_icmp->icmph_checksum = IP_CSUM(icmperr_mp, sizeof (ipha_t), 0);
212510616SSebastien.Roy@Sun.COM
212611042SErik.Nordmark@Sun.COM bzero(&ixas, sizeof (ixas));
212711042SErik.Nordmark@Sun.COM ixas.ixa_flags = IXAF_BASIC_SIMPLE_V4;
212811983SSowmini.Varadhan@Sun.COM if (new_ipha->ipha_src == INADDR_ANY) {
212911983SSowmini.Varadhan@Sun.COM ixas.ixa_flags &= ~IXAF_VERIFY_SOURCE;
213011042SErik.Nordmark@Sun.COM ixas.ixa_flags |= IXAF_SET_SOURCE;
213111983SSowmini.Varadhan@Sun.COM }
213211042SErik.Nordmark@Sun.COM
213311042SErik.Nordmark@Sun.COM ixas.ixa_zoneid = IPCL_ZONEID(connp);
213411042SErik.Nordmark@Sun.COM ixas.ixa_ipst = connp->conn_netstack->netstack_ip;
213511042SErik.Nordmark@Sun.COM ixas.ixa_cred = connp->conn_cred;
213611042SErik.Nordmark@Sun.COM ixas.ixa_cpid = NOPID;
213711042SErik.Nordmark@Sun.COM if (is_system_labeled())
213811042SErik.Nordmark@Sun.COM ixas.ixa_tsl = tsl;
213911042SErik.Nordmark@Sun.COM
214011042SErik.Nordmark@Sun.COM ixas.ixa_ifindex = 0;
214111042SErik.Nordmark@Sun.COM ixas.ixa_multicast_ttl = IP_DEFAULT_MULTICAST_TTL;
214211042SErik.Nordmark@Sun.COM
214311042SErik.Nordmark@Sun.COM (void) ip_output_simple(icmperr_mp, &ixas);
214411042SErik.Nordmark@Sun.COM ixa_cleanup(&ixas);
214510616SSebastien.Roy@Sun.COM }
214610616SSebastien.Roy@Sun.COM
214710616SSebastien.Roy@Sun.COM static void
iptun_sendicmp_v6(iptun_t * iptun,icmp6_t * icmp6,ip6_t * orig_ip6h,mblk_t * mp,ts_label_t * tsl)214811042SErik.Nordmark@Sun.COM iptun_sendicmp_v6(iptun_t *iptun, icmp6_t *icmp6, ip6_t *orig_ip6h, mblk_t *mp,
214911042SErik.Nordmark@Sun.COM ts_label_t *tsl)
215010616SSebastien.Roy@Sun.COM {
215110616SSebastien.Roy@Sun.COM size_t orig_pktsize, hdrs_size;
215210616SSebastien.Roy@Sun.COM mblk_t *icmp6err_mp;
215310616SSebastien.Roy@Sun.COM ip6_t *new_ip6h;
215410616SSebastien.Roy@Sun.COM icmp6_t *new_icmp6;
215511042SErik.Nordmark@Sun.COM ip_xmit_attr_t ixas;
215611042SErik.Nordmark@Sun.COM conn_t *connp = iptun->iptun_connp;
215710616SSebastien.Roy@Sun.COM
215810616SSebastien.Roy@Sun.COM orig_pktsize = msgdsize(mp);
215910616SSebastien.Roy@Sun.COM hdrs_size = sizeof (ip6_t) + sizeof (icmp6_t);
216010616SSebastien.Roy@Sun.COM if ((icmp6err_mp = iptun_build_icmperr(hdrs_size, mp)) == NULL) {
216110616SSebastien.Roy@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_noxmtbuf);
216210616SSebastien.Roy@Sun.COM return;
216310616SSebastien.Roy@Sun.COM }
216410616SSebastien.Roy@Sun.COM
216510616SSebastien.Roy@Sun.COM new_ip6h = (ip6_t *)icmp6err_mp->b_rptr;
216610616SSebastien.Roy@Sun.COM new_icmp6 = (icmp6_t *)(new_ip6h + 1);
216710616SSebastien.Roy@Sun.COM
216810616SSebastien.Roy@Sun.COM new_ip6h->ip6_vcf = orig_ip6h->ip6_vcf;
216910616SSebastien.Roy@Sun.COM new_ip6h->ip6_plen = htons(sizeof (icmp6_t) + orig_pktsize);
217010616SSebastien.Roy@Sun.COM new_ip6h->ip6_hops = orig_ip6h->ip6_hops;
217110616SSebastien.Roy@Sun.COM new_ip6h->ip6_nxt = IPPROTO_ICMPV6;
217210616SSebastien.Roy@Sun.COM new_ip6h->ip6_src = orig_ip6h->ip6_dst;
217310616SSebastien.Roy@Sun.COM new_ip6h->ip6_dst = orig_ip6h->ip6_src;
217410616SSebastien.Roy@Sun.COM
217510616SSebastien.Roy@Sun.COM *new_icmp6 = *icmp6;
217611042SErik.Nordmark@Sun.COM /* The checksum is calculated in ip_output_simple and friends. */
217710616SSebastien.Roy@Sun.COM new_icmp6->icmp6_cksum = new_ip6h->ip6_plen;
217810616SSebastien.Roy@Sun.COM
217911042SErik.Nordmark@Sun.COM bzero(&ixas, sizeof (ixas));
218011042SErik.Nordmark@Sun.COM ixas.ixa_flags = IXAF_BASIC_SIMPLE_V6;
218111983SSowmini.Varadhan@Sun.COM if (IN6_IS_ADDR_UNSPECIFIED(&new_ip6h->ip6_src)) {
218211983SSowmini.Varadhan@Sun.COM ixas.ixa_flags &= ~IXAF_VERIFY_SOURCE;
218311042SErik.Nordmark@Sun.COM ixas.ixa_flags |= IXAF_SET_SOURCE;
218411983SSowmini.Varadhan@Sun.COM }
218511042SErik.Nordmark@Sun.COM
218611042SErik.Nordmark@Sun.COM ixas.ixa_zoneid = IPCL_ZONEID(connp);
218711042SErik.Nordmark@Sun.COM ixas.ixa_ipst = connp->conn_netstack->netstack_ip;
218811042SErik.Nordmark@Sun.COM ixas.ixa_cred = connp->conn_cred;
218911042SErik.Nordmark@Sun.COM ixas.ixa_cpid = NOPID;
219011042SErik.Nordmark@Sun.COM if (is_system_labeled())
219111042SErik.Nordmark@Sun.COM ixas.ixa_tsl = tsl;
219211042SErik.Nordmark@Sun.COM
219311042SErik.Nordmark@Sun.COM ixas.ixa_ifindex = 0;
219411042SErik.Nordmark@Sun.COM ixas.ixa_multicast_ttl = IP_DEFAULT_MULTICAST_TTL;
219511042SErik.Nordmark@Sun.COM
219611042SErik.Nordmark@Sun.COM (void) ip_output_simple(icmp6err_mp, &ixas);
219711042SErik.Nordmark@Sun.COM ixa_cleanup(&ixas);
219810616SSebastien.Roy@Sun.COM }
219910616SSebastien.Roy@Sun.COM
220010616SSebastien.Roy@Sun.COM static void
iptun_icmp_error_v4(iptun_t * iptun,ipha_t * orig_ipha,mblk_t * mp,uint8_t type,uint8_t code,ts_label_t * tsl)220110616SSebastien.Roy@Sun.COM iptun_icmp_error_v4(iptun_t *iptun, ipha_t *orig_ipha, mblk_t *mp,
220211042SErik.Nordmark@Sun.COM uint8_t type, uint8_t code, ts_label_t *tsl)
220310616SSebastien.Roy@Sun.COM {
220410616SSebastien.Roy@Sun.COM icmph_t icmp;
220510616SSebastien.Roy@Sun.COM
220610616SSebastien.Roy@Sun.COM bzero(&icmp, sizeof (icmp));
220710616SSebastien.Roy@Sun.COM icmp.icmph_type = type;
220810616SSebastien.Roy@Sun.COM icmp.icmph_code = code;
220910616SSebastien.Roy@Sun.COM
221011042SErik.Nordmark@Sun.COM iptun_sendicmp_v4(iptun, &icmp, orig_ipha, mp, tsl);
221110616SSebastien.Roy@Sun.COM }
221210616SSebastien.Roy@Sun.COM
221310616SSebastien.Roy@Sun.COM static void
iptun_icmp_fragneeded_v4(iptun_t * iptun,uint32_t newmtu,ipha_t * orig_ipha,mblk_t * mp,ts_label_t * tsl)221410616SSebastien.Roy@Sun.COM iptun_icmp_fragneeded_v4(iptun_t *iptun, uint32_t newmtu, ipha_t *orig_ipha,
221511042SErik.Nordmark@Sun.COM mblk_t *mp, ts_label_t *tsl)
221610616SSebastien.Roy@Sun.COM {
221710616SSebastien.Roy@Sun.COM icmph_t icmp;
221810616SSebastien.Roy@Sun.COM
221910616SSebastien.Roy@Sun.COM icmp.icmph_type = ICMP_DEST_UNREACHABLE;
222010616SSebastien.Roy@Sun.COM icmp.icmph_code = ICMP_FRAGMENTATION_NEEDED;
222110616SSebastien.Roy@Sun.COM icmp.icmph_du_zero = 0;
222210616SSebastien.Roy@Sun.COM icmp.icmph_du_mtu = htons(newmtu);
222310616SSebastien.Roy@Sun.COM
222411042SErik.Nordmark@Sun.COM iptun_sendicmp_v4(iptun, &icmp, orig_ipha, mp, tsl);
222510616SSebastien.Roy@Sun.COM }
222610616SSebastien.Roy@Sun.COM
222710616SSebastien.Roy@Sun.COM static void
iptun_icmp_error_v6(iptun_t * iptun,ip6_t * orig_ip6h,mblk_t * mp,uint8_t type,uint8_t code,uint32_t offset,ts_label_t * tsl)222810616SSebastien.Roy@Sun.COM iptun_icmp_error_v6(iptun_t *iptun, ip6_t *orig_ip6h, mblk_t *mp,
222911042SErik.Nordmark@Sun.COM uint8_t type, uint8_t code, uint32_t offset, ts_label_t *tsl)
223010616SSebastien.Roy@Sun.COM {
223110616SSebastien.Roy@Sun.COM icmp6_t icmp6;
223210616SSebastien.Roy@Sun.COM
223310616SSebastien.Roy@Sun.COM bzero(&icmp6, sizeof (icmp6));
223410616SSebastien.Roy@Sun.COM icmp6.icmp6_type = type;
223510616SSebastien.Roy@Sun.COM icmp6.icmp6_code = code;
223610616SSebastien.Roy@Sun.COM if (type == ICMP6_PARAM_PROB)
223710616SSebastien.Roy@Sun.COM icmp6.icmp6_pptr = htonl(offset);
223810616SSebastien.Roy@Sun.COM
223911042SErik.Nordmark@Sun.COM iptun_sendicmp_v6(iptun, &icmp6, orig_ip6h, mp, tsl);
224010616SSebastien.Roy@Sun.COM }
224110616SSebastien.Roy@Sun.COM
224210616SSebastien.Roy@Sun.COM static void
iptun_icmp_toobig_v6(iptun_t * iptun,uint32_t newmtu,ip6_t * orig_ip6h,mblk_t * mp,ts_label_t * tsl)224310616SSebastien.Roy@Sun.COM iptun_icmp_toobig_v6(iptun_t *iptun, uint32_t newmtu, ip6_t *orig_ip6h,
224411042SErik.Nordmark@Sun.COM mblk_t *mp, ts_label_t *tsl)
224510616SSebastien.Roy@Sun.COM {
224610616SSebastien.Roy@Sun.COM icmp6_t icmp6;
224710616SSebastien.Roy@Sun.COM
224810616SSebastien.Roy@Sun.COM icmp6.icmp6_type = ICMP6_PACKET_TOO_BIG;
224910616SSebastien.Roy@Sun.COM icmp6.icmp6_code = 0;
225010616SSebastien.Roy@Sun.COM icmp6.icmp6_mtu = htonl(newmtu);
225110616SSebastien.Roy@Sun.COM
225211042SErik.Nordmark@Sun.COM iptun_sendicmp_v6(iptun, &icmp6, orig_ip6h, mp, tsl);
225310616SSebastien.Roy@Sun.COM }
225410616SSebastien.Roy@Sun.COM
225510616SSebastien.Roy@Sun.COM /*
225610616SSebastien.Roy@Sun.COM * Determines if the packet pointed to by ipha or ip6h is an ICMP error. The
225710616SSebastien.Roy@Sun.COM * mp argument is only used to do bounds checking.
225810616SSebastien.Roy@Sun.COM */
225910616SSebastien.Roy@Sun.COM static boolean_t
is_icmp_error(mblk_t * mp,ipha_t * ipha,ip6_t * ip6h)226010616SSebastien.Roy@Sun.COM is_icmp_error(mblk_t *mp, ipha_t *ipha, ip6_t *ip6h)
226110616SSebastien.Roy@Sun.COM {
226210616SSebastien.Roy@Sun.COM uint16_t hlen;
226310616SSebastien.Roy@Sun.COM
226410616SSebastien.Roy@Sun.COM if (ipha != NULL) {
226510616SSebastien.Roy@Sun.COM icmph_t *icmph;
226610616SSebastien.Roy@Sun.COM
226710616SSebastien.Roy@Sun.COM ASSERT(ip6h == NULL);
226810616SSebastien.Roy@Sun.COM if (ipha->ipha_protocol != IPPROTO_ICMP)
226910616SSebastien.Roy@Sun.COM return (B_FALSE);
227010616SSebastien.Roy@Sun.COM
227110616SSebastien.Roy@Sun.COM hlen = IPH_HDR_LENGTH(ipha);
227210616SSebastien.Roy@Sun.COM icmph = (icmph_t *)((uint8_t *)ipha + hlen);
227310616SSebastien.Roy@Sun.COM return (ICMP_IS_ERROR(icmph->icmph_type) ||
227410616SSebastien.Roy@Sun.COM icmph->icmph_type == ICMP_REDIRECT);
227510616SSebastien.Roy@Sun.COM } else {
227610616SSebastien.Roy@Sun.COM icmp6_t *icmp6;
227710616SSebastien.Roy@Sun.COM uint8_t *nexthdrp;
227810616SSebastien.Roy@Sun.COM
227910616SSebastien.Roy@Sun.COM ASSERT(ip6h != NULL);
228010616SSebastien.Roy@Sun.COM if (!ip_hdr_length_nexthdr_v6(mp, ip6h, &hlen, &nexthdrp) ||
228110616SSebastien.Roy@Sun.COM *nexthdrp != IPPROTO_ICMPV6) {
228210616SSebastien.Roy@Sun.COM return (B_FALSE);
228310616SSebastien.Roy@Sun.COM }
228410616SSebastien.Roy@Sun.COM
228510616SSebastien.Roy@Sun.COM icmp6 = (icmp6_t *)((uint8_t *)ip6h + hlen);
228610616SSebastien.Roy@Sun.COM return (ICMP6_IS_ERROR(icmp6->icmp6_type) ||
228710616SSebastien.Roy@Sun.COM icmp6->icmp6_type == ND_REDIRECT);
228810616SSebastien.Roy@Sun.COM }
228910616SSebastien.Roy@Sun.COM }
229010616SSebastien.Roy@Sun.COM
229110616SSebastien.Roy@Sun.COM /*
229210616SSebastien.Roy@Sun.COM * Find inner and outer IP headers from a tunneled packet as setup for calls
229310616SSebastien.Roy@Sun.COM * into ipsec_tun_{in,out}bound().
229411042SErik.Nordmark@Sun.COM * Note that we need to allow the outer header to be in a separate mblk from
229511042SErik.Nordmark@Sun.COM * the inner header.
229611042SErik.Nordmark@Sun.COM * If the caller knows the outer_hlen, the caller passes it in. Otherwise zero.
229710616SSebastien.Roy@Sun.COM */
229810616SSebastien.Roy@Sun.COM static size_t
iptun_find_headers(mblk_t * mp,size_t outer_hlen,ipha_t ** outer4,ipha_t ** inner4,ip6_t ** outer6,ip6_t ** inner6)229911042SErik.Nordmark@Sun.COM iptun_find_headers(mblk_t *mp, size_t outer_hlen, ipha_t **outer4,
230011042SErik.Nordmark@Sun.COM ipha_t **inner4, ip6_t **outer6, ip6_t **inner6)
230110616SSebastien.Roy@Sun.COM {
230210616SSebastien.Roy@Sun.COM ipha_t *ipha;
230310616SSebastien.Roy@Sun.COM size_t first_mblkl = MBLKL(mp);
230410616SSebastien.Roy@Sun.COM mblk_t *inner_mp;
230510616SSebastien.Roy@Sun.COM
230610616SSebastien.Roy@Sun.COM /*
230710616SSebastien.Roy@Sun.COM * Don't bother handling packets that don't have a full IP header in
230810616SSebastien.Roy@Sun.COM * the fist mblk. For the input path, the ip module ensures that this
230910616SSebastien.Roy@Sun.COM * won't happen, and on the output path, the IP tunneling MAC-type
231010616SSebastien.Roy@Sun.COM * plugins ensure that this also won't happen.
231110616SSebastien.Roy@Sun.COM */
231210616SSebastien.Roy@Sun.COM if (first_mblkl < sizeof (ipha_t))
231310616SSebastien.Roy@Sun.COM return (0);
231410616SSebastien.Roy@Sun.COM ipha = (ipha_t *)(mp->b_rptr);
231510616SSebastien.Roy@Sun.COM switch (IPH_HDR_VERSION(ipha)) {
231610616SSebastien.Roy@Sun.COM case IPV4_VERSION:
231710616SSebastien.Roy@Sun.COM *outer4 = ipha;
231810616SSebastien.Roy@Sun.COM *outer6 = NULL;
231911042SErik.Nordmark@Sun.COM if (outer_hlen == 0)
232011042SErik.Nordmark@Sun.COM outer_hlen = IPH_HDR_LENGTH(ipha);
232110616SSebastien.Roy@Sun.COM break;
232210616SSebastien.Roy@Sun.COM case IPV6_VERSION:
232310616SSebastien.Roy@Sun.COM *outer4 = NULL;
232410616SSebastien.Roy@Sun.COM *outer6 = (ip6_t *)ipha;
232511042SErik.Nordmark@Sun.COM if (outer_hlen == 0)
232611042SErik.Nordmark@Sun.COM outer_hlen = ip_hdr_length_v6(mp, (ip6_t *)ipha);
232710616SSebastien.Roy@Sun.COM break;
232810616SSebastien.Roy@Sun.COM default:
232910616SSebastien.Roy@Sun.COM return (0);
233010616SSebastien.Roy@Sun.COM }
233110616SSebastien.Roy@Sun.COM
233210616SSebastien.Roy@Sun.COM if (first_mblkl < outer_hlen ||
233310616SSebastien.Roy@Sun.COM (first_mblkl == outer_hlen && mp->b_cont == NULL))
233410616SSebastien.Roy@Sun.COM return (0);
233510616SSebastien.Roy@Sun.COM
233610616SSebastien.Roy@Sun.COM /*
233710616SSebastien.Roy@Sun.COM * We don't bother doing a pullup here since the outer header will
233810616SSebastien.Roy@Sun.COM * just get stripped off soon on input anyway. We just want to ensure
233910616SSebastien.Roy@Sun.COM * that the inner* pointer points to a full header.
234010616SSebastien.Roy@Sun.COM */
234110616SSebastien.Roy@Sun.COM if (first_mblkl == outer_hlen) {
234210616SSebastien.Roy@Sun.COM inner_mp = mp->b_cont;
234310616SSebastien.Roy@Sun.COM ipha = (ipha_t *)inner_mp->b_rptr;
234410616SSebastien.Roy@Sun.COM } else {
234510616SSebastien.Roy@Sun.COM inner_mp = mp;
234610616SSebastien.Roy@Sun.COM ipha = (ipha_t *)(mp->b_rptr + outer_hlen);
234710616SSebastien.Roy@Sun.COM }
234810616SSebastien.Roy@Sun.COM switch (IPH_HDR_VERSION(ipha)) {
234910616SSebastien.Roy@Sun.COM case IPV4_VERSION:
235010616SSebastien.Roy@Sun.COM if (inner_mp->b_wptr - (uint8_t *)ipha < sizeof (ipha_t))
235110616SSebastien.Roy@Sun.COM return (0);
235210616SSebastien.Roy@Sun.COM *inner4 = ipha;
235310616SSebastien.Roy@Sun.COM *inner6 = NULL;
235410616SSebastien.Roy@Sun.COM break;
235510616SSebastien.Roy@Sun.COM case IPV6_VERSION:
235610616SSebastien.Roy@Sun.COM if (inner_mp->b_wptr - (uint8_t *)ipha < sizeof (ip6_t))
235710616SSebastien.Roy@Sun.COM return (0);
235810616SSebastien.Roy@Sun.COM *inner4 = NULL;
235910616SSebastien.Roy@Sun.COM *inner6 = (ip6_t *)ipha;
236010616SSebastien.Roy@Sun.COM break;
236110616SSebastien.Roy@Sun.COM default:
236210616SSebastien.Roy@Sun.COM return (0);
236310616SSebastien.Roy@Sun.COM }
236410616SSebastien.Roy@Sun.COM
236510616SSebastien.Roy@Sun.COM return (outer_hlen);
236610616SSebastien.Roy@Sun.COM }
236710616SSebastien.Roy@Sun.COM
236810616SSebastien.Roy@Sun.COM /*
236910616SSebastien.Roy@Sun.COM * Received ICMP error in response to an X over IPv4 packet that we
237010616SSebastien.Roy@Sun.COM * transmitted.
237110616SSebastien.Roy@Sun.COM *
237210616SSebastien.Roy@Sun.COM * NOTE: "outer" refers to what's inside the ICMP payload. We will get one of
237310616SSebastien.Roy@Sun.COM * the following:
237410616SSebastien.Roy@Sun.COM *
237510616SSebastien.Roy@Sun.COM * [IPv4(0)][ICMPv4][IPv4(1)][IPv4(2)][ULP]
237610616SSebastien.Roy@Sun.COM *
237710616SSebastien.Roy@Sun.COM * or
237810616SSebastien.Roy@Sun.COM *
237910616SSebastien.Roy@Sun.COM * [IPv4(0)][ICMPv4][IPv4(1)][IPv6][ULP]
238010616SSebastien.Roy@Sun.COM *
238110616SSebastien.Roy@Sun.COM * And "outer4" will get set to IPv4(1), and inner[46] will correspond to
238210616SSebastien.Roy@Sun.COM * whatever the very-inner packet is (IPv4(2) or IPv6).
238310616SSebastien.Roy@Sun.COM */
238410616SSebastien.Roy@Sun.COM static void
iptun_input_icmp_v4(iptun_t * iptun,mblk_t * data_mp,icmph_t * icmph,ip_recv_attr_t * ira)238511042SErik.Nordmark@Sun.COM iptun_input_icmp_v4(iptun_t *iptun, mblk_t *data_mp, icmph_t *icmph,
238611042SErik.Nordmark@Sun.COM ip_recv_attr_t *ira)
238710616SSebastien.Roy@Sun.COM {
238810616SSebastien.Roy@Sun.COM uint8_t *orig;
238910616SSebastien.Roy@Sun.COM ipha_t *outer4, *inner4;
239010616SSebastien.Roy@Sun.COM ip6_t *outer6, *inner6;
239110616SSebastien.Roy@Sun.COM int outer_hlen;
239210616SSebastien.Roy@Sun.COM uint8_t type, code;
239310616SSebastien.Roy@Sun.COM
239410616SSebastien.Roy@Sun.COM ASSERT(data_mp->b_cont == NULL);
239510616SSebastien.Roy@Sun.COM /*
239610616SSebastien.Roy@Sun.COM * Temporarily move b_rptr forward so that iptun_find_headers() can
239710616SSebastien.Roy@Sun.COM * find headers in the ICMP packet payload.
239810616SSebastien.Roy@Sun.COM */
239910616SSebastien.Roy@Sun.COM orig = data_mp->b_rptr;
240010616SSebastien.Roy@Sun.COM data_mp->b_rptr = (uint8_t *)(icmph + 1);
240110616SSebastien.Roy@Sun.COM /*
240210616SSebastien.Roy@Sun.COM * The ip module ensures that ICMP errors contain at least the
240310616SSebastien.Roy@Sun.COM * original IP header (otherwise, the error would never have made it
240410616SSebastien.Roy@Sun.COM * here).
240510616SSebastien.Roy@Sun.COM */
240610616SSebastien.Roy@Sun.COM ASSERT(MBLKL(data_mp) >= 0);
240711042SErik.Nordmark@Sun.COM outer_hlen = iptun_find_headers(data_mp, 0, &outer4, &inner4, &outer6,
240810616SSebastien.Roy@Sun.COM &inner6);
240910616SSebastien.Roy@Sun.COM ASSERT(outer6 == NULL);
241010616SSebastien.Roy@Sun.COM data_mp->b_rptr = orig;
241110616SSebastien.Roy@Sun.COM if (outer_hlen == 0) {
241211042SErik.Nordmark@Sun.COM iptun_drop_pkt(data_mp, &iptun->iptun_ierrors);
241310616SSebastien.Roy@Sun.COM return;
241410616SSebastien.Roy@Sun.COM }
241510616SSebastien.Roy@Sun.COM
241610616SSebastien.Roy@Sun.COM /* Only ICMP errors due to tunneled packets should reach here. */
241710616SSebastien.Roy@Sun.COM ASSERT(outer4->ipha_protocol == IPPROTO_ENCAP ||
241810616SSebastien.Roy@Sun.COM outer4->ipha_protocol == IPPROTO_IPV6);
241910616SSebastien.Roy@Sun.COM
242011042SErik.Nordmark@Sun.COM data_mp = ipsec_tun_inbound(ira, data_mp, iptun->iptun_itp,
242111042SErik.Nordmark@Sun.COM inner4, inner6, outer4, outer6, -outer_hlen, iptun->iptun_ns);
242211042SErik.Nordmark@Sun.COM if (data_mp == NULL) {
242310616SSebastien.Roy@Sun.COM /* Callee did all of the freeing. */
242410616SSebastien.Roy@Sun.COM atomic_inc_64(&iptun->iptun_ierrors);
242510616SSebastien.Roy@Sun.COM return;
242610616SSebastien.Roy@Sun.COM }
242710616SSebastien.Roy@Sun.COM /* We should never see reassembled fragment here. */
242810616SSebastien.Roy@Sun.COM ASSERT(data_mp->b_next == NULL);
242910616SSebastien.Roy@Sun.COM
243010616SSebastien.Roy@Sun.COM data_mp->b_rptr = (uint8_t *)outer4 + outer_hlen;
243110616SSebastien.Roy@Sun.COM
243210616SSebastien.Roy@Sun.COM /*
243310616SSebastien.Roy@Sun.COM * If the original packet being transmitted was itself an ICMP error,
243410616SSebastien.Roy@Sun.COM * then drop this packet. We don't want to generate an ICMP error in
243510616SSebastien.Roy@Sun.COM * response to an ICMP error.
243610616SSebastien.Roy@Sun.COM */
243710616SSebastien.Roy@Sun.COM if (is_icmp_error(data_mp, inner4, inner6)) {
243810616SSebastien.Roy@Sun.COM iptun_drop_pkt(data_mp, &iptun->iptun_norcvbuf);
243910616SSebastien.Roy@Sun.COM return;
244010616SSebastien.Roy@Sun.COM }
244110616SSebastien.Roy@Sun.COM
244210616SSebastien.Roy@Sun.COM switch (icmph->icmph_type) {
244310616SSebastien.Roy@Sun.COM case ICMP_DEST_UNREACHABLE:
244410616SSebastien.Roy@Sun.COM type = (inner4 != NULL ? icmph->icmph_type : ICMP6_DST_UNREACH);
244510616SSebastien.Roy@Sun.COM switch (icmph->icmph_code) {
244610616SSebastien.Roy@Sun.COM case ICMP_FRAGMENTATION_NEEDED: {
244710616SSebastien.Roy@Sun.COM uint32_t newmtu;
244810616SSebastien.Roy@Sun.COM
244910616SSebastien.Roy@Sun.COM /*
245010616SSebastien.Roy@Sun.COM * We reconcile this with the fact that the tunnel may
245110616SSebastien.Roy@Sun.COM * also have IPsec policy by letting iptun_update_mtu
245210616SSebastien.Roy@Sun.COM * take care of it.
245310616SSebastien.Roy@Sun.COM */
245411042SErik.Nordmark@Sun.COM newmtu = iptun_update_mtu(iptun, NULL,
245511042SErik.Nordmark@Sun.COM ntohs(icmph->icmph_du_mtu));
245610616SSebastien.Roy@Sun.COM
245710616SSebastien.Roy@Sun.COM if (inner4 != NULL) {
245810616SSebastien.Roy@Sun.COM iptun_icmp_fragneeded_v4(iptun, newmtu, inner4,
245911042SErik.Nordmark@Sun.COM data_mp, ira->ira_tsl);
246010616SSebastien.Roy@Sun.COM } else {
246110616SSebastien.Roy@Sun.COM iptun_icmp_toobig_v6(iptun, newmtu, inner6,
246211042SErik.Nordmark@Sun.COM data_mp, ira->ira_tsl);
246310616SSebastien.Roy@Sun.COM }
246410616SSebastien.Roy@Sun.COM return;
246510616SSebastien.Roy@Sun.COM }
246610616SSebastien.Roy@Sun.COM case ICMP_DEST_NET_UNREACH_ADMIN:
246710616SSebastien.Roy@Sun.COM case ICMP_DEST_HOST_UNREACH_ADMIN:
246810616SSebastien.Roy@Sun.COM code = (inner4 != NULL ? ICMP_DEST_NET_UNREACH_ADMIN :
246910616SSebastien.Roy@Sun.COM ICMP6_DST_UNREACH_ADMIN);
247010616SSebastien.Roy@Sun.COM break;
247110616SSebastien.Roy@Sun.COM default:
247210616SSebastien.Roy@Sun.COM code = (inner4 != NULL ? ICMP_HOST_UNREACHABLE :
247310616SSebastien.Roy@Sun.COM ICMP6_DST_UNREACH_ADDR);
247410616SSebastien.Roy@Sun.COM break;
247510616SSebastien.Roy@Sun.COM }
247610616SSebastien.Roy@Sun.COM break;
247710616SSebastien.Roy@Sun.COM case ICMP_TIME_EXCEEDED:
247810616SSebastien.Roy@Sun.COM if (inner6 != NULL) {
247910616SSebastien.Roy@Sun.COM type = ICMP6_TIME_EXCEEDED;
248010616SSebastien.Roy@Sun.COM code = 0;
248110616SSebastien.Roy@Sun.COM } /* else we're already set. */
248210616SSebastien.Roy@Sun.COM break;
248310616SSebastien.Roy@Sun.COM case ICMP_PARAM_PROBLEM:
248410616SSebastien.Roy@Sun.COM /*
248510616SSebastien.Roy@Sun.COM * This is a problem with the outer header we transmitted.
248610616SSebastien.Roy@Sun.COM * Treat this as an output error.
248710616SSebastien.Roy@Sun.COM */
248810616SSebastien.Roy@Sun.COM iptun_drop_pkt(data_mp, &iptun->iptun_oerrors);
248910616SSebastien.Roy@Sun.COM return;
249010616SSebastien.Roy@Sun.COM default:
249110616SSebastien.Roy@Sun.COM iptun_drop_pkt(data_mp, &iptun->iptun_norcvbuf);
249210616SSebastien.Roy@Sun.COM return;
249310616SSebastien.Roy@Sun.COM }
249410616SSebastien.Roy@Sun.COM
249511042SErik.Nordmark@Sun.COM if (inner4 != NULL) {
249611042SErik.Nordmark@Sun.COM iptun_icmp_error_v4(iptun, inner4, data_mp, type, code,
249711042SErik.Nordmark@Sun.COM ira->ira_tsl);
249811042SErik.Nordmark@Sun.COM } else {
249911042SErik.Nordmark@Sun.COM iptun_icmp_error_v6(iptun, inner6, data_mp, type, code, 0,
250011042SErik.Nordmark@Sun.COM ira->ira_tsl);
250111042SErik.Nordmark@Sun.COM }
250210616SSebastien.Roy@Sun.COM }
250310616SSebastien.Roy@Sun.COM
250410616SSebastien.Roy@Sun.COM /*
250510616SSebastien.Roy@Sun.COM * Return B_TRUE if the IPv6 packet pointed to by ip6h contains a Tunnel
250610616SSebastien.Roy@Sun.COM * Encapsulation Limit destination option. If there is one, set encaplim_ptr
250710616SSebastien.Roy@Sun.COM * to point to the option value.
250810616SSebastien.Roy@Sun.COM */
250910616SSebastien.Roy@Sun.COM static boolean_t
iptun_find_encaplimit(mblk_t * mp,ip6_t * ip6h,uint8_t ** encaplim_ptr)251010616SSebastien.Roy@Sun.COM iptun_find_encaplimit(mblk_t *mp, ip6_t *ip6h, uint8_t **encaplim_ptr)
251110616SSebastien.Roy@Sun.COM {
251211042SErik.Nordmark@Sun.COM ip_pkt_t pkt;
251310616SSebastien.Roy@Sun.COM uint8_t *endptr;
251410616SSebastien.Roy@Sun.COM ip6_dest_t *destp;
251510616SSebastien.Roy@Sun.COM struct ip6_opt *optp;
251610616SSebastien.Roy@Sun.COM
251710616SSebastien.Roy@Sun.COM pkt.ipp_fields = 0; /* must be initialized */
251811042SErik.Nordmark@Sun.COM (void) ip_find_hdr_v6(mp, ip6h, B_FALSE, &pkt, NULL);
251910616SSebastien.Roy@Sun.COM if ((pkt.ipp_fields & IPPF_DSTOPTS) != 0) {
252010616SSebastien.Roy@Sun.COM destp = pkt.ipp_dstopts;
252111042SErik.Nordmark@Sun.COM } else if ((pkt.ipp_fields & IPPF_RTHDRDSTOPTS) != 0) {
252211042SErik.Nordmark@Sun.COM destp = pkt.ipp_rthdrdstopts;
252310616SSebastien.Roy@Sun.COM } else {
252410616SSebastien.Roy@Sun.COM return (B_FALSE);
252510616SSebastien.Roy@Sun.COM }
252610616SSebastien.Roy@Sun.COM
252710616SSebastien.Roy@Sun.COM endptr = (uint8_t *)destp + 8 * (destp->ip6d_len + 1);
252810616SSebastien.Roy@Sun.COM optp = (struct ip6_opt *)(destp + 1);
252910616SSebastien.Roy@Sun.COM while (endptr - (uint8_t *)optp > sizeof (*optp)) {
253010616SSebastien.Roy@Sun.COM if (optp->ip6o_type == IP6OPT_TUNNEL_LIMIT) {
253110616SSebastien.Roy@Sun.COM if ((uint8_t *)(optp + 1) >= endptr)
253210616SSebastien.Roy@Sun.COM return (B_FALSE);
253310616SSebastien.Roy@Sun.COM *encaplim_ptr = (uint8_t *)&optp[1];
253410616SSebastien.Roy@Sun.COM return (B_TRUE);
253510616SSebastien.Roy@Sun.COM }
253610616SSebastien.Roy@Sun.COM optp = (struct ip6_opt *)((uint8_t *)optp + optp->ip6o_len + 2);
253710616SSebastien.Roy@Sun.COM }
253810616SSebastien.Roy@Sun.COM return (B_FALSE);
253910616SSebastien.Roy@Sun.COM }
254010616SSebastien.Roy@Sun.COM
254110616SSebastien.Roy@Sun.COM /*
254210616SSebastien.Roy@Sun.COM * Received ICMPv6 error in response to an X over IPv6 packet that we
254310616SSebastien.Roy@Sun.COM * transmitted.
254410616SSebastien.Roy@Sun.COM *
254510616SSebastien.Roy@Sun.COM * NOTE: "outer" refers to what's inside the ICMP payload. We will get one of
254610616SSebastien.Roy@Sun.COM * the following:
254710616SSebastien.Roy@Sun.COM *
254810616SSebastien.Roy@Sun.COM * [IPv6(0)][ICMPv6][IPv6(1)][IPv4][ULP]
254910616SSebastien.Roy@Sun.COM *
255010616SSebastien.Roy@Sun.COM * or
255110616SSebastien.Roy@Sun.COM *
255210616SSebastien.Roy@Sun.COM * [IPv6(0)][ICMPv6][IPv6(1)][IPv6(2)][ULP]
255310616SSebastien.Roy@Sun.COM *
255410616SSebastien.Roy@Sun.COM * And "outer6" will get set to IPv6(1), and inner[46] will correspond to
255510616SSebastien.Roy@Sun.COM * whatever the very-inner packet is (IPv4 or IPv6(2)).
255610616SSebastien.Roy@Sun.COM */
255710616SSebastien.Roy@Sun.COM static void
iptun_input_icmp_v6(iptun_t * iptun,mblk_t * data_mp,icmp6_t * icmp6h,ip_recv_attr_t * ira)255811042SErik.Nordmark@Sun.COM iptun_input_icmp_v6(iptun_t *iptun, mblk_t *data_mp, icmp6_t *icmp6h,
255911042SErik.Nordmark@Sun.COM ip_recv_attr_t *ira)
256010616SSebastien.Roy@Sun.COM {
256110616SSebastien.Roy@Sun.COM uint8_t *orig;
256210616SSebastien.Roy@Sun.COM ipha_t *outer4, *inner4;
256310616SSebastien.Roy@Sun.COM ip6_t *outer6, *inner6;
256410616SSebastien.Roy@Sun.COM int outer_hlen;
256510616SSebastien.Roy@Sun.COM uint8_t type, code;
256610616SSebastien.Roy@Sun.COM
256710616SSebastien.Roy@Sun.COM ASSERT(data_mp->b_cont == NULL);
256810616SSebastien.Roy@Sun.COM
256910616SSebastien.Roy@Sun.COM /*
257010616SSebastien.Roy@Sun.COM * Temporarily move b_rptr forward so that iptun_find_headers() can
257110616SSebastien.Roy@Sun.COM * find IP headers in the ICMP packet payload.
257210616SSebastien.Roy@Sun.COM */
257310616SSebastien.Roy@Sun.COM orig = data_mp->b_rptr;
257410616SSebastien.Roy@Sun.COM data_mp->b_rptr = (uint8_t *)(icmp6h + 1);
257510616SSebastien.Roy@Sun.COM /*
257610616SSebastien.Roy@Sun.COM * The ip module ensures that ICMP errors contain at least the
257710616SSebastien.Roy@Sun.COM * original IP header (otherwise, the error would never have made it
257810616SSebastien.Roy@Sun.COM * here).
257910616SSebastien.Roy@Sun.COM */
258010616SSebastien.Roy@Sun.COM ASSERT(MBLKL(data_mp) >= 0);
258111042SErik.Nordmark@Sun.COM outer_hlen = iptun_find_headers(data_mp, 0, &outer4, &inner4, &outer6,
258210616SSebastien.Roy@Sun.COM &inner6);
258310616SSebastien.Roy@Sun.COM ASSERT(outer4 == NULL);
258410616SSebastien.Roy@Sun.COM data_mp->b_rptr = orig; /* Restore r_ptr */
258510616SSebastien.Roy@Sun.COM if (outer_hlen == 0) {
258611042SErik.Nordmark@Sun.COM iptun_drop_pkt(data_mp, &iptun->iptun_ierrors);
258710616SSebastien.Roy@Sun.COM return;
258810616SSebastien.Roy@Sun.COM }
258910616SSebastien.Roy@Sun.COM
259011042SErik.Nordmark@Sun.COM data_mp = ipsec_tun_inbound(ira, data_mp, iptun->iptun_itp,
259111042SErik.Nordmark@Sun.COM inner4, inner6, outer4, outer6, -outer_hlen, iptun->iptun_ns);
259211042SErik.Nordmark@Sun.COM if (data_mp == NULL) {
259310616SSebastien.Roy@Sun.COM /* Callee did all of the freeing. */
259410616SSebastien.Roy@Sun.COM atomic_inc_64(&iptun->iptun_ierrors);
259510616SSebastien.Roy@Sun.COM return;
259610616SSebastien.Roy@Sun.COM }
259710616SSebastien.Roy@Sun.COM /* We should never see reassembled fragment here. */
259810616SSebastien.Roy@Sun.COM ASSERT(data_mp->b_next == NULL);
259910616SSebastien.Roy@Sun.COM
260010616SSebastien.Roy@Sun.COM data_mp->b_rptr = (uint8_t *)outer6 + outer_hlen;
260110616SSebastien.Roy@Sun.COM
260210616SSebastien.Roy@Sun.COM /*
260310616SSebastien.Roy@Sun.COM * If the original packet being transmitted was itself an ICMP error,
260410616SSebastien.Roy@Sun.COM * then drop this packet. We don't want to generate an ICMP error in
260510616SSebastien.Roy@Sun.COM * response to an ICMP error.
260610616SSebastien.Roy@Sun.COM */
260710616SSebastien.Roy@Sun.COM if (is_icmp_error(data_mp, inner4, inner6)) {
260810616SSebastien.Roy@Sun.COM iptun_drop_pkt(data_mp, &iptun->iptun_norcvbuf);
260910616SSebastien.Roy@Sun.COM return;
261010616SSebastien.Roy@Sun.COM }
261110616SSebastien.Roy@Sun.COM
261210616SSebastien.Roy@Sun.COM switch (icmp6h->icmp6_type) {
261310616SSebastien.Roy@Sun.COM case ICMP6_PARAM_PROB: {
261410616SSebastien.Roy@Sun.COM uint8_t *encaplim_ptr;
261510616SSebastien.Roy@Sun.COM
261610616SSebastien.Roy@Sun.COM /*
261710616SSebastien.Roy@Sun.COM * If the ICMPv6 error points to a valid Tunnel Encapsulation
261810616SSebastien.Roy@Sun.COM * Limit option and the limit value is 0, then fall through
261910616SSebastien.Roy@Sun.COM * and send a host unreachable message. Otherwise, treat the
262010616SSebastien.Roy@Sun.COM * error as an output error, as there must have been a problem
262110616SSebastien.Roy@Sun.COM * with a packet we sent.
262210616SSebastien.Roy@Sun.COM */
262310616SSebastien.Roy@Sun.COM if (!iptun_find_encaplimit(data_mp, outer6, &encaplim_ptr) ||
262410616SSebastien.Roy@Sun.COM (icmp6h->icmp6_pptr !=
262510616SSebastien.Roy@Sun.COM ((ptrdiff_t)encaplim_ptr - (ptrdiff_t)outer6)) ||
262610616SSebastien.Roy@Sun.COM *encaplim_ptr != 0) {
262710616SSebastien.Roy@Sun.COM iptun_drop_pkt(data_mp, &iptun->iptun_oerrors);
262810616SSebastien.Roy@Sun.COM return;
262910616SSebastien.Roy@Sun.COM }
263010616SSebastien.Roy@Sun.COM /* FALLTHRU */
263110616SSebastien.Roy@Sun.COM }
263210616SSebastien.Roy@Sun.COM case ICMP6_TIME_EXCEEDED:
263310616SSebastien.Roy@Sun.COM case ICMP6_DST_UNREACH:
263410616SSebastien.Roy@Sun.COM type = (inner4 != NULL ? ICMP_DEST_UNREACHABLE :
263510616SSebastien.Roy@Sun.COM ICMP6_DST_UNREACH);
263610616SSebastien.Roy@Sun.COM code = (inner4 != NULL ? ICMP_HOST_UNREACHABLE :
263710616SSebastien.Roy@Sun.COM ICMP6_DST_UNREACH_ADDR);
263810616SSebastien.Roy@Sun.COM break;
263910616SSebastien.Roy@Sun.COM case ICMP6_PACKET_TOO_BIG: {
264010616SSebastien.Roy@Sun.COM uint32_t newmtu;
264110616SSebastien.Roy@Sun.COM
264210616SSebastien.Roy@Sun.COM /*
264310616SSebastien.Roy@Sun.COM * We reconcile this with the fact that the tunnel may also
264410616SSebastien.Roy@Sun.COM * have IPsec policy by letting iptun_update_mtu take care of
264510616SSebastien.Roy@Sun.COM * it.
264610616SSebastien.Roy@Sun.COM */
264711042SErik.Nordmark@Sun.COM newmtu = iptun_update_mtu(iptun, NULL,
264811042SErik.Nordmark@Sun.COM ntohl(icmp6h->icmp6_mtu));
264910616SSebastien.Roy@Sun.COM
265010616SSebastien.Roy@Sun.COM if (inner4 != NULL) {
265110616SSebastien.Roy@Sun.COM iptun_icmp_fragneeded_v4(iptun, newmtu, inner4,
265211042SErik.Nordmark@Sun.COM data_mp, ira->ira_tsl);
265310616SSebastien.Roy@Sun.COM } else {
265411042SErik.Nordmark@Sun.COM iptun_icmp_toobig_v6(iptun, newmtu, inner6, data_mp,
265511042SErik.Nordmark@Sun.COM ira->ira_tsl);
265610616SSebastien.Roy@Sun.COM }
265710616SSebastien.Roy@Sun.COM return;
265810616SSebastien.Roy@Sun.COM }
265910616SSebastien.Roy@Sun.COM default:
266010616SSebastien.Roy@Sun.COM iptun_drop_pkt(data_mp, &iptun->iptun_norcvbuf);
266110616SSebastien.Roy@Sun.COM return;
266210616SSebastien.Roy@Sun.COM }
266310616SSebastien.Roy@Sun.COM
266411042SErik.Nordmark@Sun.COM if (inner4 != NULL) {
266511042SErik.Nordmark@Sun.COM iptun_icmp_error_v4(iptun, inner4, data_mp, type, code,
266611042SErik.Nordmark@Sun.COM ira->ira_tsl);
266711042SErik.Nordmark@Sun.COM } else {
266811042SErik.Nordmark@Sun.COM iptun_icmp_error_v6(iptun, inner6, data_mp, type, code, 0,
266911042SErik.Nordmark@Sun.COM ira->ira_tsl);
267011042SErik.Nordmark@Sun.COM }
267110616SSebastien.Roy@Sun.COM }
267210616SSebastien.Roy@Sun.COM
267311042SErik.Nordmark@Sun.COM /*
267411042SErik.Nordmark@Sun.COM * Called as conn_recvicmp from IP for ICMP errors.
267511042SErik.Nordmark@Sun.COM */
267611042SErik.Nordmark@Sun.COM /* ARGSUSED2 */
267710616SSebastien.Roy@Sun.COM static void
iptun_input_icmp(void * arg,mblk_t * mp,void * arg2,ip_recv_attr_t * ira)267811042SErik.Nordmark@Sun.COM iptun_input_icmp(void *arg, mblk_t *mp, void *arg2, ip_recv_attr_t *ira)
267910616SSebastien.Roy@Sun.COM {
268011042SErik.Nordmark@Sun.COM conn_t *connp = arg;
268111042SErik.Nordmark@Sun.COM iptun_t *iptun = connp->conn_iptun;
268211042SErik.Nordmark@Sun.COM mblk_t *tmpmp;
268311042SErik.Nordmark@Sun.COM size_t hlen;
268411042SErik.Nordmark@Sun.COM
268511042SErik.Nordmark@Sun.COM ASSERT(IPCL_IS_IPTUN(connp));
268611042SErik.Nordmark@Sun.COM
268711042SErik.Nordmark@Sun.COM if (mp->b_cont != NULL) {
268810616SSebastien.Roy@Sun.COM /*
268910616SSebastien.Roy@Sun.COM * Since ICMP error processing necessitates access to bits
269010616SSebastien.Roy@Sun.COM * that are within the ICMP error payload (the original packet
269110616SSebastien.Roy@Sun.COM * that caused the error), pull everything up into a single
269210616SSebastien.Roy@Sun.COM * block for convenience.
269310616SSebastien.Roy@Sun.COM */
269411042SErik.Nordmark@Sun.COM if ((tmpmp = msgpullup(mp, -1)) == NULL) {
269511042SErik.Nordmark@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_norcvbuf);
269610616SSebastien.Roy@Sun.COM return;
269710616SSebastien.Roy@Sun.COM }
269811042SErik.Nordmark@Sun.COM freemsg(mp);
269911042SErik.Nordmark@Sun.COM mp = tmpmp;
270010616SSebastien.Roy@Sun.COM }
270110616SSebastien.Roy@Sun.COM
270211042SErik.Nordmark@Sun.COM hlen = ira->ira_ip_hdr_length;
270310616SSebastien.Roy@Sun.COM switch (iptun->iptun_typeinfo->iti_ipvers) {
270410616SSebastien.Roy@Sun.COM case IPV4_VERSION:
270510616SSebastien.Roy@Sun.COM /*
270610616SSebastien.Roy@Sun.COM * The outer IP header coming up from IP is always ipha_t
270710616SSebastien.Roy@Sun.COM * alligned (otherwise, we would have crashed in ip).
270810616SSebastien.Roy@Sun.COM */
270911042SErik.Nordmark@Sun.COM iptun_input_icmp_v4(iptun, mp, (icmph_t *)(mp->b_rptr + hlen),
271011042SErik.Nordmark@Sun.COM ira);
271110616SSebastien.Roy@Sun.COM break;
271210616SSebastien.Roy@Sun.COM case IPV6_VERSION:
271311042SErik.Nordmark@Sun.COM iptun_input_icmp_v6(iptun, mp, (icmp6_t *)(mp->b_rptr + hlen),
271411042SErik.Nordmark@Sun.COM ira);
271510616SSebastien.Roy@Sun.COM break;
271610616SSebastien.Roy@Sun.COM }
271710616SSebastien.Roy@Sun.COM }
271810616SSebastien.Roy@Sun.COM
271910616SSebastien.Roy@Sun.COM static boolean_t
iptun_in_6to4_ok(iptun_t * iptun,ipha_t * outer4,ip6_t * inner6)272010616SSebastien.Roy@Sun.COM iptun_in_6to4_ok(iptun_t *iptun, ipha_t *outer4, ip6_t *inner6)
272110616SSebastien.Roy@Sun.COM {
272210616SSebastien.Roy@Sun.COM ipaddr_t v4addr;
272310616SSebastien.Roy@Sun.COM
272410616SSebastien.Roy@Sun.COM /*
272510722SSebastien.Roy@Sun.COM * It's possible that someone sent us an IPv4-in-IPv4 packet with the
272610722SSebastien.Roy@Sun.COM * IPv4 address of a 6to4 tunnel as the destination.
272710722SSebastien.Roy@Sun.COM */
272810722SSebastien.Roy@Sun.COM if (inner6 == NULL)
272910722SSebastien.Roy@Sun.COM return (B_FALSE);
273010722SSebastien.Roy@Sun.COM
273110722SSebastien.Roy@Sun.COM /*
273210616SSebastien.Roy@Sun.COM * Make sure that the IPv6 destination is within the site that this
273310616SSebastien.Roy@Sun.COM * 6to4 tunnel is routing for. We don't want people bouncing random
273410616SSebastien.Roy@Sun.COM * tunneled IPv6 packets through this 6to4 router.
273510616SSebastien.Roy@Sun.COM */
273610616SSebastien.Roy@Sun.COM IN6_6TO4_TO_V4ADDR(&inner6->ip6_dst, (struct in_addr *)&v4addr);
273710616SSebastien.Roy@Sun.COM if (outer4->ipha_dst != v4addr)
273810616SSebastien.Roy@Sun.COM return (B_FALSE);
273910616SSebastien.Roy@Sun.COM
274010616SSebastien.Roy@Sun.COM if (IN6_IS_ADDR_6TO4(&inner6->ip6_src)) {
274110616SSebastien.Roy@Sun.COM /*
274210616SSebastien.Roy@Sun.COM * Section 9 of RFC 3056 (security considerations) suggests
274310616SSebastien.Roy@Sun.COM * that when a packet is from a 6to4 site (i.e., it's not a
274410616SSebastien.Roy@Sun.COM * global address being forwarded froma relay router), make
274510616SSebastien.Roy@Sun.COM * sure that the packet was tunneled by that site's 6to4
274610616SSebastien.Roy@Sun.COM * router.
274710616SSebastien.Roy@Sun.COM */
274810616SSebastien.Roy@Sun.COM IN6_6TO4_TO_V4ADDR(&inner6->ip6_src, (struct in_addr *)&v4addr);
274910616SSebastien.Roy@Sun.COM if (outer4->ipha_src != v4addr)
275010616SSebastien.Roy@Sun.COM return (B_FALSE);
275110616SSebastien.Roy@Sun.COM } else {
275210616SSebastien.Roy@Sun.COM /*
275310616SSebastien.Roy@Sun.COM * Only accept packets from a relay router if we've configured
275410616SSebastien.Roy@Sun.COM * outbound relay router functionality.
275510616SSebastien.Roy@Sun.COM */
275610616SSebastien.Roy@Sun.COM if (iptun->iptun_iptuns->iptuns_relay_rtr_addr == INADDR_ANY)
275710616SSebastien.Roy@Sun.COM return (B_FALSE);
275810616SSebastien.Roy@Sun.COM }
275910616SSebastien.Roy@Sun.COM
276010616SSebastien.Roy@Sun.COM return (B_TRUE);
276110616SSebastien.Roy@Sun.COM }
276210616SSebastien.Roy@Sun.COM
276310616SSebastien.Roy@Sun.COM /*
276410616SSebastien.Roy@Sun.COM * Input function for everything that comes up from the ip module below us.
276510616SSebastien.Roy@Sun.COM * This is called directly from the ip module via connp->conn_recv().
276610616SSebastien.Roy@Sun.COM *
276711042SErik.Nordmark@Sun.COM * We receive M_DATA messages with IP-in-IP tunneled packets.
276810616SSebastien.Roy@Sun.COM */
276911042SErik.Nordmark@Sun.COM /* ARGSUSED2 */
277010616SSebastien.Roy@Sun.COM static void
iptun_input(void * arg,mblk_t * data_mp,void * arg2,ip_recv_attr_t * ira)277111042SErik.Nordmark@Sun.COM iptun_input(void *arg, mblk_t *data_mp, void *arg2, ip_recv_attr_t *ira)
277210616SSebastien.Roy@Sun.COM {
277310616SSebastien.Roy@Sun.COM conn_t *connp = arg;
277410616SSebastien.Roy@Sun.COM iptun_t *iptun = connp->conn_iptun;
277510616SSebastien.Roy@Sun.COM int outer_hlen;
277610616SSebastien.Roy@Sun.COM ipha_t *outer4, *inner4;
277710616SSebastien.Roy@Sun.COM ip6_t *outer6, *inner6;
277810616SSebastien.Roy@Sun.COM
277910616SSebastien.Roy@Sun.COM ASSERT(IPCL_IS_IPTUN(connp));
278011042SErik.Nordmark@Sun.COM ASSERT(DB_TYPE(data_mp) == M_DATA);
278111042SErik.Nordmark@Sun.COM
278211042SErik.Nordmark@Sun.COM outer_hlen = iptun_find_headers(data_mp, ira->ira_ip_hdr_length,
278311042SErik.Nordmark@Sun.COM &outer4, &inner4, &outer6, &inner6);
278411042SErik.Nordmark@Sun.COM if (outer_hlen == 0)
278510616SSebastien.Roy@Sun.COM goto drop;
278610616SSebastien.Roy@Sun.COM
278710616SSebastien.Roy@Sun.COM /*
278810616SSebastien.Roy@Sun.COM * If the system is labeled, we call tsol_check_dest() on the packet
278910616SSebastien.Roy@Sun.COM * destination (our local tunnel address) to ensure that the packet as
279010616SSebastien.Roy@Sun.COM * labeled should be allowed to be sent to us. We don't need to call
279110616SSebastien.Roy@Sun.COM * the more involved tsol_receive_local() since the tunnel link itself
279210616SSebastien.Roy@Sun.COM * cannot be assigned to shared-stack non-global zones.
279310616SSebastien.Roy@Sun.COM */
279411042SErik.Nordmark@Sun.COM if (ira->ira_flags & IRAF_SYSTEM_LABELED) {
279511042SErik.Nordmark@Sun.COM if (ira->ira_tsl == NULL)
279610616SSebastien.Roy@Sun.COM goto drop;
279711042SErik.Nordmark@Sun.COM if (tsol_check_dest(ira->ira_tsl, (outer4 != NULL ?
279810616SSebastien.Roy@Sun.COM (void *)&outer4->ipha_dst : (void *)&outer6->ip6_dst),
279910616SSebastien.Roy@Sun.COM (outer4 != NULL ? IPV4_VERSION : IPV6_VERSION),
280011042SErik.Nordmark@Sun.COM CONN_MAC_DEFAULT, B_FALSE, NULL) != 0)
280110616SSebastien.Roy@Sun.COM goto drop;
280210616SSebastien.Roy@Sun.COM }
280310616SSebastien.Roy@Sun.COM
280411042SErik.Nordmark@Sun.COM data_mp = ipsec_tun_inbound(ira, data_mp, iptun->iptun_itp,
280511042SErik.Nordmark@Sun.COM inner4, inner6, outer4, outer6, outer_hlen, iptun->iptun_ns);
280611042SErik.Nordmark@Sun.COM if (data_mp == NULL) {
280710692Sdanmcd@sun.com /* Callee did all of the freeing. */
280810692Sdanmcd@sun.com return;
280910616SSebastien.Roy@Sun.COM }
281010616SSebastien.Roy@Sun.COM
281110616SSebastien.Roy@Sun.COM if (iptun->iptun_typeinfo->iti_type == IPTUN_TYPE_6TO4 &&
281210616SSebastien.Roy@Sun.COM !iptun_in_6to4_ok(iptun, outer4, inner6))
281310616SSebastien.Roy@Sun.COM goto drop;
281410616SSebastien.Roy@Sun.COM
281510616SSebastien.Roy@Sun.COM /*
281610616SSebastien.Roy@Sun.COM * We need to statistically account for each packet individually, so
281710616SSebastien.Roy@Sun.COM * we might as well split up any b_next chains here.
281810616SSebastien.Roy@Sun.COM */
281910616SSebastien.Roy@Sun.COM do {
282011042SErik.Nordmark@Sun.COM mblk_t *mp;
282111042SErik.Nordmark@Sun.COM
282210616SSebastien.Roy@Sun.COM mp = data_mp->b_next;
282310616SSebastien.Roy@Sun.COM data_mp->b_next = NULL;
282410616SSebastien.Roy@Sun.COM
282510616SSebastien.Roy@Sun.COM atomic_inc_64(&iptun->iptun_ipackets);
282610616SSebastien.Roy@Sun.COM atomic_add_64(&iptun->iptun_rbytes, msgdsize(data_mp));
282710616SSebastien.Roy@Sun.COM mac_rx(iptun->iptun_mh, NULL, data_mp);
282810616SSebastien.Roy@Sun.COM
282910616SSebastien.Roy@Sun.COM data_mp = mp;
283010616SSebastien.Roy@Sun.COM } while (data_mp != NULL);
283110616SSebastien.Roy@Sun.COM return;
283210616SSebastien.Roy@Sun.COM drop:
283311042SErik.Nordmark@Sun.COM iptun_drop_pkt(data_mp, &iptun->iptun_ierrors);
283410616SSebastien.Roy@Sun.COM }
283510616SSebastien.Roy@Sun.COM
283610616SSebastien.Roy@Sun.COM /*
283710616SSebastien.Roy@Sun.COM * Do 6to4-specific header-processing on output. Return B_TRUE if the packet
283810616SSebastien.Roy@Sun.COM * was processed without issue, or B_FALSE if the packet had issues and should
283910616SSebastien.Roy@Sun.COM * be dropped.
284010616SSebastien.Roy@Sun.COM */
284110616SSebastien.Roy@Sun.COM static boolean_t
iptun_out_process_6to4(iptun_t * iptun,ipha_t * outer4,ip6_t * inner6)284210616SSebastien.Roy@Sun.COM iptun_out_process_6to4(iptun_t *iptun, ipha_t *outer4, ip6_t *inner6)
284310616SSebastien.Roy@Sun.COM {
284410616SSebastien.Roy@Sun.COM ipaddr_t v4addr;
284510616SSebastien.Roy@Sun.COM
284610616SSebastien.Roy@Sun.COM /*
284710616SSebastien.Roy@Sun.COM * IPv6 source must be a 6to4 address. This is because a conscious
284810616SSebastien.Roy@Sun.COM * decision was made to not allow a Solaris system to be used as a
284910616SSebastien.Roy@Sun.COM * relay router (for security reasons) when 6to4 was initially
285010616SSebastien.Roy@Sun.COM * integrated. If this decision is ever reversed, the following check
285110616SSebastien.Roy@Sun.COM * can be removed.
285210616SSebastien.Roy@Sun.COM */
285310616SSebastien.Roy@Sun.COM if (!IN6_IS_ADDR_6TO4(&inner6->ip6_src))
285410616SSebastien.Roy@Sun.COM return (B_FALSE);
285510616SSebastien.Roy@Sun.COM
285610616SSebastien.Roy@Sun.COM /*
285710616SSebastien.Roy@Sun.COM * RFC3056 mandates that the IPv4 source MUST be set to the IPv4
285810616SSebastien.Roy@Sun.COM * portion of the 6to4 IPv6 source address. In other words, make sure
285910616SSebastien.Roy@Sun.COM * that we're tunneling packets from our own 6to4 site.
286010616SSebastien.Roy@Sun.COM */
286110616SSebastien.Roy@Sun.COM IN6_6TO4_TO_V4ADDR(&inner6->ip6_src, (struct in_addr *)&v4addr);
286210616SSebastien.Roy@Sun.COM if (outer4->ipha_src != v4addr)
286310616SSebastien.Roy@Sun.COM return (B_FALSE);
286410616SSebastien.Roy@Sun.COM
286510616SSebastien.Roy@Sun.COM /*
286610616SSebastien.Roy@Sun.COM * Automatically set the destination of the outer IPv4 header as
286710616SSebastien.Roy@Sun.COM * described in RFC3056. There are two possibilities:
286810616SSebastien.Roy@Sun.COM *
286910616SSebastien.Roy@Sun.COM * a. If the IPv6 destination is a 6to4 address, set the IPv4 address
287010616SSebastien.Roy@Sun.COM * to the IPv4 portion of the 6to4 address.
287110616SSebastien.Roy@Sun.COM * b. If the IPv6 destination is a native IPv6 address, set the IPv4
287210616SSebastien.Roy@Sun.COM * destination to the address of a relay router.
287310616SSebastien.Roy@Sun.COM *
287410616SSebastien.Roy@Sun.COM * Design Note: b shouldn't be necessary here, and this is a flaw in
287510616SSebastien.Roy@Sun.COM * the design of the 6to4relay command. Instead of setting a 6to4
287610616SSebastien.Roy@Sun.COM * relay address in this module via an ioctl, the 6to4relay command
287710616SSebastien.Roy@Sun.COM * could simply add a IPv6 route for native IPv6 addresses (such as a
287810616SSebastien.Roy@Sun.COM * default route) in the forwarding table that uses a 6to4 destination
287910616SSebastien.Roy@Sun.COM * as its next hop, and the IPv4 portion of that address could be a
288010616SSebastien.Roy@Sun.COM * 6to4 relay address. In order for this to work, IP would have to
288110616SSebastien.Roy@Sun.COM * resolve the next hop address, which would necessitate a link-layer
288210616SSebastien.Roy@Sun.COM * address resolver for 6to4 links, which doesn't exist today.
288310616SSebastien.Roy@Sun.COM *
288410616SSebastien.Roy@Sun.COM * In fact, if a resolver existed for 6to4 links, then setting the
288510616SSebastien.Roy@Sun.COM * IPv4 destination in the outer header could be done as part of
288610616SSebastien.Roy@Sun.COM * link-layer address resolution and fast-path header generation, and
288710616SSebastien.Roy@Sun.COM * not here.
288810616SSebastien.Roy@Sun.COM */
288910616SSebastien.Roy@Sun.COM if (IN6_IS_ADDR_6TO4(&inner6->ip6_dst)) {
289010616SSebastien.Roy@Sun.COM /* destination is a 6to4 router */
289110616SSebastien.Roy@Sun.COM IN6_6TO4_TO_V4ADDR(&inner6->ip6_dst,
289210616SSebastien.Roy@Sun.COM (struct in_addr *)&outer4->ipha_dst);
289311042SErik.Nordmark@Sun.COM
289411042SErik.Nordmark@Sun.COM /* Reject attempts to send to INADDR_ANY */
289511042SErik.Nordmark@Sun.COM if (outer4->ipha_dst == INADDR_ANY)
289611042SErik.Nordmark@Sun.COM return (B_FALSE);
289710616SSebastien.Roy@Sun.COM } else {
289810616SSebastien.Roy@Sun.COM /*
289910616SSebastien.Roy@Sun.COM * The destination is a native IPv6 address. If output to a
290010616SSebastien.Roy@Sun.COM * relay-router is enabled, use the relay-router's IPv4
290110616SSebastien.Roy@Sun.COM * address as the destination.
290210616SSebastien.Roy@Sun.COM */
290310616SSebastien.Roy@Sun.COM if (iptun->iptun_iptuns->iptuns_relay_rtr_addr == INADDR_ANY)
290410616SSebastien.Roy@Sun.COM return (B_FALSE);
290510616SSebastien.Roy@Sun.COM outer4->ipha_dst = iptun->iptun_iptuns->iptuns_relay_rtr_addr;
290610616SSebastien.Roy@Sun.COM }
290710616SSebastien.Roy@Sun.COM
290810616SSebastien.Roy@Sun.COM /*
290910616SSebastien.Roy@Sun.COM * If the outer source and destination are equal, this means that the
291010616SSebastien.Roy@Sun.COM * 6to4 router somehow forwarded an IPv6 packet destined for its own
291110616SSebastien.Roy@Sun.COM * 6to4 site to its 6to4 tunnel interface, which will result in this
291210616SSebastien.Roy@Sun.COM * packet infinitely bouncing between ip and iptun.
291310616SSebastien.Roy@Sun.COM */
291410616SSebastien.Roy@Sun.COM return (outer4->ipha_src != outer4->ipha_dst);
291510616SSebastien.Roy@Sun.COM }
291610616SSebastien.Roy@Sun.COM
291710616SSebastien.Roy@Sun.COM /*
291810616SSebastien.Roy@Sun.COM * Process output packets with outer IPv4 headers. Frees mp and bumps stat on
291910616SSebastien.Roy@Sun.COM * error.
292010616SSebastien.Roy@Sun.COM */
292110616SSebastien.Roy@Sun.COM static mblk_t *
iptun_out_process_ipv4(iptun_t * iptun,mblk_t * mp,ipha_t * outer4,ipha_t * inner4,ip6_t * inner6,ip_xmit_attr_t * ixa)292210616SSebastien.Roy@Sun.COM iptun_out_process_ipv4(iptun_t *iptun, mblk_t *mp, ipha_t *outer4,
292311042SErik.Nordmark@Sun.COM ipha_t *inner4, ip6_t *inner6, ip_xmit_attr_t *ixa)
292410616SSebastien.Roy@Sun.COM {
292510616SSebastien.Roy@Sun.COM uint8_t *innerptr = (inner4 != NULL ?
292610616SSebastien.Roy@Sun.COM (uint8_t *)inner4 : (uint8_t *)inner6);
292711042SErik.Nordmark@Sun.COM size_t minmtu = iptun->iptun_typeinfo->iti_minmtu;
292810616SSebastien.Roy@Sun.COM
292910616SSebastien.Roy@Sun.COM if (inner4 != NULL) {
293010616SSebastien.Roy@Sun.COM ASSERT(outer4->ipha_protocol == IPPROTO_ENCAP);
293110616SSebastien.Roy@Sun.COM /*
293210616SSebastien.Roy@Sun.COM * Copy the tos from the inner IPv4 header. We mask off ECN
293310616SSebastien.Roy@Sun.COM * bits (bits 6 and 7) because there is currently no
293410616SSebastien.Roy@Sun.COM * tunnel-tunnel communication to determine if both sides
293510616SSebastien.Roy@Sun.COM * support ECN. We opt for the safe choice: don't copy the
293610616SSebastien.Roy@Sun.COM * ECN bits when doing encapsulation.
293710616SSebastien.Roy@Sun.COM */
293810616SSebastien.Roy@Sun.COM outer4->ipha_type_of_service =
293910616SSebastien.Roy@Sun.COM inner4->ipha_type_of_service & ~0x03;
294010616SSebastien.Roy@Sun.COM } else {
294110616SSebastien.Roy@Sun.COM ASSERT(outer4->ipha_protocol == IPPROTO_IPV6 &&
294210616SSebastien.Roy@Sun.COM inner6 != NULL);
294310616SSebastien.Roy@Sun.COM }
294411042SErik.Nordmark@Sun.COM if (ixa->ixa_flags & IXAF_PMTU_IPV4_DF)
294511042SErik.Nordmark@Sun.COM outer4->ipha_fragment_offset_and_flags |= IPH_DF_HTONS;
294611042SErik.Nordmark@Sun.COM else
294711042SErik.Nordmark@Sun.COM outer4->ipha_fragment_offset_and_flags &= ~IPH_DF_HTONS;
294810616SSebastien.Roy@Sun.COM
294910616SSebastien.Roy@Sun.COM /*
295010616SSebastien.Roy@Sun.COM * As described in section 3.2.2 of RFC4213, if the packet payload is
295110616SSebastien.Roy@Sun.COM * less than or equal to the minimum MTU size, then we need to allow
295210616SSebastien.Roy@Sun.COM * IPv4 to fragment the packet. The reason is that even if we end up
295310616SSebastien.Roy@Sun.COM * receiving an ICMP frag-needed, the interface above this tunnel
295410616SSebastien.Roy@Sun.COM * won't be allowed to drop its MTU as a result, since the packet was
295510616SSebastien.Roy@Sun.COM * already smaller than the smallest allowable MTU for that interface.
295610616SSebastien.Roy@Sun.COM */
295711042SErik.Nordmark@Sun.COM if (mp->b_wptr - innerptr <= minmtu) {
295810616SSebastien.Roy@Sun.COM outer4->ipha_fragment_offset_and_flags = 0;
295911042SErik.Nordmark@Sun.COM ixa->ixa_flags &= ~IXAF_DONTFRAG;
296011042SErik.Nordmark@Sun.COM } else if (!(ixa->ixa_flags & IXAF_PMTU_TOO_SMALL) &&
296111042SErik.Nordmark@Sun.COM (iptun->iptun_typeinfo->iti_type != IPTUN_TYPE_6TO4)) {
296211042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_DONTFRAG;
296311042SErik.Nordmark@Sun.COM }
296411042SErik.Nordmark@Sun.COM
296511042SErik.Nordmark@Sun.COM ixa->ixa_ip_hdr_length = IPH_HDR_LENGTH(outer4);
296611042SErik.Nordmark@Sun.COM ixa->ixa_pktlen = msgdsize(mp);
296711042SErik.Nordmark@Sun.COM ixa->ixa_protocol = outer4->ipha_protocol;
296811042SErik.Nordmark@Sun.COM
296911042SErik.Nordmark@Sun.COM outer4->ipha_length = htons(ixa->ixa_pktlen);
297010616SSebastien.Roy@Sun.COM return (mp);
297110616SSebastien.Roy@Sun.COM }
297210616SSebastien.Roy@Sun.COM
297310616SSebastien.Roy@Sun.COM /*
297410616SSebastien.Roy@Sun.COM * Insert an encapsulation limit destination option in the packet provided.
297510616SSebastien.Roy@Sun.COM * Always consumes the mp argument and returns a new mblk pointer.
297610616SSebastien.Roy@Sun.COM */
297710616SSebastien.Roy@Sun.COM static mblk_t *
iptun_insert_encaplimit(iptun_t * iptun,mblk_t * mp,ip6_t * outer6,uint8_t limit)297810616SSebastien.Roy@Sun.COM iptun_insert_encaplimit(iptun_t *iptun, mblk_t *mp, ip6_t *outer6,
297910616SSebastien.Roy@Sun.COM uint8_t limit)
298010616SSebastien.Roy@Sun.COM {
298110616SSebastien.Roy@Sun.COM mblk_t *newmp;
298210616SSebastien.Roy@Sun.COM iptun_ipv6hdrs_t *newouter6;
298310616SSebastien.Roy@Sun.COM
298410616SSebastien.Roy@Sun.COM ASSERT(outer6->ip6_nxt == IPPROTO_IPV6);
298510616SSebastien.Roy@Sun.COM ASSERT(mp->b_cont == NULL);
298610616SSebastien.Roy@Sun.COM
298710616SSebastien.Roy@Sun.COM mp->b_rptr += sizeof (ip6_t);
298811042SErik.Nordmark@Sun.COM newmp = allocb(sizeof (iptun_ipv6hdrs_t) + MBLKL(mp), BPRI_MED);
298910616SSebastien.Roy@Sun.COM if (newmp == NULL) {
299010616SSebastien.Roy@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_noxmtbuf);
299110616SSebastien.Roy@Sun.COM return (NULL);
299210616SSebastien.Roy@Sun.COM }
299310616SSebastien.Roy@Sun.COM newmp->b_wptr += sizeof (iptun_ipv6hdrs_t);
299410616SSebastien.Roy@Sun.COM /* Copy the payload (Starting with the inner IPv6 header). */
299510616SSebastien.Roy@Sun.COM bcopy(mp->b_rptr, newmp->b_wptr, MBLKL(mp));
299610616SSebastien.Roy@Sun.COM newmp->b_wptr += MBLKL(mp);
299710616SSebastien.Roy@Sun.COM newouter6 = (iptun_ipv6hdrs_t *)newmp->b_rptr;
299810616SSebastien.Roy@Sun.COM /* Now copy the outer IPv6 header. */
299910616SSebastien.Roy@Sun.COM bcopy(outer6, &newouter6->it6h_ip6h, sizeof (ip6_t));
300010616SSebastien.Roy@Sun.COM newouter6->it6h_ip6h.ip6_nxt = IPPROTO_DSTOPTS;
300110616SSebastien.Roy@Sun.COM newouter6->it6h_encaplim = iptun_encaplim_init;
300210616SSebastien.Roy@Sun.COM newouter6->it6h_encaplim.iel_destopt.ip6d_nxt = outer6->ip6_nxt;
300310616SSebastien.Roy@Sun.COM newouter6->it6h_encaplim.iel_telopt.ip6ot_encap_limit = limit;
300410616SSebastien.Roy@Sun.COM
300510616SSebastien.Roy@Sun.COM /*
300610616SSebastien.Roy@Sun.COM * The payload length will be set at the end of
300710616SSebastien.Roy@Sun.COM * iptun_out_process_ipv6().
300810616SSebastien.Roy@Sun.COM */
300910616SSebastien.Roy@Sun.COM
301010616SSebastien.Roy@Sun.COM freemsg(mp);
301110616SSebastien.Roy@Sun.COM return (newmp);
301210616SSebastien.Roy@Sun.COM }
301310616SSebastien.Roy@Sun.COM
301410616SSebastien.Roy@Sun.COM /*
301510616SSebastien.Roy@Sun.COM * Process output packets with outer IPv6 headers. Frees mp and bumps stats
301610616SSebastien.Roy@Sun.COM * on error.
301710616SSebastien.Roy@Sun.COM */
301810616SSebastien.Roy@Sun.COM static mblk_t *
iptun_out_process_ipv6(iptun_t * iptun,mblk_t * mp,ip6_t * outer6,ipha_t * inner4,ip6_t * inner6,ip_xmit_attr_t * ixa)301911042SErik.Nordmark@Sun.COM iptun_out_process_ipv6(iptun_t *iptun, mblk_t *mp, ip6_t *outer6,
302011042SErik.Nordmark@Sun.COM ipha_t *inner4, ip6_t *inner6, ip_xmit_attr_t *ixa)
302110616SSebastien.Roy@Sun.COM {
302211042SErik.Nordmark@Sun.COM uint8_t *innerptr = (inner4 != NULL ?
302311042SErik.Nordmark@Sun.COM (uint8_t *)inner4 : (uint8_t *)inner6);
302411042SErik.Nordmark@Sun.COM size_t minmtu = iptun->iptun_typeinfo->iti_minmtu;
302510616SSebastien.Roy@Sun.COM uint8_t *limit, *configlimit;
302610616SSebastien.Roy@Sun.COM uint32_t offset;
302710616SSebastien.Roy@Sun.COM iptun_ipv6hdrs_t *v6hdrs;
302810616SSebastien.Roy@Sun.COM
302910616SSebastien.Roy@Sun.COM if (inner6 != NULL && iptun_find_encaplimit(mp, inner6, &limit)) {
303010616SSebastien.Roy@Sun.COM /*
303110616SSebastien.Roy@Sun.COM * The inner packet is an IPv6 packet which itself contains an
303210616SSebastien.Roy@Sun.COM * encapsulation limit option. The limit variable points to
303310616SSebastien.Roy@Sun.COM * the value in the embedded option. Process the
303410616SSebastien.Roy@Sun.COM * encapsulation limit option as specified in RFC 2473.
303510616SSebastien.Roy@Sun.COM *
303610616SSebastien.Roy@Sun.COM * If limit is 0, then we've exceeded the limit and we need to
303710616SSebastien.Roy@Sun.COM * send back an ICMPv6 parameter problem message.
303810616SSebastien.Roy@Sun.COM *
303910616SSebastien.Roy@Sun.COM * If limit is > 0, then we decrement it by 1 and make sure
304010616SSebastien.Roy@Sun.COM * that the encapsulation limit option in the outer header
304110616SSebastien.Roy@Sun.COM * reflects that (adding an option if one isn't already
304210616SSebastien.Roy@Sun.COM * there).
304310616SSebastien.Roy@Sun.COM */
304410616SSebastien.Roy@Sun.COM ASSERT(limit > mp->b_rptr && limit < mp->b_wptr);
304510616SSebastien.Roy@Sun.COM if (*limit == 0) {
304610616SSebastien.Roy@Sun.COM mp->b_rptr = (uint8_t *)inner6;
304710616SSebastien.Roy@Sun.COM offset = limit - mp->b_rptr;
304810616SSebastien.Roy@Sun.COM iptun_icmp_error_v6(iptun, inner6, mp, ICMP6_PARAM_PROB,
304911042SErik.Nordmark@Sun.COM 0, offset, ixa->ixa_tsl);
305010616SSebastien.Roy@Sun.COM atomic_inc_64(&iptun->iptun_noxmtbuf);
305110616SSebastien.Roy@Sun.COM return (NULL);
305210616SSebastien.Roy@Sun.COM }
305310616SSebastien.Roy@Sun.COM
305410616SSebastien.Roy@Sun.COM /*
305510616SSebastien.Roy@Sun.COM * The outer header requires an encapsulation limit option.
305610616SSebastien.Roy@Sun.COM * If there isn't one already, add one.
305710616SSebastien.Roy@Sun.COM */
305810616SSebastien.Roy@Sun.COM if (iptun->iptun_encaplimit == 0) {
305910616SSebastien.Roy@Sun.COM if ((mp = iptun_insert_encaplimit(iptun, mp, outer6,
306010616SSebastien.Roy@Sun.COM (*limit - 1))) == NULL)
306110616SSebastien.Roy@Sun.COM return (NULL);
306211042SErik.Nordmark@Sun.COM v6hdrs = (iptun_ipv6hdrs_t *)mp->b_rptr;
306310616SSebastien.Roy@Sun.COM } else {
306410616SSebastien.Roy@Sun.COM /*
306510616SSebastien.Roy@Sun.COM * There is an existing encapsulation limit option in
306610616SSebastien.Roy@Sun.COM * the outer header. If the inner encapsulation limit
306710616SSebastien.Roy@Sun.COM * is less than the configured encapsulation limit,
306810616SSebastien.Roy@Sun.COM * update the outer encapsulation limit to reflect
306910616SSebastien.Roy@Sun.COM * this lesser value.
307010616SSebastien.Roy@Sun.COM */
307110616SSebastien.Roy@Sun.COM v6hdrs = (iptun_ipv6hdrs_t *)mp->b_rptr;
307210616SSebastien.Roy@Sun.COM configlimit =
307310616SSebastien.Roy@Sun.COM &v6hdrs->it6h_encaplim.iel_telopt.ip6ot_encap_limit;
307410616SSebastien.Roy@Sun.COM if ((*limit - 1) < *configlimit)
307510616SSebastien.Roy@Sun.COM *configlimit = (*limit - 1);
307610616SSebastien.Roy@Sun.COM }
307711042SErik.Nordmark@Sun.COM ixa->ixa_ip_hdr_length = sizeof (iptun_ipv6hdrs_t);
307811042SErik.Nordmark@Sun.COM ixa->ixa_protocol = v6hdrs->it6h_encaplim.iel_destopt.ip6d_nxt;
307911042SErik.Nordmark@Sun.COM } else {
308011042SErik.Nordmark@Sun.COM ixa->ixa_ip_hdr_length = sizeof (ip6_t);
308111042SErik.Nordmark@Sun.COM ixa->ixa_protocol = outer6->ip6_nxt;
308210616SSebastien.Roy@Sun.COM }
308311042SErik.Nordmark@Sun.COM /*
308411042SErik.Nordmark@Sun.COM * See iptun_output_process_ipv4() why we allow fragmentation for
308511042SErik.Nordmark@Sun.COM * small packets
308611042SErik.Nordmark@Sun.COM */
308711042SErik.Nordmark@Sun.COM if (mp->b_wptr - innerptr <= minmtu)
308811042SErik.Nordmark@Sun.COM ixa->ixa_flags &= ~IXAF_DONTFRAG;
308911042SErik.Nordmark@Sun.COM else if (!(ixa->ixa_flags & IXAF_PMTU_TOO_SMALL))
309011042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_DONTFRAG;
309111042SErik.Nordmark@Sun.COM
309211042SErik.Nordmark@Sun.COM ixa->ixa_pktlen = msgdsize(mp);
309311042SErik.Nordmark@Sun.COM outer6->ip6_plen = htons(ixa->ixa_pktlen - sizeof (ip6_t));
309410616SSebastien.Roy@Sun.COM return (mp);
309510616SSebastien.Roy@Sun.COM }
309610616SSebastien.Roy@Sun.COM
309710616SSebastien.Roy@Sun.COM /*
309810616SSebastien.Roy@Sun.COM * The IP tunneling MAC-type plugins have already done most of the header
309910616SSebastien.Roy@Sun.COM * processing and validity checks. We are simply responsible for multiplexing
310010616SSebastien.Roy@Sun.COM * down to the ip module below us.
310110616SSebastien.Roy@Sun.COM */
310210616SSebastien.Roy@Sun.COM static void
iptun_output(iptun_t * iptun,mblk_t * mp)310310616SSebastien.Roy@Sun.COM iptun_output(iptun_t *iptun, mblk_t *mp)
310410616SSebastien.Roy@Sun.COM {
310510616SSebastien.Roy@Sun.COM conn_t *connp = iptun->iptun_connp;
310610616SSebastien.Roy@Sun.COM mblk_t *newmp;
310711042SErik.Nordmark@Sun.COM int error;
310811042SErik.Nordmark@Sun.COM ip_xmit_attr_t *ixa;
310910616SSebastien.Roy@Sun.COM
311010616SSebastien.Roy@Sun.COM ASSERT(mp->b_datap->db_type == M_DATA);
311110616SSebastien.Roy@Sun.COM
311210616SSebastien.Roy@Sun.COM if (mp->b_cont != NULL) {
311310616SSebastien.Roy@Sun.COM if ((newmp = msgpullup(mp, -1)) == NULL) {
311410616SSebastien.Roy@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_noxmtbuf);
311510616SSebastien.Roy@Sun.COM return;
311610616SSebastien.Roy@Sun.COM }
311710616SSebastien.Roy@Sun.COM freemsg(mp);
311810616SSebastien.Roy@Sun.COM mp = newmp;
311910616SSebastien.Roy@Sun.COM }
312010616SSebastien.Roy@Sun.COM
312111042SErik.Nordmark@Sun.COM if (iptun->iptun_typeinfo->iti_type == IPTUN_TYPE_6TO4) {
312211042SErik.Nordmark@Sun.COM iptun_output_6to4(iptun, mp);
312311042SErik.Nordmark@Sun.COM return;
312411042SErik.Nordmark@Sun.COM }
312511042SErik.Nordmark@Sun.COM
312611042SErik.Nordmark@Sun.COM if (is_system_labeled()) {
312711042SErik.Nordmark@Sun.COM /*
312811042SErik.Nordmark@Sun.COM * Since the label can be different meaning a potentially
312911042SErik.Nordmark@Sun.COM * different IRE,we always use a unique ip_xmit_attr_t.
313011042SErik.Nordmark@Sun.COM */
313111042SErik.Nordmark@Sun.COM ixa = conn_get_ixa_exclusive(connp);
313211042SErik.Nordmark@Sun.COM } else {
313311042SErik.Nordmark@Sun.COM /*
313411042SErik.Nordmark@Sun.COM * If no other thread is using conn_ixa this just gets a
313511042SErik.Nordmark@Sun.COM * reference to conn_ixa. Otherwise we get a safe copy of
313611042SErik.Nordmark@Sun.COM * conn_ixa.
313711042SErik.Nordmark@Sun.COM */
313811042SErik.Nordmark@Sun.COM ixa = conn_get_ixa(connp, B_FALSE);
313911042SErik.Nordmark@Sun.COM }
314011042SErik.Nordmark@Sun.COM if (ixa == NULL) {
314111042SErik.Nordmark@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_oerrors);
314211042SErik.Nordmark@Sun.COM return;
314311042SErik.Nordmark@Sun.COM }
314411042SErik.Nordmark@Sun.COM
314511042SErik.Nordmark@Sun.COM /*
314611042SErik.Nordmark@Sun.COM * In case we got a safe copy of conn_ixa, then we need
314711042SErik.Nordmark@Sun.COM * to fill in any pointers in it.
314811042SErik.Nordmark@Sun.COM */
314911042SErik.Nordmark@Sun.COM if (ixa->ixa_ire == NULL) {
315011042SErik.Nordmark@Sun.COM error = ip_attr_connect(connp, ixa, &connp->conn_saddr_v6,
315111042SErik.Nordmark@Sun.COM &connp->conn_faddr_v6, &connp->conn_faddr_v6, 0,
315211042SErik.Nordmark@Sun.COM NULL, NULL, 0);
315311042SErik.Nordmark@Sun.COM if (error != 0) {
315411042SErik.Nordmark@Sun.COM if (ixa->ixa_ire != NULL &&
315511042SErik.Nordmark@Sun.COM (error == EHOSTUNREACH || error == ENETUNREACH)) {
315611042SErik.Nordmark@Sun.COM /*
315711042SErik.Nordmark@Sun.COM * Let conn_ip_output/ire_send_noroute return
315811042SErik.Nordmark@Sun.COM * the error and send any local ICMP error.
315911042SErik.Nordmark@Sun.COM */
316011042SErik.Nordmark@Sun.COM error = 0;
316111042SErik.Nordmark@Sun.COM } else {
316211042SErik.Nordmark@Sun.COM ixa_refrele(ixa);
316311042SErik.Nordmark@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_oerrors);
316411042SErik.Nordmark@Sun.COM return;
316511042SErik.Nordmark@Sun.COM }
316611042SErik.Nordmark@Sun.COM }
316711042SErik.Nordmark@Sun.COM }
316811042SErik.Nordmark@Sun.COM
316911042SErik.Nordmark@Sun.COM iptun_output_common(iptun, ixa, mp);
317011042SErik.Nordmark@Sun.COM ixa_refrele(ixa);
317111042SErik.Nordmark@Sun.COM }
317211042SErik.Nordmark@Sun.COM
317311042SErik.Nordmark@Sun.COM /*
317411042SErik.Nordmark@Sun.COM * We use an ixa based on the last destination.
317511042SErik.Nordmark@Sun.COM */
317611042SErik.Nordmark@Sun.COM static void
iptun_output_6to4(iptun_t * iptun,mblk_t * mp)317711042SErik.Nordmark@Sun.COM iptun_output_6to4(iptun_t *iptun, mblk_t *mp)
317811042SErik.Nordmark@Sun.COM {
317911042SErik.Nordmark@Sun.COM conn_t *connp = iptun->iptun_connp;
318011042SErik.Nordmark@Sun.COM ipha_t *outer4, *inner4;
318111042SErik.Nordmark@Sun.COM ip6_t *outer6, *inner6;
318211042SErik.Nordmark@Sun.COM ip_xmit_attr_t *ixa;
318311042SErik.Nordmark@Sun.COM ip_xmit_attr_t *oldixa;
318411042SErik.Nordmark@Sun.COM int error;
318511042SErik.Nordmark@Sun.COM boolean_t need_connect;
318611042SErik.Nordmark@Sun.COM in6_addr_t v6dst;
318711042SErik.Nordmark@Sun.COM
318811042SErik.Nordmark@Sun.COM ASSERT(mp->b_cont == NULL); /* Verified by iptun_output */
318911042SErik.Nordmark@Sun.COM
319011042SErik.Nordmark@Sun.COM /* Make sure we set ipha_dst before we look at ipha_dst */
319111042SErik.Nordmark@Sun.COM
319211042SErik.Nordmark@Sun.COM (void) iptun_find_headers(mp, 0, &outer4, &inner4, &outer6, &inner6);
319311042SErik.Nordmark@Sun.COM ASSERT(outer4 != NULL);
319411042SErik.Nordmark@Sun.COM if (!iptun_out_process_6to4(iptun, outer4, inner6)) {
319511042SErik.Nordmark@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_oerrors);
319611042SErik.Nordmark@Sun.COM return;
319711042SErik.Nordmark@Sun.COM }
319811042SErik.Nordmark@Sun.COM
319911042SErik.Nordmark@Sun.COM if (is_system_labeled()) {
320011042SErik.Nordmark@Sun.COM /*
320111042SErik.Nordmark@Sun.COM * Since the label can be different meaning a potentially
320211042SErik.Nordmark@Sun.COM * different IRE,we always use a unique ip_xmit_attr_t.
320311042SErik.Nordmark@Sun.COM */
320411042SErik.Nordmark@Sun.COM ixa = conn_get_ixa_exclusive(connp);
320511042SErik.Nordmark@Sun.COM } else {
320611042SErik.Nordmark@Sun.COM /*
320711042SErik.Nordmark@Sun.COM * If no other thread is using conn_ixa this just gets a
320811042SErik.Nordmark@Sun.COM * reference to conn_ixa. Otherwise we get a safe copy of
320911042SErik.Nordmark@Sun.COM * conn_ixa.
321011042SErik.Nordmark@Sun.COM */
321111042SErik.Nordmark@Sun.COM ixa = conn_get_ixa(connp, B_FALSE);
321211042SErik.Nordmark@Sun.COM }
321311042SErik.Nordmark@Sun.COM if (ixa == NULL) {
321411042SErik.Nordmark@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_oerrors);
321511042SErik.Nordmark@Sun.COM return;
321611042SErik.Nordmark@Sun.COM }
321711042SErik.Nordmark@Sun.COM
321811042SErik.Nordmark@Sun.COM mutex_enter(&connp->conn_lock);
321911042SErik.Nordmark@Sun.COM if (connp->conn_v4lastdst == outer4->ipha_dst) {
322011042SErik.Nordmark@Sun.COM need_connect = (ixa->ixa_ire == NULL);
322111042SErik.Nordmark@Sun.COM } else {
322211042SErik.Nordmark@Sun.COM /* In case previous destination was multirt */
322311042SErik.Nordmark@Sun.COM ip_attr_newdst(ixa);
322411042SErik.Nordmark@Sun.COM
322511042SErik.Nordmark@Sun.COM /*
322611042SErik.Nordmark@Sun.COM * We later update conn_ixa when we update conn_v4lastdst
322711042SErik.Nordmark@Sun.COM * which enables subsequent packets to avoid redoing
322811042SErik.Nordmark@Sun.COM * ip_attr_connect
322911042SErik.Nordmark@Sun.COM */
323011042SErik.Nordmark@Sun.COM need_connect = B_TRUE;
323111042SErik.Nordmark@Sun.COM }
323211042SErik.Nordmark@Sun.COM mutex_exit(&connp->conn_lock);
323311042SErik.Nordmark@Sun.COM
323411042SErik.Nordmark@Sun.COM /*
323511042SErik.Nordmark@Sun.COM * In case we got a safe copy of conn_ixa, or otherwise we don't
323611042SErik.Nordmark@Sun.COM * have a current ixa_ire, then we need to fill in any pointers in
323711042SErik.Nordmark@Sun.COM * the ixa.
323811042SErik.Nordmark@Sun.COM */
323911042SErik.Nordmark@Sun.COM if (need_connect) {
324011042SErik.Nordmark@Sun.COM IN6_IPADDR_TO_V4MAPPED(outer4->ipha_dst, &v6dst);
324111042SErik.Nordmark@Sun.COM
324211042SErik.Nordmark@Sun.COM /* We handle IPsec in iptun_output_common */
324311042SErik.Nordmark@Sun.COM error = ip_attr_connect(connp, ixa, &connp->conn_saddr_v6,
324411042SErik.Nordmark@Sun.COM &v6dst, &v6dst, 0, NULL, NULL, 0);
324511042SErik.Nordmark@Sun.COM if (error != 0) {
324611042SErik.Nordmark@Sun.COM if (ixa->ixa_ire != NULL &&
324711042SErik.Nordmark@Sun.COM (error == EHOSTUNREACH || error == ENETUNREACH)) {
324811042SErik.Nordmark@Sun.COM /*
324911042SErik.Nordmark@Sun.COM * Let conn_ip_output/ire_send_noroute return
325011042SErik.Nordmark@Sun.COM * the error and send any local ICMP error.
325111042SErik.Nordmark@Sun.COM */
325211042SErik.Nordmark@Sun.COM error = 0;
325311042SErik.Nordmark@Sun.COM } else {
325411042SErik.Nordmark@Sun.COM ixa_refrele(ixa);
325511042SErik.Nordmark@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_oerrors);
325611042SErik.Nordmark@Sun.COM return;
325711042SErik.Nordmark@Sun.COM }
325811042SErik.Nordmark@Sun.COM }
325911042SErik.Nordmark@Sun.COM }
326011042SErik.Nordmark@Sun.COM
326111042SErik.Nordmark@Sun.COM iptun_output_common(iptun, ixa, mp);
326211042SErik.Nordmark@Sun.COM
326311042SErik.Nordmark@Sun.COM /* Atomically replace conn_ixa and conn_v4lastdst */
326411042SErik.Nordmark@Sun.COM mutex_enter(&connp->conn_lock);
326511042SErik.Nordmark@Sun.COM if (connp->conn_v4lastdst != outer4->ipha_dst) {
326611042SErik.Nordmark@Sun.COM /* Remember the dst which corresponds to conn_ixa */
326711042SErik.Nordmark@Sun.COM connp->conn_v6lastdst = v6dst;
326811042SErik.Nordmark@Sun.COM oldixa = conn_replace_ixa(connp, ixa);
326911042SErik.Nordmark@Sun.COM } else {
327011042SErik.Nordmark@Sun.COM oldixa = NULL;
327111042SErik.Nordmark@Sun.COM }
327211042SErik.Nordmark@Sun.COM mutex_exit(&connp->conn_lock);
327311042SErik.Nordmark@Sun.COM ixa_refrele(ixa);
327411042SErik.Nordmark@Sun.COM if (oldixa != NULL)
327511042SErik.Nordmark@Sun.COM ixa_refrele(oldixa);
327611042SErik.Nordmark@Sun.COM }
327711042SErik.Nordmark@Sun.COM
327811042SErik.Nordmark@Sun.COM /*
327911042SErik.Nordmark@Sun.COM * Check the destination/label. Modifies *mpp by adding/removing CIPSO.
328011042SErik.Nordmark@Sun.COM *
328111042SErik.Nordmark@Sun.COM * We get the label from the message in order to honor the
328211042SErik.Nordmark@Sun.COM * ULPs/IPs choice of label. This will be NULL for forwarded
328311042SErik.Nordmark@Sun.COM * packets, neighbor discovery packets and some others.
328411042SErik.Nordmark@Sun.COM */
328511042SErik.Nordmark@Sun.COM static int
iptun_output_check_label(mblk_t ** mpp,ip_xmit_attr_t * ixa)328611042SErik.Nordmark@Sun.COM iptun_output_check_label(mblk_t **mpp, ip_xmit_attr_t *ixa)
328711042SErik.Nordmark@Sun.COM {
328811042SErik.Nordmark@Sun.COM cred_t *cr;
328911042SErik.Nordmark@Sun.COM int adjust;
329011042SErik.Nordmark@Sun.COM int iplen;
329111042SErik.Nordmark@Sun.COM int err;
329211042SErik.Nordmark@Sun.COM ts_label_t *effective_tsl = NULL;
329311042SErik.Nordmark@Sun.COM
329411042SErik.Nordmark@Sun.COM
329511042SErik.Nordmark@Sun.COM ASSERT(is_system_labeled());
329611042SErik.Nordmark@Sun.COM
329711042SErik.Nordmark@Sun.COM cr = msg_getcred(*mpp, NULL);
329811042SErik.Nordmark@Sun.COM if (cr == NULL)
329911042SErik.Nordmark@Sun.COM return (0);
330011042SErik.Nordmark@Sun.COM
330111042SErik.Nordmark@Sun.COM /*
330211042SErik.Nordmark@Sun.COM * We need to start with a label based on the IP/ULP above us
330311042SErik.Nordmark@Sun.COM */
330411042SErik.Nordmark@Sun.COM ip_xmit_attr_restore_tsl(ixa, cr);
330511042SErik.Nordmark@Sun.COM
330611042SErik.Nordmark@Sun.COM /*
330711042SErik.Nordmark@Sun.COM * Need to update packet with any CIPSO option since
330811042SErik.Nordmark@Sun.COM * conn_ip_output doesn't do that.
330911042SErik.Nordmark@Sun.COM */
331011042SErik.Nordmark@Sun.COM if (ixa->ixa_flags & IXAF_IS_IPV4) {
331111042SErik.Nordmark@Sun.COM ipha_t *ipha;
331211042SErik.Nordmark@Sun.COM
331311042SErik.Nordmark@Sun.COM ipha = (ipha_t *)(*mpp)->b_rptr;
331411042SErik.Nordmark@Sun.COM iplen = ntohs(ipha->ipha_length);
331511042SErik.Nordmark@Sun.COM err = tsol_check_label_v4(ixa->ixa_tsl,
331611042SErik.Nordmark@Sun.COM ixa->ixa_zoneid, mpp, CONN_MAC_DEFAULT, B_FALSE,
331711042SErik.Nordmark@Sun.COM ixa->ixa_ipst, &effective_tsl);
331811042SErik.Nordmark@Sun.COM if (err != 0)
331911042SErik.Nordmark@Sun.COM return (err);
332011042SErik.Nordmark@Sun.COM
332111042SErik.Nordmark@Sun.COM ipha = (ipha_t *)(*mpp)->b_rptr;
332211042SErik.Nordmark@Sun.COM adjust = (int)ntohs(ipha->ipha_length) - iplen;
332311042SErik.Nordmark@Sun.COM } else {
332411042SErik.Nordmark@Sun.COM ip6_t *ip6h;
332511042SErik.Nordmark@Sun.COM
332611042SErik.Nordmark@Sun.COM ip6h = (ip6_t *)(*mpp)->b_rptr;
332711042SErik.Nordmark@Sun.COM iplen = ntohs(ip6h->ip6_plen);
332811042SErik.Nordmark@Sun.COM
332911042SErik.Nordmark@Sun.COM err = tsol_check_label_v6(ixa->ixa_tsl,
333011042SErik.Nordmark@Sun.COM ixa->ixa_zoneid, mpp, CONN_MAC_DEFAULT, B_FALSE,
333111042SErik.Nordmark@Sun.COM ixa->ixa_ipst, &effective_tsl);
333211042SErik.Nordmark@Sun.COM if (err != 0)
333311042SErik.Nordmark@Sun.COM return (err);
333411042SErik.Nordmark@Sun.COM
333511042SErik.Nordmark@Sun.COM ip6h = (ip6_t *)(*mpp)->b_rptr;
333611042SErik.Nordmark@Sun.COM adjust = (int)ntohs(ip6h->ip6_plen) - iplen;
333711042SErik.Nordmark@Sun.COM }
333811042SErik.Nordmark@Sun.COM
333911042SErik.Nordmark@Sun.COM if (effective_tsl != NULL) {
334011042SErik.Nordmark@Sun.COM /* Update the label */
334111042SErik.Nordmark@Sun.COM ip_xmit_attr_replace_tsl(ixa, effective_tsl);
334211042SErik.Nordmark@Sun.COM }
334311042SErik.Nordmark@Sun.COM ixa->ixa_pktlen += adjust;
334411042SErik.Nordmark@Sun.COM ixa->ixa_ip_hdr_length += adjust;
334511042SErik.Nordmark@Sun.COM return (0);
334611042SErik.Nordmark@Sun.COM }
334711042SErik.Nordmark@Sun.COM
334811042SErik.Nordmark@Sun.COM
334911042SErik.Nordmark@Sun.COM static void
iptun_output_common(iptun_t * iptun,ip_xmit_attr_t * ixa,mblk_t * mp)335011042SErik.Nordmark@Sun.COM iptun_output_common(iptun_t *iptun, ip_xmit_attr_t *ixa, mblk_t *mp)
335111042SErik.Nordmark@Sun.COM {
335211042SErik.Nordmark@Sun.COM ipsec_tun_pol_t *itp = iptun->iptun_itp;
335311042SErik.Nordmark@Sun.COM int outer_hlen;
335411042SErik.Nordmark@Sun.COM mblk_t *newmp;
335511042SErik.Nordmark@Sun.COM ipha_t *outer4, *inner4;
335611042SErik.Nordmark@Sun.COM ip6_t *outer6, *inner6;
335711042SErik.Nordmark@Sun.COM int error;
335811042SErik.Nordmark@Sun.COM boolean_t update_pktlen;
335911042SErik.Nordmark@Sun.COM
336011042SErik.Nordmark@Sun.COM ASSERT(ixa->ixa_ire != NULL);
336111042SErik.Nordmark@Sun.COM
336211042SErik.Nordmark@Sun.COM outer_hlen = iptun_find_headers(mp, 0, &outer4, &inner4, &outer6,
336311042SErik.Nordmark@Sun.COM &inner6);
336410616SSebastien.Roy@Sun.COM if (outer_hlen == 0) {
336510616SSebastien.Roy@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_oerrors);
336610616SSebastien.Roy@Sun.COM return;
336710616SSebastien.Roy@Sun.COM }
336810616SSebastien.Roy@Sun.COM
336911305SPaul.Wernau@Sun.COM /* Save IXAF_DONTFRAG value */
337011305SPaul.Wernau@Sun.COM iaflags_t dontfrag = ixa->ixa_flags & IXAF_DONTFRAG;
337111305SPaul.Wernau@Sun.COM
337210616SSebastien.Roy@Sun.COM /* Perform header processing. */
337311042SErik.Nordmark@Sun.COM if (outer4 != NULL) {
337411042SErik.Nordmark@Sun.COM mp = iptun_out_process_ipv4(iptun, mp, outer4, inner4, inner6,
337511042SErik.Nordmark@Sun.COM ixa);
337611042SErik.Nordmark@Sun.COM } else {
337711042SErik.Nordmark@Sun.COM mp = iptun_out_process_ipv6(iptun, mp, outer6, inner4, inner6,
337811042SErik.Nordmark@Sun.COM ixa);
337911042SErik.Nordmark@Sun.COM }
338010616SSebastien.Roy@Sun.COM if (mp == NULL)
338110616SSebastien.Roy@Sun.COM return;
338210616SSebastien.Roy@Sun.COM
338310616SSebastien.Roy@Sun.COM /*
338410616SSebastien.Roy@Sun.COM * Let's hope the compiler optimizes this with "branch taken".
338510616SSebastien.Roy@Sun.COM */
338610616SSebastien.Roy@Sun.COM if (itp != NULL && (itp->itp_flags & ITPF_P_ACTIVE)) {
338711042SErik.Nordmark@Sun.COM /* This updates the ip_xmit_attr_t */
338811042SErik.Nordmark@Sun.COM mp = ipsec_tun_outbound(mp, iptun, inner4, inner6, outer4,
338911042SErik.Nordmark@Sun.COM outer6, outer_hlen, ixa);
339011042SErik.Nordmark@Sun.COM if (mp == NULL) {
339110616SSebastien.Roy@Sun.COM atomic_inc_64(&iptun->iptun_oerrors);
339210616SSebastien.Roy@Sun.COM return;
339310616SSebastien.Roy@Sun.COM }
339411042SErik.Nordmark@Sun.COM if (is_system_labeled()) {
339511042SErik.Nordmark@Sun.COM /*
339611042SErik.Nordmark@Sun.COM * Might change the packet by adding/removing CIPSO.
339711042SErik.Nordmark@Sun.COM * After this caller inner* and outer* and outer_hlen
339811042SErik.Nordmark@Sun.COM * might be invalid.
339911042SErik.Nordmark@Sun.COM */
340011042SErik.Nordmark@Sun.COM error = iptun_output_check_label(&mp, ixa);
340111042SErik.Nordmark@Sun.COM if (error != 0) {
340211042SErik.Nordmark@Sun.COM ip2dbg(("label check failed (%d)\n", error));
340311042SErik.Nordmark@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_oerrors);
340411042SErik.Nordmark@Sun.COM return;
340511042SErik.Nordmark@Sun.COM }
340611042SErik.Nordmark@Sun.COM }
340711042SErik.Nordmark@Sun.COM
340810616SSebastien.Roy@Sun.COM /*
340910616SSebastien.Roy@Sun.COM * ipsec_tun_outbound() returns a chain of tunneled IP
341010616SSebastien.Roy@Sun.COM * fragments linked with b_next (or a single message if the
341111042SErik.Nordmark@Sun.COM * tunneled packet wasn't a fragment).
341211042SErik.Nordmark@Sun.COM * If fragcache returned a list then we need to update
341311042SErik.Nordmark@Sun.COM * ixa_pktlen for all packets in the list.
341411042SErik.Nordmark@Sun.COM */
341511042SErik.Nordmark@Sun.COM update_pktlen = (mp->b_next != NULL);
341611042SErik.Nordmark@Sun.COM
341711042SErik.Nordmark@Sun.COM /*
341811042SErik.Nordmark@Sun.COM * Otherwise, we're good to go. The ixa has been updated with
341910616SSebastien.Roy@Sun.COM * instructions for outbound IPsec processing.
342010616SSebastien.Roy@Sun.COM */
342110616SSebastien.Roy@Sun.COM for (newmp = mp; newmp != NULL; newmp = mp) {
342211305SPaul.Wernau@Sun.COM size_t minmtu = iptun->iptun_typeinfo->iti_minmtu;
342311305SPaul.Wernau@Sun.COM
342410616SSebastien.Roy@Sun.COM atomic_inc_64(&iptun->iptun_opackets);
342511042SErik.Nordmark@Sun.COM atomic_add_64(&iptun->iptun_obytes, ixa->ixa_pktlen);
342610616SSebastien.Roy@Sun.COM mp = mp->b_next;
342710616SSebastien.Roy@Sun.COM newmp->b_next = NULL;
342811042SErik.Nordmark@Sun.COM
342911305SPaul.Wernau@Sun.COM /*
343011305SPaul.Wernau@Sun.COM * The IXAF_DONTFRAG flag is global, but there is
343111305SPaul.Wernau@Sun.COM * a chain here. Check if we're really already
343211305SPaul.Wernau@Sun.COM * smaller than the minimum allowed MTU and reset here
343311305SPaul.Wernau@Sun.COM * appropriately. Otherwise one small packet can kill
343411305SPaul.Wernau@Sun.COM * the whole chain's path mtu discovery.
343511305SPaul.Wernau@Sun.COM * In addition, update the pktlen to the length of
343611305SPaul.Wernau@Sun.COM * the actual packet being processed.
343711305SPaul.Wernau@Sun.COM */
343811305SPaul.Wernau@Sun.COM if (update_pktlen) {
343911305SPaul.Wernau@Sun.COM ixa->ixa_pktlen = msgdsize(newmp);
344011305SPaul.Wernau@Sun.COM if (ixa->ixa_pktlen <= minmtu)
344111305SPaul.Wernau@Sun.COM ixa->ixa_flags &= ~IXAF_DONTFRAG;
344211305SPaul.Wernau@Sun.COM }
344311042SErik.Nordmark@Sun.COM
344411042SErik.Nordmark@Sun.COM atomic_inc_64(&iptun->iptun_opackets);
344511042SErik.Nordmark@Sun.COM atomic_add_64(&iptun->iptun_obytes, ixa->ixa_pktlen);
344611042SErik.Nordmark@Sun.COM
344711042SErik.Nordmark@Sun.COM error = conn_ip_output(newmp, ixa);
344811305SPaul.Wernau@Sun.COM
344911305SPaul.Wernau@Sun.COM /* Restore IXAF_DONTFRAG value */
345011305SPaul.Wernau@Sun.COM ixa->ixa_flags |= dontfrag;
345111305SPaul.Wernau@Sun.COM
345211042SErik.Nordmark@Sun.COM if (error == EMSGSIZE) {
345311042SErik.Nordmark@Sun.COM /* IPsec policy might have changed */
345411042SErik.Nordmark@Sun.COM (void) iptun_update_mtu(iptun, ixa, 0);
345511042SErik.Nordmark@Sun.COM }
345610616SSebastien.Roy@Sun.COM }
345710616SSebastien.Roy@Sun.COM } else {
345810616SSebastien.Roy@Sun.COM /*
345910616SSebastien.Roy@Sun.COM * The ip module will potentially apply global policy to the
346010616SSebastien.Roy@Sun.COM * packet in its output path if there's no active tunnel
346110616SSebastien.Roy@Sun.COM * policy.
346210616SSebastien.Roy@Sun.COM */
346311042SErik.Nordmark@Sun.COM ASSERT(ixa->ixa_ipsec_policy == NULL);
346411042SErik.Nordmark@Sun.COM mp = ip_output_attach_policy(mp, outer4, outer6, NULL, ixa);
346511042SErik.Nordmark@Sun.COM if (mp == NULL) {
346611042SErik.Nordmark@Sun.COM atomic_inc_64(&iptun->iptun_oerrors);
346711042SErik.Nordmark@Sun.COM return;
346811042SErik.Nordmark@Sun.COM }
346911042SErik.Nordmark@Sun.COM if (is_system_labeled()) {
347011042SErik.Nordmark@Sun.COM /*
347111042SErik.Nordmark@Sun.COM * Might change the packet by adding/removing CIPSO.
347211042SErik.Nordmark@Sun.COM * After this caller inner* and outer* and outer_hlen
347311042SErik.Nordmark@Sun.COM * might be invalid.
347411042SErik.Nordmark@Sun.COM */
347511042SErik.Nordmark@Sun.COM error = iptun_output_check_label(&mp, ixa);
347611042SErik.Nordmark@Sun.COM if (error != 0) {
347711042SErik.Nordmark@Sun.COM ip2dbg(("label check failed (%d)\n", error));
347811042SErik.Nordmark@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_oerrors);
347911042SErik.Nordmark@Sun.COM return;
348011042SErik.Nordmark@Sun.COM }
348111042SErik.Nordmark@Sun.COM }
348211042SErik.Nordmark@Sun.COM
348310616SSebastien.Roy@Sun.COM atomic_inc_64(&iptun->iptun_opackets);
348411042SErik.Nordmark@Sun.COM atomic_add_64(&iptun->iptun_obytes, ixa->ixa_pktlen);
348511042SErik.Nordmark@Sun.COM
348611042SErik.Nordmark@Sun.COM error = conn_ip_output(mp, ixa);
348711042SErik.Nordmark@Sun.COM if (error == EMSGSIZE) {
348811042SErik.Nordmark@Sun.COM /* IPsec policy might have changed */
348911042SErik.Nordmark@Sun.COM (void) iptun_update_mtu(iptun, ixa, 0);
349011042SErik.Nordmark@Sun.COM }
349110616SSebastien.Roy@Sun.COM }
349211042SErik.Nordmark@Sun.COM if (ixa->ixa_flags & IXAF_IPSEC_SECURE)
349311042SErik.Nordmark@Sun.COM ipsec_out_release_refs(ixa);
349410616SSebastien.Roy@Sun.COM }
349510616SSebastien.Roy@Sun.COM
349610616SSebastien.Roy@Sun.COM static mac_callbacks_t iptun_m_callbacks = {
349711878SVenu.Iyer@Sun.COM .mc_callbacks = (MC_SETPROP | MC_GETPROP | MC_PROPINFO),
349810616SSebastien.Roy@Sun.COM .mc_getstat = iptun_m_getstat,
349910616SSebastien.Roy@Sun.COM .mc_start = iptun_m_start,
350010616SSebastien.Roy@Sun.COM .mc_stop = iptun_m_stop,
350110616SSebastien.Roy@Sun.COM .mc_setpromisc = iptun_m_setpromisc,
350210616SSebastien.Roy@Sun.COM .mc_multicst = iptun_m_multicst,
350310616SSebastien.Roy@Sun.COM .mc_unicst = iptun_m_unicst,
350410616SSebastien.Roy@Sun.COM .mc_tx = iptun_m_tx,
350511878SVenu.Iyer@Sun.COM .mc_reserved = NULL,
350610616SSebastien.Roy@Sun.COM .mc_setprop = iptun_m_setprop,
350711878SVenu.Iyer@Sun.COM .mc_getprop = iptun_m_getprop,
350811878SVenu.Iyer@Sun.COM .mc_propinfo = iptun_m_propinfo
350910616SSebastien.Roy@Sun.COM };
3510