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 /* 2210616SSebastien.Roy@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 2310616SSebastien.Roy@Sun.COM * Use is subject to license terms. 2410616SSebastien.Roy@Sun.COM */ 2510616SSebastien.Roy@Sun.COM 2610616SSebastien.Roy@Sun.COM /* 2710616SSebastien.Roy@Sun.COM * iptun - IP Tunneling Driver 2810616SSebastien.Roy@Sun.COM * 2910616SSebastien.Roy@Sun.COM * This module is a GLDv3 driver that implements virtual datalinks over IP 3010616SSebastien.Roy@Sun.COM * (a.k.a, IP tunneling). The datalinks are managed through a dld ioctl 3110616SSebastien.Roy@Sun.COM * interface (see iptun_ctl.c), and registered with GLDv3 using 3210616SSebastien.Roy@Sun.COM * mac_register(). It implements the logic for various forms of IP (IPv4 or 3310616SSebastien.Roy@Sun.COM * IPv6) encapsulation within IP (IPv4 or IPv6) by interacting with the ip 3410616SSebastien.Roy@Sun.COM * module below it. Each virtual IP tunnel datalink has a conn_t associated 3510616SSebastien.Roy@Sun.COM * with it representing the "outer" IP connection. 3610616SSebastien.Roy@Sun.COM * 3710616SSebastien.Roy@Sun.COM * The module implements the following locking semantics: 3810616SSebastien.Roy@Sun.COM * 3910616SSebastien.Roy@Sun.COM * Lookups and deletions in iptun_hash are synchronized using iptun_hash_lock. 4010616SSebastien.Roy@Sun.COM * See comments above iptun_hash_lock for details. 4110616SSebastien.Roy@Sun.COM * 4210616SSebastien.Roy@Sun.COM * No locks are ever held while calling up to GLDv3. The general architecture 4310616SSebastien.Roy@Sun.COM * of GLDv3 requires this, as the mac perimeter (essentially a lock) for a 4410616SSebastien.Roy@Sun.COM * given link will be held while making downcalls (iptun_m_*() callbacks). 4510616SSebastien.Roy@Sun.COM * Because we need to hold locks while handling downcalls, holding these locks 4610616SSebastien.Roy@Sun.COM * while issuing upcalls results in deadlock scenarios. See the block comment 4710616SSebastien.Roy@Sun.COM * above iptun_task_cb() for details on how we safely issue upcalls without 4810616SSebastien.Roy@Sun.COM * holding any locks. 4910616SSebastien.Roy@Sun.COM * 5010616SSebastien.Roy@Sun.COM * The contents of each iptun_t is protected by an iptun_mutex which is held 5110616SSebastien.Roy@Sun.COM * in iptun_enter() (called by iptun_enter_by_linkid()), and exited in 5210616SSebastien.Roy@Sun.COM * iptun_exit(). 5310616SSebastien.Roy@Sun.COM * 5410616SSebastien.Roy@Sun.COM * See comments in iptun_delete() and iptun_free() for details on how the 5510616SSebastien.Roy@Sun.COM * iptun_t is deleted safely. 5610616SSebastien.Roy@Sun.COM */ 5710616SSebastien.Roy@Sun.COM 5810616SSebastien.Roy@Sun.COM #include <sys/types.h> 5910616SSebastien.Roy@Sun.COM #include <sys/kmem.h> 6010616SSebastien.Roy@Sun.COM #include <sys/errno.h> 6110616SSebastien.Roy@Sun.COM #include <sys/modhash.h> 6210616SSebastien.Roy@Sun.COM #include <sys/list.h> 6310616SSebastien.Roy@Sun.COM #include <sys/strsun.h> 6410616SSebastien.Roy@Sun.COM #include <sys/file.h> 6510616SSebastien.Roy@Sun.COM #include <sys/systm.h> 6610616SSebastien.Roy@Sun.COM #include <sys/tihdr.h> 6710616SSebastien.Roy@Sun.COM #include <sys/param.h> 6810616SSebastien.Roy@Sun.COM #include <sys/mac_provider.h> 6910616SSebastien.Roy@Sun.COM #include <sys/mac_ipv4.h> 7010616SSebastien.Roy@Sun.COM #include <sys/mac_ipv6.h> 7110616SSebastien.Roy@Sun.COM #include <sys/mac_6to4.h> 7210616SSebastien.Roy@Sun.COM #include <sys/tsol/tnet.h> 7310616SSebastien.Roy@Sun.COM #include <sys/sunldi.h> 7410616SSebastien.Roy@Sun.COM #include <netinet/in.h> 7510616SSebastien.Roy@Sun.COM #include <netinet/ip6.h> 7610616SSebastien.Roy@Sun.COM #include <inet/ip.h> 7710616SSebastien.Roy@Sun.COM #include <inet/ip_ire.h> 7810616SSebastien.Roy@Sun.COM #include <inet/ipsec_impl.h> 7910616SSebastien.Roy@Sun.COM #include <inet/iptun.h> 8010616SSebastien.Roy@Sun.COM #include "iptun_impl.h" 8110616SSebastien.Roy@Sun.COM 8210616SSebastien.Roy@Sun.COM /* Do the tunnel type and address family match? */ 8310616SSebastien.Roy@Sun.COM #define IPTUN_ADDR_MATCH(iptun_type, family) \ 8410616SSebastien.Roy@Sun.COM ((iptun_type == IPTUN_TYPE_IPV4 && family == AF_INET) || \ 8510616SSebastien.Roy@Sun.COM (iptun_type == IPTUN_TYPE_IPV6 && family == AF_INET6) || \ 8610616SSebastien.Roy@Sun.COM (iptun_type == IPTUN_TYPE_6TO4 && family == AF_INET)) 8710616SSebastien.Roy@Sun.COM 8810616SSebastien.Roy@Sun.COM #define IPTUN_HASH_KEY(key) ((mod_hash_key_t)(uintptr_t)(key)) 8910616SSebastien.Roy@Sun.COM 9010616SSebastien.Roy@Sun.COM #define IPTUNQ_DEV "/dev/iptunq" 9110616SSebastien.Roy@Sun.COM 9210616SSebastien.Roy@Sun.COM #define IPTUN_MIN_IPV4_MTU 576 /* ip.h still uses 68 (!) */ 9310616SSebastien.Roy@Sun.COM #define IPTUN_MIN_IPV6_MTU IPV6_MIN_MTU 9410616SSebastien.Roy@Sun.COM #define IPTUN_MAX_IPV4_MTU (IP_MAXPACKET - sizeof (ipha_t)) 9510616SSebastien.Roy@Sun.COM #define IPTUN_MAX_IPV6_MTU (IP_MAXPACKET - sizeof (ip6_t) - \ 9610616SSebastien.Roy@Sun.COM sizeof (iptun_encaplim_t)) 9710616SSebastien.Roy@Sun.COM 9810616SSebastien.Roy@Sun.COM #define IPTUN_MIN_HOPLIMIT 1 9910616SSebastien.Roy@Sun.COM #define IPTUN_MAX_HOPLIMIT UINT8_MAX 10010616SSebastien.Roy@Sun.COM 10110616SSebastien.Roy@Sun.COM #define IPTUN_MIN_ENCAPLIMIT 0 10210616SSebastien.Roy@Sun.COM #define IPTUN_MAX_ENCAPLIMIT UINT8_MAX 10310616SSebastien.Roy@Sun.COM 10410616SSebastien.Roy@Sun.COM #define IPTUN_IPSEC_REQ_MASK (IPSEC_PREF_REQUIRED | IPSEC_PREF_NEVER) 10510616SSebastien.Roy@Sun.COM 10610616SSebastien.Roy@Sun.COM static iptun_encaplim_t iptun_encaplim_init = { 10710616SSebastien.Roy@Sun.COM { IPPROTO_NONE, 0 }, 10810616SSebastien.Roy@Sun.COM IP6OPT_TUNNEL_LIMIT, 10910616SSebastien.Roy@Sun.COM 1, 11010616SSebastien.Roy@Sun.COM IPTUN_DEFAULT_ENCAPLIMIT, /* filled in with actual value later */ 11110616SSebastien.Roy@Sun.COM IP6OPT_PADN, 11210616SSebastien.Roy@Sun.COM 1, 11310616SSebastien.Roy@Sun.COM 0 11410616SSebastien.Roy@Sun.COM }; 11510616SSebastien.Roy@Sun.COM 11610616SSebastien.Roy@Sun.COM /* Table containing per-iptun-type information. */ 11710616SSebastien.Roy@Sun.COM static iptun_typeinfo_t iptun_type_table[] = { 11810616SSebastien.Roy@Sun.COM { IPTUN_TYPE_IPV4, MAC_PLUGIN_IDENT_IPV4, IPV4_VERSION, ip_output, 11910616SSebastien.Roy@Sun.COM IPTUN_MIN_IPV4_MTU, IPTUN_MAX_IPV4_MTU, B_TRUE }, 12010616SSebastien.Roy@Sun.COM { IPTUN_TYPE_IPV6, MAC_PLUGIN_IDENT_IPV6, IPV6_VERSION, ip_output_v6, 12110616SSebastien.Roy@Sun.COM IPTUN_MIN_IPV6_MTU, IPTUN_MAX_IPV6_MTU, B_TRUE }, 12210616SSebastien.Roy@Sun.COM { IPTUN_TYPE_6TO4, MAC_PLUGIN_IDENT_6TO4, IPV4_VERSION, ip_output, 12310616SSebastien.Roy@Sun.COM IPTUN_MIN_IPV4_MTU, IPTUN_MAX_IPV4_MTU, B_FALSE }, 12410616SSebastien.Roy@Sun.COM { IPTUN_TYPE_UNKNOWN, NULL, 0, NULL, 0, 0, B_FALSE } 12510616SSebastien.Roy@Sun.COM }; 12610616SSebastien.Roy@Sun.COM 12710616SSebastien.Roy@Sun.COM /* 12810616SSebastien.Roy@Sun.COM * iptun_hash is an iptun_t lookup table by link ID protected by 12910616SSebastien.Roy@Sun.COM * iptun_hash_lock. While the hash table's integrity is maintained via 13010616SSebastien.Roy@Sun.COM * internal locking in the mod_hash_*() functions, we need additional locking 13110616SSebastien.Roy@Sun.COM * so that an iptun_t cannot be deleted after a hash lookup has returned an 13210616SSebastien.Roy@Sun.COM * iptun_t and before iptun_lock has been entered. As such, we use 13310616SSebastien.Roy@Sun.COM * iptun_hash_lock when doing lookups and removals from iptun_hash. 13410616SSebastien.Roy@Sun.COM */ 13510616SSebastien.Roy@Sun.COM mod_hash_t *iptun_hash; 13610616SSebastien.Roy@Sun.COM static kmutex_t iptun_hash_lock; 13710616SSebastien.Roy@Sun.COM 13810616SSebastien.Roy@Sun.COM static uint_t iptun_tunnelcount; /* total for all stacks */ 13910616SSebastien.Roy@Sun.COM kmem_cache_t *iptun_cache; 14010616SSebastien.Roy@Sun.COM ddi_taskq_t *iptun_taskq; 14110616SSebastien.Roy@Sun.COM 14210616SSebastien.Roy@Sun.COM typedef enum { 14310616SSebastien.Roy@Sun.COM IPTUN_TASK_PMTU_UPDATE, /* obtain new destination path-MTU */ 14410616SSebastien.Roy@Sun.COM IPTUN_TASK_MTU_UPDATE, /* tell mac about new tunnel link MTU */ 14510616SSebastien.Roy@Sun.COM IPTUN_TASK_LADDR_UPDATE, /* tell mac about new local address */ 14610616SSebastien.Roy@Sun.COM IPTUN_TASK_RADDR_UPDATE, /* tell mac about new remote address */ 14710616SSebastien.Roy@Sun.COM IPTUN_TASK_LINK_UPDATE, /* tell mac about new link state */ 14810616SSebastien.Roy@Sun.COM IPTUN_TASK_PDATA_UPDATE /* tell mac about updated plugin data */ 14910616SSebastien.Roy@Sun.COM } iptun_task_t; 15010616SSebastien.Roy@Sun.COM 15110616SSebastien.Roy@Sun.COM typedef struct iptun_task_data_s { 15210616SSebastien.Roy@Sun.COM iptun_task_t itd_task; 15310616SSebastien.Roy@Sun.COM datalink_id_t itd_linkid; 15410616SSebastien.Roy@Sun.COM } iptun_task_data_t; 15510616SSebastien.Roy@Sun.COM 15610616SSebastien.Roy@Sun.COM static void iptun_task_dispatch(iptun_t *, iptun_task_t); 15710616SSebastien.Roy@Sun.COM static int iptun_enter(iptun_t *); 15810616SSebastien.Roy@Sun.COM static void iptun_exit(iptun_t *); 15910616SSebastien.Roy@Sun.COM static void iptun_headergen(iptun_t *, boolean_t); 16010616SSebastien.Roy@Sun.COM static void iptun_drop_pkt(mblk_t *, uint64_t *); 16110616SSebastien.Roy@Sun.COM static void iptun_input(void *, mblk_t *, void *); 16210616SSebastien.Roy@Sun.COM static void iptun_output(iptun_t *, mblk_t *); 16310616SSebastien.Roy@Sun.COM static uint32_t iptun_get_maxmtu(iptun_t *, uint32_t); 16410616SSebastien.Roy@Sun.COM static uint32_t iptun_update_mtu(iptun_t *, uint32_t); 16510616SSebastien.Roy@Sun.COM static uint32_t iptun_get_dst_pmtu(iptun_t *); 16610616SSebastien.Roy@Sun.COM static int iptun_setladdr(iptun_t *, const struct sockaddr_storage *); 16710616SSebastien.Roy@Sun.COM 16810616SSebastien.Roy@Sun.COM static mac_callbacks_t iptun_m_callbacks; 16910616SSebastien.Roy@Sun.COM 17010616SSebastien.Roy@Sun.COM static int 17110616SSebastien.Roy@Sun.COM iptun_m_getstat(void *arg, uint_t stat, uint64_t *val) 17210616SSebastien.Roy@Sun.COM { 17310616SSebastien.Roy@Sun.COM iptun_t *iptun = arg; 17410616SSebastien.Roy@Sun.COM int err = 0; 17510616SSebastien.Roy@Sun.COM 17610616SSebastien.Roy@Sun.COM switch (stat) { 17710616SSebastien.Roy@Sun.COM case MAC_STAT_IERRORS: 17810616SSebastien.Roy@Sun.COM *val = iptun->iptun_ierrors; 17910616SSebastien.Roy@Sun.COM break; 18010616SSebastien.Roy@Sun.COM case MAC_STAT_OERRORS: 18110616SSebastien.Roy@Sun.COM *val = iptun->iptun_oerrors; 18210616SSebastien.Roy@Sun.COM break; 18310616SSebastien.Roy@Sun.COM case MAC_STAT_RBYTES: 18410616SSebastien.Roy@Sun.COM *val = iptun->iptun_rbytes; 18510616SSebastien.Roy@Sun.COM break; 18610616SSebastien.Roy@Sun.COM case MAC_STAT_IPACKETS: 18710616SSebastien.Roy@Sun.COM *val = iptun->iptun_ipackets; 18810616SSebastien.Roy@Sun.COM break; 18910616SSebastien.Roy@Sun.COM case MAC_STAT_OBYTES: 19010616SSebastien.Roy@Sun.COM *val = iptun->iptun_obytes; 19110616SSebastien.Roy@Sun.COM break; 19210616SSebastien.Roy@Sun.COM case MAC_STAT_OPACKETS: 19310616SSebastien.Roy@Sun.COM *val = iptun->iptun_opackets; 19410616SSebastien.Roy@Sun.COM break; 19510616SSebastien.Roy@Sun.COM case MAC_STAT_NORCVBUF: 19610616SSebastien.Roy@Sun.COM *val = iptun->iptun_norcvbuf; 19710616SSebastien.Roy@Sun.COM break; 19810616SSebastien.Roy@Sun.COM case MAC_STAT_NOXMTBUF: 19910616SSebastien.Roy@Sun.COM *val = iptun->iptun_noxmtbuf; 20010616SSebastien.Roy@Sun.COM break; 20110616SSebastien.Roy@Sun.COM default: 20210616SSebastien.Roy@Sun.COM err = ENOTSUP; 20310616SSebastien.Roy@Sun.COM } 20410616SSebastien.Roy@Sun.COM 20510616SSebastien.Roy@Sun.COM return (err); 20610616SSebastien.Roy@Sun.COM } 20710616SSebastien.Roy@Sun.COM 20810616SSebastien.Roy@Sun.COM static int 20910616SSebastien.Roy@Sun.COM iptun_m_start(void *arg) 21010616SSebastien.Roy@Sun.COM { 21110616SSebastien.Roy@Sun.COM iptun_t *iptun = arg; 21210616SSebastien.Roy@Sun.COM int err; 21310616SSebastien.Roy@Sun.COM 21410616SSebastien.Roy@Sun.COM if ((err = iptun_enter(iptun)) == 0) { 21510616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_MAC_STARTED; 21610616SSebastien.Roy@Sun.COM iptun_task_dispatch(iptun, IPTUN_TASK_LINK_UPDATE); 21710616SSebastien.Roy@Sun.COM iptun_exit(iptun); 21810616SSebastien.Roy@Sun.COM } 21910616SSebastien.Roy@Sun.COM return (err); 22010616SSebastien.Roy@Sun.COM } 22110616SSebastien.Roy@Sun.COM 22210616SSebastien.Roy@Sun.COM static void 22310616SSebastien.Roy@Sun.COM iptun_m_stop(void *arg) 22410616SSebastien.Roy@Sun.COM { 22510616SSebastien.Roy@Sun.COM iptun_t *iptun = arg; 22610616SSebastien.Roy@Sun.COM 22710616SSebastien.Roy@Sun.COM if (iptun_enter(iptun) == 0) { 22810616SSebastien.Roy@Sun.COM iptun->iptun_flags &= ~IPTUN_MAC_STARTED; 22910616SSebastien.Roy@Sun.COM iptun_task_dispatch(iptun, IPTUN_TASK_LINK_UPDATE); 23010616SSebastien.Roy@Sun.COM iptun_exit(iptun); 23110616SSebastien.Roy@Sun.COM } 23210616SSebastien.Roy@Sun.COM } 23310616SSebastien.Roy@Sun.COM 23410616SSebastien.Roy@Sun.COM /* 23510616SSebastien.Roy@Sun.COM * iptun_m_setpromisc() does nothing and always succeeds. This is because a 23610616SSebastien.Roy@Sun.COM * tunnel data-link only ever receives packets that are destined exclusively 23710616SSebastien.Roy@Sun.COM * for the local address of the tunnel. 23810616SSebastien.Roy@Sun.COM */ 23910616SSebastien.Roy@Sun.COM /* ARGSUSED */ 24010616SSebastien.Roy@Sun.COM static int 24110616SSebastien.Roy@Sun.COM iptun_m_setpromisc(void *arg, boolean_t on) 24210616SSebastien.Roy@Sun.COM { 24310616SSebastien.Roy@Sun.COM return (0); 24410616SSebastien.Roy@Sun.COM } 24510616SSebastien.Roy@Sun.COM 24610616SSebastien.Roy@Sun.COM /* ARGSUSED */ 24710616SSebastien.Roy@Sun.COM static int 24810616SSebastien.Roy@Sun.COM iptun_m_multicst(void *arg, boolean_t add, const uint8_t *addrp) 24910616SSebastien.Roy@Sun.COM { 25010616SSebastien.Roy@Sun.COM return (ENOTSUP); 25110616SSebastien.Roy@Sun.COM } 25210616SSebastien.Roy@Sun.COM 25310616SSebastien.Roy@Sun.COM /* 25410616SSebastien.Roy@Sun.COM * iptun_m_unicst() sets the local address. 25510616SSebastien.Roy@Sun.COM */ 25610616SSebastien.Roy@Sun.COM /* ARGSUSED */ 25710616SSebastien.Roy@Sun.COM static int 25810616SSebastien.Roy@Sun.COM iptun_m_unicst(void *arg, const uint8_t *addrp) 25910616SSebastien.Roy@Sun.COM { 26010616SSebastien.Roy@Sun.COM iptun_t *iptun = arg; 26110616SSebastien.Roy@Sun.COM int err; 26210616SSebastien.Roy@Sun.COM struct sockaddr_storage ss; 26310616SSebastien.Roy@Sun.COM struct sockaddr_in *sin; 26410616SSebastien.Roy@Sun.COM struct sockaddr_in6 *sin6; 26510616SSebastien.Roy@Sun.COM 26610616SSebastien.Roy@Sun.COM if ((err = iptun_enter(iptun)) == 0) { 26710616SSebastien.Roy@Sun.COM switch (iptun->iptun_typeinfo->iti_ipvers) { 26810616SSebastien.Roy@Sun.COM case IPV4_VERSION: 26910616SSebastien.Roy@Sun.COM sin = (struct sockaddr_in *)&ss; 27010616SSebastien.Roy@Sun.COM sin->sin_family = AF_INET; 27110616SSebastien.Roy@Sun.COM bcopy(addrp, &sin->sin_addr, sizeof (in_addr_t)); 27210616SSebastien.Roy@Sun.COM break; 27310616SSebastien.Roy@Sun.COM case IPV6_VERSION: 27410616SSebastien.Roy@Sun.COM sin6 = (struct sockaddr_in6 *)&ss; 27510616SSebastien.Roy@Sun.COM sin6->sin6_family = AF_INET6; 27610616SSebastien.Roy@Sun.COM bcopy(addrp, &sin6->sin6_addr, sizeof (in6_addr_t)); 27710616SSebastien.Roy@Sun.COM break; 27810616SSebastien.Roy@Sun.COM default: 27910616SSebastien.Roy@Sun.COM ASSERT(0); 28010616SSebastien.Roy@Sun.COM } 28110616SSebastien.Roy@Sun.COM err = iptun_setladdr(iptun, &ss); 28210616SSebastien.Roy@Sun.COM iptun_exit(iptun); 28310616SSebastien.Roy@Sun.COM } 28410616SSebastien.Roy@Sun.COM return (err); 28510616SSebastien.Roy@Sun.COM } 28610616SSebastien.Roy@Sun.COM 28710616SSebastien.Roy@Sun.COM static mblk_t * 28810616SSebastien.Roy@Sun.COM iptun_m_tx(void *arg, mblk_t *mpchain) 28910616SSebastien.Roy@Sun.COM { 29010616SSebastien.Roy@Sun.COM mblk_t *mp, *nmp; 29110616SSebastien.Roy@Sun.COM iptun_t *iptun = arg; 29210616SSebastien.Roy@Sun.COM 29310616SSebastien.Roy@Sun.COM if (!IS_IPTUN_RUNNING(iptun)) { 29410616SSebastien.Roy@Sun.COM iptun_drop_pkt(mpchain, &iptun->iptun_noxmtbuf); 29510616SSebastien.Roy@Sun.COM return (NULL); 29610616SSebastien.Roy@Sun.COM } 29710616SSebastien.Roy@Sun.COM 29810616SSebastien.Roy@Sun.COM /* 29910616SSebastien.Roy@Sun.COM * Request the destination's path MTU information regularly in case 30010616SSebastien.Roy@Sun.COM * path MTU has increased. 30110616SSebastien.Roy@Sun.COM */ 30210616SSebastien.Roy@Sun.COM if (IPTUN_PMTU_TOO_OLD(iptun)) 30310616SSebastien.Roy@Sun.COM iptun_task_dispatch(iptun, IPTUN_TASK_PMTU_UPDATE); 30410616SSebastien.Roy@Sun.COM 30510616SSebastien.Roy@Sun.COM for (mp = mpchain; mp != NULL; mp = nmp) { 30610616SSebastien.Roy@Sun.COM nmp = mp->b_next; 30710616SSebastien.Roy@Sun.COM mp->b_next = NULL; 30810616SSebastien.Roy@Sun.COM iptun_output(iptun, mp); 30910616SSebastien.Roy@Sun.COM } 31010616SSebastien.Roy@Sun.COM 31110616SSebastien.Roy@Sun.COM return (NULL); 31210616SSebastien.Roy@Sun.COM } 31310616SSebastien.Roy@Sun.COM 31410616SSebastien.Roy@Sun.COM /* ARGSUSED */ 31510616SSebastien.Roy@Sun.COM static int 31610616SSebastien.Roy@Sun.COM iptun_m_setprop(void *barg, const char *pr_name, mac_prop_id_t pr_num, 31710616SSebastien.Roy@Sun.COM uint_t pr_valsize, const void *pr_val) 31810616SSebastien.Roy@Sun.COM { 31910616SSebastien.Roy@Sun.COM iptun_t *iptun = barg; 32010616SSebastien.Roy@Sun.COM uint32_t value = *(uint32_t *)pr_val; 32110616SSebastien.Roy@Sun.COM int err; 32210616SSebastien.Roy@Sun.COM 32310616SSebastien.Roy@Sun.COM /* 32410616SSebastien.Roy@Sun.COM * We need to enter this iptun_t since we'll be modifying the outer 32510616SSebastien.Roy@Sun.COM * header. 32610616SSebastien.Roy@Sun.COM */ 32710616SSebastien.Roy@Sun.COM if ((err = iptun_enter(iptun)) != 0) 32810616SSebastien.Roy@Sun.COM return (err); 32910616SSebastien.Roy@Sun.COM 33010616SSebastien.Roy@Sun.COM switch (pr_num) { 33110616SSebastien.Roy@Sun.COM case MAC_PROP_IPTUN_HOPLIMIT: 33210616SSebastien.Roy@Sun.COM if (value < IPTUN_MIN_HOPLIMIT || value > IPTUN_MAX_HOPLIMIT) { 33310616SSebastien.Roy@Sun.COM err = EINVAL; 33410616SSebastien.Roy@Sun.COM break; 33510616SSebastien.Roy@Sun.COM } 33610616SSebastien.Roy@Sun.COM if (value != iptun->iptun_hoplimit) { 33710616SSebastien.Roy@Sun.COM iptun->iptun_hoplimit = (uint8_t)value; 33810616SSebastien.Roy@Sun.COM iptun_headergen(iptun, B_TRUE); 33910616SSebastien.Roy@Sun.COM } 34010616SSebastien.Roy@Sun.COM break; 34110616SSebastien.Roy@Sun.COM case MAC_PROP_IPTUN_ENCAPLIMIT: 34210616SSebastien.Roy@Sun.COM if (iptun->iptun_typeinfo->iti_type != IPTUN_TYPE_IPV6 || 34310616SSebastien.Roy@Sun.COM value > IPTUN_MAX_ENCAPLIMIT) { 34410616SSebastien.Roy@Sun.COM err = EINVAL; 34510616SSebastien.Roy@Sun.COM break; 34610616SSebastien.Roy@Sun.COM } 34710616SSebastien.Roy@Sun.COM if (value != iptun->iptun_encaplimit) { 34810616SSebastien.Roy@Sun.COM iptun->iptun_encaplimit = (uint8_t)value; 34910616SSebastien.Roy@Sun.COM iptun_headergen(iptun, B_TRUE); 35010616SSebastien.Roy@Sun.COM } 35110616SSebastien.Roy@Sun.COM break; 35210616SSebastien.Roy@Sun.COM case MAC_PROP_MTU: { 35310616SSebastien.Roy@Sun.COM uint32_t maxmtu = iptun_get_maxmtu(iptun, 0); 35410616SSebastien.Roy@Sun.COM 35510616SSebastien.Roy@Sun.COM if (value < iptun->iptun_typeinfo->iti_minmtu || 35610616SSebastien.Roy@Sun.COM value > maxmtu) { 35710616SSebastien.Roy@Sun.COM err = EINVAL; 35810616SSebastien.Roy@Sun.COM break; 35910616SSebastien.Roy@Sun.COM } 36010616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_FIXED_MTU; 36110616SSebastien.Roy@Sun.COM if (value != iptun->iptun_mtu) { 36210616SSebastien.Roy@Sun.COM iptun->iptun_mtu = value; 36310616SSebastien.Roy@Sun.COM iptun_task_dispatch(iptun, IPTUN_TASK_MTU_UPDATE); 36410616SSebastien.Roy@Sun.COM } 36510616SSebastien.Roy@Sun.COM break; 36610616SSebastien.Roy@Sun.COM } 36710616SSebastien.Roy@Sun.COM default: 36810616SSebastien.Roy@Sun.COM err = EINVAL; 36910616SSebastien.Roy@Sun.COM } 37010616SSebastien.Roy@Sun.COM iptun_exit(iptun); 37110616SSebastien.Roy@Sun.COM return (err); 37210616SSebastien.Roy@Sun.COM } 37310616SSebastien.Roy@Sun.COM 37410616SSebastien.Roy@Sun.COM /* ARGSUSED */ 37510616SSebastien.Roy@Sun.COM static int 37610616SSebastien.Roy@Sun.COM iptun_m_getprop(void *barg, const char *pr_name, mac_prop_id_t pr_num, 37710616SSebastien.Roy@Sun.COM uint_t pr_flags, uint_t pr_valsize, void *pr_val, uint_t *perm) 37810616SSebastien.Roy@Sun.COM { 37910616SSebastien.Roy@Sun.COM iptun_t *iptun = barg; 38010616SSebastien.Roy@Sun.COM mac_propval_range_t range; 38110616SSebastien.Roy@Sun.COM boolean_t is_default = (pr_flags & MAC_PROP_DEFAULT); 38210616SSebastien.Roy@Sun.COM boolean_t is_possible = (pr_flags & MAC_PROP_POSSIBLE); 38310616SSebastien.Roy@Sun.COM int err; 38410616SSebastien.Roy@Sun.COM 38510616SSebastien.Roy@Sun.COM if ((err = iptun_enter(iptun)) != 0) 38610616SSebastien.Roy@Sun.COM return (err); 38710616SSebastien.Roy@Sun.COM 38810616SSebastien.Roy@Sun.COM if ((pr_flags & ~(MAC_PROP_DEFAULT | MAC_PROP_POSSIBLE)) != 0) { 38910616SSebastien.Roy@Sun.COM err = ENOTSUP; 39010616SSebastien.Roy@Sun.COM goto done; 39110616SSebastien.Roy@Sun.COM } 39210616SSebastien.Roy@Sun.COM if (is_default && is_possible) { 39310616SSebastien.Roy@Sun.COM err = EINVAL; 39410616SSebastien.Roy@Sun.COM goto done; 39510616SSebastien.Roy@Sun.COM } 39610616SSebastien.Roy@Sun.COM 39710616SSebastien.Roy@Sun.COM *perm = MAC_PROP_PERM_RW; 39810616SSebastien.Roy@Sun.COM 39910616SSebastien.Roy@Sun.COM if (is_possible) { 40010616SSebastien.Roy@Sun.COM if (pr_valsize < sizeof (mac_propval_range_t)) { 40110616SSebastien.Roy@Sun.COM err = EINVAL; 40210616SSebastien.Roy@Sun.COM goto done; 40310616SSebastien.Roy@Sun.COM } 40410616SSebastien.Roy@Sun.COM range.mpr_count = 1; 40510616SSebastien.Roy@Sun.COM range.mpr_type = MAC_PROPVAL_UINT32; 40610616SSebastien.Roy@Sun.COM } else if (pr_valsize < sizeof (uint32_t)) { 40710616SSebastien.Roy@Sun.COM err = EINVAL; 40810616SSebastien.Roy@Sun.COM goto done; 40910616SSebastien.Roy@Sun.COM } 41010616SSebastien.Roy@Sun.COM 41110616SSebastien.Roy@Sun.COM switch (pr_num) { 41210616SSebastien.Roy@Sun.COM case MAC_PROP_IPTUN_HOPLIMIT: 41310616SSebastien.Roy@Sun.COM if (is_possible) { 41410616SSebastien.Roy@Sun.COM range.range_uint32[0].mpur_min = IPTUN_MIN_HOPLIMIT; 41510616SSebastien.Roy@Sun.COM range.range_uint32[0].mpur_max = IPTUN_MAX_HOPLIMIT; 41610616SSebastien.Roy@Sun.COM } else if (is_default) { 41710616SSebastien.Roy@Sun.COM *(uint32_t *)pr_val = IPTUN_DEFAULT_HOPLIMIT; 41810616SSebastien.Roy@Sun.COM } else { 41910616SSebastien.Roy@Sun.COM *(uint32_t *)pr_val = iptun->iptun_hoplimit; 42010616SSebastien.Roy@Sun.COM } 42110616SSebastien.Roy@Sun.COM break; 42210616SSebastien.Roy@Sun.COM case MAC_PROP_IPTUN_ENCAPLIMIT: 42310616SSebastien.Roy@Sun.COM if (iptun->iptun_typeinfo->iti_type != IPTUN_TYPE_IPV6) { 42410616SSebastien.Roy@Sun.COM err = ENOTSUP; 42510616SSebastien.Roy@Sun.COM goto done; 42610616SSebastien.Roy@Sun.COM } 42710616SSebastien.Roy@Sun.COM if (is_possible) { 42810616SSebastien.Roy@Sun.COM range.range_uint32[0].mpur_min = IPTUN_MIN_ENCAPLIMIT; 42910616SSebastien.Roy@Sun.COM range.range_uint32[0].mpur_max = IPTUN_MAX_ENCAPLIMIT; 43010616SSebastien.Roy@Sun.COM } else if (is_default) { 43110616SSebastien.Roy@Sun.COM *(uint32_t *)pr_val = IPTUN_DEFAULT_ENCAPLIMIT; 43210616SSebastien.Roy@Sun.COM } else { 43310616SSebastien.Roy@Sun.COM *(uint32_t *)pr_val = iptun->iptun_encaplimit; 43410616SSebastien.Roy@Sun.COM } 43510616SSebastien.Roy@Sun.COM break; 43610616SSebastien.Roy@Sun.COM case MAC_PROP_MTU: { 43710616SSebastien.Roy@Sun.COM uint32_t maxmtu = iptun_get_maxmtu(iptun, 0); 43810616SSebastien.Roy@Sun.COM 43910616SSebastien.Roy@Sun.COM if (is_possible) { 44010616SSebastien.Roy@Sun.COM range.range_uint32[0].mpur_min = 44110616SSebastien.Roy@Sun.COM iptun->iptun_typeinfo->iti_minmtu; 44210616SSebastien.Roy@Sun.COM range.range_uint32[0].mpur_max = maxmtu; 44310616SSebastien.Roy@Sun.COM } else { 44410616SSebastien.Roy@Sun.COM /* 44510616SSebastien.Roy@Sun.COM * The MAC module knows the current value and should 44610616SSebastien.Roy@Sun.COM * never call us for it. There is also no default 44710616SSebastien.Roy@Sun.COM * MTU, as by default, it is a dynamic property. 44810616SSebastien.Roy@Sun.COM */ 44910616SSebastien.Roy@Sun.COM err = ENOTSUP; 45010616SSebastien.Roy@Sun.COM goto done; 45110616SSebastien.Roy@Sun.COM } 45210616SSebastien.Roy@Sun.COM break; 45310616SSebastien.Roy@Sun.COM } 45410616SSebastien.Roy@Sun.COM default: 45510616SSebastien.Roy@Sun.COM err = EINVAL; 45610616SSebastien.Roy@Sun.COM goto done; 45710616SSebastien.Roy@Sun.COM } 45810616SSebastien.Roy@Sun.COM if (is_possible) 45910616SSebastien.Roy@Sun.COM bcopy(&range, pr_val, sizeof (range)); 46010616SSebastien.Roy@Sun.COM done: 46110616SSebastien.Roy@Sun.COM iptun_exit(iptun); 46210616SSebastien.Roy@Sun.COM return (err); 46310616SSebastien.Roy@Sun.COM } 46410616SSebastien.Roy@Sun.COM 46510616SSebastien.Roy@Sun.COM uint_t 46610616SSebastien.Roy@Sun.COM iptun_count(void) 46710616SSebastien.Roy@Sun.COM { 46810616SSebastien.Roy@Sun.COM return (iptun_tunnelcount); 46910616SSebastien.Roy@Sun.COM } 47010616SSebastien.Roy@Sun.COM 47110616SSebastien.Roy@Sun.COM /* 47210616SSebastien.Roy@Sun.COM * Enter an iptun_t exclusively. This is essentially just a mutex, but we 47310616SSebastien.Roy@Sun.COM * don't allow iptun_enter() to succeed on a tunnel if it's in the process of 47410616SSebastien.Roy@Sun.COM * being deleted. 47510616SSebastien.Roy@Sun.COM */ 47610616SSebastien.Roy@Sun.COM static int 47710616SSebastien.Roy@Sun.COM iptun_enter(iptun_t *iptun) 47810616SSebastien.Roy@Sun.COM { 47910616SSebastien.Roy@Sun.COM mutex_enter(&iptun->iptun_lock); 48010616SSebastien.Roy@Sun.COM while (iptun->iptun_flags & IPTUN_DELETE_PENDING) 48110616SSebastien.Roy@Sun.COM cv_wait(&iptun->iptun_enter_cv, &iptun->iptun_lock); 48210616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_CONDEMNED) { 48310616SSebastien.Roy@Sun.COM mutex_exit(&iptun->iptun_lock); 48410616SSebastien.Roy@Sun.COM return (ENOENT); 48510616SSebastien.Roy@Sun.COM } 48610616SSebastien.Roy@Sun.COM return (0); 48710616SSebastien.Roy@Sun.COM } 48810616SSebastien.Roy@Sun.COM 48910616SSebastien.Roy@Sun.COM /* 49010616SSebastien.Roy@Sun.COM * Exit the tunnel entered in iptun_enter(). 49110616SSebastien.Roy@Sun.COM */ 49210616SSebastien.Roy@Sun.COM static void 49310616SSebastien.Roy@Sun.COM iptun_exit(iptun_t *iptun) 49410616SSebastien.Roy@Sun.COM { 49510616SSebastien.Roy@Sun.COM mutex_exit(&iptun->iptun_lock); 49610616SSebastien.Roy@Sun.COM } 49710616SSebastien.Roy@Sun.COM 49810616SSebastien.Roy@Sun.COM /* 49910616SSebastien.Roy@Sun.COM * Enter the IP tunnel instance by datalink ID. 50010616SSebastien.Roy@Sun.COM */ 50110616SSebastien.Roy@Sun.COM static int 50210616SSebastien.Roy@Sun.COM iptun_enter_by_linkid(datalink_id_t linkid, iptun_t **iptun) 50310616SSebastien.Roy@Sun.COM { 50410616SSebastien.Roy@Sun.COM int err; 50510616SSebastien.Roy@Sun.COM 50610616SSebastien.Roy@Sun.COM mutex_enter(&iptun_hash_lock); 50710616SSebastien.Roy@Sun.COM if (mod_hash_find(iptun_hash, IPTUN_HASH_KEY(linkid), 50810616SSebastien.Roy@Sun.COM (mod_hash_val_t *)iptun) == 0) 50910616SSebastien.Roy@Sun.COM err = iptun_enter(*iptun); 51010616SSebastien.Roy@Sun.COM else 51110616SSebastien.Roy@Sun.COM err = ENOENT; 51210616SSebastien.Roy@Sun.COM if (err != 0) 51310616SSebastien.Roy@Sun.COM *iptun = NULL; 51410616SSebastien.Roy@Sun.COM mutex_exit(&iptun_hash_lock); 51510616SSebastien.Roy@Sun.COM return (err); 51610616SSebastien.Roy@Sun.COM } 51710616SSebastien.Roy@Sun.COM 51810616SSebastien.Roy@Sun.COM /* 51910616SSebastien.Roy@Sun.COM * Handle tasks that were deferred through the iptun_taskq. These fall into 52010616SSebastien.Roy@Sun.COM * two categories: 52110616SSebastien.Roy@Sun.COM * 52210616SSebastien.Roy@Sun.COM * 1. Tasks that were defered because we didn't want to spend time doing them 52310616SSebastien.Roy@Sun.COM * while in the data path. Only IPTUN_TASK_PMTU_UPDATE falls into this 52410616SSebastien.Roy@Sun.COM * category. 52510616SSebastien.Roy@Sun.COM * 52610616SSebastien.Roy@Sun.COM * 2. Tasks that were defered because they require calling up to the mac 52710616SSebastien.Roy@Sun.COM * module, and we can't call up to the mac module while holding locks. 52810616SSebastien.Roy@Sun.COM * 52910616SSebastien.Roy@Sun.COM * Handling 1 is easy; we just lookup the iptun_t, perform the task, exit the 53010616SSebastien.Roy@Sun.COM * tunnel, and we're done. 53110616SSebastien.Roy@Sun.COM * 53210616SSebastien.Roy@Sun.COM * Handling 2 is tricky to get right without introducing race conditions and 53310616SSebastien.Roy@Sun.COM * deadlocks with the mac module, as we cannot issue an upcall while in the 53410616SSebastien.Roy@Sun.COM * iptun_t. The reason is that upcalls may try and enter the mac perimeter, 53510616SSebastien.Roy@Sun.COM * while iptun callbacks (such as iptun_m_setprop()) called from the mac 53610616SSebastien.Roy@Sun.COM * module will already have the perimeter held, and will then try and enter 53710616SSebastien.Roy@Sun.COM * the iptun_t. You can see the lock ordering problem with this; this will 53810616SSebastien.Roy@Sun.COM * deadlock. 53910616SSebastien.Roy@Sun.COM * 54010616SSebastien.Roy@Sun.COM * The safe way to do this is to enter the iptun_t in question and copy the 54110616SSebastien.Roy@Sun.COM * information we need out of it so that we can exit it and know that the 54210616SSebastien.Roy@Sun.COM * information being passed up to the upcalls won't be subject to modification 54310616SSebastien.Roy@Sun.COM * by other threads. The problem now is that we need to exit it prior to 54410616SSebastien.Roy@Sun.COM * issuing the upcall, but once we do this, a thread could come along and 54510616SSebastien.Roy@Sun.COM * delete the iptun_t and thus the mac handle required to issue the upcall. 54610616SSebastien.Roy@Sun.COM * To prevent this, we set the IPTUN_UPCALL_PENDING flag prior to exiting the 54710616SSebastien.Roy@Sun.COM * iptun_t. This flag is the condition associated with iptun_upcall_cv, which 54810616SSebastien.Roy@Sun.COM * iptun_delete() will cv_wait() on. When the upcall completes, we clear 54910616SSebastien.Roy@Sun.COM * IPTUN_UPCALL_PENDING and cv_signal() any potentially waiting 55010616SSebastien.Roy@Sun.COM * iptun_delete(). We can thus still safely use iptun->iptun_mh after having 55110616SSebastien.Roy@Sun.COM * exited the iptun_t. 55210616SSebastien.Roy@Sun.COM */ 55310616SSebastien.Roy@Sun.COM static void 55410616SSebastien.Roy@Sun.COM iptun_task_cb(void *arg) 55510616SSebastien.Roy@Sun.COM { 55610616SSebastien.Roy@Sun.COM iptun_task_data_t *itd = arg; 55710616SSebastien.Roy@Sun.COM iptun_task_t task = itd->itd_task; 55810616SSebastien.Roy@Sun.COM datalink_id_t linkid = itd->itd_linkid; 55910616SSebastien.Roy@Sun.COM iptun_t *iptun; 56010616SSebastien.Roy@Sun.COM uint32_t mtu; 56110616SSebastien.Roy@Sun.COM iptun_addr_t addr; 56210616SSebastien.Roy@Sun.COM link_state_t linkstate; 56310616SSebastien.Roy@Sun.COM size_t header_size; 56410616SSebastien.Roy@Sun.COM iptun_header_t header; 56510616SSebastien.Roy@Sun.COM 56610616SSebastien.Roy@Sun.COM kmem_free(itd, sizeof (*itd)); 56710616SSebastien.Roy@Sun.COM 56810616SSebastien.Roy@Sun.COM /* 56910616SSebastien.Roy@Sun.COM * Note that if the lookup fails, it's because the tunnel was deleted 57010616SSebastien.Roy@Sun.COM * between the time the task was dispatched and now. That isn't an 57110616SSebastien.Roy@Sun.COM * error. 57210616SSebastien.Roy@Sun.COM */ 57310616SSebastien.Roy@Sun.COM if (iptun_enter_by_linkid(linkid, &iptun) != 0) 57410616SSebastien.Roy@Sun.COM return; 57510616SSebastien.Roy@Sun.COM 57610616SSebastien.Roy@Sun.COM if (task == IPTUN_TASK_PMTU_UPDATE) { 57710616SSebastien.Roy@Sun.COM (void) iptun_update_mtu(iptun, 0); 57810616SSebastien.Roy@Sun.COM iptun_exit(iptun); 57910616SSebastien.Roy@Sun.COM return; 58010616SSebastien.Roy@Sun.COM } 58110616SSebastien.Roy@Sun.COM 58210616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_UPCALL_PENDING; 58310616SSebastien.Roy@Sun.COM 58410616SSebastien.Roy@Sun.COM switch (task) { 58510616SSebastien.Roy@Sun.COM case IPTUN_TASK_MTU_UPDATE: 58610616SSebastien.Roy@Sun.COM mtu = iptun->iptun_mtu; 58710616SSebastien.Roy@Sun.COM break; 58810616SSebastien.Roy@Sun.COM case IPTUN_TASK_LADDR_UPDATE: 58910616SSebastien.Roy@Sun.COM addr = iptun->iptun_laddr; 59010616SSebastien.Roy@Sun.COM break; 59110616SSebastien.Roy@Sun.COM case IPTUN_TASK_RADDR_UPDATE: 59210616SSebastien.Roy@Sun.COM addr = iptun->iptun_raddr; 59310616SSebastien.Roy@Sun.COM break; 59410616SSebastien.Roy@Sun.COM case IPTUN_TASK_LINK_UPDATE: 59510616SSebastien.Roy@Sun.COM linkstate = IS_IPTUN_RUNNING(iptun) ? 59610616SSebastien.Roy@Sun.COM LINK_STATE_UP : LINK_STATE_DOWN; 59710616SSebastien.Roy@Sun.COM break; 59810616SSebastien.Roy@Sun.COM case IPTUN_TASK_PDATA_UPDATE: 59910616SSebastien.Roy@Sun.COM header_size = iptun->iptun_header_size; 60010616SSebastien.Roy@Sun.COM header = iptun->iptun_header; 60110616SSebastien.Roy@Sun.COM break; 60210616SSebastien.Roy@Sun.COM default: 60310616SSebastien.Roy@Sun.COM ASSERT(0); 60410616SSebastien.Roy@Sun.COM } 60510616SSebastien.Roy@Sun.COM 60610616SSebastien.Roy@Sun.COM iptun_exit(iptun); 60710616SSebastien.Roy@Sun.COM 60810616SSebastien.Roy@Sun.COM switch (task) { 60910616SSebastien.Roy@Sun.COM case IPTUN_TASK_MTU_UPDATE: 61010616SSebastien.Roy@Sun.COM (void) mac_maxsdu_update(iptun->iptun_mh, mtu); 61110616SSebastien.Roy@Sun.COM break; 61210616SSebastien.Roy@Sun.COM case IPTUN_TASK_LADDR_UPDATE: 61310616SSebastien.Roy@Sun.COM mac_unicst_update(iptun->iptun_mh, (uint8_t *)&addr.ia_addr); 61410616SSebastien.Roy@Sun.COM break; 61510616SSebastien.Roy@Sun.COM case IPTUN_TASK_RADDR_UPDATE: 61610616SSebastien.Roy@Sun.COM mac_dst_update(iptun->iptun_mh, (uint8_t *)&addr.ia_addr); 61710616SSebastien.Roy@Sun.COM break; 61810616SSebastien.Roy@Sun.COM case IPTUN_TASK_LINK_UPDATE: 61910616SSebastien.Roy@Sun.COM mac_link_update(iptun->iptun_mh, linkstate); 62010616SSebastien.Roy@Sun.COM break; 62110616SSebastien.Roy@Sun.COM case IPTUN_TASK_PDATA_UPDATE: 62210616SSebastien.Roy@Sun.COM if (mac_pdata_update(iptun->iptun_mh, 62310616SSebastien.Roy@Sun.COM header_size == 0 ? NULL : &header, header_size) != 0) 62410616SSebastien.Roy@Sun.COM atomic_inc_64(&iptun->iptun_taskq_fail); 62510616SSebastien.Roy@Sun.COM break; 62610616SSebastien.Roy@Sun.COM } 62710616SSebastien.Roy@Sun.COM 62810616SSebastien.Roy@Sun.COM mutex_enter(&iptun->iptun_lock); 62910616SSebastien.Roy@Sun.COM iptun->iptun_flags &= ~IPTUN_UPCALL_PENDING; 63010616SSebastien.Roy@Sun.COM cv_signal(&iptun->iptun_upcall_cv); 63110616SSebastien.Roy@Sun.COM mutex_exit(&iptun->iptun_lock); 63210616SSebastien.Roy@Sun.COM } 63310616SSebastien.Roy@Sun.COM 63410616SSebastien.Roy@Sun.COM static void 63510616SSebastien.Roy@Sun.COM iptun_task_dispatch(iptun_t *iptun, iptun_task_t iptun_task) 63610616SSebastien.Roy@Sun.COM { 63710616SSebastien.Roy@Sun.COM iptun_task_data_t *itd; 63810616SSebastien.Roy@Sun.COM 63910616SSebastien.Roy@Sun.COM itd = kmem_alloc(sizeof (*itd), KM_NOSLEEP); 64010616SSebastien.Roy@Sun.COM if (itd == NULL) { 64110616SSebastien.Roy@Sun.COM atomic_inc_64(&iptun->iptun_taskq_fail); 64210616SSebastien.Roy@Sun.COM return; 64310616SSebastien.Roy@Sun.COM } 64410616SSebastien.Roy@Sun.COM itd->itd_task = iptun_task; 64510616SSebastien.Roy@Sun.COM itd->itd_linkid = iptun->iptun_linkid; 64610616SSebastien.Roy@Sun.COM if (ddi_taskq_dispatch(iptun_taskq, iptun_task_cb, itd, DDI_NOSLEEP)) { 64710616SSebastien.Roy@Sun.COM atomic_inc_64(&iptun->iptun_taskq_fail); 64810616SSebastien.Roy@Sun.COM kmem_free(itd, sizeof (*itd)); 64910616SSebastien.Roy@Sun.COM } 65010616SSebastien.Roy@Sun.COM } 65110616SSebastien.Roy@Sun.COM 65210616SSebastien.Roy@Sun.COM /* 65310616SSebastien.Roy@Sun.COM * Convert an iptun_addr_t to sockaddr_storage. 65410616SSebastien.Roy@Sun.COM */ 65510616SSebastien.Roy@Sun.COM static void 65610616SSebastien.Roy@Sun.COM iptun_getaddr(iptun_addr_t *iptun_addr, struct sockaddr_storage *ss) 65710616SSebastien.Roy@Sun.COM { 65810616SSebastien.Roy@Sun.COM struct sockaddr_in *sin; 65910616SSebastien.Roy@Sun.COM struct sockaddr_in6 *sin6; 66010616SSebastien.Roy@Sun.COM 66110616SSebastien.Roy@Sun.COM bzero(ss, sizeof (*ss)); 66210616SSebastien.Roy@Sun.COM switch (iptun_addr->ia_family) { 66310616SSebastien.Roy@Sun.COM case AF_INET: 66410616SSebastien.Roy@Sun.COM sin = (struct sockaddr_in *)ss; 66510616SSebastien.Roy@Sun.COM sin->sin_addr.s_addr = iptun_addr->ia_addr.iau_addr4; 66610616SSebastien.Roy@Sun.COM break; 66710616SSebastien.Roy@Sun.COM case AF_INET6: 66810616SSebastien.Roy@Sun.COM sin6 = (struct sockaddr_in6 *)ss; 66910616SSebastien.Roy@Sun.COM sin6->sin6_addr = iptun_addr->ia_addr.iau_addr6; 67010616SSebastien.Roy@Sun.COM break; 67110616SSebastien.Roy@Sun.COM default: 67210616SSebastien.Roy@Sun.COM ASSERT(0); 67310616SSebastien.Roy@Sun.COM } 67410616SSebastien.Roy@Sun.COM ss->ss_family = iptun_addr->ia_family; 67510616SSebastien.Roy@Sun.COM } 67610616SSebastien.Roy@Sun.COM 67710616SSebastien.Roy@Sun.COM /* 67810616SSebastien.Roy@Sun.COM * General purpose function to set an IP tunnel source or destination address. 67910616SSebastien.Roy@Sun.COM */ 68010616SSebastien.Roy@Sun.COM static int 68110616SSebastien.Roy@Sun.COM iptun_setaddr(iptun_type_t iptun_type, iptun_addr_t *iptun_addr, 68210616SSebastien.Roy@Sun.COM const struct sockaddr_storage *ss) 68310616SSebastien.Roy@Sun.COM { 68410616SSebastien.Roy@Sun.COM if (!IPTUN_ADDR_MATCH(iptun_type, ss->ss_family)) 68510616SSebastien.Roy@Sun.COM return (EINVAL); 68610616SSebastien.Roy@Sun.COM 68710616SSebastien.Roy@Sun.COM switch (ss->ss_family) { 68810616SSebastien.Roy@Sun.COM case AF_INET: { 68910616SSebastien.Roy@Sun.COM struct sockaddr_in *sin = (struct sockaddr_in *)ss; 69010616SSebastien.Roy@Sun.COM 69110616SSebastien.Roy@Sun.COM if ((sin->sin_addr.s_addr == INADDR_ANY) || 69210616SSebastien.Roy@Sun.COM (sin->sin_addr.s_addr == INADDR_BROADCAST) || 69310616SSebastien.Roy@Sun.COM CLASSD(sin->sin_addr.s_addr)) { 69410616SSebastien.Roy@Sun.COM return (EADDRNOTAVAIL); 69510616SSebastien.Roy@Sun.COM } 69610616SSebastien.Roy@Sun.COM iptun_addr->ia_addr.iau_addr4 = sin->sin_addr.s_addr; 69710616SSebastien.Roy@Sun.COM break; 69810616SSebastien.Roy@Sun.COM } 69910616SSebastien.Roy@Sun.COM case AF_INET6: { 70010616SSebastien.Roy@Sun.COM struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ss; 70110616SSebastien.Roy@Sun.COM 70210616SSebastien.Roy@Sun.COM if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) || 70310616SSebastien.Roy@Sun.COM IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr) || 70410616SSebastien.Roy@Sun.COM IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { 70510616SSebastien.Roy@Sun.COM return (EADDRNOTAVAIL); 70610616SSebastien.Roy@Sun.COM } 70710616SSebastien.Roy@Sun.COM iptun_addr->ia_addr.iau_addr6 = sin6->sin6_addr; 70810616SSebastien.Roy@Sun.COM break; 70910616SSebastien.Roy@Sun.COM } 71010616SSebastien.Roy@Sun.COM default: 71110616SSebastien.Roy@Sun.COM return (EAFNOSUPPORT); 71210616SSebastien.Roy@Sun.COM } 71310616SSebastien.Roy@Sun.COM iptun_addr->ia_family = ss->ss_family; 71410616SSebastien.Roy@Sun.COM return (0); 71510616SSebastien.Roy@Sun.COM } 71610616SSebastien.Roy@Sun.COM 71710616SSebastien.Roy@Sun.COM static int 71810616SSebastien.Roy@Sun.COM iptun_setladdr(iptun_t *iptun, const struct sockaddr_storage *laddr) 71910616SSebastien.Roy@Sun.COM { 72010616SSebastien.Roy@Sun.COM return (iptun_setaddr(iptun->iptun_typeinfo->iti_type, 72110616SSebastien.Roy@Sun.COM &iptun->iptun_laddr, laddr)); 72210616SSebastien.Roy@Sun.COM } 72310616SSebastien.Roy@Sun.COM 72410616SSebastien.Roy@Sun.COM static int 72510616SSebastien.Roy@Sun.COM iptun_setraddr(iptun_t *iptun, const struct sockaddr_storage *raddr) 72610616SSebastien.Roy@Sun.COM { 72710616SSebastien.Roy@Sun.COM if (!(iptun->iptun_typeinfo->iti_hasraddr)) 72810616SSebastien.Roy@Sun.COM return (EINVAL); 72910616SSebastien.Roy@Sun.COM return (iptun_setaddr(iptun->iptun_typeinfo->iti_type, 73010616SSebastien.Roy@Sun.COM &iptun->iptun_raddr, raddr)); 73110616SSebastien.Roy@Sun.COM } 73210616SSebastien.Roy@Sun.COM 73310616SSebastien.Roy@Sun.COM static boolean_t 73410616SSebastien.Roy@Sun.COM iptun_canbind(iptun_t *iptun) 73510616SSebastien.Roy@Sun.COM { 73610616SSebastien.Roy@Sun.COM /* 73710616SSebastien.Roy@Sun.COM * A tunnel may bind when its source address has been set, and if its 73810616SSebastien.Roy@Sun.COM * tunnel type requires one, also its destination address. 73910616SSebastien.Roy@Sun.COM */ 74010616SSebastien.Roy@Sun.COM return ((iptun->iptun_flags & IPTUN_LADDR) && 74110616SSebastien.Roy@Sun.COM ((iptun->iptun_flags & IPTUN_RADDR) || 74210616SSebastien.Roy@Sun.COM !(iptun->iptun_typeinfo->iti_hasraddr))); 74310616SSebastien.Roy@Sun.COM } 74410616SSebastien.Roy@Sun.COM 74510616SSebastien.Roy@Sun.COM static int 74610616SSebastien.Roy@Sun.COM iptun_bind(iptun_t *iptun) 74710616SSebastien.Roy@Sun.COM { 74810616SSebastien.Roy@Sun.COM conn_t *connp = iptun->iptun_connp; 74910616SSebastien.Roy@Sun.COM int err; 75010616SSebastien.Roy@Sun.COM 75110616SSebastien.Roy@Sun.COM ASSERT(iptun_canbind(iptun)); 75210616SSebastien.Roy@Sun.COM 75310616SSebastien.Roy@Sun.COM switch (iptun->iptun_typeinfo->iti_type) { 75410616SSebastien.Roy@Sun.COM case IPTUN_TYPE_IPV4: 75510616SSebastien.Roy@Sun.COM /* 75610616SSebastien.Roy@Sun.COM * When we set a tunnel's destination address, we do not care 75710616SSebastien.Roy@Sun.COM * if the destination is reachable. Transient routing issues 75810616SSebastien.Roy@Sun.COM * should not inhibit the creation of a tunnel interface, for 75910616SSebastien.Roy@Sun.COM * example. For that reason, we pass in B_FALSE for the 76010616SSebastien.Roy@Sun.COM * verify_dst argument of ip_proto_bind_connected_v4() (and 76110616SSebastien.Roy@Sun.COM * similarly for IPv6 tunnels below). 76210616SSebastien.Roy@Sun.COM */ 76310616SSebastien.Roy@Sun.COM err = ip_proto_bind_connected_v4(connp, NULL, IPPROTO_ENCAP, 76410616SSebastien.Roy@Sun.COM &iptun->iptun_laddr4, 0, iptun->iptun_raddr4, 0, B_TRUE, 76510616SSebastien.Roy@Sun.COM B_FALSE, iptun->iptun_cred); 76610616SSebastien.Roy@Sun.COM break; 76710616SSebastien.Roy@Sun.COM case IPTUN_TYPE_IPV6: 76810616SSebastien.Roy@Sun.COM err = ip_proto_bind_connected_v6(connp, NULL, IPPROTO_IPV6, 76910616SSebastien.Roy@Sun.COM &iptun->iptun_laddr6, 0, &iptun->iptun_raddr6, NULL, 0, 77010616SSebastien.Roy@Sun.COM B_TRUE, B_FALSE, iptun->iptun_cred); 77110616SSebastien.Roy@Sun.COM break; 77210616SSebastien.Roy@Sun.COM case IPTUN_TYPE_6TO4: 77310616SSebastien.Roy@Sun.COM err = ip_proto_bind_laddr_v4(connp, NULL, IPPROTO_IPV6, 77410616SSebastien.Roy@Sun.COM iptun->iptun_laddr4, 0, B_TRUE); 77510616SSebastien.Roy@Sun.COM break; 77610616SSebastien.Roy@Sun.COM } 77710616SSebastien.Roy@Sun.COM 77810616SSebastien.Roy@Sun.COM if (err == 0) { 77910616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_BOUND; 78010616SSebastien.Roy@Sun.COM 78110616SSebastien.Roy@Sun.COM /* 78210616SSebastien.Roy@Sun.COM * Now that we're bound with ip below us, this is a good time 78310616SSebastien.Roy@Sun.COM * to initialize the destination path MTU and to re-calculate 78410616SSebastien.Roy@Sun.COM * the tunnel's link MTU. 78510616SSebastien.Roy@Sun.COM */ 78610616SSebastien.Roy@Sun.COM (void) iptun_update_mtu(iptun, 0); 78710616SSebastien.Roy@Sun.COM 78810616SSebastien.Roy@Sun.COM if (IS_IPTUN_RUNNING(iptun)) 78910616SSebastien.Roy@Sun.COM iptun_task_dispatch(iptun, IPTUN_TASK_LINK_UPDATE); 79010616SSebastien.Roy@Sun.COM } 79110616SSebastien.Roy@Sun.COM return (err); 79210616SSebastien.Roy@Sun.COM } 79310616SSebastien.Roy@Sun.COM 79410616SSebastien.Roy@Sun.COM static void 79510616SSebastien.Roy@Sun.COM iptun_unbind(iptun_t *iptun) 79610616SSebastien.Roy@Sun.COM { 79710616SSebastien.Roy@Sun.COM ASSERT(iptun->iptun_flags & IPTUN_BOUND); 79810616SSebastien.Roy@Sun.COM ASSERT(mutex_owned(&iptun->iptun_lock) || 79910616SSebastien.Roy@Sun.COM (iptun->iptun_flags & IPTUN_CONDEMNED)); 80010616SSebastien.Roy@Sun.COM ip_unbind(iptun->iptun_connp); 80110616SSebastien.Roy@Sun.COM iptun->iptun_flags &= ~IPTUN_BOUND; 80210616SSebastien.Roy@Sun.COM if (!(iptun->iptun_flags & IPTUN_CONDEMNED)) 80310616SSebastien.Roy@Sun.COM iptun_task_dispatch(iptun, IPTUN_TASK_LINK_UPDATE); 80410616SSebastien.Roy@Sun.COM } 80510616SSebastien.Roy@Sun.COM 80610616SSebastien.Roy@Sun.COM /* 80710616SSebastien.Roy@Sun.COM * Re-generate the template data-link header for a given IP tunnel given the 80810616SSebastien.Roy@Sun.COM * tunnel's current parameters. 80910616SSebastien.Roy@Sun.COM */ 81010616SSebastien.Roy@Sun.COM static void 81110616SSebastien.Roy@Sun.COM iptun_headergen(iptun_t *iptun, boolean_t update_mac) 81210616SSebastien.Roy@Sun.COM { 81310616SSebastien.Roy@Sun.COM switch (iptun->iptun_typeinfo->iti_ipvers) { 81410616SSebastien.Roy@Sun.COM case IPV4_VERSION: 81510616SSebastien.Roy@Sun.COM /* 81610616SSebastien.Roy@Sun.COM * We only need to use a custom IP header if the administrator 81710616SSebastien.Roy@Sun.COM * has supplied a non-default hoplimit. 81810616SSebastien.Roy@Sun.COM */ 81910616SSebastien.Roy@Sun.COM if (iptun->iptun_hoplimit == IPTUN_DEFAULT_HOPLIMIT) { 82010616SSebastien.Roy@Sun.COM iptun->iptun_header_size = 0; 82110616SSebastien.Roy@Sun.COM break; 82210616SSebastien.Roy@Sun.COM } 82310616SSebastien.Roy@Sun.COM iptun->iptun_header_size = sizeof (ipha_t); 82410616SSebastien.Roy@Sun.COM iptun->iptun_header4.ipha_version_and_hdr_length = 82510616SSebastien.Roy@Sun.COM IP_SIMPLE_HDR_VERSION; 82610616SSebastien.Roy@Sun.COM iptun->iptun_header4.ipha_fragment_offset_and_flags = 82710616SSebastien.Roy@Sun.COM htons(IPH_DF); 82810616SSebastien.Roy@Sun.COM iptun->iptun_header4.ipha_ttl = iptun->iptun_hoplimit; 82910616SSebastien.Roy@Sun.COM break; 83010616SSebastien.Roy@Sun.COM case IPV6_VERSION: { 83110616SSebastien.Roy@Sun.COM ip6_t *ip6hp = &iptun->iptun_header6.it6h_ip6h; 83210616SSebastien.Roy@Sun.COM 83310616SSebastien.Roy@Sun.COM /* 83410616SSebastien.Roy@Sun.COM * We only need to use a custom IPv6 header if either the 83510616SSebastien.Roy@Sun.COM * administrator has supplied a non-default hoplimit, or we 83610616SSebastien.Roy@Sun.COM * need to include an encapsulation limit option in the outer 83710616SSebastien.Roy@Sun.COM * header. 83810616SSebastien.Roy@Sun.COM */ 83910616SSebastien.Roy@Sun.COM if (iptun->iptun_hoplimit == IPTUN_DEFAULT_HOPLIMIT && 84010616SSebastien.Roy@Sun.COM iptun->iptun_encaplimit == 0) { 84110616SSebastien.Roy@Sun.COM iptun->iptun_header_size = 0; 84210616SSebastien.Roy@Sun.COM break; 84310616SSebastien.Roy@Sun.COM } 84410616SSebastien.Roy@Sun.COM 84510616SSebastien.Roy@Sun.COM (void) memset(ip6hp, 0, sizeof (*ip6hp)); 84610616SSebastien.Roy@Sun.COM if (iptun->iptun_encaplimit == 0) { 84710616SSebastien.Roy@Sun.COM iptun->iptun_header_size = sizeof (ip6_t); 84810616SSebastien.Roy@Sun.COM ip6hp->ip6_nxt = IPPROTO_NONE; 84910616SSebastien.Roy@Sun.COM } else { 85010616SSebastien.Roy@Sun.COM iptun_encaplim_t *iel; 85110616SSebastien.Roy@Sun.COM 85210616SSebastien.Roy@Sun.COM iptun->iptun_header_size = sizeof (iptun_ipv6hdrs_t); 85310616SSebastien.Roy@Sun.COM /* 85410616SSebastien.Roy@Sun.COM * The mac_ipv6 plugin requires ip6_plen to be in host 85510616SSebastien.Roy@Sun.COM * byte order and reflect the extension headers 85610616SSebastien.Roy@Sun.COM * present in the template. The actual network byte 85710616SSebastien.Roy@Sun.COM * order ip6_plen will be set on a per-packet basis on 85810616SSebastien.Roy@Sun.COM * transmit. 85910616SSebastien.Roy@Sun.COM */ 86010616SSebastien.Roy@Sun.COM ip6hp->ip6_plen = sizeof (*iel); 86110616SSebastien.Roy@Sun.COM ip6hp->ip6_nxt = IPPROTO_DSTOPTS; 86210616SSebastien.Roy@Sun.COM iel = &iptun->iptun_header6.it6h_encaplim; 86310616SSebastien.Roy@Sun.COM *iel = iptun_encaplim_init; 86410616SSebastien.Roy@Sun.COM iel->iel_telopt.ip6ot_encap_limit = 86510616SSebastien.Roy@Sun.COM iptun->iptun_encaplimit; 86610616SSebastien.Roy@Sun.COM } 86710616SSebastien.Roy@Sun.COM 86810616SSebastien.Roy@Sun.COM ip6hp->ip6_hlim = iptun->iptun_hoplimit; 86910616SSebastien.Roy@Sun.COM break; 87010616SSebastien.Roy@Sun.COM } 87110616SSebastien.Roy@Sun.COM } 87210616SSebastien.Roy@Sun.COM 87310616SSebastien.Roy@Sun.COM if (update_mac) 87410616SSebastien.Roy@Sun.COM iptun_task_dispatch(iptun, IPTUN_TASK_PDATA_UPDATE); 87510616SSebastien.Roy@Sun.COM } 87610616SSebastien.Roy@Sun.COM 87710616SSebastien.Roy@Sun.COM /* 87810616SSebastien.Roy@Sun.COM * Insert inbound and outbound IPv4 and IPv6 policy into the given policy 87910616SSebastien.Roy@Sun.COM * head. 88010616SSebastien.Roy@Sun.COM */ 88110616SSebastien.Roy@Sun.COM static boolean_t 88210616SSebastien.Roy@Sun.COM iptun_insert_simple_policies(ipsec_policy_head_t *ph, ipsec_act_t *actp, 88310616SSebastien.Roy@Sun.COM uint_t n, netstack_t *ns) 88410616SSebastien.Roy@Sun.COM { 88510616SSebastien.Roy@Sun.COM int f = IPSEC_AF_V4; 88610616SSebastien.Roy@Sun.COM 88710616SSebastien.Roy@Sun.COM if (!ipsec_polhead_insert(ph, actp, n, f, IPSEC_TYPE_INBOUND, ns) || 88810616SSebastien.Roy@Sun.COM !ipsec_polhead_insert(ph, actp, n, f, IPSEC_TYPE_OUTBOUND, ns)) 88910616SSebastien.Roy@Sun.COM return (B_FALSE); 89010616SSebastien.Roy@Sun.COM 89110616SSebastien.Roy@Sun.COM f = IPSEC_AF_V6; 89210616SSebastien.Roy@Sun.COM return (ipsec_polhead_insert(ph, actp, n, f, IPSEC_TYPE_INBOUND, ns) && 89310616SSebastien.Roy@Sun.COM ipsec_polhead_insert(ph, actp, n, f, IPSEC_TYPE_OUTBOUND, ns)); 89410616SSebastien.Roy@Sun.COM } 89510616SSebastien.Roy@Sun.COM 89610616SSebastien.Roy@Sun.COM /* 89710616SSebastien.Roy@Sun.COM * Used to set IPsec policy when policy is set through the IPTUN_CREATE or 89810616SSebastien.Roy@Sun.COM * IPTUN_MODIFY ioctls. 89910616SSebastien.Roy@Sun.COM */ 90010616SSebastien.Roy@Sun.COM static int 90110616SSebastien.Roy@Sun.COM iptun_set_sec_simple(iptun_t *iptun, const ipsec_req_t *ipsr) 90210616SSebastien.Roy@Sun.COM { 90310616SSebastien.Roy@Sun.COM int rc = 0; 90410616SSebastien.Roy@Sun.COM uint_t nact; 90510616SSebastien.Roy@Sun.COM ipsec_act_t *actp = NULL; 90610616SSebastien.Roy@Sun.COM boolean_t clear_all, old_policy = B_FALSE; 90710616SSebastien.Roy@Sun.COM ipsec_tun_pol_t *itp; 90810616SSebastien.Roy@Sun.COM char name[MAXLINKNAMELEN]; 90910616SSebastien.Roy@Sun.COM uint64_t gen; 91010616SSebastien.Roy@Sun.COM netstack_t *ns = iptun->iptun_ns; 91110616SSebastien.Roy@Sun.COM 91210616SSebastien.Roy@Sun.COM /* Can't specify self-encap on a tunnel. */ 91310616SSebastien.Roy@Sun.COM if (ipsr->ipsr_self_encap_req != 0) 91410616SSebastien.Roy@Sun.COM return (EINVAL); 91510616SSebastien.Roy@Sun.COM 91610616SSebastien.Roy@Sun.COM /* 91710616SSebastien.Roy@Sun.COM * If it's a "clear-all" entry, unset the security flags and resume 91810616SSebastien.Roy@Sun.COM * normal cleartext (or inherit-from-global) policy. 91910616SSebastien.Roy@Sun.COM */ 92010616SSebastien.Roy@Sun.COM clear_all = ((ipsr->ipsr_ah_req & IPTUN_IPSEC_REQ_MASK) == 0 && 92110616SSebastien.Roy@Sun.COM (ipsr->ipsr_esp_req & IPTUN_IPSEC_REQ_MASK) == 0); 92210616SSebastien.Roy@Sun.COM 92310616SSebastien.Roy@Sun.COM ASSERT(mutex_owned(&iptun->iptun_lock)); 92410616SSebastien.Roy@Sun.COM itp = iptun->iptun_itp; 92510616SSebastien.Roy@Sun.COM if (itp == NULL) { 92610616SSebastien.Roy@Sun.COM if (clear_all) 92710616SSebastien.Roy@Sun.COM goto bail; 92810616SSebastien.Roy@Sun.COM if ((rc = dls_mgmt_get_linkinfo(iptun->iptun_linkid, name, NULL, 92910616SSebastien.Roy@Sun.COM NULL, NULL)) != 0) 93010616SSebastien.Roy@Sun.COM goto bail; 93110616SSebastien.Roy@Sun.COM ASSERT(name[0] != '\0'); 93210616SSebastien.Roy@Sun.COM if ((itp = create_tunnel_policy(name, &rc, &gen, ns)) == NULL) 93310616SSebastien.Roy@Sun.COM goto bail; 93410616SSebastien.Roy@Sun.COM iptun->iptun_itp = itp; 93510616SSebastien.Roy@Sun.COM } 93610616SSebastien.Roy@Sun.COM 93710616SSebastien.Roy@Sun.COM /* Allocate the actvec now, before holding itp or polhead locks. */ 93810616SSebastien.Roy@Sun.COM ipsec_actvec_from_req(ipsr, &actp, &nact, ns); 93910616SSebastien.Roy@Sun.COM if (actp == NULL) { 94010616SSebastien.Roy@Sun.COM rc = ENOMEM; 94110616SSebastien.Roy@Sun.COM goto bail; 94210616SSebastien.Roy@Sun.COM } 94310616SSebastien.Roy@Sun.COM 94410616SSebastien.Roy@Sun.COM /* 94510616SSebastien.Roy@Sun.COM * Just write on the active polhead. Save the primary/secondary stuff 94610616SSebastien.Roy@Sun.COM * for spdsock operations. 94710616SSebastien.Roy@Sun.COM * 94810616SSebastien.Roy@Sun.COM * Mutex because we need to write to the polhead AND flags atomically. 94910616SSebastien.Roy@Sun.COM * Other threads will acquire the polhead lock as a reader if the 95010616SSebastien.Roy@Sun.COM * (unprotected) flag is set. 95110616SSebastien.Roy@Sun.COM */ 95210616SSebastien.Roy@Sun.COM mutex_enter(&itp->itp_lock); 95310616SSebastien.Roy@Sun.COM if (itp->itp_flags & ITPF_P_TUNNEL) { 95410616SSebastien.Roy@Sun.COM /* Oops, we lost a race. Let's get out of here. */ 95510616SSebastien.Roy@Sun.COM rc = EBUSY; 95610616SSebastien.Roy@Sun.COM goto mutex_bail; 95710616SSebastien.Roy@Sun.COM } 95810616SSebastien.Roy@Sun.COM old_policy = ((itp->itp_flags & ITPF_P_ACTIVE) != 0); 95910616SSebastien.Roy@Sun.COM 96010616SSebastien.Roy@Sun.COM if (old_policy) { 96110616SSebastien.Roy@Sun.COM ITPF_CLONE(itp->itp_flags); 96210616SSebastien.Roy@Sun.COM rc = ipsec_copy_polhead(itp->itp_policy, itp->itp_inactive, ns); 96310616SSebastien.Roy@Sun.COM if (rc != 0) { 96410616SSebastien.Roy@Sun.COM /* inactive has already been cleared. */ 96510616SSebastien.Roy@Sun.COM itp->itp_flags &= ~ITPF_IFLAGS; 96610616SSebastien.Roy@Sun.COM goto mutex_bail; 96710616SSebastien.Roy@Sun.COM } 96810616SSebastien.Roy@Sun.COM rw_enter(&itp->itp_policy->iph_lock, RW_WRITER); 96910616SSebastien.Roy@Sun.COM ipsec_polhead_flush(itp->itp_policy, ns); 97010616SSebastien.Roy@Sun.COM } else { 97110616SSebastien.Roy@Sun.COM /* Else assume itp->itp_policy is already flushed. */ 97210616SSebastien.Roy@Sun.COM rw_enter(&itp->itp_policy->iph_lock, RW_WRITER); 97310616SSebastien.Roy@Sun.COM } 97410616SSebastien.Roy@Sun.COM 97510616SSebastien.Roy@Sun.COM if (clear_all) { 97610616SSebastien.Roy@Sun.COM ASSERT(avl_numnodes(&itp->itp_policy->iph_rulebyid) == 0); 97710616SSebastien.Roy@Sun.COM itp->itp_flags &= ~ITPF_PFLAGS; 97810616SSebastien.Roy@Sun.COM rw_exit(&itp->itp_policy->iph_lock); 97910616SSebastien.Roy@Sun.COM old_policy = B_FALSE; /* Clear out the inactive one too. */ 98010616SSebastien.Roy@Sun.COM goto recover_bail; 98110616SSebastien.Roy@Sun.COM } 98210616SSebastien.Roy@Sun.COM 98310616SSebastien.Roy@Sun.COM if (iptun_insert_simple_policies(itp->itp_policy, actp, nact, ns)) { 98410616SSebastien.Roy@Sun.COM rw_exit(&itp->itp_policy->iph_lock); 98510616SSebastien.Roy@Sun.COM /* 98610616SSebastien.Roy@Sun.COM * Adjust MTU and make sure the DL side knows what's up. 98710616SSebastien.Roy@Sun.COM */ 98810616SSebastien.Roy@Sun.COM itp->itp_flags = ITPF_P_ACTIVE; 98910616SSebastien.Roy@Sun.COM (void) iptun_update_mtu(iptun, 0); 99010616SSebastien.Roy@Sun.COM old_policy = B_FALSE; /* Blank out inactive - we succeeded */ 99110616SSebastien.Roy@Sun.COM } else { 99210616SSebastien.Roy@Sun.COM rw_exit(&itp->itp_policy->iph_lock); 99310616SSebastien.Roy@Sun.COM rc = ENOMEM; 99410616SSebastien.Roy@Sun.COM } 99510616SSebastien.Roy@Sun.COM 99610616SSebastien.Roy@Sun.COM recover_bail: 99710616SSebastien.Roy@Sun.COM if (old_policy) { 99810616SSebastien.Roy@Sun.COM /* Recover policy in in active polhead. */ 99910616SSebastien.Roy@Sun.COM ipsec_swap_policy(itp->itp_policy, itp->itp_inactive, ns); 100010616SSebastien.Roy@Sun.COM ITPF_SWAP(itp->itp_flags); 100110616SSebastien.Roy@Sun.COM } 100210616SSebastien.Roy@Sun.COM 100310616SSebastien.Roy@Sun.COM /* Clear policy in inactive polhead. */ 100410616SSebastien.Roy@Sun.COM itp->itp_flags &= ~ITPF_IFLAGS; 100510616SSebastien.Roy@Sun.COM rw_enter(&itp->itp_inactive->iph_lock, RW_WRITER); 100610616SSebastien.Roy@Sun.COM ipsec_polhead_flush(itp->itp_inactive, ns); 100710616SSebastien.Roy@Sun.COM rw_exit(&itp->itp_inactive->iph_lock); 100810616SSebastien.Roy@Sun.COM 100910616SSebastien.Roy@Sun.COM mutex_bail: 101010616SSebastien.Roy@Sun.COM mutex_exit(&itp->itp_lock); 101110616SSebastien.Roy@Sun.COM 101210616SSebastien.Roy@Sun.COM bail: 101310616SSebastien.Roy@Sun.COM if (actp != NULL) 101410616SSebastien.Roy@Sun.COM ipsec_actvec_free(actp, nact); 101510616SSebastien.Roy@Sun.COM 101610616SSebastien.Roy@Sun.COM return (rc); 101710616SSebastien.Roy@Sun.COM } 101810616SSebastien.Roy@Sun.COM 101910616SSebastien.Roy@Sun.COM static iptun_typeinfo_t * 102010616SSebastien.Roy@Sun.COM iptun_gettypeinfo(iptun_type_t type) 102110616SSebastien.Roy@Sun.COM { 102210616SSebastien.Roy@Sun.COM int i; 102310616SSebastien.Roy@Sun.COM 102410616SSebastien.Roy@Sun.COM for (i = 0; iptun_type_table[i].iti_type != IPTUN_TYPE_UNKNOWN; i++) { 102510616SSebastien.Roy@Sun.COM if (iptun_type_table[i].iti_type == type) 102610616SSebastien.Roy@Sun.COM break; 102710616SSebastien.Roy@Sun.COM } 102810616SSebastien.Roy@Sun.COM return (&iptun_type_table[i]); 102910616SSebastien.Roy@Sun.COM } 103010616SSebastien.Roy@Sun.COM 103110616SSebastien.Roy@Sun.COM /* 103210616SSebastien.Roy@Sun.COM * Set the parameters included in ik on the tunnel iptun. Parameters that can 103310616SSebastien.Roy@Sun.COM * only be set at creation time are set in iptun_create(). 103410616SSebastien.Roy@Sun.COM */ 103510616SSebastien.Roy@Sun.COM static int 103610616SSebastien.Roy@Sun.COM iptun_setparams(iptun_t *iptun, const iptun_kparams_t *ik) 103710616SSebastien.Roy@Sun.COM { 103810616SSebastien.Roy@Sun.COM int err = 0; 103910616SSebastien.Roy@Sun.COM netstack_t *ns = iptun->iptun_ns; 104010616SSebastien.Roy@Sun.COM iptun_addr_t orig_laddr, orig_raddr; 104110616SSebastien.Roy@Sun.COM uint_t orig_flags = iptun->iptun_flags; 104210616SSebastien.Roy@Sun.COM 104310616SSebastien.Roy@Sun.COM if (ik->iptun_kparam_flags & IPTUN_KPARAM_LADDR) { 104410616SSebastien.Roy@Sun.COM if (orig_flags & IPTUN_LADDR) 104510616SSebastien.Roy@Sun.COM orig_laddr = iptun->iptun_laddr; 104610616SSebastien.Roy@Sun.COM if ((err = iptun_setladdr(iptun, &ik->iptun_kparam_laddr)) != 0) 104710616SSebastien.Roy@Sun.COM return (err); 104810616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_LADDR; 104910616SSebastien.Roy@Sun.COM } 105010616SSebastien.Roy@Sun.COM 105110616SSebastien.Roy@Sun.COM if (ik->iptun_kparam_flags & IPTUN_KPARAM_RADDR) { 105210616SSebastien.Roy@Sun.COM if (orig_flags & IPTUN_RADDR) 105310616SSebastien.Roy@Sun.COM orig_raddr = iptun->iptun_raddr; 105410616SSebastien.Roy@Sun.COM if ((err = iptun_setraddr(iptun, &ik->iptun_kparam_raddr)) != 0) 105510616SSebastien.Roy@Sun.COM goto done; 105610616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_RADDR; 105710616SSebastien.Roy@Sun.COM } 105810616SSebastien.Roy@Sun.COM 105910616SSebastien.Roy@Sun.COM if (ik->iptun_kparam_flags & IPTUN_KPARAM_SECINFO) { 106010616SSebastien.Roy@Sun.COM /* 106110616SSebastien.Roy@Sun.COM * Set IPsec policy originating from the ifconfig(1M) command 106210616SSebastien.Roy@Sun.COM * line. This is traditionally called "simple" policy because 106310616SSebastien.Roy@Sun.COM * the ipsec_req_t (iptun_kparam_secinfo) can only describe a 106410616SSebastien.Roy@Sun.COM * simple policy of "do ESP on everything" and/or "do AH on 106510616SSebastien.Roy@Sun.COM * everything" (as opposed to the rich policy that can be 106610616SSebastien.Roy@Sun.COM * defined with ipsecconf(1M)). 106710616SSebastien.Roy@Sun.COM */ 106810616SSebastien.Roy@Sun.COM if (iptun->iptun_typeinfo->iti_type == IPTUN_TYPE_6TO4) { 106910616SSebastien.Roy@Sun.COM /* 107010616SSebastien.Roy@Sun.COM * Can't set security properties for automatic 107110616SSebastien.Roy@Sun.COM * tunnels. 107210616SSebastien.Roy@Sun.COM */ 107310616SSebastien.Roy@Sun.COM err = EINVAL; 107410616SSebastien.Roy@Sun.COM goto done; 107510616SSebastien.Roy@Sun.COM } 107610616SSebastien.Roy@Sun.COM 107710616SSebastien.Roy@Sun.COM if (!ipsec_loaded(ns->netstack_ipsec)) { 107810616SSebastien.Roy@Sun.COM /* If IPsec can be loaded, try and load it now. */ 107910616SSebastien.Roy@Sun.COM if (ipsec_failed(ns->netstack_ipsec)) { 108010616SSebastien.Roy@Sun.COM err = EPROTONOSUPPORT; 108110616SSebastien.Roy@Sun.COM goto done; 108210616SSebastien.Roy@Sun.COM } 108310616SSebastien.Roy@Sun.COM ipsec_loader_loadnow(ns->netstack_ipsec); 108410616SSebastien.Roy@Sun.COM /* 108510616SSebastien.Roy@Sun.COM * ipsec_loader_loadnow() returns while IPsec is 108610616SSebastien.Roy@Sun.COM * loaded asynchronously. While a method exists to 108710616SSebastien.Roy@Sun.COM * wait for IPsec to load (ipsec_loader_wait()), it 108810616SSebastien.Roy@Sun.COM * requires use of a STREAMS queue to do a qwait(). 108910616SSebastien.Roy@Sun.COM * We're not in STREAMS context here, and so we can't 109010616SSebastien.Roy@Sun.COM * use it. This is not a problem in practice because 109110616SSebastien.Roy@Sun.COM * in the vast majority of cases, key management and 109210616SSebastien.Roy@Sun.COM * global policy will have loaded before any tunnels 109310616SSebastien.Roy@Sun.COM * are plumbed, and so IPsec will already have been 109410616SSebastien.Roy@Sun.COM * loaded. 109510616SSebastien.Roy@Sun.COM */ 109610616SSebastien.Roy@Sun.COM err = EAGAIN; 109710616SSebastien.Roy@Sun.COM goto done; 109810616SSebastien.Roy@Sun.COM } 109910616SSebastien.Roy@Sun.COM 110010616SSebastien.Roy@Sun.COM err = iptun_set_sec_simple(iptun, &ik->iptun_kparam_secinfo); 110110616SSebastien.Roy@Sun.COM if (err == 0) { 110210616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_SIMPLE_POLICY; 110310616SSebastien.Roy@Sun.COM iptun->iptun_simple_policy = ik->iptun_kparam_secinfo; 110410616SSebastien.Roy@Sun.COM } 110510616SSebastien.Roy@Sun.COM } 110610616SSebastien.Roy@Sun.COM done: 110710616SSebastien.Roy@Sun.COM if (err != 0) { 110810616SSebastien.Roy@Sun.COM /* Restore original source and destination. */ 110910616SSebastien.Roy@Sun.COM if (ik->iptun_kparam_flags & IPTUN_KPARAM_LADDR && 111010616SSebastien.Roy@Sun.COM (orig_flags & IPTUN_LADDR)) 111110616SSebastien.Roy@Sun.COM iptun->iptun_laddr = orig_laddr; 111210616SSebastien.Roy@Sun.COM if ((ik->iptun_kparam_flags & IPTUN_KPARAM_RADDR) && 111310616SSebastien.Roy@Sun.COM (orig_flags & IPTUN_RADDR)) 111410616SSebastien.Roy@Sun.COM iptun->iptun_raddr = orig_raddr; 111510616SSebastien.Roy@Sun.COM iptun->iptun_flags = orig_flags; 111610616SSebastien.Roy@Sun.COM } 111710616SSebastien.Roy@Sun.COM return (err); 111810616SSebastien.Roy@Sun.COM } 111910616SSebastien.Roy@Sun.COM 112010616SSebastien.Roy@Sun.COM static int 112110616SSebastien.Roy@Sun.COM iptun_register(iptun_t *iptun) 112210616SSebastien.Roy@Sun.COM { 112310616SSebastien.Roy@Sun.COM mac_register_t *mac; 112410616SSebastien.Roy@Sun.COM int err; 112510616SSebastien.Roy@Sun.COM 112610616SSebastien.Roy@Sun.COM ASSERT(!(iptun->iptun_flags & IPTUN_MAC_REGISTERED)); 112710616SSebastien.Roy@Sun.COM 112810616SSebastien.Roy@Sun.COM if ((mac = mac_alloc(MAC_VERSION)) == NULL) 112910616SSebastien.Roy@Sun.COM return (EINVAL); 113010616SSebastien.Roy@Sun.COM 113110616SSebastien.Roy@Sun.COM mac->m_type_ident = iptun->iptun_typeinfo->iti_ident; 113210616SSebastien.Roy@Sun.COM mac->m_driver = iptun; 113310616SSebastien.Roy@Sun.COM mac->m_dip = iptun_dip; 113410616SSebastien.Roy@Sun.COM mac->m_instance = (uint_t)-1; 113510616SSebastien.Roy@Sun.COM mac->m_src_addr = (uint8_t *)&iptun->iptun_laddr.ia_addr; 113610616SSebastien.Roy@Sun.COM mac->m_dst_addr = iptun->iptun_typeinfo->iti_hasraddr ? 113710616SSebastien.Roy@Sun.COM (uint8_t *)&iptun->iptun_raddr.ia_addr : NULL; 113810616SSebastien.Roy@Sun.COM mac->m_callbacks = &iptun_m_callbacks; 113910616SSebastien.Roy@Sun.COM mac->m_min_sdu = iptun->iptun_typeinfo->iti_minmtu; 114010616SSebastien.Roy@Sun.COM mac->m_max_sdu = iptun->iptun_mtu; 114110616SSebastien.Roy@Sun.COM if (iptun->iptun_header_size != 0) { 114210616SSebastien.Roy@Sun.COM mac->m_pdata = &iptun->iptun_header; 114310616SSebastien.Roy@Sun.COM mac->m_pdata_size = iptun->iptun_header_size; 114410616SSebastien.Roy@Sun.COM } 114510616SSebastien.Roy@Sun.COM if ((err = mac_register(mac, &iptun->iptun_mh)) == 0) 114610616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_MAC_REGISTERED; 114710616SSebastien.Roy@Sun.COM mac_free(mac); 114810616SSebastien.Roy@Sun.COM return (err); 114910616SSebastien.Roy@Sun.COM } 115010616SSebastien.Roy@Sun.COM 115110616SSebastien.Roy@Sun.COM static int 115210616SSebastien.Roy@Sun.COM iptun_unregister(iptun_t *iptun) 115310616SSebastien.Roy@Sun.COM { 115410616SSebastien.Roy@Sun.COM int err; 115510616SSebastien.Roy@Sun.COM 115610616SSebastien.Roy@Sun.COM ASSERT(iptun->iptun_flags & IPTUN_MAC_REGISTERED); 115710616SSebastien.Roy@Sun.COM if ((err = mac_unregister(iptun->iptun_mh)) == 0) 115810616SSebastien.Roy@Sun.COM iptun->iptun_flags &= ~IPTUN_MAC_REGISTERED; 115910616SSebastien.Roy@Sun.COM return (err); 116010616SSebastien.Roy@Sun.COM } 116110616SSebastien.Roy@Sun.COM 116210616SSebastien.Roy@Sun.COM static conn_t * 116310616SSebastien.Roy@Sun.COM iptun_conn_create(iptun_t *iptun, netstack_t *ns, cred_t *credp) 116410616SSebastien.Roy@Sun.COM { 116510616SSebastien.Roy@Sun.COM conn_t *connp; 116610616SSebastien.Roy@Sun.COM 116710616SSebastien.Roy@Sun.COM if ((connp = ipcl_conn_create(IPCL_IPCCONN, KM_NOSLEEP, ns)) == NULL) 116810616SSebastien.Roy@Sun.COM return (NULL); 116910616SSebastien.Roy@Sun.COM 117010616SSebastien.Roy@Sun.COM connp->conn_flags |= IPCL_IPTUN; 117110616SSebastien.Roy@Sun.COM connp->conn_iptun = iptun; 117210616SSebastien.Roy@Sun.COM connp->conn_recv = iptun_input; 117310616SSebastien.Roy@Sun.COM connp->conn_rq = ns->netstack_iptun->iptuns_g_q; 117410616SSebastien.Roy@Sun.COM connp->conn_wq = WR(connp->conn_rq); 117510616SSebastien.Roy@Sun.COM /* 117610616SSebastien.Roy@Sun.COM * For exclusive stacks we set conn_zoneid to GLOBAL_ZONEID as is done 117710616SSebastien.Roy@Sun.COM * for all other conn_t's. 117810616SSebastien.Roy@Sun.COM * 117910616SSebastien.Roy@Sun.COM * Note that there's an important distinction between iptun_zoneid and 118010616SSebastien.Roy@Sun.COM * conn_zoneid. The conn_zoneid is set to GLOBAL_ZONEID in non-global 118110616SSebastien.Roy@Sun.COM * exclusive stack zones to make the ip module believe that the 118210616SSebastien.Roy@Sun.COM * non-global zone is actually a global zone. Therefore, when 118310616SSebastien.Roy@Sun.COM * interacting with the ip module, we must always use conn_zoneid. 118410616SSebastien.Roy@Sun.COM */ 118510616SSebastien.Roy@Sun.COM connp->conn_zoneid = (ns->netstack_stackid == GLOBAL_NETSTACKID) ? 118610616SSebastien.Roy@Sun.COM crgetzoneid(credp) : GLOBAL_ZONEID; 118710616SSebastien.Roy@Sun.COM connp->conn_cred = credp; 118810616SSebastien.Roy@Sun.COM /* crfree() is done in ipcl_conn_destroy(), called by CONN_DEC_REF() */ 118910616SSebastien.Roy@Sun.COM crhold(connp->conn_cred); 119010616SSebastien.Roy@Sun.COM 119110616SSebastien.Roy@Sun.COM connp->conn_send = iptun->iptun_typeinfo->iti_txfunc; 119210616SSebastien.Roy@Sun.COM connp->conn_af_isv6 = iptun->iptun_typeinfo->iti_ipvers == IPV6_VERSION; 119310616SSebastien.Roy@Sun.COM ASSERT(connp->conn_ref == 1); 119410616SSebastien.Roy@Sun.COM 119510616SSebastien.Roy@Sun.COM mutex_enter(&connp->conn_lock); 119610616SSebastien.Roy@Sun.COM connp->conn_state_flags &= ~CONN_INCIPIENT; 119710616SSebastien.Roy@Sun.COM mutex_exit(&connp->conn_lock); 119810616SSebastien.Roy@Sun.COM return (connp); 119910616SSebastien.Roy@Sun.COM } 120010616SSebastien.Roy@Sun.COM 120110616SSebastien.Roy@Sun.COM static void 120210616SSebastien.Roy@Sun.COM iptun_conn_destroy(conn_t *connp) 120310616SSebastien.Roy@Sun.COM { 120410616SSebastien.Roy@Sun.COM ip_quiesce_conn(connp); 120510616SSebastien.Roy@Sun.COM connp->conn_iptun = NULL; 120610616SSebastien.Roy@Sun.COM ASSERT(connp->conn_ref == 1); 120710616SSebastien.Roy@Sun.COM CONN_DEC_REF(connp); 120810616SSebastien.Roy@Sun.COM } 120910616SSebastien.Roy@Sun.COM 121010616SSebastien.Roy@Sun.COM static int 121110616SSebastien.Roy@Sun.COM iptun_create_g_q(iptun_stack_t *iptuns, cred_t *credp) 121210616SSebastien.Roy@Sun.COM { 121310616SSebastien.Roy@Sun.COM int err; 121410616SSebastien.Roy@Sun.COM conn_t *connp; 121510616SSebastien.Roy@Sun.COM 121610616SSebastien.Roy@Sun.COM ASSERT(iptuns->iptuns_g_q == NULL); 121710616SSebastien.Roy@Sun.COM /* 121810616SSebastien.Roy@Sun.COM * The global queue for this stack is set when iptunq_open() calls 121910616SSebastien.Roy@Sun.COM * iptun_set_g_q(). 122010616SSebastien.Roy@Sun.COM */ 122110616SSebastien.Roy@Sun.COM err = ldi_open_by_name(IPTUNQ_DEV, FWRITE|FREAD, credp, 122210616SSebastien.Roy@Sun.COM &iptuns->iptuns_g_q_lh, iptun_ldi_ident); 122310616SSebastien.Roy@Sun.COM if (err == 0) { 122410616SSebastien.Roy@Sun.COM connp = iptuns->iptuns_g_q->q_ptr; 122510616SSebastien.Roy@Sun.COM connp->conn_recv = iptun_input; 122610616SSebastien.Roy@Sun.COM } 122710616SSebastien.Roy@Sun.COM return (err); 122810616SSebastien.Roy@Sun.COM } 122910616SSebastien.Roy@Sun.COM 123010616SSebastien.Roy@Sun.COM static iptun_t * 123110616SSebastien.Roy@Sun.COM iptun_alloc(void) 123210616SSebastien.Roy@Sun.COM { 123310616SSebastien.Roy@Sun.COM iptun_t *iptun; 123410616SSebastien.Roy@Sun.COM 123510616SSebastien.Roy@Sun.COM if ((iptun = kmem_cache_alloc(iptun_cache, KM_NOSLEEP)) != NULL) { 123610616SSebastien.Roy@Sun.COM bzero(iptun, sizeof (*iptun)); 123710616SSebastien.Roy@Sun.COM atomic_inc_32(&iptun_tunnelcount); 123810616SSebastien.Roy@Sun.COM } 123910616SSebastien.Roy@Sun.COM return (iptun); 124010616SSebastien.Roy@Sun.COM } 124110616SSebastien.Roy@Sun.COM 124210616SSebastien.Roy@Sun.COM static void 124310616SSebastien.Roy@Sun.COM iptun_free(iptun_t *iptun) 124410616SSebastien.Roy@Sun.COM { 124510616SSebastien.Roy@Sun.COM ASSERT(iptun->iptun_flags & IPTUN_CONDEMNED); 124610616SSebastien.Roy@Sun.COM 124710616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_HASH_INSERTED) { 124810616SSebastien.Roy@Sun.COM iptun_stack_t *iptuns = iptun->iptun_iptuns; 124910616SSebastien.Roy@Sun.COM 125010616SSebastien.Roy@Sun.COM mutex_enter(&iptun_hash_lock); 125110616SSebastien.Roy@Sun.COM VERIFY(mod_hash_remove(iptun_hash, 125210616SSebastien.Roy@Sun.COM IPTUN_HASH_KEY(iptun->iptun_linkid), 125310616SSebastien.Roy@Sun.COM (mod_hash_val_t *)&iptun) == 0); 125410616SSebastien.Roy@Sun.COM mutex_exit(&iptun_hash_lock); 125510616SSebastien.Roy@Sun.COM iptun->iptun_flags &= ~IPTUN_HASH_INSERTED; 125610616SSebastien.Roy@Sun.COM mutex_enter(&iptuns->iptuns_lock); 125710616SSebastien.Roy@Sun.COM list_remove(&iptuns->iptuns_iptunlist, iptun); 125810616SSebastien.Roy@Sun.COM mutex_exit(&iptuns->iptuns_lock); 125910616SSebastien.Roy@Sun.COM } 126010616SSebastien.Roy@Sun.COM 126110616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_BOUND) 126210616SSebastien.Roy@Sun.COM iptun_unbind(iptun); 126310616SSebastien.Roy@Sun.COM 126410616SSebastien.Roy@Sun.COM /* 126510616SSebastien.Roy@Sun.COM * After iptun_unregister(), there will be no threads executing a 126610616SSebastien.Roy@Sun.COM * downcall from the mac module, including in the tx datapath. 126710616SSebastien.Roy@Sun.COM */ 126810616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_MAC_REGISTERED) 126910616SSebastien.Roy@Sun.COM VERIFY(iptun_unregister(iptun) == 0); 127010616SSebastien.Roy@Sun.COM 127110616SSebastien.Roy@Sun.COM if (iptun->iptun_itp != NULL) { 127210616SSebastien.Roy@Sun.COM /* 127310616SSebastien.Roy@Sun.COM * Remove from the AVL tree, AND release the reference iptun_t 127410616SSebastien.Roy@Sun.COM * itself holds on the ITP. 127510616SSebastien.Roy@Sun.COM */ 127610616SSebastien.Roy@Sun.COM itp_unlink(iptun->iptun_itp, iptun->iptun_ns); 127710616SSebastien.Roy@Sun.COM ITP_REFRELE(iptun->iptun_itp, iptun->iptun_ns); 127810616SSebastien.Roy@Sun.COM iptun->iptun_itp = NULL; 127910616SSebastien.Roy@Sun.COM iptun->iptun_flags &= ~IPTUN_SIMPLE_POLICY; 128010616SSebastien.Roy@Sun.COM } 128110616SSebastien.Roy@Sun.COM 128210616SSebastien.Roy@Sun.COM /* 128310616SSebastien.Roy@Sun.COM * After ipcl_conn_destroy(), there will be no threads executing an 128410616SSebastien.Roy@Sun.COM * upcall from ip (i.e., iptun_input()), and it is then safe to free 128510616SSebastien.Roy@Sun.COM * the iptun_t. 128610616SSebastien.Roy@Sun.COM */ 128710616SSebastien.Roy@Sun.COM if (iptun->iptun_connp != NULL) { 128810616SSebastien.Roy@Sun.COM iptun_conn_destroy(iptun->iptun_connp); 128910616SSebastien.Roy@Sun.COM iptun->iptun_connp = NULL; 129010616SSebastien.Roy@Sun.COM } 129110616SSebastien.Roy@Sun.COM 129210616SSebastien.Roy@Sun.COM netstack_rele(iptun->iptun_ns); 129310616SSebastien.Roy@Sun.COM iptun->iptun_ns = NULL; 129410616SSebastien.Roy@Sun.COM crfree(iptun->iptun_cred); 129510616SSebastien.Roy@Sun.COM iptun->iptun_cred = NULL; 129610616SSebastien.Roy@Sun.COM 129710616SSebastien.Roy@Sun.COM kmem_cache_free(iptun_cache, iptun); 129810616SSebastien.Roy@Sun.COM atomic_dec_32(&iptun_tunnelcount); 129910616SSebastien.Roy@Sun.COM } 130010616SSebastien.Roy@Sun.COM 130110616SSebastien.Roy@Sun.COM int 130210616SSebastien.Roy@Sun.COM iptun_create(iptun_kparams_t *ik, cred_t *credp) 130310616SSebastien.Roy@Sun.COM { 130410616SSebastien.Roy@Sun.COM iptun_t *iptun = NULL; 130510616SSebastien.Roy@Sun.COM int err = 0, mherr; 130610616SSebastien.Roy@Sun.COM char linkname[MAXLINKNAMELEN]; 130710616SSebastien.Roy@Sun.COM ipsec_tun_pol_t *itp; 130810616SSebastien.Roy@Sun.COM netstack_t *ns = NULL; 130910616SSebastien.Roy@Sun.COM iptun_stack_t *iptuns; 131010616SSebastien.Roy@Sun.COM datalink_id_t tmpid; 131110616SSebastien.Roy@Sun.COM zoneid_t zoneid = crgetzoneid(credp); 131210616SSebastien.Roy@Sun.COM boolean_t link_created = B_FALSE; 131310616SSebastien.Roy@Sun.COM 131410616SSebastien.Roy@Sun.COM /* The tunnel type is mandatory */ 131510616SSebastien.Roy@Sun.COM if (!(ik->iptun_kparam_flags & IPTUN_KPARAM_TYPE)) 131610616SSebastien.Roy@Sun.COM return (EINVAL); 131710616SSebastien.Roy@Sun.COM 131810616SSebastien.Roy@Sun.COM /* 131910616SSebastien.Roy@Sun.COM * Is the linkid that the caller wishes to associate with this new 132010616SSebastien.Roy@Sun.COM * tunnel assigned to this zone? 132110616SSebastien.Roy@Sun.COM */ 132210616SSebastien.Roy@Sun.COM if (zone_check_datalink(&zoneid, ik->iptun_kparam_linkid) != 0) { 132310616SSebastien.Roy@Sun.COM if (zoneid != GLOBAL_ZONEID) 132410616SSebastien.Roy@Sun.COM return (EINVAL); 132510616SSebastien.Roy@Sun.COM } else if (zoneid == GLOBAL_ZONEID) { 132610616SSebastien.Roy@Sun.COM return (EINVAL); 132710616SSebastien.Roy@Sun.COM } 132810616SSebastien.Roy@Sun.COM 132910616SSebastien.Roy@Sun.COM /* 133010616SSebastien.Roy@Sun.COM * Make sure that we're not trying to create a tunnel that has already 133110616SSebastien.Roy@Sun.COM * been created. 133210616SSebastien.Roy@Sun.COM */ 133310616SSebastien.Roy@Sun.COM if (iptun_enter_by_linkid(ik->iptun_kparam_linkid, &iptun) == 0) { 133410616SSebastien.Roy@Sun.COM iptun_exit(iptun); 133510616SSebastien.Roy@Sun.COM iptun = NULL; 133610616SSebastien.Roy@Sun.COM err = EEXIST; 133710616SSebastien.Roy@Sun.COM goto done; 133810616SSebastien.Roy@Sun.COM } 133910616SSebastien.Roy@Sun.COM 134010616SSebastien.Roy@Sun.COM ns = netstack_find_by_cred(credp); 134110616SSebastien.Roy@Sun.COM iptuns = ns->netstack_iptun; 134210616SSebastien.Roy@Sun.COM 134310616SSebastien.Roy@Sun.COM /* 134410616SSebastien.Roy@Sun.COM * Before we create any tunnel, we need to ensure that the default 134510616SSebastien.Roy@Sun.COM * STREAMS queue (used to satisfy the ip module's requirement for one) 134610616SSebastien.Roy@Sun.COM * is created. We only do this once per stack. The stream is closed 134710616SSebastien.Roy@Sun.COM * when the stack is destroyed in iptun_stack_fni(). 134810616SSebastien.Roy@Sun.COM */ 134910616SSebastien.Roy@Sun.COM mutex_enter(&iptuns->iptuns_lock); 135010616SSebastien.Roy@Sun.COM if (iptuns->iptuns_g_q == NULL) 135110616SSebastien.Roy@Sun.COM err = iptun_create_g_q(iptuns, zone_kcred()); 135210616SSebastien.Roy@Sun.COM mutex_exit(&iptuns->iptuns_lock); 135310616SSebastien.Roy@Sun.COM if (err != 0) 135410616SSebastien.Roy@Sun.COM goto done; 135510616SSebastien.Roy@Sun.COM 135610616SSebastien.Roy@Sun.COM if ((iptun = iptun_alloc()) == NULL) { 135710616SSebastien.Roy@Sun.COM err = ENOMEM; 135810616SSebastien.Roy@Sun.COM goto done; 135910616SSebastien.Roy@Sun.COM } 136010616SSebastien.Roy@Sun.COM 136110616SSebastien.Roy@Sun.COM iptun->iptun_linkid = ik->iptun_kparam_linkid; 136210616SSebastien.Roy@Sun.COM iptun->iptun_zoneid = zoneid; 136310616SSebastien.Roy@Sun.COM crhold(credp); 136410616SSebastien.Roy@Sun.COM iptun->iptun_cred = credp; 136510616SSebastien.Roy@Sun.COM iptun->iptun_ns = ns; 136610616SSebastien.Roy@Sun.COM 136710616SSebastien.Roy@Sun.COM iptun->iptun_typeinfo = iptun_gettypeinfo(ik->iptun_kparam_type); 136810616SSebastien.Roy@Sun.COM if (iptun->iptun_typeinfo->iti_type == IPTUN_TYPE_UNKNOWN) { 136910616SSebastien.Roy@Sun.COM err = EINVAL; 137010616SSebastien.Roy@Sun.COM goto done; 137110616SSebastien.Roy@Sun.COM } 137210616SSebastien.Roy@Sun.COM 137310616SSebastien.Roy@Sun.COM if (ik->iptun_kparam_flags & IPTUN_KPARAM_IMPLICIT) 137410616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_IMPLICIT; 137510616SSebastien.Roy@Sun.COM 137610616SSebastien.Roy@Sun.COM if ((err = iptun_setparams(iptun, ik)) != 0) 137710616SSebastien.Roy@Sun.COM goto done; 137810616SSebastien.Roy@Sun.COM 137910616SSebastien.Roy@Sun.COM iptun->iptun_hoplimit = IPTUN_DEFAULT_HOPLIMIT; 138010616SSebastien.Roy@Sun.COM if (iptun->iptun_typeinfo->iti_type == IPTUN_TYPE_IPV6) 138110616SSebastien.Roy@Sun.COM iptun->iptun_encaplimit = IPTUN_DEFAULT_ENCAPLIMIT; 138210616SSebastien.Roy@Sun.COM 138310616SSebastien.Roy@Sun.COM iptun_headergen(iptun, B_FALSE); 138410616SSebastien.Roy@Sun.COM 138510616SSebastien.Roy@Sun.COM iptun->iptun_connp = iptun_conn_create(iptun, ns, credp); 138610616SSebastien.Roy@Sun.COM if (iptun->iptun_connp == NULL) { 138710616SSebastien.Roy@Sun.COM err = ENOMEM; 138810616SSebastien.Roy@Sun.COM goto done; 138910616SSebastien.Roy@Sun.COM } 139010616SSebastien.Roy@Sun.COM 139110616SSebastien.Roy@Sun.COM iptun->iptun_mtu = iptun->iptun_typeinfo->iti_maxmtu; 139210616SSebastien.Roy@Sun.COM iptun->iptun_dpmtu = iptun->iptun_mtu; 139310616SSebastien.Roy@Sun.COM 139410616SSebastien.Roy@Sun.COM /* 139510616SSebastien.Roy@Sun.COM * Find an ITP based on linkname. If we have parms already set via 139610616SSebastien.Roy@Sun.COM * the iptun_setparams() call above, it may have created an ITP for 139710616SSebastien.Roy@Sun.COM * us. We always try get_tunnel_policy() for DEBUG correctness 139810616SSebastien.Roy@Sun.COM * checks, and we may wish to refactor this to only check when 139910616SSebastien.Roy@Sun.COM * iptun_itp is NULL. 140010616SSebastien.Roy@Sun.COM */ 140110616SSebastien.Roy@Sun.COM if ((err = dls_mgmt_get_linkinfo(iptun->iptun_linkid, linkname, NULL, 140210616SSebastien.Roy@Sun.COM NULL, NULL)) != 0) 140310616SSebastien.Roy@Sun.COM goto done; 140410616SSebastien.Roy@Sun.COM if ((itp = get_tunnel_policy(linkname, ns)) != NULL) 140510616SSebastien.Roy@Sun.COM iptun->iptun_itp = itp; 140610616SSebastien.Roy@Sun.COM 140710616SSebastien.Roy@Sun.COM /* 140810616SSebastien.Roy@Sun.COM * See if we have the necessary IP addresses assigned to this tunnel 140910616SSebastien.Roy@Sun.COM * to try and bind them with ip underneath us. If we're not ready to 141010616SSebastien.Roy@Sun.COM * bind yet, then we'll defer the bind operation until the addresses 141110616SSebastien.Roy@Sun.COM * are modified. 141210616SSebastien.Roy@Sun.COM */ 141310616SSebastien.Roy@Sun.COM if (iptun_canbind(iptun) && ((err = iptun_bind(iptun)) != 0)) 141410616SSebastien.Roy@Sun.COM goto done; 141510616SSebastien.Roy@Sun.COM 141610616SSebastien.Roy@Sun.COM if ((err = iptun_register(iptun)) != 0) 141710616SSebastien.Roy@Sun.COM goto done; 141810616SSebastien.Roy@Sun.COM 141910616SSebastien.Roy@Sun.COM err = dls_devnet_create(iptun->iptun_mh, iptun->iptun_linkid, 142010616SSebastien.Roy@Sun.COM iptun->iptun_zoneid); 142110616SSebastien.Roy@Sun.COM if (err != 0) 142210616SSebastien.Roy@Sun.COM goto done; 142310616SSebastien.Roy@Sun.COM link_created = B_TRUE; 142410616SSebastien.Roy@Sun.COM 142510616SSebastien.Roy@Sun.COM /* 142610616SSebastien.Roy@Sun.COM * We hash by link-id as that is the key used by all other iptun 142710616SSebastien.Roy@Sun.COM * interfaces (modify, delete, etc.). 142810616SSebastien.Roy@Sun.COM */ 142910616SSebastien.Roy@Sun.COM if ((mherr = mod_hash_insert(iptun_hash, 143010616SSebastien.Roy@Sun.COM IPTUN_HASH_KEY(iptun->iptun_linkid), (mod_hash_val_t)iptun)) == 0) { 143110616SSebastien.Roy@Sun.COM mutex_enter(&iptuns->iptuns_lock); 143210616SSebastien.Roy@Sun.COM list_insert_head(&iptuns->iptuns_iptunlist, iptun); 143310616SSebastien.Roy@Sun.COM mutex_exit(&iptuns->iptuns_lock); 143410616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_HASH_INSERTED; 143510616SSebastien.Roy@Sun.COM } else if (mherr == MH_ERR_NOMEM) { 143610616SSebastien.Roy@Sun.COM err = ENOMEM; 143710616SSebastien.Roy@Sun.COM } else if (mherr == MH_ERR_DUPLICATE) { 143810616SSebastien.Roy@Sun.COM err = EEXIST; 143910616SSebastien.Roy@Sun.COM } else { 144010616SSebastien.Roy@Sun.COM err = EINVAL; 144110616SSebastien.Roy@Sun.COM } 144210616SSebastien.Roy@Sun.COM 144310616SSebastien.Roy@Sun.COM done: 144410616SSebastien.Roy@Sun.COM if (iptun == NULL && ns != NULL) 144510616SSebastien.Roy@Sun.COM netstack_rele(ns); 144610616SSebastien.Roy@Sun.COM if (err != 0 && iptun != NULL) { 144710616SSebastien.Roy@Sun.COM if (link_created) { 144810616SSebastien.Roy@Sun.COM (void) dls_devnet_destroy(iptun->iptun_mh, &tmpid, 144910616SSebastien.Roy@Sun.COM B_TRUE); 145010616SSebastien.Roy@Sun.COM } 145110616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_CONDEMNED; 145210616SSebastien.Roy@Sun.COM iptun_free(iptun); 145310616SSebastien.Roy@Sun.COM } 145410616SSebastien.Roy@Sun.COM return (err); 145510616SSebastien.Roy@Sun.COM } 145610616SSebastien.Roy@Sun.COM 145710616SSebastien.Roy@Sun.COM int 145810616SSebastien.Roy@Sun.COM iptun_delete(datalink_id_t linkid, cred_t *credp) 145910616SSebastien.Roy@Sun.COM { 146010616SSebastien.Roy@Sun.COM int err; 146110616SSebastien.Roy@Sun.COM iptun_t *iptun = NULL; 146210616SSebastien.Roy@Sun.COM 146310616SSebastien.Roy@Sun.COM if ((err = iptun_enter_by_linkid(linkid, &iptun)) != 0) 146410616SSebastien.Roy@Sun.COM return (err); 146510616SSebastien.Roy@Sun.COM 146610616SSebastien.Roy@Sun.COM /* One cannot delete a tunnel that belongs to another zone. */ 146710616SSebastien.Roy@Sun.COM if (iptun->iptun_zoneid != crgetzoneid(credp)) { 146810616SSebastien.Roy@Sun.COM iptun_exit(iptun); 146910616SSebastien.Roy@Sun.COM return (EACCES); 147010616SSebastien.Roy@Sun.COM } 147110616SSebastien.Roy@Sun.COM 147210616SSebastien.Roy@Sun.COM /* 147310616SSebastien.Roy@Sun.COM * We need to exit iptun in order to issue calls up the stack such as 147410616SSebastien.Roy@Sun.COM * dls_devnet_destroy(). If we call up while still in iptun, deadlock 147510616SSebastien.Roy@Sun.COM * with calls coming down the stack is possible. We prevent other 147610616SSebastien.Roy@Sun.COM * threads from entering this iptun after we've exited it by setting 147710616SSebastien.Roy@Sun.COM * the IPTUN_DELETE_PENDING flag. This will cause callers of 147810616SSebastien.Roy@Sun.COM * iptun_enter() to block waiting on iptun_enter_cv. The assumption 147910616SSebastien.Roy@Sun.COM * here is that the functions we're calling while IPTUN_DELETE_PENDING 148010616SSebastien.Roy@Sun.COM * is set dont resuult in an iptun_enter() call, as that would result 148110616SSebastien.Roy@Sun.COM * in deadlock. 148210616SSebastien.Roy@Sun.COM */ 148310616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_DELETE_PENDING; 148410616SSebastien.Roy@Sun.COM 148510616SSebastien.Roy@Sun.COM /* Wait for any pending upcall to the mac module to complete. */ 148610616SSebastien.Roy@Sun.COM while (iptun->iptun_flags & IPTUN_UPCALL_PENDING) 148710616SSebastien.Roy@Sun.COM cv_wait(&iptun->iptun_upcall_cv, &iptun->iptun_lock); 148810616SSebastien.Roy@Sun.COM 148910616SSebastien.Roy@Sun.COM iptun_exit(iptun); 149010616SSebastien.Roy@Sun.COM 149110616SSebastien.Roy@Sun.COM if ((err = dls_devnet_destroy(iptun->iptun_mh, &linkid, B_TRUE)) == 0) { 149210616SSebastien.Roy@Sun.COM /* 149310616SSebastien.Roy@Sun.COM * mac_disable() will fail with EBUSY if there are references 149410616SSebastien.Roy@Sun.COM * to the iptun MAC. If there are none, then mac_disable() 149510616SSebastien.Roy@Sun.COM * will assure that none can be acquired until the MAC is 149610616SSebastien.Roy@Sun.COM * unregistered. 149710616SSebastien.Roy@Sun.COM * 149810616SSebastien.Roy@Sun.COM * XXX CR 6791335 prevents us from calling mac_disable() prior 149910616SSebastien.Roy@Sun.COM * to dls_devnet_destroy(), so we unfortunately need to 150010616SSebastien.Roy@Sun.COM * attempt to re-create the devnet node if mac_disable() 150110616SSebastien.Roy@Sun.COM * fails. 150210616SSebastien.Roy@Sun.COM */ 150310616SSebastien.Roy@Sun.COM if ((err = mac_disable(iptun->iptun_mh)) != 0) { 150410616SSebastien.Roy@Sun.COM (void) dls_devnet_create(iptun->iptun_mh, linkid, 150510616SSebastien.Roy@Sun.COM iptun->iptun_zoneid); 150610616SSebastien.Roy@Sun.COM } 150710616SSebastien.Roy@Sun.COM } 150810616SSebastien.Roy@Sun.COM 150910616SSebastien.Roy@Sun.COM /* 151010616SSebastien.Roy@Sun.COM * Now that we know the fate of this iptun_t, we need to clear 151110616SSebastien.Roy@Sun.COM * IPTUN_DELETE_PENDING, and set IPTUN_CONDEMNED if the iptun_t is 151210616SSebastien.Roy@Sun.COM * slated to be freed. Either way, we need to signal the threads 151310616SSebastien.Roy@Sun.COM * waiting in iptun_enter() so that they can either fail if 151410616SSebastien.Roy@Sun.COM * IPTUN_CONDEMNED is set, or continue if it's not. 151510616SSebastien.Roy@Sun.COM */ 151610616SSebastien.Roy@Sun.COM mutex_enter(&iptun->iptun_lock); 151710616SSebastien.Roy@Sun.COM iptun->iptun_flags &= ~IPTUN_DELETE_PENDING; 151810616SSebastien.Roy@Sun.COM if (err == 0) 151910616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_CONDEMNED; 152010616SSebastien.Roy@Sun.COM cv_broadcast(&iptun->iptun_enter_cv); 152110616SSebastien.Roy@Sun.COM mutex_exit(&iptun->iptun_lock); 152210616SSebastien.Roy@Sun.COM 152310616SSebastien.Roy@Sun.COM /* 152410616SSebastien.Roy@Sun.COM * Note that there is no danger in calling iptun_free() after having 152510616SSebastien.Roy@Sun.COM * dropped the iptun_lock since callers of iptun_enter() at this point 152610616SSebastien.Roy@Sun.COM * are doing so from iptun_enter_by_linkid() (mac_disable() got rid of 152710616SSebastien.Roy@Sun.COM * threads entering from mac callbacks which call iptun_enter() 152810616SSebastien.Roy@Sun.COM * directly) which holds iptun_hash_lock, and iptun_free() grabs this 152910616SSebastien.Roy@Sun.COM * lock in order to remove the iptun_t from the hash table. 153010616SSebastien.Roy@Sun.COM */ 153110616SSebastien.Roy@Sun.COM if (err == 0) 153210616SSebastien.Roy@Sun.COM iptun_free(iptun); 153310616SSebastien.Roy@Sun.COM 153410616SSebastien.Roy@Sun.COM return (err); 153510616SSebastien.Roy@Sun.COM } 153610616SSebastien.Roy@Sun.COM 153710616SSebastien.Roy@Sun.COM int 153810616SSebastien.Roy@Sun.COM iptun_modify(const iptun_kparams_t *ik, cred_t *credp) 153910616SSebastien.Roy@Sun.COM { 154010616SSebastien.Roy@Sun.COM iptun_t *iptun; 154110616SSebastien.Roy@Sun.COM boolean_t laddr_change = B_FALSE, raddr_change = B_FALSE; 154210616SSebastien.Roy@Sun.COM int err; 154310616SSebastien.Roy@Sun.COM 154410616SSebastien.Roy@Sun.COM if ((err = iptun_enter_by_linkid(ik->iptun_kparam_linkid, &iptun)) != 0) 154510616SSebastien.Roy@Sun.COM return (err); 154610616SSebastien.Roy@Sun.COM 154710616SSebastien.Roy@Sun.COM /* One cannot modify a tunnel that belongs to another zone. */ 154810616SSebastien.Roy@Sun.COM if (iptun->iptun_zoneid != crgetzoneid(credp)) { 154910616SSebastien.Roy@Sun.COM err = EACCES; 155010616SSebastien.Roy@Sun.COM goto done; 155110616SSebastien.Roy@Sun.COM } 155210616SSebastien.Roy@Sun.COM 155310616SSebastien.Roy@Sun.COM /* The tunnel type cannot be changed */ 155410616SSebastien.Roy@Sun.COM if (ik->iptun_kparam_flags & IPTUN_KPARAM_TYPE) { 155510616SSebastien.Roy@Sun.COM err = EINVAL; 155610616SSebastien.Roy@Sun.COM goto done; 155710616SSebastien.Roy@Sun.COM } 155810616SSebastien.Roy@Sun.COM 155910616SSebastien.Roy@Sun.COM if ((err = iptun_setparams(iptun, ik)) != 0) 156010616SSebastien.Roy@Sun.COM goto done; 156110616SSebastien.Roy@Sun.COM iptun_headergen(iptun, B_FALSE); 156210616SSebastien.Roy@Sun.COM 156310616SSebastien.Roy@Sun.COM /* 156410616SSebastien.Roy@Sun.COM * If any of the tunnel's addresses has been modified and the tunnel 156510616SSebastien.Roy@Sun.COM * has the necessary addresses assigned to it, we need to try to bind 156610616SSebastien.Roy@Sun.COM * with ip underneath us. If we're not ready to bind yet, then we'll 156710616SSebastien.Roy@Sun.COM * try again when the addresses are modified later. 156810616SSebastien.Roy@Sun.COM */ 156910616SSebastien.Roy@Sun.COM laddr_change = (ik->iptun_kparam_flags & IPTUN_KPARAM_LADDR); 157010616SSebastien.Roy@Sun.COM raddr_change = (ik->iptun_kparam_flags & IPTUN_KPARAM_RADDR); 157110616SSebastien.Roy@Sun.COM if (laddr_change || raddr_change) { 157210616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_BOUND) 157310616SSebastien.Roy@Sun.COM iptun_unbind(iptun); 157410616SSebastien.Roy@Sun.COM if (iptun_canbind(iptun) && (err = iptun_bind(iptun)) != 0) { 157510616SSebastien.Roy@Sun.COM if (laddr_change) 157610616SSebastien.Roy@Sun.COM iptun->iptun_flags &= ~IPTUN_LADDR; 157710616SSebastien.Roy@Sun.COM if (raddr_change) 157810616SSebastien.Roy@Sun.COM iptun->iptun_flags &= ~IPTUN_RADDR; 157910616SSebastien.Roy@Sun.COM goto done; 158010616SSebastien.Roy@Sun.COM } 158110616SSebastien.Roy@Sun.COM } 158210616SSebastien.Roy@Sun.COM 158310616SSebastien.Roy@Sun.COM if (laddr_change) 158410616SSebastien.Roy@Sun.COM iptun_task_dispatch(iptun, IPTUN_TASK_LADDR_UPDATE); 158510616SSebastien.Roy@Sun.COM if (raddr_change) 158610616SSebastien.Roy@Sun.COM iptun_task_dispatch(iptun, IPTUN_TASK_RADDR_UPDATE); 158710616SSebastien.Roy@Sun.COM 158810616SSebastien.Roy@Sun.COM done: 158910616SSebastien.Roy@Sun.COM iptun_exit(iptun); 159010616SSebastien.Roy@Sun.COM return (err); 159110616SSebastien.Roy@Sun.COM } 159210616SSebastien.Roy@Sun.COM 159310616SSebastien.Roy@Sun.COM /* Given an IP tunnel's datalink id, fill in its parameters. */ 159410616SSebastien.Roy@Sun.COM int 159510616SSebastien.Roy@Sun.COM iptun_info(iptun_kparams_t *ik, cred_t *credp) 159610616SSebastien.Roy@Sun.COM { 159710616SSebastien.Roy@Sun.COM iptun_t *iptun; 159810616SSebastien.Roy@Sun.COM int err; 159910616SSebastien.Roy@Sun.COM 160010616SSebastien.Roy@Sun.COM /* Is the tunnel link visible from the caller's zone? */ 160110616SSebastien.Roy@Sun.COM if (!dls_devnet_islinkvisible(ik->iptun_kparam_linkid, 160210616SSebastien.Roy@Sun.COM crgetzoneid(credp))) 160310616SSebastien.Roy@Sun.COM return (ENOENT); 160410616SSebastien.Roy@Sun.COM 160510616SSebastien.Roy@Sun.COM if ((err = iptun_enter_by_linkid(ik->iptun_kparam_linkid, &iptun)) != 0) 160610616SSebastien.Roy@Sun.COM return (err); 160710616SSebastien.Roy@Sun.COM 160810616SSebastien.Roy@Sun.COM bzero(ik, sizeof (iptun_kparams_t)); 160910616SSebastien.Roy@Sun.COM 161010616SSebastien.Roy@Sun.COM ik->iptun_kparam_linkid = iptun->iptun_linkid; 161110616SSebastien.Roy@Sun.COM ik->iptun_kparam_type = iptun->iptun_typeinfo->iti_type; 161210616SSebastien.Roy@Sun.COM ik->iptun_kparam_flags |= IPTUN_KPARAM_TYPE; 161310616SSebastien.Roy@Sun.COM 161410616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_LADDR) { 161510616SSebastien.Roy@Sun.COM iptun_getaddr(&iptun->iptun_laddr, &ik->iptun_kparam_laddr); 161610616SSebastien.Roy@Sun.COM ik->iptun_kparam_flags |= IPTUN_KPARAM_LADDR; 161710616SSebastien.Roy@Sun.COM } 161810616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_RADDR) { 161910616SSebastien.Roy@Sun.COM iptun_getaddr(&iptun->iptun_raddr, &ik->iptun_kparam_raddr); 162010616SSebastien.Roy@Sun.COM ik->iptun_kparam_flags |= IPTUN_KPARAM_RADDR; 162110616SSebastien.Roy@Sun.COM } 162210616SSebastien.Roy@Sun.COM 162310616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_IMPLICIT) 162410616SSebastien.Roy@Sun.COM ik->iptun_kparam_flags |= IPTUN_KPARAM_IMPLICIT; 162510616SSebastien.Roy@Sun.COM 162610616SSebastien.Roy@Sun.COM if (iptun->iptun_itp != NULL) { 162710616SSebastien.Roy@Sun.COM mutex_enter(&iptun->iptun_itp->itp_lock); 162810616SSebastien.Roy@Sun.COM if (iptun->iptun_itp->itp_flags & ITPF_P_ACTIVE) { 162910616SSebastien.Roy@Sun.COM ik->iptun_kparam_flags |= IPTUN_KPARAM_IPSECPOL; 163010616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_SIMPLE_POLICY) { 163110616SSebastien.Roy@Sun.COM ik->iptun_kparam_flags |= IPTUN_KPARAM_SECINFO; 163210616SSebastien.Roy@Sun.COM ik->iptun_kparam_secinfo = 163310616SSebastien.Roy@Sun.COM iptun->iptun_simple_policy; 163410616SSebastien.Roy@Sun.COM } 163510616SSebastien.Roy@Sun.COM } 163610616SSebastien.Roy@Sun.COM mutex_exit(&iptun->iptun_itp->itp_lock); 163710616SSebastien.Roy@Sun.COM } 163810616SSebastien.Roy@Sun.COM 163910616SSebastien.Roy@Sun.COM done: 164010616SSebastien.Roy@Sun.COM iptun_exit(iptun); 164110616SSebastien.Roy@Sun.COM return (err); 164210616SSebastien.Roy@Sun.COM } 164310616SSebastien.Roy@Sun.COM 164410616SSebastien.Roy@Sun.COM int 164510616SSebastien.Roy@Sun.COM iptun_set_6to4relay(netstack_t *ns, ipaddr_t relay_addr) 164610616SSebastien.Roy@Sun.COM { 164710616SSebastien.Roy@Sun.COM if (relay_addr == INADDR_BROADCAST || CLASSD(relay_addr)) 164810616SSebastien.Roy@Sun.COM return (EADDRNOTAVAIL); 164910616SSebastien.Roy@Sun.COM ns->netstack_iptun->iptuns_relay_rtr_addr = relay_addr; 165010616SSebastien.Roy@Sun.COM return (0); 165110616SSebastien.Roy@Sun.COM } 165210616SSebastien.Roy@Sun.COM 165310616SSebastien.Roy@Sun.COM void 165410616SSebastien.Roy@Sun.COM iptun_get_6to4relay(netstack_t *ns, ipaddr_t *relay_addr) 165510616SSebastien.Roy@Sun.COM { 165610616SSebastien.Roy@Sun.COM *relay_addr = ns->netstack_iptun->iptuns_relay_rtr_addr; 165710616SSebastien.Roy@Sun.COM } 165810616SSebastien.Roy@Sun.COM 165910616SSebastien.Roy@Sun.COM void 166010616SSebastien.Roy@Sun.COM iptun_set_policy(datalink_id_t linkid, ipsec_tun_pol_t *itp) 166110616SSebastien.Roy@Sun.COM { 166210616SSebastien.Roy@Sun.COM iptun_t *iptun; 166310616SSebastien.Roy@Sun.COM 166410616SSebastien.Roy@Sun.COM if (iptun_enter_by_linkid(linkid, &iptun) != 0) 166510616SSebastien.Roy@Sun.COM return; 166610616SSebastien.Roy@Sun.COM if (iptun->iptun_itp != itp) { 166710616SSebastien.Roy@Sun.COM ASSERT(iptun->iptun_itp == NULL); 166810616SSebastien.Roy@Sun.COM ITP_REFHOLD(itp); 166910616SSebastien.Roy@Sun.COM iptun->iptun_itp = itp; 167010616SSebastien.Roy@Sun.COM /* IPsec policy means IPsec overhead, which means lower MTU. */ 167110616SSebastien.Roy@Sun.COM (void) iptun_update_mtu(iptun, 0); 167210616SSebastien.Roy@Sun.COM } 167310616SSebastien.Roy@Sun.COM iptun_exit(iptun); 167410616SSebastien.Roy@Sun.COM } 167510616SSebastien.Roy@Sun.COM 167610616SSebastien.Roy@Sun.COM /* 167710616SSebastien.Roy@Sun.COM * Obtain the path MTU to the tunnel destination. 167810616SSebastien.Roy@Sun.COM */ 167910616SSebastien.Roy@Sun.COM static uint32_t 168010616SSebastien.Roy@Sun.COM iptun_get_dst_pmtu(iptun_t *iptun) 168110616SSebastien.Roy@Sun.COM { 168210616SSebastien.Roy@Sun.COM ire_t *ire = NULL; 168310616SSebastien.Roy@Sun.COM ip_stack_t *ipst = iptun->iptun_ns->netstack_ip; 168410616SSebastien.Roy@Sun.COM uint32_t pmtu = 0; 168510616SSebastien.Roy@Sun.COM 168610616SSebastien.Roy@Sun.COM /* 168710616SSebastien.Roy@Sun.COM * We only obtain the destination IRE for tunnels that have a remote 168810616SSebastien.Roy@Sun.COM * tunnel address. 168910616SSebastien.Roy@Sun.COM */ 169010616SSebastien.Roy@Sun.COM if (!(iptun->iptun_flags & IPTUN_RADDR)) 169110616SSebastien.Roy@Sun.COM return (0); 169210616SSebastien.Roy@Sun.COM 169310616SSebastien.Roy@Sun.COM switch (iptun->iptun_typeinfo->iti_ipvers) { 169410616SSebastien.Roy@Sun.COM case IPV4_VERSION: 169510616SSebastien.Roy@Sun.COM ire = ire_route_lookup(iptun->iptun_raddr4, INADDR_ANY, 169610616SSebastien.Roy@Sun.COM INADDR_ANY, 0, NULL, NULL, iptun->iptun_connp->conn_zoneid, 169710616SSebastien.Roy@Sun.COM NULL, (MATCH_IRE_RECURSIVE | MATCH_IRE_DEFAULT), ipst); 169810616SSebastien.Roy@Sun.COM break; 169910616SSebastien.Roy@Sun.COM case IPV6_VERSION: 170010616SSebastien.Roy@Sun.COM ire = ire_route_lookup_v6(&iptun->iptun_raddr6, NULL, NULL, 0, 170110616SSebastien.Roy@Sun.COM NULL, NULL, iptun->iptun_connp->conn_zoneid, NULL, 170210616SSebastien.Roy@Sun.COM (MATCH_IRE_RECURSIVE | MATCH_IRE_DEFAULT), ipst); 170310616SSebastien.Roy@Sun.COM break; 170410616SSebastien.Roy@Sun.COM } 170510616SSebastien.Roy@Sun.COM 170610616SSebastien.Roy@Sun.COM if (ire != NULL) { 170710616SSebastien.Roy@Sun.COM pmtu = ire->ire_max_frag; 170810616SSebastien.Roy@Sun.COM ire_refrele(ire); 170910616SSebastien.Roy@Sun.COM } 171010616SSebastien.Roy@Sun.COM return (pmtu); 171110616SSebastien.Roy@Sun.COM } 171210616SSebastien.Roy@Sun.COM 171310616SSebastien.Roy@Sun.COM /* 171410616SSebastien.Roy@Sun.COM * Returns the max of old_ovhd and the overhead associated with pol. 171510616SSebastien.Roy@Sun.COM */ 171610616SSebastien.Roy@Sun.COM static uint32_t 171710616SSebastien.Roy@Sun.COM iptun_max_policy_overhead(ipsec_policy_t *pol, uint32_t old_ovhd) 171810616SSebastien.Roy@Sun.COM { 171910616SSebastien.Roy@Sun.COM uint32_t new_ovhd = old_ovhd; 172010616SSebastien.Roy@Sun.COM 172110616SSebastien.Roy@Sun.COM while (pol != NULL) { 172210616SSebastien.Roy@Sun.COM new_ovhd = max(new_ovhd, 172310616SSebastien.Roy@Sun.COM ipsec_act_ovhd(&pol->ipsp_act->ipa_act)); 172410616SSebastien.Roy@Sun.COM pol = pol->ipsp_hash.hash_next; 172510616SSebastien.Roy@Sun.COM } 172610616SSebastien.Roy@Sun.COM return (new_ovhd); 172710616SSebastien.Roy@Sun.COM } 172810616SSebastien.Roy@Sun.COM 172910616SSebastien.Roy@Sun.COM static uint32_t 173010616SSebastien.Roy@Sun.COM iptun_get_ipsec_overhead(iptun_t *iptun) 173110616SSebastien.Roy@Sun.COM { 173210616SSebastien.Roy@Sun.COM ipsec_policy_root_t *ipr; 173310616SSebastien.Roy@Sun.COM ipsec_policy_head_t *iph; 173410616SSebastien.Roy@Sun.COM ipsec_policy_t *pol; 173510616SSebastien.Roy@Sun.COM ipsec_selector_t sel; 173610616SSebastien.Roy@Sun.COM int i; 173710616SSebastien.Roy@Sun.COM uint32_t ipsec_ovhd = 0; 173810616SSebastien.Roy@Sun.COM ipsec_tun_pol_t *itp = iptun->iptun_itp; 173910616SSebastien.Roy@Sun.COM netstack_t *ns = iptun->iptun_ns; 174010616SSebastien.Roy@Sun.COM 174110616SSebastien.Roy@Sun.COM if (itp == NULL || !(itp->itp_flags & ITPF_P_ACTIVE)) { 174210616SSebastien.Roy@Sun.COM /* 174310616SSebastien.Roy@Sun.COM * Consult global policy, just in case. This will only work 174410616SSebastien.Roy@Sun.COM * if we have both source and destination addresses to work 174510616SSebastien.Roy@Sun.COM * with. 174610616SSebastien.Roy@Sun.COM */ 174710616SSebastien.Roy@Sun.COM if ((iptun->iptun_flags & (IPTUN_LADDR|IPTUN_RADDR)) != 174810616SSebastien.Roy@Sun.COM (IPTUN_LADDR|IPTUN_RADDR)) 174910616SSebastien.Roy@Sun.COM return (0); 175010616SSebastien.Roy@Sun.COM 175110616SSebastien.Roy@Sun.COM iph = ipsec_system_policy(ns); 175210616SSebastien.Roy@Sun.COM bzero(&sel, sizeof (sel)); 175310616SSebastien.Roy@Sun.COM sel.ips_isv4 = 175410616SSebastien.Roy@Sun.COM (iptun->iptun_typeinfo->iti_ipvers == IPV4_VERSION); 175510616SSebastien.Roy@Sun.COM switch (iptun->iptun_typeinfo->iti_ipvers) { 175610616SSebastien.Roy@Sun.COM case IPV4_VERSION: 175710616SSebastien.Roy@Sun.COM sel.ips_local_addr_v4 = iptun->iptun_laddr4; 175810616SSebastien.Roy@Sun.COM sel.ips_remote_addr_v4 = iptun->iptun_raddr4; 175910616SSebastien.Roy@Sun.COM break; 176010616SSebastien.Roy@Sun.COM case IPV6_VERSION: 176110616SSebastien.Roy@Sun.COM sel.ips_local_addr_v6 = iptun->iptun_laddr6; 176210616SSebastien.Roy@Sun.COM sel.ips_remote_addr_v6 = iptun->iptun_raddr6; 176310616SSebastien.Roy@Sun.COM break; 176410616SSebastien.Roy@Sun.COM } 176510616SSebastien.Roy@Sun.COM /* Check for both IPv4 and IPv6. */ 176610616SSebastien.Roy@Sun.COM sel.ips_protocol = IPPROTO_ENCAP; 176710616SSebastien.Roy@Sun.COM pol = ipsec_find_policy_head(NULL, iph, IPSEC_TYPE_OUTBOUND, 176810616SSebastien.Roy@Sun.COM &sel, ns); 176910616SSebastien.Roy@Sun.COM if (pol != NULL) { 177010616SSebastien.Roy@Sun.COM ipsec_ovhd = ipsec_act_ovhd(&pol->ipsp_act->ipa_act); 177110616SSebastien.Roy@Sun.COM IPPOL_REFRELE(pol, ns); 177210616SSebastien.Roy@Sun.COM } 177310616SSebastien.Roy@Sun.COM sel.ips_protocol = IPPROTO_IPV6; 177410616SSebastien.Roy@Sun.COM pol = ipsec_find_policy_head(NULL, iph, IPSEC_TYPE_OUTBOUND, 177510616SSebastien.Roy@Sun.COM &sel, ns); 177610616SSebastien.Roy@Sun.COM if (pol != NULL) { 177710616SSebastien.Roy@Sun.COM ipsec_ovhd = max(ipsec_ovhd, 177810616SSebastien.Roy@Sun.COM ipsec_act_ovhd(&pol->ipsp_act->ipa_act)); 177910616SSebastien.Roy@Sun.COM IPPOL_REFRELE(pol, ns); 178010616SSebastien.Roy@Sun.COM } 178110616SSebastien.Roy@Sun.COM IPPH_REFRELE(iph, ns); 178210616SSebastien.Roy@Sun.COM } else { 178310616SSebastien.Roy@Sun.COM /* 178410616SSebastien.Roy@Sun.COM * Look through all of the possible IPsec actions for the 178510616SSebastien.Roy@Sun.COM * tunnel, and find the largest potential IPsec overhead. 178610616SSebastien.Roy@Sun.COM */ 178710616SSebastien.Roy@Sun.COM iph = itp->itp_policy; 178810616SSebastien.Roy@Sun.COM rw_enter(&iph->iph_lock, RW_READER); 178910616SSebastien.Roy@Sun.COM ipr = &(iph->iph_root[IPSEC_TYPE_OUTBOUND]); 179010616SSebastien.Roy@Sun.COM ipsec_ovhd = iptun_max_policy_overhead( 179110616SSebastien.Roy@Sun.COM ipr->ipr_nonhash[IPSEC_AF_V4], 0); 179210616SSebastien.Roy@Sun.COM ipsec_ovhd = iptun_max_policy_overhead( 179310616SSebastien.Roy@Sun.COM ipr->ipr_nonhash[IPSEC_AF_V6], ipsec_ovhd); 179410616SSebastien.Roy@Sun.COM for (i = 0; i < ipr->ipr_nchains; i++) { 179510616SSebastien.Roy@Sun.COM ipsec_ovhd = iptun_max_policy_overhead( 179610616SSebastien.Roy@Sun.COM ipr->ipr_hash[i].hash_head, ipsec_ovhd); 179710616SSebastien.Roy@Sun.COM } 179810616SSebastien.Roy@Sun.COM rw_exit(&iph->iph_lock); 179910616SSebastien.Roy@Sun.COM } 180010616SSebastien.Roy@Sun.COM 180110616SSebastien.Roy@Sun.COM return (ipsec_ovhd); 180210616SSebastien.Roy@Sun.COM } 180310616SSebastien.Roy@Sun.COM 180410616SSebastien.Roy@Sun.COM /* 180510616SSebastien.Roy@Sun.COM * Calculate and return the maximum possible MTU for the given tunnel. 180610616SSebastien.Roy@Sun.COM */ 180710616SSebastien.Roy@Sun.COM static uint32_t 180810616SSebastien.Roy@Sun.COM iptun_get_maxmtu(iptun_t *iptun, uint32_t new_pmtu) 180910616SSebastien.Roy@Sun.COM { 181010616SSebastien.Roy@Sun.COM size_t header_size, ipsec_overhead; 181110616SSebastien.Roy@Sun.COM uint32_t maxmtu, pmtu; 181210616SSebastien.Roy@Sun.COM 181310616SSebastien.Roy@Sun.COM /* 181410616SSebastien.Roy@Sun.COM * Start with the path-MTU to the remote address, which is either 181510616SSebastien.Roy@Sun.COM * provided as the new_pmtu argument, or obtained using 181610616SSebastien.Roy@Sun.COM * iptun_get_dst_pmtu(). 181710616SSebastien.Roy@Sun.COM */ 181810616SSebastien.Roy@Sun.COM if (new_pmtu != 0) { 181910616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_RADDR) { 182010616SSebastien.Roy@Sun.COM iptun->iptun_dpmtu = new_pmtu; 182110616SSebastien.Roy@Sun.COM iptun->iptun_dpmtu_lastupdate = ddi_get_lbolt(); 182210616SSebastien.Roy@Sun.COM } 182310616SSebastien.Roy@Sun.COM pmtu = new_pmtu; 182410616SSebastien.Roy@Sun.COM } else if (iptun->iptun_flags & IPTUN_RADDR) { 182510616SSebastien.Roy@Sun.COM if ((pmtu = iptun_get_dst_pmtu(iptun)) == 0) { 182610616SSebastien.Roy@Sun.COM /* 182710616SSebastien.Roy@Sun.COM * We weren't able to obtain the path-MTU of the 182810616SSebastien.Roy@Sun.COM * destination. Use the previous value. 182910616SSebastien.Roy@Sun.COM */ 183010616SSebastien.Roy@Sun.COM pmtu = iptun->iptun_dpmtu; 183110616SSebastien.Roy@Sun.COM } else { 183210616SSebastien.Roy@Sun.COM iptun->iptun_dpmtu = pmtu; 183310616SSebastien.Roy@Sun.COM iptun->iptun_dpmtu_lastupdate = ddi_get_lbolt(); 183410616SSebastien.Roy@Sun.COM } 183510616SSebastien.Roy@Sun.COM } else { 183610616SSebastien.Roy@Sun.COM /* 183710616SSebastien.Roy@Sun.COM * We have no path-MTU information to go on, use the maximum 183810616SSebastien.Roy@Sun.COM * possible value. 183910616SSebastien.Roy@Sun.COM */ 184010616SSebastien.Roy@Sun.COM pmtu = iptun->iptun_typeinfo->iti_maxmtu; 184110616SSebastien.Roy@Sun.COM } 184210616SSebastien.Roy@Sun.COM 184310616SSebastien.Roy@Sun.COM /* 184410616SSebastien.Roy@Sun.COM * Now calculate tunneling overhead and subtract that from the 184510616SSebastien.Roy@Sun.COM * path-MTU information obtained above. 184610616SSebastien.Roy@Sun.COM */ 184710616SSebastien.Roy@Sun.COM if (iptun->iptun_header_size != 0) { 184810616SSebastien.Roy@Sun.COM header_size = iptun->iptun_header_size; 184910616SSebastien.Roy@Sun.COM } else { 185010616SSebastien.Roy@Sun.COM switch (iptun->iptun_typeinfo->iti_ipvers) { 185110616SSebastien.Roy@Sun.COM case IPV4_VERSION: 185210616SSebastien.Roy@Sun.COM header_size = sizeof (ipha_t); 185310616SSebastien.Roy@Sun.COM break; 185410616SSebastien.Roy@Sun.COM case IPV6_VERSION: 185510616SSebastien.Roy@Sun.COM header_size = sizeof (iptun_ipv6hdrs_t); 185610616SSebastien.Roy@Sun.COM break; 185710616SSebastien.Roy@Sun.COM } 185810616SSebastien.Roy@Sun.COM } 185910616SSebastien.Roy@Sun.COM 186010616SSebastien.Roy@Sun.COM ipsec_overhead = iptun_get_ipsec_overhead(iptun); 186110616SSebastien.Roy@Sun.COM 186210616SSebastien.Roy@Sun.COM maxmtu = pmtu - (header_size + ipsec_overhead); 186310616SSebastien.Roy@Sun.COM return (max(maxmtu, iptun->iptun_typeinfo->iti_minmtu)); 186410616SSebastien.Roy@Sun.COM } 186510616SSebastien.Roy@Sun.COM 186610616SSebastien.Roy@Sun.COM /* 186710616SSebastien.Roy@Sun.COM * Re-calculate the tunnel's MTU and notify the MAC layer of any change in 186810616SSebastien.Roy@Sun.COM * MTU. The new_pmtu argument is the new path MTU to the tunnel destination 186910616SSebastien.Roy@Sun.COM * to be used in the tunnel MTU calculation. Passing in 0 for new_pmtu causes 187010616SSebastien.Roy@Sun.COM * the path MTU to be dynamically updated using iptun_update_pmtu(). 187110616SSebastien.Roy@Sun.COM * 187210616SSebastien.Roy@Sun.COM * If the calculated tunnel MTU is different than its previous value, then we 187310616SSebastien.Roy@Sun.COM * notify the MAC layer above us of this change using mac_maxsdu_update(). 187410616SSebastien.Roy@Sun.COM */ 187510616SSebastien.Roy@Sun.COM static uint32_t 187610616SSebastien.Roy@Sun.COM iptun_update_mtu(iptun_t *iptun, uint32_t new_pmtu) 187710616SSebastien.Roy@Sun.COM { 187810616SSebastien.Roy@Sun.COM uint32_t newmtu; 187910616SSebastien.Roy@Sun.COM 188010616SSebastien.Roy@Sun.COM /* 188110616SSebastien.Roy@Sun.COM * We return the current MTU without updating it if it was pegged to a 188210616SSebastien.Roy@Sun.COM * static value using the MAC_PROP_MTU link property. 188310616SSebastien.Roy@Sun.COM */ 188410616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_FIXED_MTU) 188510616SSebastien.Roy@Sun.COM return (iptun->iptun_mtu); 188610616SSebastien.Roy@Sun.COM 188710616SSebastien.Roy@Sun.COM /* If the MTU isn't fixed, then use the maximum possible value. */ 188810616SSebastien.Roy@Sun.COM newmtu = iptun_get_maxmtu(iptun, new_pmtu); 188910616SSebastien.Roy@Sun.COM 189010616SSebastien.Roy@Sun.COM /* 189110616SSebastien.Roy@Sun.COM * We only dynamically adjust the tunnel MTU for tunnels with 189210616SSebastien.Roy@Sun.COM * destinations because dynamic MTU calculations are based on the 189310616SSebastien.Roy@Sun.COM * destination path-MTU. 189410616SSebastien.Roy@Sun.COM */ 189510616SSebastien.Roy@Sun.COM if ((iptun->iptun_flags & IPTUN_RADDR) && newmtu != iptun->iptun_mtu) { 189610616SSebastien.Roy@Sun.COM iptun->iptun_mtu = newmtu; 189710616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_MAC_REGISTERED) 189810616SSebastien.Roy@Sun.COM iptun_task_dispatch(iptun, IPTUN_TASK_MTU_UPDATE); 189910616SSebastien.Roy@Sun.COM } 190010616SSebastien.Roy@Sun.COM 190110616SSebastien.Roy@Sun.COM return (newmtu); 190210616SSebastien.Roy@Sun.COM } 190310616SSebastien.Roy@Sun.COM 190410616SSebastien.Roy@Sun.COM /* 190510616SSebastien.Roy@Sun.COM * Frees a packet or packet chain and bumps stat for each freed packet. 190610616SSebastien.Roy@Sun.COM */ 190710616SSebastien.Roy@Sun.COM static void 190810616SSebastien.Roy@Sun.COM iptun_drop_pkt(mblk_t *mp, uint64_t *stat) 190910616SSebastien.Roy@Sun.COM { 191010616SSebastien.Roy@Sun.COM mblk_t *pktmp; 191110616SSebastien.Roy@Sun.COM 191210616SSebastien.Roy@Sun.COM for (pktmp = mp; pktmp != NULL; pktmp = mp) { 191310616SSebastien.Roy@Sun.COM mp = mp->b_next; 191410616SSebastien.Roy@Sun.COM pktmp->b_next = NULL; 191510616SSebastien.Roy@Sun.COM if (stat != NULL) 191610616SSebastien.Roy@Sun.COM atomic_inc_64(stat); 191710616SSebastien.Roy@Sun.COM freemsg(pktmp); 191810616SSebastien.Roy@Sun.COM } 191910616SSebastien.Roy@Sun.COM } 192010616SSebastien.Roy@Sun.COM 192110616SSebastien.Roy@Sun.COM /* 192210616SSebastien.Roy@Sun.COM * Allocate and return a new mblk to hold an IP and ICMP header, and chain the 192310616SSebastien.Roy@Sun.COM * original packet to its b_cont. Returns NULL on failure. 192410616SSebastien.Roy@Sun.COM */ 192510616SSebastien.Roy@Sun.COM static mblk_t * 192610616SSebastien.Roy@Sun.COM iptun_build_icmperr(size_t hdrs_size, mblk_t *orig_pkt) 192710616SSebastien.Roy@Sun.COM { 192810616SSebastien.Roy@Sun.COM mblk_t *icmperr_mp; 192910616SSebastien.Roy@Sun.COM 193010616SSebastien.Roy@Sun.COM if ((icmperr_mp = allocb_tmpl(hdrs_size, orig_pkt)) != NULL) { 193110616SSebastien.Roy@Sun.COM icmperr_mp->b_wptr += hdrs_size; 193210616SSebastien.Roy@Sun.COM /* tack on the offending packet */ 193310616SSebastien.Roy@Sun.COM icmperr_mp->b_cont = orig_pkt; 193410616SSebastien.Roy@Sun.COM } 193510616SSebastien.Roy@Sun.COM return (icmperr_mp); 193610616SSebastien.Roy@Sun.COM } 193710616SSebastien.Roy@Sun.COM 193810616SSebastien.Roy@Sun.COM /* 193910616SSebastien.Roy@Sun.COM * Transmit an ICMP error. mp->b_rptr points at the packet to be included in 194010616SSebastien.Roy@Sun.COM * the ICMP error. 194110616SSebastien.Roy@Sun.COM */ 194210616SSebastien.Roy@Sun.COM static void 194310616SSebastien.Roy@Sun.COM iptun_sendicmp_v4(iptun_t *iptun, icmph_t *icmp, ipha_t *orig_ipha, mblk_t *mp) 194410616SSebastien.Roy@Sun.COM { 194510616SSebastien.Roy@Sun.COM size_t orig_pktsize, hdrs_size; 194610616SSebastien.Roy@Sun.COM mblk_t *icmperr_mp; 194710616SSebastien.Roy@Sun.COM ipha_t *new_ipha; 194810616SSebastien.Roy@Sun.COM icmph_t *new_icmp; 194910616SSebastien.Roy@Sun.COM 195010616SSebastien.Roy@Sun.COM orig_pktsize = msgdsize(mp); 195110616SSebastien.Roy@Sun.COM hdrs_size = sizeof (ipha_t) + sizeof (icmph_t); 195210616SSebastien.Roy@Sun.COM if ((icmperr_mp = iptun_build_icmperr(hdrs_size, mp)) == NULL) { 195310616SSebastien.Roy@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_noxmtbuf); 195410616SSebastien.Roy@Sun.COM return; 195510616SSebastien.Roy@Sun.COM } 195610616SSebastien.Roy@Sun.COM 195710616SSebastien.Roy@Sun.COM new_ipha = (ipha_t *)icmperr_mp->b_rptr; 195810616SSebastien.Roy@Sun.COM new_icmp = (icmph_t *)(new_ipha + 1); 195910616SSebastien.Roy@Sun.COM 196010616SSebastien.Roy@Sun.COM new_ipha->ipha_version_and_hdr_length = IP_SIMPLE_HDR_VERSION; 196110616SSebastien.Roy@Sun.COM new_ipha->ipha_type_of_service = 0; 196210616SSebastien.Roy@Sun.COM new_ipha->ipha_ident = 0; 196310616SSebastien.Roy@Sun.COM new_ipha->ipha_fragment_offset_and_flags = 0; 196410616SSebastien.Roy@Sun.COM new_ipha->ipha_ttl = orig_ipha->ipha_ttl; 196510616SSebastien.Roy@Sun.COM new_ipha->ipha_protocol = IPPROTO_ICMP; 196610616SSebastien.Roy@Sun.COM new_ipha->ipha_src = orig_ipha->ipha_dst; 196710616SSebastien.Roy@Sun.COM new_ipha->ipha_dst = orig_ipha->ipha_src; 196810616SSebastien.Roy@Sun.COM new_ipha->ipha_hdr_checksum = 0; /* will be computed by ip */ 196910616SSebastien.Roy@Sun.COM new_ipha->ipha_length = htons(hdrs_size + orig_pktsize); 197010616SSebastien.Roy@Sun.COM 197110616SSebastien.Roy@Sun.COM *new_icmp = *icmp; 197210616SSebastien.Roy@Sun.COM new_icmp->icmph_checksum = 0; 197310616SSebastien.Roy@Sun.COM new_icmp->icmph_checksum = IP_CSUM(icmperr_mp, sizeof (ipha_t), 0); 197410616SSebastien.Roy@Sun.COM 197510616SSebastien.Roy@Sun.COM ip_output(iptun->iptun_connp, icmperr_mp, iptun->iptun_connp->conn_wq, 197610616SSebastien.Roy@Sun.COM IP_WPUT); 197710616SSebastien.Roy@Sun.COM } 197810616SSebastien.Roy@Sun.COM 197910616SSebastien.Roy@Sun.COM static void 198010616SSebastien.Roy@Sun.COM iptun_sendicmp_v6(iptun_t *iptun, icmp6_t *icmp6, ip6_t *orig_ip6h, mblk_t *mp) 198110616SSebastien.Roy@Sun.COM { 198210616SSebastien.Roy@Sun.COM size_t orig_pktsize, hdrs_size; 198310616SSebastien.Roy@Sun.COM mblk_t *icmp6err_mp; 198410616SSebastien.Roy@Sun.COM ip6_t *new_ip6h; 198510616SSebastien.Roy@Sun.COM icmp6_t *new_icmp6; 198610616SSebastien.Roy@Sun.COM 198710616SSebastien.Roy@Sun.COM orig_pktsize = msgdsize(mp); 198810616SSebastien.Roy@Sun.COM hdrs_size = sizeof (ip6_t) + sizeof (icmp6_t); 198910616SSebastien.Roy@Sun.COM if ((icmp6err_mp = iptun_build_icmperr(hdrs_size, mp)) == NULL) { 199010616SSebastien.Roy@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_noxmtbuf); 199110616SSebastien.Roy@Sun.COM return; 199210616SSebastien.Roy@Sun.COM } 199310616SSebastien.Roy@Sun.COM 199410616SSebastien.Roy@Sun.COM new_ip6h = (ip6_t *)icmp6err_mp->b_rptr; 199510616SSebastien.Roy@Sun.COM new_icmp6 = (icmp6_t *)(new_ip6h + 1); 199610616SSebastien.Roy@Sun.COM 199710616SSebastien.Roy@Sun.COM new_ip6h->ip6_vcf = orig_ip6h->ip6_vcf; 199810616SSebastien.Roy@Sun.COM new_ip6h->ip6_plen = htons(sizeof (icmp6_t) + orig_pktsize); 199910616SSebastien.Roy@Sun.COM new_ip6h->ip6_hops = orig_ip6h->ip6_hops; 200010616SSebastien.Roy@Sun.COM new_ip6h->ip6_nxt = IPPROTO_ICMPV6; 200110616SSebastien.Roy@Sun.COM new_ip6h->ip6_src = orig_ip6h->ip6_dst; 200210616SSebastien.Roy@Sun.COM new_ip6h->ip6_dst = orig_ip6h->ip6_src; 200310616SSebastien.Roy@Sun.COM 200410616SSebastien.Roy@Sun.COM *new_icmp6 = *icmp6; 200510616SSebastien.Roy@Sun.COM /* The checksum is calculated in ip_wput_ire_v6(). */ 200610616SSebastien.Roy@Sun.COM new_icmp6->icmp6_cksum = new_ip6h->ip6_plen; 200710616SSebastien.Roy@Sun.COM 200810616SSebastien.Roy@Sun.COM ip_output_v6(iptun->iptun_connp, icmp6err_mp, 200910616SSebastien.Roy@Sun.COM iptun->iptun_connp->conn_wq, IP_WPUT); 201010616SSebastien.Roy@Sun.COM } 201110616SSebastien.Roy@Sun.COM 201210616SSebastien.Roy@Sun.COM static void 201310616SSebastien.Roy@Sun.COM iptun_icmp_error_v4(iptun_t *iptun, ipha_t *orig_ipha, mblk_t *mp, 201410616SSebastien.Roy@Sun.COM uint8_t type, uint8_t code) 201510616SSebastien.Roy@Sun.COM { 201610616SSebastien.Roy@Sun.COM icmph_t icmp; 201710616SSebastien.Roy@Sun.COM 201810616SSebastien.Roy@Sun.COM bzero(&icmp, sizeof (icmp)); 201910616SSebastien.Roy@Sun.COM icmp.icmph_type = type; 202010616SSebastien.Roy@Sun.COM icmp.icmph_code = code; 202110616SSebastien.Roy@Sun.COM 202210616SSebastien.Roy@Sun.COM iptun_sendicmp_v4(iptun, &icmp, orig_ipha, mp); 202310616SSebastien.Roy@Sun.COM } 202410616SSebastien.Roy@Sun.COM 202510616SSebastien.Roy@Sun.COM static void 202610616SSebastien.Roy@Sun.COM iptun_icmp_fragneeded_v4(iptun_t *iptun, uint32_t newmtu, ipha_t *orig_ipha, 202710616SSebastien.Roy@Sun.COM mblk_t *mp) 202810616SSebastien.Roy@Sun.COM { 202910616SSebastien.Roy@Sun.COM icmph_t icmp; 203010616SSebastien.Roy@Sun.COM 203110616SSebastien.Roy@Sun.COM icmp.icmph_type = ICMP_DEST_UNREACHABLE; 203210616SSebastien.Roy@Sun.COM icmp.icmph_code = ICMP_FRAGMENTATION_NEEDED; 203310616SSebastien.Roy@Sun.COM icmp.icmph_du_zero = 0; 203410616SSebastien.Roy@Sun.COM icmp.icmph_du_mtu = htons(newmtu); 203510616SSebastien.Roy@Sun.COM 203610616SSebastien.Roy@Sun.COM iptun_sendicmp_v4(iptun, &icmp, orig_ipha, mp); 203710616SSebastien.Roy@Sun.COM } 203810616SSebastien.Roy@Sun.COM 203910616SSebastien.Roy@Sun.COM static void 204010616SSebastien.Roy@Sun.COM iptun_icmp_error_v6(iptun_t *iptun, ip6_t *orig_ip6h, mblk_t *mp, 204110616SSebastien.Roy@Sun.COM uint8_t type, uint8_t code, uint32_t offset) 204210616SSebastien.Roy@Sun.COM { 204310616SSebastien.Roy@Sun.COM icmp6_t icmp6; 204410616SSebastien.Roy@Sun.COM 204510616SSebastien.Roy@Sun.COM bzero(&icmp6, sizeof (icmp6)); 204610616SSebastien.Roy@Sun.COM icmp6.icmp6_type = type; 204710616SSebastien.Roy@Sun.COM icmp6.icmp6_code = code; 204810616SSebastien.Roy@Sun.COM if (type == ICMP6_PARAM_PROB) 204910616SSebastien.Roy@Sun.COM icmp6.icmp6_pptr = htonl(offset); 205010616SSebastien.Roy@Sun.COM 205110616SSebastien.Roy@Sun.COM iptun_sendicmp_v6(iptun, &icmp6, orig_ip6h, mp); 205210616SSebastien.Roy@Sun.COM } 205310616SSebastien.Roy@Sun.COM 205410616SSebastien.Roy@Sun.COM static void 205510616SSebastien.Roy@Sun.COM iptun_icmp_toobig_v6(iptun_t *iptun, uint32_t newmtu, ip6_t *orig_ip6h, 205610616SSebastien.Roy@Sun.COM mblk_t *mp) 205710616SSebastien.Roy@Sun.COM { 205810616SSebastien.Roy@Sun.COM icmp6_t icmp6; 205910616SSebastien.Roy@Sun.COM 206010616SSebastien.Roy@Sun.COM icmp6.icmp6_type = ICMP6_PACKET_TOO_BIG; 206110616SSebastien.Roy@Sun.COM icmp6.icmp6_code = 0; 206210616SSebastien.Roy@Sun.COM icmp6.icmp6_mtu = htonl(newmtu); 206310616SSebastien.Roy@Sun.COM 206410616SSebastien.Roy@Sun.COM iptun_sendicmp_v6(iptun, &icmp6, orig_ip6h, mp); 206510616SSebastien.Roy@Sun.COM } 206610616SSebastien.Roy@Sun.COM 206710616SSebastien.Roy@Sun.COM /* 206810616SSebastien.Roy@Sun.COM * Determines if the packet pointed to by ipha or ip6h is an ICMP error. The 206910616SSebastien.Roy@Sun.COM * mp argument is only used to do bounds checking. 207010616SSebastien.Roy@Sun.COM */ 207110616SSebastien.Roy@Sun.COM static boolean_t 207210616SSebastien.Roy@Sun.COM is_icmp_error(mblk_t *mp, ipha_t *ipha, ip6_t *ip6h) 207310616SSebastien.Roy@Sun.COM { 207410616SSebastien.Roy@Sun.COM uint16_t hlen; 207510616SSebastien.Roy@Sun.COM 207610616SSebastien.Roy@Sun.COM if (ipha != NULL) { 207710616SSebastien.Roy@Sun.COM icmph_t *icmph; 207810616SSebastien.Roy@Sun.COM 207910616SSebastien.Roy@Sun.COM ASSERT(ip6h == NULL); 208010616SSebastien.Roy@Sun.COM if (ipha->ipha_protocol != IPPROTO_ICMP) 208110616SSebastien.Roy@Sun.COM return (B_FALSE); 208210616SSebastien.Roy@Sun.COM 208310616SSebastien.Roy@Sun.COM hlen = IPH_HDR_LENGTH(ipha); 208410616SSebastien.Roy@Sun.COM icmph = (icmph_t *)((uint8_t *)ipha + hlen); 208510616SSebastien.Roy@Sun.COM return (ICMP_IS_ERROR(icmph->icmph_type) || 208610616SSebastien.Roy@Sun.COM icmph->icmph_type == ICMP_REDIRECT); 208710616SSebastien.Roy@Sun.COM } else { 208810616SSebastien.Roy@Sun.COM icmp6_t *icmp6; 208910616SSebastien.Roy@Sun.COM uint8_t *nexthdrp; 209010616SSebastien.Roy@Sun.COM 209110616SSebastien.Roy@Sun.COM ASSERT(ip6h != NULL); 209210616SSebastien.Roy@Sun.COM if (!ip_hdr_length_nexthdr_v6(mp, ip6h, &hlen, &nexthdrp) || 209310616SSebastien.Roy@Sun.COM *nexthdrp != IPPROTO_ICMPV6) { 209410616SSebastien.Roy@Sun.COM return (B_FALSE); 209510616SSebastien.Roy@Sun.COM } 209610616SSebastien.Roy@Sun.COM 209710616SSebastien.Roy@Sun.COM icmp6 = (icmp6_t *)((uint8_t *)ip6h + hlen); 209810616SSebastien.Roy@Sun.COM return (ICMP6_IS_ERROR(icmp6->icmp6_type) || 209910616SSebastien.Roy@Sun.COM icmp6->icmp6_type == ND_REDIRECT); 210010616SSebastien.Roy@Sun.COM } 210110616SSebastien.Roy@Sun.COM } 210210616SSebastien.Roy@Sun.COM 210310616SSebastien.Roy@Sun.COM /* 210410616SSebastien.Roy@Sun.COM * Find inner and outer IP headers from a tunneled packet as setup for calls 210510616SSebastien.Roy@Sun.COM * into ipsec_tun_{in,out}bound(). 210610616SSebastien.Roy@Sun.COM */ 210710616SSebastien.Roy@Sun.COM static size_t 210810616SSebastien.Roy@Sun.COM iptun_find_headers(mblk_t *mp, ipha_t **outer4, ipha_t **inner4, ip6_t **outer6, 210910616SSebastien.Roy@Sun.COM ip6_t **inner6) 211010616SSebastien.Roy@Sun.COM { 211110616SSebastien.Roy@Sun.COM ipha_t *ipha; 211210616SSebastien.Roy@Sun.COM size_t outer_hlen; 211310616SSebastien.Roy@Sun.COM size_t first_mblkl = MBLKL(mp); 211410616SSebastien.Roy@Sun.COM mblk_t *inner_mp; 211510616SSebastien.Roy@Sun.COM 211610616SSebastien.Roy@Sun.COM /* 211710616SSebastien.Roy@Sun.COM * Don't bother handling packets that don't have a full IP header in 211810616SSebastien.Roy@Sun.COM * the fist mblk. For the input path, the ip module ensures that this 211910616SSebastien.Roy@Sun.COM * won't happen, and on the output path, the IP tunneling MAC-type 212010616SSebastien.Roy@Sun.COM * plugins ensure that this also won't happen. 212110616SSebastien.Roy@Sun.COM */ 212210616SSebastien.Roy@Sun.COM if (first_mblkl < sizeof (ipha_t)) 212310616SSebastien.Roy@Sun.COM return (0); 212410616SSebastien.Roy@Sun.COM ipha = (ipha_t *)(mp->b_rptr); 212510616SSebastien.Roy@Sun.COM switch (IPH_HDR_VERSION(ipha)) { 212610616SSebastien.Roy@Sun.COM case IPV4_VERSION: 212710616SSebastien.Roy@Sun.COM *outer4 = ipha; 212810616SSebastien.Roy@Sun.COM *outer6 = NULL; 212910616SSebastien.Roy@Sun.COM outer_hlen = IPH_HDR_LENGTH(ipha); 213010616SSebastien.Roy@Sun.COM break; 213110616SSebastien.Roy@Sun.COM case IPV6_VERSION: 213210616SSebastien.Roy@Sun.COM *outer4 = NULL; 213310616SSebastien.Roy@Sun.COM *outer6 = (ip6_t *)ipha; 213410616SSebastien.Roy@Sun.COM outer_hlen = ip_hdr_length_v6(mp, (ip6_t *)ipha); 213510616SSebastien.Roy@Sun.COM break; 213610616SSebastien.Roy@Sun.COM default: 213710616SSebastien.Roy@Sun.COM return (0); 213810616SSebastien.Roy@Sun.COM } 213910616SSebastien.Roy@Sun.COM 214010616SSebastien.Roy@Sun.COM if (first_mblkl < outer_hlen || 214110616SSebastien.Roy@Sun.COM (first_mblkl == outer_hlen && mp->b_cont == NULL)) 214210616SSebastien.Roy@Sun.COM return (0); 214310616SSebastien.Roy@Sun.COM 214410616SSebastien.Roy@Sun.COM /* 214510616SSebastien.Roy@Sun.COM * We don't bother doing a pullup here since the outer header will 214610616SSebastien.Roy@Sun.COM * just get stripped off soon on input anyway. We just want to ensure 214710616SSebastien.Roy@Sun.COM * that the inner* pointer points to a full header. 214810616SSebastien.Roy@Sun.COM */ 214910616SSebastien.Roy@Sun.COM if (first_mblkl == outer_hlen) { 215010616SSebastien.Roy@Sun.COM inner_mp = mp->b_cont; 215110616SSebastien.Roy@Sun.COM ipha = (ipha_t *)inner_mp->b_rptr; 215210616SSebastien.Roy@Sun.COM } else { 215310616SSebastien.Roy@Sun.COM inner_mp = mp; 215410616SSebastien.Roy@Sun.COM ipha = (ipha_t *)(mp->b_rptr + outer_hlen); 215510616SSebastien.Roy@Sun.COM } 215610616SSebastien.Roy@Sun.COM switch (IPH_HDR_VERSION(ipha)) { 215710616SSebastien.Roy@Sun.COM case IPV4_VERSION: 215810616SSebastien.Roy@Sun.COM if (inner_mp->b_wptr - (uint8_t *)ipha < sizeof (ipha_t)) 215910616SSebastien.Roy@Sun.COM return (0); 216010616SSebastien.Roy@Sun.COM *inner4 = ipha; 216110616SSebastien.Roy@Sun.COM *inner6 = NULL; 216210616SSebastien.Roy@Sun.COM break; 216310616SSebastien.Roy@Sun.COM case IPV6_VERSION: 216410616SSebastien.Roy@Sun.COM if (inner_mp->b_wptr - (uint8_t *)ipha < sizeof (ip6_t)) 216510616SSebastien.Roy@Sun.COM return (0); 216610616SSebastien.Roy@Sun.COM *inner4 = NULL; 216710616SSebastien.Roy@Sun.COM *inner6 = (ip6_t *)ipha; 216810616SSebastien.Roy@Sun.COM break; 216910616SSebastien.Roy@Sun.COM default: 217010616SSebastien.Roy@Sun.COM return (0); 217110616SSebastien.Roy@Sun.COM } 217210616SSebastien.Roy@Sun.COM 217310616SSebastien.Roy@Sun.COM return (outer_hlen); 217410616SSebastien.Roy@Sun.COM } 217510616SSebastien.Roy@Sun.COM 217610616SSebastien.Roy@Sun.COM /* 217710616SSebastien.Roy@Sun.COM * Received ICMP error in response to an X over IPv4 packet that we 217810616SSebastien.Roy@Sun.COM * transmitted. 217910616SSebastien.Roy@Sun.COM * 218010616SSebastien.Roy@Sun.COM * NOTE: "outer" refers to what's inside the ICMP payload. We will get one of 218110616SSebastien.Roy@Sun.COM * the following: 218210616SSebastien.Roy@Sun.COM * 218310616SSebastien.Roy@Sun.COM * [IPv4(0)][ICMPv4][IPv4(1)][IPv4(2)][ULP] 218410616SSebastien.Roy@Sun.COM * 218510616SSebastien.Roy@Sun.COM * or 218610616SSebastien.Roy@Sun.COM * 218710616SSebastien.Roy@Sun.COM * [IPv4(0)][ICMPv4][IPv4(1)][IPv6][ULP] 218810616SSebastien.Roy@Sun.COM * 218910616SSebastien.Roy@Sun.COM * And "outer4" will get set to IPv4(1), and inner[46] will correspond to 219010616SSebastien.Roy@Sun.COM * whatever the very-inner packet is (IPv4(2) or IPv6). 219110616SSebastien.Roy@Sun.COM */ 219210616SSebastien.Roy@Sun.COM static void 219310616SSebastien.Roy@Sun.COM iptun_input_icmp_v4(iptun_t *iptun, mblk_t *ipsec_mp, mblk_t *data_mp, 219410616SSebastien.Roy@Sun.COM icmph_t *icmph) 219510616SSebastien.Roy@Sun.COM { 219610616SSebastien.Roy@Sun.COM uint8_t *orig; 219710616SSebastien.Roy@Sun.COM ipha_t *outer4, *inner4; 219810616SSebastien.Roy@Sun.COM ip6_t *outer6, *inner6; 219910616SSebastien.Roy@Sun.COM int outer_hlen; 220010616SSebastien.Roy@Sun.COM uint8_t type, code; 220110616SSebastien.Roy@Sun.COM 220210616SSebastien.Roy@Sun.COM /* 220310616SSebastien.Roy@Sun.COM * Change the db_type to M_DATA because subsequent operations assume 220410616SSebastien.Roy@Sun.COM * the ICMP packet is M_DATA again (i.e. calls to msgdsize()). 220510616SSebastien.Roy@Sun.COM */ 220610616SSebastien.Roy@Sun.COM data_mp->b_datap->db_type = M_DATA; 220710616SSebastien.Roy@Sun.COM 220810616SSebastien.Roy@Sun.COM ASSERT(data_mp->b_cont == NULL); 220910616SSebastien.Roy@Sun.COM /* 221010616SSebastien.Roy@Sun.COM * Temporarily move b_rptr forward so that iptun_find_headers() can 221110616SSebastien.Roy@Sun.COM * find headers in the ICMP packet payload. 221210616SSebastien.Roy@Sun.COM */ 221310616SSebastien.Roy@Sun.COM orig = data_mp->b_rptr; 221410616SSebastien.Roy@Sun.COM data_mp->b_rptr = (uint8_t *)(icmph + 1); 221510616SSebastien.Roy@Sun.COM /* 221610616SSebastien.Roy@Sun.COM * The ip module ensures that ICMP errors contain at least the 221710616SSebastien.Roy@Sun.COM * original IP header (otherwise, the error would never have made it 221810616SSebastien.Roy@Sun.COM * here). 221910616SSebastien.Roy@Sun.COM */ 222010616SSebastien.Roy@Sun.COM ASSERT(MBLKL(data_mp) >= 0); 222110616SSebastien.Roy@Sun.COM outer_hlen = iptun_find_headers(data_mp, &outer4, &inner4, &outer6, 222210616SSebastien.Roy@Sun.COM &inner6); 222310616SSebastien.Roy@Sun.COM ASSERT(outer6 == NULL); 222410616SSebastien.Roy@Sun.COM data_mp->b_rptr = orig; 222510616SSebastien.Roy@Sun.COM if (outer_hlen == 0) { 222610616SSebastien.Roy@Sun.COM iptun_drop_pkt((ipsec_mp != NULL ? ipsec_mp : data_mp), 222710616SSebastien.Roy@Sun.COM &iptun->iptun_ierrors); 222810616SSebastien.Roy@Sun.COM return; 222910616SSebastien.Roy@Sun.COM } 223010616SSebastien.Roy@Sun.COM 223110616SSebastien.Roy@Sun.COM /* Only ICMP errors due to tunneled packets should reach here. */ 223210616SSebastien.Roy@Sun.COM ASSERT(outer4->ipha_protocol == IPPROTO_ENCAP || 223310616SSebastien.Roy@Sun.COM outer4->ipha_protocol == IPPROTO_IPV6); 223410616SSebastien.Roy@Sun.COM 223510616SSebastien.Roy@Sun.COM /* ipsec_tun_inbound() always frees ipsec_mp. */ 223610616SSebastien.Roy@Sun.COM if (!ipsec_tun_inbound(ipsec_mp, &data_mp, iptun->iptun_itp, 223710616SSebastien.Roy@Sun.COM inner4, inner6, outer4, outer6, -outer_hlen, 223810616SSebastien.Roy@Sun.COM iptun->iptun_ns)) { 223910616SSebastien.Roy@Sun.COM /* Callee did all of the freeing. */ 224010616SSebastien.Roy@Sun.COM atomic_inc_64(&iptun->iptun_ierrors); 224110616SSebastien.Roy@Sun.COM return; 224210616SSebastien.Roy@Sun.COM } 224310616SSebastien.Roy@Sun.COM /* We should never see reassembled fragment here. */ 224410616SSebastien.Roy@Sun.COM ASSERT(data_mp->b_next == NULL); 224510616SSebastien.Roy@Sun.COM 224610616SSebastien.Roy@Sun.COM data_mp->b_rptr = (uint8_t *)outer4 + outer_hlen; 224710616SSebastien.Roy@Sun.COM 224810616SSebastien.Roy@Sun.COM /* 224910616SSebastien.Roy@Sun.COM * If the original packet being transmitted was itself an ICMP error, 225010616SSebastien.Roy@Sun.COM * then drop this packet. We don't want to generate an ICMP error in 225110616SSebastien.Roy@Sun.COM * response to an ICMP error. 225210616SSebastien.Roy@Sun.COM */ 225310616SSebastien.Roy@Sun.COM if (is_icmp_error(data_mp, inner4, inner6)) { 225410616SSebastien.Roy@Sun.COM iptun_drop_pkt(data_mp, &iptun->iptun_norcvbuf); 225510616SSebastien.Roy@Sun.COM return; 225610616SSebastien.Roy@Sun.COM } 225710616SSebastien.Roy@Sun.COM 225810616SSebastien.Roy@Sun.COM switch (icmph->icmph_type) { 225910616SSebastien.Roy@Sun.COM case ICMP_DEST_UNREACHABLE: 226010616SSebastien.Roy@Sun.COM type = (inner4 != NULL ? icmph->icmph_type : ICMP6_DST_UNREACH); 226110616SSebastien.Roy@Sun.COM switch (icmph->icmph_code) { 226210616SSebastien.Roy@Sun.COM case ICMP_FRAGMENTATION_NEEDED: { 226310616SSebastien.Roy@Sun.COM uint32_t newmtu; 226410616SSebastien.Roy@Sun.COM 226510616SSebastien.Roy@Sun.COM /* 226610616SSebastien.Roy@Sun.COM * We reconcile this with the fact that the tunnel may 226710616SSebastien.Roy@Sun.COM * also have IPsec policy by letting iptun_update_mtu 226810616SSebastien.Roy@Sun.COM * take care of it. 226910616SSebastien.Roy@Sun.COM */ 227010616SSebastien.Roy@Sun.COM newmtu = 227110616SSebastien.Roy@Sun.COM iptun_update_mtu(iptun, ntohs(icmph->icmph_du_mtu)); 227210616SSebastien.Roy@Sun.COM 227310616SSebastien.Roy@Sun.COM if (inner4 != NULL) { 227410616SSebastien.Roy@Sun.COM iptun_icmp_fragneeded_v4(iptun, newmtu, inner4, 227510616SSebastien.Roy@Sun.COM data_mp); 227610616SSebastien.Roy@Sun.COM } else { 227710616SSebastien.Roy@Sun.COM iptun_icmp_toobig_v6(iptun, newmtu, inner6, 227810616SSebastien.Roy@Sun.COM data_mp); 227910616SSebastien.Roy@Sun.COM } 228010616SSebastien.Roy@Sun.COM return; 228110616SSebastien.Roy@Sun.COM } 228210616SSebastien.Roy@Sun.COM case ICMP_DEST_NET_UNREACH_ADMIN: 228310616SSebastien.Roy@Sun.COM case ICMP_DEST_HOST_UNREACH_ADMIN: 228410616SSebastien.Roy@Sun.COM code = (inner4 != NULL ? ICMP_DEST_NET_UNREACH_ADMIN : 228510616SSebastien.Roy@Sun.COM ICMP6_DST_UNREACH_ADMIN); 228610616SSebastien.Roy@Sun.COM break; 228710616SSebastien.Roy@Sun.COM default: 228810616SSebastien.Roy@Sun.COM code = (inner4 != NULL ? ICMP_HOST_UNREACHABLE : 228910616SSebastien.Roy@Sun.COM ICMP6_DST_UNREACH_ADDR); 229010616SSebastien.Roy@Sun.COM break; 229110616SSebastien.Roy@Sun.COM } 229210616SSebastien.Roy@Sun.COM break; 229310616SSebastien.Roy@Sun.COM case ICMP_TIME_EXCEEDED: 229410616SSebastien.Roy@Sun.COM if (inner6 != NULL) { 229510616SSebastien.Roy@Sun.COM type = ICMP6_TIME_EXCEEDED; 229610616SSebastien.Roy@Sun.COM code = 0; 229710616SSebastien.Roy@Sun.COM } /* else we're already set. */ 229810616SSebastien.Roy@Sun.COM break; 229910616SSebastien.Roy@Sun.COM case ICMP_PARAM_PROBLEM: 230010616SSebastien.Roy@Sun.COM /* 230110616SSebastien.Roy@Sun.COM * This is a problem with the outer header we transmitted. 230210616SSebastien.Roy@Sun.COM * Treat this as an output error. 230310616SSebastien.Roy@Sun.COM */ 230410616SSebastien.Roy@Sun.COM iptun_drop_pkt(data_mp, &iptun->iptun_oerrors); 230510616SSebastien.Roy@Sun.COM return; 230610616SSebastien.Roy@Sun.COM default: 230710616SSebastien.Roy@Sun.COM iptun_drop_pkt(data_mp, &iptun->iptun_norcvbuf); 230810616SSebastien.Roy@Sun.COM return; 230910616SSebastien.Roy@Sun.COM } 231010616SSebastien.Roy@Sun.COM 231110616SSebastien.Roy@Sun.COM if (inner4 != NULL) 231210616SSebastien.Roy@Sun.COM iptun_icmp_error_v4(iptun, inner4, data_mp, type, code); 231310616SSebastien.Roy@Sun.COM else 231410616SSebastien.Roy@Sun.COM iptun_icmp_error_v6(iptun, inner6, data_mp, type, code, 0); 231510616SSebastien.Roy@Sun.COM } 231610616SSebastien.Roy@Sun.COM 231710616SSebastien.Roy@Sun.COM /* 231810616SSebastien.Roy@Sun.COM * Return B_TRUE if the IPv6 packet pointed to by ip6h contains a Tunnel 231910616SSebastien.Roy@Sun.COM * Encapsulation Limit destination option. If there is one, set encaplim_ptr 232010616SSebastien.Roy@Sun.COM * to point to the option value. 232110616SSebastien.Roy@Sun.COM */ 232210616SSebastien.Roy@Sun.COM static boolean_t 232310616SSebastien.Roy@Sun.COM iptun_find_encaplimit(mblk_t *mp, ip6_t *ip6h, uint8_t **encaplim_ptr) 232410616SSebastien.Roy@Sun.COM { 232510616SSebastien.Roy@Sun.COM ip6_pkt_t pkt; 232610616SSebastien.Roy@Sun.COM uint8_t *endptr; 232710616SSebastien.Roy@Sun.COM ip6_dest_t *destp; 232810616SSebastien.Roy@Sun.COM struct ip6_opt *optp; 232910616SSebastien.Roy@Sun.COM 233010616SSebastien.Roy@Sun.COM pkt.ipp_fields = 0; /* must be initialized */ 233110616SSebastien.Roy@Sun.COM (void) ip_find_hdr_v6(mp, ip6h, &pkt, NULL); 233210616SSebastien.Roy@Sun.COM if ((pkt.ipp_fields & IPPF_DSTOPTS) != 0) { 233310616SSebastien.Roy@Sun.COM destp = pkt.ipp_dstopts; 233410616SSebastien.Roy@Sun.COM } else if ((pkt.ipp_fields & IPPF_RTDSTOPTS) != 0) { 233510616SSebastien.Roy@Sun.COM destp = pkt.ipp_rtdstopts; 233610616SSebastien.Roy@Sun.COM } else { 233710616SSebastien.Roy@Sun.COM return (B_FALSE); 233810616SSebastien.Roy@Sun.COM } 233910616SSebastien.Roy@Sun.COM 234010616SSebastien.Roy@Sun.COM endptr = (uint8_t *)destp + 8 * (destp->ip6d_len + 1); 234110616SSebastien.Roy@Sun.COM optp = (struct ip6_opt *)(destp + 1); 234210616SSebastien.Roy@Sun.COM while (endptr - (uint8_t *)optp > sizeof (*optp)) { 234310616SSebastien.Roy@Sun.COM if (optp->ip6o_type == IP6OPT_TUNNEL_LIMIT) { 234410616SSebastien.Roy@Sun.COM if ((uint8_t *)(optp + 1) >= endptr) 234510616SSebastien.Roy@Sun.COM return (B_FALSE); 234610616SSebastien.Roy@Sun.COM *encaplim_ptr = (uint8_t *)&optp[1]; 234710616SSebastien.Roy@Sun.COM return (B_TRUE); 234810616SSebastien.Roy@Sun.COM } 234910616SSebastien.Roy@Sun.COM optp = (struct ip6_opt *)((uint8_t *)optp + optp->ip6o_len + 2); 235010616SSebastien.Roy@Sun.COM } 235110616SSebastien.Roy@Sun.COM return (B_FALSE); 235210616SSebastien.Roy@Sun.COM } 235310616SSebastien.Roy@Sun.COM 235410616SSebastien.Roy@Sun.COM /* 235510616SSebastien.Roy@Sun.COM * Received ICMPv6 error in response to an X over IPv6 packet that we 235610616SSebastien.Roy@Sun.COM * transmitted. 235710616SSebastien.Roy@Sun.COM * 235810616SSebastien.Roy@Sun.COM * NOTE: "outer" refers to what's inside the ICMP payload. We will get one of 235910616SSebastien.Roy@Sun.COM * the following: 236010616SSebastien.Roy@Sun.COM * 236110616SSebastien.Roy@Sun.COM * [IPv6(0)][ICMPv6][IPv6(1)][IPv4][ULP] 236210616SSebastien.Roy@Sun.COM * 236310616SSebastien.Roy@Sun.COM * or 236410616SSebastien.Roy@Sun.COM * 236510616SSebastien.Roy@Sun.COM * [IPv6(0)][ICMPv6][IPv6(1)][IPv6(2)][ULP] 236610616SSebastien.Roy@Sun.COM * 236710616SSebastien.Roy@Sun.COM * And "outer6" will get set to IPv6(1), and inner[46] will correspond to 236810616SSebastien.Roy@Sun.COM * whatever the very-inner packet is (IPv4 or IPv6(2)). 236910616SSebastien.Roy@Sun.COM */ 237010616SSebastien.Roy@Sun.COM static void 237110616SSebastien.Roy@Sun.COM iptun_input_icmp_v6(iptun_t *iptun, mblk_t *ipsec_mp, mblk_t *data_mp, 237210616SSebastien.Roy@Sun.COM icmp6_t *icmp6h) 237310616SSebastien.Roy@Sun.COM { 237410616SSebastien.Roy@Sun.COM uint8_t *orig; 237510616SSebastien.Roy@Sun.COM ipha_t *outer4, *inner4; 237610616SSebastien.Roy@Sun.COM ip6_t *outer6, *inner6; 237710616SSebastien.Roy@Sun.COM int outer_hlen; 237810616SSebastien.Roy@Sun.COM uint8_t type, code; 237910616SSebastien.Roy@Sun.COM 238010616SSebastien.Roy@Sun.COM /* 238110616SSebastien.Roy@Sun.COM * Change the db_type to M_DATA because subsequent operations assume 238210616SSebastien.Roy@Sun.COM * the ICMP packet is M_DATA again (i.e. calls to msgdsize().) 238310616SSebastien.Roy@Sun.COM */ 238410616SSebastien.Roy@Sun.COM data_mp->b_datap->db_type = M_DATA; 238510616SSebastien.Roy@Sun.COM 238610616SSebastien.Roy@Sun.COM ASSERT(data_mp->b_cont == NULL); 238710616SSebastien.Roy@Sun.COM 238810616SSebastien.Roy@Sun.COM /* 238910616SSebastien.Roy@Sun.COM * Temporarily move b_rptr forward so that iptun_find_headers() can 239010616SSebastien.Roy@Sun.COM * find IP headers in the ICMP packet payload. 239110616SSebastien.Roy@Sun.COM */ 239210616SSebastien.Roy@Sun.COM orig = data_mp->b_rptr; 239310616SSebastien.Roy@Sun.COM data_mp->b_rptr = (uint8_t *)(icmp6h + 1); 239410616SSebastien.Roy@Sun.COM /* 239510616SSebastien.Roy@Sun.COM * The ip module ensures that ICMP errors contain at least the 239610616SSebastien.Roy@Sun.COM * original IP header (otherwise, the error would never have made it 239710616SSebastien.Roy@Sun.COM * here). 239810616SSebastien.Roy@Sun.COM */ 239910616SSebastien.Roy@Sun.COM ASSERT(MBLKL(data_mp) >= 0); 240010616SSebastien.Roy@Sun.COM outer_hlen = iptun_find_headers(data_mp, &outer4, &inner4, &outer6, 240110616SSebastien.Roy@Sun.COM &inner6); 240210616SSebastien.Roy@Sun.COM ASSERT(outer4 == NULL); 240310616SSebastien.Roy@Sun.COM data_mp->b_rptr = orig; /* Restore r_ptr */ 240410616SSebastien.Roy@Sun.COM if (outer_hlen == 0) { 240510616SSebastien.Roy@Sun.COM iptun_drop_pkt((ipsec_mp != NULL ? ipsec_mp : data_mp), 240610616SSebastien.Roy@Sun.COM &iptun->iptun_ierrors); 240710616SSebastien.Roy@Sun.COM return; 240810616SSebastien.Roy@Sun.COM } 240910616SSebastien.Roy@Sun.COM 241010616SSebastien.Roy@Sun.COM if (!ipsec_tun_inbound(ipsec_mp, &data_mp, iptun->iptun_itp, 241110616SSebastien.Roy@Sun.COM inner4, inner6, outer4, outer6, -outer_hlen, 241210616SSebastien.Roy@Sun.COM iptun->iptun_ns)) { 241310616SSebastien.Roy@Sun.COM /* Callee did all of the freeing. */ 241410616SSebastien.Roy@Sun.COM atomic_inc_64(&iptun->iptun_ierrors); 241510616SSebastien.Roy@Sun.COM return; 241610616SSebastien.Roy@Sun.COM } 241710616SSebastien.Roy@Sun.COM /* We should never see reassembled fragment here. */ 241810616SSebastien.Roy@Sun.COM ASSERT(data_mp->b_next == NULL); 241910616SSebastien.Roy@Sun.COM 242010616SSebastien.Roy@Sun.COM data_mp->b_rptr = (uint8_t *)outer6 + outer_hlen; 242110616SSebastien.Roy@Sun.COM 242210616SSebastien.Roy@Sun.COM /* 242310616SSebastien.Roy@Sun.COM * If the original packet being transmitted was itself an ICMP error, 242410616SSebastien.Roy@Sun.COM * then drop this packet. We don't want to generate an ICMP error in 242510616SSebastien.Roy@Sun.COM * response to an ICMP error. 242610616SSebastien.Roy@Sun.COM */ 242710616SSebastien.Roy@Sun.COM if (is_icmp_error(data_mp, inner4, inner6)) { 242810616SSebastien.Roy@Sun.COM iptun_drop_pkt(data_mp, &iptun->iptun_norcvbuf); 242910616SSebastien.Roy@Sun.COM return; 243010616SSebastien.Roy@Sun.COM } 243110616SSebastien.Roy@Sun.COM 243210616SSebastien.Roy@Sun.COM switch (icmp6h->icmp6_type) { 243310616SSebastien.Roy@Sun.COM case ICMP6_PARAM_PROB: { 243410616SSebastien.Roy@Sun.COM uint8_t *encaplim_ptr; 243510616SSebastien.Roy@Sun.COM 243610616SSebastien.Roy@Sun.COM /* 243710616SSebastien.Roy@Sun.COM * If the ICMPv6 error points to a valid Tunnel Encapsulation 243810616SSebastien.Roy@Sun.COM * Limit option and the limit value is 0, then fall through 243910616SSebastien.Roy@Sun.COM * and send a host unreachable message. Otherwise, treat the 244010616SSebastien.Roy@Sun.COM * error as an output error, as there must have been a problem 244110616SSebastien.Roy@Sun.COM * with a packet we sent. 244210616SSebastien.Roy@Sun.COM */ 244310616SSebastien.Roy@Sun.COM if (!iptun_find_encaplimit(data_mp, outer6, &encaplim_ptr) || 244410616SSebastien.Roy@Sun.COM (icmp6h->icmp6_pptr != 244510616SSebastien.Roy@Sun.COM ((ptrdiff_t)encaplim_ptr - (ptrdiff_t)outer6)) || 244610616SSebastien.Roy@Sun.COM *encaplim_ptr != 0) { 244710616SSebastien.Roy@Sun.COM iptun_drop_pkt(data_mp, &iptun->iptun_oerrors); 244810616SSebastien.Roy@Sun.COM return; 244910616SSebastien.Roy@Sun.COM } 245010616SSebastien.Roy@Sun.COM /* FALLTHRU */ 245110616SSebastien.Roy@Sun.COM } 245210616SSebastien.Roy@Sun.COM case ICMP6_TIME_EXCEEDED: 245310616SSebastien.Roy@Sun.COM case ICMP6_DST_UNREACH: 245410616SSebastien.Roy@Sun.COM type = (inner4 != NULL ? ICMP_DEST_UNREACHABLE : 245510616SSebastien.Roy@Sun.COM ICMP6_DST_UNREACH); 245610616SSebastien.Roy@Sun.COM code = (inner4 != NULL ? ICMP_HOST_UNREACHABLE : 245710616SSebastien.Roy@Sun.COM ICMP6_DST_UNREACH_ADDR); 245810616SSebastien.Roy@Sun.COM break; 245910616SSebastien.Roy@Sun.COM case ICMP6_PACKET_TOO_BIG: { 246010616SSebastien.Roy@Sun.COM uint32_t newmtu; 246110616SSebastien.Roy@Sun.COM 246210616SSebastien.Roy@Sun.COM /* 246310616SSebastien.Roy@Sun.COM * We reconcile this with the fact that the tunnel may also 246410616SSebastien.Roy@Sun.COM * have IPsec policy by letting iptun_update_mtu take care of 246510616SSebastien.Roy@Sun.COM * it. 246610616SSebastien.Roy@Sun.COM */ 246710616SSebastien.Roy@Sun.COM newmtu = iptun_update_mtu(iptun, ntohl(icmp6h->icmp6_mtu)); 246810616SSebastien.Roy@Sun.COM 246910616SSebastien.Roy@Sun.COM if (inner4 != NULL) { 247010616SSebastien.Roy@Sun.COM iptun_icmp_fragneeded_v4(iptun, newmtu, inner4, 247110616SSebastien.Roy@Sun.COM data_mp); 247210616SSebastien.Roy@Sun.COM } else { 247310616SSebastien.Roy@Sun.COM iptun_icmp_toobig_v6(iptun, newmtu, inner6, data_mp); 247410616SSebastien.Roy@Sun.COM } 247510616SSebastien.Roy@Sun.COM return; 247610616SSebastien.Roy@Sun.COM } 247710616SSebastien.Roy@Sun.COM default: 247810616SSebastien.Roy@Sun.COM iptun_drop_pkt(data_mp, &iptun->iptun_norcvbuf); 247910616SSebastien.Roy@Sun.COM return; 248010616SSebastien.Roy@Sun.COM } 248110616SSebastien.Roy@Sun.COM 248210616SSebastien.Roy@Sun.COM if (inner4 != NULL) 248310616SSebastien.Roy@Sun.COM iptun_icmp_error_v4(iptun, inner4, data_mp, type, code); 248410616SSebastien.Roy@Sun.COM else 248510616SSebastien.Roy@Sun.COM iptun_icmp_error_v6(iptun, inner6, data_mp, type, code, 0); 248610616SSebastien.Roy@Sun.COM } 248710616SSebastien.Roy@Sun.COM 248810616SSebastien.Roy@Sun.COM static void 248910616SSebastien.Roy@Sun.COM iptun_input_icmp(iptun_t *iptun, mblk_t *ipsec_mp, mblk_t *data_mp) 249010616SSebastien.Roy@Sun.COM { 249110616SSebastien.Roy@Sun.COM mblk_t *tmpmp; 249210616SSebastien.Roy@Sun.COM size_t hlen; 249310616SSebastien.Roy@Sun.COM 249410616SSebastien.Roy@Sun.COM if (data_mp->b_cont != NULL) { 249510616SSebastien.Roy@Sun.COM /* 249610616SSebastien.Roy@Sun.COM * Since ICMP error processing necessitates access to bits 249710616SSebastien.Roy@Sun.COM * that are within the ICMP error payload (the original packet 249810616SSebastien.Roy@Sun.COM * that caused the error), pull everything up into a single 249910616SSebastien.Roy@Sun.COM * block for convenience. 250010616SSebastien.Roy@Sun.COM */ 250110616SSebastien.Roy@Sun.COM data_mp->b_datap->db_type = M_DATA; 250210616SSebastien.Roy@Sun.COM if ((tmpmp = msgpullup(data_mp, -1)) == NULL) { 250310616SSebastien.Roy@Sun.COM iptun_drop_pkt((ipsec_mp != NULL ? ipsec_mp : data_mp), 250410616SSebastien.Roy@Sun.COM &iptun->iptun_norcvbuf); 250510616SSebastien.Roy@Sun.COM return; 250610616SSebastien.Roy@Sun.COM } 250710616SSebastien.Roy@Sun.COM freemsg(data_mp); 250810616SSebastien.Roy@Sun.COM data_mp = tmpmp; 250910616SSebastien.Roy@Sun.COM if (ipsec_mp != NULL) 251010616SSebastien.Roy@Sun.COM ipsec_mp->b_cont = data_mp; 251110616SSebastien.Roy@Sun.COM } 251210616SSebastien.Roy@Sun.COM 251310616SSebastien.Roy@Sun.COM switch (iptun->iptun_typeinfo->iti_ipvers) { 251410616SSebastien.Roy@Sun.COM case IPV4_VERSION: 251510616SSebastien.Roy@Sun.COM /* 251610616SSebastien.Roy@Sun.COM * The outer IP header coming up from IP is always ipha_t 251710616SSebastien.Roy@Sun.COM * alligned (otherwise, we would have crashed in ip). 251810616SSebastien.Roy@Sun.COM */ 251910616SSebastien.Roy@Sun.COM hlen = IPH_HDR_LENGTH((ipha_t *)data_mp->b_rptr); 252010616SSebastien.Roy@Sun.COM iptun_input_icmp_v4(iptun, ipsec_mp, data_mp, 252110616SSebastien.Roy@Sun.COM (icmph_t *)(data_mp->b_rptr + hlen)); 252210616SSebastien.Roy@Sun.COM break; 252310616SSebastien.Roy@Sun.COM case IPV6_VERSION: 252410616SSebastien.Roy@Sun.COM hlen = ip_hdr_length_v6(data_mp, (ip6_t *)data_mp->b_rptr); 252510616SSebastien.Roy@Sun.COM iptun_input_icmp_v6(iptun, ipsec_mp, data_mp, 252610616SSebastien.Roy@Sun.COM (icmp6_t *)(data_mp->b_rptr + hlen)); 252710616SSebastien.Roy@Sun.COM break; 252810616SSebastien.Roy@Sun.COM } 252910616SSebastien.Roy@Sun.COM } 253010616SSebastien.Roy@Sun.COM 253110616SSebastien.Roy@Sun.COM static boolean_t 253210616SSebastien.Roy@Sun.COM iptun_in_6to4_ok(iptun_t *iptun, ipha_t *outer4, ip6_t *inner6) 253310616SSebastien.Roy@Sun.COM { 253410616SSebastien.Roy@Sun.COM ipaddr_t v4addr; 253510616SSebastien.Roy@Sun.COM 253610616SSebastien.Roy@Sun.COM /* 2537*10722SSebastien.Roy@Sun.COM * It's possible that someone sent us an IPv4-in-IPv4 packet with the 2538*10722SSebastien.Roy@Sun.COM * IPv4 address of a 6to4 tunnel as the destination. 2539*10722SSebastien.Roy@Sun.COM */ 2540*10722SSebastien.Roy@Sun.COM if (inner6 == NULL) 2541*10722SSebastien.Roy@Sun.COM return (B_FALSE); 2542*10722SSebastien.Roy@Sun.COM 2543*10722SSebastien.Roy@Sun.COM /* 254410616SSebastien.Roy@Sun.COM * Make sure that the IPv6 destination is within the site that this 254510616SSebastien.Roy@Sun.COM * 6to4 tunnel is routing for. We don't want people bouncing random 254610616SSebastien.Roy@Sun.COM * tunneled IPv6 packets through this 6to4 router. 254710616SSebastien.Roy@Sun.COM */ 254810616SSebastien.Roy@Sun.COM IN6_6TO4_TO_V4ADDR(&inner6->ip6_dst, (struct in_addr *)&v4addr); 254910616SSebastien.Roy@Sun.COM if (outer4->ipha_dst != v4addr) 255010616SSebastien.Roy@Sun.COM return (B_FALSE); 255110616SSebastien.Roy@Sun.COM 255210616SSebastien.Roy@Sun.COM if (IN6_IS_ADDR_6TO4(&inner6->ip6_src)) { 255310616SSebastien.Roy@Sun.COM /* 255410616SSebastien.Roy@Sun.COM * Section 9 of RFC 3056 (security considerations) suggests 255510616SSebastien.Roy@Sun.COM * that when a packet is from a 6to4 site (i.e., it's not a 255610616SSebastien.Roy@Sun.COM * global address being forwarded froma relay router), make 255710616SSebastien.Roy@Sun.COM * sure that the packet was tunneled by that site's 6to4 255810616SSebastien.Roy@Sun.COM * router. 255910616SSebastien.Roy@Sun.COM */ 256010616SSebastien.Roy@Sun.COM IN6_6TO4_TO_V4ADDR(&inner6->ip6_src, (struct in_addr *)&v4addr); 256110616SSebastien.Roy@Sun.COM if (outer4->ipha_src != v4addr) 256210616SSebastien.Roy@Sun.COM return (B_FALSE); 256310616SSebastien.Roy@Sun.COM } else { 256410616SSebastien.Roy@Sun.COM /* 256510616SSebastien.Roy@Sun.COM * Only accept packets from a relay router if we've configured 256610616SSebastien.Roy@Sun.COM * outbound relay router functionality. 256710616SSebastien.Roy@Sun.COM */ 256810616SSebastien.Roy@Sun.COM if (iptun->iptun_iptuns->iptuns_relay_rtr_addr == INADDR_ANY) 256910616SSebastien.Roy@Sun.COM return (B_FALSE); 257010616SSebastien.Roy@Sun.COM } 257110616SSebastien.Roy@Sun.COM 257210616SSebastien.Roy@Sun.COM return (B_TRUE); 257310616SSebastien.Roy@Sun.COM } 257410616SSebastien.Roy@Sun.COM 257510616SSebastien.Roy@Sun.COM /* 257610616SSebastien.Roy@Sun.COM * Input function for everything that comes up from the ip module below us. 257710616SSebastien.Roy@Sun.COM * This is called directly from the ip module via connp->conn_recv(). 257810616SSebastien.Roy@Sun.COM * 257910616SSebastien.Roy@Sun.COM * There are two kinds of packets that can arrive here: (1) IP-in-IP tunneled 258010616SSebastien.Roy@Sun.COM * packets and (2) ICMP errors containing IP-in-IP packets transmitted by us. 258110616SSebastien.Roy@Sun.COM * They have the following structure: 258210616SSebastien.Roy@Sun.COM * 258310616SSebastien.Roy@Sun.COM * 1) M_DATA 258410616SSebastien.Roy@Sun.COM * 2) M_CTL[->M_DATA] 258510616SSebastien.Roy@Sun.COM * 258610616SSebastien.Roy@Sun.COM * (2) Is an M_CTL optionally followed by M_DATA, where the M_CTL block is the 258710616SSebastien.Roy@Sun.COM * start of the actual ICMP packet (it doesn't contain any special control 258810616SSebastien.Roy@Sun.COM * information). 258910616SSebastien.Roy@Sun.COM * 259010616SSebastien.Roy@Sun.COM * Either (1) or (2) can be IPsec-protected, in which case an M_CTL block 259110616SSebastien.Roy@Sun.COM * containing an ipsec_in_t will have been prepended to either (1) or (2), 259210616SSebastien.Roy@Sun.COM * making a total of four combinations of possible mblk chains: 259310616SSebastien.Roy@Sun.COM * 259410616SSebastien.Roy@Sun.COM * A) (1) 259510616SSebastien.Roy@Sun.COM * B) (2) 259610616SSebastien.Roy@Sun.COM * C) M_CTL(ipsec_in_t)->(1) 259710616SSebastien.Roy@Sun.COM * D) M_CTL(ipsec_in_t)->(2) 259810616SSebastien.Roy@Sun.COM */ 259910616SSebastien.Roy@Sun.COM /* ARGSUSED */ 260010616SSebastien.Roy@Sun.COM static void 260110616SSebastien.Roy@Sun.COM iptun_input(void *arg, mblk_t *mp, void *arg2) 260210616SSebastien.Roy@Sun.COM { 260310616SSebastien.Roy@Sun.COM conn_t *connp = arg; 260410616SSebastien.Roy@Sun.COM iptun_t *iptun = connp->conn_iptun; 260510616SSebastien.Roy@Sun.COM int outer_hlen; 260610616SSebastien.Roy@Sun.COM ipha_t *outer4, *inner4; 260710616SSebastien.Roy@Sun.COM ip6_t *outer6, *inner6; 260810616SSebastien.Roy@Sun.COM mblk_t *data_mp = mp; 260910616SSebastien.Roy@Sun.COM 261010616SSebastien.Roy@Sun.COM ASSERT(IPCL_IS_IPTUN(connp)); 261110616SSebastien.Roy@Sun.COM ASSERT(DB_TYPE(mp) == M_DATA || DB_TYPE(mp) == M_CTL); 261210616SSebastien.Roy@Sun.COM 261310616SSebastien.Roy@Sun.COM if (DB_TYPE(mp) == M_CTL) { 261410616SSebastien.Roy@Sun.COM if (((ipsec_in_t *)(mp->b_rptr))->ipsec_in_type != IPSEC_IN) { 261510616SSebastien.Roy@Sun.COM iptun_input_icmp(iptun, NULL, mp); 261610616SSebastien.Roy@Sun.COM return; 261710616SSebastien.Roy@Sun.COM } 261810616SSebastien.Roy@Sun.COM 261910616SSebastien.Roy@Sun.COM data_mp = mp->b_cont; 262010616SSebastien.Roy@Sun.COM if (DB_TYPE(data_mp) == M_CTL) { 262110616SSebastien.Roy@Sun.COM /* Protected ICMP packet. */ 262210616SSebastien.Roy@Sun.COM iptun_input_icmp(iptun, mp, data_mp); 262310616SSebastien.Roy@Sun.COM return; 262410616SSebastien.Roy@Sun.COM } 262510616SSebastien.Roy@Sun.COM } 262610616SSebastien.Roy@Sun.COM 262710616SSebastien.Roy@Sun.COM /* 262810616SSebastien.Roy@Sun.COM * Request the destination's path MTU information regularly in case 262910616SSebastien.Roy@Sun.COM * path MTU has increased. 263010616SSebastien.Roy@Sun.COM */ 263110616SSebastien.Roy@Sun.COM if (IPTUN_PMTU_TOO_OLD(iptun)) 263210616SSebastien.Roy@Sun.COM iptun_task_dispatch(iptun, IPTUN_TASK_PMTU_UPDATE); 263310616SSebastien.Roy@Sun.COM 263410616SSebastien.Roy@Sun.COM if ((outer_hlen = iptun_find_headers(data_mp, &outer4, &inner4, &outer6, 263510616SSebastien.Roy@Sun.COM &inner6)) == 0) 263610616SSebastien.Roy@Sun.COM goto drop; 263710616SSebastien.Roy@Sun.COM 263810616SSebastien.Roy@Sun.COM /* 263910616SSebastien.Roy@Sun.COM * If the system is labeled, we call tsol_check_dest() on the packet 264010616SSebastien.Roy@Sun.COM * destination (our local tunnel address) to ensure that the packet as 264110616SSebastien.Roy@Sun.COM * labeled should be allowed to be sent to us. We don't need to call 264210616SSebastien.Roy@Sun.COM * the more involved tsol_receive_local() since the tunnel link itself 264310616SSebastien.Roy@Sun.COM * cannot be assigned to shared-stack non-global zones. 264410616SSebastien.Roy@Sun.COM */ 264510616SSebastien.Roy@Sun.COM if (is_system_labeled()) { 264610616SSebastien.Roy@Sun.COM cred_t *msg_cred; 264710616SSebastien.Roy@Sun.COM 264810616SSebastien.Roy@Sun.COM if ((msg_cred = msg_getcred(data_mp, NULL)) == NULL) 264910616SSebastien.Roy@Sun.COM goto drop; 265010616SSebastien.Roy@Sun.COM if (tsol_check_dest(msg_cred, (outer4 != NULL ? 265110616SSebastien.Roy@Sun.COM (void *)&outer4->ipha_dst : (void *)&outer6->ip6_dst), 265210616SSebastien.Roy@Sun.COM (outer4 != NULL ? IPV4_VERSION : IPV6_VERSION), 265310616SSebastien.Roy@Sun.COM B_FALSE, NULL) != 0) 265410616SSebastien.Roy@Sun.COM goto drop; 265510616SSebastien.Roy@Sun.COM } 265610616SSebastien.Roy@Sun.COM 265710697Sdanmcd@sun.com if (!ipsec_tun_inbound((mp == data_mp ? NULL : mp), &data_mp, 265810697Sdanmcd@sun.com iptun->iptun_itp, inner4, inner6, outer4, outer6, outer_hlen, 265910697Sdanmcd@sun.com iptun->iptun_ns)) { 266010692Sdanmcd@sun.com /* Callee did all of the freeing. */ 266110692Sdanmcd@sun.com return; 266210616SSebastien.Roy@Sun.COM } 266310692Sdanmcd@sun.com mp = data_mp; 266410616SSebastien.Roy@Sun.COM 266510616SSebastien.Roy@Sun.COM if (iptun->iptun_typeinfo->iti_type == IPTUN_TYPE_6TO4 && 266610616SSebastien.Roy@Sun.COM !iptun_in_6to4_ok(iptun, outer4, inner6)) 266710616SSebastien.Roy@Sun.COM goto drop; 266810616SSebastien.Roy@Sun.COM 266910616SSebastien.Roy@Sun.COM /* 267010616SSebastien.Roy@Sun.COM * We need to statistically account for each packet individually, so 267110616SSebastien.Roy@Sun.COM * we might as well split up any b_next chains here. 267210616SSebastien.Roy@Sun.COM */ 267310616SSebastien.Roy@Sun.COM do { 267410616SSebastien.Roy@Sun.COM mp = data_mp->b_next; 267510616SSebastien.Roy@Sun.COM data_mp->b_next = NULL; 267610616SSebastien.Roy@Sun.COM 267710616SSebastien.Roy@Sun.COM atomic_inc_64(&iptun->iptun_ipackets); 267810616SSebastien.Roy@Sun.COM atomic_add_64(&iptun->iptun_rbytes, msgdsize(data_mp)); 267910616SSebastien.Roy@Sun.COM mac_rx(iptun->iptun_mh, NULL, data_mp); 268010616SSebastien.Roy@Sun.COM 268110616SSebastien.Roy@Sun.COM data_mp = mp; 268210616SSebastien.Roy@Sun.COM } while (data_mp != NULL); 268310616SSebastien.Roy@Sun.COM return; 268410616SSebastien.Roy@Sun.COM drop: 268510616SSebastien.Roy@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_ierrors); 268610616SSebastien.Roy@Sun.COM } 268710616SSebastien.Roy@Sun.COM 268810616SSebastien.Roy@Sun.COM /* 268910616SSebastien.Roy@Sun.COM * Do 6to4-specific header-processing on output. Return B_TRUE if the packet 269010616SSebastien.Roy@Sun.COM * was processed without issue, or B_FALSE if the packet had issues and should 269110616SSebastien.Roy@Sun.COM * be dropped. 269210616SSebastien.Roy@Sun.COM */ 269310616SSebastien.Roy@Sun.COM static boolean_t 269410616SSebastien.Roy@Sun.COM iptun_out_process_6to4(iptun_t *iptun, ipha_t *outer4, ip6_t *inner6) 269510616SSebastien.Roy@Sun.COM { 269610616SSebastien.Roy@Sun.COM ipaddr_t v4addr; 269710616SSebastien.Roy@Sun.COM 269810616SSebastien.Roy@Sun.COM /* 269910616SSebastien.Roy@Sun.COM * IPv6 source must be a 6to4 address. This is because a conscious 270010616SSebastien.Roy@Sun.COM * decision was made to not allow a Solaris system to be used as a 270110616SSebastien.Roy@Sun.COM * relay router (for security reasons) when 6to4 was initially 270210616SSebastien.Roy@Sun.COM * integrated. If this decision is ever reversed, the following check 270310616SSebastien.Roy@Sun.COM * can be removed. 270410616SSebastien.Roy@Sun.COM */ 270510616SSebastien.Roy@Sun.COM if (!IN6_IS_ADDR_6TO4(&inner6->ip6_src)) 270610616SSebastien.Roy@Sun.COM return (B_FALSE); 270710616SSebastien.Roy@Sun.COM 270810616SSebastien.Roy@Sun.COM /* 270910616SSebastien.Roy@Sun.COM * RFC3056 mandates that the IPv4 source MUST be set to the IPv4 271010616SSebastien.Roy@Sun.COM * portion of the 6to4 IPv6 source address. In other words, make sure 271110616SSebastien.Roy@Sun.COM * that we're tunneling packets from our own 6to4 site. 271210616SSebastien.Roy@Sun.COM */ 271310616SSebastien.Roy@Sun.COM IN6_6TO4_TO_V4ADDR(&inner6->ip6_src, (struct in_addr *)&v4addr); 271410616SSebastien.Roy@Sun.COM if (outer4->ipha_src != v4addr) 271510616SSebastien.Roy@Sun.COM return (B_FALSE); 271610616SSebastien.Roy@Sun.COM 271710616SSebastien.Roy@Sun.COM /* 271810616SSebastien.Roy@Sun.COM * Automatically set the destination of the outer IPv4 header as 271910616SSebastien.Roy@Sun.COM * described in RFC3056. There are two possibilities: 272010616SSebastien.Roy@Sun.COM * 272110616SSebastien.Roy@Sun.COM * a. If the IPv6 destination is a 6to4 address, set the IPv4 address 272210616SSebastien.Roy@Sun.COM * to the IPv4 portion of the 6to4 address. 272310616SSebastien.Roy@Sun.COM * b. If the IPv6 destination is a native IPv6 address, set the IPv4 272410616SSebastien.Roy@Sun.COM * destination to the address of a relay router. 272510616SSebastien.Roy@Sun.COM * 272610616SSebastien.Roy@Sun.COM * Design Note: b shouldn't be necessary here, and this is a flaw in 272710616SSebastien.Roy@Sun.COM * the design of the 6to4relay command. Instead of setting a 6to4 272810616SSebastien.Roy@Sun.COM * relay address in this module via an ioctl, the 6to4relay command 272910616SSebastien.Roy@Sun.COM * could simply add a IPv6 route for native IPv6 addresses (such as a 273010616SSebastien.Roy@Sun.COM * default route) in the forwarding table that uses a 6to4 destination 273110616SSebastien.Roy@Sun.COM * as its next hop, and the IPv4 portion of that address could be a 273210616SSebastien.Roy@Sun.COM * 6to4 relay address. In order for this to work, IP would have to 273310616SSebastien.Roy@Sun.COM * resolve the next hop address, which would necessitate a link-layer 273410616SSebastien.Roy@Sun.COM * address resolver for 6to4 links, which doesn't exist today. 273510616SSebastien.Roy@Sun.COM * 273610616SSebastien.Roy@Sun.COM * In fact, if a resolver existed for 6to4 links, then setting the 273710616SSebastien.Roy@Sun.COM * IPv4 destination in the outer header could be done as part of 273810616SSebastien.Roy@Sun.COM * link-layer address resolution and fast-path header generation, and 273910616SSebastien.Roy@Sun.COM * not here. 274010616SSebastien.Roy@Sun.COM */ 274110616SSebastien.Roy@Sun.COM if (IN6_IS_ADDR_6TO4(&inner6->ip6_dst)) { 274210616SSebastien.Roy@Sun.COM /* destination is a 6to4 router */ 274310616SSebastien.Roy@Sun.COM IN6_6TO4_TO_V4ADDR(&inner6->ip6_dst, 274410616SSebastien.Roy@Sun.COM (struct in_addr *)&outer4->ipha_dst); 274510616SSebastien.Roy@Sun.COM } else { 274610616SSebastien.Roy@Sun.COM /* 274710616SSebastien.Roy@Sun.COM * The destination is a native IPv6 address. If output to a 274810616SSebastien.Roy@Sun.COM * relay-router is enabled, use the relay-router's IPv4 274910616SSebastien.Roy@Sun.COM * address as the destination. 275010616SSebastien.Roy@Sun.COM */ 275110616SSebastien.Roy@Sun.COM if (iptun->iptun_iptuns->iptuns_relay_rtr_addr == INADDR_ANY) 275210616SSebastien.Roy@Sun.COM return (B_FALSE); 275310616SSebastien.Roy@Sun.COM outer4->ipha_dst = iptun->iptun_iptuns->iptuns_relay_rtr_addr; 275410616SSebastien.Roy@Sun.COM } 275510616SSebastien.Roy@Sun.COM 275610616SSebastien.Roy@Sun.COM /* 275710616SSebastien.Roy@Sun.COM * If the outer source and destination are equal, this means that the 275810616SSebastien.Roy@Sun.COM * 6to4 router somehow forwarded an IPv6 packet destined for its own 275910616SSebastien.Roy@Sun.COM * 6to4 site to its 6to4 tunnel interface, which will result in this 276010616SSebastien.Roy@Sun.COM * packet infinitely bouncing between ip and iptun. 276110616SSebastien.Roy@Sun.COM */ 276210616SSebastien.Roy@Sun.COM return (outer4->ipha_src != outer4->ipha_dst); 276310616SSebastien.Roy@Sun.COM } 276410616SSebastien.Roy@Sun.COM 276510616SSebastien.Roy@Sun.COM /* 276610616SSebastien.Roy@Sun.COM * Process output packets with outer IPv4 headers. Frees mp and bumps stat on 276710616SSebastien.Roy@Sun.COM * error. 276810616SSebastien.Roy@Sun.COM */ 276910616SSebastien.Roy@Sun.COM static mblk_t * 277010616SSebastien.Roy@Sun.COM iptun_out_process_ipv4(iptun_t *iptun, mblk_t *mp, ipha_t *outer4, 277110616SSebastien.Roy@Sun.COM ipha_t *inner4, ip6_t *inner6) 277210616SSebastien.Roy@Sun.COM { 277310616SSebastien.Roy@Sun.COM uint8_t *innerptr = (inner4 != NULL ? 277410616SSebastien.Roy@Sun.COM (uint8_t *)inner4 : (uint8_t *)inner6); 277510616SSebastien.Roy@Sun.COM size_t minmtu = (inner4 != NULL ? 277610616SSebastien.Roy@Sun.COM IPTUN_MIN_IPV4_MTU : IPTUN_MIN_IPV6_MTU); 277710616SSebastien.Roy@Sun.COM 277810616SSebastien.Roy@Sun.COM if (inner4 != NULL) { 277910616SSebastien.Roy@Sun.COM ASSERT(outer4->ipha_protocol == IPPROTO_ENCAP); 278010616SSebastien.Roy@Sun.COM /* 278110616SSebastien.Roy@Sun.COM * Copy the tos from the inner IPv4 header. We mask off ECN 278210616SSebastien.Roy@Sun.COM * bits (bits 6 and 7) because there is currently no 278310616SSebastien.Roy@Sun.COM * tunnel-tunnel communication to determine if both sides 278410616SSebastien.Roy@Sun.COM * support ECN. We opt for the safe choice: don't copy the 278510616SSebastien.Roy@Sun.COM * ECN bits when doing encapsulation. 278610616SSebastien.Roy@Sun.COM */ 278710616SSebastien.Roy@Sun.COM outer4->ipha_type_of_service = 278810616SSebastien.Roy@Sun.COM inner4->ipha_type_of_service & ~0x03; 278910616SSebastien.Roy@Sun.COM } else { 279010616SSebastien.Roy@Sun.COM ASSERT(outer4->ipha_protocol == IPPROTO_IPV6 && 279110616SSebastien.Roy@Sun.COM inner6 != NULL); 279210616SSebastien.Roy@Sun.COM 279310616SSebastien.Roy@Sun.COM if (iptun->iptun_typeinfo->iti_type == IPTUN_TYPE_6TO4 && 279410616SSebastien.Roy@Sun.COM !iptun_out_process_6to4(iptun, outer4, inner6)) { 279510616SSebastien.Roy@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_oerrors); 279610616SSebastien.Roy@Sun.COM return (NULL); 279710616SSebastien.Roy@Sun.COM } 279810616SSebastien.Roy@Sun.COM } 279910616SSebastien.Roy@Sun.COM 280010616SSebastien.Roy@Sun.COM /* 280110616SSebastien.Roy@Sun.COM * As described in section 3.2.2 of RFC4213, if the packet payload is 280210616SSebastien.Roy@Sun.COM * less than or equal to the minimum MTU size, then we need to allow 280310616SSebastien.Roy@Sun.COM * IPv4 to fragment the packet. The reason is that even if we end up 280410616SSebastien.Roy@Sun.COM * receiving an ICMP frag-needed, the interface above this tunnel 280510616SSebastien.Roy@Sun.COM * won't be allowed to drop its MTU as a result, since the packet was 280610616SSebastien.Roy@Sun.COM * already smaller than the smallest allowable MTU for that interface. 280710616SSebastien.Roy@Sun.COM */ 280810616SSebastien.Roy@Sun.COM if (mp->b_wptr - innerptr <= minmtu) 280910616SSebastien.Roy@Sun.COM outer4->ipha_fragment_offset_and_flags = 0; 281010616SSebastien.Roy@Sun.COM 281110616SSebastien.Roy@Sun.COM outer4->ipha_length = htons(msgdsize(mp)); 281210616SSebastien.Roy@Sun.COM 281310616SSebastien.Roy@Sun.COM return (mp); 281410616SSebastien.Roy@Sun.COM } 281510616SSebastien.Roy@Sun.COM 281610616SSebastien.Roy@Sun.COM /* 281710616SSebastien.Roy@Sun.COM * Insert an encapsulation limit destination option in the packet provided. 281810616SSebastien.Roy@Sun.COM * Always consumes the mp argument and returns a new mblk pointer. 281910616SSebastien.Roy@Sun.COM */ 282010616SSebastien.Roy@Sun.COM static mblk_t * 282110616SSebastien.Roy@Sun.COM iptun_insert_encaplimit(iptun_t *iptun, mblk_t *mp, ip6_t *outer6, 282210616SSebastien.Roy@Sun.COM uint8_t limit) 282310616SSebastien.Roy@Sun.COM { 282410616SSebastien.Roy@Sun.COM mblk_t *newmp; 282510616SSebastien.Roy@Sun.COM iptun_ipv6hdrs_t *newouter6; 282610616SSebastien.Roy@Sun.COM 282710616SSebastien.Roy@Sun.COM ASSERT(outer6->ip6_nxt == IPPROTO_IPV6); 282810616SSebastien.Roy@Sun.COM ASSERT(mp->b_cont == NULL); 282910616SSebastien.Roy@Sun.COM 283010616SSebastien.Roy@Sun.COM mp->b_rptr += sizeof (ip6_t); 283110616SSebastien.Roy@Sun.COM newmp = allocb_tmpl(sizeof (iptun_ipv6hdrs_t) + MBLKL(mp), mp); 283210616SSebastien.Roy@Sun.COM if (newmp == NULL) { 283310616SSebastien.Roy@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_noxmtbuf); 283410616SSebastien.Roy@Sun.COM return (NULL); 283510616SSebastien.Roy@Sun.COM } 283610616SSebastien.Roy@Sun.COM newmp->b_wptr += sizeof (iptun_ipv6hdrs_t); 283710616SSebastien.Roy@Sun.COM /* Copy the payload (Starting with the inner IPv6 header). */ 283810616SSebastien.Roy@Sun.COM bcopy(mp->b_rptr, newmp->b_wptr, MBLKL(mp)); 283910616SSebastien.Roy@Sun.COM newmp->b_wptr += MBLKL(mp); 284010616SSebastien.Roy@Sun.COM newouter6 = (iptun_ipv6hdrs_t *)newmp->b_rptr; 284110616SSebastien.Roy@Sun.COM /* Now copy the outer IPv6 header. */ 284210616SSebastien.Roy@Sun.COM bcopy(outer6, &newouter6->it6h_ip6h, sizeof (ip6_t)); 284310616SSebastien.Roy@Sun.COM newouter6->it6h_ip6h.ip6_nxt = IPPROTO_DSTOPTS; 284410616SSebastien.Roy@Sun.COM newouter6->it6h_encaplim = iptun_encaplim_init; 284510616SSebastien.Roy@Sun.COM newouter6->it6h_encaplim.iel_destopt.ip6d_nxt = outer6->ip6_nxt; 284610616SSebastien.Roy@Sun.COM newouter6->it6h_encaplim.iel_telopt.ip6ot_encap_limit = limit; 284710616SSebastien.Roy@Sun.COM 284810616SSebastien.Roy@Sun.COM /* 284910616SSebastien.Roy@Sun.COM * The payload length will be set at the end of 285010616SSebastien.Roy@Sun.COM * iptun_out_process_ipv6(). 285110616SSebastien.Roy@Sun.COM */ 285210616SSebastien.Roy@Sun.COM 285310616SSebastien.Roy@Sun.COM freemsg(mp); 285410616SSebastien.Roy@Sun.COM return (newmp); 285510616SSebastien.Roy@Sun.COM } 285610616SSebastien.Roy@Sun.COM 285710616SSebastien.Roy@Sun.COM /* 285810616SSebastien.Roy@Sun.COM * Process output packets with outer IPv6 headers. Frees mp and bumps stats 285910616SSebastien.Roy@Sun.COM * on error. 286010616SSebastien.Roy@Sun.COM */ 286110616SSebastien.Roy@Sun.COM static mblk_t * 286210616SSebastien.Roy@Sun.COM iptun_out_process_ipv6(iptun_t *iptun, mblk_t *mp, ip6_t *outer6, ip6_t *inner6) 286310616SSebastien.Roy@Sun.COM { 286410616SSebastien.Roy@Sun.COM uint8_t *limit, *configlimit; 286510616SSebastien.Roy@Sun.COM uint32_t offset; 286610616SSebastien.Roy@Sun.COM iptun_ipv6hdrs_t *v6hdrs; 286710616SSebastien.Roy@Sun.COM 286810616SSebastien.Roy@Sun.COM if (inner6 != NULL && iptun_find_encaplimit(mp, inner6, &limit)) { 286910616SSebastien.Roy@Sun.COM /* 287010616SSebastien.Roy@Sun.COM * The inner packet is an IPv6 packet which itself contains an 287110616SSebastien.Roy@Sun.COM * encapsulation limit option. The limit variable points to 287210616SSebastien.Roy@Sun.COM * the value in the embedded option. Process the 287310616SSebastien.Roy@Sun.COM * encapsulation limit option as specified in RFC 2473. 287410616SSebastien.Roy@Sun.COM * 287510616SSebastien.Roy@Sun.COM * If limit is 0, then we've exceeded the limit and we need to 287610616SSebastien.Roy@Sun.COM * send back an ICMPv6 parameter problem message. 287710616SSebastien.Roy@Sun.COM * 287810616SSebastien.Roy@Sun.COM * If limit is > 0, then we decrement it by 1 and make sure 287910616SSebastien.Roy@Sun.COM * that the encapsulation limit option in the outer header 288010616SSebastien.Roy@Sun.COM * reflects that (adding an option if one isn't already 288110616SSebastien.Roy@Sun.COM * there). 288210616SSebastien.Roy@Sun.COM */ 288310616SSebastien.Roy@Sun.COM ASSERT(limit > mp->b_rptr && limit < mp->b_wptr); 288410616SSebastien.Roy@Sun.COM if (*limit == 0) { 288510616SSebastien.Roy@Sun.COM mp->b_rptr = (uint8_t *)inner6; 288610616SSebastien.Roy@Sun.COM offset = limit - mp->b_rptr; 288710616SSebastien.Roy@Sun.COM iptun_icmp_error_v6(iptun, inner6, mp, ICMP6_PARAM_PROB, 288810616SSebastien.Roy@Sun.COM 0, offset); 288910616SSebastien.Roy@Sun.COM atomic_inc_64(&iptun->iptun_noxmtbuf); 289010616SSebastien.Roy@Sun.COM return (NULL); 289110616SSebastien.Roy@Sun.COM } 289210616SSebastien.Roy@Sun.COM 289310616SSebastien.Roy@Sun.COM /* 289410616SSebastien.Roy@Sun.COM * The outer header requires an encapsulation limit option. 289510616SSebastien.Roy@Sun.COM * If there isn't one already, add one. 289610616SSebastien.Roy@Sun.COM */ 289710616SSebastien.Roy@Sun.COM if (iptun->iptun_encaplimit == 0) { 289810616SSebastien.Roy@Sun.COM if ((mp = iptun_insert_encaplimit(iptun, mp, outer6, 289910616SSebastien.Roy@Sun.COM (*limit - 1))) == NULL) 290010616SSebastien.Roy@Sun.COM return (NULL); 290110616SSebastien.Roy@Sun.COM } else { 290210616SSebastien.Roy@Sun.COM /* 290310616SSebastien.Roy@Sun.COM * There is an existing encapsulation limit option in 290410616SSebastien.Roy@Sun.COM * the outer header. If the inner encapsulation limit 290510616SSebastien.Roy@Sun.COM * is less than the configured encapsulation limit, 290610616SSebastien.Roy@Sun.COM * update the outer encapsulation limit to reflect 290710616SSebastien.Roy@Sun.COM * this lesser value. 290810616SSebastien.Roy@Sun.COM */ 290910616SSebastien.Roy@Sun.COM v6hdrs = (iptun_ipv6hdrs_t *)mp->b_rptr; 291010616SSebastien.Roy@Sun.COM configlimit = 291110616SSebastien.Roy@Sun.COM &v6hdrs->it6h_encaplim.iel_telopt.ip6ot_encap_limit; 291210616SSebastien.Roy@Sun.COM if ((*limit - 1) < *configlimit) 291310616SSebastien.Roy@Sun.COM *configlimit = (*limit - 1); 291410616SSebastien.Roy@Sun.COM } 291510616SSebastien.Roy@Sun.COM } 291610616SSebastien.Roy@Sun.COM 291710616SSebastien.Roy@Sun.COM outer6->ip6_plen = htons(msgdsize(mp) - sizeof (ip6_t)); 291810616SSebastien.Roy@Sun.COM return (mp); 291910616SSebastien.Roy@Sun.COM } 292010616SSebastien.Roy@Sun.COM 292110616SSebastien.Roy@Sun.COM /* 292210616SSebastien.Roy@Sun.COM * The IP tunneling MAC-type plugins have already done most of the header 292310616SSebastien.Roy@Sun.COM * processing and validity checks. We are simply responsible for multiplexing 292410616SSebastien.Roy@Sun.COM * down to the ip module below us. 292510616SSebastien.Roy@Sun.COM */ 292610616SSebastien.Roy@Sun.COM static void 292710616SSebastien.Roy@Sun.COM iptun_output(iptun_t *iptun, mblk_t *mp) 292810616SSebastien.Roy@Sun.COM { 292910616SSebastien.Roy@Sun.COM conn_t *connp = iptun->iptun_connp; 293010616SSebastien.Roy@Sun.COM int outer_hlen; 293110616SSebastien.Roy@Sun.COM mblk_t *newmp; 293210616SSebastien.Roy@Sun.COM ipha_t *outer4, *inner4; 293310616SSebastien.Roy@Sun.COM ip6_t *outer6, *inner6; 293410616SSebastien.Roy@Sun.COM ipsec_tun_pol_t *itp = iptun->iptun_itp; 293510616SSebastien.Roy@Sun.COM 293610616SSebastien.Roy@Sun.COM ASSERT(mp->b_datap->db_type == M_DATA); 293710616SSebastien.Roy@Sun.COM 293810616SSebastien.Roy@Sun.COM if (mp->b_cont != NULL) { 293910616SSebastien.Roy@Sun.COM if ((newmp = msgpullup(mp, -1)) == NULL) { 294010616SSebastien.Roy@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_noxmtbuf); 294110616SSebastien.Roy@Sun.COM return; 294210616SSebastien.Roy@Sun.COM } 294310616SSebastien.Roy@Sun.COM freemsg(mp); 294410616SSebastien.Roy@Sun.COM mp = newmp; 294510616SSebastien.Roy@Sun.COM } 294610616SSebastien.Roy@Sun.COM 294710616SSebastien.Roy@Sun.COM outer_hlen = iptun_find_headers(mp, &outer4, &inner4, &outer6, &inner6); 294810616SSebastien.Roy@Sun.COM if (outer_hlen == 0) { 294910616SSebastien.Roy@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_oerrors); 295010616SSebastien.Roy@Sun.COM return; 295110616SSebastien.Roy@Sun.COM } 295210616SSebastien.Roy@Sun.COM 295310616SSebastien.Roy@Sun.COM /* Perform header processing. */ 295410616SSebastien.Roy@Sun.COM if (outer4 != NULL) 295510616SSebastien.Roy@Sun.COM mp = iptun_out_process_ipv4(iptun, mp, outer4, inner4, inner6); 295610616SSebastien.Roy@Sun.COM else 295710616SSebastien.Roy@Sun.COM mp = iptun_out_process_ipv6(iptun, mp, outer6, inner6); 295810616SSebastien.Roy@Sun.COM if (mp == NULL) 295910616SSebastien.Roy@Sun.COM return; 296010616SSebastien.Roy@Sun.COM 296110616SSebastien.Roy@Sun.COM /* 296210616SSebastien.Roy@Sun.COM * Let's hope the compiler optimizes this with "branch taken". 296310616SSebastien.Roy@Sun.COM */ 296410616SSebastien.Roy@Sun.COM if (itp != NULL && (itp->itp_flags & ITPF_P_ACTIVE)) { 296510616SSebastien.Roy@Sun.COM if ((mp = ipsec_tun_outbound(mp, iptun, inner4, inner6, outer4, 296610616SSebastien.Roy@Sun.COM outer6, outer_hlen)) == NULL) { 296710616SSebastien.Roy@Sun.COM /* ipsec_tun_outbound() frees mp on error. */ 296810616SSebastien.Roy@Sun.COM atomic_inc_64(&iptun->iptun_oerrors); 296910616SSebastien.Roy@Sun.COM return; 297010616SSebastien.Roy@Sun.COM } 297110616SSebastien.Roy@Sun.COM /* 297210616SSebastien.Roy@Sun.COM * ipsec_tun_outbound() returns a chain of tunneled IP 297310616SSebastien.Roy@Sun.COM * fragments linked with b_next (or a single message if the 297410616SSebastien.Roy@Sun.COM * tunneled packet wasn't a fragment). Each message in the 297510616SSebastien.Roy@Sun.COM * chain is prepended by an IPSEC_OUT M_CTL block with 297610616SSebastien.Roy@Sun.COM * instructions for outbound IPsec processing. 297710616SSebastien.Roy@Sun.COM */ 297810616SSebastien.Roy@Sun.COM for (newmp = mp; newmp != NULL; newmp = mp) { 297910616SSebastien.Roy@Sun.COM ASSERT(newmp->b_datap->db_type == M_CTL); 298010616SSebastien.Roy@Sun.COM atomic_inc_64(&iptun->iptun_opackets); 298110616SSebastien.Roy@Sun.COM atomic_add_64(&iptun->iptun_obytes, 298210616SSebastien.Roy@Sun.COM msgdsize(newmp->b_cont)); 298310616SSebastien.Roy@Sun.COM mp = mp->b_next; 298410616SSebastien.Roy@Sun.COM newmp->b_next = NULL; 298510616SSebastien.Roy@Sun.COM connp->conn_send(connp, newmp, connp->conn_wq, IP_WPUT); 298610616SSebastien.Roy@Sun.COM } 298710616SSebastien.Roy@Sun.COM } else { 298810616SSebastien.Roy@Sun.COM /* 298910616SSebastien.Roy@Sun.COM * The ip module will potentially apply global policy to the 299010616SSebastien.Roy@Sun.COM * packet in its output path if there's no active tunnel 299110616SSebastien.Roy@Sun.COM * policy. 299210616SSebastien.Roy@Sun.COM */ 299310616SSebastien.Roy@Sun.COM atomic_inc_64(&iptun->iptun_opackets); 299410616SSebastien.Roy@Sun.COM atomic_add_64(&iptun->iptun_obytes, msgdsize(mp)); 299510616SSebastien.Roy@Sun.COM connp->conn_send(connp, mp, connp->conn_wq, IP_WPUT); 299610616SSebastien.Roy@Sun.COM } 299710616SSebastien.Roy@Sun.COM } 299810616SSebastien.Roy@Sun.COM 299910616SSebastien.Roy@Sun.COM /* 300010616SSebastien.Roy@Sun.COM * Note that the setting or clearing iptun_{set,get}_g_q() is serialized via 300110616SSebastien.Roy@Sun.COM * iptuns_lock and iptunq_open(), so we must never be in a situation where 300210616SSebastien.Roy@Sun.COM * iptun_set_g_q() is called if the queue has already been set or vice versa 300310616SSebastien.Roy@Sun.COM * (hence the ASSERT()s.) 300410616SSebastien.Roy@Sun.COM */ 300510616SSebastien.Roy@Sun.COM void 300610616SSebastien.Roy@Sun.COM iptun_set_g_q(netstack_t *ns, queue_t *q) 300710616SSebastien.Roy@Sun.COM { 300810616SSebastien.Roy@Sun.COM ASSERT(ns->netstack_iptun->iptuns_g_q == NULL); 300910616SSebastien.Roy@Sun.COM ns->netstack_iptun->iptuns_g_q = q; 301010616SSebastien.Roy@Sun.COM } 301110616SSebastien.Roy@Sun.COM 301210616SSebastien.Roy@Sun.COM void 301310616SSebastien.Roy@Sun.COM iptun_clear_g_q(netstack_t *ns) 301410616SSebastien.Roy@Sun.COM { 301510616SSebastien.Roy@Sun.COM ASSERT(ns->netstack_iptun->iptuns_g_q != NULL); 301610616SSebastien.Roy@Sun.COM ns->netstack_iptun->iptuns_g_q = NULL; 301710616SSebastien.Roy@Sun.COM } 301810616SSebastien.Roy@Sun.COM 301910616SSebastien.Roy@Sun.COM static mac_callbacks_t iptun_m_callbacks = { 302010616SSebastien.Roy@Sun.COM .mc_callbacks = (MC_SETPROP | MC_GETPROP), 302110616SSebastien.Roy@Sun.COM .mc_getstat = iptun_m_getstat, 302210616SSebastien.Roy@Sun.COM .mc_start = iptun_m_start, 302310616SSebastien.Roy@Sun.COM .mc_stop = iptun_m_stop, 302410616SSebastien.Roy@Sun.COM .mc_setpromisc = iptun_m_setpromisc, 302510616SSebastien.Roy@Sun.COM .mc_multicst = iptun_m_multicst, 302610616SSebastien.Roy@Sun.COM .mc_unicst = iptun_m_unicst, 302710616SSebastien.Roy@Sun.COM .mc_tx = iptun_m_tx, 302810616SSebastien.Roy@Sun.COM .mc_setprop = iptun_m_setprop, 302910616SSebastien.Roy@Sun.COM .mc_getprop = iptun_m_getprop 303010616SSebastien.Roy@Sun.COM }; 3031