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 /* 2211456Sdanmcd@sun.com * Copyright 2010 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> 7911042SErik.Nordmark@Sun.COM #include <sys/tsol/label.h> 8011042SErik.Nordmark@Sun.COM #include <sys/tsol/tnet.h> 8110616SSebastien.Roy@Sun.COM #include <inet/iptun.h> 8210616SSebastien.Roy@Sun.COM #include "iptun_impl.h" 8310616SSebastien.Roy@Sun.COM 8410616SSebastien.Roy@Sun.COM /* Do the tunnel type and address family match? */ 8510616SSebastien.Roy@Sun.COM #define IPTUN_ADDR_MATCH(iptun_type, family) \ 8610616SSebastien.Roy@Sun.COM ((iptun_type == IPTUN_TYPE_IPV4 && family == AF_INET) || \ 8710616SSebastien.Roy@Sun.COM (iptun_type == IPTUN_TYPE_IPV6 && family == AF_INET6) || \ 8810616SSebastien.Roy@Sun.COM (iptun_type == IPTUN_TYPE_6TO4 && family == AF_INET)) 8910616SSebastien.Roy@Sun.COM 9010616SSebastien.Roy@Sun.COM #define IPTUN_HASH_KEY(key) ((mod_hash_key_t)(uintptr_t)(key)) 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 11611042SErik.Nordmark@Sun.COM /* 11711042SErik.Nordmark@Sun.COM * Table containing per-iptun-type information. 11811042SErik.Nordmark@Sun.COM * Since IPv6 can run over all of these we have the IPv6 min as the min MTU. 11911042SErik.Nordmark@Sun.COM */ 12010616SSebastien.Roy@Sun.COM static iptun_typeinfo_t iptun_type_table[] = { 12111042SErik.Nordmark@Sun.COM { IPTUN_TYPE_IPV4, MAC_PLUGIN_IDENT_IPV4, IPV4_VERSION, 12211042SErik.Nordmark@Sun.COM IPTUN_MIN_IPV6_MTU, IPTUN_MAX_IPV4_MTU, B_TRUE }, 12311042SErik.Nordmark@Sun.COM { IPTUN_TYPE_IPV6, MAC_PLUGIN_IDENT_IPV6, IPV6_VERSION, 12410616SSebastien.Roy@Sun.COM IPTUN_MIN_IPV6_MTU, IPTUN_MAX_IPV6_MTU, B_TRUE }, 12511042SErik.Nordmark@Sun.COM { IPTUN_TYPE_6TO4, MAC_PLUGIN_IDENT_6TO4, IPV4_VERSION, 12611042SErik.Nordmark@Sun.COM IPTUN_MIN_IPV6_MTU, IPTUN_MAX_IPV4_MTU, B_FALSE }, 12711042SErik.Nordmark@Sun.COM { IPTUN_TYPE_UNKNOWN, NULL, 0, 0, 0, B_FALSE } 12810616SSebastien.Roy@Sun.COM }; 12910616SSebastien.Roy@Sun.COM 13010616SSebastien.Roy@Sun.COM /* 13110616SSebastien.Roy@Sun.COM * iptun_hash is an iptun_t lookup table by link ID protected by 13210616SSebastien.Roy@Sun.COM * iptun_hash_lock. While the hash table's integrity is maintained via 13310616SSebastien.Roy@Sun.COM * internal locking in the mod_hash_*() functions, we need additional locking 13410616SSebastien.Roy@Sun.COM * so that an iptun_t cannot be deleted after a hash lookup has returned an 13510616SSebastien.Roy@Sun.COM * iptun_t and before iptun_lock has been entered. As such, we use 13610616SSebastien.Roy@Sun.COM * iptun_hash_lock when doing lookups and removals from iptun_hash. 13710616SSebastien.Roy@Sun.COM */ 13810616SSebastien.Roy@Sun.COM mod_hash_t *iptun_hash; 13910616SSebastien.Roy@Sun.COM static kmutex_t iptun_hash_lock; 14010616SSebastien.Roy@Sun.COM 14110616SSebastien.Roy@Sun.COM static uint_t iptun_tunnelcount; /* total for all stacks */ 14210616SSebastien.Roy@Sun.COM kmem_cache_t *iptun_cache; 14310616SSebastien.Roy@Sun.COM ddi_taskq_t *iptun_taskq; 14410616SSebastien.Roy@Sun.COM 14510616SSebastien.Roy@Sun.COM typedef enum { 14610616SSebastien.Roy@Sun.COM IPTUN_TASK_MTU_UPDATE, /* tell mac about new tunnel link MTU */ 14710616SSebastien.Roy@Sun.COM IPTUN_TASK_LADDR_UPDATE, /* tell mac about new local address */ 14810616SSebastien.Roy@Sun.COM IPTUN_TASK_RADDR_UPDATE, /* tell mac about new remote address */ 14910616SSebastien.Roy@Sun.COM IPTUN_TASK_LINK_UPDATE, /* tell mac about new link state */ 15010616SSebastien.Roy@Sun.COM IPTUN_TASK_PDATA_UPDATE /* tell mac about updated plugin data */ 15110616SSebastien.Roy@Sun.COM } iptun_task_t; 15210616SSebastien.Roy@Sun.COM 15310616SSebastien.Roy@Sun.COM typedef struct iptun_task_data_s { 15410616SSebastien.Roy@Sun.COM iptun_task_t itd_task; 15510616SSebastien.Roy@Sun.COM datalink_id_t itd_linkid; 15610616SSebastien.Roy@Sun.COM } iptun_task_data_t; 15710616SSebastien.Roy@Sun.COM 15810616SSebastien.Roy@Sun.COM static void iptun_task_dispatch(iptun_t *, iptun_task_t); 15910616SSebastien.Roy@Sun.COM static int iptun_enter(iptun_t *); 16010616SSebastien.Roy@Sun.COM static void iptun_exit(iptun_t *); 16110616SSebastien.Roy@Sun.COM static void iptun_headergen(iptun_t *, boolean_t); 16210616SSebastien.Roy@Sun.COM static void iptun_drop_pkt(mblk_t *, uint64_t *); 16311042SErik.Nordmark@Sun.COM static void iptun_input(void *, mblk_t *, void *, ip_recv_attr_t *); 16411042SErik.Nordmark@Sun.COM static void iptun_input_icmp(void *, mblk_t *, void *, ip_recv_attr_t *); 16510616SSebastien.Roy@Sun.COM static void iptun_output(iptun_t *, mblk_t *); 16611042SErik.Nordmark@Sun.COM static uint32_t iptun_get_maxmtu(iptun_t *, ip_xmit_attr_t *, uint32_t); 16711042SErik.Nordmark@Sun.COM static uint32_t iptun_update_mtu(iptun_t *, ip_xmit_attr_t *, uint32_t); 16811042SErik.Nordmark@Sun.COM static uint32_t iptun_get_dst_pmtu(iptun_t *, ip_xmit_attr_t *); 16911042SErik.Nordmark@Sun.COM static void iptun_update_dst_pmtu(iptun_t *, ip_xmit_attr_t *); 17010616SSebastien.Roy@Sun.COM static int iptun_setladdr(iptun_t *, const struct sockaddr_storage *); 17110616SSebastien.Roy@Sun.COM 17211042SErik.Nordmark@Sun.COM static void iptun_output_6to4(iptun_t *, mblk_t *); 17311042SErik.Nordmark@Sun.COM static void iptun_output_common(iptun_t *, ip_xmit_attr_t *, mblk_t *); 17411042SErik.Nordmark@Sun.COM static boolean_t iptun_verifyicmp(conn_t *, void *, icmph_t *, icmp6_t *, 17511042SErik.Nordmark@Sun.COM ip_recv_attr_t *); 17611042SErik.Nordmark@Sun.COM 17711042SErik.Nordmark@Sun.COM static void iptun_notify(void *, ip_xmit_attr_t *, ixa_notify_type_t, 17811042SErik.Nordmark@Sun.COM ixa_notify_arg_t); 17911042SErik.Nordmark@Sun.COM 18010616SSebastien.Roy@Sun.COM static mac_callbacks_t iptun_m_callbacks; 18110616SSebastien.Roy@Sun.COM 18210616SSebastien.Roy@Sun.COM static int 18310616SSebastien.Roy@Sun.COM iptun_m_getstat(void *arg, uint_t stat, uint64_t *val) 18410616SSebastien.Roy@Sun.COM { 18510616SSebastien.Roy@Sun.COM iptun_t *iptun = arg; 18610616SSebastien.Roy@Sun.COM int err = 0; 18710616SSebastien.Roy@Sun.COM 18810616SSebastien.Roy@Sun.COM switch (stat) { 18910616SSebastien.Roy@Sun.COM case MAC_STAT_IERRORS: 19010616SSebastien.Roy@Sun.COM *val = iptun->iptun_ierrors; 19110616SSebastien.Roy@Sun.COM break; 19210616SSebastien.Roy@Sun.COM case MAC_STAT_OERRORS: 19310616SSebastien.Roy@Sun.COM *val = iptun->iptun_oerrors; 19410616SSebastien.Roy@Sun.COM break; 19510616SSebastien.Roy@Sun.COM case MAC_STAT_RBYTES: 19610616SSebastien.Roy@Sun.COM *val = iptun->iptun_rbytes; 19710616SSebastien.Roy@Sun.COM break; 19810616SSebastien.Roy@Sun.COM case MAC_STAT_IPACKETS: 19910616SSebastien.Roy@Sun.COM *val = iptun->iptun_ipackets; 20010616SSebastien.Roy@Sun.COM break; 20110616SSebastien.Roy@Sun.COM case MAC_STAT_OBYTES: 20210616SSebastien.Roy@Sun.COM *val = iptun->iptun_obytes; 20310616SSebastien.Roy@Sun.COM break; 20410616SSebastien.Roy@Sun.COM case MAC_STAT_OPACKETS: 20510616SSebastien.Roy@Sun.COM *val = iptun->iptun_opackets; 20610616SSebastien.Roy@Sun.COM break; 20710616SSebastien.Roy@Sun.COM case MAC_STAT_NORCVBUF: 20810616SSebastien.Roy@Sun.COM *val = iptun->iptun_norcvbuf; 20910616SSebastien.Roy@Sun.COM break; 21010616SSebastien.Roy@Sun.COM case MAC_STAT_NOXMTBUF: 21110616SSebastien.Roy@Sun.COM *val = iptun->iptun_noxmtbuf; 21210616SSebastien.Roy@Sun.COM break; 21310616SSebastien.Roy@Sun.COM default: 21410616SSebastien.Roy@Sun.COM err = ENOTSUP; 21510616SSebastien.Roy@Sun.COM } 21610616SSebastien.Roy@Sun.COM 21710616SSebastien.Roy@Sun.COM return (err); 21810616SSebastien.Roy@Sun.COM } 21910616SSebastien.Roy@Sun.COM 22010616SSebastien.Roy@Sun.COM static int 22110616SSebastien.Roy@Sun.COM iptun_m_start(void *arg) 22210616SSebastien.Roy@Sun.COM { 22310616SSebastien.Roy@Sun.COM iptun_t *iptun = arg; 22410616SSebastien.Roy@Sun.COM int err; 22510616SSebastien.Roy@Sun.COM 22610616SSebastien.Roy@Sun.COM if ((err = iptun_enter(iptun)) == 0) { 22710616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_MAC_STARTED; 22810616SSebastien.Roy@Sun.COM iptun_task_dispatch(iptun, IPTUN_TASK_LINK_UPDATE); 22910616SSebastien.Roy@Sun.COM iptun_exit(iptun); 23010616SSebastien.Roy@Sun.COM } 23110616SSebastien.Roy@Sun.COM return (err); 23210616SSebastien.Roy@Sun.COM } 23310616SSebastien.Roy@Sun.COM 23410616SSebastien.Roy@Sun.COM static void 23510616SSebastien.Roy@Sun.COM iptun_m_stop(void *arg) 23610616SSebastien.Roy@Sun.COM { 23710616SSebastien.Roy@Sun.COM iptun_t *iptun = arg; 23810616SSebastien.Roy@Sun.COM 23910616SSebastien.Roy@Sun.COM if (iptun_enter(iptun) == 0) { 24010616SSebastien.Roy@Sun.COM iptun->iptun_flags &= ~IPTUN_MAC_STARTED; 24110616SSebastien.Roy@Sun.COM iptun_task_dispatch(iptun, IPTUN_TASK_LINK_UPDATE); 24210616SSebastien.Roy@Sun.COM iptun_exit(iptun); 24310616SSebastien.Roy@Sun.COM } 24410616SSebastien.Roy@Sun.COM } 24510616SSebastien.Roy@Sun.COM 24610616SSebastien.Roy@Sun.COM /* 24710616SSebastien.Roy@Sun.COM * iptun_m_setpromisc() does nothing and always succeeds. This is because a 24810616SSebastien.Roy@Sun.COM * tunnel data-link only ever receives packets that are destined exclusively 24910616SSebastien.Roy@Sun.COM * for the local address of the tunnel. 25010616SSebastien.Roy@Sun.COM */ 25110616SSebastien.Roy@Sun.COM /* ARGSUSED */ 25210616SSebastien.Roy@Sun.COM static int 25310616SSebastien.Roy@Sun.COM iptun_m_setpromisc(void *arg, boolean_t on) 25410616SSebastien.Roy@Sun.COM { 25510616SSebastien.Roy@Sun.COM return (0); 25610616SSebastien.Roy@Sun.COM } 25710616SSebastien.Roy@Sun.COM 25810616SSebastien.Roy@Sun.COM /* ARGSUSED */ 25910616SSebastien.Roy@Sun.COM static int 26010616SSebastien.Roy@Sun.COM iptun_m_multicst(void *arg, boolean_t add, const uint8_t *addrp) 26110616SSebastien.Roy@Sun.COM { 26210616SSebastien.Roy@Sun.COM return (ENOTSUP); 26310616SSebastien.Roy@Sun.COM } 26410616SSebastien.Roy@Sun.COM 26510616SSebastien.Roy@Sun.COM /* 26610616SSebastien.Roy@Sun.COM * iptun_m_unicst() sets the local address. 26710616SSebastien.Roy@Sun.COM */ 26810616SSebastien.Roy@Sun.COM /* ARGSUSED */ 26910616SSebastien.Roy@Sun.COM static int 27010616SSebastien.Roy@Sun.COM iptun_m_unicst(void *arg, const uint8_t *addrp) 27110616SSebastien.Roy@Sun.COM { 27210616SSebastien.Roy@Sun.COM iptun_t *iptun = arg; 27310616SSebastien.Roy@Sun.COM int err; 27410616SSebastien.Roy@Sun.COM struct sockaddr_storage ss; 27510616SSebastien.Roy@Sun.COM struct sockaddr_in *sin; 27610616SSebastien.Roy@Sun.COM struct sockaddr_in6 *sin6; 27710616SSebastien.Roy@Sun.COM 27810616SSebastien.Roy@Sun.COM if ((err = iptun_enter(iptun)) == 0) { 27910616SSebastien.Roy@Sun.COM switch (iptun->iptun_typeinfo->iti_ipvers) { 28010616SSebastien.Roy@Sun.COM case IPV4_VERSION: 28110616SSebastien.Roy@Sun.COM sin = (struct sockaddr_in *)&ss; 28210616SSebastien.Roy@Sun.COM sin->sin_family = AF_INET; 28310616SSebastien.Roy@Sun.COM bcopy(addrp, &sin->sin_addr, sizeof (in_addr_t)); 28410616SSebastien.Roy@Sun.COM break; 28510616SSebastien.Roy@Sun.COM case IPV6_VERSION: 28610616SSebastien.Roy@Sun.COM sin6 = (struct sockaddr_in6 *)&ss; 28710616SSebastien.Roy@Sun.COM sin6->sin6_family = AF_INET6; 28810616SSebastien.Roy@Sun.COM bcopy(addrp, &sin6->sin6_addr, sizeof (in6_addr_t)); 28910616SSebastien.Roy@Sun.COM break; 29010616SSebastien.Roy@Sun.COM default: 29110616SSebastien.Roy@Sun.COM ASSERT(0); 29210616SSebastien.Roy@Sun.COM } 29310616SSebastien.Roy@Sun.COM err = iptun_setladdr(iptun, &ss); 29410616SSebastien.Roy@Sun.COM iptun_exit(iptun); 29510616SSebastien.Roy@Sun.COM } 29610616SSebastien.Roy@Sun.COM return (err); 29710616SSebastien.Roy@Sun.COM } 29810616SSebastien.Roy@Sun.COM 29910616SSebastien.Roy@Sun.COM static mblk_t * 30010616SSebastien.Roy@Sun.COM iptun_m_tx(void *arg, mblk_t *mpchain) 30110616SSebastien.Roy@Sun.COM { 30210616SSebastien.Roy@Sun.COM mblk_t *mp, *nmp; 30310616SSebastien.Roy@Sun.COM iptun_t *iptun = arg; 30410616SSebastien.Roy@Sun.COM 30510616SSebastien.Roy@Sun.COM if (!IS_IPTUN_RUNNING(iptun)) { 30610616SSebastien.Roy@Sun.COM iptun_drop_pkt(mpchain, &iptun->iptun_noxmtbuf); 30710616SSebastien.Roy@Sun.COM return (NULL); 30810616SSebastien.Roy@Sun.COM } 30910616SSebastien.Roy@Sun.COM 31010616SSebastien.Roy@Sun.COM for (mp = mpchain; mp != NULL; mp = nmp) { 31110616SSebastien.Roy@Sun.COM nmp = mp->b_next; 31210616SSebastien.Roy@Sun.COM mp->b_next = NULL; 31310616SSebastien.Roy@Sun.COM iptun_output(iptun, mp); 31410616SSebastien.Roy@Sun.COM } 31510616SSebastien.Roy@Sun.COM 31610616SSebastien.Roy@Sun.COM return (NULL); 31710616SSebastien.Roy@Sun.COM } 31810616SSebastien.Roy@Sun.COM 31910616SSebastien.Roy@Sun.COM /* ARGSUSED */ 32010616SSebastien.Roy@Sun.COM static int 32110616SSebastien.Roy@Sun.COM iptun_m_setprop(void *barg, const char *pr_name, mac_prop_id_t pr_num, 32210616SSebastien.Roy@Sun.COM uint_t pr_valsize, const void *pr_val) 32310616SSebastien.Roy@Sun.COM { 32410616SSebastien.Roy@Sun.COM iptun_t *iptun = barg; 32510616SSebastien.Roy@Sun.COM uint32_t value = *(uint32_t *)pr_val; 32610616SSebastien.Roy@Sun.COM int err; 32710616SSebastien.Roy@Sun.COM 32810616SSebastien.Roy@Sun.COM /* 32910616SSebastien.Roy@Sun.COM * We need to enter this iptun_t since we'll be modifying the outer 33010616SSebastien.Roy@Sun.COM * header. 33110616SSebastien.Roy@Sun.COM */ 33210616SSebastien.Roy@Sun.COM if ((err = iptun_enter(iptun)) != 0) 33310616SSebastien.Roy@Sun.COM return (err); 33410616SSebastien.Roy@Sun.COM 33510616SSebastien.Roy@Sun.COM switch (pr_num) { 33610616SSebastien.Roy@Sun.COM case MAC_PROP_IPTUN_HOPLIMIT: 33710616SSebastien.Roy@Sun.COM if (value < IPTUN_MIN_HOPLIMIT || value > IPTUN_MAX_HOPLIMIT) { 33810616SSebastien.Roy@Sun.COM err = EINVAL; 33910616SSebastien.Roy@Sun.COM break; 34010616SSebastien.Roy@Sun.COM } 34110616SSebastien.Roy@Sun.COM if (value != iptun->iptun_hoplimit) { 34210616SSebastien.Roy@Sun.COM iptun->iptun_hoplimit = (uint8_t)value; 34310616SSebastien.Roy@Sun.COM iptun_headergen(iptun, B_TRUE); 34410616SSebastien.Roy@Sun.COM } 34510616SSebastien.Roy@Sun.COM break; 34610616SSebastien.Roy@Sun.COM case MAC_PROP_IPTUN_ENCAPLIMIT: 34710616SSebastien.Roy@Sun.COM if (iptun->iptun_typeinfo->iti_type != IPTUN_TYPE_IPV6 || 34810616SSebastien.Roy@Sun.COM value > IPTUN_MAX_ENCAPLIMIT) { 34910616SSebastien.Roy@Sun.COM err = EINVAL; 35010616SSebastien.Roy@Sun.COM break; 35110616SSebastien.Roy@Sun.COM } 35210616SSebastien.Roy@Sun.COM if (value != iptun->iptun_encaplimit) { 35310616SSebastien.Roy@Sun.COM iptun->iptun_encaplimit = (uint8_t)value; 35410616SSebastien.Roy@Sun.COM iptun_headergen(iptun, B_TRUE); 35510616SSebastien.Roy@Sun.COM } 35610616SSebastien.Roy@Sun.COM break; 35710616SSebastien.Roy@Sun.COM case MAC_PROP_MTU: { 35811042SErik.Nordmark@Sun.COM uint32_t maxmtu = iptun_get_maxmtu(iptun, NULL, 0); 35910616SSebastien.Roy@Sun.COM 36010616SSebastien.Roy@Sun.COM if (value < iptun->iptun_typeinfo->iti_minmtu || 36110616SSebastien.Roy@Sun.COM value > maxmtu) { 36210616SSebastien.Roy@Sun.COM err = EINVAL; 36310616SSebastien.Roy@Sun.COM break; 36410616SSebastien.Roy@Sun.COM } 36510616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_FIXED_MTU; 36610616SSebastien.Roy@Sun.COM if (value != iptun->iptun_mtu) { 36710616SSebastien.Roy@Sun.COM iptun->iptun_mtu = value; 36810616SSebastien.Roy@Sun.COM iptun_task_dispatch(iptun, IPTUN_TASK_MTU_UPDATE); 36910616SSebastien.Roy@Sun.COM } 37010616SSebastien.Roy@Sun.COM break; 37110616SSebastien.Roy@Sun.COM } 37210616SSebastien.Roy@Sun.COM default: 37310616SSebastien.Roy@Sun.COM err = EINVAL; 37410616SSebastien.Roy@Sun.COM } 37510616SSebastien.Roy@Sun.COM iptun_exit(iptun); 37610616SSebastien.Roy@Sun.COM return (err); 37710616SSebastien.Roy@Sun.COM } 37810616SSebastien.Roy@Sun.COM 37910616SSebastien.Roy@Sun.COM /* ARGSUSED */ 38010616SSebastien.Roy@Sun.COM static int 38110616SSebastien.Roy@Sun.COM iptun_m_getprop(void *barg, const char *pr_name, mac_prop_id_t pr_num, 38210616SSebastien.Roy@Sun.COM uint_t pr_flags, uint_t pr_valsize, void *pr_val, uint_t *perm) 38310616SSebastien.Roy@Sun.COM { 38410616SSebastien.Roy@Sun.COM iptun_t *iptun = barg; 38510616SSebastien.Roy@Sun.COM mac_propval_range_t range; 38610616SSebastien.Roy@Sun.COM boolean_t is_default = (pr_flags & MAC_PROP_DEFAULT); 38710616SSebastien.Roy@Sun.COM boolean_t is_possible = (pr_flags & MAC_PROP_POSSIBLE); 38810616SSebastien.Roy@Sun.COM int err; 38910616SSebastien.Roy@Sun.COM 39010616SSebastien.Roy@Sun.COM if ((err = iptun_enter(iptun)) != 0) 39110616SSebastien.Roy@Sun.COM return (err); 39210616SSebastien.Roy@Sun.COM 39310616SSebastien.Roy@Sun.COM if ((pr_flags & ~(MAC_PROP_DEFAULT | MAC_PROP_POSSIBLE)) != 0) { 39410616SSebastien.Roy@Sun.COM err = ENOTSUP; 39510616SSebastien.Roy@Sun.COM goto done; 39610616SSebastien.Roy@Sun.COM } 39710616SSebastien.Roy@Sun.COM if (is_default && is_possible) { 39810616SSebastien.Roy@Sun.COM err = EINVAL; 39910616SSebastien.Roy@Sun.COM goto done; 40010616SSebastien.Roy@Sun.COM } 40110616SSebastien.Roy@Sun.COM 40210616SSebastien.Roy@Sun.COM *perm = MAC_PROP_PERM_RW; 40310616SSebastien.Roy@Sun.COM 40410616SSebastien.Roy@Sun.COM if (is_possible) { 40510616SSebastien.Roy@Sun.COM if (pr_valsize < sizeof (mac_propval_range_t)) { 40610616SSebastien.Roy@Sun.COM err = EINVAL; 40710616SSebastien.Roy@Sun.COM goto done; 40810616SSebastien.Roy@Sun.COM } 40910616SSebastien.Roy@Sun.COM range.mpr_count = 1; 41010616SSebastien.Roy@Sun.COM range.mpr_type = MAC_PROPVAL_UINT32; 41110616SSebastien.Roy@Sun.COM } else if (pr_valsize < sizeof (uint32_t)) { 41210616SSebastien.Roy@Sun.COM err = EINVAL; 41310616SSebastien.Roy@Sun.COM goto done; 41410616SSebastien.Roy@Sun.COM } 41510616SSebastien.Roy@Sun.COM 41610616SSebastien.Roy@Sun.COM switch (pr_num) { 41710616SSebastien.Roy@Sun.COM case MAC_PROP_IPTUN_HOPLIMIT: 41810616SSebastien.Roy@Sun.COM if (is_possible) { 41910616SSebastien.Roy@Sun.COM range.range_uint32[0].mpur_min = IPTUN_MIN_HOPLIMIT; 42010616SSebastien.Roy@Sun.COM range.range_uint32[0].mpur_max = IPTUN_MAX_HOPLIMIT; 42110616SSebastien.Roy@Sun.COM } else if (is_default) { 42210616SSebastien.Roy@Sun.COM *(uint32_t *)pr_val = IPTUN_DEFAULT_HOPLIMIT; 42310616SSebastien.Roy@Sun.COM } else { 42410616SSebastien.Roy@Sun.COM *(uint32_t *)pr_val = iptun->iptun_hoplimit; 42510616SSebastien.Roy@Sun.COM } 42610616SSebastien.Roy@Sun.COM break; 42710616SSebastien.Roy@Sun.COM case MAC_PROP_IPTUN_ENCAPLIMIT: 42810616SSebastien.Roy@Sun.COM if (iptun->iptun_typeinfo->iti_type != IPTUN_TYPE_IPV6) { 42910616SSebastien.Roy@Sun.COM err = ENOTSUP; 43010616SSebastien.Roy@Sun.COM goto done; 43110616SSebastien.Roy@Sun.COM } 43210616SSebastien.Roy@Sun.COM if (is_possible) { 43310616SSebastien.Roy@Sun.COM range.range_uint32[0].mpur_min = IPTUN_MIN_ENCAPLIMIT; 43410616SSebastien.Roy@Sun.COM range.range_uint32[0].mpur_max = IPTUN_MAX_ENCAPLIMIT; 43510616SSebastien.Roy@Sun.COM } else if (is_default) { 43610616SSebastien.Roy@Sun.COM *(uint32_t *)pr_val = IPTUN_DEFAULT_ENCAPLIMIT; 43710616SSebastien.Roy@Sun.COM } else { 43810616SSebastien.Roy@Sun.COM *(uint32_t *)pr_val = iptun->iptun_encaplimit; 43910616SSebastien.Roy@Sun.COM } 44010616SSebastien.Roy@Sun.COM break; 44110616SSebastien.Roy@Sun.COM case MAC_PROP_MTU: { 44211042SErik.Nordmark@Sun.COM uint32_t maxmtu = iptun_get_maxmtu(iptun, NULL, 0); 44310616SSebastien.Roy@Sun.COM 44410616SSebastien.Roy@Sun.COM if (is_possible) { 44510616SSebastien.Roy@Sun.COM range.range_uint32[0].mpur_min = 44610616SSebastien.Roy@Sun.COM iptun->iptun_typeinfo->iti_minmtu; 44710616SSebastien.Roy@Sun.COM range.range_uint32[0].mpur_max = maxmtu; 44810616SSebastien.Roy@Sun.COM } else { 44910616SSebastien.Roy@Sun.COM /* 45010616SSebastien.Roy@Sun.COM * The MAC module knows the current value and should 45110616SSebastien.Roy@Sun.COM * never call us for it. There is also no default 45210616SSebastien.Roy@Sun.COM * MTU, as by default, it is a dynamic property. 45310616SSebastien.Roy@Sun.COM */ 45410616SSebastien.Roy@Sun.COM err = ENOTSUP; 45510616SSebastien.Roy@Sun.COM goto done; 45610616SSebastien.Roy@Sun.COM } 45710616SSebastien.Roy@Sun.COM break; 45810616SSebastien.Roy@Sun.COM } 45910616SSebastien.Roy@Sun.COM default: 46010616SSebastien.Roy@Sun.COM err = EINVAL; 46110616SSebastien.Roy@Sun.COM goto done; 46210616SSebastien.Roy@Sun.COM } 46310616SSebastien.Roy@Sun.COM if (is_possible) 46410616SSebastien.Roy@Sun.COM bcopy(&range, pr_val, sizeof (range)); 46510616SSebastien.Roy@Sun.COM done: 46610616SSebastien.Roy@Sun.COM iptun_exit(iptun); 46710616SSebastien.Roy@Sun.COM return (err); 46810616SSebastien.Roy@Sun.COM } 46910616SSebastien.Roy@Sun.COM 47010616SSebastien.Roy@Sun.COM uint_t 47110616SSebastien.Roy@Sun.COM iptun_count(void) 47210616SSebastien.Roy@Sun.COM { 47310616SSebastien.Roy@Sun.COM return (iptun_tunnelcount); 47410616SSebastien.Roy@Sun.COM } 47510616SSebastien.Roy@Sun.COM 47610616SSebastien.Roy@Sun.COM /* 47710616SSebastien.Roy@Sun.COM * Enter an iptun_t exclusively. This is essentially just a mutex, but we 47810616SSebastien.Roy@Sun.COM * don't allow iptun_enter() to succeed on a tunnel if it's in the process of 47910616SSebastien.Roy@Sun.COM * being deleted. 48010616SSebastien.Roy@Sun.COM */ 48110616SSebastien.Roy@Sun.COM static int 48210616SSebastien.Roy@Sun.COM iptun_enter(iptun_t *iptun) 48310616SSebastien.Roy@Sun.COM { 48410616SSebastien.Roy@Sun.COM mutex_enter(&iptun->iptun_lock); 48510616SSebastien.Roy@Sun.COM while (iptun->iptun_flags & IPTUN_DELETE_PENDING) 48610616SSebastien.Roy@Sun.COM cv_wait(&iptun->iptun_enter_cv, &iptun->iptun_lock); 48710616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_CONDEMNED) { 48810616SSebastien.Roy@Sun.COM mutex_exit(&iptun->iptun_lock); 48910616SSebastien.Roy@Sun.COM return (ENOENT); 49010616SSebastien.Roy@Sun.COM } 49110616SSebastien.Roy@Sun.COM return (0); 49210616SSebastien.Roy@Sun.COM } 49310616SSebastien.Roy@Sun.COM 49410616SSebastien.Roy@Sun.COM /* 49510616SSebastien.Roy@Sun.COM * Exit the tunnel entered in iptun_enter(). 49610616SSebastien.Roy@Sun.COM */ 49710616SSebastien.Roy@Sun.COM static void 49810616SSebastien.Roy@Sun.COM iptun_exit(iptun_t *iptun) 49910616SSebastien.Roy@Sun.COM { 50010616SSebastien.Roy@Sun.COM mutex_exit(&iptun->iptun_lock); 50110616SSebastien.Roy@Sun.COM } 50210616SSebastien.Roy@Sun.COM 50310616SSebastien.Roy@Sun.COM /* 50410616SSebastien.Roy@Sun.COM * Enter the IP tunnel instance by datalink ID. 50510616SSebastien.Roy@Sun.COM */ 50610616SSebastien.Roy@Sun.COM static int 50710616SSebastien.Roy@Sun.COM iptun_enter_by_linkid(datalink_id_t linkid, iptun_t **iptun) 50810616SSebastien.Roy@Sun.COM { 50910616SSebastien.Roy@Sun.COM int err; 51010616SSebastien.Roy@Sun.COM 51110616SSebastien.Roy@Sun.COM mutex_enter(&iptun_hash_lock); 51210616SSebastien.Roy@Sun.COM if (mod_hash_find(iptun_hash, IPTUN_HASH_KEY(linkid), 51310616SSebastien.Roy@Sun.COM (mod_hash_val_t *)iptun) == 0) 51410616SSebastien.Roy@Sun.COM err = iptun_enter(*iptun); 51510616SSebastien.Roy@Sun.COM else 51610616SSebastien.Roy@Sun.COM err = ENOENT; 51710616SSebastien.Roy@Sun.COM if (err != 0) 51810616SSebastien.Roy@Sun.COM *iptun = NULL; 51910616SSebastien.Roy@Sun.COM mutex_exit(&iptun_hash_lock); 52010616SSebastien.Roy@Sun.COM return (err); 52110616SSebastien.Roy@Sun.COM } 52210616SSebastien.Roy@Sun.COM 52310616SSebastien.Roy@Sun.COM /* 52411042SErik.Nordmark@Sun.COM * Handle tasks that were deferred through the iptun_taskq because they require 52511042SErik.Nordmark@Sun.COM * calling up to the mac module, and we can't call up to the mac module while 52611042SErik.Nordmark@Sun.COM * holding locks. 52710616SSebastien.Roy@Sun.COM * 52811042SErik.Nordmark@Sun.COM * This is tricky to get right without introducing race conditions and 52910616SSebastien.Roy@Sun.COM * deadlocks with the mac module, as we cannot issue an upcall while in the 53010616SSebastien.Roy@Sun.COM * iptun_t. The reason is that upcalls may try and enter the mac perimeter, 53110616SSebastien.Roy@Sun.COM * while iptun callbacks (such as iptun_m_setprop()) called from the mac 53210616SSebastien.Roy@Sun.COM * module will already have the perimeter held, and will then try and enter 53310616SSebastien.Roy@Sun.COM * the iptun_t. You can see the lock ordering problem with this; this will 53410616SSebastien.Roy@Sun.COM * deadlock. 53510616SSebastien.Roy@Sun.COM * 53610616SSebastien.Roy@Sun.COM * The safe way to do this is to enter the iptun_t in question and copy the 53710616SSebastien.Roy@Sun.COM * information we need out of it so that we can exit it and know that the 53810616SSebastien.Roy@Sun.COM * information being passed up to the upcalls won't be subject to modification 53910616SSebastien.Roy@Sun.COM * by other threads. The problem now is that we need to exit it prior to 54010616SSebastien.Roy@Sun.COM * issuing the upcall, but once we do this, a thread could come along and 54110616SSebastien.Roy@Sun.COM * delete the iptun_t and thus the mac handle required to issue the upcall. 54210616SSebastien.Roy@Sun.COM * To prevent this, we set the IPTUN_UPCALL_PENDING flag prior to exiting the 54310616SSebastien.Roy@Sun.COM * iptun_t. This flag is the condition associated with iptun_upcall_cv, which 54410616SSebastien.Roy@Sun.COM * iptun_delete() will cv_wait() on. When the upcall completes, we clear 54510616SSebastien.Roy@Sun.COM * IPTUN_UPCALL_PENDING and cv_signal() any potentially waiting 54610616SSebastien.Roy@Sun.COM * iptun_delete(). We can thus still safely use iptun->iptun_mh after having 54710616SSebastien.Roy@Sun.COM * exited the iptun_t. 54810616SSebastien.Roy@Sun.COM */ 54910616SSebastien.Roy@Sun.COM static void 55010616SSebastien.Roy@Sun.COM iptun_task_cb(void *arg) 55110616SSebastien.Roy@Sun.COM { 55210616SSebastien.Roy@Sun.COM iptun_task_data_t *itd = arg; 55310616SSebastien.Roy@Sun.COM iptun_task_t task = itd->itd_task; 55410616SSebastien.Roy@Sun.COM datalink_id_t linkid = itd->itd_linkid; 55510616SSebastien.Roy@Sun.COM iptun_t *iptun; 55610616SSebastien.Roy@Sun.COM uint32_t mtu; 55710616SSebastien.Roy@Sun.COM iptun_addr_t addr; 55810616SSebastien.Roy@Sun.COM link_state_t linkstate; 55910616SSebastien.Roy@Sun.COM size_t header_size; 56010616SSebastien.Roy@Sun.COM iptun_header_t header; 56110616SSebastien.Roy@Sun.COM 56210616SSebastien.Roy@Sun.COM kmem_free(itd, sizeof (*itd)); 56310616SSebastien.Roy@Sun.COM 56410616SSebastien.Roy@Sun.COM /* 56510616SSebastien.Roy@Sun.COM * Note that if the lookup fails, it's because the tunnel was deleted 56610616SSebastien.Roy@Sun.COM * between the time the task was dispatched and now. That isn't an 56710616SSebastien.Roy@Sun.COM * error. 56810616SSebastien.Roy@Sun.COM */ 56910616SSebastien.Roy@Sun.COM if (iptun_enter_by_linkid(linkid, &iptun) != 0) 57010616SSebastien.Roy@Sun.COM return; 57110616SSebastien.Roy@Sun.COM 57210616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_UPCALL_PENDING; 57310616SSebastien.Roy@Sun.COM 57410616SSebastien.Roy@Sun.COM switch (task) { 57510616SSebastien.Roy@Sun.COM case IPTUN_TASK_MTU_UPDATE: 57610616SSebastien.Roy@Sun.COM mtu = iptun->iptun_mtu; 57710616SSebastien.Roy@Sun.COM break; 57810616SSebastien.Roy@Sun.COM case IPTUN_TASK_LADDR_UPDATE: 57910616SSebastien.Roy@Sun.COM addr = iptun->iptun_laddr; 58010616SSebastien.Roy@Sun.COM break; 58110616SSebastien.Roy@Sun.COM case IPTUN_TASK_RADDR_UPDATE: 58210616SSebastien.Roy@Sun.COM addr = iptun->iptun_raddr; 58310616SSebastien.Roy@Sun.COM break; 58410616SSebastien.Roy@Sun.COM case IPTUN_TASK_LINK_UPDATE: 58510616SSebastien.Roy@Sun.COM linkstate = IS_IPTUN_RUNNING(iptun) ? 58610616SSebastien.Roy@Sun.COM LINK_STATE_UP : LINK_STATE_DOWN; 58710616SSebastien.Roy@Sun.COM break; 58810616SSebastien.Roy@Sun.COM case IPTUN_TASK_PDATA_UPDATE: 58910616SSebastien.Roy@Sun.COM header_size = iptun->iptun_header_size; 59010616SSebastien.Roy@Sun.COM header = iptun->iptun_header; 59110616SSebastien.Roy@Sun.COM break; 59210616SSebastien.Roy@Sun.COM default: 59310616SSebastien.Roy@Sun.COM ASSERT(0); 59410616SSebastien.Roy@Sun.COM } 59510616SSebastien.Roy@Sun.COM 59610616SSebastien.Roy@Sun.COM iptun_exit(iptun); 59710616SSebastien.Roy@Sun.COM 59810616SSebastien.Roy@Sun.COM switch (task) { 59910616SSebastien.Roy@Sun.COM case IPTUN_TASK_MTU_UPDATE: 60010616SSebastien.Roy@Sun.COM (void) mac_maxsdu_update(iptun->iptun_mh, mtu); 60110616SSebastien.Roy@Sun.COM break; 60210616SSebastien.Roy@Sun.COM case IPTUN_TASK_LADDR_UPDATE: 60310616SSebastien.Roy@Sun.COM mac_unicst_update(iptun->iptun_mh, (uint8_t *)&addr.ia_addr); 60410616SSebastien.Roy@Sun.COM break; 60510616SSebastien.Roy@Sun.COM case IPTUN_TASK_RADDR_UPDATE: 60610616SSebastien.Roy@Sun.COM mac_dst_update(iptun->iptun_mh, (uint8_t *)&addr.ia_addr); 60710616SSebastien.Roy@Sun.COM break; 60810616SSebastien.Roy@Sun.COM case IPTUN_TASK_LINK_UPDATE: 60910616SSebastien.Roy@Sun.COM mac_link_update(iptun->iptun_mh, linkstate); 61010616SSebastien.Roy@Sun.COM break; 61110616SSebastien.Roy@Sun.COM case IPTUN_TASK_PDATA_UPDATE: 61210616SSebastien.Roy@Sun.COM if (mac_pdata_update(iptun->iptun_mh, 61310616SSebastien.Roy@Sun.COM header_size == 0 ? NULL : &header, header_size) != 0) 61410616SSebastien.Roy@Sun.COM atomic_inc_64(&iptun->iptun_taskq_fail); 61510616SSebastien.Roy@Sun.COM break; 61610616SSebastien.Roy@Sun.COM } 61710616SSebastien.Roy@Sun.COM 61810616SSebastien.Roy@Sun.COM mutex_enter(&iptun->iptun_lock); 61910616SSebastien.Roy@Sun.COM iptun->iptun_flags &= ~IPTUN_UPCALL_PENDING; 62010616SSebastien.Roy@Sun.COM cv_signal(&iptun->iptun_upcall_cv); 62110616SSebastien.Roy@Sun.COM mutex_exit(&iptun->iptun_lock); 62210616SSebastien.Roy@Sun.COM } 62310616SSebastien.Roy@Sun.COM 62410616SSebastien.Roy@Sun.COM static void 62510616SSebastien.Roy@Sun.COM iptun_task_dispatch(iptun_t *iptun, iptun_task_t iptun_task) 62610616SSebastien.Roy@Sun.COM { 62710616SSebastien.Roy@Sun.COM iptun_task_data_t *itd; 62810616SSebastien.Roy@Sun.COM 62910616SSebastien.Roy@Sun.COM itd = kmem_alloc(sizeof (*itd), KM_NOSLEEP); 63010616SSebastien.Roy@Sun.COM if (itd == NULL) { 63110616SSebastien.Roy@Sun.COM atomic_inc_64(&iptun->iptun_taskq_fail); 63210616SSebastien.Roy@Sun.COM return; 63310616SSebastien.Roy@Sun.COM } 63410616SSebastien.Roy@Sun.COM itd->itd_task = iptun_task; 63510616SSebastien.Roy@Sun.COM itd->itd_linkid = iptun->iptun_linkid; 63610616SSebastien.Roy@Sun.COM if (ddi_taskq_dispatch(iptun_taskq, iptun_task_cb, itd, DDI_NOSLEEP)) { 63710616SSebastien.Roy@Sun.COM atomic_inc_64(&iptun->iptun_taskq_fail); 63810616SSebastien.Roy@Sun.COM kmem_free(itd, sizeof (*itd)); 63910616SSebastien.Roy@Sun.COM } 64010616SSebastien.Roy@Sun.COM } 64110616SSebastien.Roy@Sun.COM 64210616SSebastien.Roy@Sun.COM /* 64310616SSebastien.Roy@Sun.COM * Convert an iptun_addr_t to sockaddr_storage. 64410616SSebastien.Roy@Sun.COM */ 64510616SSebastien.Roy@Sun.COM static void 64610616SSebastien.Roy@Sun.COM iptun_getaddr(iptun_addr_t *iptun_addr, struct sockaddr_storage *ss) 64710616SSebastien.Roy@Sun.COM { 64810616SSebastien.Roy@Sun.COM struct sockaddr_in *sin; 64910616SSebastien.Roy@Sun.COM struct sockaddr_in6 *sin6; 65010616SSebastien.Roy@Sun.COM 65110616SSebastien.Roy@Sun.COM bzero(ss, sizeof (*ss)); 65210616SSebastien.Roy@Sun.COM switch (iptun_addr->ia_family) { 65310616SSebastien.Roy@Sun.COM case AF_INET: 65410616SSebastien.Roy@Sun.COM sin = (struct sockaddr_in *)ss; 65510616SSebastien.Roy@Sun.COM sin->sin_addr.s_addr = iptun_addr->ia_addr.iau_addr4; 65610616SSebastien.Roy@Sun.COM break; 65710616SSebastien.Roy@Sun.COM case AF_INET6: 65810616SSebastien.Roy@Sun.COM sin6 = (struct sockaddr_in6 *)ss; 65910616SSebastien.Roy@Sun.COM sin6->sin6_addr = iptun_addr->ia_addr.iau_addr6; 66010616SSebastien.Roy@Sun.COM break; 66110616SSebastien.Roy@Sun.COM default: 66210616SSebastien.Roy@Sun.COM ASSERT(0); 66310616SSebastien.Roy@Sun.COM } 66410616SSebastien.Roy@Sun.COM ss->ss_family = iptun_addr->ia_family; 66510616SSebastien.Roy@Sun.COM } 66610616SSebastien.Roy@Sun.COM 66710616SSebastien.Roy@Sun.COM /* 66810616SSebastien.Roy@Sun.COM * General purpose function to set an IP tunnel source or destination address. 66910616SSebastien.Roy@Sun.COM */ 67010616SSebastien.Roy@Sun.COM static int 67110616SSebastien.Roy@Sun.COM iptun_setaddr(iptun_type_t iptun_type, iptun_addr_t *iptun_addr, 67210616SSebastien.Roy@Sun.COM const struct sockaddr_storage *ss) 67310616SSebastien.Roy@Sun.COM { 67410616SSebastien.Roy@Sun.COM if (!IPTUN_ADDR_MATCH(iptun_type, ss->ss_family)) 67510616SSebastien.Roy@Sun.COM return (EINVAL); 67610616SSebastien.Roy@Sun.COM 67710616SSebastien.Roy@Sun.COM switch (ss->ss_family) { 67810616SSebastien.Roy@Sun.COM case AF_INET: { 67910616SSebastien.Roy@Sun.COM struct sockaddr_in *sin = (struct sockaddr_in *)ss; 68010616SSebastien.Roy@Sun.COM 68110616SSebastien.Roy@Sun.COM if ((sin->sin_addr.s_addr == INADDR_ANY) || 68210616SSebastien.Roy@Sun.COM (sin->sin_addr.s_addr == INADDR_BROADCAST) || 68310616SSebastien.Roy@Sun.COM CLASSD(sin->sin_addr.s_addr)) { 68410616SSebastien.Roy@Sun.COM return (EADDRNOTAVAIL); 68510616SSebastien.Roy@Sun.COM } 68610616SSebastien.Roy@Sun.COM iptun_addr->ia_addr.iau_addr4 = sin->sin_addr.s_addr; 68710616SSebastien.Roy@Sun.COM break; 68810616SSebastien.Roy@Sun.COM } 68910616SSebastien.Roy@Sun.COM case AF_INET6: { 69010616SSebastien.Roy@Sun.COM struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ss; 69110616SSebastien.Roy@Sun.COM 69210616SSebastien.Roy@Sun.COM if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) || 69310616SSebastien.Roy@Sun.COM IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr) || 69410616SSebastien.Roy@Sun.COM IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { 69510616SSebastien.Roy@Sun.COM return (EADDRNOTAVAIL); 69610616SSebastien.Roy@Sun.COM } 69710616SSebastien.Roy@Sun.COM iptun_addr->ia_addr.iau_addr6 = sin6->sin6_addr; 69810616SSebastien.Roy@Sun.COM break; 69910616SSebastien.Roy@Sun.COM } 70010616SSebastien.Roy@Sun.COM default: 70110616SSebastien.Roy@Sun.COM return (EAFNOSUPPORT); 70210616SSebastien.Roy@Sun.COM } 70310616SSebastien.Roy@Sun.COM iptun_addr->ia_family = ss->ss_family; 70410616SSebastien.Roy@Sun.COM return (0); 70510616SSebastien.Roy@Sun.COM } 70610616SSebastien.Roy@Sun.COM 70710616SSebastien.Roy@Sun.COM static int 70810616SSebastien.Roy@Sun.COM iptun_setladdr(iptun_t *iptun, const struct sockaddr_storage *laddr) 70910616SSebastien.Roy@Sun.COM { 71010616SSebastien.Roy@Sun.COM return (iptun_setaddr(iptun->iptun_typeinfo->iti_type, 71110616SSebastien.Roy@Sun.COM &iptun->iptun_laddr, laddr)); 71210616SSebastien.Roy@Sun.COM } 71310616SSebastien.Roy@Sun.COM 71410616SSebastien.Roy@Sun.COM static int 71510616SSebastien.Roy@Sun.COM iptun_setraddr(iptun_t *iptun, const struct sockaddr_storage *raddr) 71610616SSebastien.Roy@Sun.COM { 71710616SSebastien.Roy@Sun.COM if (!(iptun->iptun_typeinfo->iti_hasraddr)) 71810616SSebastien.Roy@Sun.COM return (EINVAL); 71910616SSebastien.Roy@Sun.COM return (iptun_setaddr(iptun->iptun_typeinfo->iti_type, 72010616SSebastien.Roy@Sun.COM &iptun->iptun_raddr, raddr)); 72110616SSebastien.Roy@Sun.COM } 72210616SSebastien.Roy@Sun.COM 72310616SSebastien.Roy@Sun.COM static boolean_t 72410616SSebastien.Roy@Sun.COM iptun_canbind(iptun_t *iptun) 72510616SSebastien.Roy@Sun.COM { 72610616SSebastien.Roy@Sun.COM /* 72710616SSebastien.Roy@Sun.COM * A tunnel may bind when its source address has been set, and if its 72810616SSebastien.Roy@Sun.COM * tunnel type requires one, also its destination address. 72910616SSebastien.Roy@Sun.COM */ 73010616SSebastien.Roy@Sun.COM return ((iptun->iptun_flags & IPTUN_LADDR) && 73110616SSebastien.Roy@Sun.COM ((iptun->iptun_flags & IPTUN_RADDR) || 73210616SSebastien.Roy@Sun.COM !(iptun->iptun_typeinfo->iti_hasraddr))); 73310616SSebastien.Roy@Sun.COM } 73410616SSebastien.Roy@Sun.COM 73511042SErik.Nordmark@Sun.COM /* 73611042SErik.Nordmark@Sun.COM * Verify that the local address is valid, and insert in the fanout 73711042SErik.Nordmark@Sun.COM */ 73810616SSebastien.Roy@Sun.COM static int 73910616SSebastien.Roy@Sun.COM iptun_bind(iptun_t *iptun) 74010616SSebastien.Roy@Sun.COM { 74111042SErik.Nordmark@Sun.COM conn_t *connp = iptun->iptun_connp; 74211042SErik.Nordmark@Sun.COM int error = 0; 74311042SErik.Nordmark@Sun.COM ip_xmit_attr_t *ixa; 74411042SErik.Nordmark@Sun.COM iulp_t uinfo; 74511042SErik.Nordmark@Sun.COM ip_stack_t *ipst = connp->conn_netstack->netstack_ip; 74611042SErik.Nordmark@Sun.COM 74711042SErik.Nordmark@Sun.COM /* Get an exclusive ixa for this thread, and replace conn_ixa */ 74811042SErik.Nordmark@Sun.COM ixa = conn_get_ixa(connp, B_TRUE); 74911042SErik.Nordmark@Sun.COM if (ixa == NULL) 75011042SErik.Nordmark@Sun.COM return (ENOMEM); 75111042SErik.Nordmark@Sun.COM ASSERT(ixa->ixa_refcnt >= 2); 75211042SErik.Nordmark@Sun.COM ASSERT(ixa == connp->conn_ixa); 75311042SErik.Nordmark@Sun.COM 75411042SErik.Nordmark@Sun.COM /* We create PMTU state including for 6to4 */ 75511042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_PMTU_DISCOVERY; 75610616SSebastien.Roy@Sun.COM 75710616SSebastien.Roy@Sun.COM ASSERT(iptun_canbind(iptun)); 75810616SSebastien.Roy@Sun.COM 75911042SErik.Nordmark@Sun.COM mutex_enter(&connp->conn_lock); 76011042SErik.Nordmark@Sun.COM /* 76111042SErik.Nordmark@Sun.COM * Note that conn_proto can't be set since the upper protocol 76211042SErik.Nordmark@Sun.COM * can be both 41 and 4 when IPv6 and IPv4 are over the same tunnel. 76311042SErik.Nordmark@Sun.COM * ipcl_iptun_classify doesn't use conn_proto. 76411042SErik.Nordmark@Sun.COM */ 76511042SErik.Nordmark@Sun.COM connp->conn_ipversion = iptun->iptun_typeinfo->iti_ipvers; 76611042SErik.Nordmark@Sun.COM 76710616SSebastien.Roy@Sun.COM switch (iptun->iptun_typeinfo->iti_type) { 76810616SSebastien.Roy@Sun.COM case IPTUN_TYPE_IPV4: 76911042SErik.Nordmark@Sun.COM IN6_IPADDR_TO_V4MAPPED(iptun->iptun_laddr4, 77011042SErik.Nordmark@Sun.COM &connp->conn_laddr_v6); 77111042SErik.Nordmark@Sun.COM IN6_IPADDR_TO_V4MAPPED(iptun->iptun_raddr4, 77211042SErik.Nordmark@Sun.COM &connp->conn_faddr_v6); 77311042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_IS_IPV4; 77411042SErik.Nordmark@Sun.COM if (ip_laddr_verify_v4(iptun->iptun_laddr4, IPCL_ZONEID(connp), 77511042SErik.Nordmark@Sun.COM ipst, B_FALSE) != IPVL_UNICAST_UP) { 77611042SErik.Nordmark@Sun.COM mutex_exit(&connp->conn_lock); 77711042SErik.Nordmark@Sun.COM error = EADDRNOTAVAIL; 77811042SErik.Nordmark@Sun.COM goto done; 77911042SErik.Nordmark@Sun.COM } 78010616SSebastien.Roy@Sun.COM break; 78110616SSebastien.Roy@Sun.COM case IPTUN_TYPE_IPV6: 78211042SErik.Nordmark@Sun.COM connp->conn_laddr_v6 = iptun->iptun_laddr6; 78311042SErik.Nordmark@Sun.COM connp->conn_faddr_v6 = iptun->iptun_raddr6; 78411042SErik.Nordmark@Sun.COM ixa->ixa_flags &= ~IXAF_IS_IPV4; 78511042SErik.Nordmark@Sun.COM /* We use a zero scopeid for now */ 78611042SErik.Nordmark@Sun.COM if (ip_laddr_verify_v6(&iptun->iptun_laddr6, IPCL_ZONEID(connp), 78711042SErik.Nordmark@Sun.COM ipst, B_FALSE, 0) != IPVL_UNICAST_UP) { 78811042SErik.Nordmark@Sun.COM mutex_exit(&connp->conn_lock); 78911042SErik.Nordmark@Sun.COM error = EADDRNOTAVAIL; 79011042SErik.Nordmark@Sun.COM goto done; 79111042SErik.Nordmark@Sun.COM } 79210616SSebastien.Roy@Sun.COM break; 79310616SSebastien.Roy@Sun.COM case IPTUN_TYPE_6TO4: 79411042SErik.Nordmark@Sun.COM IN6_IPADDR_TO_V4MAPPED(iptun->iptun_laddr4, 79511042SErik.Nordmark@Sun.COM &connp->conn_laddr_v6); 79611042SErik.Nordmark@Sun.COM IN6_IPADDR_TO_V4MAPPED(INADDR_ANY, &connp->conn_faddr_v6); 79711042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_IS_IPV4; 79811042SErik.Nordmark@Sun.COM mutex_exit(&connp->conn_lock); 79911042SErik.Nordmark@Sun.COM 80011042SErik.Nordmark@Sun.COM switch (ip_laddr_verify_v4(iptun->iptun_laddr4, 80111042SErik.Nordmark@Sun.COM IPCL_ZONEID(connp), ipst, B_FALSE)) { 80211042SErik.Nordmark@Sun.COM case IPVL_UNICAST_UP: 80311042SErik.Nordmark@Sun.COM case IPVL_UNICAST_DOWN: 80411042SErik.Nordmark@Sun.COM break; 80511042SErik.Nordmark@Sun.COM default: 80611042SErik.Nordmark@Sun.COM error = EADDRNOTAVAIL; 80711042SErik.Nordmark@Sun.COM goto done; 80811042SErik.Nordmark@Sun.COM } 80911042SErik.Nordmark@Sun.COM goto insert; 81010616SSebastien.Roy@Sun.COM } 81110616SSebastien.Roy@Sun.COM 81211042SErik.Nordmark@Sun.COM /* In case previous destination was multirt */ 81311042SErik.Nordmark@Sun.COM ip_attr_newdst(ixa); 81411042SErik.Nordmark@Sun.COM 81511042SErik.Nordmark@Sun.COM /* 81611042SErik.Nordmark@Sun.COM * When we set a tunnel's destination address, we do not 81711042SErik.Nordmark@Sun.COM * care if the destination is reachable. Transient routing 81811042SErik.Nordmark@Sun.COM * issues should not inhibit the creation of a tunnel 81911042SErik.Nordmark@Sun.COM * interface, for example. Thus we pass B_FALSE here. 82011042SErik.Nordmark@Sun.COM */ 82111042SErik.Nordmark@Sun.COM connp->conn_saddr_v6 = connp->conn_laddr_v6; 82211042SErik.Nordmark@Sun.COM mutex_exit(&connp->conn_lock); 82311042SErik.Nordmark@Sun.COM 82411042SErik.Nordmark@Sun.COM /* As long as the MTU is large we avoid fragmentation */ 82511042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_DONTFRAG | IXAF_PMTU_IPV4_DF; 82611042SErik.Nordmark@Sun.COM 82711042SErik.Nordmark@Sun.COM /* We handle IPsec in iptun_output_common */ 82811042SErik.Nordmark@Sun.COM error = ip_attr_connect(connp, ixa, &connp->conn_saddr_v6, 82911042SErik.Nordmark@Sun.COM &connp->conn_faddr_v6, &connp->conn_faddr_v6, 0, 83011042SErik.Nordmark@Sun.COM &connp->conn_saddr_v6, &uinfo, 0); 83111042SErik.Nordmark@Sun.COM 83211042SErik.Nordmark@Sun.COM if (error != 0) 83311042SErik.Nordmark@Sun.COM goto done; 83411042SErik.Nordmark@Sun.COM 83511042SErik.Nordmark@Sun.COM /* saddr shouldn't change since it was already set */ 83611042SErik.Nordmark@Sun.COM ASSERT(IN6_ARE_ADDR_EQUAL(&connp->conn_laddr_v6, 83711042SErik.Nordmark@Sun.COM &connp->conn_saddr_v6)); 83811042SErik.Nordmark@Sun.COM 83911042SErik.Nordmark@Sun.COM /* We set IXAF_VERIFY_PMTU to catch PMTU increases */ 84011042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_VERIFY_PMTU; 84111042SErik.Nordmark@Sun.COM ASSERT(uinfo.iulp_mtu != 0); 84211042SErik.Nordmark@Sun.COM 84311042SErik.Nordmark@Sun.COM /* 84411042SErik.Nordmark@Sun.COM * Allow setting new policies. 84511042SErik.Nordmark@Sun.COM * The addresses/ports are already set, thus the IPsec policy calls 84611042SErik.Nordmark@Sun.COM * can handle their passed-in conn's. 84711042SErik.Nordmark@Sun.COM */ 84811042SErik.Nordmark@Sun.COM connp->conn_policy_cached = B_FALSE; 84911042SErik.Nordmark@Sun.COM 85011042SErik.Nordmark@Sun.COM insert: 85111042SErik.Nordmark@Sun.COM error = ipcl_conn_insert(connp); 85211042SErik.Nordmark@Sun.COM if (error != 0) 85311042SErik.Nordmark@Sun.COM goto done; 85411042SErik.Nordmark@Sun.COM 85511042SErik.Nordmark@Sun.COM /* Record this as the "last" send even though we haven't sent any */ 85611042SErik.Nordmark@Sun.COM connp->conn_v6lastdst = connp->conn_faddr_v6; 85711042SErik.Nordmark@Sun.COM 85811042SErik.Nordmark@Sun.COM iptun->iptun_flags |= IPTUN_BOUND; 85911042SErik.Nordmark@Sun.COM /* 86011042SErik.Nordmark@Sun.COM * Now that we're bound with ip below us, this is a good 86111042SErik.Nordmark@Sun.COM * time to initialize the destination path MTU and to 86211042SErik.Nordmark@Sun.COM * re-calculate the tunnel's link MTU. 86311042SErik.Nordmark@Sun.COM */ 86411042SErik.Nordmark@Sun.COM (void) iptun_update_mtu(iptun, ixa, 0); 86511042SErik.Nordmark@Sun.COM 86611042SErik.Nordmark@Sun.COM if (IS_IPTUN_RUNNING(iptun)) 86711042SErik.Nordmark@Sun.COM iptun_task_dispatch(iptun, IPTUN_TASK_LINK_UPDATE); 86811042SErik.Nordmark@Sun.COM 86911042SErik.Nordmark@Sun.COM done: 87011042SErik.Nordmark@Sun.COM ixa_refrele(ixa); 87111042SErik.Nordmark@Sun.COM return (error); 87210616SSebastien.Roy@Sun.COM } 87310616SSebastien.Roy@Sun.COM 87410616SSebastien.Roy@Sun.COM static void 87510616SSebastien.Roy@Sun.COM iptun_unbind(iptun_t *iptun) 87610616SSebastien.Roy@Sun.COM { 87710616SSebastien.Roy@Sun.COM ASSERT(iptun->iptun_flags & IPTUN_BOUND); 87810616SSebastien.Roy@Sun.COM ASSERT(mutex_owned(&iptun->iptun_lock) || 87910616SSebastien.Roy@Sun.COM (iptun->iptun_flags & IPTUN_CONDEMNED)); 88010616SSebastien.Roy@Sun.COM ip_unbind(iptun->iptun_connp); 88110616SSebastien.Roy@Sun.COM iptun->iptun_flags &= ~IPTUN_BOUND; 88210616SSebastien.Roy@Sun.COM if (!(iptun->iptun_flags & IPTUN_CONDEMNED)) 88310616SSebastien.Roy@Sun.COM iptun_task_dispatch(iptun, IPTUN_TASK_LINK_UPDATE); 88410616SSebastien.Roy@Sun.COM } 88510616SSebastien.Roy@Sun.COM 88610616SSebastien.Roy@Sun.COM /* 88710616SSebastien.Roy@Sun.COM * Re-generate the template data-link header for a given IP tunnel given the 88810616SSebastien.Roy@Sun.COM * tunnel's current parameters. 88910616SSebastien.Roy@Sun.COM */ 89010616SSebastien.Roy@Sun.COM static void 89110616SSebastien.Roy@Sun.COM iptun_headergen(iptun_t *iptun, boolean_t update_mac) 89210616SSebastien.Roy@Sun.COM { 89310616SSebastien.Roy@Sun.COM switch (iptun->iptun_typeinfo->iti_ipvers) { 89410616SSebastien.Roy@Sun.COM case IPV4_VERSION: 89510616SSebastien.Roy@Sun.COM /* 89610616SSebastien.Roy@Sun.COM * We only need to use a custom IP header if the administrator 89710616SSebastien.Roy@Sun.COM * has supplied a non-default hoplimit. 89810616SSebastien.Roy@Sun.COM */ 89910616SSebastien.Roy@Sun.COM if (iptun->iptun_hoplimit == IPTUN_DEFAULT_HOPLIMIT) { 90010616SSebastien.Roy@Sun.COM iptun->iptun_header_size = 0; 90110616SSebastien.Roy@Sun.COM break; 90210616SSebastien.Roy@Sun.COM } 90310616SSebastien.Roy@Sun.COM iptun->iptun_header_size = sizeof (ipha_t); 90410616SSebastien.Roy@Sun.COM iptun->iptun_header4.ipha_version_and_hdr_length = 90510616SSebastien.Roy@Sun.COM IP_SIMPLE_HDR_VERSION; 90610616SSebastien.Roy@Sun.COM iptun->iptun_header4.ipha_fragment_offset_and_flags = 90710616SSebastien.Roy@Sun.COM htons(IPH_DF); 90810616SSebastien.Roy@Sun.COM iptun->iptun_header4.ipha_ttl = iptun->iptun_hoplimit; 90910616SSebastien.Roy@Sun.COM break; 91010616SSebastien.Roy@Sun.COM case IPV6_VERSION: { 91110616SSebastien.Roy@Sun.COM ip6_t *ip6hp = &iptun->iptun_header6.it6h_ip6h; 91210616SSebastien.Roy@Sun.COM 91310616SSebastien.Roy@Sun.COM /* 91410616SSebastien.Roy@Sun.COM * We only need to use a custom IPv6 header if either the 91510616SSebastien.Roy@Sun.COM * administrator has supplied a non-default hoplimit, or we 91610616SSebastien.Roy@Sun.COM * need to include an encapsulation limit option in the outer 91710616SSebastien.Roy@Sun.COM * header. 91810616SSebastien.Roy@Sun.COM */ 91910616SSebastien.Roy@Sun.COM if (iptun->iptun_hoplimit == IPTUN_DEFAULT_HOPLIMIT && 92010616SSebastien.Roy@Sun.COM iptun->iptun_encaplimit == 0) { 92110616SSebastien.Roy@Sun.COM iptun->iptun_header_size = 0; 92210616SSebastien.Roy@Sun.COM break; 92310616SSebastien.Roy@Sun.COM } 92410616SSebastien.Roy@Sun.COM 92510616SSebastien.Roy@Sun.COM (void) memset(ip6hp, 0, sizeof (*ip6hp)); 92610616SSebastien.Roy@Sun.COM if (iptun->iptun_encaplimit == 0) { 92710616SSebastien.Roy@Sun.COM iptun->iptun_header_size = sizeof (ip6_t); 92810616SSebastien.Roy@Sun.COM ip6hp->ip6_nxt = IPPROTO_NONE; 92910616SSebastien.Roy@Sun.COM } else { 93010616SSebastien.Roy@Sun.COM iptun_encaplim_t *iel; 93110616SSebastien.Roy@Sun.COM 93210616SSebastien.Roy@Sun.COM iptun->iptun_header_size = sizeof (iptun_ipv6hdrs_t); 93310616SSebastien.Roy@Sun.COM /* 93410616SSebastien.Roy@Sun.COM * The mac_ipv6 plugin requires ip6_plen to be in host 93510616SSebastien.Roy@Sun.COM * byte order and reflect the extension headers 93610616SSebastien.Roy@Sun.COM * present in the template. The actual network byte 93710616SSebastien.Roy@Sun.COM * order ip6_plen will be set on a per-packet basis on 93810616SSebastien.Roy@Sun.COM * transmit. 93910616SSebastien.Roy@Sun.COM */ 94010616SSebastien.Roy@Sun.COM ip6hp->ip6_plen = sizeof (*iel); 94110616SSebastien.Roy@Sun.COM ip6hp->ip6_nxt = IPPROTO_DSTOPTS; 94210616SSebastien.Roy@Sun.COM iel = &iptun->iptun_header6.it6h_encaplim; 94310616SSebastien.Roy@Sun.COM *iel = iptun_encaplim_init; 94410616SSebastien.Roy@Sun.COM iel->iel_telopt.ip6ot_encap_limit = 94510616SSebastien.Roy@Sun.COM iptun->iptun_encaplimit; 94610616SSebastien.Roy@Sun.COM } 94710616SSebastien.Roy@Sun.COM 94810616SSebastien.Roy@Sun.COM ip6hp->ip6_hlim = iptun->iptun_hoplimit; 94910616SSebastien.Roy@Sun.COM break; 95010616SSebastien.Roy@Sun.COM } 95110616SSebastien.Roy@Sun.COM } 95210616SSebastien.Roy@Sun.COM 95310616SSebastien.Roy@Sun.COM if (update_mac) 95410616SSebastien.Roy@Sun.COM iptun_task_dispatch(iptun, IPTUN_TASK_PDATA_UPDATE); 95510616SSebastien.Roy@Sun.COM } 95610616SSebastien.Roy@Sun.COM 95710616SSebastien.Roy@Sun.COM /* 95810616SSebastien.Roy@Sun.COM * Insert inbound and outbound IPv4 and IPv6 policy into the given policy 95910616SSebastien.Roy@Sun.COM * head. 96010616SSebastien.Roy@Sun.COM */ 96110616SSebastien.Roy@Sun.COM static boolean_t 96210616SSebastien.Roy@Sun.COM iptun_insert_simple_policies(ipsec_policy_head_t *ph, ipsec_act_t *actp, 96310616SSebastien.Roy@Sun.COM uint_t n, netstack_t *ns) 96410616SSebastien.Roy@Sun.COM { 96510616SSebastien.Roy@Sun.COM int f = IPSEC_AF_V4; 96610616SSebastien.Roy@Sun.COM 96710616SSebastien.Roy@Sun.COM if (!ipsec_polhead_insert(ph, actp, n, f, IPSEC_TYPE_INBOUND, ns) || 96810616SSebastien.Roy@Sun.COM !ipsec_polhead_insert(ph, actp, n, f, IPSEC_TYPE_OUTBOUND, ns)) 96910616SSebastien.Roy@Sun.COM return (B_FALSE); 97010616SSebastien.Roy@Sun.COM 97110616SSebastien.Roy@Sun.COM f = IPSEC_AF_V6; 97210616SSebastien.Roy@Sun.COM return (ipsec_polhead_insert(ph, actp, n, f, IPSEC_TYPE_INBOUND, ns) && 97310616SSebastien.Roy@Sun.COM ipsec_polhead_insert(ph, actp, n, f, IPSEC_TYPE_OUTBOUND, ns)); 97410616SSebastien.Roy@Sun.COM } 97510616SSebastien.Roy@Sun.COM 97610616SSebastien.Roy@Sun.COM /* 97710616SSebastien.Roy@Sun.COM * Used to set IPsec policy when policy is set through the IPTUN_CREATE or 97810616SSebastien.Roy@Sun.COM * IPTUN_MODIFY ioctls. 97910616SSebastien.Roy@Sun.COM */ 98010616SSebastien.Roy@Sun.COM static int 98110616SSebastien.Roy@Sun.COM iptun_set_sec_simple(iptun_t *iptun, const ipsec_req_t *ipsr) 98210616SSebastien.Roy@Sun.COM { 98310616SSebastien.Roy@Sun.COM int rc = 0; 98410616SSebastien.Roy@Sun.COM uint_t nact; 98510616SSebastien.Roy@Sun.COM ipsec_act_t *actp = NULL; 98610616SSebastien.Roy@Sun.COM boolean_t clear_all, old_policy = B_FALSE; 98710616SSebastien.Roy@Sun.COM ipsec_tun_pol_t *itp; 98810616SSebastien.Roy@Sun.COM char name[MAXLINKNAMELEN]; 98910616SSebastien.Roy@Sun.COM uint64_t gen; 99010616SSebastien.Roy@Sun.COM netstack_t *ns = iptun->iptun_ns; 99110616SSebastien.Roy@Sun.COM 99210616SSebastien.Roy@Sun.COM /* Can't specify self-encap on a tunnel. */ 99310616SSebastien.Roy@Sun.COM if (ipsr->ipsr_self_encap_req != 0) 99410616SSebastien.Roy@Sun.COM return (EINVAL); 99510616SSebastien.Roy@Sun.COM 99610616SSebastien.Roy@Sun.COM /* 99710616SSebastien.Roy@Sun.COM * If it's a "clear-all" entry, unset the security flags and resume 99810616SSebastien.Roy@Sun.COM * normal cleartext (or inherit-from-global) policy. 99910616SSebastien.Roy@Sun.COM */ 100010616SSebastien.Roy@Sun.COM clear_all = ((ipsr->ipsr_ah_req & IPTUN_IPSEC_REQ_MASK) == 0 && 100110616SSebastien.Roy@Sun.COM (ipsr->ipsr_esp_req & IPTUN_IPSEC_REQ_MASK) == 0); 100210616SSebastien.Roy@Sun.COM 100310616SSebastien.Roy@Sun.COM ASSERT(mutex_owned(&iptun->iptun_lock)); 100410616SSebastien.Roy@Sun.COM itp = iptun->iptun_itp; 100510616SSebastien.Roy@Sun.COM if (itp == NULL) { 100610616SSebastien.Roy@Sun.COM if (clear_all) 100710616SSebastien.Roy@Sun.COM goto bail; 100810616SSebastien.Roy@Sun.COM if ((rc = dls_mgmt_get_linkinfo(iptun->iptun_linkid, name, NULL, 100910616SSebastien.Roy@Sun.COM NULL, NULL)) != 0) 101010616SSebastien.Roy@Sun.COM goto bail; 101110616SSebastien.Roy@Sun.COM ASSERT(name[0] != '\0'); 101210616SSebastien.Roy@Sun.COM if ((itp = create_tunnel_policy(name, &rc, &gen, ns)) == NULL) 101310616SSebastien.Roy@Sun.COM goto bail; 101410616SSebastien.Roy@Sun.COM iptun->iptun_itp = itp; 101510616SSebastien.Roy@Sun.COM } 101610616SSebastien.Roy@Sun.COM 101710616SSebastien.Roy@Sun.COM /* Allocate the actvec now, before holding itp or polhead locks. */ 101810616SSebastien.Roy@Sun.COM ipsec_actvec_from_req(ipsr, &actp, &nact, ns); 101910616SSebastien.Roy@Sun.COM if (actp == NULL) { 102010616SSebastien.Roy@Sun.COM rc = ENOMEM; 102110616SSebastien.Roy@Sun.COM goto bail; 102210616SSebastien.Roy@Sun.COM } 102310616SSebastien.Roy@Sun.COM 102410616SSebastien.Roy@Sun.COM /* 102510616SSebastien.Roy@Sun.COM * Just write on the active polhead. Save the primary/secondary stuff 102610616SSebastien.Roy@Sun.COM * for spdsock operations. 102710616SSebastien.Roy@Sun.COM * 102810616SSebastien.Roy@Sun.COM * Mutex because we need to write to the polhead AND flags atomically. 102910616SSebastien.Roy@Sun.COM * Other threads will acquire the polhead lock as a reader if the 103010616SSebastien.Roy@Sun.COM * (unprotected) flag is set. 103110616SSebastien.Roy@Sun.COM */ 103210616SSebastien.Roy@Sun.COM mutex_enter(&itp->itp_lock); 103310616SSebastien.Roy@Sun.COM if (itp->itp_flags & ITPF_P_TUNNEL) { 103410616SSebastien.Roy@Sun.COM /* Oops, we lost a race. Let's get out of here. */ 103510616SSebastien.Roy@Sun.COM rc = EBUSY; 103610616SSebastien.Roy@Sun.COM goto mutex_bail; 103710616SSebastien.Roy@Sun.COM } 103810616SSebastien.Roy@Sun.COM old_policy = ((itp->itp_flags & ITPF_P_ACTIVE) != 0); 103910616SSebastien.Roy@Sun.COM 104010616SSebastien.Roy@Sun.COM if (old_policy) { 104110616SSebastien.Roy@Sun.COM ITPF_CLONE(itp->itp_flags); 104210616SSebastien.Roy@Sun.COM rc = ipsec_copy_polhead(itp->itp_policy, itp->itp_inactive, ns); 104310616SSebastien.Roy@Sun.COM if (rc != 0) { 104410616SSebastien.Roy@Sun.COM /* inactive has already been cleared. */ 104510616SSebastien.Roy@Sun.COM itp->itp_flags &= ~ITPF_IFLAGS; 104610616SSebastien.Roy@Sun.COM goto mutex_bail; 104710616SSebastien.Roy@Sun.COM } 104810616SSebastien.Roy@Sun.COM rw_enter(&itp->itp_policy->iph_lock, RW_WRITER); 104910616SSebastien.Roy@Sun.COM ipsec_polhead_flush(itp->itp_policy, ns); 105010616SSebastien.Roy@Sun.COM } else { 105110616SSebastien.Roy@Sun.COM /* Else assume itp->itp_policy is already flushed. */ 105210616SSebastien.Roy@Sun.COM rw_enter(&itp->itp_policy->iph_lock, RW_WRITER); 105310616SSebastien.Roy@Sun.COM } 105410616SSebastien.Roy@Sun.COM 105510616SSebastien.Roy@Sun.COM if (clear_all) { 105610616SSebastien.Roy@Sun.COM ASSERT(avl_numnodes(&itp->itp_policy->iph_rulebyid) == 0); 105710616SSebastien.Roy@Sun.COM itp->itp_flags &= ~ITPF_PFLAGS; 105810616SSebastien.Roy@Sun.COM rw_exit(&itp->itp_policy->iph_lock); 105910616SSebastien.Roy@Sun.COM old_policy = B_FALSE; /* Clear out the inactive one too. */ 106010616SSebastien.Roy@Sun.COM goto recover_bail; 106110616SSebastien.Roy@Sun.COM } 106210616SSebastien.Roy@Sun.COM 106310616SSebastien.Roy@Sun.COM if (iptun_insert_simple_policies(itp->itp_policy, actp, nact, ns)) { 106410616SSebastien.Roy@Sun.COM rw_exit(&itp->itp_policy->iph_lock); 106510616SSebastien.Roy@Sun.COM /* 106610616SSebastien.Roy@Sun.COM * Adjust MTU and make sure the DL side knows what's up. 106710616SSebastien.Roy@Sun.COM */ 106810616SSebastien.Roy@Sun.COM itp->itp_flags = ITPF_P_ACTIVE; 106911042SErik.Nordmark@Sun.COM (void) iptun_update_mtu(iptun, NULL, 0); 107010616SSebastien.Roy@Sun.COM old_policy = B_FALSE; /* Blank out inactive - we succeeded */ 107110616SSebastien.Roy@Sun.COM } else { 107210616SSebastien.Roy@Sun.COM rw_exit(&itp->itp_policy->iph_lock); 107310616SSebastien.Roy@Sun.COM rc = ENOMEM; 107410616SSebastien.Roy@Sun.COM } 107510616SSebastien.Roy@Sun.COM 107610616SSebastien.Roy@Sun.COM recover_bail: 107710616SSebastien.Roy@Sun.COM if (old_policy) { 107810616SSebastien.Roy@Sun.COM /* Recover policy in in active polhead. */ 107910616SSebastien.Roy@Sun.COM ipsec_swap_policy(itp->itp_policy, itp->itp_inactive, ns); 108010616SSebastien.Roy@Sun.COM ITPF_SWAP(itp->itp_flags); 108110616SSebastien.Roy@Sun.COM } 108210616SSebastien.Roy@Sun.COM 108310616SSebastien.Roy@Sun.COM /* Clear policy in inactive polhead. */ 108410616SSebastien.Roy@Sun.COM itp->itp_flags &= ~ITPF_IFLAGS; 108510616SSebastien.Roy@Sun.COM rw_enter(&itp->itp_inactive->iph_lock, RW_WRITER); 108610616SSebastien.Roy@Sun.COM ipsec_polhead_flush(itp->itp_inactive, ns); 108710616SSebastien.Roy@Sun.COM rw_exit(&itp->itp_inactive->iph_lock); 108810616SSebastien.Roy@Sun.COM 108910616SSebastien.Roy@Sun.COM mutex_bail: 109010616SSebastien.Roy@Sun.COM mutex_exit(&itp->itp_lock); 109110616SSebastien.Roy@Sun.COM 109210616SSebastien.Roy@Sun.COM bail: 109310616SSebastien.Roy@Sun.COM if (actp != NULL) 109410616SSebastien.Roy@Sun.COM ipsec_actvec_free(actp, nact); 109510616SSebastien.Roy@Sun.COM 109610616SSebastien.Roy@Sun.COM return (rc); 109710616SSebastien.Roy@Sun.COM } 109810616SSebastien.Roy@Sun.COM 109910616SSebastien.Roy@Sun.COM static iptun_typeinfo_t * 110010616SSebastien.Roy@Sun.COM iptun_gettypeinfo(iptun_type_t type) 110110616SSebastien.Roy@Sun.COM { 110210616SSebastien.Roy@Sun.COM int i; 110310616SSebastien.Roy@Sun.COM 110410616SSebastien.Roy@Sun.COM for (i = 0; iptun_type_table[i].iti_type != IPTUN_TYPE_UNKNOWN; i++) { 110510616SSebastien.Roy@Sun.COM if (iptun_type_table[i].iti_type == type) 110610616SSebastien.Roy@Sun.COM break; 110710616SSebastien.Roy@Sun.COM } 110810616SSebastien.Roy@Sun.COM return (&iptun_type_table[i]); 110910616SSebastien.Roy@Sun.COM } 111010616SSebastien.Roy@Sun.COM 111110616SSebastien.Roy@Sun.COM /* 111210616SSebastien.Roy@Sun.COM * Set the parameters included in ik on the tunnel iptun. Parameters that can 111310616SSebastien.Roy@Sun.COM * only be set at creation time are set in iptun_create(). 111410616SSebastien.Roy@Sun.COM */ 111510616SSebastien.Roy@Sun.COM static int 111610616SSebastien.Roy@Sun.COM iptun_setparams(iptun_t *iptun, const iptun_kparams_t *ik) 111710616SSebastien.Roy@Sun.COM { 111810616SSebastien.Roy@Sun.COM int err = 0; 111910616SSebastien.Roy@Sun.COM netstack_t *ns = iptun->iptun_ns; 112010616SSebastien.Roy@Sun.COM iptun_addr_t orig_laddr, orig_raddr; 112110616SSebastien.Roy@Sun.COM uint_t orig_flags = iptun->iptun_flags; 112210616SSebastien.Roy@Sun.COM 112310616SSebastien.Roy@Sun.COM if (ik->iptun_kparam_flags & IPTUN_KPARAM_LADDR) { 112410616SSebastien.Roy@Sun.COM if (orig_flags & IPTUN_LADDR) 112510616SSebastien.Roy@Sun.COM orig_laddr = iptun->iptun_laddr; 112610616SSebastien.Roy@Sun.COM if ((err = iptun_setladdr(iptun, &ik->iptun_kparam_laddr)) != 0) 112710616SSebastien.Roy@Sun.COM return (err); 112810616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_LADDR; 112910616SSebastien.Roy@Sun.COM } 113010616SSebastien.Roy@Sun.COM 113110616SSebastien.Roy@Sun.COM if (ik->iptun_kparam_flags & IPTUN_KPARAM_RADDR) { 113210616SSebastien.Roy@Sun.COM if (orig_flags & IPTUN_RADDR) 113310616SSebastien.Roy@Sun.COM orig_raddr = iptun->iptun_raddr; 113410616SSebastien.Roy@Sun.COM if ((err = iptun_setraddr(iptun, &ik->iptun_kparam_raddr)) != 0) 113510616SSebastien.Roy@Sun.COM goto done; 113610616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_RADDR; 113710616SSebastien.Roy@Sun.COM } 113810616SSebastien.Roy@Sun.COM 113910616SSebastien.Roy@Sun.COM if (ik->iptun_kparam_flags & IPTUN_KPARAM_SECINFO) { 114010616SSebastien.Roy@Sun.COM /* 114110616SSebastien.Roy@Sun.COM * Set IPsec policy originating from the ifconfig(1M) command 114210616SSebastien.Roy@Sun.COM * line. This is traditionally called "simple" policy because 114310616SSebastien.Roy@Sun.COM * the ipsec_req_t (iptun_kparam_secinfo) can only describe a 114410616SSebastien.Roy@Sun.COM * simple policy of "do ESP on everything" and/or "do AH on 114510616SSebastien.Roy@Sun.COM * everything" (as opposed to the rich policy that can be 114610616SSebastien.Roy@Sun.COM * defined with ipsecconf(1M)). 114710616SSebastien.Roy@Sun.COM */ 114810616SSebastien.Roy@Sun.COM if (iptun->iptun_typeinfo->iti_type == IPTUN_TYPE_6TO4) { 114910616SSebastien.Roy@Sun.COM /* 115010616SSebastien.Roy@Sun.COM * Can't set security properties for automatic 115110616SSebastien.Roy@Sun.COM * tunnels. 115210616SSebastien.Roy@Sun.COM */ 115310616SSebastien.Roy@Sun.COM err = EINVAL; 115410616SSebastien.Roy@Sun.COM goto done; 115510616SSebastien.Roy@Sun.COM } 115610616SSebastien.Roy@Sun.COM 115710616SSebastien.Roy@Sun.COM if (!ipsec_loaded(ns->netstack_ipsec)) { 115810616SSebastien.Roy@Sun.COM /* If IPsec can be loaded, try and load it now. */ 115910616SSebastien.Roy@Sun.COM if (ipsec_failed(ns->netstack_ipsec)) { 116010616SSebastien.Roy@Sun.COM err = EPROTONOSUPPORT; 116110616SSebastien.Roy@Sun.COM goto done; 116210616SSebastien.Roy@Sun.COM } 116310616SSebastien.Roy@Sun.COM ipsec_loader_loadnow(ns->netstack_ipsec); 116410616SSebastien.Roy@Sun.COM /* 116510616SSebastien.Roy@Sun.COM * ipsec_loader_loadnow() returns while IPsec is 116610616SSebastien.Roy@Sun.COM * loaded asynchronously. While a method exists to 116710616SSebastien.Roy@Sun.COM * wait for IPsec to load (ipsec_loader_wait()), it 116810616SSebastien.Roy@Sun.COM * requires use of a STREAMS queue to do a qwait(). 116910616SSebastien.Roy@Sun.COM * We're not in STREAMS context here, and so we can't 117010616SSebastien.Roy@Sun.COM * use it. This is not a problem in practice because 117110616SSebastien.Roy@Sun.COM * in the vast majority of cases, key management and 117210616SSebastien.Roy@Sun.COM * global policy will have loaded before any tunnels 117310616SSebastien.Roy@Sun.COM * are plumbed, and so IPsec will already have been 117410616SSebastien.Roy@Sun.COM * loaded. 117510616SSebastien.Roy@Sun.COM */ 117610616SSebastien.Roy@Sun.COM err = EAGAIN; 117710616SSebastien.Roy@Sun.COM goto done; 117810616SSebastien.Roy@Sun.COM } 117910616SSebastien.Roy@Sun.COM 118010616SSebastien.Roy@Sun.COM err = iptun_set_sec_simple(iptun, &ik->iptun_kparam_secinfo); 118110616SSebastien.Roy@Sun.COM if (err == 0) { 118210616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_SIMPLE_POLICY; 118310616SSebastien.Roy@Sun.COM iptun->iptun_simple_policy = ik->iptun_kparam_secinfo; 118410616SSebastien.Roy@Sun.COM } 118510616SSebastien.Roy@Sun.COM } 118610616SSebastien.Roy@Sun.COM done: 118710616SSebastien.Roy@Sun.COM if (err != 0) { 118810616SSebastien.Roy@Sun.COM /* Restore original source and destination. */ 118910616SSebastien.Roy@Sun.COM if (ik->iptun_kparam_flags & IPTUN_KPARAM_LADDR && 119010616SSebastien.Roy@Sun.COM (orig_flags & IPTUN_LADDR)) 119110616SSebastien.Roy@Sun.COM iptun->iptun_laddr = orig_laddr; 119210616SSebastien.Roy@Sun.COM if ((ik->iptun_kparam_flags & IPTUN_KPARAM_RADDR) && 119310616SSebastien.Roy@Sun.COM (orig_flags & IPTUN_RADDR)) 119410616SSebastien.Roy@Sun.COM iptun->iptun_raddr = orig_raddr; 119510616SSebastien.Roy@Sun.COM iptun->iptun_flags = orig_flags; 119610616SSebastien.Roy@Sun.COM } 119710616SSebastien.Roy@Sun.COM return (err); 119810616SSebastien.Roy@Sun.COM } 119910616SSebastien.Roy@Sun.COM 120010616SSebastien.Roy@Sun.COM static int 120110616SSebastien.Roy@Sun.COM iptun_register(iptun_t *iptun) 120210616SSebastien.Roy@Sun.COM { 120310616SSebastien.Roy@Sun.COM mac_register_t *mac; 120410616SSebastien.Roy@Sun.COM int err; 120510616SSebastien.Roy@Sun.COM 120610616SSebastien.Roy@Sun.COM ASSERT(!(iptun->iptun_flags & IPTUN_MAC_REGISTERED)); 120710616SSebastien.Roy@Sun.COM 120810616SSebastien.Roy@Sun.COM if ((mac = mac_alloc(MAC_VERSION)) == NULL) 120910616SSebastien.Roy@Sun.COM return (EINVAL); 121010616SSebastien.Roy@Sun.COM 121110616SSebastien.Roy@Sun.COM mac->m_type_ident = iptun->iptun_typeinfo->iti_ident; 121210616SSebastien.Roy@Sun.COM mac->m_driver = iptun; 121310616SSebastien.Roy@Sun.COM mac->m_dip = iptun_dip; 121410616SSebastien.Roy@Sun.COM mac->m_instance = (uint_t)-1; 121510616SSebastien.Roy@Sun.COM mac->m_src_addr = (uint8_t *)&iptun->iptun_laddr.ia_addr; 121610616SSebastien.Roy@Sun.COM mac->m_dst_addr = iptun->iptun_typeinfo->iti_hasraddr ? 121710616SSebastien.Roy@Sun.COM (uint8_t *)&iptun->iptun_raddr.ia_addr : NULL; 121810616SSebastien.Roy@Sun.COM mac->m_callbacks = &iptun_m_callbacks; 121910616SSebastien.Roy@Sun.COM mac->m_min_sdu = iptun->iptun_typeinfo->iti_minmtu; 122010616SSebastien.Roy@Sun.COM mac->m_max_sdu = iptun->iptun_mtu; 122110616SSebastien.Roy@Sun.COM if (iptun->iptun_header_size != 0) { 122210616SSebastien.Roy@Sun.COM mac->m_pdata = &iptun->iptun_header; 122310616SSebastien.Roy@Sun.COM mac->m_pdata_size = iptun->iptun_header_size; 122410616SSebastien.Roy@Sun.COM } 122510616SSebastien.Roy@Sun.COM if ((err = mac_register(mac, &iptun->iptun_mh)) == 0) 122610616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_MAC_REGISTERED; 122710616SSebastien.Roy@Sun.COM mac_free(mac); 122810616SSebastien.Roy@Sun.COM return (err); 122910616SSebastien.Roy@Sun.COM } 123010616SSebastien.Roy@Sun.COM 123110616SSebastien.Roy@Sun.COM static int 123210616SSebastien.Roy@Sun.COM iptun_unregister(iptun_t *iptun) 123310616SSebastien.Roy@Sun.COM { 123410616SSebastien.Roy@Sun.COM int err; 123510616SSebastien.Roy@Sun.COM 123610616SSebastien.Roy@Sun.COM ASSERT(iptun->iptun_flags & IPTUN_MAC_REGISTERED); 123710616SSebastien.Roy@Sun.COM if ((err = mac_unregister(iptun->iptun_mh)) == 0) 123810616SSebastien.Roy@Sun.COM iptun->iptun_flags &= ~IPTUN_MAC_REGISTERED; 123910616SSebastien.Roy@Sun.COM return (err); 124010616SSebastien.Roy@Sun.COM } 124110616SSebastien.Roy@Sun.COM 124210616SSebastien.Roy@Sun.COM static conn_t * 124310616SSebastien.Roy@Sun.COM iptun_conn_create(iptun_t *iptun, netstack_t *ns, cred_t *credp) 124410616SSebastien.Roy@Sun.COM { 124510616SSebastien.Roy@Sun.COM conn_t *connp; 124610616SSebastien.Roy@Sun.COM 124710616SSebastien.Roy@Sun.COM if ((connp = ipcl_conn_create(IPCL_IPCCONN, KM_NOSLEEP, ns)) == NULL) 124810616SSebastien.Roy@Sun.COM return (NULL); 124910616SSebastien.Roy@Sun.COM 125010616SSebastien.Roy@Sun.COM connp->conn_flags |= IPCL_IPTUN; 125110616SSebastien.Roy@Sun.COM connp->conn_iptun = iptun; 125210616SSebastien.Roy@Sun.COM connp->conn_recv = iptun_input; 125311042SErik.Nordmark@Sun.COM connp->conn_recvicmp = iptun_input_icmp; 125411042SErik.Nordmark@Sun.COM connp->conn_verifyicmp = iptun_verifyicmp; 125511042SErik.Nordmark@Sun.COM 125611042SErik.Nordmark@Sun.COM /* 125711042SErik.Nordmark@Sun.COM * Register iptun_notify to listen to capability changes detected by IP. 125811042SErik.Nordmark@Sun.COM * This upcall is made in the context of the call to conn_ip_output. 125911042SErik.Nordmark@Sun.COM */ 126011042SErik.Nordmark@Sun.COM connp->conn_ixa->ixa_notify = iptun_notify; 126111042SErik.Nordmark@Sun.COM connp->conn_ixa->ixa_notify_cookie = iptun; 126211042SErik.Nordmark@Sun.COM 126310616SSebastien.Roy@Sun.COM /* 126410616SSebastien.Roy@Sun.COM * For exclusive stacks we set conn_zoneid to GLOBAL_ZONEID as is done 126510616SSebastien.Roy@Sun.COM * for all other conn_t's. 126610616SSebastien.Roy@Sun.COM * 126710616SSebastien.Roy@Sun.COM * Note that there's an important distinction between iptun_zoneid and 126810616SSebastien.Roy@Sun.COM * conn_zoneid. The conn_zoneid is set to GLOBAL_ZONEID in non-global 126910616SSebastien.Roy@Sun.COM * exclusive stack zones to make the ip module believe that the 127010616SSebastien.Roy@Sun.COM * non-global zone is actually a global zone. Therefore, when 127110616SSebastien.Roy@Sun.COM * interacting with the ip module, we must always use conn_zoneid. 127210616SSebastien.Roy@Sun.COM */ 127310616SSebastien.Roy@Sun.COM connp->conn_zoneid = (ns->netstack_stackid == GLOBAL_NETSTACKID) ? 127410616SSebastien.Roy@Sun.COM crgetzoneid(credp) : GLOBAL_ZONEID; 127510616SSebastien.Roy@Sun.COM connp->conn_cred = credp; 127610616SSebastien.Roy@Sun.COM /* crfree() is done in ipcl_conn_destroy(), called by CONN_DEC_REF() */ 127710616SSebastien.Roy@Sun.COM crhold(connp->conn_cred); 127811042SErik.Nordmark@Sun.COM connp->conn_cpid = NOPID; 127911042SErik.Nordmark@Sun.COM 128011042SErik.Nordmark@Sun.COM /* conn_allzones can not be set this early, hence no IPCL_ZONEID */ 128111042SErik.Nordmark@Sun.COM connp->conn_ixa->ixa_zoneid = connp->conn_zoneid; 128210616SSebastien.Roy@Sun.COM ASSERT(connp->conn_ref == 1); 128310616SSebastien.Roy@Sun.COM 128411042SErik.Nordmark@Sun.COM /* Cache things in ixa without an extra refhold */ 1285*11849SErik.Nordmark@Sun.COM ASSERT(!(connp->conn_ixa->ixa_free_flags & IXA_FREE_CRED)); 128611042SErik.Nordmark@Sun.COM connp->conn_ixa->ixa_cred = connp->conn_cred; 128711042SErik.Nordmark@Sun.COM connp->conn_ixa->ixa_cpid = connp->conn_cpid; 128811042SErik.Nordmark@Sun.COM if (is_system_labeled()) 128911042SErik.Nordmark@Sun.COM connp->conn_ixa->ixa_tsl = crgetlabel(connp->conn_cred); 129011042SErik.Nordmark@Sun.COM 129111042SErik.Nordmark@Sun.COM /* 129211042SErik.Nordmark@Sun.COM * Have conn_ip_output drop packets should our outer source 129311042SErik.Nordmark@Sun.COM * go invalid 129411042SErik.Nordmark@Sun.COM */ 129511042SErik.Nordmark@Sun.COM connp->conn_ixa->ixa_flags |= IXAF_VERIFY_SOURCE; 129611042SErik.Nordmark@Sun.COM 129711042SErik.Nordmark@Sun.COM switch (iptun->iptun_typeinfo->iti_ipvers) { 129811042SErik.Nordmark@Sun.COM case IPV4_VERSION: 129911042SErik.Nordmark@Sun.COM connp->conn_family = AF_INET6; 130011042SErik.Nordmark@Sun.COM break; 130111042SErik.Nordmark@Sun.COM case IPV6_VERSION: 130211042SErik.Nordmark@Sun.COM connp->conn_family = AF_INET; 130311042SErik.Nordmark@Sun.COM break; 130411042SErik.Nordmark@Sun.COM } 130510616SSebastien.Roy@Sun.COM mutex_enter(&connp->conn_lock); 130610616SSebastien.Roy@Sun.COM connp->conn_state_flags &= ~CONN_INCIPIENT; 130710616SSebastien.Roy@Sun.COM mutex_exit(&connp->conn_lock); 130810616SSebastien.Roy@Sun.COM return (connp); 130910616SSebastien.Roy@Sun.COM } 131010616SSebastien.Roy@Sun.COM 131110616SSebastien.Roy@Sun.COM static void 131210616SSebastien.Roy@Sun.COM iptun_conn_destroy(conn_t *connp) 131310616SSebastien.Roy@Sun.COM { 131410616SSebastien.Roy@Sun.COM ip_quiesce_conn(connp); 131510616SSebastien.Roy@Sun.COM connp->conn_iptun = NULL; 131610616SSebastien.Roy@Sun.COM ASSERT(connp->conn_ref == 1); 131710616SSebastien.Roy@Sun.COM CONN_DEC_REF(connp); 131810616SSebastien.Roy@Sun.COM } 131910616SSebastien.Roy@Sun.COM 132010616SSebastien.Roy@Sun.COM static iptun_t * 132110616SSebastien.Roy@Sun.COM iptun_alloc(void) 132210616SSebastien.Roy@Sun.COM { 132310616SSebastien.Roy@Sun.COM iptun_t *iptun; 132410616SSebastien.Roy@Sun.COM 132510616SSebastien.Roy@Sun.COM if ((iptun = kmem_cache_alloc(iptun_cache, KM_NOSLEEP)) != NULL) { 132610616SSebastien.Roy@Sun.COM bzero(iptun, sizeof (*iptun)); 132710616SSebastien.Roy@Sun.COM atomic_inc_32(&iptun_tunnelcount); 132810616SSebastien.Roy@Sun.COM } 132910616SSebastien.Roy@Sun.COM return (iptun); 133010616SSebastien.Roy@Sun.COM } 133110616SSebastien.Roy@Sun.COM 133210616SSebastien.Roy@Sun.COM static void 133310616SSebastien.Roy@Sun.COM iptun_free(iptun_t *iptun) 133410616SSebastien.Roy@Sun.COM { 133510616SSebastien.Roy@Sun.COM ASSERT(iptun->iptun_flags & IPTUN_CONDEMNED); 133610616SSebastien.Roy@Sun.COM 133710616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_HASH_INSERTED) { 133810616SSebastien.Roy@Sun.COM iptun_stack_t *iptuns = iptun->iptun_iptuns; 133910616SSebastien.Roy@Sun.COM 134010616SSebastien.Roy@Sun.COM mutex_enter(&iptun_hash_lock); 134110616SSebastien.Roy@Sun.COM VERIFY(mod_hash_remove(iptun_hash, 134210616SSebastien.Roy@Sun.COM IPTUN_HASH_KEY(iptun->iptun_linkid), 134310616SSebastien.Roy@Sun.COM (mod_hash_val_t *)&iptun) == 0); 134410616SSebastien.Roy@Sun.COM mutex_exit(&iptun_hash_lock); 134510616SSebastien.Roy@Sun.COM iptun->iptun_flags &= ~IPTUN_HASH_INSERTED; 134610616SSebastien.Roy@Sun.COM mutex_enter(&iptuns->iptuns_lock); 134710616SSebastien.Roy@Sun.COM list_remove(&iptuns->iptuns_iptunlist, iptun); 134810616SSebastien.Roy@Sun.COM mutex_exit(&iptuns->iptuns_lock); 134910616SSebastien.Roy@Sun.COM } 135010616SSebastien.Roy@Sun.COM 135110616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_BOUND) 135210616SSebastien.Roy@Sun.COM iptun_unbind(iptun); 135310616SSebastien.Roy@Sun.COM 135410616SSebastien.Roy@Sun.COM /* 135510616SSebastien.Roy@Sun.COM * After iptun_unregister(), there will be no threads executing a 135610616SSebastien.Roy@Sun.COM * downcall from the mac module, including in the tx datapath. 135710616SSebastien.Roy@Sun.COM */ 135810616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_MAC_REGISTERED) 135910616SSebastien.Roy@Sun.COM VERIFY(iptun_unregister(iptun) == 0); 136010616SSebastien.Roy@Sun.COM 136110616SSebastien.Roy@Sun.COM if (iptun->iptun_itp != NULL) { 136210616SSebastien.Roy@Sun.COM /* 136310616SSebastien.Roy@Sun.COM * Remove from the AVL tree, AND release the reference iptun_t 136410616SSebastien.Roy@Sun.COM * itself holds on the ITP. 136510616SSebastien.Roy@Sun.COM */ 136610616SSebastien.Roy@Sun.COM itp_unlink(iptun->iptun_itp, iptun->iptun_ns); 136710616SSebastien.Roy@Sun.COM ITP_REFRELE(iptun->iptun_itp, iptun->iptun_ns); 136810616SSebastien.Roy@Sun.COM iptun->iptun_itp = NULL; 136910616SSebastien.Roy@Sun.COM iptun->iptun_flags &= ~IPTUN_SIMPLE_POLICY; 137010616SSebastien.Roy@Sun.COM } 137110616SSebastien.Roy@Sun.COM 137210616SSebastien.Roy@Sun.COM /* 137310616SSebastien.Roy@Sun.COM * After ipcl_conn_destroy(), there will be no threads executing an 137410616SSebastien.Roy@Sun.COM * upcall from ip (i.e., iptun_input()), and it is then safe to free 137510616SSebastien.Roy@Sun.COM * the iptun_t. 137610616SSebastien.Roy@Sun.COM */ 137710616SSebastien.Roy@Sun.COM if (iptun->iptun_connp != NULL) { 137810616SSebastien.Roy@Sun.COM iptun_conn_destroy(iptun->iptun_connp); 137910616SSebastien.Roy@Sun.COM iptun->iptun_connp = NULL; 138010616SSebastien.Roy@Sun.COM } 138110616SSebastien.Roy@Sun.COM 138210616SSebastien.Roy@Sun.COM kmem_cache_free(iptun_cache, iptun); 138310616SSebastien.Roy@Sun.COM atomic_dec_32(&iptun_tunnelcount); 138410616SSebastien.Roy@Sun.COM } 138510616SSebastien.Roy@Sun.COM 138610616SSebastien.Roy@Sun.COM int 138710616SSebastien.Roy@Sun.COM iptun_create(iptun_kparams_t *ik, cred_t *credp) 138810616SSebastien.Roy@Sun.COM { 138910616SSebastien.Roy@Sun.COM iptun_t *iptun = NULL; 139010616SSebastien.Roy@Sun.COM int err = 0, mherr; 139110616SSebastien.Roy@Sun.COM char linkname[MAXLINKNAMELEN]; 139210616SSebastien.Roy@Sun.COM ipsec_tun_pol_t *itp; 139310616SSebastien.Roy@Sun.COM netstack_t *ns = NULL; 139410616SSebastien.Roy@Sun.COM iptun_stack_t *iptuns; 139510616SSebastien.Roy@Sun.COM datalink_id_t tmpid; 139610616SSebastien.Roy@Sun.COM zoneid_t zoneid = crgetzoneid(credp); 139710616SSebastien.Roy@Sun.COM boolean_t link_created = B_FALSE; 139810616SSebastien.Roy@Sun.COM 139910616SSebastien.Roy@Sun.COM /* The tunnel type is mandatory */ 140010616SSebastien.Roy@Sun.COM if (!(ik->iptun_kparam_flags & IPTUN_KPARAM_TYPE)) 140110616SSebastien.Roy@Sun.COM return (EINVAL); 140210616SSebastien.Roy@Sun.COM 140310616SSebastien.Roy@Sun.COM /* 140410616SSebastien.Roy@Sun.COM * Is the linkid that the caller wishes to associate with this new 140510616SSebastien.Roy@Sun.COM * tunnel assigned to this zone? 140610616SSebastien.Roy@Sun.COM */ 140710616SSebastien.Roy@Sun.COM if (zone_check_datalink(&zoneid, ik->iptun_kparam_linkid) != 0) { 140810616SSebastien.Roy@Sun.COM if (zoneid != GLOBAL_ZONEID) 140910616SSebastien.Roy@Sun.COM return (EINVAL); 141010616SSebastien.Roy@Sun.COM } else if (zoneid == GLOBAL_ZONEID) { 141110616SSebastien.Roy@Sun.COM return (EINVAL); 141210616SSebastien.Roy@Sun.COM } 141310616SSebastien.Roy@Sun.COM 141410616SSebastien.Roy@Sun.COM /* 141510616SSebastien.Roy@Sun.COM * Make sure that we're not trying to create a tunnel that has already 141610616SSebastien.Roy@Sun.COM * been created. 141710616SSebastien.Roy@Sun.COM */ 141810616SSebastien.Roy@Sun.COM if (iptun_enter_by_linkid(ik->iptun_kparam_linkid, &iptun) == 0) { 141910616SSebastien.Roy@Sun.COM iptun_exit(iptun); 142010616SSebastien.Roy@Sun.COM iptun = NULL; 142110616SSebastien.Roy@Sun.COM err = EEXIST; 142210616SSebastien.Roy@Sun.COM goto done; 142310616SSebastien.Roy@Sun.COM } 142410616SSebastien.Roy@Sun.COM 142510616SSebastien.Roy@Sun.COM ns = netstack_find_by_cred(credp); 142610616SSebastien.Roy@Sun.COM iptuns = ns->netstack_iptun; 142710616SSebastien.Roy@Sun.COM 142810616SSebastien.Roy@Sun.COM if ((iptun = iptun_alloc()) == NULL) { 142910616SSebastien.Roy@Sun.COM err = ENOMEM; 143010616SSebastien.Roy@Sun.COM goto done; 143110616SSebastien.Roy@Sun.COM } 143210616SSebastien.Roy@Sun.COM 143310616SSebastien.Roy@Sun.COM iptun->iptun_linkid = ik->iptun_kparam_linkid; 143410616SSebastien.Roy@Sun.COM iptun->iptun_zoneid = zoneid; 143510616SSebastien.Roy@Sun.COM iptun->iptun_ns = ns; 143610616SSebastien.Roy@Sun.COM 143710616SSebastien.Roy@Sun.COM iptun->iptun_typeinfo = iptun_gettypeinfo(ik->iptun_kparam_type); 143810616SSebastien.Roy@Sun.COM if (iptun->iptun_typeinfo->iti_type == IPTUN_TYPE_UNKNOWN) { 143910616SSebastien.Roy@Sun.COM err = EINVAL; 144010616SSebastien.Roy@Sun.COM goto done; 144110616SSebastien.Roy@Sun.COM } 144210616SSebastien.Roy@Sun.COM 144310616SSebastien.Roy@Sun.COM if (ik->iptun_kparam_flags & IPTUN_KPARAM_IMPLICIT) 144410616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_IMPLICIT; 144510616SSebastien.Roy@Sun.COM 144610616SSebastien.Roy@Sun.COM if ((err = iptun_setparams(iptun, ik)) != 0) 144710616SSebastien.Roy@Sun.COM goto done; 144810616SSebastien.Roy@Sun.COM 144910616SSebastien.Roy@Sun.COM iptun->iptun_hoplimit = IPTUN_DEFAULT_HOPLIMIT; 145010616SSebastien.Roy@Sun.COM if (iptun->iptun_typeinfo->iti_type == IPTUN_TYPE_IPV6) 145110616SSebastien.Roy@Sun.COM iptun->iptun_encaplimit = IPTUN_DEFAULT_ENCAPLIMIT; 145210616SSebastien.Roy@Sun.COM 145310616SSebastien.Roy@Sun.COM iptun_headergen(iptun, B_FALSE); 145410616SSebastien.Roy@Sun.COM 145510616SSebastien.Roy@Sun.COM iptun->iptun_connp = iptun_conn_create(iptun, ns, credp); 145610616SSebastien.Roy@Sun.COM if (iptun->iptun_connp == NULL) { 145710616SSebastien.Roy@Sun.COM err = ENOMEM; 145810616SSebastien.Roy@Sun.COM goto done; 145910616SSebastien.Roy@Sun.COM } 146010616SSebastien.Roy@Sun.COM 146110616SSebastien.Roy@Sun.COM iptun->iptun_mtu = iptun->iptun_typeinfo->iti_maxmtu; 146210616SSebastien.Roy@Sun.COM iptun->iptun_dpmtu = iptun->iptun_mtu; 146310616SSebastien.Roy@Sun.COM 146410616SSebastien.Roy@Sun.COM /* 146510616SSebastien.Roy@Sun.COM * Find an ITP based on linkname. If we have parms already set via 146610616SSebastien.Roy@Sun.COM * the iptun_setparams() call above, it may have created an ITP for 146710616SSebastien.Roy@Sun.COM * us. We always try get_tunnel_policy() for DEBUG correctness 146810616SSebastien.Roy@Sun.COM * checks, and we may wish to refactor this to only check when 146910616SSebastien.Roy@Sun.COM * iptun_itp is NULL. 147010616SSebastien.Roy@Sun.COM */ 147110616SSebastien.Roy@Sun.COM if ((err = dls_mgmt_get_linkinfo(iptun->iptun_linkid, linkname, NULL, 147210616SSebastien.Roy@Sun.COM NULL, NULL)) != 0) 147310616SSebastien.Roy@Sun.COM goto done; 147410616SSebastien.Roy@Sun.COM if ((itp = get_tunnel_policy(linkname, ns)) != NULL) 147510616SSebastien.Roy@Sun.COM iptun->iptun_itp = itp; 147610616SSebastien.Roy@Sun.COM 147710616SSebastien.Roy@Sun.COM /* 147810616SSebastien.Roy@Sun.COM * See if we have the necessary IP addresses assigned to this tunnel 147910616SSebastien.Roy@Sun.COM * to try and bind them with ip underneath us. If we're not ready to 148010616SSebastien.Roy@Sun.COM * bind yet, then we'll defer the bind operation until the addresses 148110616SSebastien.Roy@Sun.COM * are modified. 148210616SSebastien.Roy@Sun.COM */ 148310616SSebastien.Roy@Sun.COM if (iptun_canbind(iptun) && ((err = iptun_bind(iptun)) != 0)) 148410616SSebastien.Roy@Sun.COM goto done; 148510616SSebastien.Roy@Sun.COM 148610616SSebastien.Roy@Sun.COM if ((err = iptun_register(iptun)) != 0) 148710616SSebastien.Roy@Sun.COM goto done; 148810616SSebastien.Roy@Sun.COM 148910616SSebastien.Roy@Sun.COM err = dls_devnet_create(iptun->iptun_mh, iptun->iptun_linkid, 149010616SSebastien.Roy@Sun.COM iptun->iptun_zoneid); 149110616SSebastien.Roy@Sun.COM if (err != 0) 149210616SSebastien.Roy@Sun.COM goto done; 149310616SSebastien.Roy@Sun.COM link_created = B_TRUE; 149410616SSebastien.Roy@Sun.COM 149510616SSebastien.Roy@Sun.COM /* 149610616SSebastien.Roy@Sun.COM * We hash by link-id as that is the key used by all other iptun 149710616SSebastien.Roy@Sun.COM * interfaces (modify, delete, etc.). 149810616SSebastien.Roy@Sun.COM */ 149910616SSebastien.Roy@Sun.COM if ((mherr = mod_hash_insert(iptun_hash, 150010616SSebastien.Roy@Sun.COM IPTUN_HASH_KEY(iptun->iptun_linkid), (mod_hash_val_t)iptun)) == 0) { 150110616SSebastien.Roy@Sun.COM mutex_enter(&iptuns->iptuns_lock); 150210616SSebastien.Roy@Sun.COM list_insert_head(&iptuns->iptuns_iptunlist, iptun); 150310616SSebastien.Roy@Sun.COM mutex_exit(&iptuns->iptuns_lock); 150410616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_HASH_INSERTED; 150510616SSebastien.Roy@Sun.COM } else if (mherr == MH_ERR_NOMEM) { 150610616SSebastien.Roy@Sun.COM err = ENOMEM; 150710616SSebastien.Roy@Sun.COM } else if (mherr == MH_ERR_DUPLICATE) { 150810616SSebastien.Roy@Sun.COM err = EEXIST; 150910616SSebastien.Roy@Sun.COM } else { 151010616SSebastien.Roy@Sun.COM err = EINVAL; 151110616SSebastien.Roy@Sun.COM } 151210616SSebastien.Roy@Sun.COM 151310616SSebastien.Roy@Sun.COM done: 151410616SSebastien.Roy@Sun.COM if (iptun == NULL && ns != NULL) 151510616SSebastien.Roy@Sun.COM netstack_rele(ns); 151610616SSebastien.Roy@Sun.COM if (err != 0 && iptun != NULL) { 151710616SSebastien.Roy@Sun.COM if (link_created) { 151810616SSebastien.Roy@Sun.COM (void) dls_devnet_destroy(iptun->iptun_mh, &tmpid, 151910616SSebastien.Roy@Sun.COM B_TRUE); 152010616SSebastien.Roy@Sun.COM } 152110616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_CONDEMNED; 152210616SSebastien.Roy@Sun.COM iptun_free(iptun); 152310616SSebastien.Roy@Sun.COM } 152410616SSebastien.Roy@Sun.COM return (err); 152510616SSebastien.Roy@Sun.COM } 152610616SSebastien.Roy@Sun.COM 152710616SSebastien.Roy@Sun.COM int 152810616SSebastien.Roy@Sun.COM iptun_delete(datalink_id_t linkid, cred_t *credp) 152910616SSebastien.Roy@Sun.COM { 153010616SSebastien.Roy@Sun.COM int err; 153110616SSebastien.Roy@Sun.COM iptun_t *iptun = NULL; 153210616SSebastien.Roy@Sun.COM 153310616SSebastien.Roy@Sun.COM if ((err = iptun_enter_by_linkid(linkid, &iptun)) != 0) 153410616SSebastien.Roy@Sun.COM return (err); 153510616SSebastien.Roy@Sun.COM 153610616SSebastien.Roy@Sun.COM /* One cannot delete a tunnel that belongs to another zone. */ 153710616SSebastien.Roy@Sun.COM if (iptun->iptun_zoneid != crgetzoneid(credp)) { 153810616SSebastien.Roy@Sun.COM iptun_exit(iptun); 153910616SSebastien.Roy@Sun.COM return (EACCES); 154010616SSebastien.Roy@Sun.COM } 154110616SSebastien.Roy@Sun.COM 154210616SSebastien.Roy@Sun.COM /* 154310616SSebastien.Roy@Sun.COM * We need to exit iptun in order to issue calls up the stack such as 154410616SSebastien.Roy@Sun.COM * dls_devnet_destroy(). If we call up while still in iptun, deadlock 154510616SSebastien.Roy@Sun.COM * with calls coming down the stack is possible. We prevent other 154610616SSebastien.Roy@Sun.COM * threads from entering this iptun after we've exited it by setting 154710616SSebastien.Roy@Sun.COM * the IPTUN_DELETE_PENDING flag. This will cause callers of 154810616SSebastien.Roy@Sun.COM * iptun_enter() to block waiting on iptun_enter_cv. The assumption 154910616SSebastien.Roy@Sun.COM * here is that the functions we're calling while IPTUN_DELETE_PENDING 155010616SSebastien.Roy@Sun.COM * is set dont resuult in an iptun_enter() call, as that would result 155110616SSebastien.Roy@Sun.COM * in deadlock. 155210616SSebastien.Roy@Sun.COM */ 155310616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_DELETE_PENDING; 155410616SSebastien.Roy@Sun.COM 155510616SSebastien.Roy@Sun.COM /* Wait for any pending upcall to the mac module to complete. */ 155610616SSebastien.Roy@Sun.COM while (iptun->iptun_flags & IPTUN_UPCALL_PENDING) 155710616SSebastien.Roy@Sun.COM cv_wait(&iptun->iptun_upcall_cv, &iptun->iptun_lock); 155810616SSebastien.Roy@Sun.COM 155910616SSebastien.Roy@Sun.COM iptun_exit(iptun); 156010616SSebastien.Roy@Sun.COM 156110616SSebastien.Roy@Sun.COM if ((err = dls_devnet_destroy(iptun->iptun_mh, &linkid, B_TRUE)) == 0) { 156210616SSebastien.Roy@Sun.COM /* 156310616SSebastien.Roy@Sun.COM * mac_disable() will fail with EBUSY if there are references 156410616SSebastien.Roy@Sun.COM * to the iptun MAC. If there are none, then mac_disable() 156510616SSebastien.Roy@Sun.COM * will assure that none can be acquired until the MAC is 156610616SSebastien.Roy@Sun.COM * unregistered. 156710616SSebastien.Roy@Sun.COM * 156810616SSebastien.Roy@Sun.COM * XXX CR 6791335 prevents us from calling mac_disable() prior 156910616SSebastien.Roy@Sun.COM * to dls_devnet_destroy(), so we unfortunately need to 157010616SSebastien.Roy@Sun.COM * attempt to re-create the devnet node if mac_disable() 157110616SSebastien.Roy@Sun.COM * fails. 157210616SSebastien.Roy@Sun.COM */ 157310616SSebastien.Roy@Sun.COM if ((err = mac_disable(iptun->iptun_mh)) != 0) { 157410616SSebastien.Roy@Sun.COM (void) dls_devnet_create(iptun->iptun_mh, linkid, 157510616SSebastien.Roy@Sun.COM iptun->iptun_zoneid); 157610616SSebastien.Roy@Sun.COM } 157710616SSebastien.Roy@Sun.COM } 157810616SSebastien.Roy@Sun.COM 157910616SSebastien.Roy@Sun.COM /* 158010616SSebastien.Roy@Sun.COM * Now that we know the fate of this iptun_t, we need to clear 158110616SSebastien.Roy@Sun.COM * IPTUN_DELETE_PENDING, and set IPTUN_CONDEMNED if the iptun_t is 158210616SSebastien.Roy@Sun.COM * slated to be freed. Either way, we need to signal the threads 158310616SSebastien.Roy@Sun.COM * waiting in iptun_enter() so that they can either fail if 158410616SSebastien.Roy@Sun.COM * IPTUN_CONDEMNED is set, or continue if it's not. 158510616SSebastien.Roy@Sun.COM */ 158610616SSebastien.Roy@Sun.COM mutex_enter(&iptun->iptun_lock); 158710616SSebastien.Roy@Sun.COM iptun->iptun_flags &= ~IPTUN_DELETE_PENDING; 158810616SSebastien.Roy@Sun.COM if (err == 0) 158910616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_CONDEMNED; 159010616SSebastien.Roy@Sun.COM cv_broadcast(&iptun->iptun_enter_cv); 159110616SSebastien.Roy@Sun.COM mutex_exit(&iptun->iptun_lock); 159210616SSebastien.Roy@Sun.COM 159310616SSebastien.Roy@Sun.COM /* 159410616SSebastien.Roy@Sun.COM * Note that there is no danger in calling iptun_free() after having 159510616SSebastien.Roy@Sun.COM * dropped the iptun_lock since callers of iptun_enter() at this point 159610616SSebastien.Roy@Sun.COM * are doing so from iptun_enter_by_linkid() (mac_disable() got rid of 159710616SSebastien.Roy@Sun.COM * threads entering from mac callbacks which call iptun_enter() 159810616SSebastien.Roy@Sun.COM * directly) which holds iptun_hash_lock, and iptun_free() grabs this 159910616SSebastien.Roy@Sun.COM * lock in order to remove the iptun_t from the hash table. 160010616SSebastien.Roy@Sun.COM */ 160110616SSebastien.Roy@Sun.COM if (err == 0) 160210616SSebastien.Roy@Sun.COM iptun_free(iptun); 160310616SSebastien.Roy@Sun.COM 160410616SSebastien.Roy@Sun.COM return (err); 160510616SSebastien.Roy@Sun.COM } 160610616SSebastien.Roy@Sun.COM 160710616SSebastien.Roy@Sun.COM int 160810616SSebastien.Roy@Sun.COM iptun_modify(const iptun_kparams_t *ik, cred_t *credp) 160910616SSebastien.Roy@Sun.COM { 161010616SSebastien.Roy@Sun.COM iptun_t *iptun; 161110616SSebastien.Roy@Sun.COM boolean_t laddr_change = B_FALSE, raddr_change = B_FALSE; 161210616SSebastien.Roy@Sun.COM int err; 161310616SSebastien.Roy@Sun.COM 161410616SSebastien.Roy@Sun.COM if ((err = iptun_enter_by_linkid(ik->iptun_kparam_linkid, &iptun)) != 0) 161510616SSebastien.Roy@Sun.COM return (err); 161610616SSebastien.Roy@Sun.COM 161710616SSebastien.Roy@Sun.COM /* One cannot modify a tunnel that belongs to another zone. */ 161810616SSebastien.Roy@Sun.COM if (iptun->iptun_zoneid != crgetzoneid(credp)) { 161910616SSebastien.Roy@Sun.COM err = EACCES; 162010616SSebastien.Roy@Sun.COM goto done; 162110616SSebastien.Roy@Sun.COM } 162210616SSebastien.Roy@Sun.COM 162310616SSebastien.Roy@Sun.COM /* The tunnel type cannot be changed */ 162410616SSebastien.Roy@Sun.COM if (ik->iptun_kparam_flags & IPTUN_KPARAM_TYPE) { 162510616SSebastien.Roy@Sun.COM err = EINVAL; 162610616SSebastien.Roy@Sun.COM goto done; 162710616SSebastien.Roy@Sun.COM } 162810616SSebastien.Roy@Sun.COM 162910616SSebastien.Roy@Sun.COM if ((err = iptun_setparams(iptun, ik)) != 0) 163010616SSebastien.Roy@Sun.COM goto done; 163110616SSebastien.Roy@Sun.COM iptun_headergen(iptun, B_FALSE); 163210616SSebastien.Roy@Sun.COM 163310616SSebastien.Roy@Sun.COM /* 163410616SSebastien.Roy@Sun.COM * If any of the tunnel's addresses has been modified and the tunnel 163510616SSebastien.Roy@Sun.COM * has the necessary addresses assigned to it, we need to try to bind 163610616SSebastien.Roy@Sun.COM * with ip underneath us. If we're not ready to bind yet, then we'll 163710616SSebastien.Roy@Sun.COM * try again when the addresses are modified later. 163810616SSebastien.Roy@Sun.COM */ 163910616SSebastien.Roy@Sun.COM laddr_change = (ik->iptun_kparam_flags & IPTUN_KPARAM_LADDR); 164010616SSebastien.Roy@Sun.COM raddr_change = (ik->iptun_kparam_flags & IPTUN_KPARAM_RADDR); 164110616SSebastien.Roy@Sun.COM if (laddr_change || raddr_change) { 164210616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_BOUND) 164310616SSebastien.Roy@Sun.COM iptun_unbind(iptun); 164410616SSebastien.Roy@Sun.COM if (iptun_canbind(iptun) && (err = iptun_bind(iptun)) != 0) { 164510616SSebastien.Roy@Sun.COM if (laddr_change) 164610616SSebastien.Roy@Sun.COM iptun->iptun_flags &= ~IPTUN_LADDR; 164710616SSebastien.Roy@Sun.COM if (raddr_change) 164810616SSebastien.Roy@Sun.COM iptun->iptun_flags &= ~IPTUN_RADDR; 164910616SSebastien.Roy@Sun.COM goto done; 165010616SSebastien.Roy@Sun.COM } 165110616SSebastien.Roy@Sun.COM } 165210616SSebastien.Roy@Sun.COM 165310616SSebastien.Roy@Sun.COM if (laddr_change) 165410616SSebastien.Roy@Sun.COM iptun_task_dispatch(iptun, IPTUN_TASK_LADDR_UPDATE); 165510616SSebastien.Roy@Sun.COM if (raddr_change) 165610616SSebastien.Roy@Sun.COM iptun_task_dispatch(iptun, IPTUN_TASK_RADDR_UPDATE); 165710616SSebastien.Roy@Sun.COM 165810616SSebastien.Roy@Sun.COM done: 165910616SSebastien.Roy@Sun.COM iptun_exit(iptun); 166010616SSebastien.Roy@Sun.COM return (err); 166110616SSebastien.Roy@Sun.COM } 166210616SSebastien.Roy@Sun.COM 166310616SSebastien.Roy@Sun.COM /* Given an IP tunnel's datalink id, fill in its parameters. */ 166410616SSebastien.Roy@Sun.COM int 166510616SSebastien.Roy@Sun.COM iptun_info(iptun_kparams_t *ik, cred_t *credp) 166610616SSebastien.Roy@Sun.COM { 166710616SSebastien.Roy@Sun.COM iptun_t *iptun; 166810616SSebastien.Roy@Sun.COM int err; 166910616SSebastien.Roy@Sun.COM 167010616SSebastien.Roy@Sun.COM /* Is the tunnel link visible from the caller's zone? */ 167110616SSebastien.Roy@Sun.COM if (!dls_devnet_islinkvisible(ik->iptun_kparam_linkid, 167210616SSebastien.Roy@Sun.COM crgetzoneid(credp))) 167310616SSebastien.Roy@Sun.COM return (ENOENT); 167410616SSebastien.Roy@Sun.COM 167510616SSebastien.Roy@Sun.COM if ((err = iptun_enter_by_linkid(ik->iptun_kparam_linkid, &iptun)) != 0) 167610616SSebastien.Roy@Sun.COM return (err); 167710616SSebastien.Roy@Sun.COM 167810616SSebastien.Roy@Sun.COM bzero(ik, sizeof (iptun_kparams_t)); 167910616SSebastien.Roy@Sun.COM 168010616SSebastien.Roy@Sun.COM ik->iptun_kparam_linkid = iptun->iptun_linkid; 168110616SSebastien.Roy@Sun.COM ik->iptun_kparam_type = iptun->iptun_typeinfo->iti_type; 168210616SSebastien.Roy@Sun.COM ik->iptun_kparam_flags |= IPTUN_KPARAM_TYPE; 168310616SSebastien.Roy@Sun.COM 168410616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_LADDR) { 168510616SSebastien.Roy@Sun.COM iptun_getaddr(&iptun->iptun_laddr, &ik->iptun_kparam_laddr); 168610616SSebastien.Roy@Sun.COM ik->iptun_kparam_flags |= IPTUN_KPARAM_LADDR; 168710616SSebastien.Roy@Sun.COM } 168810616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_RADDR) { 168910616SSebastien.Roy@Sun.COM iptun_getaddr(&iptun->iptun_raddr, &ik->iptun_kparam_raddr); 169010616SSebastien.Roy@Sun.COM ik->iptun_kparam_flags |= IPTUN_KPARAM_RADDR; 169110616SSebastien.Roy@Sun.COM } 169210616SSebastien.Roy@Sun.COM 169310616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_IMPLICIT) 169410616SSebastien.Roy@Sun.COM ik->iptun_kparam_flags |= IPTUN_KPARAM_IMPLICIT; 169510616SSebastien.Roy@Sun.COM 169610616SSebastien.Roy@Sun.COM if (iptun->iptun_itp != NULL) { 169710616SSebastien.Roy@Sun.COM mutex_enter(&iptun->iptun_itp->itp_lock); 169810616SSebastien.Roy@Sun.COM if (iptun->iptun_itp->itp_flags & ITPF_P_ACTIVE) { 169910616SSebastien.Roy@Sun.COM ik->iptun_kparam_flags |= IPTUN_KPARAM_IPSECPOL; 170010616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_SIMPLE_POLICY) { 170110616SSebastien.Roy@Sun.COM ik->iptun_kparam_flags |= IPTUN_KPARAM_SECINFO; 170210616SSebastien.Roy@Sun.COM ik->iptun_kparam_secinfo = 170310616SSebastien.Roy@Sun.COM iptun->iptun_simple_policy; 170410616SSebastien.Roy@Sun.COM } 170510616SSebastien.Roy@Sun.COM } 170610616SSebastien.Roy@Sun.COM mutex_exit(&iptun->iptun_itp->itp_lock); 170710616SSebastien.Roy@Sun.COM } 170810616SSebastien.Roy@Sun.COM 170910616SSebastien.Roy@Sun.COM done: 171010616SSebastien.Roy@Sun.COM iptun_exit(iptun); 171110616SSebastien.Roy@Sun.COM return (err); 171210616SSebastien.Roy@Sun.COM } 171310616SSebastien.Roy@Sun.COM 171410616SSebastien.Roy@Sun.COM int 171510616SSebastien.Roy@Sun.COM iptun_set_6to4relay(netstack_t *ns, ipaddr_t relay_addr) 171610616SSebastien.Roy@Sun.COM { 171710616SSebastien.Roy@Sun.COM if (relay_addr == INADDR_BROADCAST || CLASSD(relay_addr)) 171810616SSebastien.Roy@Sun.COM return (EADDRNOTAVAIL); 171910616SSebastien.Roy@Sun.COM ns->netstack_iptun->iptuns_relay_rtr_addr = relay_addr; 172010616SSebastien.Roy@Sun.COM return (0); 172110616SSebastien.Roy@Sun.COM } 172210616SSebastien.Roy@Sun.COM 172310616SSebastien.Roy@Sun.COM void 172410616SSebastien.Roy@Sun.COM iptun_get_6to4relay(netstack_t *ns, ipaddr_t *relay_addr) 172510616SSebastien.Roy@Sun.COM { 172610616SSebastien.Roy@Sun.COM *relay_addr = ns->netstack_iptun->iptuns_relay_rtr_addr; 172710616SSebastien.Roy@Sun.COM } 172810616SSebastien.Roy@Sun.COM 172910616SSebastien.Roy@Sun.COM void 173010616SSebastien.Roy@Sun.COM iptun_set_policy(datalink_id_t linkid, ipsec_tun_pol_t *itp) 173110616SSebastien.Roy@Sun.COM { 173210616SSebastien.Roy@Sun.COM iptun_t *iptun; 173310616SSebastien.Roy@Sun.COM 173410616SSebastien.Roy@Sun.COM if (iptun_enter_by_linkid(linkid, &iptun) != 0) 173510616SSebastien.Roy@Sun.COM return; 173610616SSebastien.Roy@Sun.COM if (iptun->iptun_itp != itp) { 173710616SSebastien.Roy@Sun.COM ASSERT(iptun->iptun_itp == NULL); 173810616SSebastien.Roy@Sun.COM ITP_REFHOLD(itp); 173910616SSebastien.Roy@Sun.COM iptun->iptun_itp = itp; 174010616SSebastien.Roy@Sun.COM } 174111456Sdanmcd@sun.com /* 174211456Sdanmcd@sun.com * IPsec policy means IPsec overhead, which means lower MTU. 174311456Sdanmcd@sun.com * Refresh the MTU for this tunnel. 174411456Sdanmcd@sun.com */ 174511456Sdanmcd@sun.com (void) iptun_update_mtu(iptun, NULL, 0); 174610616SSebastien.Roy@Sun.COM iptun_exit(iptun); 174710616SSebastien.Roy@Sun.COM } 174810616SSebastien.Roy@Sun.COM 174910616SSebastien.Roy@Sun.COM /* 175010616SSebastien.Roy@Sun.COM * Obtain the path MTU to the tunnel destination. 175111042SErik.Nordmark@Sun.COM * Can return zero in some cases. 175210616SSebastien.Roy@Sun.COM */ 175310616SSebastien.Roy@Sun.COM static uint32_t 175411042SErik.Nordmark@Sun.COM iptun_get_dst_pmtu(iptun_t *iptun, ip_xmit_attr_t *ixa) 175510616SSebastien.Roy@Sun.COM { 175610616SSebastien.Roy@Sun.COM uint32_t pmtu = 0; 175711042SErik.Nordmark@Sun.COM conn_t *connp = iptun->iptun_connp; 175811042SErik.Nordmark@Sun.COM boolean_t need_rele = B_FALSE; 175910616SSebastien.Roy@Sun.COM 176010616SSebastien.Roy@Sun.COM /* 176111042SErik.Nordmark@Sun.COM * We only obtain the pmtu for tunnels that have a remote tunnel 176211042SErik.Nordmark@Sun.COM * address. 176310616SSebastien.Roy@Sun.COM */ 176410616SSebastien.Roy@Sun.COM if (!(iptun->iptun_flags & IPTUN_RADDR)) 176510616SSebastien.Roy@Sun.COM return (0); 176610616SSebastien.Roy@Sun.COM 176711042SErik.Nordmark@Sun.COM if (ixa == NULL) { 176811042SErik.Nordmark@Sun.COM ixa = conn_get_ixa(connp, B_FALSE); 176911042SErik.Nordmark@Sun.COM if (ixa == NULL) 177011042SErik.Nordmark@Sun.COM return (0); 177111042SErik.Nordmark@Sun.COM need_rele = B_TRUE; 177211042SErik.Nordmark@Sun.COM } 177311042SErik.Nordmark@Sun.COM /* 177411042SErik.Nordmark@Sun.COM * Guard against ICMP errors before we have sent, as well as against 177511042SErik.Nordmark@Sun.COM * and a thread which held conn_ixa. 177611042SErik.Nordmark@Sun.COM */ 177711042SErik.Nordmark@Sun.COM if (ixa->ixa_ire != NULL) { 177811042SErik.Nordmark@Sun.COM pmtu = ip_get_pmtu(ixa); 177911042SErik.Nordmark@Sun.COM 178011042SErik.Nordmark@Sun.COM /* 178111042SErik.Nordmark@Sun.COM * For both IPv4 and IPv6 we can have indication that the outer 178211042SErik.Nordmark@Sun.COM * header needs fragmentation. 178311042SErik.Nordmark@Sun.COM */ 178411042SErik.Nordmark@Sun.COM if (ixa->ixa_flags & IXAF_PMTU_TOO_SMALL) { 178511042SErik.Nordmark@Sun.COM /* Must allow fragmentation in ip_output */ 178611042SErik.Nordmark@Sun.COM ixa->ixa_flags &= ~IXAF_DONTFRAG; 178711042SErik.Nordmark@Sun.COM } else if (iptun->iptun_typeinfo->iti_type != IPTUN_TYPE_6TO4) { 178811042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_DONTFRAG; 178911042SErik.Nordmark@Sun.COM } else { 179011042SErik.Nordmark@Sun.COM /* ip_get_pmtu might have set this - we don't want it */ 179111042SErik.Nordmark@Sun.COM ixa->ixa_flags &= ~IXAF_PMTU_IPV4_DF; 179211042SErik.Nordmark@Sun.COM } 179311042SErik.Nordmark@Sun.COM } 179411042SErik.Nordmark@Sun.COM 179511042SErik.Nordmark@Sun.COM if (need_rele) 179611042SErik.Nordmark@Sun.COM ixa_refrele(ixa); 179711042SErik.Nordmark@Sun.COM return (pmtu); 179811042SErik.Nordmark@Sun.COM } 179911042SErik.Nordmark@Sun.COM 180011042SErik.Nordmark@Sun.COM /* 180111042SErik.Nordmark@Sun.COM * Update the ip_xmit_attr_t to capture the current lower path mtu as known 180211042SErik.Nordmark@Sun.COM * by ip. 180311042SErik.Nordmark@Sun.COM */ 180411042SErik.Nordmark@Sun.COM static void 180511042SErik.Nordmark@Sun.COM iptun_update_dst_pmtu(iptun_t *iptun, ip_xmit_attr_t *ixa) 180611042SErik.Nordmark@Sun.COM { 180711042SErik.Nordmark@Sun.COM uint32_t pmtu; 180811042SErik.Nordmark@Sun.COM conn_t *connp = iptun->iptun_connp; 180911042SErik.Nordmark@Sun.COM boolean_t need_rele = B_FALSE; 181011042SErik.Nordmark@Sun.COM 181111042SErik.Nordmark@Sun.COM /* IXAF_VERIFY_PMTU is not set if we don't have a fixed destination */ 181211042SErik.Nordmark@Sun.COM if (!(iptun->iptun_flags & IPTUN_RADDR)) 181311042SErik.Nordmark@Sun.COM return; 181411042SErik.Nordmark@Sun.COM 181511042SErik.Nordmark@Sun.COM if (ixa == NULL) { 181611042SErik.Nordmark@Sun.COM ixa = conn_get_ixa(connp, B_FALSE); 181711042SErik.Nordmark@Sun.COM if (ixa == NULL) 181811042SErik.Nordmark@Sun.COM return; 181911042SErik.Nordmark@Sun.COM need_rele = B_TRUE; 182011042SErik.Nordmark@Sun.COM } 182111042SErik.Nordmark@Sun.COM /* 182211042SErik.Nordmark@Sun.COM * Guard against ICMP errors before we have sent, as well as against 182311042SErik.Nordmark@Sun.COM * and a thread which held conn_ixa. 182411042SErik.Nordmark@Sun.COM */ 182511042SErik.Nordmark@Sun.COM if (ixa->ixa_ire != NULL) { 182611042SErik.Nordmark@Sun.COM pmtu = ip_get_pmtu(ixa); 182711042SErik.Nordmark@Sun.COM /* 182811042SErik.Nordmark@Sun.COM * Update ixa_fragsize and ixa_pmtu. 182911042SErik.Nordmark@Sun.COM */ 183011042SErik.Nordmark@Sun.COM ixa->ixa_fragsize = ixa->ixa_pmtu = pmtu; 183111042SErik.Nordmark@Sun.COM 183211042SErik.Nordmark@Sun.COM /* 183311042SErik.Nordmark@Sun.COM * For both IPv4 and IPv6 we can have indication that the outer 183411042SErik.Nordmark@Sun.COM * header needs fragmentation. 183511042SErik.Nordmark@Sun.COM */ 183611042SErik.Nordmark@Sun.COM if (ixa->ixa_flags & IXAF_PMTU_TOO_SMALL) { 183711042SErik.Nordmark@Sun.COM /* Must allow fragmentation in ip_output */ 183811042SErik.Nordmark@Sun.COM ixa->ixa_flags &= ~IXAF_DONTFRAG; 183911042SErik.Nordmark@Sun.COM } else if (iptun->iptun_typeinfo->iti_type != IPTUN_TYPE_6TO4) { 184011042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_DONTFRAG; 184111042SErik.Nordmark@Sun.COM } else { 184211042SErik.Nordmark@Sun.COM /* ip_get_pmtu might have set this - we don't want it */ 184311042SErik.Nordmark@Sun.COM ixa->ixa_flags &= ~IXAF_PMTU_IPV4_DF; 184411042SErik.Nordmark@Sun.COM } 184511042SErik.Nordmark@Sun.COM } 184611042SErik.Nordmark@Sun.COM 184711042SErik.Nordmark@Sun.COM if (need_rele) 184811042SErik.Nordmark@Sun.COM ixa_refrele(ixa); 184911042SErik.Nordmark@Sun.COM } 185011042SErik.Nordmark@Sun.COM 185111042SErik.Nordmark@Sun.COM /* 185211042SErik.Nordmark@Sun.COM * There is nothing that iptun can verify in addition to IP having 185311042SErik.Nordmark@Sun.COM * verified the IP addresses in the fanout. 185411042SErik.Nordmark@Sun.COM */ 185511042SErik.Nordmark@Sun.COM /* ARGSUSED */ 185611042SErik.Nordmark@Sun.COM static boolean_t 185711042SErik.Nordmark@Sun.COM iptun_verifyicmp(conn_t *connp, void *arg2, icmph_t *icmph, icmp6_t *icmp6, 185811042SErik.Nordmark@Sun.COM ip_recv_attr_t *ira) 185911042SErik.Nordmark@Sun.COM { 186011042SErik.Nordmark@Sun.COM return (B_TRUE); 186111042SErik.Nordmark@Sun.COM } 186211042SErik.Nordmark@Sun.COM 186311042SErik.Nordmark@Sun.COM /* 186411042SErik.Nordmark@Sun.COM * Notify function registered with ip_xmit_attr_t. 186511042SErik.Nordmark@Sun.COM */ 186611042SErik.Nordmark@Sun.COM static void 186711042SErik.Nordmark@Sun.COM iptun_notify(void *arg, ip_xmit_attr_t *ixa, ixa_notify_type_t ntype, 186811042SErik.Nordmark@Sun.COM ixa_notify_arg_t narg) 186911042SErik.Nordmark@Sun.COM { 187011042SErik.Nordmark@Sun.COM iptun_t *iptun = (iptun_t *)arg; 187111042SErik.Nordmark@Sun.COM 187211042SErik.Nordmark@Sun.COM switch (ntype) { 187311042SErik.Nordmark@Sun.COM case IXAN_PMTU: 187411042SErik.Nordmark@Sun.COM (void) iptun_update_mtu(iptun, ixa, narg); 187510616SSebastien.Roy@Sun.COM break; 187610616SSebastien.Roy@Sun.COM } 187710616SSebastien.Roy@Sun.COM } 187810616SSebastien.Roy@Sun.COM 187910616SSebastien.Roy@Sun.COM /* 188010616SSebastien.Roy@Sun.COM * Returns the max of old_ovhd and the overhead associated with pol. 188110616SSebastien.Roy@Sun.COM */ 188210616SSebastien.Roy@Sun.COM static uint32_t 188310616SSebastien.Roy@Sun.COM iptun_max_policy_overhead(ipsec_policy_t *pol, uint32_t old_ovhd) 188410616SSebastien.Roy@Sun.COM { 188510616SSebastien.Roy@Sun.COM uint32_t new_ovhd = old_ovhd; 188610616SSebastien.Roy@Sun.COM 188710616SSebastien.Roy@Sun.COM while (pol != NULL) { 188810616SSebastien.Roy@Sun.COM new_ovhd = max(new_ovhd, 188910616SSebastien.Roy@Sun.COM ipsec_act_ovhd(&pol->ipsp_act->ipa_act)); 189010616SSebastien.Roy@Sun.COM pol = pol->ipsp_hash.hash_next; 189110616SSebastien.Roy@Sun.COM } 189210616SSebastien.Roy@Sun.COM return (new_ovhd); 189310616SSebastien.Roy@Sun.COM } 189410616SSebastien.Roy@Sun.COM 189510616SSebastien.Roy@Sun.COM static uint32_t 189610616SSebastien.Roy@Sun.COM iptun_get_ipsec_overhead(iptun_t *iptun) 189710616SSebastien.Roy@Sun.COM { 189810616SSebastien.Roy@Sun.COM ipsec_policy_root_t *ipr; 189910616SSebastien.Roy@Sun.COM ipsec_policy_head_t *iph; 190010616SSebastien.Roy@Sun.COM ipsec_policy_t *pol; 190110616SSebastien.Roy@Sun.COM ipsec_selector_t sel; 190210616SSebastien.Roy@Sun.COM int i; 190310616SSebastien.Roy@Sun.COM uint32_t ipsec_ovhd = 0; 190410616SSebastien.Roy@Sun.COM ipsec_tun_pol_t *itp = iptun->iptun_itp; 190510616SSebastien.Roy@Sun.COM netstack_t *ns = iptun->iptun_ns; 190610616SSebastien.Roy@Sun.COM 190710616SSebastien.Roy@Sun.COM if (itp == NULL || !(itp->itp_flags & ITPF_P_ACTIVE)) { 190810616SSebastien.Roy@Sun.COM /* 190910616SSebastien.Roy@Sun.COM * Consult global policy, just in case. This will only work 191010616SSebastien.Roy@Sun.COM * if we have both source and destination addresses to work 191110616SSebastien.Roy@Sun.COM * with. 191210616SSebastien.Roy@Sun.COM */ 191310616SSebastien.Roy@Sun.COM if ((iptun->iptun_flags & (IPTUN_LADDR|IPTUN_RADDR)) != 191410616SSebastien.Roy@Sun.COM (IPTUN_LADDR|IPTUN_RADDR)) 191510616SSebastien.Roy@Sun.COM return (0); 191610616SSebastien.Roy@Sun.COM 191710616SSebastien.Roy@Sun.COM iph = ipsec_system_policy(ns); 191810616SSebastien.Roy@Sun.COM bzero(&sel, sizeof (sel)); 191910616SSebastien.Roy@Sun.COM sel.ips_isv4 = 192010616SSebastien.Roy@Sun.COM (iptun->iptun_typeinfo->iti_ipvers == IPV4_VERSION); 192110616SSebastien.Roy@Sun.COM switch (iptun->iptun_typeinfo->iti_ipvers) { 192210616SSebastien.Roy@Sun.COM case IPV4_VERSION: 192310616SSebastien.Roy@Sun.COM sel.ips_local_addr_v4 = iptun->iptun_laddr4; 192410616SSebastien.Roy@Sun.COM sel.ips_remote_addr_v4 = iptun->iptun_raddr4; 192510616SSebastien.Roy@Sun.COM break; 192610616SSebastien.Roy@Sun.COM case IPV6_VERSION: 192710616SSebastien.Roy@Sun.COM sel.ips_local_addr_v6 = iptun->iptun_laddr6; 192810616SSebastien.Roy@Sun.COM sel.ips_remote_addr_v6 = iptun->iptun_raddr6; 192910616SSebastien.Roy@Sun.COM break; 193010616SSebastien.Roy@Sun.COM } 193110616SSebastien.Roy@Sun.COM /* Check for both IPv4 and IPv6. */ 193210616SSebastien.Roy@Sun.COM sel.ips_protocol = IPPROTO_ENCAP; 193310616SSebastien.Roy@Sun.COM pol = ipsec_find_policy_head(NULL, iph, IPSEC_TYPE_OUTBOUND, 193411042SErik.Nordmark@Sun.COM &sel); 193510616SSebastien.Roy@Sun.COM if (pol != NULL) { 193610616SSebastien.Roy@Sun.COM ipsec_ovhd = ipsec_act_ovhd(&pol->ipsp_act->ipa_act); 193711042SErik.Nordmark@Sun.COM IPPOL_REFRELE(pol); 193810616SSebastien.Roy@Sun.COM } 193910616SSebastien.Roy@Sun.COM sel.ips_protocol = IPPROTO_IPV6; 194010616SSebastien.Roy@Sun.COM pol = ipsec_find_policy_head(NULL, iph, IPSEC_TYPE_OUTBOUND, 194111042SErik.Nordmark@Sun.COM &sel); 194210616SSebastien.Roy@Sun.COM if (pol != NULL) { 194310616SSebastien.Roy@Sun.COM ipsec_ovhd = max(ipsec_ovhd, 194410616SSebastien.Roy@Sun.COM ipsec_act_ovhd(&pol->ipsp_act->ipa_act)); 194511042SErik.Nordmark@Sun.COM IPPOL_REFRELE(pol); 194610616SSebastien.Roy@Sun.COM } 194710616SSebastien.Roy@Sun.COM IPPH_REFRELE(iph, ns); 194810616SSebastien.Roy@Sun.COM } else { 194910616SSebastien.Roy@Sun.COM /* 195010616SSebastien.Roy@Sun.COM * Look through all of the possible IPsec actions for the 195110616SSebastien.Roy@Sun.COM * tunnel, and find the largest potential IPsec overhead. 195210616SSebastien.Roy@Sun.COM */ 195310616SSebastien.Roy@Sun.COM iph = itp->itp_policy; 195410616SSebastien.Roy@Sun.COM rw_enter(&iph->iph_lock, RW_READER); 195510616SSebastien.Roy@Sun.COM ipr = &(iph->iph_root[IPSEC_TYPE_OUTBOUND]); 195610616SSebastien.Roy@Sun.COM ipsec_ovhd = iptun_max_policy_overhead( 195710616SSebastien.Roy@Sun.COM ipr->ipr_nonhash[IPSEC_AF_V4], 0); 195810616SSebastien.Roy@Sun.COM ipsec_ovhd = iptun_max_policy_overhead( 195910616SSebastien.Roy@Sun.COM ipr->ipr_nonhash[IPSEC_AF_V6], ipsec_ovhd); 196010616SSebastien.Roy@Sun.COM for (i = 0; i < ipr->ipr_nchains; i++) { 196110616SSebastien.Roy@Sun.COM ipsec_ovhd = iptun_max_policy_overhead( 196210616SSebastien.Roy@Sun.COM ipr->ipr_hash[i].hash_head, ipsec_ovhd); 196310616SSebastien.Roy@Sun.COM } 196410616SSebastien.Roy@Sun.COM rw_exit(&iph->iph_lock); 196510616SSebastien.Roy@Sun.COM } 196610616SSebastien.Roy@Sun.COM 196710616SSebastien.Roy@Sun.COM return (ipsec_ovhd); 196810616SSebastien.Roy@Sun.COM } 196910616SSebastien.Roy@Sun.COM 197010616SSebastien.Roy@Sun.COM /* 197111042SErik.Nordmark@Sun.COM * Calculate and return the maximum possible upper MTU for the given tunnel. 197211042SErik.Nordmark@Sun.COM * 197311042SErik.Nordmark@Sun.COM * If new_pmtu is set then we also need to update the lower path MTU information 197411042SErik.Nordmark@Sun.COM * in the ip_xmit_attr_t. That is needed since we set IXAF_VERIFY_PMTU so that 197511042SErik.Nordmark@Sun.COM * we are notified by conn_ip_output() when the path MTU increases. 197610616SSebastien.Roy@Sun.COM */ 197710616SSebastien.Roy@Sun.COM static uint32_t 197811042SErik.Nordmark@Sun.COM iptun_get_maxmtu(iptun_t *iptun, ip_xmit_attr_t *ixa, uint32_t new_pmtu) 197910616SSebastien.Roy@Sun.COM { 198010616SSebastien.Roy@Sun.COM size_t header_size, ipsec_overhead; 198110616SSebastien.Roy@Sun.COM uint32_t maxmtu, pmtu; 198210616SSebastien.Roy@Sun.COM 198310616SSebastien.Roy@Sun.COM /* 198410616SSebastien.Roy@Sun.COM * Start with the path-MTU to the remote address, which is either 198510616SSebastien.Roy@Sun.COM * provided as the new_pmtu argument, or obtained using 198610616SSebastien.Roy@Sun.COM * iptun_get_dst_pmtu(). 198710616SSebastien.Roy@Sun.COM */ 198810616SSebastien.Roy@Sun.COM if (new_pmtu != 0) { 198911042SErik.Nordmark@Sun.COM if (iptun->iptun_flags & IPTUN_RADDR) 199010616SSebastien.Roy@Sun.COM iptun->iptun_dpmtu = new_pmtu; 199110616SSebastien.Roy@Sun.COM pmtu = new_pmtu; 199210616SSebastien.Roy@Sun.COM } else if (iptun->iptun_flags & IPTUN_RADDR) { 199311042SErik.Nordmark@Sun.COM if ((pmtu = iptun_get_dst_pmtu(iptun, ixa)) == 0) { 199410616SSebastien.Roy@Sun.COM /* 199510616SSebastien.Roy@Sun.COM * We weren't able to obtain the path-MTU of the 199610616SSebastien.Roy@Sun.COM * destination. Use the previous value. 199710616SSebastien.Roy@Sun.COM */ 199810616SSebastien.Roy@Sun.COM pmtu = iptun->iptun_dpmtu; 199910616SSebastien.Roy@Sun.COM } else { 200010616SSebastien.Roy@Sun.COM iptun->iptun_dpmtu = pmtu; 200110616SSebastien.Roy@Sun.COM } 200210616SSebastien.Roy@Sun.COM } else { 200310616SSebastien.Roy@Sun.COM /* 200410616SSebastien.Roy@Sun.COM * We have no path-MTU information to go on, use the maximum 200510616SSebastien.Roy@Sun.COM * possible value. 200610616SSebastien.Roy@Sun.COM */ 200710616SSebastien.Roy@Sun.COM pmtu = iptun->iptun_typeinfo->iti_maxmtu; 200810616SSebastien.Roy@Sun.COM } 200910616SSebastien.Roy@Sun.COM 201010616SSebastien.Roy@Sun.COM /* 201110616SSebastien.Roy@Sun.COM * Now calculate tunneling overhead and subtract that from the 201210616SSebastien.Roy@Sun.COM * path-MTU information obtained above. 201310616SSebastien.Roy@Sun.COM */ 201410616SSebastien.Roy@Sun.COM if (iptun->iptun_header_size != 0) { 201510616SSebastien.Roy@Sun.COM header_size = iptun->iptun_header_size; 201610616SSebastien.Roy@Sun.COM } else { 201710616SSebastien.Roy@Sun.COM switch (iptun->iptun_typeinfo->iti_ipvers) { 201810616SSebastien.Roy@Sun.COM case IPV4_VERSION: 201910616SSebastien.Roy@Sun.COM header_size = sizeof (ipha_t); 202010934Ssommerfeld@sun.com if (is_system_labeled()) 202110934Ssommerfeld@sun.com header_size += IP_MAX_OPT_LENGTH; 202210616SSebastien.Roy@Sun.COM break; 202310616SSebastien.Roy@Sun.COM case IPV6_VERSION: 202410616SSebastien.Roy@Sun.COM header_size = sizeof (iptun_ipv6hdrs_t); 202510616SSebastien.Roy@Sun.COM break; 202610616SSebastien.Roy@Sun.COM } 202710616SSebastien.Roy@Sun.COM } 202810616SSebastien.Roy@Sun.COM 202910616SSebastien.Roy@Sun.COM ipsec_overhead = iptun_get_ipsec_overhead(iptun); 203010616SSebastien.Roy@Sun.COM 203110616SSebastien.Roy@Sun.COM maxmtu = pmtu - (header_size + ipsec_overhead); 203210616SSebastien.Roy@Sun.COM return (max(maxmtu, iptun->iptun_typeinfo->iti_minmtu)); 203310616SSebastien.Roy@Sun.COM } 203410616SSebastien.Roy@Sun.COM 203510616SSebastien.Roy@Sun.COM /* 203611042SErik.Nordmark@Sun.COM * Re-calculate the tunnel's MTU as seen from above and notify the MAC layer 203711042SErik.Nordmark@Sun.COM * of any change in MTU. The new_pmtu argument is the new lower path MTU to 203811042SErik.Nordmark@Sun.COM * the tunnel destination to be used in the tunnel MTU calculation. Passing 203911042SErik.Nordmark@Sun.COM * in 0 for new_pmtu causes the lower path MTU to be dynamically updated using 204011042SErik.Nordmark@Sun.COM * ip_get_pmtu(). 204110616SSebastien.Roy@Sun.COM * 204210616SSebastien.Roy@Sun.COM * If the calculated tunnel MTU is different than its previous value, then we 204310616SSebastien.Roy@Sun.COM * notify the MAC layer above us of this change using mac_maxsdu_update(). 204410616SSebastien.Roy@Sun.COM */ 204510616SSebastien.Roy@Sun.COM static uint32_t 204611042SErik.Nordmark@Sun.COM iptun_update_mtu(iptun_t *iptun, ip_xmit_attr_t *ixa, uint32_t new_pmtu) 204710616SSebastien.Roy@Sun.COM { 204810616SSebastien.Roy@Sun.COM uint32_t newmtu; 204910616SSebastien.Roy@Sun.COM 205011042SErik.Nordmark@Sun.COM /* We always update the ixa since we might have set IXAF_VERIFY_PMTU */ 205111042SErik.Nordmark@Sun.COM iptun_update_dst_pmtu(iptun, ixa); 205211042SErik.Nordmark@Sun.COM 205310616SSebastien.Roy@Sun.COM /* 205410616SSebastien.Roy@Sun.COM * We return the current MTU without updating it if it was pegged to a 205510616SSebastien.Roy@Sun.COM * static value using the MAC_PROP_MTU link property. 205610616SSebastien.Roy@Sun.COM */ 205710616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_FIXED_MTU) 205810616SSebastien.Roy@Sun.COM return (iptun->iptun_mtu); 205910616SSebastien.Roy@Sun.COM 206010616SSebastien.Roy@Sun.COM /* If the MTU isn't fixed, then use the maximum possible value. */ 206111042SErik.Nordmark@Sun.COM newmtu = iptun_get_maxmtu(iptun, ixa, new_pmtu); 206210616SSebastien.Roy@Sun.COM /* 206310616SSebastien.Roy@Sun.COM * We only dynamically adjust the tunnel MTU for tunnels with 206410616SSebastien.Roy@Sun.COM * destinations because dynamic MTU calculations are based on the 206510616SSebastien.Roy@Sun.COM * destination path-MTU. 206610616SSebastien.Roy@Sun.COM */ 206710616SSebastien.Roy@Sun.COM if ((iptun->iptun_flags & IPTUN_RADDR) && newmtu != iptun->iptun_mtu) { 206810616SSebastien.Roy@Sun.COM iptun->iptun_mtu = newmtu; 206910616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_MAC_REGISTERED) 207010616SSebastien.Roy@Sun.COM iptun_task_dispatch(iptun, IPTUN_TASK_MTU_UPDATE); 207110616SSebastien.Roy@Sun.COM } 207210616SSebastien.Roy@Sun.COM 207310616SSebastien.Roy@Sun.COM return (newmtu); 207410616SSebastien.Roy@Sun.COM } 207510616SSebastien.Roy@Sun.COM 207610616SSebastien.Roy@Sun.COM /* 207710616SSebastien.Roy@Sun.COM * Frees a packet or packet chain and bumps stat for each freed packet. 207810616SSebastien.Roy@Sun.COM */ 207910616SSebastien.Roy@Sun.COM static void 208010616SSebastien.Roy@Sun.COM iptun_drop_pkt(mblk_t *mp, uint64_t *stat) 208110616SSebastien.Roy@Sun.COM { 208210616SSebastien.Roy@Sun.COM mblk_t *pktmp; 208310616SSebastien.Roy@Sun.COM 208410616SSebastien.Roy@Sun.COM for (pktmp = mp; pktmp != NULL; pktmp = mp) { 208510616SSebastien.Roy@Sun.COM mp = mp->b_next; 208610616SSebastien.Roy@Sun.COM pktmp->b_next = NULL; 208710616SSebastien.Roy@Sun.COM if (stat != NULL) 208810616SSebastien.Roy@Sun.COM atomic_inc_64(stat); 208910616SSebastien.Roy@Sun.COM freemsg(pktmp); 209010616SSebastien.Roy@Sun.COM } 209110616SSebastien.Roy@Sun.COM } 209210616SSebastien.Roy@Sun.COM 209310616SSebastien.Roy@Sun.COM /* 209410616SSebastien.Roy@Sun.COM * Allocate and return a new mblk to hold an IP and ICMP header, and chain the 209510616SSebastien.Roy@Sun.COM * original packet to its b_cont. Returns NULL on failure. 209610616SSebastien.Roy@Sun.COM */ 209710616SSebastien.Roy@Sun.COM static mblk_t * 209810616SSebastien.Roy@Sun.COM iptun_build_icmperr(size_t hdrs_size, mblk_t *orig_pkt) 209910616SSebastien.Roy@Sun.COM { 210010616SSebastien.Roy@Sun.COM mblk_t *icmperr_mp; 210110616SSebastien.Roy@Sun.COM 210211042SErik.Nordmark@Sun.COM if ((icmperr_mp = allocb(hdrs_size, BPRI_MED)) != NULL) { 210310616SSebastien.Roy@Sun.COM icmperr_mp->b_wptr += hdrs_size; 210410616SSebastien.Roy@Sun.COM /* tack on the offending packet */ 210510616SSebastien.Roy@Sun.COM icmperr_mp->b_cont = orig_pkt; 210610616SSebastien.Roy@Sun.COM } 210710616SSebastien.Roy@Sun.COM return (icmperr_mp); 210810616SSebastien.Roy@Sun.COM } 210910616SSebastien.Roy@Sun.COM 211010616SSebastien.Roy@Sun.COM /* 211110616SSebastien.Roy@Sun.COM * Transmit an ICMP error. mp->b_rptr points at the packet to be included in 211210616SSebastien.Roy@Sun.COM * the ICMP error. 211310616SSebastien.Roy@Sun.COM */ 211410616SSebastien.Roy@Sun.COM static void 211511042SErik.Nordmark@Sun.COM iptun_sendicmp_v4(iptun_t *iptun, icmph_t *icmp, ipha_t *orig_ipha, mblk_t *mp, 211611042SErik.Nordmark@Sun.COM ts_label_t *tsl) 211710616SSebastien.Roy@Sun.COM { 211810616SSebastien.Roy@Sun.COM size_t orig_pktsize, hdrs_size; 211910616SSebastien.Roy@Sun.COM mblk_t *icmperr_mp; 212010616SSebastien.Roy@Sun.COM ipha_t *new_ipha; 212110616SSebastien.Roy@Sun.COM icmph_t *new_icmp; 212211042SErik.Nordmark@Sun.COM ip_xmit_attr_t ixas; 212311042SErik.Nordmark@Sun.COM conn_t *connp = iptun->iptun_connp; 212410616SSebastien.Roy@Sun.COM 212510616SSebastien.Roy@Sun.COM orig_pktsize = msgdsize(mp); 212610616SSebastien.Roy@Sun.COM hdrs_size = sizeof (ipha_t) + sizeof (icmph_t); 212710616SSebastien.Roy@Sun.COM if ((icmperr_mp = iptun_build_icmperr(hdrs_size, mp)) == NULL) { 212810616SSebastien.Roy@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_noxmtbuf); 212910616SSebastien.Roy@Sun.COM return; 213010616SSebastien.Roy@Sun.COM } 213110616SSebastien.Roy@Sun.COM 213210616SSebastien.Roy@Sun.COM new_ipha = (ipha_t *)icmperr_mp->b_rptr; 213310616SSebastien.Roy@Sun.COM new_icmp = (icmph_t *)(new_ipha + 1); 213410616SSebastien.Roy@Sun.COM 213510616SSebastien.Roy@Sun.COM new_ipha->ipha_version_and_hdr_length = IP_SIMPLE_HDR_VERSION; 213610616SSebastien.Roy@Sun.COM new_ipha->ipha_type_of_service = 0; 213710616SSebastien.Roy@Sun.COM new_ipha->ipha_ident = 0; 213810616SSebastien.Roy@Sun.COM new_ipha->ipha_fragment_offset_and_flags = 0; 213910616SSebastien.Roy@Sun.COM new_ipha->ipha_ttl = orig_ipha->ipha_ttl; 214010616SSebastien.Roy@Sun.COM new_ipha->ipha_protocol = IPPROTO_ICMP; 214110616SSebastien.Roy@Sun.COM new_ipha->ipha_src = orig_ipha->ipha_dst; 214210616SSebastien.Roy@Sun.COM new_ipha->ipha_dst = orig_ipha->ipha_src; 214310616SSebastien.Roy@Sun.COM new_ipha->ipha_hdr_checksum = 0; /* will be computed by ip */ 214410616SSebastien.Roy@Sun.COM new_ipha->ipha_length = htons(hdrs_size + orig_pktsize); 214510616SSebastien.Roy@Sun.COM 214610616SSebastien.Roy@Sun.COM *new_icmp = *icmp; 214710616SSebastien.Roy@Sun.COM new_icmp->icmph_checksum = 0; 214810616SSebastien.Roy@Sun.COM new_icmp->icmph_checksum = IP_CSUM(icmperr_mp, sizeof (ipha_t), 0); 214910616SSebastien.Roy@Sun.COM 215011042SErik.Nordmark@Sun.COM bzero(&ixas, sizeof (ixas)); 215111042SErik.Nordmark@Sun.COM ixas.ixa_flags = IXAF_BASIC_SIMPLE_V4; 215211042SErik.Nordmark@Sun.COM if (new_ipha->ipha_src == INADDR_ANY) 215311042SErik.Nordmark@Sun.COM ixas.ixa_flags |= IXAF_SET_SOURCE; 215411042SErik.Nordmark@Sun.COM 215511042SErik.Nordmark@Sun.COM ixas.ixa_zoneid = IPCL_ZONEID(connp); 215611042SErik.Nordmark@Sun.COM ixas.ixa_ipst = connp->conn_netstack->netstack_ip; 215711042SErik.Nordmark@Sun.COM ixas.ixa_cred = connp->conn_cred; 215811042SErik.Nordmark@Sun.COM ixas.ixa_cpid = NOPID; 215911042SErik.Nordmark@Sun.COM if (is_system_labeled()) 216011042SErik.Nordmark@Sun.COM ixas.ixa_tsl = tsl; 216111042SErik.Nordmark@Sun.COM 216211042SErik.Nordmark@Sun.COM ixas.ixa_ifindex = 0; 216311042SErik.Nordmark@Sun.COM ixas.ixa_multicast_ttl = IP_DEFAULT_MULTICAST_TTL; 216411042SErik.Nordmark@Sun.COM 216511042SErik.Nordmark@Sun.COM (void) ip_output_simple(icmperr_mp, &ixas); 216611042SErik.Nordmark@Sun.COM ixa_cleanup(&ixas); 216710616SSebastien.Roy@Sun.COM } 216810616SSebastien.Roy@Sun.COM 216910616SSebastien.Roy@Sun.COM static void 217011042SErik.Nordmark@Sun.COM iptun_sendicmp_v6(iptun_t *iptun, icmp6_t *icmp6, ip6_t *orig_ip6h, mblk_t *mp, 217111042SErik.Nordmark@Sun.COM ts_label_t *tsl) 217210616SSebastien.Roy@Sun.COM { 217310616SSebastien.Roy@Sun.COM size_t orig_pktsize, hdrs_size; 217410616SSebastien.Roy@Sun.COM mblk_t *icmp6err_mp; 217510616SSebastien.Roy@Sun.COM ip6_t *new_ip6h; 217610616SSebastien.Roy@Sun.COM icmp6_t *new_icmp6; 217711042SErik.Nordmark@Sun.COM ip_xmit_attr_t ixas; 217811042SErik.Nordmark@Sun.COM conn_t *connp = iptun->iptun_connp; 217910616SSebastien.Roy@Sun.COM 218010616SSebastien.Roy@Sun.COM orig_pktsize = msgdsize(mp); 218110616SSebastien.Roy@Sun.COM hdrs_size = sizeof (ip6_t) + sizeof (icmp6_t); 218210616SSebastien.Roy@Sun.COM if ((icmp6err_mp = iptun_build_icmperr(hdrs_size, mp)) == NULL) { 218310616SSebastien.Roy@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_noxmtbuf); 218410616SSebastien.Roy@Sun.COM return; 218510616SSebastien.Roy@Sun.COM } 218610616SSebastien.Roy@Sun.COM 218710616SSebastien.Roy@Sun.COM new_ip6h = (ip6_t *)icmp6err_mp->b_rptr; 218810616SSebastien.Roy@Sun.COM new_icmp6 = (icmp6_t *)(new_ip6h + 1); 218910616SSebastien.Roy@Sun.COM 219010616SSebastien.Roy@Sun.COM new_ip6h->ip6_vcf = orig_ip6h->ip6_vcf; 219110616SSebastien.Roy@Sun.COM new_ip6h->ip6_plen = htons(sizeof (icmp6_t) + orig_pktsize); 219210616SSebastien.Roy@Sun.COM new_ip6h->ip6_hops = orig_ip6h->ip6_hops; 219310616SSebastien.Roy@Sun.COM new_ip6h->ip6_nxt = IPPROTO_ICMPV6; 219410616SSebastien.Roy@Sun.COM new_ip6h->ip6_src = orig_ip6h->ip6_dst; 219510616SSebastien.Roy@Sun.COM new_ip6h->ip6_dst = orig_ip6h->ip6_src; 219610616SSebastien.Roy@Sun.COM 219710616SSebastien.Roy@Sun.COM *new_icmp6 = *icmp6; 219811042SErik.Nordmark@Sun.COM /* The checksum is calculated in ip_output_simple and friends. */ 219910616SSebastien.Roy@Sun.COM new_icmp6->icmp6_cksum = new_ip6h->ip6_plen; 220010616SSebastien.Roy@Sun.COM 220111042SErik.Nordmark@Sun.COM bzero(&ixas, sizeof (ixas)); 220211042SErik.Nordmark@Sun.COM ixas.ixa_flags = IXAF_BASIC_SIMPLE_V6; 220311042SErik.Nordmark@Sun.COM if (IN6_IS_ADDR_UNSPECIFIED(&new_ip6h->ip6_src)) 220411042SErik.Nordmark@Sun.COM ixas.ixa_flags |= IXAF_SET_SOURCE; 220511042SErik.Nordmark@Sun.COM 220611042SErik.Nordmark@Sun.COM ixas.ixa_zoneid = IPCL_ZONEID(connp); 220711042SErik.Nordmark@Sun.COM ixas.ixa_ipst = connp->conn_netstack->netstack_ip; 220811042SErik.Nordmark@Sun.COM ixas.ixa_cred = connp->conn_cred; 220911042SErik.Nordmark@Sun.COM ixas.ixa_cpid = NOPID; 221011042SErik.Nordmark@Sun.COM if (is_system_labeled()) 221111042SErik.Nordmark@Sun.COM ixas.ixa_tsl = tsl; 221211042SErik.Nordmark@Sun.COM 221311042SErik.Nordmark@Sun.COM ixas.ixa_ifindex = 0; 221411042SErik.Nordmark@Sun.COM ixas.ixa_multicast_ttl = IP_DEFAULT_MULTICAST_TTL; 221511042SErik.Nordmark@Sun.COM 221611042SErik.Nordmark@Sun.COM (void) ip_output_simple(icmp6err_mp, &ixas); 221711042SErik.Nordmark@Sun.COM ixa_cleanup(&ixas); 221810616SSebastien.Roy@Sun.COM } 221910616SSebastien.Roy@Sun.COM 222010616SSebastien.Roy@Sun.COM static void 222110616SSebastien.Roy@Sun.COM iptun_icmp_error_v4(iptun_t *iptun, ipha_t *orig_ipha, mblk_t *mp, 222211042SErik.Nordmark@Sun.COM uint8_t type, uint8_t code, ts_label_t *tsl) 222310616SSebastien.Roy@Sun.COM { 222410616SSebastien.Roy@Sun.COM icmph_t icmp; 222510616SSebastien.Roy@Sun.COM 222610616SSebastien.Roy@Sun.COM bzero(&icmp, sizeof (icmp)); 222710616SSebastien.Roy@Sun.COM icmp.icmph_type = type; 222810616SSebastien.Roy@Sun.COM icmp.icmph_code = code; 222910616SSebastien.Roy@Sun.COM 223011042SErik.Nordmark@Sun.COM iptun_sendicmp_v4(iptun, &icmp, orig_ipha, mp, tsl); 223110616SSebastien.Roy@Sun.COM } 223210616SSebastien.Roy@Sun.COM 223310616SSebastien.Roy@Sun.COM static void 223410616SSebastien.Roy@Sun.COM iptun_icmp_fragneeded_v4(iptun_t *iptun, uint32_t newmtu, ipha_t *orig_ipha, 223511042SErik.Nordmark@Sun.COM mblk_t *mp, ts_label_t *tsl) 223610616SSebastien.Roy@Sun.COM { 223710616SSebastien.Roy@Sun.COM icmph_t icmp; 223810616SSebastien.Roy@Sun.COM 223910616SSebastien.Roy@Sun.COM icmp.icmph_type = ICMP_DEST_UNREACHABLE; 224010616SSebastien.Roy@Sun.COM icmp.icmph_code = ICMP_FRAGMENTATION_NEEDED; 224110616SSebastien.Roy@Sun.COM icmp.icmph_du_zero = 0; 224210616SSebastien.Roy@Sun.COM icmp.icmph_du_mtu = htons(newmtu); 224310616SSebastien.Roy@Sun.COM 224411042SErik.Nordmark@Sun.COM iptun_sendicmp_v4(iptun, &icmp, orig_ipha, mp, tsl); 224510616SSebastien.Roy@Sun.COM } 224610616SSebastien.Roy@Sun.COM 224710616SSebastien.Roy@Sun.COM static void 224810616SSebastien.Roy@Sun.COM iptun_icmp_error_v6(iptun_t *iptun, ip6_t *orig_ip6h, mblk_t *mp, 224911042SErik.Nordmark@Sun.COM uint8_t type, uint8_t code, uint32_t offset, ts_label_t *tsl) 225010616SSebastien.Roy@Sun.COM { 225110616SSebastien.Roy@Sun.COM icmp6_t icmp6; 225210616SSebastien.Roy@Sun.COM 225310616SSebastien.Roy@Sun.COM bzero(&icmp6, sizeof (icmp6)); 225410616SSebastien.Roy@Sun.COM icmp6.icmp6_type = type; 225510616SSebastien.Roy@Sun.COM icmp6.icmp6_code = code; 225610616SSebastien.Roy@Sun.COM if (type == ICMP6_PARAM_PROB) 225710616SSebastien.Roy@Sun.COM icmp6.icmp6_pptr = htonl(offset); 225810616SSebastien.Roy@Sun.COM 225911042SErik.Nordmark@Sun.COM iptun_sendicmp_v6(iptun, &icmp6, orig_ip6h, mp, tsl); 226010616SSebastien.Roy@Sun.COM } 226110616SSebastien.Roy@Sun.COM 226210616SSebastien.Roy@Sun.COM static void 226310616SSebastien.Roy@Sun.COM iptun_icmp_toobig_v6(iptun_t *iptun, uint32_t newmtu, ip6_t *orig_ip6h, 226411042SErik.Nordmark@Sun.COM mblk_t *mp, ts_label_t *tsl) 226510616SSebastien.Roy@Sun.COM { 226610616SSebastien.Roy@Sun.COM icmp6_t icmp6; 226710616SSebastien.Roy@Sun.COM 226810616SSebastien.Roy@Sun.COM icmp6.icmp6_type = ICMP6_PACKET_TOO_BIG; 226910616SSebastien.Roy@Sun.COM icmp6.icmp6_code = 0; 227010616SSebastien.Roy@Sun.COM icmp6.icmp6_mtu = htonl(newmtu); 227110616SSebastien.Roy@Sun.COM 227211042SErik.Nordmark@Sun.COM iptun_sendicmp_v6(iptun, &icmp6, orig_ip6h, mp, tsl); 227310616SSebastien.Roy@Sun.COM } 227410616SSebastien.Roy@Sun.COM 227510616SSebastien.Roy@Sun.COM /* 227610616SSebastien.Roy@Sun.COM * Determines if the packet pointed to by ipha or ip6h is an ICMP error. The 227710616SSebastien.Roy@Sun.COM * mp argument is only used to do bounds checking. 227810616SSebastien.Roy@Sun.COM */ 227910616SSebastien.Roy@Sun.COM static boolean_t 228010616SSebastien.Roy@Sun.COM is_icmp_error(mblk_t *mp, ipha_t *ipha, ip6_t *ip6h) 228110616SSebastien.Roy@Sun.COM { 228210616SSebastien.Roy@Sun.COM uint16_t hlen; 228310616SSebastien.Roy@Sun.COM 228410616SSebastien.Roy@Sun.COM if (ipha != NULL) { 228510616SSebastien.Roy@Sun.COM icmph_t *icmph; 228610616SSebastien.Roy@Sun.COM 228710616SSebastien.Roy@Sun.COM ASSERT(ip6h == NULL); 228810616SSebastien.Roy@Sun.COM if (ipha->ipha_protocol != IPPROTO_ICMP) 228910616SSebastien.Roy@Sun.COM return (B_FALSE); 229010616SSebastien.Roy@Sun.COM 229110616SSebastien.Roy@Sun.COM hlen = IPH_HDR_LENGTH(ipha); 229210616SSebastien.Roy@Sun.COM icmph = (icmph_t *)((uint8_t *)ipha + hlen); 229310616SSebastien.Roy@Sun.COM return (ICMP_IS_ERROR(icmph->icmph_type) || 229410616SSebastien.Roy@Sun.COM icmph->icmph_type == ICMP_REDIRECT); 229510616SSebastien.Roy@Sun.COM } else { 229610616SSebastien.Roy@Sun.COM icmp6_t *icmp6; 229710616SSebastien.Roy@Sun.COM uint8_t *nexthdrp; 229810616SSebastien.Roy@Sun.COM 229910616SSebastien.Roy@Sun.COM ASSERT(ip6h != NULL); 230010616SSebastien.Roy@Sun.COM if (!ip_hdr_length_nexthdr_v6(mp, ip6h, &hlen, &nexthdrp) || 230110616SSebastien.Roy@Sun.COM *nexthdrp != IPPROTO_ICMPV6) { 230210616SSebastien.Roy@Sun.COM return (B_FALSE); 230310616SSebastien.Roy@Sun.COM } 230410616SSebastien.Roy@Sun.COM 230510616SSebastien.Roy@Sun.COM icmp6 = (icmp6_t *)((uint8_t *)ip6h + hlen); 230610616SSebastien.Roy@Sun.COM return (ICMP6_IS_ERROR(icmp6->icmp6_type) || 230710616SSebastien.Roy@Sun.COM icmp6->icmp6_type == ND_REDIRECT); 230810616SSebastien.Roy@Sun.COM } 230910616SSebastien.Roy@Sun.COM } 231010616SSebastien.Roy@Sun.COM 231110616SSebastien.Roy@Sun.COM /* 231210616SSebastien.Roy@Sun.COM * Find inner and outer IP headers from a tunneled packet as setup for calls 231310616SSebastien.Roy@Sun.COM * into ipsec_tun_{in,out}bound(). 231411042SErik.Nordmark@Sun.COM * Note that we need to allow the outer header to be in a separate mblk from 231511042SErik.Nordmark@Sun.COM * the inner header. 231611042SErik.Nordmark@Sun.COM * If the caller knows the outer_hlen, the caller passes it in. Otherwise zero. 231710616SSebastien.Roy@Sun.COM */ 231810616SSebastien.Roy@Sun.COM static size_t 231911042SErik.Nordmark@Sun.COM iptun_find_headers(mblk_t *mp, size_t outer_hlen, ipha_t **outer4, 232011042SErik.Nordmark@Sun.COM ipha_t **inner4, ip6_t **outer6, ip6_t **inner6) 232110616SSebastien.Roy@Sun.COM { 232210616SSebastien.Roy@Sun.COM ipha_t *ipha; 232310616SSebastien.Roy@Sun.COM size_t first_mblkl = MBLKL(mp); 232410616SSebastien.Roy@Sun.COM mblk_t *inner_mp; 232510616SSebastien.Roy@Sun.COM 232610616SSebastien.Roy@Sun.COM /* 232710616SSebastien.Roy@Sun.COM * Don't bother handling packets that don't have a full IP header in 232810616SSebastien.Roy@Sun.COM * the fist mblk. For the input path, the ip module ensures that this 232910616SSebastien.Roy@Sun.COM * won't happen, and on the output path, the IP tunneling MAC-type 233010616SSebastien.Roy@Sun.COM * plugins ensure that this also won't happen. 233110616SSebastien.Roy@Sun.COM */ 233210616SSebastien.Roy@Sun.COM if (first_mblkl < sizeof (ipha_t)) 233310616SSebastien.Roy@Sun.COM return (0); 233410616SSebastien.Roy@Sun.COM ipha = (ipha_t *)(mp->b_rptr); 233510616SSebastien.Roy@Sun.COM switch (IPH_HDR_VERSION(ipha)) { 233610616SSebastien.Roy@Sun.COM case IPV4_VERSION: 233710616SSebastien.Roy@Sun.COM *outer4 = ipha; 233810616SSebastien.Roy@Sun.COM *outer6 = NULL; 233911042SErik.Nordmark@Sun.COM if (outer_hlen == 0) 234011042SErik.Nordmark@Sun.COM outer_hlen = IPH_HDR_LENGTH(ipha); 234110616SSebastien.Roy@Sun.COM break; 234210616SSebastien.Roy@Sun.COM case IPV6_VERSION: 234310616SSebastien.Roy@Sun.COM *outer4 = NULL; 234410616SSebastien.Roy@Sun.COM *outer6 = (ip6_t *)ipha; 234511042SErik.Nordmark@Sun.COM if (outer_hlen == 0) 234611042SErik.Nordmark@Sun.COM outer_hlen = ip_hdr_length_v6(mp, (ip6_t *)ipha); 234710616SSebastien.Roy@Sun.COM break; 234810616SSebastien.Roy@Sun.COM default: 234910616SSebastien.Roy@Sun.COM return (0); 235010616SSebastien.Roy@Sun.COM } 235110616SSebastien.Roy@Sun.COM 235210616SSebastien.Roy@Sun.COM if (first_mblkl < outer_hlen || 235310616SSebastien.Roy@Sun.COM (first_mblkl == outer_hlen && mp->b_cont == NULL)) 235410616SSebastien.Roy@Sun.COM return (0); 235510616SSebastien.Roy@Sun.COM 235610616SSebastien.Roy@Sun.COM /* 235710616SSebastien.Roy@Sun.COM * We don't bother doing a pullup here since the outer header will 235810616SSebastien.Roy@Sun.COM * just get stripped off soon on input anyway. We just want to ensure 235910616SSebastien.Roy@Sun.COM * that the inner* pointer points to a full header. 236010616SSebastien.Roy@Sun.COM */ 236110616SSebastien.Roy@Sun.COM if (first_mblkl == outer_hlen) { 236210616SSebastien.Roy@Sun.COM inner_mp = mp->b_cont; 236310616SSebastien.Roy@Sun.COM ipha = (ipha_t *)inner_mp->b_rptr; 236410616SSebastien.Roy@Sun.COM } else { 236510616SSebastien.Roy@Sun.COM inner_mp = mp; 236610616SSebastien.Roy@Sun.COM ipha = (ipha_t *)(mp->b_rptr + outer_hlen); 236710616SSebastien.Roy@Sun.COM } 236810616SSebastien.Roy@Sun.COM switch (IPH_HDR_VERSION(ipha)) { 236910616SSebastien.Roy@Sun.COM case IPV4_VERSION: 237010616SSebastien.Roy@Sun.COM if (inner_mp->b_wptr - (uint8_t *)ipha < sizeof (ipha_t)) 237110616SSebastien.Roy@Sun.COM return (0); 237210616SSebastien.Roy@Sun.COM *inner4 = ipha; 237310616SSebastien.Roy@Sun.COM *inner6 = NULL; 237410616SSebastien.Roy@Sun.COM break; 237510616SSebastien.Roy@Sun.COM case IPV6_VERSION: 237610616SSebastien.Roy@Sun.COM if (inner_mp->b_wptr - (uint8_t *)ipha < sizeof (ip6_t)) 237710616SSebastien.Roy@Sun.COM return (0); 237810616SSebastien.Roy@Sun.COM *inner4 = NULL; 237910616SSebastien.Roy@Sun.COM *inner6 = (ip6_t *)ipha; 238010616SSebastien.Roy@Sun.COM break; 238110616SSebastien.Roy@Sun.COM default: 238210616SSebastien.Roy@Sun.COM return (0); 238310616SSebastien.Roy@Sun.COM } 238410616SSebastien.Roy@Sun.COM 238510616SSebastien.Roy@Sun.COM return (outer_hlen); 238610616SSebastien.Roy@Sun.COM } 238710616SSebastien.Roy@Sun.COM 238810616SSebastien.Roy@Sun.COM /* 238910616SSebastien.Roy@Sun.COM * Received ICMP error in response to an X over IPv4 packet that we 239010616SSebastien.Roy@Sun.COM * transmitted. 239110616SSebastien.Roy@Sun.COM * 239210616SSebastien.Roy@Sun.COM * NOTE: "outer" refers to what's inside the ICMP payload. We will get one of 239310616SSebastien.Roy@Sun.COM * the following: 239410616SSebastien.Roy@Sun.COM * 239510616SSebastien.Roy@Sun.COM * [IPv4(0)][ICMPv4][IPv4(1)][IPv4(2)][ULP] 239610616SSebastien.Roy@Sun.COM * 239710616SSebastien.Roy@Sun.COM * or 239810616SSebastien.Roy@Sun.COM * 239910616SSebastien.Roy@Sun.COM * [IPv4(0)][ICMPv4][IPv4(1)][IPv6][ULP] 240010616SSebastien.Roy@Sun.COM * 240110616SSebastien.Roy@Sun.COM * And "outer4" will get set to IPv4(1), and inner[46] will correspond to 240210616SSebastien.Roy@Sun.COM * whatever the very-inner packet is (IPv4(2) or IPv6). 240310616SSebastien.Roy@Sun.COM */ 240410616SSebastien.Roy@Sun.COM static void 240511042SErik.Nordmark@Sun.COM iptun_input_icmp_v4(iptun_t *iptun, mblk_t *data_mp, icmph_t *icmph, 240611042SErik.Nordmark@Sun.COM ip_recv_attr_t *ira) 240710616SSebastien.Roy@Sun.COM { 240810616SSebastien.Roy@Sun.COM uint8_t *orig; 240910616SSebastien.Roy@Sun.COM ipha_t *outer4, *inner4; 241010616SSebastien.Roy@Sun.COM ip6_t *outer6, *inner6; 241110616SSebastien.Roy@Sun.COM int outer_hlen; 241210616SSebastien.Roy@Sun.COM uint8_t type, code; 241310616SSebastien.Roy@Sun.COM 241410616SSebastien.Roy@Sun.COM ASSERT(data_mp->b_cont == NULL); 241510616SSebastien.Roy@Sun.COM /* 241610616SSebastien.Roy@Sun.COM * Temporarily move b_rptr forward so that iptun_find_headers() can 241710616SSebastien.Roy@Sun.COM * find headers in the ICMP packet payload. 241810616SSebastien.Roy@Sun.COM */ 241910616SSebastien.Roy@Sun.COM orig = data_mp->b_rptr; 242010616SSebastien.Roy@Sun.COM data_mp->b_rptr = (uint8_t *)(icmph + 1); 242110616SSebastien.Roy@Sun.COM /* 242210616SSebastien.Roy@Sun.COM * The ip module ensures that ICMP errors contain at least the 242310616SSebastien.Roy@Sun.COM * original IP header (otherwise, the error would never have made it 242410616SSebastien.Roy@Sun.COM * here). 242510616SSebastien.Roy@Sun.COM */ 242610616SSebastien.Roy@Sun.COM ASSERT(MBLKL(data_mp) >= 0); 242711042SErik.Nordmark@Sun.COM outer_hlen = iptun_find_headers(data_mp, 0, &outer4, &inner4, &outer6, 242810616SSebastien.Roy@Sun.COM &inner6); 242910616SSebastien.Roy@Sun.COM ASSERT(outer6 == NULL); 243010616SSebastien.Roy@Sun.COM data_mp->b_rptr = orig; 243110616SSebastien.Roy@Sun.COM if (outer_hlen == 0) { 243211042SErik.Nordmark@Sun.COM iptun_drop_pkt(data_mp, &iptun->iptun_ierrors); 243310616SSebastien.Roy@Sun.COM return; 243410616SSebastien.Roy@Sun.COM } 243510616SSebastien.Roy@Sun.COM 243610616SSebastien.Roy@Sun.COM /* Only ICMP errors due to tunneled packets should reach here. */ 243710616SSebastien.Roy@Sun.COM ASSERT(outer4->ipha_protocol == IPPROTO_ENCAP || 243810616SSebastien.Roy@Sun.COM outer4->ipha_protocol == IPPROTO_IPV6); 243910616SSebastien.Roy@Sun.COM 244011042SErik.Nordmark@Sun.COM data_mp = ipsec_tun_inbound(ira, data_mp, iptun->iptun_itp, 244111042SErik.Nordmark@Sun.COM inner4, inner6, outer4, outer6, -outer_hlen, iptun->iptun_ns); 244211042SErik.Nordmark@Sun.COM if (data_mp == NULL) { 244310616SSebastien.Roy@Sun.COM /* Callee did all of the freeing. */ 244410616SSebastien.Roy@Sun.COM atomic_inc_64(&iptun->iptun_ierrors); 244510616SSebastien.Roy@Sun.COM return; 244610616SSebastien.Roy@Sun.COM } 244710616SSebastien.Roy@Sun.COM /* We should never see reassembled fragment here. */ 244810616SSebastien.Roy@Sun.COM ASSERT(data_mp->b_next == NULL); 244910616SSebastien.Roy@Sun.COM 245010616SSebastien.Roy@Sun.COM data_mp->b_rptr = (uint8_t *)outer4 + outer_hlen; 245110616SSebastien.Roy@Sun.COM 245210616SSebastien.Roy@Sun.COM /* 245310616SSebastien.Roy@Sun.COM * If the original packet being transmitted was itself an ICMP error, 245410616SSebastien.Roy@Sun.COM * then drop this packet. We don't want to generate an ICMP error in 245510616SSebastien.Roy@Sun.COM * response to an ICMP error. 245610616SSebastien.Roy@Sun.COM */ 245710616SSebastien.Roy@Sun.COM if (is_icmp_error(data_mp, inner4, inner6)) { 245810616SSebastien.Roy@Sun.COM iptun_drop_pkt(data_mp, &iptun->iptun_norcvbuf); 245910616SSebastien.Roy@Sun.COM return; 246010616SSebastien.Roy@Sun.COM } 246110616SSebastien.Roy@Sun.COM 246210616SSebastien.Roy@Sun.COM switch (icmph->icmph_type) { 246310616SSebastien.Roy@Sun.COM case ICMP_DEST_UNREACHABLE: 246410616SSebastien.Roy@Sun.COM type = (inner4 != NULL ? icmph->icmph_type : ICMP6_DST_UNREACH); 246510616SSebastien.Roy@Sun.COM switch (icmph->icmph_code) { 246610616SSebastien.Roy@Sun.COM case ICMP_FRAGMENTATION_NEEDED: { 246710616SSebastien.Roy@Sun.COM uint32_t newmtu; 246810616SSebastien.Roy@Sun.COM 246910616SSebastien.Roy@Sun.COM /* 247010616SSebastien.Roy@Sun.COM * We reconcile this with the fact that the tunnel may 247110616SSebastien.Roy@Sun.COM * also have IPsec policy by letting iptun_update_mtu 247210616SSebastien.Roy@Sun.COM * take care of it. 247310616SSebastien.Roy@Sun.COM */ 247411042SErik.Nordmark@Sun.COM newmtu = iptun_update_mtu(iptun, NULL, 247511042SErik.Nordmark@Sun.COM ntohs(icmph->icmph_du_mtu)); 247610616SSebastien.Roy@Sun.COM 247710616SSebastien.Roy@Sun.COM if (inner4 != NULL) { 247810616SSebastien.Roy@Sun.COM iptun_icmp_fragneeded_v4(iptun, newmtu, inner4, 247911042SErik.Nordmark@Sun.COM data_mp, ira->ira_tsl); 248010616SSebastien.Roy@Sun.COM } else { 248110616SSebastien.Roy@Sun.COM iptun_icmp_toobig_v6(iptun, newmtu, inner6, 248211042SErik.Nordmark@Sun.COM data_mp, ira->ira_tsl); 248310616SSebastien.Roy@Sun.COM } 248410616SSebastien.Roy@Sun.COM return; 248510616SSebastien.Roy@Sun.COM } 248610616SSebastien.Roy@Sun.COM case ICMP_DEST_NET_UNREACH_ADMIN: 248710616SSebastien.Roy@Sun.COM case ICMP_DEST_HOST_UNREACH_ADMIN: 248810616SSebastien.Roy@Sun.COM code = (inner4 != NULL ? ICMP_DEST_NET_UNREACH_ADMIN : 248910616SSebastien.Roy@Sun.COM ICMP6_DST_UNREACH_ADMIN); 249010616SSebastien.Roy@Sun.COM break; 249110616SSebastien.Roy@Sun.COM default: 249210616SSebastien.Roy@Sun.COM code = (inner4 != NULL ? ICMP_HOST_UNREACHABLE : 249310616SSebastien.Roy@Sun.COM ICMP6_DST_UNREACH_ADDR); 249410616SSebastien.Roy@Sun.COM break; 249510616SSebastien.Roy@Sun.COM } 249610616SSebastien.Roy@Sun.COM break; 249710616SSebastien.Roy@Sun.COM case ICMP_TIME_EXCEEDED: 249810616SSebastien.Roy@Sun.COM if (inner6 != NULL) { 249910616SSebastien.Roy@Sun.COM type = ICMP6_TIME_EXCEEDED; 250010616SSebastien.Roy@Sun.COM code = 0; 250110616SSebastien.Roy@Sun.COM } /* else we're already set. */ 250210616SSebastien.Roy@Sun.COM break; 250310616SSebastien.Roy@Sun.COM case ICMP_PARAM_PROBLEM: 250410616SSebastien.Roy@Sun.COM /* 250510616SSebastien.Roy@Sun.COM * This is a problem with the outer header we transmitted. 250610616SSebastien.Roy@Sun.COM * Treat this as an output error. 250710616SSebastien.Roy@Sun.COM */ 250810616SSebastien.Roy@Sun.COM iptun_drop_pkt(data_mp, &iptun->iptun_oerrors); 250910616SSebastien.Roy@Sun.COM return; 251010616SSebastien.Roy@Sun.COM default: 251110616SSebastien.Roy@Sun.COM iptun_drop_pkt(data_mp, &iptun->iptun_norcvbuf); 251210616SSebastien.Roy@Sun.COM return; 251310616SSebastien.Roy@Sun.COM } 251410616SSebastien.Roy@Sun.COM 251511042SErik.Nordmark@Sun.COM if (inner4 != NULL) { 251611042SErik.Nordmark@Sun.COM iptun_icmp_error_v4(iptun, inner4, data_mp, type, code, 251711042SErik.Nordmark@Sun.COM ira->ira_tsl); 251811042SErik.Nordmark@Sun.COM } else { 251911042SErik.Nordmark@Sun.COM iptun_icmp_error_v6(iptun, inner6, data_mp, type, code, 0, 252011042SErik.Nordmark@Sun.COM ira->ira_tsl); 252111042SErik.Nordmark@Sun.COM } 252210616SSebastien.Roy@Sun.COM } 252310616SSebastien.Roy@Sun.COM 252410616SSebastien.Roy@Sun.COM /* 252510616SSebastien.Roy@Sun.COM * Return B_TRUE if the IPv6 packet pointed to by ip6h contains a Tunnel 252610616SSebastien.Roy@Sun.COM * Encapsulation Limit destination option. If there is one, set encaplim_ptr 252710616SSebastien.Roy@Sun.COM * to point to the option value. 252810616SSebastien.Roy@Sun.COM */ 252910616SSebastien.Roy@Sun.COM static boolean_t 253010616SSebastien.Roy@Sun.COM iptun_find_encaplimit(mblk_t *mp, ip6_t *ip6h, uint8_t **encaplim_ptr) 253110616SSebastien.Roy@Sun.COM { 253211042SErik.Nordmark@Sun.COM ip_pkt_t pkt; 253310616SSebastien.Roy@Sun.COM uint8_t *endptr; 253410616SSebastien.Roy@Sun.COM ip6_dest_t *destp; 253510616SSebastien.Roy@Sun.COM struct ip6_opt *optp; 253610616SSebastien.Roy@Sun.COM 253710616SSebastien.Roy@Sun.COM pkt.ipp_fields = 0; /* must be initialized */ 253811042SErik.Nordmark@Sun.COM (void) ip_find_hdr_v6(mp, ip6h, B_FALSE, &pkt, NULL); 253910616SSebastien.Roy@Sun.COM if ((pkt.ipp_fields & IPPF_DSTOPTS) != 0) { 254010616SSebastien.Roy@Sun.COM destp = pkt.ipp_dstopts; 254111042SErik.Nordmark@Sun.COM } else if ((pkt.ipp_fields & IPPF_RTHDRDSTOPTS) != 0) { 254211042SErik.Nordmark@Sun.COM destp = pkt.ipp_rthdrdstopts; 254310616SSebastien.Roy@Sun.COM } else { 254410616SSebastien.Roy@Sun.COM return (B_FALSE); 254510616SSebastien.Roy@Sun.COM } 254610616SSebastien.Roy@Sun.COM 254710616SSebastien.Roy@Sun.COM endptr = (uint8_t *)destp + 8 * (destp->ip6d_len + 1); 254810616SSebastien.Roy@Sun.COM optp = (struct ip6_opt *)(destp + 1); 254910616SSebastien.Roy@Sun.COM while (endptr - (uint8_t *)optp > sizeof (*optp)) { 255010616SSebastien.Roy@Sun.COM if (optp->ip6o_type == IP6OPT_TUNNEL_LIMIT) { 255110616SSebastien.Roy@Sun.COM if ((uint8_t *)(optp + 1) >= endptr) 255210616SSebastien.Roy@Sun.COM return (B_FALSE); 255310616SSebastien.Roy@Sun.COM *encaplim_ptr = (uint8_t *)&optp[1]; 255410616SSebastien.Roy@Sun.COM return (B_TRUE); 255510616SSebastien.Roy@Sun.COM } 255610616SSebastien.Roy@Sun.COM optp = (struct ip6_opt *)((uint8_t *)optp + optp->ip6o_len + 2); 255710616SSebastien.Roy@Sun.COM } 255810616SSebastien.Roy@Sun.COM return (B_FALSE); 255910616SSebastien.Roy@Sun.COM } 256010616SSebastien.Roy@Sun.COM 256110616SSebastien.Roy@Sun.COM /* 256210616SSebastien.Roy@Sun.COM * Received ICMPv6 error in response to an X over IPv6 packet that we 256310616SSebastien.Roy@Sun.COM * transmitted. 256410616SSebastien.Roy@Sun.COM * 256510616SSebastien.Roy@Sun.COM * NOTE: "outer" refers to what's inside the ICMP payload. We will get one of 256610616SSebastien.Roy@Sun.COM * the following: 256710616SSebastien.Roy@Sun.COM * 256810616SSebastien.Roy@Sun.COM * [IPv6(0)][ICMPv6][IPv6(1)][IPv4][ULP] 256910616SSebastien.Roy@Sun.COM * 257010616SSebastien.Roy@Sun.COM * or 257110616SSebastien.Roy@Sun.COM * 257210616SSebastien.Roy@Sun.COM * [IPv6(0)][ICMPv6][IPv6(1)][IPv6(2)][ULP] 257310616SSebastien.Roy@Sun.COM * 257410616SSebastien.Roy@Sun.COM * And "outer6" will get set to IPv6(1), and inner[46] will correspond to 257510616SSebastien.Roy@Sun.COM * whatever the very-inner packet is (IPv4 or IPv6(2)). 257610616SSebastien.Roy@Sun.COM */ 257710616SSebastien.Roy@Sun.COM static void 257811042SErik.Nordmark@Sun.COM iptun_input_icmp_v6(iptun_t *iptun, mblk_t *data_mp, icmp6_t *icmp6h, 257911042SErik.Nordmark@Sun.COM ip_recv_attr_t *ira) 258010616SSebastien.Roy@Sun.COM { 258110616SSebastien.Roy@Sun.COM uint8_t *orig; 258210616SSebastien.Roy@Sun.COM ipha_t *outer4, *inner4; 258310616SSebastien.Roy@Sun.COM ip6_t *outer6, *inner6; 258410616SSebastien.Roy@Sun.COM int outer_hlen; 258510616SSebastien.Roy@Sun.COM uint8_t type, code; 258610616SSebastien.Roy@Sun.COM 258710616SSebastien.Roy@Sun.COM ASSERT(data_mp->b_cont == NULL); 258810616SSebastien.Roy@Sun.COM 258910616SSebastien.Roy@Sun.COM /* 259010616SSebastien.Roy@Sun.COM * Temporarily move b_rptr forward so that iptun_find_headers() can 259110616SSebastien.Roy@Sun.COM * find IP headers in the ICMP packet payload. 259210616SSebastien.Roy@Sun.COM */ 259310616SSebastien.Roy@Sun.COM orig = data_mp->b_rptr; 259410616SSebastien.Roy@Sun.COM data_mp->b_rptr = (uint8_t *)(icmp6h + 1); 259510616SSebastien.Roy@Sun.COM /* 259610616SSebastien.Roy@Sun.COM * The ip module ensures that ICMP errors contain at least the 259710616SSebastien.Roy@Sun.COM * original IP header (otherwise, the error would never have made it 259810616SSebastien.Roy@Sun.COM * here). 259910616SSebastien.Roy@Sun.COM */ 260010616SSebastien.Roy@Sun.COM ASSERT(MBLKL(data_mp) >= 0); 260111042SErik.Nordmark@Sun.COM outer_hlen = iptun_find_headers(data_mp, 0, &outer4, &inner4, &outer6, 260210616SSebastien.Roy@Sun.COM &inner6); 260310616SSebastien.Roy@Sun.COM ASSERT(outer4 == NULL); 260410616SSebastien.Roy@Sun.COM data_mp->b_rptr = orig; /* Restore r_ptr */ 260510616SSebastien.Roy@Sun.COM if (outer_hlen == 0) { 260611042SErik.Nordmark@Sun.COM iptun_drop_pkt(data_mp, &iptun->iptun_ierrors); 260710616SSebastien.Roy@Sun.COM return; 260810616SSebastien.Roy@Sun.COM } 260910616SSebastien.Roy@Sun.COM 261011042SErik.Nordmark@Sun.COM data_mp = ipsec_tun_inbound(ira, data_mp, iptun->iptun_itp, 261111042SErik.Nordmark@Sun.COM inner4, inner6, outer4, outer6, -outer_hlen, iptun->iptun_ns); 261211042SErik.Nordmark@Sun.COM if (data_mp == NULL) { 261310616SSebastien.Roy@Sun.COM /* Callee did all of the freeing. */ 261410616SSebastien.Roy@Sun.COM atomic_inc_64(&iptun->iptun_ierrors); 261510616SSebastien.Roy@Sun.COM return; 261610616SSebastien.Roy@Sun.COM } 261710616SSebastien.Roy@Sun.COM /* We should never see reassembled fragment here. */ 261810616SSebastien.Roy@Sun.COM ASSERT(data_mp->b_next == NULL); 261910616SSebastien.Roy@Sun.COM 262010616SSebastien.Roy@Sun.COM data_mp->b_rptr = (uint8_t *)outer6 + outer_hlen; 262110616SSebastien.Roy@Sun.COM 262210616SSebastien.Roy@Sun.COM /* 262310616SSebastien.Roy@Sun.COM * If the original packet being transmitted was itself an ICMP error, 262410616SSebastien.Roy@Sun.COM * then drop this packet. We don't want to generate an ICMP error in 262510616SSebastien.Roy@Sun.COM * response to an ICMP error. 262610616SSebastien.Roy@Sun.COM */ 262710616SSebastien.Roy@Sun.COM if (is_icmp_error(data_mp, inner4, inner6)) { 262810616SSebastien.Roy@Sun.COM iptun_drop_pkt(data_mp, &iptun->iptun_norcvbuf); 262910616SSebastien.Roy@Sun.COM return; 263010616SSebastien.Roy@Sun.COM } 263110616SSebastien.Roy@Sun.COM 263210616SSebastien.Roy@Sun.COM switch (icmp6h->icmp6_type) { 263310616SSebastien.Roy@Sun.COM case ICMP6_PARAM_PROB: { 263410616SSebastien.Roy@Sun.COM uint8_t *encaplim_ptr; 263510616SSebastien.Roy@Sun.COM 263610616SSebastien.Roy@Sun.COM /* 263710616SSebastien.Roy@Sun.COM * If the ICMPv6 error points to a valid Tunnel Encapsulation 263810616SSebastien.Roy@Sun.COM * Limit option and the limit value is 0, then fall through 263910616SSebastien.Roy@Sun.COM * and send a host unreachable message. Otherwise, treat the 264010616SSebastien.Roy@Sun.COM * error as an output error, as there must have been a problem 264110616SSebastien.Roy@Sun.COM * with a packet we sent. 264210616SSebastien.Roy@Sun.COM */ 264310616SSebastien.Roy@Sun.COM if (!iptun_find_encaplimit(data_mp, outer6, &encaplim_ptr) || 264410616SSebastien.Roy@Sun.COM (icmp6h->icmp6_pptr != 264510616SSebastien.Roy@Sun.COM ((ptrdiff_t)encaplim_ptr - (ptrdiff_t)outer6)) || 264610616SSebastien.Roy@Sun.COM *encaplim_ptr != 0) { 264710616SSebastien.Roy@Sun.COM iptun_drop_pkt(data_mp, &iptun->iptun_oerrors); 264810616SSebastien.Roy@Sun.COM return; 264910616SSebastien.Roy@Sun.COM } 265010616SSebastien.Roy@Sun.COM /* FALLTHRU */ 265110616SSebastien.Roy@Sun.COM } 265210616SSebastien.Roy@Sun.COM case ICMP6_TIME_EXCEEDED: 265310616SSebastien.Roy@Sun.COM case ICMP6_DST_UNREACH: 265410616SSebastien.Roy@Sun.COM type = (inner4 != NULL ? ICMP_DEST_UNREACHABLE : 265510616SSebastien.Roy@Sun.COM ICMP6_DST_UNREACH); 265610616SSebastien.Roy@Sun.COM code = (inner4 != NULL ? ICMP_HOST_UNREACHABLE : 265710616SSebastien.Roy@Sun.COM ICMP6_DST_UNREACH_ADDR); 265810616SSebastien.Roy@Sun.COM break; 265910616SSebastien.Roy@Sun.COM case ICMP6_PACKET_TOO_BIG: { 266010616SSebastien.Roy@Sun.COM uint32_t newmtu; 266110616SSebastien.Roy@Sun.COM 266210616SSebastien.Roy@Sun.COM /* 266310616SSebastien.Roy@Sun.COM * We reconcile this with the fact that the tunnel may also 266410616SSebastien.Roy@Sun.COM * have IPsec policy by letting iptun_update_mtu take care of 266510616SSebastien.Roy@Sun.COM * it. 266610616SSebastien.Roy@Sun.COM */ 266711042SErik.Nordmark@Sun.COM newmtu = iptun_update_mtu(iptun, NULL, 266811042SErik.Nordmark@Sun.COM ntohl(icmp6h->icmp6_mtu)); 266910616SSebastien.Roy@Sun.COM 267010616SSebastien.Roy@Sun.COM if (inner4 != NULL) { 267110616SSebastien.Roy@Sun.COM iptun_icmp_fragneeded_v4(iptun, newmtu, inner4, 267211042SErik.Nordmark@Sun.COM data_mp, ira->ira_tsl); 267310616SSebastien.Roy@Sun.COM } else { 267411042SErik.Nordmark@Sun.COM iptun_icmp_toobig_v6(iptun, newmtu, inner6, data_mp, 267511042SErik.Nordmark@Sun.COM ira->ira_tsl); 267610616SSebastien.Roy@Sun.COM } 267710616SSebastien.Roy@Sun.COM return; 267810616SSebastien.Roy@Sun.COM } 267910616SSebastien.Roy@Sun.COM default: 268010616SSebastien.Roy@Sun.COM iptun_drop_pkt(data_mp, &iptun->iptun_norcvbuf); 268110616SSebastien.Roy@Sun.COM return; 268210616SSebastien.Roy@Sun.COM } 268310616SSebastien.Roy@Sun.COM 268411042SErik.Nordmark@Sun.COM if (inner4 != NULL) { 268511042SErik.Nordmark@Sun.COM iptun_icmp_error_v4(iptun, inner4, data_mp, type, code, 268611042SErik.Nordmark@Sun.COM ira->ira_tsl); 268711042SErik.Nordmark@Sun.COM } else { 268811042SErik.Nordmark@Sun.COM iptun_icmp_error_v6(iptun, inner6, data_mp, type, code, 0, 268911042SErik.Nordmark@Sun.COM ira->ira_tsl); 269011042SErik.Nordmark@Sun.COM } 269110616SSebastien.Roy@Sun.COM } 269210616SSebastien.Roy@Sun.COM 269311042SErik.Nordmark@Sun.COM /* 269411042SErik.Nordmark@Sun.COM * Called as conn_recvicmp from IP for ICMP errors. 269511042SErik.Nordmark@Sun.COM */ 269611042SErik.Nordmark@Sun.COM /* ARGSUSED2 */ 269710616SSebastien.Roy@Sun.COM static void 269811042SErik.Nordmark@Sun.COM iptun_input_icmp(void *arg, mblk_t *mp, void *arg2, ip_recv_attr_t *ira) 269910616SSebastien.Roy@Sun.COM { 270011042SErik.Nordmark@Sun.COM conn_t *connp = arg; 270111042SErik.Nordmark@Sun.COM iptun_t *iptun = connp->conn_iptun; 270211042SErik.Nordmark@Sun.COM mblk_t *tmpmp; 270311042SErik.Nordmark@Sun.COM size_t hlen; 270411042SErik.Nordmark@Sun.COM 270511042SErik.Nordmark@Sun.COM ASSERT(IPCL_IS_IPTUN(connp)); 270611042SErik.Nordmark@Sun.COM 270711042SErik.Nordmark@Sun.COM if (mp->b_cont != NULL) { 270810616SSebastien.Roy@Sun.COM /* 270910616SSebastien.Roy@Sun.COM * Since ICMP error processing necessitates access to bits 271010616SSebastien.Roy@Sun.COM * that are within the ICMP error payload (the original packet 271110616SSebastien.Roy@Sun.COM * that caused the error), pull everything up into a single 271210616SSebastien.Roy@Sun.COM * block for convenience. 271310616SSebastien.Roy@Sun.COM */ 271411042SErik.Nordmark@Sun.COM if ((tmpmp = msgpullup(mp, -1)) == NULL) { 271511042SErik.Nordmark@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_norcvbuf); 271610616SSebastien.Roy@Sun.COM return; 271710616SSebastien.Roy@Sun.COM } 271811042SErik.Nordmark@Sun.COM freemsg(mp); 271911042SErik.Nordmark@Sun.COM mp = tmpmp; 272010616SSebastien.Roy@Sun.COM } 272110616SSebastien.Roy@Sun.COM 272211042SErik.Nordmark@Sun.COM hlen = ira->ira_ip_hdr_length; 272310616SSebastien.Roy@Sun.COM switch (iptun->iptun_typeinfo->iti_ipvers) { 272410616SSebastien.Roy@Sun.COM case IPV4_VERSION: 272510616SSebastien.Roy@Sun.COM /* 272610616SSebastien.Roy@Sun.COM * The outer IP header coming up from IP is always ipha_t 272710616SSebastien.Roy@Sun.COM * alligned (otherwise, we would have crashed in ip). 272810616SSebastien.Roy@Sun.COM */ 272911042SErik.Nordmark@Sun.COM iptun_input_icmp_v4(iptun, mp, (icmph_t *)(mp->b_rptr + hlen), 273011042SErik.Nordmark@Sun.COM ira); 273110616SSebastien.Roy@Sun.COM break; 273210616SSebastien.Roy@Sun.COM case IPV6_VERSION: 273311042SErik.Nordmark@Sun.COM iptun_input_icmp_v6(iptun, mp, (icmp6_t *)(mp->b_rptr + hlen), 273411042SErik.Nordmark@Sun.COM ira); 273510616SSebastien.Roy@Sun.COM break; 273610616SSebastien.Roy@Sun.COM } 273710616SSebastien.Roy@Sun.COM } 273810616SSebastien.Roy@Sun.COM 273910616SSebastien.Roy@Sun.COM static boolean_t 274010616SSebastien.Roy@Sun.COM iptun_in_6to4_ok(iptun_t *iptun, ipha_t *outer4, ip6_t *inner6) 274110616SSebastien.Roy@Sun.COM { 274210616SSebastien.Roy@Sun.COM ipaddr_t v4addr; 274310616SSebastien.Roy@Sun.COM 274410616SSebastien.Roy@Sun.COM /* 274510722SSebastien.Roy@Sun.COM * It's possible that someone sent us an IPv4-in-IPv4 packet with the 274610722SSebastien.Roy@Sun.COM * IPv4 address of a 6to4 tunnel as the destination. 274710722SSebastien.Roy@Sun.COM */ 274810722SSebastien.Roy@Sun.COM if (inner6 == NULL) 274910722SSebastien.Roy@Sun.COM return (B_FALSE); 275010722SSebastien.Roy@Sun.COM 275110722SSebastien.Roy@Sun.COM /* 275210616SSebastien.Roy@Sun.COM * Make sure that the IPv6 destination is within the site that this 275310616SSebastien.Roy@Sun.COM * 6to4 tunnel is routing for. We don't want people bouncing random 275410616SSebastien.Roy@Sun.COM * tunneled IPv6 packets through this 6to4 router. 275510616SSebastien.Roy@Sun.COM */ 275610616SSebastien.Roy@Sun.COM IN6_6TO4_TO_V4ADDR(&inner6->ip6_dst, (struct in_addr *)&v4addr); 275710616SSebastien.Roy@Sun.COM if (outer4->ipha_dst != v4addr) 275810616SSebastien.Roy@Sun.COM return (B_FALSE); 275910616SSebastien.Roy@Sun.COM 276010616SSebastien.Roy@Sun.COM if (IN6_IS_ADDR_6TO4(&inner6->ip6_src)) { 276110616SSebastien.Roy@Sun.COM /* 276210616SSebastien.Roy@Sun.COM * Section 9 of RFC 3056 (security considerations) suggests 276310616SSebastien.Roy@Sun.COM * that when a packet is from a 6to4 site (i.e., it's not a 276410616SSebastien.Roy@Sun.COM * global address being forwarded froma relay router), make 276510616SSebastien.Roy@Sun.COM * sure that the packet was tunneled by that site's 6to4 276610616SSebastien.Roy@Sun.COM * router. 276710616SSebastien.Roy@Sun.COM */ 276810616SSebastien.Roy@Sun.COM IN6_6TO4_TO_V4ADDR(&inner6->ip6_src, (struct in_addr *)&v4addr); 276910616SSebastien.Roy@Sun.COM if (outer4->ipha_src != v4addr) 277010616SSebastien.Roy@Sun.COM return (B_FALSE); 277110616SSebastien.Roy@Sun.COM } else { 277210616SSebastien.Roy@Sun.COM /* 277310616SSebastien.Roy@Sun.COM * Only accept packets from a relay router if we've configured 277410616SSebastien.Roy@Sun.COM * outbound relay router functionality. 277510616SSebastien.Roy@Sun.COM */ 277610616SSebastien.Roy@Sun.COM if (iptun->iptun_iptuns->iptuns_relay_rtr_addr == INADDR_ANY) 277710616SSebastien.Roy@Sun.COM return (B_FALSE); 277810616SSebastien.Roy@Sun.COM } 277910616SSebastien.Roy@Sun.COM 278010616SSebastien.Roy@Sun.COM return (B_TRUE); 278110616SSebastien.Roy@Sun.COM } 278210616SSebastien.Roy@Sun.COM 278310616SSebastien.Roy@Sun.COM /* 278410616SSebastien.Roy@Sun.COM * Input function for everything that comes up from the ip module below us. 278510616SSebastien.Roy@Sun.COM * This is called directly from the ip module via connp->conn_recv(). 278610616SSebastien.Roy@Sun.COM * 278711042SErik.Nordmark@Sun.COM * We receive M_DATA messages with IP-in-IP tunneled packets. 278810616SSebastien.Roy@Sun.COM */ 278911042SErik.Nordmark@Sun.COM /* ARGSUSED2 */ 279010616SSebastien.Roy@Sun.COM static void 279111042SErik.Nordmark@Sun.COM iptun_input(void *arg, mblk_t *data_mp, void *arg2, ip_recv_attr_t *ira) 279210616SSebastien.Roy@Sun.COM { 279310616SSebastien.Roy@Sun.COM conn_t *connp = arg; 279410616SSebastien.Roy@Sun.COM iptun_t *iptun = connp->conn_iptun; 279510616SSebastien.Roy@Sun.COM int outer_hlen; 279610616SSebastien.Roy@Sun.COM ipha_t *outer4, *inner4; 279710616SSebastien.Roy@Sun.COM ip6_t *outer6, *inner6; 279810616SSebastien.Roy@Sun.COM 279910616SSebastien.Roy@Sun.COM ASSERT(IPCL_IS_IPTUN(connp)); 280011042SErik.Nordmark@Sun.COM ASSERT(DB_TYPE(data_mp) == M_DATA); 280111042SErik.Nordmark@Sun.COM 280211042SErik.Nordmark@Sun.COM outer_hlen = iptun_find_headers(data_mp, ira->ira_ip_hdr_length, 280311042SErik.Nordmark@Sun.COM &outer4, &inner4, &outer6, &inner6); 280411042SErik.Nordmark@Sun.COM if (outer_hlen == 0) 280510616SSebastien.Roy@Sun.COM goto drop; 280610616SSebastien.Roy@Sun.COM 280710616SSebastien.Roy@Sun.COM /* 280810616SSebastien.Roy@Sun.COM * If the system is labeled, we call tsol_check_dest() on the packet 280910616SSebastien.Roy@Sun.COM * destination (our local tunnel address) to ensure that the packet as 281010616SSebastien.Roy@Sun.COM * labeled should be allowed to be sent to us. We don't need to call 281110616SSebastien.Roy@Sun.COM * the more involved tsol_receive_local() since the tunnel link itself 281210616SSebastien.Roy@Sun.COM * cannot be assigned to shared-stack non-global zones. 281310616SSebastien.Roy@Sun.COM */ 281411042SErik.Nordmark@Sun.COM if (ira->ira_flags & IRAF_SYSTEM_LABELED) { 281511042SErik.Nordmark@Sun.COM if (ira->ira_tsl == NULL) 281610616SSebastien.Roy@Sun.COM goto drop; 281711042SErik.Nordmark@Sun.COM if (tsol_check_dest(ira->ira_tsl, (outer4 != NULL ? 281810616SSebastien.Roy@Sun.COM (void *)&outer4->ipha_dst : (void *)&outer6->ip6_dst), 281910616SSebastien.Roy@Sun.COM (outer4 != NULL ? IPV4_VERSION : IPV6_VERSION), 282011042SErik.Nordmark@Sun.COM CONN_MAC_DEFAULT, B_FALSE, NULL) != 0) 282110616SSebastien.Roy@Sun.COM goto drop; 282210616SSebastien.Roy@Sun.COM } 282310616SSebastien.Roy@Sun.COM 282411042SErik.Nordmark@Sun.COM data_mp = ipsec_tun_inbound(ira, data_mp, iptun->iptun_itp, 282511042SErik.Nordmark@Sun.COM inner4, inner6, outer4, outer6, outer_hlen, iptun->iptun_ns); 282611042SErik.Nordmark@Sun.COM if (data_mp == NULL) { 282710692Sdanmcd@sun.com /* Callee did all of the freeing. */ 282810692Sdanmcd@sun.com return; 282910616SSebastien.Roy@Sun.COM } 283010616SSebastien.Roy@Sun.COM 283110616SSebastien.Roy@Sun.COM if (iptun->iptun_typeinfo->iti_type == IPTUN_TYPE_6TO4 && 283210616SSebastien.Roy@Sun.COM !iptun_in_6to4_ok(iptun, outer4, inner6)) 283310616SSebastien.Roy@Sun.COM goto drop; 283410616SSebastien.Roy@Sun.COM 283510616SSebastien.Roy@Sun.COM /* 283610616SSebastien.Roy@Sun.COM * We need to statistically account for each packet individually, so 283710616SSebastien.Roy@Sun.COM * we might as well split up any b_next chains here. 283810616SSebastien.Roy@Sun.COM */ 283910616SSebastien.Roy@Sun.COM do { 284011042SErik.Nordmark@Sun.COM mblk_t *mp; 284111042SErik.Nordmark@Sun.COM 284210616SSebastien.Roy@Sun.COM mp = data_mp->b_next; 284310616SSebastien.Roy@Sun.COM data_mp->b_next = NULL; 284410616SSebastien.Roy@Sun.COM 284510616SSebastien.Roy@Sun.COM atomic_inc_64(&iptun->iptun_ipackets); 284610616SSebastien.Roy@Sun.COM atomic_add_64(&iptun->iptun_rbytes, msgdsize(data_mp)); 284710616SSebastien.Roy@Sun.COM mac_rx(iptun->iptun_mh, NULL, data_mp); 284810616SSebastien.Roy@Sun.COM 284910616SSebastien.Roy@Sun.COM data_mp = mp; 285010616SSebastien.Roy@Sun.COM } while (data_mp != NULL); 285110616SSebastien.Roy@Sun.COM return; 285210616SSebastien.Roy@Sun.COM drop: 285311042SErik.Nordmark@Sun.COM iptun_drop_pkt(data_mp, &iptun->iptun_ierrors); 285410616SSebastien.Roy@Sun.COM } 285510616SSebastien.Roy@Sun.COM 285610616SSebastien.Roy@Sun.COM /* 285710616SSebastien.Roy@Sun.COM * Do 6to4-specific header-processing on output. Return B_TRUE if the packet 285810616SSebastien.Roy@Sun.COM * was processed without issue, or B_FALSE if the packet had issues and should 285910616SSebastien.Roy@Sun.COM * be dropped. 286010616SSebastien.Roy@Sun.COM */ 286110616SSebastien.Roy@Sun.COM static boolean_t 286210616SSebastien.Roy@Sun.COM iptun_out_process_6to4(iptun_t *iptun, ipha_t *outer4, ip6_t *inner6) 286310616SSebastien.Roy@Sun.COM { 286410616SSebastien.Roy@Sun.COM ipaddr_t v4addr; 286510616SSebastien.Roy@Sun.COM 286610616SSebastien.Roy@Sun.COM /* 286710616SSebastien.Roy@Sun.COM * IPv6 source must be a 6to4 address. This is because a conscious 286810616SSebastien.Roy@Sun.COM * decision was made to not allow a Solaris system to be used as a 286910616SSebastien.Roy@Sun.COM * relay router (for security reasons) when 6to4 was initially 287010616SSebastien.Roy@Sun.COM * integrated. If this decision is ever reversed, the following check 287110616SSebastien.Roy@Sun.COM * can be removed. 287210616SSebastien.Roy@Sun.COM */ 287310616SSebastien.Roy@Sun.COM if (!IN6_IS_ADDR_6TO4(&inner6->ip6_src)) 287410616SSebastien.Roy@Sun.COM return (B_FALSE); 287510616SSebastien.Roy@Sun.COM 287610616SSebastien.Roy@Sun.COM /* 287710616SSebastien.Roy@Sun.COM * RFC3056 mandates that the IPv4 source MUST be set to the IPv4 287810616SSebastien.Roy@Sun.COM * portion of the 6to4 IPv6 source address. In other words, make sure 287910616SSebastien.Roy@Sun.COM * that we're tunneling packets from our own 6to4 site. 288010616SSebastien.Roy@Sun.COM */ 288110616SSebastien.Roy@Sun.COM IN6_6TO4_TO_V4ADDR(&inner6->ip6_src, (struct in_addr *)&v4addr); 288210616SSebastien.Roy@Sun.COM if (outer4->ipha_src != v4addr) 288310616SSebastien.Roy@Sun.COM return (B_FALSE); 288410616SSebastien.Roy@Sun.COM 288510616SSebastien.Roy@Sun.COM /* 288610616SSebastien.Roy@Sun.COM * Automatically set the destination of the outer IPv4 header as 288710616SSebastien.Roy@Sun.COM * described in RFC3056. There are two possibilities: 288810616SSebastien.Roy@Sun.COM * 288910616SSebastien.Roy@Sun.COM * a. If the IPv6 destination is a 6to4 address, set the IPv4 address 289010616SSebastien.Roy@Sun.COM * to the IPv4 portion of the 6to4 address. 289110616SSebastien.Roy@Sun.COM * b. If the IPv6 destination is a native IPv6 address, set the IPv4 289210616SSebastien.Roy@Sun.COM * destination to the address of a relay router. 289310616SSebastien.Roy@Sun.COM * 289410616SSebastien.Roy@Sun.COM * Design Note: b shouldn't be necessary here, and this is a flaw in 289510616SSebastien.Roy@Sun.COM * the design of the 6to4relay command. Instead of setting a 6to4 289610616SSebastien.Roy@Sun.COM * relay address in this module via an ioctl, the 6to4relay command 289710616SSebastien.Roy@Sun.COM * could simply add a IPv6 route for native IPv6 addresses (such as a 289810616SSebastien.Roy@Sun.COM * default route) in the forwarding table that uses a 6to4 destination 289910616SSebastien.Roy@Sun.COM * as its next hop, and the IPv4 portion of that address could be a 290010616SSebastien.Roy@Sun.COM * 6to4 relay address. In order for this to work, IP would have to 290110616SSebastien.Roy@Sun.COM * resolve the next hop address, which would necessitate a link-layer 290210616SSebastien.Roy@Sun.COM * address resolver for 6to4 links, which doesn't exist today. 290310616SSebastien.Roy@Sun.COM * 290410616SSebastien.Roy@Sun.COM * In fact, if a resolver existed for 6to4 links, then setting the 290510616SSebastien.Roy@Sun.COM * IPv4 destination in the outer header could be done as part of 290610616SSebastien.Roy@Sun.COM * link-layer address resolution and fast-path header generation, and 290710616SSebastien.Roy@Sun.COM * not here. 290810616SSebastien.Roy@Sun.COM */ 290910616SSebastien.Roy@Sun.COM if (IN6_IS_ADDR_6TO4(&inner6->ip6_dst)) { 291010616SSebastien.Roy@Sun.COM /* destination is a 6to4 router */ 291110616SSebastien.Roy@Sun.COM IN6_6TO4_TO_V4ADDR(&inner6->ip6_dst, 291210616SSebastien.Roy@Sun.COM (struct in_addr *)&outer4->ipha_dst); 291311042SErik.Nordmark@Sun.COM 291411042SErik.Nordmark@Sun.COM /* Reject attempts to send to INADDR_ANY */ 291511042SErik.Nordmark@Sun.COM if (outer4->ipha_dst == INADDR_ANY) 291611042SErik.Nordmark@Sun.COM return (B_FALSE); 291710616SSebastien.Roy@Sun.COM } else { 291810616SSebastien.Roy@Sun.COM /* 291910616SSebastien.Roy@Sun.COM * The destination is a native IPv6 address. If output to a 292010616SSebastien.Roy@Sun.COM * relay-router is enabled, use the relay-router's IPv4 292110616SSebastien.Roy@Sun.COM * address as the destination. 292210616SSebastien.Roy@Sun.COM */ 292310616SSebastien.Roy@Sun.COM if (iptun->iptun_iptuns->iptuns_relay_rtr_addr == INADDR_ANY) 292410616SSebastien.Roy@Sun.COM return (B_FALSE); 292510616SSebastien.Roy@Sun.COM outer4->ipha_dst = iptun->iptun_iptuns->iptuns_relay_rtr_addr; 292610616SSebastien.Roy@Sun.COM } 292710616SSebastien.Roy@Sun.COM 292810616SSebastien.Roy@Sun.COM /* 292910616SSebastien.Roy@Sun.COM * If the outer source and destination are equal, this means that the 293010616SSebastien.Roy@Sun.COM * 6to4 router somehow forwarded an IPv6 packet destined for its own 293110616SSebastien.Roy@Sun.COM * 6to4 site to its 6to4 tunnel interface, which will result in this 293210616SSebastien.Roy@Sun.COM * packet infinitely bouncing between ip and iptun. 293310616SSebastien.Roy@Sun.COM */ 293410616SSebastien.Roy@Sun.COM return (outer4->ipha_src != outer4->ipha_dst); 293510616SSebastien.Roy@Sun.COM } 293610616SSebastien.Roy@Sun.COM 293710616SSebastien.Roy@Sun.COM /* 293810616SSebastien.Roy@Sun.COM * Process output packets with outer IPv4 headers. Frees mp and bumps stat on 293910616SSebastien.Roy@Sun.COM * error. 294010616SSebastien.Roy@Sun.COM */ 294110616SSebastien.Roy@Sun.COM static mblk_t * 294210616SSebastien.Roy@Sun.COM iptun_out_process_ipv4(iptun_t *iptun, mblk_t *mp, ipha_t *outer4, 294311042SErik.Nordmark@Sun.COM ipha_t *inner4, ip6_t *inner6, ip_xmit_attr_t *ixa) 294410616SSebastien.Roy@Sun.COM { 294510616SSebastien.Roy@Sun.COM uint8_t *innerptr = (inner4 != NULL ? 294610616SSebastien.Roy@Sun.COM (uint8_t *)inner4 : (uint8_t *)inner6); 294711042SErik.Nordmark@Sun.COM size_t minmtu = iptun->iptun_typeinfo->iti_minmtu; 294810616SSebastien.Roy@Sun.COM 294910616SSebastien.Roy@Sun.COM if (inner4 != NULL) { 295010616SSebastien.Roy@Sun.COM ASSERT(outer4->ipha_protocol == IPPROTO_ENCAP); 295110616SSebastien.Roy@Sun.COM /* 295210616SSebastien.Roy@Sun.COM * Copy the tos from the inner IPv4 header. We mask off ECN 295310616SSebastien.Roy@Sun.COM * bits (bits 6 and 7) because there is currently no 295410616SSebastien.Roy@Sun.COM * tunnel-tunnel communication to determine if both sides 295510616SSebastien.Roy@Sun.COM * support ECN. We opt for the safe choice: don't copy the 295610616SSebastien.Roy@Sun.COM * ECN bits when doing encapsulation. 295710616SSebastien.Roy@Sun.COM */ 295810616SSebastien.Roy@Sun.COM outer4->ipha_type_of_service = 295910616SSebastien.Roy@Sun.COM inner4->ipha_type_of_service & ~0x03; 296010616SSebastien.Roy@Sun.COM } else { 296110616SSebastien.Roy@Sun.COM ASSERT(outer4->ipha_protocol == IPPROTO_IPV6 && 296210616SSebastien.Roy@Sun.COM inner6 != NULL); 296310616SSebastien.Roy@Sun.COM } 296411042SErik.Nordmark@Sun.COM if (ixa->ixa_flags & IXAF_PMTU_IPV4_DF) 296511042SErik.Nordmark@Sun.COM outer4->ipha_fragment_offset_and_flags |= IPH_DF_HTONS; 296611042SErik.Nordmark@Sun.COM else 296711042SErik.Nordmark@Sun.COM outer4->ipha_fragment_offset_and_flags &= ~IPH_DF_HTONS; 296810616SSebastien.Roy@Sun.COM 296910616SSebastien.Roy@Sun.COM /* 297010616SSebastien.Roy@Sun.COM * As described in section 3.2.2 of RFC4213, if the packet payload is 297110616SSebastien.Roy@Sun.COM * less than or equal to the minimum MTU size, then we need to allow 297210616SSebastien.Roy@Sun.COM * IPv4 to fragment the packet. The reason is that even if we end up 297310616SSebastien.Roy@Sun.COM * receiving an ICMP frag-needed, the interface above this tunnel 297410616SSebastien.Roy@Sun.COM * won't be allowed to drop its MTU as a result, since the packet was 297510616SSebastien.Roy@Sun.COM * already smaller than the smallest allowable MTU for that interface. 297610616SSebastien.Roy@Sun.COM */ 297711042SErik.Nordmark@Sun.COM if (mp->b_wptr - innerptr <= minmtu) { 297810616SSebastien.Roy@Sun.COM outer4->ipha_fragment_offset_and_flags = 0; 297911042SErik.Nordmark@Sun.COM ixa->ixa_flags &= ~IXAF_DONTFRAG; 298011042SErik.Nordmark@Sun.COM } else if (!(ixa->ixa_flags & IXAF_PMTU_TOO_SMALL) && 298111042SErik.Nordmark@Sun.COM (iptun->iptun_typeinfo->iti_type != IPTUN_TYPE_6TO4)) { 298211042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_DONTFRAG; 298311042SErik.Nordmark@Sun.COM } 298411042SErik.Nordmark@Sun.COM 298511042SErik.Nordmark@Sun.COM ixa->ixa_ip_hdr_length = IPH_HDR_LENGTH(outer4); 298611042SErik.Nordmark@Sun.COM ixa->ixa_pktlen = msgdsize(mp); 298711042SErik.Nordmark@Sun.COM ixa->ixa_protocol = outer4->ipha_protocol; 298811042SErik.Nordmark@Sun.COM 298911042SErik.Nordmark@Sun.COM outer4->ipha_length = htons(ixa->ixa_pktlen); 299010616SSebastien.Roy@Sun.COM return (mp); 299110616SSebastien.Roy@Sun.COM } 299210616SSebastien.Roy@Sun.COM 299310616SSebastien.Roy@Sun.COM /* 299410616SSebastien.Roy@Sun.COM * Insert an encapsulation limit destination option in the packet provided. 299510616SSebastien.Roy@Sun.COM * Always consumes the mp argument and returns a new mblk pointer. 299610616SSebastien.Roy@Sun.COM */ 299710616SSebastien.Roy@Sun.COM static mblk_t * 299810616SSebastien.Roy@Sun.COM iptun_insert_encaplimit(iptun_t *iptun, mblk_t *mp, ip6_t *outer6, 299910616SSebastien.Roy@Sun.COM uint8_t limit) 300010616SSebastien.Roy@Sun.COM { 300110616SSebastien.Roy@Sun.COM mblk_t *newmp; 300210616SSebastien.Roy@Sun.COM iptun_ipv6hdrs_t *newouter6; 300310616SSebastien.Roy@Sun.COM 300410616SSebastien.Roy@Sun.COM ASSERT(outer6->ip6_nxt == IPPROTO_IPV6); 300510616SSebastien.Roy@Sun.COM ASSERT(mp->b_cont == NULL); 300610616SSebastien.Roy@Sun.COM 300710616SSebastien.Roy@Sun.COM mp->b_rptr += sizeof (ip6_t); 300811042SErik.Nordmark@Sun.COM newmp = allocb(sizeof (iptun_ipv6hdrs_t) + MBLKL(mp), BPRI_MED); 300910616SSebastien.Roy@Sun.COM if (newmp == NULL) { 301010616SSebastien.Roy@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_noxmtbuf); 301110616SSebastien.Roy@Sun.COM return (NULL); 301210616SSebastien.Roy@Sun.COM } 301310616SSebastien.Roy@Sun.COM newmp->b_wptr += sizeof (iptun_ipv6hdrs_t); 301410616SSebastien.Roy@Sun.COM /* Copy the payload (Starting with the inner IPv6 header). */ 301510616SSebastien.Roy@Sun.COM bcopy(mp->b_rptr, newmp->b_wptr, MBLKL(mp)); 301610616SSebastien.Roy@Sun.COM newmp->b_wptr += MBLKL(mp); 301710616SSebastien.Roy@Sun.COM newouter6 = (iptun_ipv6hdrs_t *)newmp->b_rptr; 301810616SSebastien.Roy@Sun.COM /* Now copy the outer IPv6 header. */ 301910616SSebastien.Roy@Sun.COM bcopy(outer6, &newouter6->it6h_ip6h, sizeof (ip6_t)); 302010616SSebastien.Roy@Sun.COM newouter6->it6h_ip6h.ip6_nxt = IPPROTO_DSTOPTS; 302110616SSebastien.Roy@Sun.COM newouter6->it6h_encaplim = iptun_encaplim_init; 302210616SSebastien.Roy@Sun.COM newouter6->it6h_encaplim.iel_destopt.ip6d_nxt = outer6->ip6_nxt; 302310616SSebastien.Roy@Sun.COM newouter6->it6h_encaplim.iel_telopt.ip6ot_encap_limit = limit; 302410616SSebastien.Roy@Sun.COM 302510616SSebastien.Roy@Sun.COM /* 302610616SSebastien.Roy@Sun.COM * The payload length will be set at the end of 302710616SSebastien.Roy@Sun.COM * iptun_out_process_ipv6(). 302810616SSebastien.Roy@Sun.COM */ 302910616SSebastien.Roy@Sun.COM 303010616SSebastien.Roy@Sun.COM freemsg(mp); 303110616SSebastien.Roy@Sun.COM return (newmp); 303210616SSebastien.Roy@Sun.COM } 303310616SSebastien.Roy@Sun.COM 303410616SSebastien.Roy@Sun.COM /* 303510616SSebastien.Roy@Sun.COM * Process output packets with outer IPv6 headers. Frees mp and bumps stats 303610616SSebastien.Roy@Sun.COM * on error. 303710616SSebastien.Roy@Sun.COM */ 303810616SSebastien.Roy@Sun.COM static mblk_t * 303911042SErik.Nordmark@Sun.COM iptun_out_process_ipv6(iptun_t *iptun, mblk_t *mp, ip6_t *outer6, 304011042SErik.Nordmark@Sun.COM ipha_t *inner4, ip6_t *inner6, ip_xmit_attr_t *ixa) 304110616SSebastien.Roy@Sun.COM { 304211042SErik.Nordmark@Sun.COM uint8_t *innerptr = (inner4 != NULL ? 304311042SErik.Nordmark@Sun.COM (uint8_t *)inner4 : (uint8_t *)inner6); 304411042SErik.Nordmark@Sun.COM size_t minmtu = iptun->iptun_typeinfo->iti_minmtu; 304510616SSebastien.Roy@Sun.COM uint8_t *limit, *configlimit; 304610616SSebastien.Roy@Sun.COM uint32_t offset; 304710616SSebastien.Roy@Sun.COM iptun_ipv6hdrs_t *v6hdrs; 304810616SSebastien.Roy@Sun.COM 304910616SSebastien.Roy@Sun.COM if (inner6 != NULL && iptun_find_encaplimit(mp, inner6, &limit)) { 305010616SSebastien.Roy@Sun.COM /* 305110616SSebastien.Roy@Sun.COM * The inner packet is an IPv6 packet which itself contains an 305210616SSebastien.Roy@Sun.COM * encapsulation limit option. The limit variable points to 305310616SSebastien.Roy@Sun.COM * the value in the embedded option. Process the 305410616SSebastien.Roy@Sun.COM * encapsulation limit option as specified in RFC 2473. 305510616SSebastien.Roy@Sun.COM * 305610616SSebastien.Roy@Sun.COM * If limit is 0, then we've exceeded the limit and we need to 305710616SSebastien.Roy@Sun.COM * send back an ICMPv6 parameter problem message. 305810616SSebastien.Roy@Sun.COM * 305910616SSebastien.Roy@Sun.COM * If limit is > 0, then we decrement it by 1 and make sure 306010616SSebastien.Roy@Sun.COM * that the encapsulation limit option in the outer header 306110616SSebastien.Roy@Sun.COM * reflects that (adding an option if one isn't already 306210616SSebastien.Roy@Sun.COM * there). 306310616SSebastien.Roy@Sun.COM */ 306410616SSebastien.Roy@Sun.COM ASSERT(limit > mp->b_rptr && limit < mp->b_wptr); 306510616SSebastien.Roy@Sun.COM if (*limit == 0) { 306610616SSebastien.Roy@Sun.COM mp->b_rptr = (uint8_t *)inner6; 306710616SSebastien.Roy@Sun.COM offset = limit - mp->b_rptr; 306810616SSebastien.Roy@Sun.COM iptun_icmp_error_v6(iptun, inner6, mp, ICMP6_PARAM_PROB, 306911042SErik.Nordmark@Sun.COM 0, offset, ixa->ixa_tsl); 307010616SSebastien.Roy@Sun.COM atomic_inc_64(&iptun->iptun_noxmtbuf); 307110616SSebastien.Roy@Sun.COM return (NULL); 307210616SSebastien.Roy@Sun.COM } 307310616SSebastien.Roy@Sun.COM 307410616SSebastien.Roy@Sun.COM /* 307510616SSebastien.Roy@Sun.COM * The outer header requires an encapsulation limit option. 307610616SSebastien.Roy@Sun.COM * If there isn't one already, add one. 307710616SSebastien.Roy@Sun.COM */ 307810616SSebastien.Roy@Sun.COM if (iptun->iptun_encaplimit == 0) { 307910616SSebastien.Roy@Sun.COM if ((mp = iptun_insert_encaplimit(iptun, mp, outer6, 308010616SSebastien.Roy@Sun.COM (*limit - 1))) == NULL) 308110616SSebastien.Roy@Sun.COM return (NULL); 308211042SErik.Nordmark@Sun.COM v6hdrs = (iptun_ipv6hdrs_t *)mp->b_rptr; 308310616SSebastien.Roy@Sun.COM } else { 308410616SSebastien.Roy@Sun.COM /* 308510616SSebastien.Roy@Sun.COM * There is an existing encapsulation limit option in 308610616SSebastien.Roy@Sun.COM * the outer header. If the inner encapsulation limit 308710616SSebastien.Roy@Sun.COM * is less than the configured encapsulation limit, 308810616SSebastien.Roy@Sun.COM * update the outer encapsulation limit to reflect 308910616SSebastien.Roy@Sun.COM * this lesser value. 309010616SSebastien.Roy@Sun.COM */ 309110616SSebastien.Roy@Sun.COM v6hdrs = (iptun_ipv6hdrs_t *)mp->b_rptr; 309210616SSebastien.Roy@Sun.COM configlimit = 309310616SSebastien.Roy@Sun.COM &v6hdrs->it6h_encaplim.iel_telopt.ip6ot_encap_limit; 309410616SSebastien.Roy@Sun.COM if ((*limit - 1) < *configlimit) 309510616SSebastien.Roy@Sun.COM *configlimit = (*limit - 1); 309610616SSebastien.Roy@Sun.COM } 309711042SErik.Nordmark@Sun.COM ixa->ixa_ip_hdr_length = sizeof (iptun_ipv6hdrs_t); 309811042SErik.Nordmark@Sun.COM ixa->ixa_protocol = v6hdrs->it6h_encaplim.iel_destopt.ip6d_nxt; 309911042SErik.Nordmark@Sun.COM } else { 310011042SErik.Nordmark@Sun.COM ixa->ixa_ip_hdr_length = sizeof (ip6_t); 310111042SErik.Nordmark@Sun.COM ixa->ixa_protocol = outer6->ip6_nxt; 310210616SSebastien.Roy@Sun.COM } 310311042SErik.Nordmark@Sun.COM /* 310411042SErik.Nordmark@Sun.COM * See iptun_output_process_ipv4() why we allow fragmentation for 310511042SErik.Nordmark@Sun.COM * small packets 310611042SErik.Nordmark@Sun.COM */ 310711042SErik.Nordmark@Sun.COM if (mp->b_wptr - innerptr <= minmtu) 310811042SErik.Nordmark@Sun.COM ixa->ixa_flags &= ~IXAF_DONTFRAG; 310911042SErik.Nordmark@Sun.COM else if (!(ixa->ixa_flags & IXAF_PMTU_TOO_SMALL)) 311011042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_DONTFRAG; 311111042SErik.Nordmark@Sun.COM 311211042SErik.Nordmark@Sun.COM ixa->ixa_pktlen = msgdsize(mp); 311311042SErik.Nordmark@Sun.COM outer6->ip6_plen = htons(ixa->ixa_pktlen - sizeof (ip6_t)); 311410616SSebastien.Roy@Sun.COM return (mp); 311510616SSebastien.Roy@Sun.COM } 311610616SSebastien.Roy@Sun.COM 311710616SSebastien.Roy@Sun.COM /* 311810616SSebastien.Roy@Sun.COM * The IP tunneling MAC-type plugins have already done most of the header 311910616SSebastien.Roy@Sun.COM * processing and validity checks. We are simply responsible for multiplexing 312010616SSebastien.Roy@Sun.COM * down to the ip module below us. 312110616SSebastien.Roy@Sun.COM */ 312210616SSebastien.Roy@Sun.COM static void 312310616SSebastien.Roy@Sun.COM iptun_output(iptun_t *iptun, mblk_t *mp) 312410616SSebastien.Roy@Sun.COM { 312510616SSebastien.Roy@Sun.COM conn_t *connp = iptun->iptun_connp; 312610616SSebastien.Roy@Sun.COM mblk_t *newmp; 312711042SErik.Nordmark@Sun.COM int error; 312811042SErik.Nordmark@Sun.COM ip_xmit_attr_t *ixa; 312910616SSebastien.Roy@Sun.COM 313010616SSebastien.Roy@Sun.COM ASSERT(mp->b_datap->db_type == M_DATA); 313110616SSebastien.Roy@Sun.COM 313210616SSebastien.Roy@Sun.COM if (mp->b_cont != NULL) { 313310616SSebastien.Roy@Sun.COM if ((newmp = msgpullup(mp, -1)) == NULL) { 313410616SSebastien.Roy@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_noxmtbuf); 313510616SSebastien.Roy@Sun.COM return; 313610616SSebastien.Roy@Sun.COM } 313710616SSebastien.Roy@Sun.COM freemsg(mp); 313810616SSebastien.Roy@Sun.COM mp = newmp; 313910616SSebastien.Roy@Sun.COM } 314010616SSebastien.Roy@Sun.COM 314111042SErik.Nordmark@Sun.COM if (iptun->iptun_typeinfo->iti_type == IPTUN_TYPE_6TO4) { 314211042SErik.Nordmark@Sun.COM iptun_output_6to4(iptun, mp); 314311042SErik.Nordmark@Sun.COM return; 314411042SErik.Nordmark@Sun.COM } 314511042SErik.Nordmark@Sun.COM 314611042SErik.Nordmark@Sun.COM if (is_system_labeled()) { 314711042SErik.Nordmark@Sun.COM /* 314811042SErik.Nordmark@Sun.COM * Since the label can be different meaning a potentially 314911042SErik.Nordmark@Sun.COM * different IRE,we always use a unique ip_xmit_attr_t. 315011042SErik.Nordmark@Sun.COM */ 315111042SErik.Nordmark@Sun.COM ixa = conn_get_ixa_exclusive(connp); 315211042SErik.Nordmark@Sun.COM } else { 315311042SErik.Nordmark@Sun.COM /* 315411042SErik.Nordmark@Sun.COM * If no other thread is using conn_ixa this just gets a 315511042SErik.Nordmark@Sun.COM * reference to conn_ixa. Otherwise we get a safe copy of 315611042SErik.Nordmark@Sun.COM * conn_ixa. 315711042SErik.Nordmark@Sun.COM */ 315811042SErik.Nordmark@Sun.COM ixa = conn_get_ixa(connp, B_FALSE); 315911042SErik.Nordmark@Sun.COM } 316011042SErik.Nordmark@Sun.COM if (ixa == NULL) { 316111042SErik.Nordmark@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_oerrors); 316211042SErik.Nordmark@Sun.COM return; 316311042SErik.Nordmark@Sun.COM } 316411042SErik.Nordmark@Sun.COM 316511042SErik.Nordmark@Sun.COM /* 316611042SErik.Nordmark@Sun.COM * In case we got a safe copy of conn_ixa, then we need 316711042SErik.Nordmark@Sun.COM * to fill in any pointers in it. 316811042SErik.Nordmark@Sun.COM */ 316911042SErik.Nordmark@Sun.COM if (ixa->ixa_ire == NULL) { 317011042SErik.Nordmark@Sun.COM error = ip_attr_connect(connp, ixa, &connp->conn_saddr_v6, 317111042SErik.Nordmark@Sun.COM &connp->conn_faddr_v6, &connp->conn_faddr_v6, 0, 317211042SErik.Nordmark@Sun.COM NULL, NULL, 0); 317311042SErik.Nordmark@Sun.COM if (error != 0) { 317411042SErik.Nordmark@Sun.COM if (ixa->ixa_ire != NULL && 317511042SErik.Nordmark@Sun.COM (error == EHOSTUNREACH || error == ENETUNREACH)) { 317611042SErik.Nordmark@Sun.COM /* 317711042SErik.Nordmark@Sun.COM * Let conn_ip_output/ire_send_noroute return 317811042SErik.Nordmark@Sun.COM * the error and send any local ICMP error. 317911042SErik.Nordmark@Sun.COM */ 318011042SErik.Nordmark@Sun.COM error = 0; 318111042SErik.Nordmark@Sun.COM } else { 318211042SErik.Nordmark@Sun.COM ixa_refrele(ixa); 318311042SErik.Nordmark@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_oerrors); 318411042SErik.Nordmark@Sun.COM return; 318511042SErik.Nordmark@Sun.COM } 318611042SErik.Nordmark@Sun.COM } 318711042SErik.Nordmark@Sun.COM } 318811042SErik.Nordmark@Sun.COM 318911042SErik.Nordmark@Sun.COM iptun_output_common(iptun, ixa, mp); 319011042SErik.Nordmark@Sun.COM ixa_refrele(ixa); 319111042SErik.Nordmark@Sun.COM } 319211042SErik.Nordmark@Sun.COM 319311042SErik.Nordmark@Sun.COM /* 319411042SErik.Nordmark@Sun.COM * We use an ixa based on the last destination. 319511042SErik.Nordmark@Sun.COM */ 319611042SErik.Nordmark@Sun.COM static void 319711042SErik.Nordmark@Sun.COM iptun_output_6to4(iptun_t *iptun, mblk_t *mp) 319811042SErik.Nordmark@Sun.COM { 319911042SErik.Nordmark@Sun.COM conn_t *connp = iptun->iptun_connp; 320011042SErik.Nordmark@Sun.COM ipha_t *outer4, *inner4; 320111042SErik.Nordmark@Sun.COM ip6_t *outer6, *inner6; 320211042SErik.Nordmark@Sun.COM ip_xmit_attr_t *ixa; 320311042SErik.Nordmark@Sun.COM ip_xmit_attr_t *oldixa; 320411042SErik.Nordmark@Sun.COM int error; 320511042SErik.Nordmark@Sun.COM boolean_t need_connect; 320611042SErik.Nordmark@Sun.COM in6_addr_t v6dst; 320711042SErik.Nordmark@Sun.COM 320811042SErik.Nordmark@Sun.COM ASSERT(mp->b_cont == NULL); /* Verified by iptun_output */ 320911042SErik.Nordmark@Sun.COM 321011042SErik.Nordmark@Sun.COM /* Make sure we set ipha_dst before we look at ipha_dst */ 321111042SErik.Nordmark@Sun.COM 321211042SErik.Nordmark@Sun.COM (void) iptun_find_headers(mp, 0, &outer4, &inner4, &outer6, &inner6); 321311042SErik.Nordmark@Sun.COM ASSERT(outer4 != NULL); 321411042SErik.Nordmark@Sun.COM if (!iptun_out_process_6to4(iptun, outer4, inner6)) { 321511042SErik.Nordmark@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_oerrors); 321611042SErik.Nordmark@Sun.COM return; 321711042SErik.Nordmark@Sun.COM } 321811042SErik.Nordmark@Sun.COM 321911042SErik.Nordmark@Sun.COM if (is_system_labeled()) { 322011042SErik.Nordmark@Sun.COM /* 322111042SErik.Nordmark@Sun.COM * Since the label can be different meaning a potentially 322211042SErik.Nordmark@Sun.COM * different IRE,we always use a unique ip_xmit_attr_t. 322311042SErik.Nordmark@Sun.COM */ 322411042SErik.Nordmark@Sun.COM ixa = conn_get_ixa_exclusive(connp); 322511042SErik.Nordmark@Sun.COM } else { 322611042SErik.Nordmark@Sun.COM /* 322711042SErik.Nordmark@Sun.COM * If no other thread is using conn_ixa this just gets a 322811042SErik.Nordmark@Sun.COM * reference to conn_ixa. Otherwise we get a safe copy of 322911042SErik.Nordmark@Sun.COM * conn_ixa. 323011042SErik.Nordmark@Sun.COM */ 323111042SErik.Nordmark@Sun.COM ixa = conn_get_ixa(connp, B_FALSE); 323211042SErik.Nordmark@Sun.COM } 323311042SErik.Nordmark@Sun.COM if (ixa == NULL) { 323411042SErik.Nordmark@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_oerrors); 323511042SErik.Nordmark@Sun.COM return; 323611042SErik.Nordmark@Sun.COM } 323711042SErik.Nordmark@Sun.COM 323811042SErik.Nordmark@Sun.COM mutex_enter(&connp->conn_lock); 323911042SErik.Nordmark@Sun.COM if (connp->conn_v4lastdst == outer4->ipha_dst) { 324011042SErik.Nordmark@Sun.COM need_connect = (ixa->ixa_ire == NULL); 324111042SErik.Nordmark@Sun.COM } else { 324211042SErik.Nordmark@Sun.COM /* In case previous destination was multirt */ 324311042SErik.Nordmark@Sun.COM ip_attr_newdst(ixa); 324411042SErik.Nordmark@Sun.COM 324511042SErik.Nordmark@Sun.COM /* 324611042SErik.Nordmark@Sun.COM * We later update conn_ixa when we update conn_v4lastdst 324711042SErik.Nordmark@Sun.COM * which enables subsequent packets to avoid redoing 324811042SErik.Nordmark@Sun.COM * ip_attr_connect 324911042SErik.Nordmark@Sun.COM */ 325011042SErik.Nordmark@Sun.COM need_connect = B_TRUE; 325111042SErik.Nordmark@Sun.COM } 325211042SErik.Nordmark@Sun.COM mutex_exit(&connp->conn_lock); 325311042SErik.Nordmark@Sun.COM 325411042SErik.Nordmark@Sun.COM /* 325511042SErik.Nordmark@Sun.COM * In case we got a safe copy of conn_ixa, or otherwise we don't 325611042SErik.Nordmark@Sun.COM * have a current ixa_ire, then we need to fill in any pointers in 325711042SErik.Nordmark@Sun.COM * the ixa. 325811042SErik.Nordmark@Sun.COM */ 325911042SErik.Nordmark@Sun.COM if (need_connect) { 326011042SErik.Nordmark@Sun.COM IN6_IPADDR_TO_V4MAPPED(outer4->ipha_dst, &v6dst); 326111042SErik.Nordmark@Sun.COM 326211042SErik.Nordmark@Sun.COM /* We handle IPsec in iptun_output_common */ 326311042SErik.Nordmark@Sun.COM error = ip_attr_connect(connp, ixa, &connp->conn_saddr_v6, 326411042SErik.Nordmark@Sun.COM &v6dst, &v6dst, 0, NULL, NULL, 0); 326511042SErik.Nordmark@Sun.COM if (error != 0) { 326611042SErik.Nordmark@Sun.COM if (ixa->ixa_ire != NULL && 326711042SErik.Nordmark@Sun.COM (error == EHOSTUNREACH || error == ENETUNREACH)) { 326811042SErik.Nordmark@Sun.COM /* 326911042SErik.Nordmark@Sun.COM * Let conn_ip_output/ire_send_noroute return 327011042SErik.Nordmark@Sun.COM * the error and send any local ICMP error. 327111042SErik.Nordmark@Sun.COM */ 327211042SErik.Nordmark@Sun.COM error = 0; 327311042SErik.Nordmark@Sun.COM } else { 327411042SErik.Nordmark@Sun.COM ixa_refrele(ixa); 327511042SErik.Nordmark@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_oerrors); 327611042SErik.Nordmark@Sun.COM return; 327711042SErik.Nordmark@Sun.COM } 327811042SErik.Nordmark@Sun.COM } 327911042SErik.Nordmark@Sun.COM } 328011042SErik.Nordmark@Sun.COM 328111042SErik.Nordmark@Sun.COM iptun_output_common(iptun, ixa, mp); 328211042SErik.Nordmark@Sun.COM 328311042SErik.Nordmark@Sun.COM /* Atomically replace conn_ixa and conn_v4lastdst */ 328411042SErik.Nordmark@Sun.COM mutex_enter(&connp->conn_lock); 328511042SErik.Nordmark@Sun.COM if (connp->conn_v4lastdst != outer4->ipha_dst) { 328611042SErik.Nordmark@Sun.COM /* Remember the dst which corresponds to conn_ixa */ 328711042SErik.Nordmark@Sun.COM connp->conn_v6lastdst = v6dst; 328811042SErik.Nordmark@Sun.COM oldixa = conn_replace_ixa(connp, ixa); 328911042SErik.Nordmark@Sun.COM } else { 329011042SErik.Nordmark@Sun.COM oldixa = NULL; 329111042SErik.Nordmark@Sun.COM } 329211042SErik.Nordmark@Sun.COM mutex_exit(&connp->conn_lock); 329311042SErik.Nordmark@Sun.COM ixa_refrele(ixa); 329411042SErik.Nordmark@Sun.COM if (oldixa != NULL) 329511042SErik.Nordmark@Sun.COM ixa_refrele(oldixa); 329611042SErik.Nordmark@Sun.COM } 329711042SErik.Nordmark@Sun.COM 329811042SErik.Nordmark@Sun.COM /* 329911042SErik.Nordmark@Sun.COM * Check the destination/label. Modifies *mpp by adding/removing CIPSO. 330011042SErik.Nordmark@Sun.COM * 330111042SErik.Nordmark@Sun.COM * We get the label from the message in order to honor the 330211042SErik.Nordmark@Sun.COM * ULPs/IPs choice of label. This will be NULL for forwarded 330311042SErik.Nordmark@Sun.COM * packets, neighbor discovery packets and some others. 330411042SErik.Nordmark@Sun.COM */ 330511042SErik.Nordmark@Sun.COM static int 330611042SErik.Nordmark@Sun.COM iptun_output_check_label(mblk_t **mpp, ip_xmit_attr_t *ixa) 330711042SErik.Nordmark@Sun.COM { 330811042SErik.Nordmark@Sun.COM cred_t *cr; 330911042SErik.Nordmark@Sun.COM int adjust; 331011042SErik.Nordmark@Sun.COM int iplen; 331111042SErik.Nordmark@Sun.COM int err; 331211042SErik.Nordmark@Sun.COM ts_label_t *effective_tsl = NULL; 331311042SErik.Nordmark@Sun.COM 331411042SErik.Nordmark@Sun.COM 331511042SErik.Nordmark@Sun.COM ASSERT(is_system_labeled()); 331611042SErik.Nordmark@Sun.COM 331711042SErik.Nordmark@Sun.COM cr = msg_getcred(*mpp, NULL); 331811042SErik.Nordmark@Sun.COM if (cr == NULL) 331911042SErik.Nordmark@Sun.COM return (0); 332011042SErik.Nordmark@Sun.COM 332111042SErik.Nordmark@Sun.COM /* 332211042SErik.Nordmark@Sun.COM * We need to start with a label based on the IP/ULP above us 332311042SErik.Nordmark@Sun.COM */ 332411042SErik.Nordmark@Sun.COM ip_xmit_attr_restore_tsl(ixa, cr); 332511042SErik.Nordmark@Sun.COM 332611042SErik.Nordmark@Sun.COM /* 332711042SErik.Nordmark@Sun.COM * Need to update packet with any CIPSO option since 332811042SErik.Nordmark@Sun.COM * conn_ip_output doesn't do that. 332911042SErik.Nordmark@Sun.COM */ 333011042SErik.Nordmark@Sun.COM if (ixa->ixa_flags & IXAF_IS_IPV4) { 333111042SErik.Nordmark@Sun.COM ipha_t *ipha; 333211042SErik.Nordmark@Sun.COM 333311042SErik.Nordmark@Sun.COM ipha = (ipha_t *)(*mpp)->b_rptr; 333411042SErik.Nordmark@Sun.COM iplen = ntohs(ipha->ipha_length); 333511042SErik.Nordmark@Sun.COM err = tsol_check_label_v4(ixa->ixa_tsl, 333611042SErik.Nordmark@Sun.COM ixa->ixa_zoneid, mpp, CONN_MAC_DEFAULT, B_FALSE, 333711042SErik.Nordmark@Sun.COM ixa->ixa_ipst, &effective_tsl); 333811042SErik.Nordmark@Sun.COM if (err != 0) 333911042SErik.Nordmark@Sun.COM return (err); 334011042SErik.Nordmark@Sun.COM 334111042SErik.Nordmark@Sun.COM ipha = (ipha_t *)(*mpp)->b_rptr; 334211042SErik.Nordmark@Sun.COM adjust = (int)ntohs(ipha->ipha_length) - iplen; 334311042SErik.Nordmark@Sun.COM } else { 334411042SErik.Nordmark@Sun.COM ip6_t *ip6h; 334511042SErik.Nordmark@Sun.COM 334611042SErik.Nordmark@Sun.COM ip6h = (ip6_t *)(*mpp)->b_rptr; 334711042SErik.Nordmark@Sun.COM iplen = ntohs(ip6h->ip6_plen); 334811042SErik.Nordmark@Sun.COM 334911042SErik.Nordmark@Sun.COM err = tsol_check_label_v6(ixa->ixa_tsl, 335011042SErik.Nordmark@Sun.COM ixa->ixa_zoneid, mpp, CONN_MAC_DEFAULT, B_FALSE, 335111042SErik.Nordmark@Sun.COM ixa->ixa_ipst, &effective_tsl); 335211042SErik.Nordmark@Sun.COM if (err != 0) 335311042SErik.Nordmark@Sun.COM return (err); 335411042SErik.Nordmark@Sun.COM 335511042SErik.Nordmark@Sun.COM ip6h = (ip6_t *)(*mpp)->b_rptr; 335611042SErik.Nordmark@Sun.COM adjust = (int)ntohs(ip6h->ip6_plen) - iplen; 335711042SErik.Nordmark@Sun.COM } 335811042SErik.Nordmark@Sun.COM 335911042SErik.Nordmark@Sun.COM if (effective_tsl != NULL) { 336011042SErik.Nordmark@Sun.COM /* Update the label */ 336111042SErik.Nordmark@Sun.COM ip_xmit_attr_replace_tsl(ixa, effective_tsl); 336211042SErik.Nordmark@Sun.COM } 336311042SErik.Nordmark@Sun.COM ixa->ixa_pktlen += adjust; 336411042SErik.Nordmark@Sun.COM ixa->ixa_ip_hdr_length += adjust; 336511042SErik.Nordmark@Sun.COM return (0); 336611042SErik.Nordmark@Sun.COM } 336711042SErik.Nordmark@Sun.COM 336811042SErik.Nordmark@Sun.COM 336911042SErik.Nordmark@Sun.COM static void 337011042SErik.Nordmark@Sun.COM iptun_output_common(iptun_t *iptun, ip_xmit_attr_t *ixa, mblk_t *mp) 337111042SErik.Nordmark@Sun.COM { 337211042SErik.Nordmark@Sun.COM ipsec_tun_pol_t *itp = iptun->iptun_itp; 337311042SErik.Nordmark@Sun.COM int outer_hlen; 337411042SErik.Nordmark@Sun.COM mblk_t *newmp; 337511042SErik.Nordmark@Sun.COM ipha_t *outer4, *inner4; 337611042SErik.Nordmark@Sun.COM ip6_t *outer6, *inner6; 337711042SErik.Nordmark@Sun.COM int error; 337811042SErik.Nordmark@Sun.COM boolean_t update_pktlen; 337911042SErik.Nordmark@Sun.COM 338011042SErik.Nordmark@Sun.COM ASSERT(ixa->ixa_ire != NULL); 338111042SErik.Nordmark@Sun.COM 338211042SErik.Nordmark@Sun.COM outer_hlen = iptun_find_headers(mp, 0, &outer4, &inner4, &outer6, 338311042SErik.Nordmark@Sun.COM &inner6); 338410616SSebastien.Roy@Sun.COM if (outer_hlen == 0) { 338510616SSebastien.Roy@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_oerrors); 338610616SSebastien.Roy@Sun.COM return; 338710616SSebastien.Roy@Sun.COM } 338810616SSebastien.Roy@Sun.COM 338911305SPaul.Wernau@Sun.COM /* Save IXAF_DONTFRAG value */ 339011305SPaul.Wernau@Sun.COM iaflags_t dontfrag = ixa->ixa_flags & IXAF_DONTFRAG; 339111305SPaul.Wernau@Sun.COM 339210616SSebastien.Roy@Sun.COM /* Perform header processing. */ 339311042SErik.Nordmark@Sun.COM if (outer4 != NULL) { 339411042SErik.Nordmark@Sun.COM mp = iptun_out_process_ipv4(iptun, mp, outer4, inner4, inner6, 339511042SErik.Nordmark@Sun.COM ixa); 339611042SErik.Nordmark@Sun.COM } else { 339711042SErik.Nordmark@Sun.COM mp = iptun_out_process_ipv6(iptun, mp, outer6, inner4, inner6, 339811042SErik.Nordmark@Sun.COM ixa); 339911042SErik.Nordmark@Sun.COM } 340010616SSebastien.Roy@Sun.COM if (mp == NULL) 340110616SSebastien.Roy@Sun.COM return; 340210616SSebastien.Roy@Sun.COM 340310616SSebastien.Roy@Sun.COM /* 340410616SSebastien.Roy@Sun.COM * Let's hope the compiler optimizes this with "branch taken". 340510616SSebastien.Roy@Sun.COM */ 340610616SSebastien.Roy@Sun.COM if (itp != NULL && (itp->itp_flags & ITPF_P_ACTIVE)) { 340711042SErik.Nordmark@Sun.COM /* This updates the ip_xmit_attr_t */ 340811042SErik.Nordmark@Sun.COM mp = ipsec_tun_outbound(mp, iptun, inner4, inner6, outer4, 340911042SErik.Nordmark@Sun.COM outer6, outer_hlen, ixa); 341011042SErik.Nordmark@Sun.COM if (mp == NULL) { 341110616SSebastien.Roy@Sun.COM atomic_inc_64(&iptun->iptun_oerrors); 341210616SSebastien.Roy@Sun.COM return; 341310616SSebastien.Roy@Sun.COM } 341411042SErik.Nordmark@Sun.COM if (is_system_labeled()) { 341511042SErik.Nordmark@Sun.COM /* 341611042SErik.Nordmark@Sun.COM * Might change the packet by adding/removing CIPSO. 341711042SErik.Nordmark@Sun.COM * After this caller inner* and outer* and outer_hlen 341811042SErik.Nordmark@Sun.COM * might be invalid. 341911042SErik.Nordmark@Sun.COM */ 342011042SErik.Nordmark@Sun.COM error = iptun_output_check_label(&mp, ixa); 342111042SErik.Nordmark@Sun.COM if (error != 0) { 342211042SErik.Nordmark@Sun.COM ip2dbg(("label check failed (%d)\n", error)); 342311042SErik.Nordmark@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_oerrors); 342411042SErik.Nordmark@Sun.COM return; 342511042SErik.Nordmark@Sun.COM } 342611042SErik.Nordmark@Sun.COM } 342711042SErik.Nordmark@Sun.COM 342810616SSebastien.Roy@Sun.COM /* 342910616SSebastien.Roy@Sun.COM * ipsec_tun_outbound() returns a chain of tunneled IP 343010616SSebastien.Roy@Sun.COM * fragments linked with b_next (or a single message if the 343111042SErik.Nordmark@Sun.COM * tunneled packet wasn't a fragment). 343211042SErik.Nordmark@Sun.COM * If fragcache returned a list then we need to update 343311042SErik.Nordmark@Sun.COM * ixa_pktlen for all packets in the list. 343411042SErik.Nordmark@Sun.COM */ 343511042SErik.Nordmark@Sun.COM update_pktlen = (mp->b_next != NULL); 343611042SErik.Nordmark@Sun.COM 343711042SErik.Nordmark@Sun.COM /* 343811042SErik.Nordmark@Sun.COM * Otherwise, we're good to go. The ixa has been updated with 343910616SSebastien.Roy@Sun.COM * instructions for outbound IPsec processing. 344010616SSebastien.Roy@Sun.COM */ 344110616SSebastien.Roy@Sun.COM for (newmp = mp; newmp != NULL; newmp = mp) { 344211305SPaul.Wernau@Sun.COM size_t minmtu = iptun->iptun_typeinfo->iti_minmtu; 344311305SPaul.Wernau@Sun.COM 344410616SSebastien.Roy@Sun.COM atomic_inc_64(&iptun->iptun_opackets); 344511042SErik.Nordmark@Sun.COM atomic_add_64(&iptun->iptun_obytes, ixa->ixa_pktlen); 344610616SSebastien.Roy@Sun.COM mp = mp->b_next; 344710616SSebastien.Roy@Sun.COM newmp->b_next = NULL; 344811042SErik.Nordmark@Sun.COM 344911305SPaul.Wernau@Sun.COM /* 345011305SPaul.Wernau@Sun.COM * The IXAF_DONTFRAG flag is global, but there is 345111305SPaul.Wernau@Sun.COM * a chain here. Check if we're really already 345211305SPaul.Wernau@Sun.COM * smaller than the minimum allowed MTU and reset here 345311305SPaul.Wernau@Sun.COM * appropriately. Otherwise one small packet can kill 345411305SPaul.Wernau@Sun.COM * the whole chain's path mtu discovery. 345511305SPaul.Wernau@Sun.COM * In addition, update the pktlen to the length of 345611305SPaul.Wernau@Sun.COM * the actual packet being processed. 345711305SPaul.Wernau@Sun.COM */ 345811305SPaul.Wernau@Sun.COM if (update_pktlen) { 345911305SPaul.Wernau@Sun.COM ixa->ixa_pktlen = msgdsize(newmp); 346011305SPaul.Wernau@Sun.COM if (ixa->ixa_pktlen <= minmtu) 346111305SPaul.Wernau@Sun.COM ixa->ixa_flags &= ~IXAF_DONTFRAG; 346211305SPaul.Wernau@Sun.COM } 346311042SErik.Nordmark@Sun.COM 346411042SErik.Nordmark@Sun.COM atomic_inc_64(&iptun->iptun_opackets); 346511042SErik.Nordmark@Sun.COM atomic_add_64(&iptun->iptun_obytes, ixa->ixa_pktlen); 346611042SErik.Nordmark@Sun.COM 346711042SErik.Nordmark@Sun.COM error = conn_ip_output(newmp, ixa); 346811305SPaul.Wernau@Sun.COM 346911305SPaul.Wernau@Sun.COM /* Restore IXAF_DONTFRAG value */ 347011305SPaul.Wernau@Sun.COM ixa->ixa_flags |= dontfrag; 347111305SPaul.Wernau@Sun.COM 347211042SErik.Nordmark@Sun.COM if (error == EMSGSIZE) { 347311042SErik.Nordmark@Sun.COM /* IPsec policy might have changed */ 347411042SErik.Nordmark@Sun.COM (void) iptun_update_mtu(iptun, ixa, 0); 347511042SErik.Nordmark@Sun.COM } 347610616SSebastien.Roy@Sun.COM } 347710616SSebastien.Roy@Sun.COM } else { 347810616SSebastien.Roy@Sun.COM /* 347910616SSebastien.Roy@Sun.COM * The ip module will potentially apply global policy to the 348010616SSebastien.Roy@Sun.COM * packet in its output path if there's no active tunnel 348110616SSebastien.Roy@Sun.COM * policy. 348210616SSebastien.Roy@Sun.COM */ 348311042SErik.Nordmark@Sun.COM ASSERT(ixa->ixa_ipsec_policy == NULL); 348411042SErik.Nordmark@Sun.COM mp = ip_output_attach_policy(mp, outer4, outer6, NULL, ixa); 348511042SErik.Nordmark@Sun.COM if (mp == NULL) { 348611042SErik.Nordmark@Sun.COM atomic_inc_64(&iptun->iptun_oerrors); 348711042SErik.Nordmark@Sun.COM return; 348811042SErik.Nordmark@Sun.COM } 348911042SErik.Nordmark@Sun.COM if (is_system_labeled()) { 349011042SErik.Nordmark@Sun.COM /* 349111042SErik.Nordmark@Sun.COM * Might change the packet by adding/removing CIPSO. 349211042SErik.Nordmark@Sun.COM * After this caller inner* and outer* and outer_hlen 349311042SErik.Nordmark@Sun.COM * might be invalid. 349411042SErik.Nordmark@Sun.COM */ 349511042SErik.Nordmark@Sun.COM error = iptun_output_check_label(&mp, ixa); 349611042SErik.Nordmark@Sun.COM if (error != 0) { 349711042SErik.Nordmark@Sun.COM ip2dbg(("label check failed (%d)\n", error)); 349811042SErik.Nordmark@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_oerrors); 349911042SErik.Nordmark@Sun.COM return; 350011042SErik.Nordmark@Sun.COM } 350111042SErik.Nordmark@Sun.COM } 350211042SErik.Nordmark@Sun.COM 350310616SSebastien.Roy@Sun.COM atomic_inc_64(&iptun->iptun_opackets); 350411042SErik.Nordmark@Sun.COM atomic_add_64(&iptun->iptun_obytes, ixa->ixa_pktlen); 350511042SErik.Nordmark@Sun.COM 350611042SErik.Nordmark@Sun.COM error = conn_ip_output(mp, ixa); 350711042SErik.Nordmark@Sun.COM if (error == EMSGSIZE) { 350811042SErik.Nordmark@Sun.COM /* IPsec policy might have changed */ 350911042SErik.Nordmark@Sun.COM (void) iptun_update_mtu(iptun, ixa, 0); 351011042SErik.Nordmark@Sun.COM } 351110616SSebastien.Roy@Sun.COM } 351211042SErik.Nordmark@Sun.COM if (ixa->ixa_flags & IXAF_IPSEC_SECURE) 351311042SErik.Nordmark@Sun.COM ipsec_out_release_refs(ixa); 351410616SSebastien.Roy@Sun.COM } 351510616SSebastien.Roy@Sun.COM 351610616SSebastien.Roy@Sun.COM static mac_callbacks_t iptun_m_callbacks = { 351710616SSebastien.Roy@Sun.COM .mc_callbacks = (MC_SETPROP | MC_GETPROP), 351810616SSebastien.Roy@Sun.COM .mc_getstat = iptun_m_getstat, 351910616SSebastien.Roy@Sun.COM .mc_start = iptun_m_start, 352010616SSebastien.Roy@Sun.COM .mc_stop = iptun_m_stop, 352110616SSebastien.Roy@Sun.COM .mc_setpromisc = iptun_m_setpromisc, 352210616SSebastien.Roy@Sun.COM .mc_multicst = iptun_m_multicst, 352310616SSebastien.Roy@Sun.COM .mc_unicst = iptun_m_unicst, 352410616SSebastien.Roy@Sun.COM .mc_tx = iptun_m_tx, 352510616SSebastien.Roy@Sun.COM .mc_setprop = iptun_m_setprop, 352610616SSebastien.Roy@Sun.COM .mc_getprop = iptun_m_getprop 352710616SSebastien.Roy@Sun.COM }; 3528