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, 38211878SVenu.Iyer@Sun.COM uint_t pr_valsize, void *pr_val) 38310616SSebastien.Roy@Sun.COM { 38410616SSebastien.Roy@Sun.COM iptun_t *iptun = barg; 38510616SSebastien.Roy@Sun.COM int err; 38610616SSebastien.Roy@Sun.COM 38710616SSebastien.Roy@Sun.COM if ((err = iptun_enter(iptun)) != 0) 38810616SSebastien.Roy@Sun.COM return (err); 38910616SSebastien.Roy@Sun.COM 39011878SVenu.Iyer@Sun.COM switch (pr_num) { 39111878SVenu.Iyer@Sun.COM case MAC_PROP_IPTUN_HOPLIMIT: 39211878SVenu.Iyer@Sun.COM ASSERT(pr_valsize >= sizeof (uint32_t)); 39311878SVenu.Iyer@Sun.COM *(uint32_t *)pr_val = iptun->iptun_hoplimit; 39411878SVenu.Iyer@Sun.COM break; 39511878SVenu.Iyer@Sun.COM 39611878SVenu.Iyer@Sun.COM case MAC_PROP_IPTUN_ENCAPLIMIT: 39711878SVenu.Iyer@Sun.COM *(uint32_t *)pr_val = iptun->iptun_encaplimit; 39811878SVenu.Iyer@Sun.COM break; 39911878SVenu.Iyer@Sun.COM default: 40010616SSebastien.Roy@Sun.COM err = ENOTSUP; 40110616SSebastien.Roy@Sun.COM } 40211878SVenu.Iyer@Sun.COM done: 40311878SVenu.Iyer@Sun.COM iptun_exit(iptun); 40411878SVenu.Iyer@Sun.COM return (err); 40511878SVenu.Iyer@Sun.COM } 40611878SVenu.Iyer@Sun.COM 40711878SVenu.Iyer@Sun.COM /* ARGSUSED */ 40811878SVenu.Iyer@Sun.COM static void 40911878SVenu.Iyer@Sun.COM iptun_m_propinfo(void *barg, const char *pr_name, mac_prop_id_t pr_num, 41011878SVenu.Iyer@Sun.COM mac_prop_info_handle_t prh) 41111878SVenu.Iyer@Sun.COM { 41211878SVenu.Iyer@Sun.COM iptun_t *iptun = barg; 41310616SSebastien.Roy@Sun.COM 41410616SSebastien.Roy@Sun.COM switch (pr_num) { 41510616SSebastien.Roy@Sun.COM case MAC_PROP_IPTUN_HOPLIMIT: 41611878SVenu.Iyer@Sun.COM mac_prop_info_set_range_uint32(prh, 41711878SVenu.Iyer@Sun.COM IPTUN_MIN_HOPLIMIT, IPTUN_MAX_HOPLIMIT); 41811878SVenu.Iyer@Sun.COM mac_prop_info_set_default_uint32(prh, IPTUN_DEFAULT_HOPLIMIT); 41910616SSebastien.Roy@Sun.COM break; 42011878SVenu.Iyer@Sun.COM 42110616SSebastien.Roy@Sun.COM case MAC_PROP_IPTUN_ENCAPLIMIT: 42211878SVenu.Iyer@Sun.COM if (iptun->iptun_typeinfo->iti_type != IPTUN_TYPE_IPV6) 42311878SVenu.Iyer@Sun.COM break; 42411878SVenu.Iyer@Sun.COM mac_prop_info_set_range_uint32(prh, 42511878SVenu.Iyer@Sun.COM IPTUN_MIN_ENCAPLIMIT, IPTUN_MAX_ENCAPLIMIT); 42611878SVenu.Iyer@Sun.COM mac_prop_info_set_default_uint32(prh, IPTUN_DEFAULT_ENCAPLIMIT); 42710616SSebastien.Roy@Sun.COM break; 42811878SVenu.Iyer@Sun.COM case MAC_PROP_MTU: 42911878SVenu.Iyer@Sun.COM mac_prop_info_set_range_uint32(prh, 43011878SVenu.Iyer@Sun.COM iptun->iptun_typeinfo->iti_minmtu, 43111878SVenu.Iyer@Sun.COM iptun_get_maxmtu(iptun, NULL, 0)); 43210616SSebastien.Roy@Sun.COM break; 43310616SSebastien.Roy@Sun.COM } 43410616SSebastien.Roy@Sun.COM } 43510616SSebastien.Roy@Sun.COM 43610616SSebastien.Roy@Sun.COM uint_t 43710616SSebastien.Roy@Sun.COM iptun_count(void) 43810616SSebastien.Roy@Sun.COM { 43910616SSebastien.Roy@Sun.COM return (iptun_tunnelcount); 44010616SSebastien.Roy@Sun.COM } 44110616SSebastien.Roy@Sun.COM 44210616SSebastien.Roy@Sun.COM /* 44310616SSebastien.Roy@Sun.COM * Enter an iptun_t exclusively. This is essentially just a mutex, but we 44410616SSebastien.Roy@Sun.COM * don't allow iptun_enter() to succeed on a tunnel if it's in the process of 44510616SSebastien.Roy@Sun.COM * being deleted. 44610616SSebastien.Roy@Sun.COM */ 44710616SSebastien.Roy@Sun.COM static int 44810616SSebastien.Roy@Sun.COM iptun_enter(iptun_t *iptun) 44910616SSebastien.Roy@Sun.COM { 45010616SSebastien.Roy@Sun.COM mutex_enter(&iptun->iptun_lock); 45110616SSebastien.Roy@Sun.COM while (iptun->iptun_flags & IPTUN_DELETE_PENDING) 45210616SSebastien.Roy@Sun.COM cv_wait(&iptun->iptun_enter_cv, &iptun->iptun_lock); 45310616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_CONDEMNED) { 45410616SSebastien.Roy@Sun.COM mutex_exit(&iptun->iptun_lock); 45510616SSebastien.Roy@Sun.COM return (ENOENT); 45610616SSebastien.Roy@Sun.COM } 45710616SSebastien.Roy@Sun.COM return (0); 45810616SSebastien.Roy@Sun.COM } 45910616SSebastien.Roy@Sun.COM 46010616SSebastien.Roy@Sun.COM /* 46110616SSebastien.Roy@Sun.COM * Exit the tunnel entered in iptun_enter(). 46210616SSebastien.Roy@Sun.COM */ 46310616SSebastien.Roy@Sun.COM static void 46410616SSebastien.Roy@Sun.COM iptun_exit(iptun_t *iptun) 46510616SSebastien.Roy@Sun.COM { 46610616SSebastien.Roy@Sun.COM mutex_exit(&iptun->iptun_lock); 46710616SSebastien.Roy@Sun.COM } 46810616SSebastien.Roy@Sun.COM 46910616SSebastien.Roy@Sun.COM /* 47010616SSebastien.Roy@Sun.COM * Enter the IP tunnel instance by datalink ID. 47110616SSebastien.Roy@Sun.COM */ 47210616SSebastien.Roy@Sun.COM static int 47310616SSebastien.Roy@Sun.COM iptun_enter_by_linkid(datalink_id_t linkid, iptun_t **iptun) 47410616SSebastien.Roy@Sun.COM { 47510616SSebastien.Roy@Sun.COM int err; 47610616SSebastien.Roy@Sun.COM 47710616SSebastien.Roy@Sun.COM mutex_enter(&iptun_hash_lock); 47810616SSebastien.Roy@Sun.COM if (mod_hash_find(iptun_hash, IPTUN_HASH_KEY(linkid), 47910616SSebastien.Roy@Sun.COM (mod_hash_val_t *)iptun) == 0) 48010616SSebastien.Roy@Sun.COM err = iptun_enter(*iptun); 48110616SSebastien.Roy@Sun.COM else 48210616SSebastien.Roy@Sun.COM err = ENOENT; 48310616SSebastien.Roy@Sun.COM if (err != 0) 48410616SSebastien.Roy@Sun.COM *iptun = NULL; 48510616SSebastien.Roy@Sun.COM mutex_exit(&iptun_hash_lock); 48610616SSebastien.Roy@Sun.COM return (err); 48710616SSebastien.Roy@Sun.COM } 48810616SSebastien.Roy@Sun.COM 48910616SSebastien.Roy@Sun.COM /* 49011042SErik.Nordmark@Sun.COM * Handle tasks that were deferred through the iptun_taskq because they require 49111042SErik.Nordmark@Sun.COM * calling up to the mac module, and we can't call up to the mac module while 49211042SErik.Nordmark@Sun.COM * holding locks. 49310616SSebastien.Roy@Sun.COM * 49411042SErik.Nordmark@Sun.COM * This is tricky to get right without introducing race conditions and 49510616SSebastien.Roy@Sun.COM * deadlocks with the mac module, as we cannot issue an upcall while in the 49610616SSebastien.Roy@Sun.COM * iptun_t. The reason is that upcalls may try and enter the mac perimeter, 49710616SSebastien.Roy@Sun.COM * while iptun callbacks (such as iptun_m_setprop()) called from the mac 49810616SSebastien.Roy@Sun.COM * module will already have the perimeter held, and will then try and enter 49910616SSebastien.Roy@Sun.COM * the iptun_t. You can see the lock ordering problem with this; this will 50010616SSebastien.Roy@Sun.COM * deadlock. 50110616SSebastien.Roy@Sun.COM * 50210616SSebastien.Roy@Sun.COM * The safe way to do this is to enter the iptun_t in question and copy the 50310616SSebastien.Roy@Sun.COM * information we need out of it so that we can exit it and know that the 50410616SSebastien.Roy@Sun.COM * information being passed up to the upcalls won't be subject to modification 50510616SSebastien.Roy@Sun.COM * by other threads. The problem now is that we need to exit it prior to 50610616SSebastien.Roy@Sun.COM * issuing the upcall, but once we do this, a thread could come along and 50710616SSebastien.Roy@Sun.COM * delete the iptun_t and thus the mac handle required to issue the upcall. 50810616SSebastien.Roy@Sun.COM * To prevent this, we set the IPTUN_UPCALL_PENDING flag prior to exiting the 50910616SSebastien.Roy@Sun.COM * iptun_t. This flag is the condition associated with iptun_upcall_cv, which 51010616SSebastien.Roy@Sun.COM * iptun_delete() will cv_wait() on. When the upcall completes, we clear 51110616SSebastien.Roy@Sun.COM * IPTUN_UPCALL_PENDING and cv_signal() any potentially waiting 51210616SSebastien.Roy@Sun.COM * iptun_delete(). We can thus still safely use iptun->iptun_mh after having 51310616SSebastien.Roy@Sun.COM * exited the iptun_t. 51410616SSebastien.Roy@Sun.COM */ 51510616SSebastien.Roy@Sun.COM static void 51610616SSebastien.Roy@Sun.COM iptun_task_cb(void *arg) 51710616SSebastien.Roy@Sun.COM { 51810616SSebastien.Roy@Sun.COM iptun_task_data_t *itd = arg; 51910616SSebastien.Roy@Sun.COM iptun_task_t task = itd->itd_task; 52010616SSebastien.Roy@Sun.COM datalink_id_t linkid = itd->itd_linkid; 52110616SSebastien.Roy@Sun.COM iptun_t *iptun; 52210616SSebastien.Roy@Sun.COM uint32_t mtu; 52310616SSebastien.Roy@Sun.COM iptun_addr_t addr; 52410616SSebastien.Roy@Sun.COM link_state_t linkstate; 52510616SSebastien.Roy@Sun.COM size_t header_size; 52610616SSebastien.Roy@Sun.COM iptun_header_t header; 52710616SSebastien.Roy@Sun.COM 52810616SSebastien.Roy@Sun.COM kmem_free(itd, sizeof (*itd)); 52910616SSebastien.Roy@Sun.COM 53010616SSebastien.Roy@Sun.COM /* 53110616SSebastien.Roy@Sun.COM * Note that if the lookup fails, it's because the tunnel was deleted 53210616SSebastien.Roy@Sun.COM * between the time the task was dispatched and now. That isn't an 53310616SSebastien.Roy@Sun.COM * error. 53410616SSebastien.Roy@Sun.COM */ 53510616SSebastien.Roy@Sun.COM if (iptun_enter_by_linkid(linkid, &iptun) != 0) 53610616SSebastien.Roy@Sun.COM return; 53710616SSebastien.Roy@Sun.COM 53810616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_UPCALL_PENDING; 53910616SSebastien.Roy@Sun.COM 54010616SSebastien.Roy@Sun.COM switch (task) { 54110616SSebastien.Roy@Sun.COM case IPTUN_TASK_MTU_UPDATE: 54210616SSebastien.Roy@Sun.COM mtu = iptun->iptun_mtu; 54310616SSebastien.Roy@Sun.COM break; 54410616SSebastien.Roy@Sun.COM case IPTUN_TASK_LADDR_UPDATE: 54510616SSebastien.Roy@Sun.COM addr = iptun->iptun_laddr; 54610616SSebastien.Roy@Sun.COM break; 54710616SSebastien.Roy@Sun.COM case IPTUN_TASK_RADDR_UPDATE: 54810616SSebastien.Roy@Sun.COM addr = iptun->iptun_raddr; 54910616SSebastien.Roy@Sun.COM break; 55010616SSebastien.Roy@Sun.COM case IPTUN_TASK_LINK_UPDATE: 55110616SSebastien.Roy@Sun.COM linkstate = IS_IPTUN_RUNNING(iptun) ? 55210616SSebastien.Roy@Sun.COM LINK_STATE_UP : LINK_STATE_DOWN; 55310616SSebastien.Roy@Sun.COM break; 55410616SSebastien.Roy@Sun.COM case IPTUN_TASK_PDATA_UPDATE: 55510616SSebastien.Roy@Sun.COM header_size = iptun->iptun_header_size; 55610616SSebastien.Roy@Sun.COM header = iptun->iptun_header; 55710616SSebastien.Roy@Sun.COM break; 55810616SSebastien.Roy@Sun.COM default: 55910616SSebastien.Roy@Sun.COM ASSERT(0); 56010616SSebastien.Roy@Sun.COM } 56110616SSebastien.Roy@Sun.COM 56210616SSebastien.Roy@Sun.COM iptun_exit(iptun); 56310616SSebastien.Roy@Sun.COM 56410616SSebastien.Roy@Sun.COM switch (task) { 56510616SSebastien.Roy@Sun.COM case IPTUN_TASK_MTU_UPDATE: 56610616SSebastien.Roy@Sun.COM (void) mac_maxsdu_update(iptun->iptun_mh, mtu); 56710616SSebastien.Roy@Sun.COM break; 56810616SSebastien.Roy@Sun.COM case IPTUN_TASK_LADDR_UPDATE: 56910616SSebastien.Roy@Sun.COM mac_unicst_update(iptun->iptun_mh, (uint8_t *)&addr.ia_addr); 57010616SSebastien.Roy@Sun.COM break; 57110616SSebastien.Roy@Sun.COM case IPTUN_TASK_RADDR_UPDATE: 57210616SSebastien.Roy@Sun.COM mac_dst_update(iptun->iptun_mh, (uint8_t *)&addr.ia_addr); 57310616SSebastien.Roy@Sun.COM break; 57410616SSebastien.Roy@Sun.COM case IPTUN_TASK_LINK_UPDATE: 57510616SSebastien.Roy@Sun.COM mac_link_update(iptun->iptun_mh, linkstate); 57610616SSebastien.Roy@Sun.COM break; 57710616SSebastien.Roy@Sun.COM case IPTUN_TASK_PDATA_UPDATE: 57810616SSebastien.Roy@Sun.COM if (mac_pdata_update(iptun->iptun_mh, 57910616SSebastien.Roy@Sun.COM header_size == 0 ? NULL : &header, header_size) != 0) 58010616SSebastien.Roy@Sun.COM atomic_inc_64(&iptun->iptun_taskq_fail); 58110616SSebastien.Roy@Sun.COM break; 58210616SSebastien.Roy@Sun.COM } 58310616SSebastien.Roy@Sun.COM 58410616SSebastien.Roy@Sun.COM mutex_enter(&iptun->iptun_lock); 58510616SSebastien.Roy@Sun.COM iptun->iptun_flags &= ~IPTUN_UPCALL_PENDING; 58610616SSebastien.Roy@Sun.COM cv_signal(&iptun->iptun_upcall_cv); 58710616SSebastien.Roy@Sun.COM mutex_exit(&iptun->iptun_lock); 58810616SSebastien.Roy@Sun.COM } 58910616SSebastien.Roy@Sun.COM 59010616SSebastien.Roy@Sun.COM static void 59110616SSebastien.Roy@Sun.COM iptun_task_dispatch(iptun_t *iptun, iptun_task_t iptun_task) 59210616SSebastien.Roy@Sun.COM { 59310616SSebastien.Roy@Sun.COM iptun_task_data_t *itd; 59410616SSebastien.Roy@Sun.COM 59510616SSebastien.Roy@Sun.COM itd = kmem_alloc(sizeof (*itd), KM_NOSLEEP); 59610616SSebastien.Roy@Sun.COM if (itd == NULL) { 59710616SSebastien.Roy@Sun.COM atomic_inc_64(&iptun->iptun_taskq_fail); 59810616SSebastien.Roy@Sun.COM return; 59910616SSebastien.Roy@Sun.COM } 60010616SSebastien.Roy@Sun.COM itd->itd_task = iptun_task; 60110616SSebastien.Roy@Sun.COM itd->itd_linkid = iptun->iptun_linkid; 60210616SSebastien.Roy@Sun.COM if (ddi_taskq_dispatch(iptun_taskq, iptun_task_cb, itd, DDI_NOSLEEP)) { 60310616SSebastien.Roy@Sun.COM atomic_inc_64(&iptun->iptun_taskq_fail); 60410616SSebastien.Roy@Sun.COM kmem_free(itd, sizeof (*itd)); 60510616SSebastien.Roy@Sun.COM } 60610616SSebastien.Roy@Sun.COM } 60710616SSebastien.Roy@Sun.COM 60810616SSebastien.Roy@Sun.COM /* 60910616SSebastien.Roy@Sun.COM * Convert an iptun_addr_t to sockaddr_storage. 61010616SSebastien.Roy@Sun.COM */ 61110616SSebastien.Roy@Sun.COM static void 61210616SSebastien.Roy@Sun.COM iptun_getaddr(iptun_addr_t *iptun_addr, struct sockaddr_storage *ss) 61310616SSebastien.Roy@Sun.COM { 61410616SSebastien.Roy@Sun.COM struct sockaddr_in *sin; 61510616SSebastien.Roy@Sun.COM struct sockaddr_in6 *sin6; 61610616SSebastien.Roy@Sun.COM 61710616SSebastien.Roy@Sun.COM bzero(ss, sizeof (*ss)); 61810616SSebastien.Roy@Sun.COM switch (iptun_addr->ia_family) { 61910616SSebastien.Roy@Sun.COM case AF_INET: 62010616SSebastien.Roy@Sun.COM sin = (struct sockaddr_in *)ss; 62110616SSebastien.Roy@Sun.COM sin->sin_addr.s_addr = iptun_addr->ia_addr.iau_addr4; 62210616SSebastien.Roy@Sun.COM break; 62310616SSebastien.Roy@Sun.COM case AF_INET6: 62410616SSebastien.Roy@Sun.COM sin6 = (struct sockaddr_in6 *)ss; 62510616SSebastien.Roy@Sun.COM sin6->sin6_addr = iptun_addr->ia_addr.iau_addr6; 62610616SSebastien.Roy@Sun.COM break; 62710616SSebastien.Roy@Sun.COM default: 62810616SSebastien.Roy@Sun.COM ASSERT(0); 62910616SSebastien.Roy@Sun.COM } 63010616SSebastien.Roy@Sun.COM ss->ss_family = iptun_addr->ia_family; 63110616SSebastien.Roy@Sun.COM } 63210616SSebastien.Roy@Sun.COM 63310616SSebastien.Roy@Sun.COM /* 63410616SSebastien.Roy@Sun.COM * General purpose function to set an IP tunnel source or destination address. 63510616SSebastien.Roy@Sun.COM */ 63610616SSebastien.Roy@Sun.COM static int 63710616SSebastien.Roy@Sun.COM iptun_setaddr(iptun_type_t iptun_type, iptun_addr_t *iptun_addr, 63810616SSebastien.Roy@Sun.COM const struct sockaddr_storage *ss) 63910616SSebastien.Roy@Sun.COM { 64010616SSebastien.Roy@Sun.COM if (!IPTUN_ADDR_MATCH(iptun_type, ss->ss_family)) 64110616SSebastien.Roy@Sun.COM return (EINVAL); 64210616SSebastien.Roy@Sun.COM 64310616SSebastien.Roy@Sun.COM switch (ss->ss_family) { 64410616SSebastien.Roy@Sun.COM case AF_INET: { 64510616SSebastien.Roy@Sun.COM struct sockaddr_in *sin = (struct sockaddr_in *)ss; 64610616SSebastien.Roy@Sun.COM 64710616SSebastien.Roy@Sun.COM if ((sin->sin_addr.s_addr == INADDR_ANY) || 64810616SSebastien.Roy@Sun.COM (sin->sin_addr.s_addr == INADDR_BROADCAST) || 64910616SSebastien.Roy@Sun.COM CLASSD(sin->sin_addr.s_addr)) { 65010616SSebastien.Roy@Sun.COM return (EADDRNOTAVAIL); 65110616SSebastien.Roy@Sun.COM } 65210616SSebastien.Roy@Sun.COM iptun_addr->ia_addr.iau_addr4 = sin->sin_addr.s_addr; 65310616SSebastien.Roy@Sun.COM break; 65410616SSebastien.Roy@Sun.COM } 65510616SSebastien.Roy@Sun.COM case AF_INET6: { 65610616SSebastien.Roy@Sun.COM struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ss; 65710616SSebastien.Roy@Sun.COM 65810616SSebastien.Roy@Sun.COM if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) || 65910616SSebastien.Roy@Sun.COM IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr) || 66010616SSebastien.Roy@Sun.COM IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { 66110616SSebastien.Roy@Sun.COM return (EADDRNOTAVAIL); 66210616SSebastien.Roy@Sun.COM } 66310616SSebastien.Roy@Sun.COM iptun_addr->ia_addr.iau_addr6 = sin6->sin6_addr; 66410616SSebastien.Roy@Sun.COM break; 66510616SSebastien.Roy@Sun.COM } 66610616SSebastien.Roy@Sun.COM default: 66710616SSebastien.Roy@Sun.COM return (EAFNOSUPPORT); 66810616SSebastien.Roy@Sun.COM } 66910616SSebastien.Roy@Sun.COM iptun_addr->ia_family = ss->ss_family; 67010616SSebastien.Roy@Sun.COM return (0); 67110616SSebastien.Roy@Sun.COM } 67210616SSebastien.Roy@Sun.COM 67310616SSebastien.Roy@Sun.COM static int 67410616SSebastien.Roy@Sun.COM iptun_setladdr(iptun_t *iptun, const struct sockaddr_storage *laddr) 67510616SSebastien.Roy@Sun.COM { 67610616SSebastien.Roy@Sun.COM return (iptun_setaddr(iptun->iptun_typeinfo->iti_type, 67710616SSebastien.Roy@Sun.COM &iptun->iptun_laddr, laddr)); 67810616SSebastien.Roy@Sun.COM } 67910616SSebastien.Roy@Sun.COM 68010616SSebastien.Roy@Sun.COM static int 68110616SSebastien.Roy@Sun.COM iptun_setraddr(iptun_t *iptun, const struct sockaddr_storage *raddr) 68210616SSebastien.Roy@Sun.COM { 68310616SSebastien.Roy@Sun.COM if (!(iptun->iptun_typeinfo->iti_hasraddr)) 68410616SSebastien.Roy@Sun.COM return (EINVAL); 68510616SSebastien.Roy@Sun.COM return (iptun_setaddr(iptun->iptun_typeinfo->iti_type, 68610616SSebastien.Roy@Sun.COM &iptun->iptun_raddr, raddr)); 68710616SSebastien.Roy@Sun.COM } 68810616SSebastien.Roy@Sun.COM 68910616SSebastien.Roy@Sun.COM static boolean_t 69010616SSebastien.Roy@Sun.COM iptun_canbind(iptun_t *iptun) 69110616SSebastien.Roy@Sun.COM { 69210616SSebastien.Roy@Sun.COM /* 69310616SSebastien.Roy@Sun.COM * A tunnel may bind when its source address has been set, and if its 69410616SSebastien.Roy@Sun.COM * tunnel type requires one, also its destination address. 69510616SSebastien.Roy@Sun.COM */ 69610616SSebastien.Roy@Sun.COM return ((iptun->iptun_flags & IPTUN_LADDR) && 69710616SSebastien.Roy@Sun.COM ((iptun->iptun_flags & IPTUN_RADDR) || 69810616SSebastien.Roy@Sun.COM !(iptun->iptun_typeinfo->iti_hasraddr))); 69910616SSebastien.Roy@Sun.COM } 70010616SSebastien.Roy@Sun.COM 70111042SErik.Nordmark@Sun.COM /* 70211042SErik.Nordmark@Sun.COM * Verify that the local address is valid, and insert in the fanout 70311042SErik.Nordmark@Sun.COM */ 70410616SSebastien.Roy@Sun.COM static int 70510616SSebastien.Roy@Sun.COM iptun_bind(iptun_t *iptun) 70610616SSebastien.Roy@Sun.COM { 70711042SErik.Nordmark@Sun.COM conn_t *connp = iptun->iptun_connp; 70811042SErik.Nordmark@Sun.COM int error = 0; 70911042SErik.Nordmark@Sun.COM ip_xmit_attr_t *ixa; 71011042SErik.Nordmark@Sun.COM iulp_t uinfo; 71111042SErik.Nordmark@Sun.COM ip_stack_t *ipst = connp->conn_netstack->netstack_ip; 71211042SErik.Nordmark@Sun.COM 71311042SErik.Nordmark@Sun.COM /* Get an exclusive ixa for this thread, and replace conn_ixa */ 71411042SErik.Nordmark@Sun.COM ixa = conn_get_ixa(connp, B_TRUE); 71511042SErik.Nordmark@Sun.COM if (ixa == NULL) 71611042SErik.Nordmark@Sun.COM return (ENOMEM); 71711042SErik.Nordmark@Sun.COM ASSERT(ixa->ixa_refcnt >= 2); 71811042SErik.Nordmark@Sun.COM ASSERT(ixa == connp->conn_ixa); 71911042SErik.Nordmark@Sun.COM 72011042SErik.Nordmark@Sun.COM /* We create PMTU state including for 6to4 */ 72111042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_PMTU_DISCOVERY; 72210616SSebastien.Roy@Sun.COM 72310616SSebastien.Roy@Sun.COM ASSERT(iptun_canbind(iptun)); 72410616SSebastien.Roy@Sun.COM 72511042SErik.Nordmark@Sun.COM mutex_enter(&connp->conn_lock); 72611042SErik.Nordmark@Sun.COM /* 72711042SErik.Nordmark@Sun.COM * Note that conn_proto can't be set since the upper protocol 72811042SErik.Nordmark@Sun.COM * can be both 41 and 4 when IPv6 and IPv4 are over the same tunnel. 72911042SErik.Nordmark@Sun.COM * ipcl_iptun_classify doesn't use conn_proto. 73011042SErik.Nordmark@Sun.COM */ 73111042SErik.Nordmark@Sun.COM connp->conn_ipversion = iptun->iptun_typeinfo->iti_ipvers; 73211042SErik.Nordmark@Sun.COM 73310616SSebastien.Roy@Sun.COM switch (iptun->iptun_typeinfo->iti_type) { 73410616SSebastien.Roy@Sun.COM case IPTUN_TYPE_IPV4: 73511042SErik.Nordmark@Sun.COM IN6_IPADDR_TO_V4MAPPED(iptun->iptun_laddr4, 73611042SErik.Nordmark@Sun.COM &connp->conn_laddr_v6); 73711042SErik.Nordmark@Sun.COM IN6_IPADDR_TO_V4MAPPED(iptun->iptun_raddr4, 73811042SErik.Nordmark@Sun.COM &connp->conn_faddr_v6); 73911042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_IS_IPV4; 74011042SErik.Nordmark@Sun.COM if (ip_laddr_verify_v4(iptun->iptun_laddr4, IPCL_ZONEID(connp), 74111042SErik.Nordmark@Sun.COM ipst, B_FALSE) != IPVL_UNICAST_UP) { 74211042SErik.Nordmark@Sun.COM mutex_exit(&connp->conn_lock); 74311042SErik.Nordmark@Sun.COM error = EADDRNOTAVAIL; 74411042SErik.Nordmark@Sun.COM goto done; 74511042SErik.Nordmark@Sun.COM } 74610616SSebastien.Roy@Sun.COM break; 74710616SSebastien.Roy@Sun.COM case IPTUN_TYPE_IPV6: 74811042SErik.Nordmark@Sun.COM connp->conn_laddr_v6 = iptun->iptun_laddr6; 74911042SErik.Nordmark@Sun.COM connp->conn_faddr_v6 = iptun->iptun_raddr6; 75011042SErik.Nordmark@Sun.COM ixa->ixa_flags &= ~IXAF_IS_IPV4; 75111042SErik.Nordmark@Sun.COM /* We use a zero scopeid for now */ 75211042SErik.Nordmark@Sun.COM if (ip_laddr_verify_v6(&iptun->iptun_laddr6, IPCL_ZONEID(connp), 75311042SErik.Nordmark@Sun.COM ipst, B_FALSE, 0) != IPVL_UNICAST_UP) { 75411042SErik.Nordmark@Sun.COM mutex_exit(&connp->conn_lock); 75511042SErik.Nordmark@Sun.COM error = EADDRNOTAVAIL; 75611042SErik.Nordmark@Sun.COM goto done; 75711042SErik.Nordmark@Sun.COM } 75810616SSebastien.Roy@Sun.COM break; 75910616SSebastien.Roy@Sun.COM case IPTUN_TYPE_6TO4: 76011042SErik.Nordmark@Sun.COM IN6_IPADDR_TO_V4MAPPED(iptun->iptun_laddr4, 76111042SErik.Nordmark@Sun.COM &connp->conn_laddr_v6); 76211042SErik.Nordmark@Sun.COM IN6_IPADDR_TO_V4MAPPED(INADDR_ANY, &connp->conn_faddr_v6); 76311042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_IS_IPV4; 76411042SErik.Nordmark@Sun.COM mutex_exit(&connp->conn_lock); 76511042SErik.Nordmark@Sun.COM 76611042SErik.Nordmark@Sun.COM switch (ip_laddr_verify_v4(iptun->iptun_laddr4, 76711042SErik.Nordmark@Sun.COM IPCL_ZONEID(connp), ipst, B_FALSE)) { 76811042SErik.Nordmark@Sun.COM case IPVL_UNICAST_UP: 76911042SErik.Nordmark@Sun.COM case IPVL_UNICAST_DOWN: 77011042SErik.Nordmark@Sun.COM break; 77111042SErik.Nordmark@Sun.COM default: 77211042SErik.Nordmark@Sun.COM error = EADDRNOTAVAIL; 77311042SErik.Nordmark@Sun.COM goto done; 77411042SErik.Nordmark@Sun.COM } 77511042SErik.Nordmark@Sun.COM goto insert; 77610616SSebastien.Roy@Sun.COM } 77710616SSebastien.Roy@Sun.COM 77811042SErik.Nordmark@Sun.COM /* In case previous destination was multirt */ 77911042SErik.Nordmark@Sun.COM ip_attr_newdst(ixa); 78011042SErik.Nordmark@Sun.COM 78111042SErik.Nordmark@Sun.COM /* 78211042SErik.Nordmark@Sun.COM * When we set a tunnel's destination address, we do not 78311042SErik.Nordmark@Sun.COM * care if the destination is reachable. Transient routing 78411042SErik.Nordmark@Sun.COM * issues should not inhibit the creation of a tunnel 78511042SErik.Nordmark@Sun.COM * interface, for example. Thus we pass B_FALSE here. 78611042SErik.Nordmark@Sun.COM */ 78711042SErik.Nordmark@Sun.COM connp->conn_saddr_v6 = connp->conn_laddr_v6; 78811042SErik.Nordmark@Sun.COM mutex_exit(&connp->conn_lock); 78911042SErik.Nordmark@Sun.COM 79011042SErik.Nordmark@Sun.COM /* As long as the MTU is large we avoid fragmentation */ 79111042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_DONTFRAG | IXAF_PMTU_IPV4_DF; 79211042SErik.Nordmark@Sun.COM 79311042SErik.Nordmark@Sun.COM /* We handle IPsec in iptun_output_common */ 79411042SErik.Nordmark@Sun.COM error = ip_attr_connect(connp, ixa, &connp->conn_saddr_v6, 79511042SErik.Nordmark@Sun.COM &connp->conn_faddr_v6, &connp->conn_faddr_v6, 0, 79611042SErik.Nordmark@Sun.COM &connp->conn_saddr_v6, &uinfo, 0); 79711042SErik.Nordmark@Sun.COM 79811042SErik.Nordmark@Sun.COM if (error != 0) 79911042SErik.Nordmark@Sun.COM goto done; 80011042SErik.Nordmark@Sun.COM 80111042SErik.Nordmark@Sun.COM /* saddr shouldn't change since it was already set */ 80211042SErik.Nordmark@Sun.COM ASSERT(IN6_ARE_ADDR_EQUAL(&connp->conn_laddr_v6, 80311042SErik.Nordmark@Sun.COM &connp->conn_saddr_v6)); 80411042SErik.Nordmark@Sun.COM 80511042SErik.Nordmark@Sun.COM /* We set IXAF_VERIFY_PMTU to catch PMTU increases */ 80611042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_VERIFY_PMTU; 80711042SErik.Nordmark@Sun.COM ASSERT(uinfo.iulp_mtu != 0); 80811042SErik.Nordmark@Sun.COM 80911042SErik.Nordmark@Sun.COM /* 81011042SErik.Nordmark@Sun.COM * Allow setting new policies. 81111042SErik.Nordmark@Sun.COM * The addresses/ports are already set, thus the IPsec policy calls 81211042SErik.Nordmark@Sun.COM * can handle their passed-in conn's. 81311042SErik.Nordmark@Sun.COM */ 81411042SErik.Nordmark@Sun.COM connp->conn_policy_cached = B_FALSE; 81511042SErik.Nordmark@Sun.COM 81611042SErik.Nordmark@Sun.COM insert: 81711042SErik.Nordmark@Sun.COM error = ipcl_conn_insert(connp); 81811042SErik.Nordmark@Sun.COM if (error != 0) 81911042SErik.Nordmark@Sun.COM goto done; 82011042SErik.Nordmark@Sun.COM 82111042SErik.Nordmark@Sun.COM /* Record this as the "last" send even though we haven't sent any */ 82211042SErik.Nordmark@Sun.COM connp->conn_v6lastdst = connp->conn_faddr_v6; 82311042SErik.Nordmark@Sun.COM 82411042SErik.Nordmark@Sun.COM iptun->iptun_flags |= IPTUN_BOUND; 82511042SErik.Nordmark@Sun.COM /* 82611042SErik.Nordmark@Sun.COM * Now that we're bound with ip below us, this is a good 82711042SErik.Nordmark@Sun.COM * time to initialize the destination path MTU and to 82811042SErik.Nordmark@Sun.COM * re-calculate the tunnel's link MTU. 82911042SErik.Nordmark@Sun.COM */ 83011042SErik.Nordmark@Sun.COM (void) iptun_update_mtu(iptun, ixa, 0); 83111042SErik.Nordmark@Sun.COM 83211042SErik.Nordmark@Sun.COM if (IS_IPTUN_RUNNING(iptun)) 83311042SErik.Nordmark@Sun.COM iptun_task_dispatch(iptun, IPTUN_TASK_LINK_UPDATE); 83411042SErik.Nordmark@Sun.COM 83511042SErik.Nordmark@Sun.COM done: 83611042SErik.Nordmark@Sun.COM ixa_refrele(ixa); 83711042SErik.Nordmark@Sun.COM return (error); 83810616SSebastien.Roy@Sun.COM } 83910616SSebastien.Roy@Sun.COM 84010616SSebastien.Roy@Sun.COM static void 84110616SSebastien.Roy@Sun.COM iptun_unbind(iptun_t *iptun) 84210616SSebastien.Roy@Sun.COM { 84310616SSebastien.Roy@Sun.COM ASSERT(iptun->iptun_flags & IPTUN_BOUND); 84410616SSebastien.Roy@Sun.COM ASSERT(mutex_owned(&iptun->iptun_lock) || 84510616SSebastien.Roy@Sun.COM (iptun->iptun_flags & IPTUN_CONDEMNED)); 84610616SSebastien.Roy@Sun.COM ip_unbind(iptun->iptun_connp); 84710616SSebastien.Roy@Sun.COM iptun->iptun_flags &= ~IPTUN_BOUND; 84810616SSebastien.Roy@Sun.COM if (!(iptun->iptun_flags & IPTUN_CONDEMNED)) 84910616SSebastien.Roy@Sun.COM iptun_task_dispatch(iptun, IPTUN_TASK_LINK_UPDATE); 85010616SSebastien.Roy@Sun.COM } 85110616SSebastien.Roy@Sun.COM 85210616SSebastien.Roy@Sun.COM /* 85310616SSebastien.Roy@Sun.COM * Re-generate the template data-link header for a given IP tunnel given the 85410616SSebastien.Roy@Sun.COM * tunnel's current parameters. 85510616SSebastien.Roy@Sun.COM */ 85610616SSebastien.Roy@Sun.COM static void 85710616SSebastien.Roy@Sun.COM iptun_headergen(iptun_t *iptun, boolean_t update_mac) 85810616SSebastien.Roy@Sun.COM { 85910616SSebastien.Roy@Sun.COM switch (iptun->iptun_typeinfo->iti_ipvers) { 86010616SSebastien.Roy@Sun.COM case IPV4_VERSION: 86110616SSebastien.Roy@Sun.COM /* 86210616SSebastien.Roy@Sun.COM * We only need to use a custom IP header if the administrator 86310616SSebastien.Roy@Sun.COM * has supplied a non-default hoplimit. 86410616SSebastien.Roy@Sun.COM */ 86510616SSebastien.Roy@Sun.COM if (iptun->iptun_hoplimit == IPTUN_DEFAULT_HOPLIMIT) { 86610616SSebastien.Roy@Sun.COM iptun->iptun_header_size = 0; 86710616SSebastien.Roy@Sun.COM break; 86810616SSebastien.Roy@Sun.COM } 86910616SSebastien.Roy@Sun.COM iptun->iptun_header_size = sizeof (ipha_t); 87010616SSebastien.Roy@Sun.COM iptun->iptun_header4.ipha_version_and_hdr_length = 87110616SSebastien.Roy@Sun.COM IP_SIMPLE_HDR_VERSION; 87210616SSebastien.Roy@Sun.COM iptun->iptun_header4.ipha_fragment_offset_and_flags = 87310616SSebastien.Roy@Sun.COM htons(IPH_DF); 87410616SSebastien.Roy@Sun.COM iptun->iptun_header4.ipha_ttl = iptun->iptun_hoplimit; 87510616SSebastien.Roy@Sun.COM break; 87610616SSebastien.Roy@Sun.COM case IPV6_VERSION: { 87710616SSebastien.Roy@Sun.COM ip6_t *ip6hp = &iptun->iptun_header6.it6h_ip6h; 87810616SSebastien.Roy@Sun.COM 87910616SSebastien.Roy@Sun.COM /* 88010616SSebastien.Roy@Sun.COM * We only need to use a custom IPv6 header if either the 88110616SSebastien.Roy@Sun.COM * administrator has supplied a non-default hoplimit, or we 88210616SSebastien.Roy@Sun.COM * need to include an encapsulation limit option in the outer 88310616SSebastien.Roy@Sun.COM * header. 88410616SSebastien.Roy@Sun.COM */ 88510616SSebastien.Roy@Sun.COM if (iptun->iptun_hoplimit == IPTUN_DEFAULT_HOPLIMIT && 88610616SSebastien.Roy@Sun.COM iptun->iptun_encaplimit == 0) { 88710616SSebastien.Roy@Sun.COM iptun->iptun_header_size = 0; 88810616SSebastien.Roy@Sun.COM break; 88910616SSebastien.Roy@Sun.COM } 89010616SSebastien.Roy@Sun.COM 89110616SSebastien.Roy@Sun.COM (void) memset(ip6hp, 0, sizeof (*ip6hp)); 89210616SSebastien.Roy@Sun.COM if (iptun->iptun_encaplimit == 0) { 89310616SSebastien.Roy@Sun.COM iptun->iptun_header_size = sizeof (ip6_t); 89410616SSebastien.Roy@Sun.COM ip6hp->ip6_nxt = IPPROTO_NONE; 89510616SSebastien.Roy@Sun.COM } else { 89610616SSebastien.Roy@Sun.COM iptun_encaplim_t *iel; 89710616SSebastien.Roy@Sun.COM 89810616SSebastien.Roy@Sun.COM iptun->iptun_header_size = sizeof (iptun_ipv6hdrs_t); 89910616SSebastien.Roy@Sun.COM /* 90010616SSebastien.Roy@Sun.COM * The mac_ipv6 plugin requires ip6_plen to be in host 90110616SSebastien.Roy@Sun.COM * byte order and reflect the extension headers 90210616SSebastien.Roy@Sun.COM * present in the template. The actual network byte 90310616SSebastien.Roy@Sun.COM * order ip6_plen will be set on a per-packet basis on 90410616SSebastien.Roy@Sun.COM * transmit. 90510616SSebastien.Roy@Sun.COM */ 90610616SSebastien.Roy@Sun.COM ip6hp->ip6_plen = sizeof (*iel); 90710616SSebastien.Roy@Sun.COM ip6hp->ip6_nxt = IPPROTO_DSTOPTS; 90810616SSebastien.Roy@Sun.COM iel = &iptun->iptun_header6.it6h_encaplim; 90910616SSebastien.Roy@Sun.COM *iel = iptun_encaplim_init; 91010616SSebastien.Roy@Sun.COM iel->iel_telopt.ip6ot_encap_limit = 91110616SSebastien.Roy@Sun.COM iptun->iptun_encaplimit; 91210616SSebastien.Roy@Sun.COM } 91310616SSebastien.Roy@Sun.COM 91410616SSebastien.Roy@Sun.COM ip6hp->ip6_hlim = iptun->iptun_hoplimit; 91510616SSebastien.Roy@Sun.COM break; 91610616SSebastien.Roy@Sun.COM } 91710616SSebastien.Roy@Sun.COM } 91810616SSebastien.Roy@Sun.COM 91910616SSebastien.Roy@Sun.COM if (update_mac) 92010616SSebastien.Roy@Sun.COM iptun_task_dispatch(iptun, IPTUN_TASK_PDATA_UPDATE); 92110616SSebastien.Roy@Sun.COM } 92210616SSebastien.Roy@Sun.COM 92310616SSebastien.Roy@Sun.COM /* 92410616SSebastien.Roy@Sun.COM * Insert inbound and outbound IPv4 and IPv6 policy into the given policy 92510616SSebastien.Roy@Sun.COM * head. 92610616SSebastien.Roy@Sun.COM */ 92710616SSebastien.Roy@Sun.COM static boolean_t 92810616SSebastien.Roy@Sun.COM iptun_insert_simple_policies(ipsec_policy_head_t *ph, ipsec_act_t *actp, 92910616SSebastien.Roy@Sun.COM uint_t n, netstack_t *ns) 93010616SSebastien.Roy@Sun.COM { 93110616SSebastien.Roy@Sun.COM int f = IPSEC_AF_V4; 93210616SSebastien.Roy@Sun.COM 93310616SSebastien.Roy@Sun.COM if (!ipsec_polhead_insert(ph, actp, n, f, IPSEC_TYPE_INBOUND, ns) || 93410616SSebastien.Roy@Sun.COM !ipsec_polhead_insert(ph, actp, n, f, IPSEC_TYPE_OUTBOUND, ns)) 93510616SSebastien.Roy@Sun.COM return (B_FALSE); 93610616SSebastien.Roy@Sun.COM 93710616SSebastien.Roy@Sun.COM f = IPSEC_AF_V6; 93810616SSebastien.Roy@Sun.COM return (ipsec_polhead_insert(ph, actp, n, f, IPSEC_TYPE_INBOUND, ns) && 93910616SSebastien.Roy@Sun.COM ipsec_polhead_insert(ph, actp, n, f, IPSEC_TYPE_OUTBOUND, ns)); 94010616SSebastien.Roy@Sun.COM } 94110616SSebastien.Roy@Sun.COM 94210616SSebastien.Roy@Sun.COM /* 94310616SSebastien.Roy@Sun.COM * Used to set IPsec policy when policy is set through the IPTUN_CREATE or 94410616SSebastien.Roy@Sun.COM * IPTUN_MODIFY ioctls. 94510616SSebastien.Roy@Sun.COM */ 94610616SSebastien.Roy@Sun.COM static int 94710616SSebastien.Roy@Sun.COM iptun_set_sec_simple(iptun_t *iptun, const ipsec_req_t *ipsr) 94810616SSebastien.Roy@Sun.COM { 94910616SSebastien.Roy@Sun.COM int rc = 0; 95010616SSebastien.Roy@Sun.COM uint_t nact; 95110616SSebastien.Roy@Sun.COM ipsec_act_t *actp = NULL; 95210616SSebastien.Roy@Sun.COM boolean_t clear_all, old_policy = B_FALSE; 95310616SSebastien.Roy@Sun.COM ipsec_tun_pol_t *itp; 95410616SSebastien.Roy@Sun.COM char name[MAXLINKNAMELEN]; 95510616SSebastien.Roy@Sun.COM uint64_t gen; 95610616SSebastien.Roy@Sun.COM netstack_t *ns = iptun->iptun_ns; 95710616SSebastien.Roy@Sun.COM 95810616SSebastien.Roy@Sun.COM /* Can't specify self-encap on a tunnel. */ 95910616SSebastien.Roy@Sun.COM if (ipsr->ipsr_self_encap_req != 0) 96010616SSebastien.Roy@Sun.COM return (EINVAL); 96110616SSebastien.Roy@Sun.COM 96210616SSebastien.Roy@Sun.COM /* 96310616SSebastien.Roy@Sun.COM * If it's a "clear-all" entry, unset the security flags and resume 96410616SSebastien.Roy@Sun.COM * normal cleartext (or inherit-from-global) policy. 96510616SSebastien.Roy@Sun.COM */ 96610616SSebastien.Roy@Sun.COM clear_all = ((ipsr->ipsr_ah_req & IPTUN_IPSEC_REQ_MASK) == 0 && 96710616SSebastien.Roy@Sun.COM (ipsr->ipsr_esp_req & IPTUN_IPSEC_REQ_MASK) == 0); 96810616SSebastien.Roy@Sun.COM 96910616SSebastien.Roy@Sun.COM ASSERT(mutex_owned(&iptun->iptun_lock)); 97010616SSebastien.Roy@Sun.COM itp = iptun->iptun_itp; 97110616SSebastien.Roy@Sun.COM if (itp == NULL) { 97210616SSebastien.Roy@Sun.COM if (clear_all) 97310616SSebastien.Roy@Sun.COM goto bail; 97410616SSebastien.Roy@Sun.COM if ((rc = dls_mgmt_get_linkinfo(iptun->iptun_linkid, name, NULL, 97510616SSebastien.Roy@Sun.COM NULL, NULL)) != 0) 97610616SSebastien.Roy@Sun.COM goto bail; 97710616SSebastien.Roy@Sun.COM ASSERT(name[0] != '\0'); 97810616SSebastien.Roy@Sun.COM if ((itp = create_tunnel_policy(name, &rc, &gen, ns)) == NULL) 97910616SSebastien.Roy@Sun.COM goto bail; 98010616SSebastien.Roy@Sun.COM iptun->iptun_itp = itp; 98110616SSebastien.Roy@Sun.COM } 98210616SSebastien.Roy@Sun.COM 98310616SSebastien.Roy@Sun.COM /* Allocate the actvec now, before holding itp or polhead locks. */ 98410616SSebastien.Roy@Sun.COM ipsec_actvec_from_req(ipsr, &actp, &nact, ns); 98510616SSebastien.Roy@Sun.COM if (actp == NULL) { 98610616SSebastien.Roy@Sun.COM rc = ENOMEM; 98710616SSebastien.Roy@Sun.COM goto bail; 98810616SSebastien.Roy@Sun.COM } 98910616SSebastien.Roy@Sun.COM 99010616SSebastien.Roy@Sun.COM /* 99110616SSebastien.Roy@Sun.COM * Just write on the active polhead. Save the primary/secondary stuff 99210616SSebastien.Roy@Sun.COM * for spdsock operations. 99310616SSebastien.Roy@Sun.COM * 99410616SSebastien.Roy@Sun.COM * Mutex because we need to write to the polhead AND flags atomically. 99510616SSebastien.Roy@Sun.COM * Other threads will acquire the polhead lock as a reader if the 99610616SSebastien.Roy@Sun.COM * (unprotected) flag is set. 99710616SSebastien.Roy@Sun.COM */ 99810616SSebastien.Roy@Sun.COM mutex_enter(&itp->itp_lock); 99910616SSebastien.Roy@Sun.COM if (itp->itp_flags & ITPF_P_TUNNEL) { 100010616SSebastien.Roy@Sun.COM /* Oops, we lost a race. Let's get out of here. */ 100110616SSebastien.Roy@Sun.COM rc = EBUSY; 100210616SSebastien.Roy@Sun.COM goto mutex_bail; 100310616SSebastien.Roy@Sun.COM } 100410616SSebastien.Roy@Sun.COM old_policy = ((itp->itp_flags & ITPF_P_ACTIVE) != 0); 100510616SSebastien.Roy@Sun.COM 100610616SSebastien.Roy@Sun.COM if (old_policy) { 100710616SSebastien.Roy@Sun.COM ITPF_CLONE(itp->itp_flags); 100810616SSebastien.Roy@Sun.COM rc = ipsec_copy_polhead(itp->itp_policy, itp->itp_inactive, ns); 100910616SSebastien.Roy@Sun.COM if (rc != 0) { 101010616SSebastien.Roy@Sun.COM /* inactive has already been cleared. */ 101110616SSebastien.Roy@Sun.COM itp->itp_flags &= ~ITPF_IFLAGS; 101210616SSebastien.Roy@Sun.COM goto mutex_bail; 101310616SSebastien.Roy@Sun.COM } 101410616SSebastien.Roy@Sun.COM rw_enter(&itp->itp_policy->iph_lock, RW_WRITER); 101510616SSebastien.Roy@Sun.COM ipsec_polhead_flush(itp->itp_policy, ns); 101610616SSebastien.Roy@Sun.COM } else { 101710616SSebastien.Roy@Sun.COM /* Else assume itp->itp_policy is already flushed. */ 101810616SSebastien.Roy@Sun.COM rw_enter(&itp->itp_policy->iph_lock, RW_WRITER); 101910616SSebastien.Roy@Sun.COM } 102010616SSebastien.Roy@Sun.COM 102110616SSebastien.Roy@Sun.COM if (clear_all) { 102210616SSebastien.Roy@Sun.COM ASSERT(avl_numnodes(&itp->itp_policy->iph_rulebyid) == 0); 102310616SSebastien.Roy@Sun.COM itp->itp_flags &= ~ITPF_PFLAGS; 102410616SSebastien.Roy@Sun.COM rw_exit(&itp->itp_policy->iph_lock); 102510616SSebastien.Roy@Sun.COM old_policy = B_FALSE; /* Clear out the inactive one too. */ 102610616SSebastien.Roy@Sun.COM goto recover_bail; 102710616SSebastien.Roy@Sun.COM } 102810616SSebastien.Roy@Sun.COM 102910616SSebastien.Roy@Sun.COM if (iptun_insert_simple_policies(itp->itp_policy, actp, nact, ns)) { 103010616SSebastien.Roy@Sun.COM rw_exit(&itp->itp_policy->iph_lock); 103110616SSebastien.Roy@Sun.COM /* 103210616SSebastien.Roy@Sun.COM * Adjust MTU and make sure the DL side knows what's up. 103310616SSebastien.Roy@Sun.COM */ 103410616SSebastien.Roy@Sun.COM itp->itp_flags = ITPF_P_ACTIVE; 103511042SErik.Nordmark@Sun.COM (void) iptun_update_mtu(iptun, NULL, 0); 103610616SSebastien.Roy@Sun.COM old_policy = B_FALSE; /* Blank out inactive - we succeeded */ 103710616SSebastien.Roy@Sun.COM } else { 103810616SSebastien.Roy@Sun.COM rw_exit(&itp->itp_policy->iph_lock); 103910616SSebastien.Roy@Sun.COM rc = ENOMEM; 104010616SSebastien.Roy@Sun.COM } 104110616SSebastien.Roy@Sun.COM 104210616SSebastien.Roy@Sun.COM recover_bail: 104310616SSebastien.Roy@Sun.COM if (old_policy) { 104410616SSebastien.Roy@Sun.COM /* Recover policy in in active polhead. */ 104510616SSebastien.Roy@Sun.COM ipsec_swap_policy(itp->itp_policy, itp->itp_inactive, ns); 104610616SSebastien.Roy@Sun.COM ITPF_SWAP(itp->itp_flags); 104710616SSebastien.Roy@Sun.COM } 104810616SSebastien.Roy@Sun.COM 104910616SSebastien.Roy@Sun.COM /* Clear policy in inactive polhead. */ 105010616SSebastien.Roy@Sun.COM itp->itp_flags &= ~ITPF_IFLAGS; 105110616SSebastien.Roy@Sun.COM rw_enter(&itp->itp_inactive->iph_lock, RW_WRITER); 105210616SSebastien.Roy@Sun.COM ipsec_polhead_flush(itp->itp_inactive, ns); 105310616SSebastien.Roy@Sun.COM rw_exit(&itp->itp_inactive->iph_lock); 105410616SSebastien.Roy@Sun.COM 105510616SSebastien.Roy@Sun.COM mutex_bail: 105610616SSebastien.Roy@Sun.COM mutex_exit(&itp->itp_lock); 105710616SSebastien.Roy@Sun.COM 105810616SSebastien.Roy@Sun.COM bail: 105910616SSebastien.Roy@Sun.COM if (actp != NULL) 106010616SSebastien.Roy@Sun.COM ipsec_actvec_free(actp, nact); 106110616SSebastien.Roy@Sun.COM 106210616SSebastien.Roy@Sun.COM return (rc); 106310616SSebastien.Roy@Sun.COM } 106410616SSebastien.Roy@Sun.COM 106510616SSebastien.Roy@Sun.COM static iptun_typeinfo_t * 106610616SSebastien.Roy@Sun.COM iptun_gettypeinfo(iptun_type_t type) 106710616SSebastien.Roy@Sun.COM { 106810616SSebastien.Roy@Sun.COM int i; 106910616SSebastien.Roy@Sun.COM 107010616SSebastien.Roy@Sun.COM for (i = 0; iptun_type_table[i].iti_type != IPTUN_TYPE_UNKNOWN; i++) { 107110616SSebastien.Roy@Sun.COM if (iptun_type_table[i].iti_type == type) 107210616SSebastien.Roy@Sun.COM break; 107310616SSebastien.Roy@Sun.COM } 107410616SSebastien.Roy@Sun.COM return (&iptun_type_table[i]); 107510616SSebastien.Roy@Sun.COM } 107610616SSebastien.Roy@Sun.COM 107710616SSebastien.Roy@Sun.COM /* 107810616SSebastien.Roy@Sun.COM * Set the parameters included in ik on the tunnel iptun. Parameters that can 107910616SSebastien.Roy@Sun.COM * only be set at creation time are set in iptun_create(). 108010616SSebastien.Roy@Sun.COM */ 108110616SSebastien.Roy@Sun.COM static int 108210616SSebastien.Roy@Sun.COM iptun_setparams(iptun_t *iptun, const iptun_kparams_t *ik) 108310616SSebastien.Roy@Sun.COM { 108410616SSebastien.Roy@Sun.COM int err = 0; 108510616SSebastien.Roy@Sun.COM netstack_t *ns = iptun->iptun_ns; 108610616SSebastien.Roy@Sun.COM iptun_addr_t orig_laddr, orig_raddr; 108710616SSebastien.Roy@Sun.COM uint_t orig_flags = iptun->iptun_flags; 108810616SSebastien.Roy@Sun.COM 108910616SSebastien.Roy@Sun.COM if (ik->iptun_kparam_flags & IPTUN_KPARAM_LADDR) { 109010616SSebastien.Roy@Sun.COM if (orig_flags & IPTUN_LADDR) 109110616SSebastien.Roy@Sun.COM orig_laddr = iptun->iptun_laddr; 109210616SSebastien.Roy@Sun.COM if ((err = iptun_setladdr(iptun, &ik->iptun_kparam_laddr)) != 0) 109310616SSebastien.Roy@Sun.COM return (err); 109410616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_LADDR; 109510616SSebastien.Roy@Sun.COM } 109610616SSebastien.Roy@Sun.COM 109710616SSebastien.Roy@Sun.COM if (ik->iptun_kparam_flags & IPTUN_KPARAM_RADDR) { 109810616SSebastien.Roy@Sun.COM if (orig_flags & IPTUN_RADDR) 109910616SSebastien.Roy@Sun.COM orig_raddr = iptun->iptun_raddr; 110010616SSebastien.Roy@Sun.COM if ((err = iptun_setraddr(iptun, &ik->iptun_kparam_raddr)) != 0) 110110616SSebastien.Roy@Sun.COM goto done; 110210616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_RADDR; 110310616SSebastien.Roy@Sun.COM } 110410616SSebastien.Roy@Sun.COM 110510616SSebastien.Roy@Sun.COM if (ik->iptun_kparam_flags & IPTUN_KPARAM_SECINFO) { 110610616SSebastien.Roy@Sun.COM /* 110710616SSebastien.Roy@Sun.COM * Set IPsec policy originating from the ifconfig(1M) command 110810616SSebastien.Roy@Sun.COM * line. This is traditionally called "simple" policy because 110910616SSebastien.Roy@Sun.COM * the ipsec_req_t (iptun_kparam_secinfo) can only describe a 111010616SSebastien.Roy@Sun.COM * simple policy of "do ESP on everything" and/or "do AH on 111110616SSebastien.Roy@Sun.COM * everything" (as opposed to the rich policy that can be 111210616SSebastien.Roy@Sun.COM * defined with ipsecconf(1M)). 111310616SSebastien.Roy@Sun.COM */ 111410616SSebastien.Roy@Sun.COM if (iptun->iptun_typeinfo->iti_type == IPTUN_TYPE_6TO4) { 111510616SSebastien.Roy@Sun.COM /* 111610616SSebastien.Roy@Sun.COM * Can't set security properties for automatic 111710616SSebastien.Roy@Sun.COM * tunnels. 111810616SSebastien.Roy@Sun.COM */ 111910616SSebastien.Roy@Sun.COM err = EINVAL; 112010616SSebastien.Roy@Sun.COM goto done; 112110616SSebastien.Roy@Sun.COM } 112210616SSebastien.Roy@Sun.COM 112310616SSebastien.Roy@Sun.COM if (!ipsec_loaded(ns->netstack_ipsec)) { 112410616SSebastien.Roy@Sun.COM /* If IPsec can be loaded, try and load it now. */ 112510616SSebastien.Roy@Sun.COM if (ipsec_failed(ns->netstack_ipsec)) { 112610616SSebastien.Roy@Sun.COM err = EPROTONOSUPPORT; 112710616SSebastien.Roy@Sun.COM goto done; 112810616SSebastien.Roy@Sun.COM } 112910616SSebastien.Roy@Sun.COM ipsec_loader_loadnow(ns->netstack_ipsec); 113010616SSebastien.Roy@Sun.COM /* 113110616SSebastien.Roy@Sun.COM * ipsec_loader_loadnow() returns while IPsec is 113210616SSebastien.Roy@Sun.COM * loaded asynchronously. While a method exists to 113310616SSebastien.Roy@Sun.COM * wait for IPsec to load (ipsec_loader_wait()), it 113410616SSebastien.Roy@Sun.COM * requires use of a STREAMS queue to do a qwait(). 113510616SSebastien.Roy@Sun.COM * We're not in STREAMS context here, and so we can't 113610616SSebastien.Roy@Sun.COM * use it. This is not a problem in practice because 113710616SSebastien.Roy@Sun.COM * in the vast majority of cases, key management and 113810616SSebastien.Roy@Sun.COM * global policy will have loaded before any tunnels 113910616SSebastien.Roy@Sun.COM * are plumbed, and so IPsec will already have been 114010616SSebastien.Roy@Sun.COM * loaded. 114110616SSebastien.Roy@Sun.COM */ 114210616SSebastien.Roy@Sun.COM err = EAGAIN; 114310616SSebastien.Roy@Sun.COM goto done; 114410616SSebastien.Roy@Sun.COM } 114510616SSebastien.Roy@Sun.COM 114610616SSebastien.Roy@Sun.COM err = iptun_set_sec_simple(iptun, &ik->iptun_kparam_secinfo); 114710616SSebastien.Roy@Sun.COM if (err == 0) { 114810616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_SIMPLE_POLICY; 114910616SSebastien.Roy@Sun.COM iptun->iptun_simple_policy = ik->iptun_kparam_secinfo; 115010616SSebastien.Roy@Sun.COM } 115110616SSebastien.Roy@Sun.COM } 115210616SSebastien.Roy@Sun.COM done: 115310616SSebastien.Roy@Sun.COM if (err != 0) { 115410616SSebastien.Roy@Sun.COM /* Restore original source and destination. */ 115510616SSebastien.Roy@Sun.COM if (ik->iptun_kparam_flags & IPTUN_KPARAM_LADDR && 115610616SSebastien.Roy@Sun.COM (orig_flags & IPTUN_LADDR)) 115710616SSebastien.Roy@Sun.COM iptun->iptun_laddr = orig_laddr; 115810616SSebastien.Roy@Sun.COM if ((ik->iptun_kparam_flags & IPTUN_KPARAM_RADDR) && 115910616SSebastien.Roy@Sun.COM (orig_flags & IPTUN_RADDR)) 116010616SSebastien.Roy@Sun.COM iptun->iptun_raddr = orig_raddr; 116110616SSebastien.Roy@Sun.COM iptun->iptun_flags = orig_flags; 116210616SSebastien.Roy@Sun.COM } 116310616SSebastien.Roy@Sun.COM return (err); 116410616SSebastien.Roy@Sun.COM } 116510616SSebastien.Roy@Sun.COM 116610616SSebastien.Roy@Sun.COM static int 116710616SSebastien.Roy@Sun.COM iptun_register(iptun_t *iptun) 116810616SSebastien.Roy@Sun.COM { 116910616SSebastien.Roy@Sun.COM mac_register_t *mac; 117010616SSebastien.Roy@Sun.COM int err; 117110616SSebastien.Roy@Sun.COM 117210616SSebastien.Roy@Sun.COM ASSERT(!(iptun->iptun_flags & IPTUN_MAC_REGISTERED)); 117310616SSebastien.Roy@Sun.COM 117410616SSebastien.Roy@Sun.COM if ((mac = mac_alloc(MAC_VERSION)) == NULL) 117510616SSebastien.Roy@Sun.COM return (EINVAL); 117610616SSebastien.Roy@Sun.COM 117710616SSebastien.Roy@Sun.COM mac->m_type_ident = iptun->iptun_typeinfo->iti_ident; 117810616SSebastien.Roy@Sun.COM mac->m_driver = iptun; 117910616SSebastien.Roy@Sun.COM mac->m_dip = iptun_dip; 118010616SSebastien.Roy@Sun.COM mac->m_instance = (uint_t)-1; 118110616SSebastien.Roy@Sun.COM mac->m_src_addr = (uint8_t *)&iptun->iptun_laddr.ia_addr; 118210616SSebastien.Roy@Sun.COM mac->m_dst_addr = iptun->iptun_typeinfo->iti_hasraddr ? 118310616SSebastien.Roy@Sun.COM (uint8_t *)&iptun->iptun_raddr.ia_addr : NULL; 118410616SSebastien.Roy@Sun.COM mac->m_callbacks = &iptun_m_callbacks; 118510616SSebastien.Roy@Sun.COM mac->m_min_sdu = iptun->iptun_typeinfo->iti_minmtu; 118610616SSebastien.Roy@Sun.COM mac->m_max_sdu = iptun->iptun_mtu; 118710616SSebastien.Roy@Sun.COM if (iptun->iptun_header_size != 0) { 118810616SSebastien.Roy@Sun.COM mac->m_pdata = &iptun->iptun_header; 118910616SSebastien.Roy@Sun.COM mac->m_pdata_size = iptun->iptun_header_size; 119010616SSebastien.Roy@Sun.COM } 119110616SSebastien.Roy@Sun.COM if ((err = mac_register(mac, &iptun->iptun_mh)) == 0) 119210616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_MAC_REGISTERED; 119310616SSebastien.Roy@Sun.COM mac_free(mac); 119410616SSebastien.Roy@Sun.COM return (err); 119510616SSebastien.Roy@Sun.COM } 119610616SSebastien.Roy@Sun.COM 119710616SSebastien.Roy@Sun.COM static int 119810616SSebastien.Roy@Sun.COM iptun_unregister(iptun_t *iptun) 119910616SSebastien.Roy@Sun.COM { 120010616SSebastien.Roy@Sun.COM int err; 120110616SSebastien.Roy@Sun.COM 120210616SSebastien.Roy@Sun.COM ASSERT(iptun->iptun_flags & IPTUN_MAC_REGISTERED); 120310616SSebastien.Roy@Sun.COM if ((err = mac_unregister(iptun->iptun_mh)) == 0) 120410616SSebastien.Roy@Sun.COM iptun->iptun_flags &= ~IPTUN_MAC_REGISTERED; 120510616SSebastien.Roy@Sun.COM return (err); 120610616SSebastien.Roy@Sun.COM } 120710616SSebastien.Roy@Sun.COM 120810616SSebastien.Roy@Sun.COM static conn_t * 120910616SSebastien.Roy@Sun.COM iptun_conn_create(iptun_t *iptun, netstack_t *ns, cred_t *credp) 121010616SSebastien.Roy@Sun.COM { 121110616SSebastien.Roy@Sun.COM conn_t *connp; 121210616SSebastien.Roy@Sun.COM 121310616SSebastien.Roy@Sun.COM if ((connp = ipcl_conn_create(IPCL_IPCCONN, KM_NOSLEEP, ns)) == NULL) 121410616SSebastien.Roy@Sun.COM return (NULL); 121510616SSebastien.Roy@Sun.COM 121610616SSebastien.Roy@Sun.COM connp->conn_flags |= IPCL_IPTUN; 121710616SSebastien.Roy@Sun.COM connp->conn_iptun = iptun; 121810616SSebastien.Roy@Sun.COM connp->conn_recv = iptun_input; 121911042SErik.Nordmark@Sun.COM connp->conn_recvicmp = iptun_input_icmp; 122011042SErik.Nordmark@Sun.COM connp->conn_verifyicmp = iptun_verifyicmp; 122111042SErik.Nordmark@Sun.COM 122211042SErik.Nordmark@Sun.COM /* 122311042SErik.Nordmark@Sun.COM * Register iptun_notify to listen to capability changes detected by IP. 122411042SErik.Nordmark@Sun.COM * This upcall is made in the context of the call to conn_ip_output. 122511042SErik.Nordmark@Sun.COM */ 122611042SErik.Nordmark@Sun.COM connp->conn_ixa->ixa_notify = iptun_notify; 122711042SErik.Nordmark@Sun.COM connp->conn_ixa->ixa_notify_cookie = iptun; 122811042SErik.Nordmark@Sun.COM 122910616SSebastien.Roy@Sun.COM /* 123010616SSebastien.Roy@Sun.COM * For exclusive stacks we set conn_zoneid to GLOBAL_ZONEID as is done 123110616SSebastien.Roy@Sun.COM * for all other conn_t's. 123210616SSebastien.Roy@Sun.COM * 123310616SSebastien.Roy@Sun.COM * Note that there's an important distinction between iptun_zoneid and 123410616SSebastien.Roy@Sun.COM * conn_zoneid. The conn_zoneid is set to GLOBAL_ZONEID in non-global 123510616SSebastien.Roy@Sun.COM * exclusive stack zones to make the ip module believe that the 123610616SSebastien.Roy@Sun.COM * non-global zone is actually a global zone. Therefore, when 123710616SSebastien.Roy@Sun.COM * interacting with the ip module, we must always use conn_zoneid. 123810616SSebastien.Roy@Sun.COM */ 123910616SSebastien.Roy@Sun.COM connp->conn_zoneid = (ns->netstack_stackid == GLOBAL_NETSTACKID) ? 124010616SSebastien.Roy@Sun.COM crgetzoneid(credp) : GLOBAL_ZONEID; 124110616SSebastien.Roy@Sun.COM connp->conn_cred = credp; 124210616SSebastien.Roy@Sun.COM /* crfree() is done in ipcl_conn_destroy(), called by CONN_DEC_REF() */ 124310616SSebastien.Roy@Sun.COM crhold(connp->conn_cred); 124411042SErik.Nordmark@Sun.COM connp->conn_cpid = NOPID; 124511042SErik.Nordmark@Sun.COM 124611042SErik.Nordmark@Sun.COM /* conn_allzones can not be set this early, hence no IPCL_ZONEID */ 124711042SErik.Nordmark@Sun.COM connp->conn_ixa->ixa_zoneid = connp->conn_zoneid; 124810616SSebastien.Roy@Sun.COM ASSERT(connp->conn_ref == 1); 124910616SSebastien.Roy@Sun.COM 125011042SErik.Nordmark@Sun.COM /* Cache things in ixa without an extra refhold */ 125111849SErik.Nordmark@Sun.COM ASSERT(!(connp->conn_ixa->ixa_free_flags & IXA_FREE_CRED)); 125211042SErik.Nordmark@Sun.COM connp->conn_ixa->ixa_cred = connp->conn_cred; 125311042SErik.Nordmark@Sun.COM connp->conn_ixa->ixa_cpid = connp->conn_cpid; 125411042SErik.Nordmark@Sun.COM if (is_system_labeled()) 125511042SErik.Nordmark@Sun.COM connp->conn_ixa->ixa_tsl = crgetlabel(connp->conn_cred); 125611042SErik.Nordmark@Sun.COM 125711042SErik.Nordmark@Sun.COM /* 125811042SErik.Nordmark@Sun.COM * Have conn_ip_output drop packets should our outer source 125911042SErik.Nordmark@Sun.COM * go invalid 126011042SErik.Nordmark@Sun.COM */ 126111042SErik.Nordmark@Sun.COM connp->conn_ixa->ixa_flags |= IXAF_VERIFY_SOURCE; 126211042SErik.Nordmark@Sun.COM 126311042SErik.Nordmark@Sun.COM switch (iptun->iptun_typeinfo->iti_ipvers) { 126411042SErik.Nordmark@Sun.COM case IPV4_VERSION: 126511042SErik.Nordmark@Sun.COM connp->conn_family = AF_INET6; 126611042SErik.Nordmark@Sun.COM break; 126711042SErik.Nordmark@Sun.COM case IPV6_VERSION: 126811042SErik.Nordmark@Sun.COM connp->conn_family = AF_INET; 126911042SErik.Nordmark@Sun.COM break; 127011042SErik.Nordmark@Sun.COM } 127110616SSebastien.Roy@Sun.COM mutex_enter(&connp->conn_lock); 127210616SSebastien.Roy@Sun.COM connp->conn_state_flags &= ~CONN_INCIPIENT; 127310616SSebastien.Roy@Sun.COM mutex_exit(&connp->conn_lock); 127410616SSebastien.Roy@Sun.COM return (connp); 127510616SSebastien.Roy@Sun.COM } 127610616SSebastien.Roy@Sun.COM 127710616SSebastien.Roy@Sun.COM static void 127810616SSebastien.Roy@Sun.COM iptun_conn_destroy(conn_t *connp) 127910616SSebastien.Roy@Sun.COM { 128010616SSebastien.Roy@Sun.COM ip_quiesce_conn(connp); 128110616SSebastien.Roy@Sun.COM connp->conn_iptun = NULL; 128210616SSebastien.Roy@Sun.COM ASSERT(connp->conn_ref == 1); 128310616SSebastien.Roy@Sun.COM CONN_DEC_REF(connp); 128410616SSebastien.Roy@Sun.COM } 128510616SSebastien.Roy@Sun.COM 128610616SSebastien.Roy@Sun.COM static iptun_t * 128710616SSebastien.Roy@Sun.COM iptun_alloc(void) 128810616SSebastien.Roy@Sun.COM { 128910616SSebastien.Roy@Sun.COM iptun_t *iptun; 129010616SSebastien.Roy@Sun.COM 129110616SSebastien.Roy@Sun.COM if ((iptun = kmem_cache_alloc(iptun_cache, KM_NOSLEEP)) != NULL) { 129210616SSebastien.Roy@Sun.COM bzero(iptun, sizeof (*iptun)); 129310616SSebastien.Roy@Sun.COM atomic_inc_32(&iptun_tunnelcount); 129410616SSebastien.Roy@Sun.COM } 129510616SSebastien.Roy@Sun.COM return (iptun); 129610616SSebastien.Roy@Sun.COM } 129710616SSebastien.Roy@Sun.COM 129810616SSebastien.Roy@Sun.COM static void 129910616SSebastien.Roy@Sun.COM iptun_free(iptun_t *iptun) 130010616SSebastien.Roy@Sun.COM { 130110616SSebastien.Roy@Sun.COM ASSERT(iptun->iptun_flags & IPTUN_CONDEMNED); 130210616SSebastien.Roy@Sun.COM 130310616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_HASH_INSERTED) { 130410616SSebastien.Roy@Sun.COM iptun_stack_t *iptuns = iptun->iptun_iptuns; 130510616SSebastien.Roy@Sun.COM 130610616SSebastien.Roy@Sun.COM mutex_enter(&iptun_hash_lock); 130710616SSebastien.Roy@Sun.COM VERIFY(mod_hash_remove(iptun_hash, 130810616SSebastien.Roy@Sun.COM IPTUN_HASH_KEY(iptun->iptun_linkid), 130910616SSebastien.Roy@Sun.COM (mod_hash_val_t *)&iptun) == 0); 131010616SSebastien.Roy@Sun.COM mutex_exit(&iptun_hash_lock); 131110616SSebastien.Roy@Sun.COM iptun->iptun_flags &= ~IPTUN_HASH_INSERTED; 131210616SSebastien.Roy@Sun.COM mutex_enter(&iptuns->iptuns_lock); 131310616SSebastien.Roy@Sun.COM list_remove(&iptuns->iptuns_iptunlist, iptun); 131410616SSebastien.Roy@Sun.COM mutex_exit(&iptuns->iptuns_lock); 131510616SSebastien.Roy@Sun.COM } 131610616SSebastien.Roy@Sun.COM 131710616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_BOUND) 131810616SSebastien.Roy@Sun.COM iptun_unbind(iptun); 131910616SSebastien.Roy@Sun.COM 132010616SSebastien.Roy@Sun.COM /* 132110616SSebastien.Roy@Sun.COM * After iptun_unregister(), there will be no threads executing a 132210616SSebastien.Roy@Sun.COM * downcall from the mac module, including in the tx datapath. 132310616SSebastien.Roy@Sun.COM */ 132410616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_MAC_REGISTERED) 132510616SSebastien.Roy@Sun.COM VERIFY(iptun_unregister(iptun) == 0); 132610616SSebastien.Roy@Sun.COM 132710616SSebastien.Roy@Sun.COM if (iptun->iptun_itp != NULL) { 132810616SSebastien.Roy@Sun.COM /* 132910616SSebastien.Roy@Sun.COM * Remove from the AVL tree, AND release the reference iptun_t 133010616SSebastien.Roy@Sun.COM * itself holds on the ITP. 133110616SSebastien.Roy@Sun.COM */ 133210616SSebastien.Roy@Sun.COM itp_unlink(iptun->iptun_itp, iptun->iptun_ns); 133310616SSebastien.Roy@Sun.COM ITP_REFRELE(iptun->iptun_itp, iptun->iptun_ns); 133410616SSebastien.Roy@Sun.COM iptun->iptun_itp = NULL; 133510616SSebastien.Roy@Sun.COM iptun->iptun_flags &= ~IPTUN_SIMPLE_POLICY; 133610616SSebastien.Roy@Sun.COM } 133710616SSebastien.Roy@Sun.COM 133810616SSebastien.Roy@Sun.COM /* 133910616SSebastien.Roy@Sun.COM * After ipcl_conn_destroy(), there will be no threads executing an 134010616SSebastien.Roy@Sun.COM * upcall from ip (i.e., iptun_input()), and it is then safe to free 134110616SSebastien.Roy@Sun.COM * the iptun_t. 134210616SSebastien.Roy@Sun.COM */ 134310616SSebastien.Roy@Sun.COM if (iptun->iptun_connp != NULL) { 134410616SSebastien.Roy@Sun.COM iptun_conn_destroy(iptun->iptun_connp); 134510616SSebastien.Roy@Sun.COM iptun->iptun_connp = NULL; 134610616SSebastien.Roy@Sun.COM } 134710616SSebastien.Roy@Sun.COM 134810616SSebastien.Roy@Sun.COM kmem_cache_free(iptun_cache, iptun); 134910616SSebastien.Roy@Sun.COM atomic_dec_32(&iptun_tunnelcount); 135010616SSebastien.Roy@Sun.COM } 135110616SSebastien.Roy@Sun.COM 135210616SSebastien.Roy@Sun.COM int 135310616SSebastien.Roy@Sun.COM iptun_create(iptun_kparams_t *ik, cred_t *credp) 135410616SSebastien.Roy@Sun.COM { 135510616SSebastien.Roy@Sun.COM iptun_t *iptun = NULL; 135610616SSebastien.Roy@Sun.COM int err = 0, mherr; 135710616SSebastien.Roy@Sun.COM char linkname[MAXLINKNAMELEN]; 135810616SSebastien.Roy@Sun.COM ipsec_tun_pol_t *itp; 135910616SSebastien.Roy@Sun.COM netstack_t *ns = NULL; 136010616SSebastien.Roy@Sun.COM iptun_stack_t *iptuns; 136110616SSebastien.Roy@Sun.COM datalink_id_t tmpid; 136210616SSebastien.Roy@Sun.COM zoneid_t zoneid = crgetzoneid(credp); 136310616SSebastien.Roy@Sun.COM boolean_t link_created = B_FALSE; 136410616SSebastien.Roy@Sun.COM 136510616SSebastien.Roy@Sun.COM /* The tunnel type is mandatory */ 136610616SSebastien.Roy@Sun.COM if (!(ik->iptun_kparam_flags & IPTUN_KPARAM_TYPE)) 136710616SSebastien.Roy@Sun.COM return (EINVAL); 136810616SSebastien.Roy@Sun.COM 136910616SSebastien.Roy@Sun.COM /* 137010616SSebastien.Roy@Sun.COM * Is the linkid that the caller wishes to associate with this new 137110616SSebastien.Roy@Sun.COM * tunnel assigned to this zone? 137210616SSebastien.Roy@Sun.COM */ 137310616SSebastien.Roy@Sun.COM if (zone_check_datalink(&zoneid, ik->iptun_kparam_linkid) != 0) { 137410616SSebastien.Roy@Sun.COM if (zoneid != GLOBAL_ZONEID) 137510616SSebastien.Roy@Sun.COM return (EINVAL); 137610616SSebastien.Roy@Sun.COM } else if (zoneid == GLOBAL_ZONEID) { 137710616SSebastien.Roy@Sun.COM return (EINVAL); 137810616SSebastien.Roy@Sun.COM } 137910616SSebastien.Roy@Sun.COM 138010616SSebastien.Roy@Sun.COM /* 138110616SSebastien.Roy@Sun.COM * Make sure that we're not trying to create a tunnel that has already 138210616SSebastien.Roy@Sun.COM * been created. 138310616SSebastien.Roy@Sun.COM */ 138410616SSebastien.Roy@Sun.COM if (iptun_enter_by_linkid(ik->iptun_kparam_linkid, &iptun) == 0) { 138510616SSebastien.Roy@Sun.COM iptun_exit(iptun); 138610616SSebastien.Roy@Sun.COM iptun = NULL; 138710616SSebastien.Roy@Sun.COM err = EEXIST; 138810616SSebastien.Roy@Sun.COM goto done; 138910616SSebastien.Roy@Sun.COM } 139010616SSebastien.Roy@Sun.COM 139110616SSebastien.Roy@Sun.COM ns = netstack_find_by_cred(credp); 139210616SSebastien.Roy@Sun.COM iptuns = ns->netstack_iptun; 139310616SSebastien.Roy@Sun.COM 139410616SSebastien.Roy@Sun.COM if ((iptun = iptun_alloc()) == NULL) { 139510616SSebastien.Roy@Sun.COM err = ENOMEM; 139610616SSebastien.Roy@Sun.COM goto done; 139710616SSebastien.Roy@Sun.COM } 139810616SSebastien.Roy@Sun.COM 139910616SSebastien.Roy@Sun.COM iptun->iptun_linkid = ik->iptun_kparam_linkid; 140010616SSebastien.Roy@Sun.COM iptun->iptun_zoneid = zoneid; 140110616SSebastien.Roy@Sun.COM iptun->iptun_ns = ns; 140210616SSebastien.Roy@Sun.COM 140310616SSebastien.Roy@Sun.COM iptun->iptun_typeinfo = iptun_gettypeinfo(ik->iptun_kparam_type); 140410616SSebastien.Roy@Sun.COM if (iptun->iptun_typeinfo->iti_type == IPTUN_TYPE_UNKNOWN) { 140510616SSebastien.Roy@Sun.COM err = EINVAL; 140610616SSebastien.Roy@Sun.COM goto done; 140710616SSebastien.Roy@Sun.COM } 140810616SSebastien.Roy@Sun.COM 140910616SSebastien.Roy@Sun.COM if (ik->iptun_kparam_flags & IPTUN_KPARAM_IMPLICIT) 141010616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_IMPLICIT; 141110616SSebastien.Roy@Sun.COM 141210616SSebastien.Roy@Sun.COM if ((err = iptun_setparams(iptun, ik)) != 0) 141310616SSebastien.Roy@Sun.COM goto done; 141410616SSebastien.Roy@Sun.COM 141510616SSebastien.Roy@Sun.COM iptun->iptun_hoplimit = IPTUN_DEFAULT_HOPLIMIT; 141610616SSebastien.Roy@Sun.COM if (iptun->iptun_typeinfo->iti_type == IPTUN_TYPE_IPV6) 141710616SSebastien.Roy@Sun.COM iptun->iptun_encaplimit = IPTUN_DEFAULT_ENCAPLIMIT; 141810616SSebastien.Roy@Sun.COM 141910616SSebastien.Roy@Sun.COM iptun_headergen(iptun, B_FALSE); 142010616SSebastien.Roy@Sun.COM 142110616SSebastien.Roy@Sun.COM iptun->iptun_connp = iptun_conn_create(iptun, ns, credp); 142210616SSebastien.Roy@Sun.COM if (iptun->iptun_connp == NULL) { 142310616SSebastien.Roy@Sun.COM err = ENOMEM; 142410616SSebastien.Roy@Sun.COM goto done; 142510616SSebastien.Roy@Sun.COM } 142610616SSebastien.Roy@Sun.COM 142710616SSebastien.Roy@Sun.COM iptun->iptun_mtu = iptun->iptun_typeinfo->iti_maxmtu; 142810616SSebastien.Roy@Sun.COM iptun->iptun_dpmtu = iptun->iptun_mtu; 142910616SSebastien.Roy@Sun.COM 143010616SSebastien.Roy@Sun.COM /* 143110616SSebastien.Roy@Sun.COM * Find an ITP based on linkname. If we have parms already set via 143210616SSebastien.Roy@Sun.COM * the iptun_setparams() call above, it may have created an ITP for 143310616SSebastien.Roy@Sun.COM * us. We always try get_tunnel_policy() for DEBUG correctness 143410616SSebastien.Roy@Sun.COM * checks, and we may wish to refactor this to only check when 143510616SSebastien.Roy@Sun.COM * iptun_itp is NULL. 143610616SSebastien.Roy@Sun.COM */ 143710616SSebastien.Roy@Sun.COM if ((err = dls_mgmt_get_linkinfo(iptun->iptun_linkid, linkname, NULL, 143810616SSebastien.Roy@Sun.COM NULL, NULL)) != 0) 143910616SSebastien.Roy@Sun.COM goto done; 144010616SSebastien.Roy@Sun.COM if ((itp = get_tunnel_policy(linkname, ns)) != NULL) 144110616SSebastien.Roy@Sun.COM iptun->iptun_itp = itp; 144210616SSebastien.Roy@Sun.COM 144310616SSebastien.Roy@Sun.COM /* 144410616SSebastien.Roy@Sun.COM * See if we have the necessary IP addresses assigned to this tunnel 144510616SSebastien.Roy@Sun.COM * to try and bind them with ip underneath us. If we're not ready to 144610616SSebastien.Roy@Sun.COM * bind yet, then we'll defer the bind operation until the addresses 144710616SSebastien.Roy@Sun.COM * are modified. 144810616SSebastien.Roy@Sun.COM */ 144910616SSebastien.Roy@Sun.COM if (iptun_canbind(iptun) && ((err = iptun_bind(iptun)) != 0)) 145010616SSebastien.Roy@Sun.COM goto done; 145110616SSebastien.Roy@Sun.COM 145210616SSebastien.Roy@Sun.COM if ((err = iptun_register(iptun)) != 0) 145310616SSebastien.Roy@Sun.COM goto done; 145410616SSebastien.Roy@Sun.COM 145510616SSebastien.Roy@Sun.COM err = dls_devnet_create(iptun->iptun_mh, iptun->iptun_linkid, 145610616SSebastien.Roy@Sun.COM iptun->iptun_zoneid); 145710616SSebastien.Roy@Sun.COM if (err != 0) 145810616SSebastien.Roy@Sun.COM goto done; 145910616SSebastien.Roy@Sun.COM link_created = B_TRUE; 146010616SSebastien.Roy@Sun.COM 146110616SSebastien.Roy@Sun.COM /* 146210616SSebastien.Roy@Sun.COM * We hash by link-id as that is the key used by all other iptun 146310616SSebastien.Roy@Sun.COM * interfaces (modify, delete, etc.). 146410616SSebastien.Roy@Sun.COM */ 146510616SSebastien.Roy@Sun.COM if ((mherr = mod_hash_insert(iptun_hash, 146610616SSebastien.Roy@Sun.COM IPTUN_HASH_KEY(iptun->iptun_linkid), (mod_hash_val_t)iptun)) == 0) { 146710616SSebastien.Roy@Sun.COM mutex_enter(&iptuns->iptuns_lock); 146810616SSebastien.Roy@Sun.COM list_insert_head(&iptuns->iptuns_iptunlist, iptun); 146910616SSebastien.Roy@Sun.COM mutex_exit(&iptuns->iptuns_lock); 147010616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_HASH_INSERTED; 147110616SSebastien.Roy@Sun.COM } else if (mherr == MH_ERR_NOMEM) { 147210616SSebastien.Roy@Sun.COM err = ENOMEM; 147310616SSebastien.Roy@Sun.COM } else if (mherr == MH_ERR_DUPLICATE) { 147410616SSebastien.Roy@Sun.COM err = EEXIST; 147510616SSebastien.Roy@Sun.COM } else { 147610616SSebastien.Roy@Sun.COM err = EINVAL; 147710616SSebastien.Roy@Sun.COM } 147810616SSebastien.Roy@Sun.COM 147910616SSebastien.Roy@Sun.COM done: 148010616SSebastien.Roy@Sun.COM if (iptun == NULL && ns != NULL) 148110616SSebastien.Roy@Sun.COM netstack_rele(ns); 148210616SSebastien.Roy@Sun.COM if (err != 0 && iptun != NULL) { 148310616SSebastien.Roy@Sun.COM if (link_created) { 148410616SSebastien.Roy@Sun.COM (void) dls_devnet_destroy(iptun->iptun_mh, &tmpid, 148510616SSebastien.Roy@Sun.COM B_TRUE); 148610616SSebastien.Roy@Sun.COM } 148710616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_CONDEMNED; 148810616SSebastien.Roy@Sun.COM iptun_free(iptun); 148910616SSebastien.Roy@Sun.COM } 149010616SSebastien.Roy@Sun.COM return (err); 149110616SSebastien.Roy@Sun.COM } 149210616SSebastien.Roy@Sun.COM 149310616SSebastien.Roy@Sun.COM int 149410616SSebastien.Roy@Sun.COM iptun_delete(datalink_id_t linkid, cred_t *credp) 149510616SSebastien.Roy@Sun.COM { 149610616SSebastien.Roy@Sun.COM int err; 149710616SSebastien.Roy@Sun.COM iptun_t *iptun = NULL; 149810616SSebastien.Roy@Sun.COM 149910616SSebastien.Roy@Sun.COM if ((err = iptun_enter_by_linkid(linkid, &iptun)) != 0) 150010616SSebastien.Roy@Sun.COM return (err); 150110616SSebastien.Roy@Sun.COM 150210616SSebastien.Roy@Sun.COM /* One cannot delete a tunnel that belongs to another zone. */ 150310616SSebastien.Roy@Sun.COM if (iptun->iptun_zoneid != crgetzoneid(credp)) { 150410616SSebastien.Roy@Sun.COM iptun_exit(iptun); 150510616SSebastien.Roy@Sun.COM return (EACCES); 150610616SSebastien.Roy@Sun.COM } 150710616SSebastien.Roy@Sun.COM 150810616SSebastien.Roy@Sun.COM /* 150910616SSebastien.Roy@Sun.COM * We need to exit iptun in order to issue calls up the stack such as 151010616SSebastien.Roy@Sun.COM * dls_devnet_destroy(). If we call up while still in iptun, deadlock 151110616SSebastien.Roy@Sun.COM * with calls coming down the stack is possible. We prevent other 151210616SSebastien.Roy@Sun.COM * threads from entering this iptun after we've exited it by setting 151310616SSebastien.Roy@Sun.COM * the IPTUN_DELETE_PENDING flag. This will cause callers of 151410616SSebastien.Roy@Sun.COM * iptun_enter() to block waiting on iptun_enter_cv. The assumption 151510616SSebastien.Roy@Sun.COM * here is that the functions we're calling while IPTUN_DELETE_PENDING 151610616SSebastien.Roy@Sun.COM * is set dont resuult in an iptun_enter() call, as that would result 151710616SSebastien.Roy@Sun.COM * in deadlock. 151810616SSebastien.Roy@Sun.COM */ 151910616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_DELETE_PENDING; 152010616SSebastien.Roy@Sun.COM 152110616SSebastien.Roy@Sun.COM /* Wait for any pending upcall to the mac module to complete. */ 152210616SSebastien.Roy@Sun.COM while (iptun->iptun_flags & IPTUN_UPCALL_PENDING) 152310616SSebastien.Roy@Sun.COM cv_wait(&iptun->iptun_upcall_cv, &iptun->iptun_lock); 152410616SSebastien.Roy@Sun.COM 152510616SSebastien.Roy@Sun.COM iptun_exit(iptun); 152610616SSebastien.Roy@Sun.COM 152710616SSebastien.Roy@Sun.COM if ((err = dls_devnet_destroy(iptun->iptun_mh, &linkid, B_TRUE)) == 0) { 152810616SSebastien.Roy@Sun.COM /* 152910616SSebastien.Roy@Sun.COM * mac_disable() will fail with EBUSY if there are references 153010616SSebastien.Roy@Sun.COM * to the iptun MAC. If there are none, then mac_disable() 153110616SSebastien.Roy@Sun.COM * will assure that none can be acquired until the MAC is 153210616SSebastien.Roy@Sun.COM * unregistered. 153310616SSebastien.Roy@Sun.COM * 153410616SSebastien.Roy@Sun.COM * XXX CR 6791335 prevents us from calling mac_disable() prior 153510616SSebastien.Roy@Sun.COM * to dls_devnet_destroy(), so we unfortunately need to 153610616SSebastien.Roy@Sun.COM * attempt to re-create the devnet node if mac_disable() 153710616SSebastien.Roy@Sun.COM * fails. 153810616SSebastien.Roy@Sun.COM */ 153910616SSebastien.Roy@Sun.COM if ((err = mac_disable(iptun->iptun_mh)) != 0) { 154010616SSebastien.Roy@Sun.COM (void) dls_devnet_create(iptun->iptun_mh, linkid, 154110616SSebastien.Roy@Sun.COM iptun->iptun_zoneid); 154210616SSebastien.Roy@Sun.COM } 154310616SSebastien.Roy@Sun.COM } 154410616SSebastien.Roy@Sun.COM 154510616SSebastien.Roy@Sun.COM /* 154610616SSebastien.Roy@Sun.COM * Now that we know the fate of this iptun_t, we need to clear 154710616SSebastien.Roy@Sun.COM * IPTUN_DELETE_PENDING, and set IPTUN_CONDEMNED if the iptun_t is 154810616SSebastien.Roy@Sun.COM * slated to be freed. Either way, we need to signal the threads 154910616SSebastien.Roy@Sun.COM * waiting in iptun_enter() so that they can either fail if 155010616SSebastien.Roy@Sun.COM * IPTUN_CONDEMNED is set, or continue if it's not. 155110616SSebastien.Roy@Sun.COM */ 155210616SSebastien.Roy@Sun.COM mutex_enter(&iptun->iptun_lock); 155310616SSebastien.Roy@Sun.COM iptun->iptun_flags &= ~IPTUN_DELETE_PENDING; 155410616SSebastien.Roy@Sun.COM if (err == 0) 155510616SSebastien.Roy@Sun.COM iptun->iptun_flags |= IPTUN_CONDEMNED; 155610616SSebastien.Roy@Sun.COM cv_broadcast(&iptun->iptun_enter_cv); 155710616SSebastien.Roy@Sun.COM mutex_exit(&iptun->iptun_lock); 155810616SSebastien.Roy@Sun.COM 155910616SSebastien.Roy@Sun.COM /* 156010616SSebastien.Roy@Sun.COM * Note that there is no danger in calling iptun_free() after having 156110616SSebastien.Roy@Sun.COM * dropped the iptun_lock since callers of iptun_enter() at this point 156210616SSebastien.Roy@Sun.COM * are doing so from iptun_enter_by_linkid() (mac_disable() got rid of 156310616SSebastien.Roy@Sun.COM * threads entering from mac callbacks which call iptun_enter() 156410616SSebastien.Roy@Sun.COM * directly) which holds iptun_hash_lock, and iptun_free() grabs this 156510616SSebastien.Roy@Sun.COM * lock in order to remove the iptun_t from the hash table. 156610616SSebastien.Roy@Sun.COM */ 156710616SSebastien.Roy@Sun.COM if (err == 0) 156810616SSebastien.Roy@Sun.COM iptun_free(iptun); 156910616SSebastien.Roy@Sun.COM 157010616SSebastien.Roy@Sun.COM return (err); 157110616SSebastien.Roy@Sun.COM } 157210616SSebastien.Roy@Sun.COM 157310616SSebastien.Roy@Sun.COM int 157410616SSebastien.Roy@Sun.COM iptun_modify(const iptun_kparams_t *ik, cred_t *credp) 157510616SSebastien.Roy@Sun.COM { 157610616SSebastien.Roy@Sun.COM iptun_t *iptun; 157710616SSebastien.Roy@Sun.COM boolean_t laddr_change = B_FALSE, raddr_change = B_FALSE; 157810616SSebastien.Roy@Sun.COM int err; 157910616SSebastien.Roy@Sun.COM 158010616SSebastien.Roy@Sun.COM if ((err = iptun_enter_by_linkid(ik->iptun_kparam_linkid, &iptun)) != 0) 158110616SSebastien.Roy@Sun.COM return (err); 158210616SSebastien.Roy@Sun.COM 158310616SSebastien.Roy@Sun.COM /* One cannot modify a tunnel that belongs to another zone. */ 158410616SSebastien.Roy@Sun.COM if (iptun->iptun_zoneid != crgetzoneid(credp)) { 158510616SSebastien.Roy@Sun.COM err = EACCES; 158610616SSebastien.Roy@Sun.COM goto done; 158710616SSebastien.Roy@Sun.COM } 158810616SSebastien.Roy@Sun.COM 158910616SSebastien.Roy@Sun.COM /* The tunnel type cannot be changed */ 159010616SSebastien.Roy@Sun.COM if (ik->iptun_kparam_flags & IPTUN_KPARAM_TYPE) { 159110616SSebastien.Roy@Sun.COM err = EINVAL; 159210616SSebastien.Roy@Sun.COM goto done; 159310616SSebastien.Roy@Sun.COM } 159410616SSebastien.Roy@Sun.COM 159510616SSebastien.Roy@Sun.COM if ((err = iptun_setparams(iptun, ik)) != 0) 159610616SSebastien.Roy@Sun.COM goto done; 159710616SSebastien.Roy@Sun.COM iptun_headergen(iptun, B_FALSE); 159810616SSebastien.Roy@Sun.COM 159910616SSebastien.Roy@Sun.COM /* 160010616SSebastien.Roy@Sun.COM * If any of the tunnel's addresses has been modified and the tunnel 160110616SSebastien.Roy@Sun.COM * has the necessary addresses assigned to it, we need to try to bind 160210616SSebastien.Roy@Sun.COM * with ip underneath us. If we're not ready to bind yet, then we'll 160310616SSebastien.Roy@Sun.COM * try again when the addresses are modified later. 160410616SSebastien.Roy@Sun.COM */ 160510616SSebastien.Roy@Sun.COM laddr_change = (ik->iptun_kparam_flags & IPTUN_KPARAM_LADDR); 160610616SSebastien.Roy@Sun.COM raddr_change = (ik->iptun_kparam_flags & IPTUN_KPARAM_RADDR); 160710616SSebastien.Roy@Sun.COM if (laddr_change || raddr_change) { 160810616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_BOUND) 160910616SSebastien.Roy@Sun.COM iptun_unbind(iptun); 161010616SSebastien.Roy@Sun.COM if (iptun_canbind(iptun) && (err = iptun_bind(iptun)) != 0) { 161110616SSebastien.Roy@Sun.COM if (laddr_change) 161210616SSebastien.Roy@Sun.COM iptun->iptun_flags &= ~IPTUN_LADDR; 161310616SSebastien.Roy@Sun.COM if (raddr_change) 161410616SSebastien.Roy@Sun.COM iptun->iptun_flags &= ~IPTUN_RADDR; 161510616SSebastien.Roy@Sun.COM goto done; 161610616SSebastien.Roy@Sun.COM } 161710616SSebastien.Roy@Sun.COM } 161810616SSebastien.Roy@Sun.COM 161910616SSebastien.Roy@Sun.COM if (laddr_change) 162010616SSebastien.Roy@Sun.COM iptun_task_dispatch(iptun, IPTUN_TASK_LADDR_UPDATE); 162110616SSebastien.Roy@Sun.COM if (raddr_change) 162210616SSebastien.Roy@Sun.COM iptun_task_dispatch(iptun, IPTUN_TASK_RADDR_UPDATE); 162310616SSebastien.Roy@Sun.COM 162410616SSebastien.Roy@Sun.COM done: 162510616SSebastien.Roy@Sun.COM iptun_exit(iptun); 162610616SSebastien.Roy@Sun.COM return (err); 162710616SSebastien.Roy@Sun.COM } 162810616SSebastien.Roy@Sun.COM 162910616SSebastien.Roy@Sun.COM /* Given an IP tunnel's datalink id, fill in its parameters. */ 163010616SSebastien.Roy@Sun.COM int 163110616SSebastien.Roy@Sun.COM iptun_info(iptun_kparams_t *ik, cred_t *credp) 163210616SSebastien.Roy@Sun.COM { 163310616SSebastien.Roy@Sun.COM iptun_t *iptun; 163410616SSebastien.Roy@Sun.COM int err; 163510616SSebastien.Roy@Sun.COM 163610616SSebastien.Roy@Sun.COM /* Is the tunnel link visible from the caller's zone? */ 163710616SSebastien.Roy@Sun.COM if (!dls_devnet_islinkvisible(ik->iptun_kparam_linkid, 163810616SSebastien.Roy@Sun.COM crgetzoneid(credp))) 163910616SSebastien.Roy@Sun.COM return (ENOENT); 164010616SSebastien.Roy@Sun.COM 164110616SSebastien.Roy@Sun.COM if ((err = iptun_enter_by_linkid(ik->iptun_kparam_linkid, &iptun)) != 0) 164210616SSebastien.Roy@Sun.COM return (err); 164310616SSebastien.Roy@Sun.COM 164410616SSebastien.Roy@Sun.COM bzero(ik, sizeof (iptun_kparams_t)); 164510616SSebastien.Roy@Sun.COM 164610616SSebastien.Roy@Sun.COM ik->iptun_kparam_linkid = iptun->iptun_linkid; 164710616SSebastien.Roy@Sun.COM ik->iptun_kparam_type = iptun->iptun_typeinfo->iti_type; 164810616SSebastien.Roy@Sun.COM ik->iptun_kparam_flags |= IPTUN_KPARAM_TYPE; 164910616SSebastien.Roy@Sun.COM 165010616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_LADDR) { 165110616SSebastien.Roy@Sun.COM iptun_getaddr(&iptun->iptun_laddr, &ik->iptun_kparam_laddr); 165210616SSebastien.Roy@Sun.COM ik->iptun_kparam_flags |= IPTUN_KPARAM_LADDR; 165310616SSebastien.Roy@Sun.COM } 165410616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_RADDR) { 165510616SSebastien.Roy@Sun.COM iptun_getaddr(&iptun->iptun_raddr, &ik->iptun_kparam_raddr); 165610616SSebastien.Roy@Sun.COM ik->iptun_kparam_flags |= IPTUN_KPARAM_RADDR; 165710616SSebastien.Roy@Sun.COM } 165810616SSebastien.Roy@Sun.COM 165910616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_IMPLICIT) 166010616SSebastien.Roy@Sun.COM ik->iptun_kparam_flags |= IPTUN_KPARAM_IMPLICIT; 166110616SSebastien.Roy@Sun.COM 166210616SSebastien.Roy@Sun.COM if (iptun->iptun_itp != NULL) { 166310616SSebastien.Roy@Sun.COM mutex_enter(&iptun->iptun_itp->itp_lock); 166410616SSebastien.Roy@Sun.COM if (iptun->iptun_itp->itp_flags & ITPF_P_ACTIVE) { 166510616SSebastien.Roy@Sun.COM ik->iptun_kparam_flags |= IPTUN_KPARAM_IPSECPOL; 166610616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_SIMPLE_POLICY) { 166710616SSebastien.Roy@Sun.COM ik->iptun_kparam_flags |= IPTUN_KPARAM_SECINFO; 166810616SSebastien.Roy@Sun.COM ik->iptun_kparam_secinfo = 166910616SSebastien.Roy@Sun.COM iptun->iptun_simple_policy; 167010616SSebastien.Roy@Sun.COM } 167110616SSebastien.Roy@Sun.COM } 167210616SSebastien.Roy@Sun.COM mutex_exit(&iptun->iptun_itp->itp_lock); 167310616SSebastien.Roy@Sun.COM } 167410616SSebastien.Roy@Sun.COM 167510616SSebastien.Roy@Sun.COM done: 167610616SSebastien.Roy@Sun.COM iptun_exit(iptun); 167710616SSebastien.Roy@Sun.COM return (err); 167810616SSebastien.Roy@Sun.COM } 167910616SSebastien.Roy@Sun.COM 168010616SSebastien.Roy@Sun.COM int 168110616SSebastien.Roy@Sun.COM iptun_set_6to4relay(netstack_t *ns, ipaddr_t relay_addr) 168210616SSebastien.Roy@Sun.COM { 168310616SSebastien.Roy@Sun.COM if (relay_addr == INADDR_BROADCAST || CLASSD(relay_addr)) 168410616SSebastien.Roy@Sun.COM return (EADDRNOTAVAIL); 168510616SSebastien.Roy@Sun.COM ns->netstack_iptun->iptuns_relay_rtr_addr = relay_addr; 168610616SSebastien.Roy@Sun.COM return (0); 168710616SSebastien.Roy@Sun.COM } 168810616SSebastien.Roy@Sun.COM 168910616SSebastien.Roy@Sun.COM void 169010616SSebastien.Roy@Sun.COM iptun_get_6to4relay(netstack_t *ns, ipaddr_t *relay_addr) 169110616SSebastien.Roy@Sun.COM { 169210616SSebastien.Roy@Sun.COM *relay_addr = ns->netstack_iptun->iptuns_relay_rtr_addr; 169310616SSebastien.Roy@Sun.COM } 169410616SSebastien.Roy@Sun.COM 169510616SSebastien.Roy@Sun.COM void 169610616SSebastien.Roy@Sun.COM iptun_set_policy(datalink_id_t linkid, ipsec_tun_pol_t *itp) 169710616SSebastien.Roy@Sun.COM { 169810616SSebastien.Roy@Sun.COM iptun_t *iptun; 169910616SSebastien.Roy@Sun.COM 170010616SSebastien.Roy@Sun.COM if (iptun_enter_by_linkid(linkid, &iptun) != 0) 170110616SSebastien.Roy@Sun.COM return; 170210616SSebastien.Roy@Sun.COM if (iptun->iptun_itp != itp) { 170310616SSebastien.Roy@Sun.COM ASSERT(iptun->iptun_itp == NULL); 170410616SSebastien.Roy@Sun.COM ITP_REFHOLD(itp); 170510616SSebastien.Roy@Sun.COM iptun->iptun_itp = itp; 170610616SSebastien.Roy@Sun.COM } 170711456Sdanmcd@sun.com /* 170811456Sdanmcd@sun.com * IPsec policy means IPsec overhead, which means lower MTU. 170911456Sdanmcd@sun.com * Refresh the MTU for this tunnel. 171011456Sdanmcd@sun.com */ 171111456Sdanmcd@sun.com (void) iptun_update_mtu(iptun, NULL, 0); 171210616SSebastien.Roy@Sun.COM iptun_exit(iptun); 171310616SSebastien.Roy@Sun.COM } 171410616SSebastien.Roy@Sun.COM 171510616SSebastien.Roy@Sun.COM /* 171610616SSebastien.Roy@Sun.COM * Obtain the path MTU to the tunnel destination. 171711042SErik.Nordmark@Sun.COM * Can return zero in some cases. 171810616SSebastien.Roy@Sun.COM */ 171910616SSebastien.Roy@Sun.COM static uint32_t 172011042SErik.Nordmark@Sun.COM iptun_get_dst_pmtu(iptun_t *iptun, ip_xmit_attr_t *ixa) 172110616SSebastien.Roy@Sun.COM { 172210616SSebastien.Roy@Sun.COM uint32_t pmtu = 0; 172311042SErik.Nordmark@Sun.COM conn_t *connp = iptun->iptun_connp; 172411042SErik.Nordmark@Sun.COM boolean_t need_rele = B_FALSE; 172510616SSebastien.Roy@Sun.COM 172610616SSebastien.Roy@Sun.COM /* 172711042SErik.Nordmark@Sun.COM * We only obtain the pmtu for tunnels that have a remote tunnel 172811042SErik.Nordmark@Sun.COM * address. 172910616SSebastien.Roy@Sun.COM */ 173010616SSebastien.Roy@Sun.COM if (!(iptun->iptun_flags & IPTUN_RADDR)) 173110616SSebastien.Roy@Sun.COM return (0); 173210616SSebastien.Roy@Sun.COM 173311042SErik.Nordmark@Sun.COM if (ixa == NULL) { 173411042SErik.Nordmark@Sun.COM ixa = conn_get_ixa(connp, B_FALSE); 173511042SErik.Nordmark@Sun.COM if (ixa == NULL) 173611042SErik.Nordmark@Sun.COM return (0); 173711042SErik.Nordmark@Sun.COM need_rele = B_TRUE; 173811042SErik.Nordmark@Sun.COM } 173911042SErik.Nordmark@Sun.COM /* 174011042SErik.Nordmark@Sun.COM * Guard against ICMP errors before we have sent, as well as against 174111042SErik.Nordmark@Sun.COM * and a thread which held conn_ixa. 174211042SErik.Nordmark@Sun.COM */ 174311042SErik.Nordmark@Sun.COM if (ixa->ixa_ire != NULL) { 174411042SErik.Nordmark@Sun.COM pmtu = ip_get_pmtu(ixa); 174511042SErik.Nordmark@Sun.COM 174611042SErik.Nordmark@Sun.COM /* 174711042SErik.Nordmark@Sun.COM * For both IPv4 and IPv6 we can have indication that the outer 174811042SErik.Nordmark@Sun.COM * header needs fragmentation. 174911042SErik.Nordmark@Sun.COM */ 175011042SErik.Nordmark@Sun.COM if (ixa->ixa_flags & IXAF_PMTU_TOO_SMALL) { 175111042SErik.Nordmark@Sun.COM /* Must allow fragmentation in ip_output */ 175211042SErik.Nordmark@Sun.COM ixa->ixa_flags &= ~IXAF_DONTFRAG; 175311042SErik.Nordmark@Sun.COM } else if (iptun->iptun_typeinfo->iti_type != IPTUN_TYPE_6TO4) { 175411042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_DONTFRAG; 175511042SErik.Nordmark@Sun.COM } else { 175611042SErik.Nordmark@Sun.COM /* ip_get_pmtu might have set this - we don't want it */ 175711042SErik.Nordmark@Sun.COM ixa->ixa_flags &= ~IXAF_PMTU_IPV4_DF; 175811042SErik.Nordmark@Sun.COM } 175911042SErik.Nordmark@Sun.COM } 176011042SErik.Nordmark@Sun.COM 176111042SErik.Nordmark@Sun.COM if (need_rele) 176211042SErik.Nordmark@Sun.COM ixa_refrele(ixa); 176311042SErik.Nordmark@Sun.COM return (pmtu); 176411042SErik.Nordmark@Sun.COM } 176511042SErik.Nordmark@Sun.COM 176611042SErik.Nordmark@Sun.COM /* 176711042SErik.Nordmark@Sun.COM * Update the ip_xmit_attr_t to capture the current lower path mtu as known 176811042SErik.Nordmark@Sun.COM * by ip. 176911042SErik.Nordmark@Sun.COM */ 177011042SErik.Nordmark@Sun.COM static void 177111042SErik.Nordmark@Sun.COM iptun_update_dst_pmtu(iptun_t *iptun, ip_xmit_attr_t *ixa) 177211042SErik.Nordmark@Sun.COM { 177311042SErik.Nordmark@Sun.COM uint32_t pmtu; 177411042SErik.Nordmark@Sun.COM conn_t *connp = iptun->iptun_connp; 177511042SErik.Nordmark@Sun.COM boolean_t need_rele = B_FALSE; 177611042SErik.Nordmark@Sun.COM 177711042SErik.Nordmark@Sun.COM /* IXAF_VERIFY_PMTU is not set if we don't have a fixed destination */ 177811042SErik.Nordmark@Sun.COM if (!(iptun->iptun_flags & IPTUN_RADDR)) 177911042SErik.Nordmark@Sun.COM return; 178011042SErik.Nordmark@Sun.COM 178111042SErik.Nordmark@Sun.COM if (ixa == NULL) { 178211042SErik.Nordmark@Sun.COM ixa = conn_get_ixa(connp, B_FALSE); 178311042SErik.Nordmark@Sun.COM if (ixa == NULL) 178411042SErik.Nordmark@Sun.COM return; 178511042SErik.Nordmark@Sun.COM need_rele = B_TRUE; 178611042SErik.Nordmark@Sun.COM } 178711042SErik.Nordmark@Sun.COM /* 178811042SErik.Nordmark@Sun.COM * Guard against ICMP errors before we have sent, as well as against 178911042SErik.Nordmark@Sun.COM * and a thread which held conn_ixa. 179011042SErik.Nordmark@Sun.COM */ 179111042SErik.Nordmark@Sun.COM if (ixa->ixa_ire != NULL) { 179211042SErik.Nordmark@Sun.COM pmtu = ip_get_pmtu(ixa); 179311042SErik.Nordmark@Sun.COM /* 179411042SErik.Nordmark@Sun.COM * Update ixa_fragsize and ixa_pmtu. 179511042SErik.Nordmark@Sun.COM */ 179611042SErik.Nordmark@Sun.COM ixa->ixa_fragsize = ixa->ixa_pmtu = pmtu; 179711042SErik.Nordmark@Sun.COM 179811042SErik.Nordmark@Sun.COM /* 179911042SErik.Nordmark@Sun.COM * For both IPv4 and IPv6 we can have indication that the outer 180011042SErik.Nordmark@Sun.COM * header needs fragmentation. 180111042SErik.Nordmark@Sun.COM */ 180211042SErik.Nordmark@Sun.COM if (ixa->ixa_flags & IXAF_PMTU_TOO_SMALL) { 180311042SErik.Nordmark@Sun.COM /* Must allow fragmentation in ip_output */ 180411042SErik.Nordmark@Sun.COM ixa->ixa_flags &= ~IXAF_DONTFRAG; 180511042SErik.Nordmark@Sun.COM } else if (iptun->iptun_typeinfo->iti_type != IPTUN_TYPE_6TO4) { 180611042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_DONTFRAG; 180711042SErik.Nordmark@Sun.COM } else { 180811042SErik.Nordmark@Sun.COM /* ip_get_pmtu might have set this - we don't want it */ 180911042SErik.Nordmark@Sun.COM ixa->ixa_flags &= ~IXAF_PMTU_IPV4_DF; 181011042SErik.Nordmark@Sun.COM } 181111042SErik.Nordmark@Sun.COM } 181211042SErik.Nordmark@Sun.COM 181311042SErik.Nordmark@Sun.COM if (need_rele) 181411042SErik.Nordmark@Sun.COM ixa_refrele(ixa); 181511042SErik.Nordmark@Sun.COM } 181611042SErik.Nordmark@Sun.COM 181711042SErik.Nordmark@Sun.COM /* 181811042SErik.Nordmark@Sun.COM * There is nothing that iptun can verify in addition to IP having 181911042SErik.Nordmark@Sun.COM * verified the IP addresses in the fanout. 182011042SErik.Nordmark@Sun.COM */ 182111042SErik.Nordmark@Sun.COM /* ARGSUSED */ 182211042SErik.Nordmark@Sun.COM static boolean_t 182311042SErik.Nordmark@Sun.COM iptun_verifyicmp(conn_t *connp, void *arg2, icmph_t *icmph, icmp6_t *icmp6, 182411042SErik.Nordmark@Sun.COM ip_recv_attr_t *ira) 182511042SErik.Nordmark@Sun.COM { 182611042SErik.Nordmark@Sun.COM return (B_TRUE); 182711042SErik.Nordmark@Sun.COM } 182811042SErik.Nordmark@Sun.COM 182911042SErik.Nordmark@Sun.COM /* 183011042SErik.Nordmark@Sun.COM * Notify function registered with ip_xmit_attr_t. 183111042SErik.Nordmark@Sun.COM */ 183211042SErik.Nordmark@Sun.COM static void 183311042SErik.Nordmark@Sun.COM iptun_notify(void *arg, ip_xmit_attr_t *ixa, ixa_notify_type_t ntype, 183411042SErik.Nordmark@Sun.COM ixa_notify_arg_t narg) 183511042SErik.Nordmark@Sun.COM { 183611042SErik.Nordmark@Sun.COM iptun_t *iptun = (iptun_t *)arg; 183711042SErik.Nordmark@Sun.COM 183811042SErik.Nordmark@Sun.COM switch (ntype) { 183911042SErik.Nordmark@Sun.COM case IXAN_PMTU: 184011042SErik.Nordmark@Sun.COM (void) iptun_update_mtu(iptun, ixa, narg); 184110616SSebastien.Roy@Sun.COM break; 184210616SSebastien.Roy@Sun.COM } 184310616SSebastien.Roy@Sun.COM } 184410616SSebastien.Roy@Sun.COM 184510616SSebastien.Roy@Sun.COM /* 184610616SSebastien.Roy@Sun.COM * Returns the max of old_ovhd and the overhead associated with pol. 184710616SSebastien.Roy@Sun.COM */ 184810616SSebastien.Roy@Sun.COM static uint32_t 184910616SSebastien.Roy@Sun.COM iptun_max_policy_overhead(ipsec_policy_t *pol, uint32_t old_ovhd) 185010616SSebastien.Roy@Sun.COM { 185110616SSebastien.Roy@Sun.COM uint32_t new_ovhd = old_ovhd; 185210616SSebastien.Roy@Sun.COM 185310616SSebastien.Roy@Sun.COM while (pol != NULL) { 185410616SSebastien.Roy@Sun.COM new_ovhd = max(new_ovhd, 185510616SSebastien.Roy@Sun.COM ipsec_act_ovhd(&pol->ipsp_act->ipa_act)); 185610616SSebastien.Roy@Sun.COM pol = pol->ipsp_hash.hash_next; 185710616SSebastien.Roy@Sun.COM } 185810616SSebastien.Roy@Sun.COM return (new_ovhd); 185910616SSebastien.Roy@Sun.COM } 186010616SSebastien.Roy@Sun.COM 186110616SSebastien.Roy@Sun.COM static uint32_t 186210616SSebastien.Roy@Sun.COM iptun_get_ipsec_overhead(iptun_t *iptun) 186310616SSebastien.Roy@Sun.COM { 186410616SSebastien.Roy@Sun.COM ipsec_policy_root_t *ipr; 186510616SSebastien.Roy@Sun.COM ipsec_policy_head_t *iph; 186610616SSebastien.Roy@Sun.COM ipsec_policy_t *pol; 186710616SSebastien.Roy@Sun.COM ipsec_selector_t sel; 186810616SSebastien.Roy@Sun.COM int i; 186910616SSebastien.Roy@Sun.COM uint32_t ipsec_ovhd = 0; 187010616SSebastien.Roy@Sun.COM ipsec_tun_pol_t *itp = iptun->iptun_itp; 187110616SSebastien.Roy@Sun.COM netstack_t *ns = iptun->iptun_ns; 187210616SSebastien.Roy@Sun.COM 187310616SSebastien.Roy@Sun.COM if (itp == NULL || !(itp->itp_flags & ITPF_P_ACTIVE)) { 187410616SSebastien.Roy@Sun.COM /* 187510616SSebastien.Roy@Sun.COM * Consult global policy, just in case. This will only work 187610616SSebastien.Roy@Sun.COM * if we have both source and destination addresses to work 187710616SSebastien.Roy@Sun.COM * with. 187810616SSebastien.Roy@Sun.COM */ 187910616SSebastien.Roy@Sun.COM if ((iptun->iptun_flags & (IPTUN_LADDR|IPTUN_RADDR)) != 188010616SSebastien.Roy@Sun.COM (IPTUN_LADDR|IPTUN_RADDR)) 188110616SSebastien.Roy@Sun.COM return (0); 188210616SSebastien.Roy@Sun.COM 188310616SSebastien.Roy@Sun.COM iph = ipsec_system_policy(ns); 188410616SSebastien.Roy@Sun.COM bzero(&sel, sizeof (sel)); 188510616SSebastien.Roy@Sun.COM sel.ips_isv4 = 188610616SSebastien.Roy@Sun.COM (iptun->iptun_typeinfo->iti_ipvers == IPV4_VERSION); 188710616SSebastien.Roy@Sun.COM switch (iptun->iptun_typeinfo->iti_ipvers) { 188810616SSebastien.Roy@Sun.COM case IPV4_VERSION: 188910616SSebastien.Roy@Sun.COM sel.ips_local_addr_v4 = iptun->iptun_laddr4; 189010616SSebastien.Roy@Sun.COM sel.ips_remote_addr_v4 = iptun->iptun_raddr4; 189110616SSebastien.Roy@Sun.COM break; 189210616SSebastien.Roy@Sun.COM case IPV6_VERSION: 189310616SSebastien.Roy@Sun.COM sel.ips_local_addr_v6 = iptun->iptun_laddr6; 189410616SSebastien.Roy@Sun.COM sel.ips_remote_addr_v6 = iptun->iptun_raddr6; 189510616SSebastien.Roy@Sun.COM break; 189610616SSebastien.Roy@Sun.COM } 189710616SSebastien.Roy@Sun.COM /* Check for both IPv4 and IPv6. */ 189810616SSebastien.Roy@Sun.COM sel.ips_protocol = IPPROTO_ENCAP; 189910616SSebastien.Roy@Sun.COM pol = ipsec_find_policy_head(NULL, iph, IPSEC_TYPE_OUTBOUND, 190011042SErik.Nordmark@Sun.COM &sel); 190110616SSebastien.Roy@Sun.COM if (pol != NULL) { 190210616SSebastien.Roy@Sun.COM ipsec_ovhd = ipsec_act_ovhd(&pol->ipsp_act->ipa_act); 190311042SErik.Nordmark@Sun.COM IPPOL_REFRELE(pol); 190410616SSebastien.Roy@Sun.COM } 190510616SSebastien.Roy@Sun.COM sel.ips_protocol = IPPROTO_IPV6; 190610616SSebastien.Roy@Sun.COM pol = ipsec_find_policy_head(NULL, iph, IPSEC_TYPE_OUTBOUND, 190711042SErik.Nordmark@Sun.COM &sel); 190810616SSebastien.Roy@Sun.COM if (pol != NULL) { 190910616SSebastien.Roy@Sun.COM ipsec_ovhd = max(ipsec_ovhd, 191010616SSebastien.Roy@Sun.COM ipsec_act_ovhd(&pol->ipsp_act->ipa_act)); 191111042SErik.Nordmark@Sun.COM IPPOL_REFRELE(pol); 191210616SSebastien.Roy@Sun.COM } 191310616SSebastien.Roy@Sun.COM IPPH_REFRELE(iph, ns); 191410616SSebastien.Roy@Sun.COM } else { 191510616SSebastien.Roy@Sun.COM /* 191610616SSebastien.Roy@Sun.COM * Look through all of the possible IPsec actions for the 191710616SSebastien.Roy@Sun.COM * tunnel, and find the largest potential IPsec overhead. 191810616SSebastien.Roy@Sun.COM */ 191910616SSebastien.Roy@Sun.COM iph = itp->itp_policy; 192010616SSebastien.Roy@Sun.COM rw_enter(&iph->iph_lock, RW_READER); 192110616SSebastien.Roy@Sun.COM ipr = &(iph->iph_root[IPSEC_TYPE_OUTBOUND]); 192210616SSebastien.Roy@Sun.COM ipsec_ovhd = iptun_max_policy_overhead( 192310616SSebastien.Roy@Sun.COM ipr->ipr_nonhash[IPSEC_AF_V4], 0); 192410616SSebastien.Roy@Sun.COM ipsec_ovhd = iptun_max_policy_overhead( 192510616SSebastien.Roy@Sun.COM ipr->ipr_nonhash[IPSEC_AF_V6], ipsec_ovhd); 192610616SSebastien.Roy@Sun.COM for (i = 0; i < ipr->ipr_nchains; i++) { 192710616SSebastien.Roy@Sun.COM ipsec_ovhd = iptun_max_policy_overhead( 192810616SSebastien.Roy@Sun.COM ipr->ipr_hash[i].hash_head, ipsec_ovhd); 192910616SSebastien.Roy@Sun.COM } 193010616SSebastien.Roy@Sun.COM rw_exit(&iph->iph_lock); 193110616SSebastien.Roy@Sun.COM } 193210616SSebastien.Roy@Sun.COM 193310616SSebastien.Roy@Sun.COM return (ipsec_ovhd); 193410616SSebastien.Roy@Sun.COM } 193510616SSebastien.Roy@Sun.COM 193610616SSebastien.Roy@Sun.COM /* 193711042SErik.Nordmark@Sun.COM * Calculate and return the maximum possible upper MTU for the given tunnel. 193811042SErik.Nordmark@Sun.COM * 193911042SErik.Nordmark@Sun.COM * If new_pmtu is set then we also need to update the lower path MTU information 194011042SErik.Nordmark@Sun.COM * in the ip_xmit_attr_t. That is needed since we set IXAF_VERIFY_PMTU so that 194111042SErik.Nordmark@Sun.COM * we are notified by conn_ip_output() when the path MTU increases. 194210616SSebastien.Roy@Sun.COM */ 194310616SSebastien.Roy@Sun.COM static uint32_t 194411042SErik.Nordmark@Sun.COM iptun_get_maxmtu(iptun_t *iptun, ip_xmit_attr_t *ixa, uint32_t new_pmtu) 194510616SSebastien.Roy@Sun.COM { 194610616SSebastien.Roy@Sun.COM size_t header_size, ipsec_overhead; 194710616SSebastien.Roy@Sun.COM uint32_t maxmtu, pmtu; 194810616SSebastien.Roy@Sun.COM 194910616SSebastien.Roy@Sun.COM /* 195010616SSebastien.Roy@Sun.COM * Start with the path-MTU to the remote address, which is either 195110616SSebastien.Roy@Sun.COM * provided as the new_pmtu argument, or obtained using 195210616SSebastien.Roy@Sun.COM * iptun_get_dst_pmtu(). 195310616SSebastien.Roy@Sun.COM */ 195410616SSebastien.Roy@Sun.COM if (new_pmtu != 0) { 195511042SErik.Nordmark@Sun.COM if (iptun->iptun_flags & IPTUN_RADDR) 195610616SSebastien.Roy@Sun.COM iptun->iptun_dpmtu = new_pmtu; 195710616SSebastien.Roy@Sun.COM pmtu = new_pmtu; 195810616SSebastien.Roy@Sun.COM } else if (iptun->iptun_flags & IPTUN_RADDR) { 195911042SErik.Nordmark@Sun.COM if ((pmtu = iptun_get_dst_pmtu(iptun, ixa)) == 0) { 196010616SSebastien.Roy@Sun.COM /* 196110616SSebastien.Roy@Sun.COM * We weren't able to obtain the path-MTU of the 196210616SSebastien.Roy@Sun.COM * destination. Use the previous value. 196310616SSebastien.Roy@Sun.COM */ 196410616SSebastien.Roy@Sun.COM pmtu = iptun->iptun_dpmtu; 196510616SSebastien.Roy@Sun.COM } else { 196610616SSebastien.Roy@Sun.COM iptun->iptun_dpmtu = pmtu; 196710616SSebastien.Roy@Sun.COM } 196810616SSebastien.Roy@Sun.COM } else { 196910616SSebastien.Roy@Sun.COM /* 197010616SSebastien.Roy@Sun.COM * We have no path-MTU information to go on, use the maximum 197110616SSebastien.Roy@Sun.COM * possible value. 197210616SSebastien.Roy@Sun.COM */ 197310616SSebastien.Roy@Sun.COM pmtu = iptun->iptun_typeinfo->iti_maxmtu; 197410616SSebastien.Roy@Sun.COM } 197510616SSebastien.Roy@Sun.COM 197610616SSebastien.Roy@Sun.COM /* 197710616SSebastien.Roy@Sun.COM * Now calculate tunneling overhead and subtract that from the 197810616SSebastien.Roy@Sun.COM * path-MTU information obtained above. 197910616SSebastien.Roy@Sun.COM */ 198010616SSebastien.Roy@Sun.COM if (iptun->iptun_header_size != 0) { 198110616SSebastien.Roy@Sun.COM header_size = iptun->iptun_header_size; 198210616SSebastien.Roy@Sun.COM } else { 198310616SSebastien.Roy@Sun.COM switch (iptun->iptun_typeinfo->iti_ipvers) { 198410616SSebastien.Roy@Sun.COM case IPV4_VERSION: 198510616SSebastien.Roy@Sun.COM header_size = sizeof (ipha_t); 198610934Ssommerfeld@sun.com if (is_system_labeled()) 198710934Ssommerfeld@sun.com header_size += IP_MAX_OPT_LENGTH; 198810616SSebastien.Roy@Sun.COM break; 198910616SSebastien.Roy@Sun.COM case IPV6_VERSION: 199010616SSebastien.Roy@Sun.COM header_size = sizeof (iptun_ipv6hdrs_t); 199110616SSebastien.Roy@Sun.COM break; 199210616SSebastien.Roy@Sun.COM } 199310616SSebastien.Roy@Sun.COM } 199410616SSebastien.Roy@Sun.COM 199510616SSebastien.Roy@Sun.COM ipsec_overhead = iptun_get_ipsec_overhead(iptun); 199610616SSebastien.Roy@Sun.COM 199710616SSebastien.Roy@Sun.COM maxmtu = pmtu - (header_size + ipsec_overhead); 199810616SSebastien.Roy@Sun.COM return (max(maxmtu, iptun->iptun_typeinfo->iti_minmtu)); 199910616SSebastien.Roy@Sun.COM } 200010616SSebastien.Roy@Sun.COM 200110616SSebastien.Roy@Sun.COM /* 200211042SErik.Nordmark@Sun.COM * Re-calculate the tunnel's MTU as seen from above and notify the MAC layer 200311042SErik.Nordmark@Sun.COM * of any change in MTU. The new_pmtu argument is the new lower path MTU to 200411042SErik.Nordmark@Sun.COM * the tunnel destination to be used in the tunnel MTU calculation. Passing 200511042SErik.Nordmark@Sun.COM * in 0 for new_pmtu causes the lower path MTU to be dynamically updated using 200611042SErik.Nordmark@Sun.COM * ip_get_pmtu(). 200710616SSebastien.Roy@Sun.COM * 200810616SSebastien.Roy@Sun.COM * If the calculated tunnel MTU is different than its previous value, then we 200910616SSebastien.Roy@Sun.COM * notify the MAC layer above us of this change using mac_maxsdu_update(). 201010616SSebastien.Roy@Sun.COM */ 201110616SSebastien.Roy@Sun.COM static uint32_t 201211042SErik.Nordmark@Sun.COM iptun_update_mtu(iptun_t *iptun, ip_xmit_attr_t *ixa, uint32_t new_pmtu) 201310616SSebastien.Roy@Sun.COM { 201410616SSebastien.Roy@Sun.COM uint32_t newmtu; 201510616SSebastien.Roy@Sun.COM 201611042SErik.Nordmark@Sun.COM /* We always update the ixa since we might have set IXAF_VERIFY_PMTU */ 201711042SErik.Nordmark@Sun.COM iptun_update_dst_pmtu(iptun, ixa); 201811042SErik.Nordmark@Sun.COM 201910616SSebastien.Roy@Sun.COM /* 202010616SSebastien.Roy@Sun.COM * We return the current MTU without updating it if it was pegged to a 202110616SSebastien.Roy@Sun.COM * static value using the MAC_PROP_MTU link property. 202210616SSebastien.Roy@Sun.COM */ 202310616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_FIXED_MTU) 202410616SSebastien.Roy@Sun.COM return (iptun->iptun_mtu); 202510616SSebastien.Roy@Sun.COM 202610616SSebastien.Roy@Sun.COM /* If the MTU isn't fixed, then use the maximum possible value. */ 202711042SErik.Nordmark@Sun.COM newmtu = iptun_get_maxmtu(iptun, ixa, new_pmtu); 202810616SSebastien.Roy@Sun.COM /* 202910616SSebastien.Roy@Sun.COM * We only dynamically adjust the tunnel MTU for tunnels with 203010616SSebastien.Roy@Sun.COM * destinations because dynamic MTU calculations are based on the 203110616SSebastien.Roy@Sun.COM * destination path-MTU. 203210616SSebastien.Roy@Sun.COM */ 203310616SSebastien.Roy@Sun.COM if ((iptun->iptun_flags & IPTUN_RADDR) && newmtu != iptun->iptun_mtu) { 203410616SSebastien.Roy@Sun.COM iptun->iptun_mtu = newmtu; 203510616SSebastien.Roy@Sun.COM if (iptun->iptun_flags & IPTUN_MAC_REGISTERED) 203610616SSebastien.Roy@Sun.COM iptun_task_dispatch(iptun, IPTUN_TASK_MTU_UPDATE); 203710616SSebastien.Roy@Sun.COM } 203810616SSebastien.Roy@Sun.COM 203910616SSebastien.Roy@Sun.COM return (newmtu); 204010616SSebastien.Roy@Sun.COM } 204110616SSebastien.Roy@Sun.COM 204210616SSebastien.Roy@Sun.COM /* 204310616SSebastien.Roy@Sun.COM * Frees a packet or packet chain and bumps stat for each freed packet. 204410616SSebastien.Roy@Sun.COM */ 204510616SSebastien.Roy@Sun.COM static void 204610616SSebastien.Roy@Sun.COM iptun_drop_pkt(mblk_t *mp, uint64_t *stat) 204710616SSebastien.Roy@Sun.COM { 204810616SSebastien.Roy@Sun.COM mblk_t *pktmp; 204910616SSebastien.Roy@Sun.COM 205010616SSebastien.Roy@Sun.COM for (pktmp = mp; pktmp != NULL; pktmp = mp) { 205110616SSebastien.Roy@Sun.COM mp = mp->b_next; 205210616SSebastien.Roy@Sun.COM pktmp->b_next = NULL; 205310616SSebastien.Roy@Sun.COM if (stat != NULL) 205410616SSebastien.Roy@Sun.COM atomic_inc_64(stat); 205510616SSebastien.Roy@Sun.COM freemsg(pktmp); 205610616SSebastien.Roy@Sun.COM } 205710616SSebastien.Roy@Sun.COM } 205810616SSebastien.Roy@Sun.COM 205910616SSebastien.Roy@Sun.COM /* 206010616SSebastien.Roy@Sun.COM * Allocate and return a new mblk to hold an IP and ICMP header, and chain the 206110616SSebastien.Roy@Sun.COM * original packet to its b_cont. Returns NULL on failure. 206210616SSebastien.Roy@Sun.COM */ 206310616SSebastien.Roy@Sun.COM static mblk_t * 206410616SSebastien.Roy@Sun.COM iptun_build_icmperr(size_t hdrs_size, mblk_t *orig_pkt) 206510616SSebastien.Roy@Sun.COM { 206610616SSebastien.Roy@Sun.COM mblk_t *icmperr_mp; 206710616SSebastien.Roy@Sun.COM 206811042SErik.Nordmark@Sun.COM if ((icmperr_mp = allocb(hdrs_size, BPRI_MED)) != NULL) { 206910616SSebastien.Roy@Sun.COM icmperr_mp->b_wptr += hdrs_size; 207010616SSebastien.Roy@Sun.COM /* tack on the offending packet */ 207110616SSebastien.Roy@Sun.COM icmperr_mp->b_cont = orig_pkt; 207210616SSebastien.Roy@Sun.COM } 207310616SSebastien.Roy@Sun.COM return (icmperr_mp); 207410616SSebastien.Roy@Sun.COM } 207510616SSebastien.Roy@Sun.COM 207610616SSebastien.Roy@Sun.COM /* 207710616SSebastien.Roy@Sun.COM * Transmit an ICMP error. mp->b_rptr points at the packet to be included in 207810616SSebastien.Roy@Sun.COM * the ICMP error. 207910616SSebastien.Roy@Sun.COM */ 208010616SSebastien.Roy@Sun.COM static void 208111042SErik.Nordmark@Sun.COM iptun_sendicmp_v4(iptun_t *iptun, icmph_t *icmp, ipha_t *orig_ipha, mblk_t *mp, 208211042SErik.Nordmark@Sun.COM ts_label_t *tsl) 208310616SSebastien.Roy@Sun.COM { 208410616SSebastien.Roy@Sun.COM size_t orig_pktsize, hdrs_size; 208510616SSebastien.Roy@Sun.COM mblk_t *icmperr_mp; 208610616SSebastien.Roy@Sun.COM ipha_t *new_ipha; 208710616SSebastien.Roy@Sun.COM icmph_t *new_icmp; 208811042SErik.Nordmark@Sun.COM ip_xmit_attr_t ixas; 208911042SErik.Nordmark@Sun.COM conn_t *connp = iptun->iptun_connp; 209010616SSebastien.Roy@Sun.COM 209110616SSebastien.Roy@Sun.COM orig_pktsize = msgdsize(mp); 209210616SSebastien.Roy@Sun.COM hdrs_size = sizeof (ipha_t) + sizeof (icmph_t); 209310616SSebastien.Roy@Sun.COM if ((icmperr_mp = iptun_build_icmperr(hdrs_size, mp)) == NULL) { 209410616SSebastien.Roy@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_noxmtbuf); 209510616SSebastien.Roy@Sun.COM return; 209610616SSebastien.Roy@Sun.COM } 209710616SSebastien.Roy@Sun.COM 209810616SSebastien.Roy@Sun.COM new_ipha = (ipha_t *)icmperr_mp->b_rptr; 209910616SSebastien.Roy@Sun.COM new_icmp = (icmph_t *)(new_ipha + 1); 210010616SSebastien.Roy@Sun.COM 210110616SSebastien.Roy@Sun.COM new_ipha->ipha_version_and_hdr_length = IP_SIMPLE_HDR_VERSION; 210210616SSebastien.Roy@Sun.COM new_ipha->ipha_type_of_service = 0; 210310616SSebastien.Roy@Sun.COM new_ipha->ipha_ident = 0; 210410616SSebastien.Roy@Sun.COM new_ipha->ipha_fragment_offset_and_flags = 0; 210510616SSebastien.Roy@Sun.COM new_ipha->ipha_ttl = orig_ipha->ipha_ttl; 210610616SSebastien.Roy@Sun.COM new_ipha->ipha_protocol = IPPROTO_ICMP; 210710616SSebastien.Roy@Sun.COM new_ipha->ipha_src = orig_ipha->ipha_dst; 210810616SSebastien.Roy@Sun.COM new_ipha->ipha_dst = orig_ipha->ipha_src; 210910616SSebastien.Roy@Sun.COM new_ipha->ipha_hdr_checksum = 0; /* will be computed by ip */ 211010616SSebastien.Roy@Sun.COM new_ipha->ipha_length = htons(hdrs_size + orig_pktsize); 211110616SSebastien.Roy@Sun.COM 211210616SSebastien.Roy@Sun.COM *new_icmp = *icmp; 211310616SSebastien.Roy@Sun.COM new_icmp->icmph_checksum = 0; 211410616SSebastien.Roy@Sun.COM new_icmp->icmph_checksum = IP_CSUM(icmperr_mp, sizeof (ipha_t), 0); 211510616SSebastien.Roy@Sun.COM 211611042SErik.Nordmark@Sun.COM bzero(&ixas, sizeof (ixas)); 211711042SErik.Nordmark@Sun.COM ixas.ixa_flags = IXAF_BASIC_SIMPLE_V4; 2118*11983SSowmini.Varadhan@Sun.COM if (new_ipha->ipha_src == INADDR_ANY) { 2119*11983SSowmini.Varadhan@Sun.COM ixas.ixa_flags &= ~IXAF_VERIFY_SOURCE; 212011042SErik.Nordmark@Sun.COM ixas.ixa_flags |= IXAF_SET_SOURCE; 2121*11983SSowmini.Varadhan@Sun.COM } 212211042SErik.Nordmark@Sun.COM 212311042SErik.Nordmark@Sun.COM ixas.ixa_zoneid = IPCL_ZONEID(connp); 212411042SErik.Nordmark@Sun.COM ixas.ixa_ipst = connp->conn_netstack->netstack_ip; 212511042SErik.Nordmark@Sun.COM ixas.ixa_cred = connp->conn_cred; 212611042SErik.Nordmark@Sun.COM ixas.ixa_cpid = NOPID; 212711042SErik.Nordmark@Sun.COM if (is_system_labeled()) 212811042SErik.Nordmark@Sun.COM ixas.ixa_tsl = tsl; 212911042SErik.Nordmark@Sun.COM 213011042SErik.Nordmark@Sun.COM ixas.ixa_ifindex = 0; 213111042SErik.Nordmark@Sun.COM ixas.ixa_multicast_ttl = IP_DEFAULT_MULTICAST_TTL; 213211042SErik.Nordmark@Sun.COM 213311042SErik.Nordmark@Sun.COM (void) ip_output_simple(icmperr_mp, &ixas); 213411042SErik.Nordmark@Sun.COM ixa_cleanup(&ixas); 213510616SSebastien.Roy@Sun.COM } 213610616SSebastien.Roy@Sun.COM 213710616SSebastien.Roy@Sun.COM static void 213811042SErik.Nordmark@Sun.COM iptun_sendicmp_v6(iptun_t *iptun, icmp6_t *icmp6, ip6_t *orig_ip6h, mblk_t *mp, 213911042SErik.Nordmark@Sun.COM ts_label_t *tsl) 214010616SSebastien.Roy@Sun.COM { 214110616SSebastien.Roy@Sun.COM size_t orig_pktsize, hdrs_size; 214210616SSebastien.Roy@Sun.COM mblk_t *icmp6err_mp; 214310616SSebastien.Roy@Sun.COM ip6_t *new_ip6h; 214410616SSebastien.Roy@Sun.COM icmp6_t *new_icmp6; 214511042SErik.Nordmark@Sun.COM ip_xmit_attr_t ixas; 214611042SErik.Nordmark@Sun.COM conn_t *connp = iptun->iptun_connp; 214710616SSebastien.Roy@Sun.COM 214810616SSebastien.Roy@Sun.COM orig_pktsize = msgdsize(mp); 214910616SSebastien.Roy@Sun.COM hdrs_size = sizeof (ip6_t) + sizeof (icmp6_t); 215010616SSebastien.Roy@Sun.COM if ((icmp6err_mp = iptun_build_icmperr(hdrs_size, mp)) == NULL) { 215110616SSebastien.Roy@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_noxmtbuf); 215210616SSebastien.Roy@Sun.COM return; 215310616SSebastien.Roy@Sun.COM } 215410616SSebastien.Roy@Sun.COM 215510616SSebastien.Roy@Sun.COM new_ip6h = (ip6_t *)icmp6err_mp->b_rptr; 215610616SSebastien.Roy@Sun.COM new_icmp6 = (icmp6_t *)(new_ip6h + 1); 215710616SSebastien.Roy@Sun.COM 215810616SSebastien.Roy@Sun.COM new_ip6h->ip6_vcf = orig_ip6h->ip6_vcf; 215910616SSebastien.Roy@Sun.COM new_ip6h->ip6_plen = htons(sizeof (icmp6_t) + orig_pktsize); 216010616SSebastien.Roy@Sun.COM new_ip6h->ip6_hops = orig_ip6h->ip6_hops; 216110616SSebastien.Roy@Sun.COM new_ip6h->ip6_nxt = IPPROTO_ICMPV6; 216210616SSebastien.Roy@Sun.COM new_ip6h->ip6_src = orig_ip6h->ip6_dst; 216310616SSebastien.Roy@Sun.COM new_ip6h->ip6_dst = orig_ip6h->ip6_src; 216410616SSebastien.Roy@Sun.COM 216510616SSebastien.Roy@Sun.COM *new_icmp6 = *icmp6; 216611042SErik.Nordmark@Sun.COM /* The checksum is calculated in ip_output_simple and friends. */ 216710616SSebastien.Roy@Sun.COM new_icmp6->icmp6_cksum = new_ip6h->ip6_plen; 216810616SSebastien.Roy@Sun.COM 216911042SErik.Nordmark@Sun.COM bzero(&ixas, sizeof (ixas)); 217011042SErik.Nordmark@Sun.COM ixas.ixa_flags = IXAF_BASIC_SIMPLE_V6; 2171*11983SSowmini.Varadhan@Sun.COM if (IN6_IS_ADDR_UNSPECIFIED(&new_ip6h->ip6_src)) { 2172*11983SSowmini.Varadhan@Sun.COM ixas.ixa_flags &= ~IXAF_VERIFY_SOURCE; 217311042SErik.Nordmark@Sun.COM ixas.ixa_flags |= IXAF_SET_SOURCE; 2174*11983SSowmini.Varadhan@Sun.COM } 217511042SErik.Nordmark@Sun.COM 217611042SErik.Nordmark@Sun.COM ixas.ixa_zoneid = IPCL_ZONEID(connp); 217711042SErik.Nordmark@Sun.COM ixas.ixa_ipst = connp->conn_netstack->netstack_ip; 217811042SErik.Nordmark@Sun.COM ixas.ixa_cred = connp->conn_cred; 217911042SErik.Nordmark@Sun.COM ixas.ixa_cpid = NOPID; 218011042SErik.Nordmark@Sun.COM if (is_system_labeled()) 218111042SErik.Nordmark@Sun.COM ixas.ixa_tsl = tsl; 218211042SErik.Nordmark@Sun.COM 218311042SErik.Nordmark@Sun.COM ixas.ixa_ifindex = 0; 218411042SErik.Nordmark@Sun.COM ixas.ixa_multicast_ttl = IP_DEFAULT_MULTICAST_TTL; 218511042SErik.Nordmark@Sun.COM 218611042SErik.Nordmark@Sun.COM (void) ip_output_simple(icmp6err_mp, &ixas); 218711042SErik.Nordmark@Sun.COM ixa_cleanup(&ixas); 218810616SSebastien.Roy@Sun.COM } 218910616SSebastien.Roy@Sun.COM 219010616SSebastien.Roy@Sun.COM static void 219110616SSebastien.Roy@Sun.COM iptun_icmp_error_v4(iptun_t *iptun, ipha_t *orig_ipha, mblk_t *mp, 219211042SErik.Nordmark@Sun.COM uint8_t type, uint8_t code, ts_label_t *tsl) 219310616SSebastien.Roy@Sun.COM { 219410616SSebastien.Roy@Sun.COM icmph_t icmp; 219510616SSebastien.Roy@Sun.COM 219610616SSebastien.Roy@Sun.COM bzero(&icmp, sizeof (icmp)); 219710616SSebastien.Roy@Sun.COM icmp.icmph_type = type; 219810616SSebastien.Roy@Sun.COM icmp.icmph_code = code; 219910616SSebastien.Roy@Sun.COM 220011042SErik.Nordmark@Sun.COM iptun_sendicmp_v4(iptun, &icmp, orig_ipha, mp, tsl); 220110616SSebastien.Roy@Sun.COM } 220210616SSebastien.Roy@Sun.COM 220310616SSebastien.Roy@Sun.COM static void 220410616SSebastien.Roy@Sun.COM iptun_icmp_fragneeded_v4(iptun_t *iptun, uint32_t newmtu, ipha_t *orig_ipha, 220511042SErik.Nordmark@Sun.COM mblk_t *mp, ts_label_t *tsl) 220610616SSebastien.Roy@Sun.COM { 220710616SSebastien.Roy@Sun.COM icmph_t icmp; 220810616SSebastien.Roy@Sun.COM 220910616SSebastien.Roy@Sun.COM icmp.icmph_type = ICMP_DEST_UNREACHABLE; 221010616SSebastien.Roy@Sun.COM icmp.icmph_code = ICMP_FRAGMENTATION_NEEDED; 221110616SSebastien.Roy@Sun.COM icmp.icmph_du_zero = 0; 221210616SSebastien.Roy@Sun.COM icmp.icmph_du_mtu = htons(newmtu); 221310616SSebastien.Roy@Sun.COM 221411042SErik.Nordmark@Sun.COM iptun_sendicmp_v4(iptun, &icmp, orig_ipha, mp, tsl); 221510616SSebastien.Roy@Sun.COM } 221610616SSebastien.Roy@Sun.COM 221710616SSebastien.Roy@Sun.COM static void 221810616SSebastien.Roy@Sun.COM iptun_icmp_error_v6(iptun_t *iptun, ip6_t *orig_ip6h, mblk_t *mp, 221911042SErik.Nordmark@Sun.COM uint8_t type, uint8_t code, uint32_t offset, ts_label_t *tsl) 222010616SSebastien.Roy@Sun.COM { 222110616SSebastien.Roy@Sun.COM icmp6_t icmp6; 222210616SSebastien.Roy@Sun.COM 222310616SSebastien.Roy@Sun.COM bzero(&icmp6, sizeof (icmp6)); 222410616SSebastien.Roy@Sun.COM icmp6.icmp6_type = type; 222510616SSebastien.Roy@Sun.COM icmp6.icmp6_code = code; 222610616SSebastien.Roy@Sun.COM if (type == ICMP6_PARAM_PROB) 222710616SSebastien.Roy@Sun.COM icmp6.icmp6_pptr = htonl(offset); 222810616SSebastien.Roy@Sun.COM 222911042SErik.Nordmark@Sun.COM iptun_sendicmp_v6(iptun, &icmp6, orig_ip6h, mp, tsl); 223010616SSebastien.Roy@Sun.COM } 223110616SSebastien.Roy@Sun.COM 223210616SSebastien.Roy@Sun.COM static void 223310616SSebastien.Roy@Sun.COM iptun_icmp_toobig_v6(iptun_t *iptun, uint32_t newmtu, ip6_t *orig_ip6h, 223411042SErik.Nordmark@Sun.COM mblk_t *mp, ts_label_t *tsl) 223510616SSebastien.Roy@Sun.COM { 223610616SSebastien.Roy@Sun.COM icmp6_t icmp6; 223710616SSebastien.Roy@Sun.COM 223810616SSebastien.Roy@Sun.COM icmp6.icmp6_type = ICMP6_PACKET_TOO_BIG; 223910616SSebastien.Roy@Sun.COM icmp6.icmp6_code = 0; 224010616SSebastien.Roy@Sun.COM icmp6.icmp6_mtu = htonl(newmtu); 224110616SSebastien.Roy@Sun.COM 224211042SErik.Nordmark@Sun.COM iptun_sendicmp_v6(iptun, &icmp6, orig_ip6h, mp, tsl); 224310616SSebastien.Roy@Sun.COM } 224410616SSebastien.Roy@Sun.COM 224510616SSebastien.Roy@Sun.COM /* 224610616SSebastien.Roy@Sun.COM * Determines if the packet pointed to by ipha or ip6h is an ICMP error. The 224710616SSebastien.Roy@Sun.COM * mp argument is only used to do bounds checking. 224810616SSebastien.Roy@Sun.COM */ 224910616SSebastien.Roy@Sun.COM static boolean_t 225010616SSebastien.Roy@Sun.COM is_icmp_error(mblk_t *mp, ipha_t *ipha, ip6_t *ip6h) 225110616SSebastien.Roy@Sun.COM { 225210616SSebastien.Roy@Sun.COM uint16_t hlen; 225310616SSebastien.Roy@Sun.COM 225410616SSebastien.Roy@Sun.COM if (ipha != NULL) { 225510616SSebastien.Roy@Sun.COM icmph_t *icmph; 225610616SSebastien.Roy@Sun.COM 225710616SSebastien.Roy@Sun.COM ASSERT(ip6h == NULL); 225810616SSebastien.Roy@Sun.COM if (ipha->ipha_protocol != IPPROTO_ICMP) 225910616SSebastien.Roy@Sun.COM return (B_FALSE); 226010616SSebastien.Roy@Sun.COM 226110616SSebastien.Roy@Sun.COM hlen = IPH_HDR_LENGTH(ipha); 226210616SSebastien.Roy@Sun.COM icmph = (icmph_t *)((uint8_t *)ipha + hlen); 226310616SSebastien.Roy@Sun.COM return (ICMP_IS_ERROR(icmph->icmph_type) || 226410616SSebastien.Roy@Sun.COM icmph->icmph_type == ICMP_REDIRECT); 226510616SSebastien.Roy@Sun.COM } else { 226610616SSebastien.Roy@Sun.COM icmp6_t *icmp6; 226710616SSebastien.Roy@Sun.COM uint8_t *nexthdrp; 226810616SSebastien.Roy@Sun.COM 226910616SSebastien.Roy@Sun.COM ASSERT(ip6h != NULL); 227010616SSebastien.Roy@Sun.COM if (!ip_hdr_length_nexthdr_v6(mp, ip6h, &hlen, &nexthdrp) || 227110616SSebastien.Roy@Sun.COM *nexthdrp != IPPROTO_ICMPV6) { 227210616SSebastien.Roy@Sun.COM return (B_FALSE); 227310616SSebastien.Roy@Sun.COM } 227410616SSebastien.Roy@Sun.COM 227510616SSebastien.Roy@Sun.COM icmp6 = (icmp6_t *)((uint8_t *)ip6h + hlen); 227610616SSebastien.Roy@Sun.COM return (ICMP6_IS_ERROR(icmp6->icmp6_type) || 227710616SSebastien.Roy@Sun.COM icmp6->icmp6_type == ND_REDIRECT); 227810616SSebastien.Roy@Sun.COM } 227910616SSebastien.Roy@Sun.COM } 228010616SSebastien.Roy@Sun.COM 228110616SSebastien.Roy@Sun.COM /* 228210616SSebastien.Roy@Sun.COM * Find inner and outer IP headers from a tunneled packet as setup for calls 228310616SSebastien.Roy@Sun.COM * into ipsec_tun_{in,out}bound(). 228411042SErik.Nordmark@Sun.COM * Note that we need to allow the outer header to be in a separate mblk from 228511042SErik.Nordmark@Sun.COM * the inner header. 228611042SErik.Nordmark@Sun.COM * If the caller knows the outer_hlen, the caller passes it in. Otherwise zero. 228710616SSebastien.Roy@Sun.COM */ 228810616SSebastien.Roy@Sun.COM static size_t 228911042SErik.Nordmark@Sun.COM iptun_find_headers(mblk_t *mp, size_t outer_hlen, ipha_t **outer4, 229011042SErik.Nordmark@Sun.COM ipha_t **inner4, ip6_t **outer6, ip6_t **inner6) 229110616SSebastien.Roy@Sun.COM { 229210616SSebastien.Roy@Sun.COM ipha_t *ipha; 229310616SSebastien.Roy@Sun.COM size_t first_mblkl = MBLKL(mp); 229410616SSebastien.Roy@Sun.COM mblk_t *inner_mp; 229510616SSebastien.Roy@Sun.COM 229610616SSebastien.Roy@Sun.COM /* 229710616SSebastien.Roy@Sun.COM * Don't bother handling packets that don't have a full IP header in 229810616SSebastien.Roy@Sun.COM * the fist mblk. For the input path, the ip module ensures that this 229910616SSebastien.Roy@Sun.COM * won't happen, and on the output path, the IP tunneling MAC-type 230010616SSebastien.Roy@Sun.COM * plugins ensure that this also won't happen. 230110616SSebastien.Roy@Sun.COM */ 230210616SSebastien.Roy@Sun.COM if (first_mblkl < sizeof (ipha_t)) 230310616SSebastien.Roy@Sun.COM return (0); 230410616SSebastien.Roy@Sun.COM ipha = (ipha_t *)(mp->b_rptr); 230510616SSebastien.Roy@Sun.COM switch (IPH_HDR_VERSION(ipha)) { 230610616SSebastien.Roy@Sun.COM case IPV4_VERSION: 230710616SSebastien.Roy@Sun.COM *outer4 = ipha; 230810616SSebastien.Roy@Sun.COM *outer6 = NULL; 230911042SErik.Nordmark@Sun.COM if (outer_hlen == 0) 231011042SErik.Nordmark@Sun.COM outer_hlen = IPH_HDR_LENGTH(ipha); 231110616SSebastien.Roy@Sun.COM break; 231210616SSebastien.Roy@Sun.COM case IPV6_VERSION: 231310616SSebastien.Roy@Sun.COM *outer4 = NULL; 231410616SSebastien.Roy@Sun.COM *outer6 = (ip6_t *)ipha; 231511042SErik.Nordmark@Sun.COM if (outer_hlen == 0) 231611042SErik.Nordmark@Sun.COM outer_hlen = ip_hdr_length_v6(mp, (ip6_t *)ipha); 231710616SSebastien.Roy@Sun.COM break; 231810616SSebastien.Roy@Sun.COM default: 231910616SSebastien.Roy@Sun.COM return (0); 232010616SSebastien.Roy@Sun.COM } 232110616SSebastien.Roy@Sun.COM 232210616SSebastien.Roy@Sun.COM if (first_mblkl < outer_hlen || 232310616SSebastien.Roy@Sun.COM (first_mblkl == outer_hlen && mp->b_cont == NULL)) 232410616SSebastien.Roy@Sun.COM return (0); 232510616SSebastien.Roy@Sun.COM 232610616SSebastien.Roy@Sun.COM /* 232710616SSebastien.Roy@Sun.COM * We don't bother doing a pullup here since the outer header will 232810616SSebastien.Roy@Sun.COM * just get stripped off soon on input anyway. We just want to ensure 232910616SSebastien.Roy@Sun.COM * that the inner* pointer points to a full header. 233010616SSebastien.Roy@Sun.COM */ 233110616SSebastien.Roy@Sun.COM if (first_mblkl == outer_hlen) { 233210616SSebastien.Roy@Sun.COM inner_mp = mp->b_cont; 233310616SSebastien.Roy@Sun.COM ipha = (ipha_t *)inner_mp->b_rptr; 233410616SSebastien.Roy@Sun.COM } else { 233510616SSebastien.Roy@Sun.COM inner_mp = mp; 233610616SSebastien.Roy@Sun.COM ipha = (ipha_t *)(mp->b_rptr + outer_hlen); 233710616SSebastien.Roy@Sun.COM } 233810616SSebastien.Roy@Sun.COM switch (IPH_HDR_VERSION(ipha)) { 233910616SSebastien.Roy@Sun.COM case IPV4_VERSION: 234010616SSebastien.Roy@Sun.COM if (inner_mp->b_wptr - (uint8_t *)ipha < sizeof (ipha_t)) 234110616SSebastien.Roy@Sun.COM return (0); 234210616SSebastien.Roy@Sun.COM *inner4 = ipha; 234310616SSebastien.Roy@Sun.COM *inner6 = NULL; 234410616SSebastien.Roy@Sun.COM break; 234510616SSebastien.Roy@Sun.COM case IPV6_VERSION: 234610616SSebastien.Roy@Sun.COM if (inner_mp->b_wptr - (uint8_t *)ipha < sizeof (ip6_t)) 234710616SSebastien.Roy@Sun.COM return (0); 234810616SSebastien.Roy@Sun.COM *inner4 = NULL; 234910616SSebastien.Roy@Sun.COM *inner6 = (ip6_t *)ipha; 235010616SSebastien.Roy@Sun.COM break; 235110616SSebastien.Roy@Sun.COM default: 235210616SSebastien.Roy@Sun.COM return (0); 235310616SSebastien.Roy@Sun.COM } 235410616SSebastien.Roy@Sun.COM 235510616SSebastien.Roy@Sun.COM return (outer_hlen); 235610616SSebastien.Roy@Sun.COM } 235710616SSebastien.Roy@Sun.COM 235810616SSebastien.Roy@Sun.COM /* 235910616SSebastien.Roy@Sun.COM * Received ICMP error in response to an X over IPv4 packet that we 236010616SSebastien.Roy@Sun.COM * transmitted. 236110616SSebastien.Roy@Sun.COM * 236210616SSebastien.Roy@Sun.COM * NOTE: "outer" refers to what's inside the ICMP payload. We will get one of 236310616SSebastien.Roy@Sun.COM * the following: 236410616SSebastien.Roy@Sun.COM * 236510616SSebastien.Roy@Sun.COM * [IPv4(0)][ICMPv4][IPv4(1)][IPv4(2)][ULP] 236610616SSebastien.Roy@Sun.COM * 236710616SSebastien.Roy@Sun.COM * or 236810616SSebastien.Roy@Sun.COM * 236910616SSebastien.Roy@Sun.COM * [IPv4(0)][ICMPv4][IPv4(1)][IPv6][ULP] 237010616SSebastien.Roy@Sun.COM * 237110616SSebastien.Roy@Sun.COM * And "outer4" will get set to IPv4(1), and inner[46] will correspond to 237210616SSebastien.Roy@Sun.COM * whatever the very-inner packet is (IPv4(2) or IPv6). 237310616SSebastien.Roy@Sun.COM */ 237410616SSebastien.Roy@Sun.COM static void 237511042SErik.Nordmark@Sun.COM iptun_input_icmp_v4(iptun_t *iptun, mblk_t *data_mp, icmph_t *icmph, 237611042SErik.Nordmark@Sun.COM ip_recv_attr_t *ira) 237710616SSebastien.Roy@Sun.COM { 237810616SSebastien.Roy@Sun.COM uint8_t *orig; 237910616SSebastien.Roy@Sun.COM ipha_t *outer4, *inner4; 238010616SSebastien.Roy@Sun.COM ip6_t *outer6, *inner6; 238110616SSebastien.Roy@Sun.COM int outer_hlen; 238210616SSebastien.Roy@Sun.COM uint8_t type, code; 238310616SSebastien.Roy@Sun.COM 238410616SSebastien.Roy@Sun.COM ASSERT(data_mp->b_cont == NULL); 238510616SSebastien.Roy@Sun.COM /* 238610616SSebastien.Roy@Sun.COM * Temporarily move b_rptr forward so that iptun_find_headers() can 238710616SSebastien.Roy@Sun.COM * find headers in the ICMP packet payload. 238810616SSebastien.Roy@Sun.COM */ 238910616SSebastien.Roy@Sun.COM orig = data_mp->b_rptr; 239010616SSebastien.Roy@Sun.COM data_mp->b_rptr = (uint8_t *)(icmph + 1); 239110616SSebastien.Roy@Sun.COM /* 239210616SSebastien.Roy@Sun.COM * The ip module ensures that ICMP errors contain at least the 239310616SSebastien.Roy@Sun.COM * original IP header (otherwise, the error would never have made it 239410616SSebastien.Roy@Sun.COM * here). 239510616SSebastien.Roy@Sun.COM */ 239610616SSebastien.Roy@Sun.COM ASSERT(MBLKL(data_mp) >= 0); 239711042SErik.Nordmark@Sun.COM outer_hlen = iptun_find_headers(data_mp, 0, &outer4, &inner4, &outer6, 239810616SSebastien.Roy@Sun.COM &inner6); 239910616SSebastien.Roy@Sun.COM ASSERT(outer6 == NULL); 240010616SSebastien.Roy@Sun.COM data_mp->b_rptr = orig; 240110616SSebastien.Roy@Sun.COM if (outer_hlen == 0) { 240211042SErik.Nordmark@Sun.COM iptun_drop_pkt(data_mp, &iptun->iptun_ierrors); 240310616SSebastien.Roy@Sun.COM return; 240410616SSebastien.Roy@Sun.COM } 240510616SSebastien.Roy@Sun.COM 240610616SSebastien.Roy@Sun.COM /* Only ICMP errors due to tunneled packets should reach here. */ 240710616SSebastien.Roy@Sun.COM ASSERT(outer4->ipha_protocol == IPPROTO_ENCAP || 240810616SSebastien.Roy@Sun.COM outer4->ipha_protocol == IPPROTO_IPV6); 240910616SSebastien.Roy@Sun.COM 241011042SErik.Nordmark@Sun.COM data_mp = ipsec_tun_inbound(ira, data_mp, iptun->iptun_itp, 241111042SErik.Nordmark@Sun.COM inner4, inner6, outer4, outer6, -outer_hlen, iptun->iptun_ns); 241211042SErik.Nordmark@Sun.COM if (data_mp == NULL) { 241310616SSebastien.Roy@Sun.COM /* Callee did all of the freeing. */ 241410616SSebastien.Roy@Sun.COM atomic_inc_64(&iptun->iptun_ierrors); 241510616SSebastien.Roy@Sun.COM return; 241610616SSebastien.Roy@Sun.COM } 241710616SSebastien.Roy@Sun.COM /* We should never see reassembled fragment here. */ 241810616SSebastien.Roy@Sun.COM ASSERT(data_mp->b_next == NULL); 241910616SSebastien.Roy@Sun.COM 242010616SSebastien.Roy@Sun.COM data_mp->b_rptr = (uint8_t *)outer4 + outer_hlen; 242110616SSebastien.Roy@Sun.COM 242210616SSebastien.Roy@Sun.COM /* 242310616SSebastien.Roy@Sun.COM * If the original packet being transmitted was itself an ICMP error, 242410616SSebastien.Roy@Sun.COM * then drop this packet. We don't want to generate an ICMP error in 242510616SSebastien.Roy@Sun.COM * response to an ICMP error. 242610616SSebastien.Roy@Sun.COM */ 242710616SSebastien.Roy@Sun.COM if (is_icmp_error(data_mp, inner4, inner6)) { 242810616SSebastien.Roy@Sun.COM iptun_drop_pkt(data_mp, &iptun->iptun_norcvbuf); 242910616SSebastien.Roy@Sun.COM return; 243010616SSebastien.Roy@Sun.COM } 243110616SSebastien.Roy@Sun.COM 243210616SSebastien.Roy@Sun.COM switch (icmph->icmph_type) { 243310616SSebastien.Roy@Sun.COM case ICMP_DEST_UNREACHABLE: 243410616SSebastien.Roy@Sun.COM type = (inner4 != NULL ? icmph->icmph_type : ICMP6_DST_UNREACH); 243510616SSebastien.Roy@Sun.COM switch (icmph->icmph_code) { 243610616SSebastien.Roy@Sun.COM case ICMP_FRAGMENTATION_NEEDED: { 243710616SSebastien.Roy@Sun.COM uint32_t newmtu; 243810616SSebastien.Roy@Sun.COM 243910616SSebastien.Roy@Sun.COM /* 244010616SSebastien.Roy@Sun.COM * We reconcile this with the fact that the tunnel may 244110616SSebastien.Roy@Sun.COM * also have IPsec policy by letting iptun_update_mtu 244210616SSebastien.Roy@Sun.COM * take care of it. 244310616SSebastien.Roy@Sun.COM */ 244411042SErik.Nordmark@Sun.COM newmtu = iptun_update_mtu(iptun, NULL, 244511042SErik.Nordmark@Sun.COM ntohs(icmph->icmph_du_mtu)); 244610616SSebastien.Roy@Sun.COM 244710616SSebastien.Roy@Sun.COM if (inner4 != NULL) { 244810616SSebastien.Roy@Sun.COM iptun_icmp_fragneeded_v4(iptun, newmtu, inner4, 244911042SErik.Nordmark@Sun.COM data_mp, ira->ira_tsl); 245010616SSebastien.Roy@Sun.COM } else { 245110616SSebastien.Roy@Sun.COM iptun_icmp_toobig_v6(iptun, newmtu, inner6, 245211042SErik.Nordmark@Sun.COM data_mp, ira->ira_tsl); 245310616SSebastien.Roy@Sun.COM } 245410616SSebastien.Roy@Sun.COM return; 245510616SSebastien.Roy@Sun.COM } 245610616SSebastien.Roy@Sun.COM case ICMP_DEST_NET_UNREACH_ADMIN: 245710616SSebastien.Roy@Sun.COM case ICMP_DEST_HOST_UNREACH_ADMIN: 245810616SSebastien.Roy@Sun.COM code = (inner4 != NULL ? ICMP_DEST_NET_UNREACH_ADMIN : 245910616SSebastien.Roy@Sun.COM ICMP6_DST_UNREACH_ADMIN); 246010616SSebastien.Roy@Sun.COM break; 246110616SSebastien.Roy@Sun.COM default: 246210616SSebastien.Roy@Sun.COM code = (inner4 != NULL ? ICMP_HOST_UNREACHABLE : 246310616SSebastien.Roy@Sun.COM ICMP6_DST_UNREACH_ADDR); 246410616SSebastien.Roy@Sun.COM break; 246510616SSebastien.Roy@Sun.COM } 246610616SSebastien.Roy@Sun.COM break; 246710616SSebastien.Roy@Sun.COM case ICMP_TIME_EXCEEDED: 246810616SSebastien.Roy@Sun.COM if (inner6 != NULL) { 246910616SSebastien.Roy@Sun.COM type = ICMP6_TIME_EXCEEDED; 247010616SSebastien.Roy@Sun.COM code = 0; 247110616SSebastien.Roy@Sun.COM } /* else we're already set. */ 247210616SSebastien.Roy@Sun.COM break; 247310616SSebastien.Roy@Sun.COM case ICMP_PARAM_PROBLEM: 247410616SSebastien.Roy@Sun.COM /* 247510616SSebastien.Roy@Sun.COM * This is a problem with the outer header we transmitted. 247610616SSebastien.Roy@Sun.COM * Treat this as an output error. 247710616SSebastien.Roy@Sun.COM */ 247810616SSebastien.Roy@Sun.COM iptun_drop_pkt(data_mp, &iptun->iptun_oerrors); 247910616SSebastien.Roy@Sun.COM return; 248010616SSebastien.Roy@Sun.COM default: 248110616SSebastien.Roy@Sun.COM iptun_drop_pkt(data_mp, &iptun->iptun_norcvbuf); 248210616SSebastien.Roy@Sun.COM return; 248310616SSebastien.Roy@Sun.COM } 248410616SSebastien.Roy@Sun.COM 248511042SErik.Nordmark@Sun.COM if (inner4 != NULL) { 248611042SErik.Nordmark@Sun.COM iptun_icmp_error_v4(iptun, inner4, data_mp, type, code, 248711042SErik.Nordmark@Sun.COM ira->ira_tsl); 248811042SErik.Nordmark@Sun.COM } else { 248911042SErik.Nordmark@Sun.COM iptun_icmp_error_v6(iptun, inner6, data_mp, type, code, 0, 249011042SErik.Nordmark@Sun.COM ira->ira_tsl); 249111042SErik.Nordmark@Sun.COM } 249210616SSebastien.Roy@Sun.COM } 249310616SSebastien.Roy@Sun.COM 249410616SSebastien.Roy@Sun.COM /* 249510616SSebastien.Roy@Sun.COM * Return B_TRUE if the IPv6 packet pointed to by ip6h contains a Tunnel 249610616SSebastien.Roy@Sun.COM * Encapsulation Limit destination option. If there is one, set encaplim_ptr 249710616SSebastien.Roy@Sun.COM * to point to the option value. 249810616SSebastien.Roy@Sun.COM */ 249910616SSebastien.Roy@Sun.COM static boolean_t 250010616SSebastien.Roy@Sun.COM iptun_find_encaplimit(mblk_t *mp, ip6_t *ip6h, uint8_t **encaplim_ptr) 250110616SSebastien.Roy@Sun.COM { 250211042SErik.Nordmark@Sun.COM ip_pkt_t pkt; 250310616SSebastien.Roy@Sun.COM uint8_t *endptr; 250410616SSebastien.Roy@Sun.COM ip6_dest_t *destp; 250510616SSebastien.Roy@Sun.COM struct ip6_opt *optp; 250610616SSebastien.Roy@Sun.COM 250710616SSebastien.Roy@Sun.COM pkt.ipp_fields = 0; /* must be initialized */ 250811042SErik.Nordmark@Sun.COM (void) ip_find_hdr_v6(mp, ip6h, B_FALSE, &pkt, NULL); 250910616SSebastien.Roy@Sun.COM if ((pkt.ipp_fields & IPPF_DSTOPTS) != 0) { 251010616SSebastien.Roy@Sun.COM destp = pkt.ipp_dstopts; 251111042SErik.Nordmark@Sun.COM } else if ((pkt.ipp_fields & IPPF_RTHDRDSTOPTS) != 0) { 251211042SErik.Nordmark@Sun.COM destp = pkt.ipp_rthdrdstopts; 251310616SSebastien.Roy@Sun.COM } else { 251410616SSebastien.Roy@Sun.COM return (B_FALSE); 251510616SSebastien.Roy@Sun.COM } 251610616SSebastien.Roy@Sun.COM 251710616SSebastien.Roy@Sun.COM endptr = (uint8_t *)destp + 8 * (destp->ip6d_len + 1); 251810616SSebastien.Roy@Sun.COM optp = (struct ip6_opt *)(destp + 1); 251910616SSebastien.Roy@Sun.COM while (endptr - (uint8_t *)optp > sizeof (*optp)) { 252010616SSebastien.Roy@Sun.COM if (optp->ip6o_type == IP6OPT_TUNNEL_LIMIT) { 252110616SSebastien.Roy@Sun.COM if ((uint8_t *)(optp + 1) >= endptr) 252210616SSebastien.Roy@Sun.COM return (B_FALSE); 252310616SSebastien.Roy@Sun.COM *encaplim_ptr = (uint8_t *)&optp[1]; 252410616SSebastien.Roy@Sun.COM return (B_TRUE); 252510616SSebastien.Roy@Sun.COM } 252610616SSebastien.Roy@Sun.COM optp = (struct ip6_opt *)((uint8_t *)optp + optp->ip6o_len + 2); 252710616SSebastien.Roy@Sun.COM } 252810616SSebastien.Roy@Sun.COM return (B_FALSE); 252910616SSebastien.Roy@Sun.COM } 253010616SSebastien.Roy@Sun.COM 253110616SSebastien.Roy@Sun.COM /* 253210616SSebastien.Roy@Sun.COM * Received ICMPv6 error in response to an X over IPv6 packet that we 253310616SSebastien.Roy@Sun.COM * transmitted. 253410616SSebastien.Roy@Sun.COM * 253510616SSebastien.Roy@Sun.COM * NOTE: "outer" refers to what's inside the ICMP payload. We will get one of 253610616SSebastien.Roy@Sun.COM * the following: 253710616SSebastien.Roy@Sun.COM * 253810616SSebastien.Roy@Sun.COM * [IPv6(0)][ICMPv6][IPv6(1)][IPv4][ULP] 253910616SSebastien.Roy@Sun.COM * 254010616SSebastien.Roy@Sun.COM * or 254110616SSebastien.Roy@Sun.COM * 254210616SSebastien.Roy@Sun.COM * [IPv6(0)][ICMPv6][IPv6(1)][IPv6(2)][ULP] 254310616SSebastien.Roy@Sun.COM * 254410616SSebastien.Roy@Sun.COM * And "outer6" will get set to IPv6(1), and inner[46] will correspond to 254510616SSebastien.Roy@Sun.COM * whatever the very-inner packet is (IPv4 or IPv6(2)). 254610616SSebastien.Roy@Sun.COM */ 254710616SSebastien.Roy@Sun.COM static void 254811042SErik.Nordmark@Sun.COM iptun_input_icmp_v6(iptun_t *iptun, mblk_t *data_mp, icmp6_t *icmp6h, 254911042SErik.Nordmark@Sun.COM ip_recv_attr_t *ira) 255010616SSebastien.Roy@Sun.COM { 255110616SSebastien.Roy@Sun.COM uint8_t *orig; 255210616SSebastien.Roy@Sun.COM ipha_t *outer4, *inner4; 255310616SSebastien.Roy@Sun.COM ip6_t *outer6, *inner6; 255410616SSebastien.Roy@Sun.COM int outer_hlen; 255510616SSebastien.Roy@Sun.COM uint8_t type, code; 255610616SSebastien.Roy@Sun.COM 255710616SSebastien.Roy@Sun.COM ASSERT(data_mp->b_cont == NULL); 255810616SSebastien.Roy@Sun.COM 255910616SSebastien.Roy@Sun.COM /* 256010616SSebastien.Roy@Sun.COM * Temporarily move b_rptr forward so that iptun_find_headers() can 256110616SSebastien.Roy@Sun.COM * find IP headers in the ICMP packet payload. 256210616SSebastien.Roy@Sun.COM */ 256310616SSebastien.Roy@Sun.COM orig = data_mp->b_rptr; 256410616SSebastien.Roy@Sun.COM data_mp->b_rptr = (uint8_t *)(icmp6h + 1); 256510616SSebastien.Roy@Sun.COM /* 256610616SSebastien.Roy@Sun.COM * The ip module ensures that ICMP errors contain at least the 256710616SSebastien.Roy@Sun.COM * original IP header (otherwise, the error would never have made it 256810616SSebastien.Roy@Sun.COM * here). 256910616SSebastien.Roy@Sun.COM */ 257010616SSebastien.Roy@Sun.COM ASSERT(MBLKL(data_mp) >= 0); 257111042SErik.Nordmark@Sun.COM outer_hlen = iptun_find_headers(data_mp, 0, &outer4, &inner4, &outer6, 257210616SSebastien.Roy@Sun.COM &inner6); 257310616SSebastien.Roy@Sun.COM ASSERT(outer4 == NULL); 257410616SSebastien.Roy@Sun.COM data_mp->b_rptr = orig; /* Restore r_ptr */ 257510616SSebastien.Roy@Sun.COM if (outer_hlen == 0) { 257611042SErik.Nordmark@Sun.COM iptun_drop_pkt(data_mp, &iptun->iptun_ierrors); 257710616SSebastien.Roy@Sun.COM return; 257810616SSebastien.Roy@Sun.COM } 257910616SSebastien.Roy@Sun.COM 258011042SErik.Nordmark@Sun.COM data_mp = ipsec_tun_inbound(ira, data_mp, iptun->iptun_itp, 258111042SErik.Nordmark@Sun.COM inner4, inner6, outer4, outer6, -outer_hlen, iptun->iptun_ns); 258211042SErik.Nordmark@Sun.COM if (data_mp == NULL) { 258310616SSebastien.Roy@Sun.COM /* Callee did all of the freeing. */ 258410616SSebastien.Roy@Sun.COM atomic_inc_64(&iptun->iptun_ierrors); 258510616SSebastien.Roy@Sun.COM return; 258610616SSebastien.Roy@Sun.COM } 258710616SSebastien.Roy@Sun.COM /* We should never see reassembled fragment here. */ 258810616SSebastien.Roy@Sun.COM ASSERT(data_mp->b_next == NULL); 258910616SSebastien.Roy@Sun.COM 259010616SSebastien.Roy@Sun.COM data_mp->b_rptr = (uint8_t *)outer6 + outer_hlen; 259110616SSebastien.Roy@Sun.COM 259210616SSebastien.Roy@Sun.COM /* 259310616SSebastien.Roy@Sun.COM * If the original packet being transmitted was itself an ICMP error, 259410616SSebastien.Roy@Sun.COM * then drop this packet. We don't want to generate an ICMP error in 259510616SSebastien.Roy@Sun.COM * response to an ICMP error. 259610616SSebastien.Roy@Sun.COM */ 259710616SSebastien.Roy@Sun.COM if (is_icmp_error(data_mp, inner4, inner6)) { 259810616SSebastien.Roy@Sun.COM iptun_drop_pkt(data_mp, &iptun->iptun_norcvbuf); 259910616SSebastien.Roy@Sun.COM return; 260010616SSebastien.Roy@Sun.COM } 260110616SSebastien.Roy@Sun.COM 260210616SSebastien.Roy@Sun.COM switch (icmp6h->icmp6_type) { 260310616SSebastien.Roy@Sun.COM case ICMP6_PARAM_PROB: { 260410616SSebastien.Roy@Sun.COM uint8_t *encaplim_ptr; 260510616SSebastien.Roy@Sun.COM 260610616SSebastien.Roy@Sun.COM /* 260710616SSebastien.Roy@Sun.COM * If the ICMPv6 error points to a valid Tunnel Encapsulation 260810616SSebastien.Roy@Sun.COM * Limit option and the limit value is 0, then fall through 260910616SSebastien.Roy@Sun.COM * and send a host unreachable message. Otherwise, treat the 261010616SSebastien.Roy@Sun.COM * error as an output error, as there must have been a problem 261110616SSebastien.Roy@Sun.COM * with a packet we sent. 261210616SSebastien.Roy@Sun.COM */ 261310616SSebastien.Roy@Sun.COM if (!iptun_find_encaplimit(data_mp, outer6, &encaplim_ptr) || 261410616SSebastien.Roy@Sun.COM (icmp6h->icmp6_pptr != 261510616SSebastien.Roy@Sun.COM ((ptrdiff_t)encaplim_ptr - (ptrdiff_t)outer6)) || 261610616SSebastien.Roy@Sun.COM *encaplim_ptr != 0) { 261710616SSebastien.Roy@Sun.COM iptun_drop_pkt(data_mp, &iptun->iptun_oerrors); 261810616SSebastien.Roy@Sun.COM return; 261910616SSebastien.Roy@Sun.COM } 262010616SSebastien.Roy@Sun.COM /* FALLTHRU */ 262110616SSebastien.Roy@Sun.COM } 262210616SSebastien.Roy@Sun.COM case ICMP6_TIME_EXCEEDED: 262310616SSebastien.Roy@Sun.COM case ICMP6_DST_UNREACH: 262410616SSebastien.Roy@Sun.COM type = (inner4 != NULL ? ICMP_DEST_UNREACHABLE : 262510616SSebastien.Roy@Sun.COM ICMP6_DST_UNREACH); 262610616SSebastien.Roy@Sun.COM code = (inner4 != NULL ? ICMP_HOST_UNREACHABLE : 262710616SSebastien.Roy@Sun.COM ICMP6_DST_UNREACH_ADDR); 262810616SSebastien.Roy@Sun.COM break; 262910616SSebastien.Roy@Sun.COM case ICMP6_PACKET_TOO_BIG: { 263010616SSebastien.Roy@Sun.COM uint32_t newmtu; 263110616SSebastien.Roy@Sun.COM 263210616SSebastien.Roy@Sun.COM /* 263310616SSebastien.Roy@Sun.COM * We reconcile this with the fact that the tunnel may also 263410616SSebastien.Roy@Sun.COM * have IPsec policy by letting iptun_update_mtu take care of 263510616SSebastien.Roy@Sun.COM * it. 263610616SSebastien.Roy@Sun.COM */ 263711042SErik.Nordmark@Sun.COM newmtu = iptun_update_mtu(iptun, NULL, 263811042SErik.Nordmark@Sun.COM ntohl(icmp6h->icmp6_mtu)); 263910616SSebastien.Roy@Sun.COM 264010616SSebastien.Roy@Sun.COM if (inner4 != NULL) { 264110616SSebastien.Roy@Sun.COM iptun_icmp_fragneeded_v4(iptun, newmtu, inner4, 264211042SErik.Nordmark@Sun.COM data_mp, ira->ira_tsl); 264310616SSebastien.Roy@Sun.COM } else { 264411042SErik.Nordmark@Sun.COM iptun_icmp_toobig_v6(iptun, newmtu, inner6, data_mp, 264511042SErik.Nordmark@Sun.COM ira->ira_tsl); 264610616SSebastien.Roy@Sun.COM } 264710616SSebastien.Roy@Sun.COM return; 264810616SSebastien.Roy@Sun.COM } 264910616SSebastien.Roy@Sun.COM default: 265010616SSebastien.Roy@Sun.COM iptun_drop_pkt(data_mp, &iptun->iptun_norcvbuf); 265110616SSebastien.Roy@Sun.COM return; 265210616SSebastien.Roy@Sun.COM } 265310616SSebastien.Roy@Sun.COM 265411042SErik.Nordmark@Sun.COM if (inner4 != NULL) { 265511042SErik.Nordmark@Sun.COM iptun_icmp_error_v4(iptun, inner4, data_mp, type, code, 265611042SErik.Nordmark@Sun.COM ira->ira_tsl); 265711042SErik.Nordmark@Sun.COM } else { 265811042SErik.Nordmark@Sun.COM iptun_icmp_error_v6(iptun, inner6, data_mp, type, code, 0, 265911042SErik.Nordmark@Sun.COM ira->ira_tsl); 266011042SErik.Nordmark@Sun.COM } 266110616SSebastien.Roy@Sun.COM } 266210616SSebastien.Roy@Sun.COM 266311042SErik.Nordmark@Sun.COM /* 266411042SErik.Nordmark@Sun.COM * Called as conn_recvicmp from IP for ICMP errors. 266511042SErik.Nordmark@Sun.COM */ 266611042SErik.Nordmark@Sun.COM /* ARGSUSED2 */ 266710616SSebastien.Roy@Sun.COM static void 266811042SErik.Nordmark@Sun.COM iptun_input_icmp(void *arg, mblk_t *mp, void *arg2, ip_recv_attr_t *ira) 266910616SSebastien.Roy@Sun.COM { 267011042SErik.Nordmark@Sun.COM conn_t *connp = arg; 267111042SErik.Nordmark@Sun.COM iptun_t *iptun = connp->conn_iptun; 267211042SErik.Nordmark@Sun.COM mblk_t *tmpmp; 267311042SErik.Nordmark@Sun.COM size_t hlen; 267411042SErik.Nordmark@Sun.COM 267511042SErik.Nordmark@Sun.COM ASSERT(IPCL_IS_IPTUN(connp)); 267611042SErik.Nordmark@Sun.COM 267711042SErik.Nordmark@Sun.COM if (mp->b_cont != NULL) { 267810616SSebastien.Roy@Sun.COM /* 267910616SSebastien.Roy@Sun.COM * Since ICMP error processing necessitates access to bits 268010616SSebastien.Roy@Sun.COM * that are within the ICMP error payload (the original packet 268110616SSebastien.Roy@Sun.COM * that caused the error), pull everything up into a single 268210616SSebastien.Roy@Sun.COM * block for convenience. 268310616SSebastien.Roy@Sun.COM */ 268411042SErik.Nordmark@Sun.COM if ((tmpmp = msgpullup(mp, -1)) == NULL) { 268511042SErik.Nordmark@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_norcvbuf); 268610616SSebastien.Roy@Sun.COM return; 268710616SSebastien.Roy@Sun.COM } 268811042SErik.Nordmark@Sun.COM freemsg(mp); 268911042SErik.Nordmark@Sun.COM mp = tmpmp; 269010616SSebastien.Roy@Sun.COM } 269110616SSebastien.Roy@Sun.COM 269211042SErik.Nordmark@Sun.COM hlen = ira->ira_ip_hdr_length; 269310616SSebastien.Roy@Sun.COM switch (iptun->iptun_typeinfo->iti_ipvers) { 269410616SSebastien.Roy@Sun.COM case IPV4_VERSION: 269510616SSebastien.Roy@Sun.COM /* 269610616SSebastien.Roy@Sun.COM * The outer IP header coming up from IP is always ipha_t 269710616SSebastien.Roy@Sun.COM * alligned (otherwise, we would have crashed in ip). 269810616SSebastien.Roy@Sun.COM */ 269911042SErik.Nordmark@Sun.COM iptun_input_icmp_v4(iptun, mp, (icmph_t *)(mp->b_rptr + hlen), 270011042SErik.Nordmark@Sun.COM ira); 270110616SSebastien.Roy@Sun.COM break; 270210616SSebastien.Roy@Sun.COM case IPV6_VERSION: 270311042SErik.Nordmark@Sun.COM iptun_input_icmp_v6(iptun, mp, (icmp6_t *)(mp->b_rptr + hlen), 270411042SErik.Nordmark@Sun.COM ira); 270510616SSebastien.Roy@Sun.COM break; 270610616SSebastien.Roy@Sun.COM } 270710616SSebastien.Roy@Sun.COM } 270810616SSebastien.Roy@Sun.COM 270910616SSebastien.Roy@Sun.COM static boolean_t 271010616SSebastien.Roy@Sun.COM iptun_in_6to4_ok(iptun_t *iptun, ipha_t *outer4, ip6_t *inner6) 271110616SSebastien.Roy@Sun.COM { 271210616SSebastien.Roy@Sun.COM ipaddr_t v4addr; 271310616SSebastien.Roy@Sun.COM 271410616SSebastien.Roy@Sun.COM /* 271510722SSebastien.Roy@Sun.COM * It's possible that someone sent us an IPv4-in-IPv4 packet with the 271610722SSebastien.Roy@Sun.COM * IPv4 address of a 6to4 tunnel as the destination. 271710722SSebastien.Roy@Sun.COM */ 271810722SSebastien.Roy@Sun.COM if (inner6 == NULL) 271910722SSebastien.Roy@Sun.COM return (B_FALSE); 272010722SSebastien.Roy@Sun.COM 272110722SSebastien.Roy@Sun.COM /* 272210616SSebastien.Roy@Sun.COM * Make sure that the IPv6 destination is within the site that this 272310616SSebastien.Roy@Sun.COM * 6to4 tunnel is routing for. We don't want people bouncing random 272410616SSebastien.Roy@Sun.COM * tunneled IPv6 packets through this 6to4 router. 272510616SSebastien.Roy@Sun.COM */ 272610616SSebastien.Roy@Sun.COM IN6_6TO4_TO_V4ADDR(&inner6->ip6_dst, (struct in_addr *)&v4addr); 272710616SSebastien.Roy@Sun.COM if (outer4->ipha_dst != v4addr) 272810616SSebastien.Roy@Sun.COM return (B_FALSE); 272910616SSebastien.Roy@Sun.COM 273010616SSebastien.Roy@Sun.COM if (IN6_IS_ADDR_6TO4(&inner6->ip6_src)) { 273110616SSebastien.Roy@Sun.COM /* 273210616SSebastien.Roy@Sun.COM * Section 9 of RFC 3056 (security considerations) suggests 273310616SSebastien.Roy@Sun.COM * that when a packet is from a 6to4 site (i.e., it's not a 273410616SSebastien.Roy@Sun.COM * global address being forwarded froma relay router), make 273510616SSebastien.Roy@Sun.COM * sure that the packet was tunneled by that site's 6to4 273610616SSebastien.Roy@Sun.COM * router. 273710616SSebastien.Roy@Sun.COM */ 273810616SSebastien.Roy@Sun.COM IN6_6TO4_TO_V4ADDR(&inner6->ip6_src, (struct in_addr *)&v4addr); 273910616SSebastien.Roy@Sun.COM if (outer4->ipha_src != v4addr) 274010616SSebastien.Roy@Sun.COM return (B_FALSE); 274110616SSebastien.Roy@Sun.COM } else { 274210616SSebastien.Roy@Sun.COM /* 274310616SSebastien.Roy@Sun.COM * Only accept packets from a relay router if we've configured 274410616SSebastien.Roy@Sun.COM * outbound relay router functionality. 274510616SSebastien.Roy@Sun.COM */ 274610616SSebastien.Roy@Sun.COM if (iptun->iptun_iptuns->iptuns_relay_rtr_addr == INADDR_ANY) 274710616SSebastien.Roy@Sun.COM return (B_FALSE); 274810616SSebastien.Roy@Sun.COM } 274910616SSebastien.Roy@Sun.COM 275010616SSebastien.Roy@Sun.COM return (B_TRUE); 275110616SSebastien.Roy@Sun.COM } 275210616SSebastien.Roy@Sun.COM 275310616SSebastien.Roy@Sun.COM /* 275410616SSebastien.Roy@Sun.COM * Input function for everything that comes up from the ip module below us. 275510616SSebastien.Roy@Sun.COM * This is called directly from the ip module via connp->conn_recv(). 275610616SSebastien.Roy@Sun.COM * 275711042SErik.Nordmark@Sun.COM * We receive M_DATA messages with IP-in-IP tunneled packets. 275810616SSebastien.Roy@Sun.COM */ 275911042SErik.Nordmark@Sun.COM /* ARGSUSED2 */ 276010616SSebastien.Roy@Sun.COM static void 276111042SErik.Nordmark@Sun.COM iptun_input(void *arg, mblk_t *data_mp, void *arg2, ip_recv_attr_t *ira) 276210616SSebastien.Roy@Sun.COM { 276310616SSebastien.Roy@Sun.COM conn_t *connp = arg; 276410616SSebastien.Roy@Sun.COM iptun_t *iptun = connp->conn_iptun; 276510616SSebastien.Roy@Sun.COM int outer_hlen; 276610616SSebastien.Roy@Sun.COM ipha_t *outer4, *inner4; 276710616SSebastien.Roy@Sun.COM ip6_t *outer6, *inner6; 276810616SSebastien.Roy@Sun.COM 276910616SSebastien.Roy@Sun.COM ASSERT(IPCL_IS_IPTUN(connp)); 277011042SErik.Nordmark@Sun.COM ASSERT(DB_TYPE(data_mp) == M_DATA); 277111042SErik.Nordmark@Sun.COM 277211042SErik.Nordmark@Sun.COM outer_hlen = iptun_find_headers(data_mp, ira->ira_ip_hdr_length, 277311042SErik.Nordmark@Sun.COM &outer4, &inner4, &outer6, &inner6); 277411042SErik.Nordmark@Sun.COM if (outer_hlen == 0) 277510616SSebastien.Roy@Sun.COM goto drop; 277610616SSebastien.Roy@Sun.COM 277710616SSebastien.Roy@Sun.COM /* 277810616SSebastien.Roy@Sun.COM * If the system is labeled, we call tsol_check_dest() on the packet 277910616SSebastien.Roy@Sun.COM * destination (our local tunnel address) to ensure that the packet as 278010616SSebastien.Roy@Sun.COM * labeled should be allowed to be sent to us. We don't need to call 278110616SSebastien.Roy@Sun.COM * the more involved tsol_receive_local() since the tunnel link itself 278210616SSebastien.Roy@Sun.COM * cannot be assigned to shared-stack non-global zones. 278310616SSebastien.Roy@Sun.COM */ 278411042SErik.Nordmark@Sun.COM if (ira->ira_flags & IRAF_SYSTEM_LABELED) { 278511042SErik.Nordmark@Sun.COM if (ira->ira_tsl == NULL) 278610616SSebastien.Roy@Sun.COM goto drop; 278711042SErik.Nordmark@Sun.COM if (tsol_check_dest(ira->ira_tsl, (outer4 != NULL ? 278810616SSebastien.Roy@Sun.COM (void *)&outer4->ipha_dst : (void *)&outer6->ip6_dst), 278910616SSebastien.Roy@Sun.COM (outer4 != NULL ? IPV4_VERSION : IPV6_VERSION), 279011042SErik.Nordmark@Sun.COM CONN_MAC_DEFAULT, B_FALSE, NULL) != 0) 279110616SSebastien.Roy@Sun.COM goto drop; 279210616SSebastien.Roy@Sun.COM } 279310616SSebastien.Roy@Sun.COM 279411042SErik.Nordmark@Sun.COM data_mp = ipsec_tun_inbound(ira, data_mp, iptun->iptun_itp, 279511042SErik.Nordmark@Sun.COM inner4, inner6, outer4, outer6, outer_hlen, iptun->iptun_ns); 279611042SErik.Nordmark@Sun.COM if (data_mp == NULL) { 279710692Sdanmcd@sun.com /* Callee did all of the freeing. */ 279810692Sdanmcd@sun.com return; 279910616SSebastien.Roy@Sun.COM } 280010616SSebastien.Roy@Sun.COM 280110616SSebastien.Roy@Sun.COM if (iptun->iptun_typeinfo->iti_type == IPTUN_TYPE_6TO4 && 280210616SSebastien.Roy@Sun.COM !iptun_in_6to4_ok(iptun, outer4, inner6)) 280310616SSebastien.Roy@Sun.COM goto drop; 280410616SSebastien.Roy@Sun.COM 280510616SSebastien.Roy@Sun.COM /* 280610616SSebastien.Roy@Sun.COM * We need to statistically account for each packet individually, so 280710616SSebastien.Roy@Sun.COM * we might as well split up any b_next chains here. 280810616SSebastien.Roy@Sun.COM */ 280910616SSebastien.Roy@Sun.COM do { 281011042SErik.Nordmark@Sun.COM mblk_t *mp; 281111042SErik.Nordmark@Sun.COM 281210616SSebastien.Roy@Sun.COM mp = data_mp->b_next; 281310616SSebastien.Roy@Sun.COM data_mp->b_next = NULL; 281410616SSebastien.Roy@Sun.COM 281510616SSebastien.Roy@Sun.COM atomic_inc_64(&iptun->iptun_ipackets); 281610616SSebastien.Roy@Sun.COM atomic_add_64(&iptun->iptun_rbytes, msgdsize(data_mp)); 281710616SSebastien.Roy@Sun.COM mac_rx(iptun->iptun_mh, NULL, data_mp); 281810616SSebastien.Roy@Sun.COM 281910616SSebastien.Roy@Sun.COM data_mp = mp; 282010616SSebastien.Roy@Sun.COM } while (data_mp != NULL); 282110616SSebastien.Roy@Sun.COM return; 282210616SSebastien.Roy@Sun.COM drop: 282311042SErik.Nordmark@Sun.COM iptun_drop_pkt(data_mp, &iptun->iptun_ierrors); 282410616SSebastien.Roy@Sun.COM } 282510616SSebastien.Roy@Sun.COM 282610616SSebastien.Roy@Sun.COM /* 282710616SSebastien.Roy@Sun.COM * Do 6to4-specific header-processing on output. Return B_TRUE if the packet 282810616SSebastien.Roy@Sun.COM * was processed without issue, or B_FALSE if the packet had issues and should 282910616SSebastien.Roy@Sun.COM * be dropped. 283010616SSebastien.Roy@Sun.COM */ 283110616SSebastien.Roy@Sun.COM static boolean_t 283210616SSebastien.Roy@Sun.COM iptun_out_process_6to4(iptun_t *iptun, ipha_t *outer4, ip6_t *inner6) 283310616SSebastien.Roy@Sun.COM { 283410616SSebastien.Roy@Sun.COM ipaddr_t v4addr; 283510616SSebastien.Roy@Sun.COM 283610616SSebastien.Roy@Sun.COM /* 283710616SSebastien.Roy@Sun.COM * IPv6 source must be a 6to4 address. This is because a conscious 283810616SSebastien.Roy@Sun.COM * decision was made to not allow a Solaris system to be used as a 283910616SSebastien.Roy@Sun.COM * relay router (for security reasons) when 6to4 was initially 284010616SSebastien.Roy@Sun.COM * integrated. If this decision is ever reversed, the following check 284110616SSebastien.Roy@Sun.COM * can be removed. 284210616SSebastien.Roy@Sun.COM */ 284310616SSebastien.Roy@Sun.COM if (!IN6_IS_ADDR_6TO4(&inner6->ip6_src)) 284410616SSebastien.Roy@Sun.COM return (B_FALSE); 284510616SSebastien.Roy@Sun.COM 284610616SSebastien.Roy@Sun.COM /* 284710616SSebastien.Roy@Sun.COM * RFC3056 mandates that the IPv4 source MUST be set to the IPv4 284810616SSebastien.Roy@Sun.COM * portion of the 6to4 IPv6 source address. In other words, make sure 284910616SSebastien.Roy@Sun.COM * that we're tunneling packets from our own 6to4 site. 285010616SSebastien.Roy@Sun.COM */ 285110616SSebastien.Roy@Sun.COM IN6_6TO4_TO_V4ADDR(&inner6->ip6_src, (struct in_addr *)&v4addr); 285210616SSebastien.Roy@Sun.COM if (outer4->ipha_src != v4addr) 285310616SSebastien.Roy@Sun.COM return (B_FALSE); 285410616SSebastien.Roy@Sun.COM 285510616SSebastien.Roy@Sun.COM /* 285610616SSebastien.Roy@Sun.COM * Automatically set the destination of the outer IPv4 header as 285710616SSebastien.Roy@Sun.COM * described in RFC3056. There are two possibilities: 285810616SSebastien.Roy@Sun.COM * 285910616SSebastien.Roy@Sun.COM * a. If the IPv6 destination is a 6to4 address, set the IPv4 address 286010616SSebastien.Roy@Sun.COM * to the IPv4 portion of the 6to4 address. 286110616SSebastien.Roy@Sun.COM * b. If the IPv6 destination is a native IPv6 address, set the IPv4 286210616SSebastien.Roy@Sun.COM * destination to the address of a relay router. 286310616SSebastien.Roy@Sun.COM * 286410616SSebastien.Roy@Sun.COM * Design Note: b shouldn't be necessary here, and this is a flaw in 286510616SSebastien.Roy@Sun.COM * the design of the 6to4relay command. Instead of setting a 6to4 286610616SSebastien.Roy@Sun.COM * relay address in this module via an ioctl, the 6to4relay command 286710616SSebastien.Roy@Sun.COM * could simply add a IPv6 route for native IPv6 addresses (such as a 286810616SSebastien.Roy@Sun.COM * default route) in the forwarding table that uses a 6to4 destination 286910616SSebastien.Roy@Sun.COM * as its next hop, and the IPv4 portion of that address could be a 287010616SSebastien.Roy@Sun.COM * 6to4 relay address. In order for this to work, IP would have to 287110616SSebastien.Roy@Sun.COM * resolve the next hop address, which would necessitate a link-layer 287210616SSebastien.Roy@Sun.COM * address resolver for 6to4 links, which doesn't exist today. 287310616SSebastien.Roy@Sun.COM * 287410616SSebastien.Roy@Sun.COM * In fact, if a resolver existed for 6to4 links, then setting the 287510616SSebastien.Roy@Sun.COM * IPv4 destination in the outer header could be done as part of 287610616SSebastien.Roy@Sun.COM * link-layer address resolution and fast-path header generation, and 287710616SSebastien.Roy@Sun.COM * not here. 287810616SSebastien.Roy@Sun.COM */ 287910616SSebastien.Roy@Sun.COM if (IN6_IS_ADDR_6TO4(&inner6->ip6_dst)) { 288010616SSebastien.Roy@Sun.COM /* destination is a 6to4 router */ 288110616SSebastien.Roy@Sun.COM IN6_6TO4_TO_V4ADDR(&inner6->ip6_dst, 288210616SSebastien.Roy@Sun.COM (struct in_addr *)&outer4->ipha_dst); 288311042SErik.Nordmark@Sun.COM 288411042SErik.Nordmark@Sun.COM /* Reject attempts to send to INADDR_ANY */ 288511042SErik.Nordmark@Sun.COM if (outer4->ipha_dst == INADDR_ANY) 288611042SErik.Nordmark@Sun.COM return (B_FALSE); 288710616SSebastien.Roy@Sun.COM } else { 288810616SSebastien.Roy@Sun.COM /* 288910616SSebastien.Roy@Sun.COM * The destination is a native IPv6 address. If output to a 289010616SSebastien.Roy@Sun.COM * relay-router is enabled, use the relay-router's IPv4 289110616SSebastien.Roy@Sun.COM * address as the destination. 289210616SSebastien.Roy@Sun.COM */ 289310616SSebastien.Roy@Sun.COM if (iptun->iptun_iptuns->iptuns_relay_rtr_addr == INADDR_ANY) 289410616SSebastien.Roy@Sun.COM return (B_FALSE); 289510616SSebastien.Roy@Sun.COM outer4->ipha_dst = iptun->iptun_iptuns->iptuns_relay_rtr_addr; 289610616SSebastien.Roy@Sun.COM } 289710616SSebastien.Roy@Sun.COM 289810616SSebastien.Roy@Sun.COM /* 289910616SSebastien.Roy@Sun.COM * If the outer source and destination are equal, this means that the 290010616SSebastien.Roy@Sun.COM * 6to4 router somehow forwarded an IPv6 packet destined for its own 290110616SSebastien.Roy@Sun.COM * 6to4 site to its 6to4 tunnel interface, which will result in this 290210616SSebastien.Roy@Sun.COM * packet infinitely bouncing between ip and iptun. 290310616SSebastien.Roy@Sun.COM */ 290410616SSebastien.Roy@Sun.COM return (outer4->ipha_src != outer4->ipha_dst); 290510616SSebastien.Roy@Sun.COM } 290610616SSebastien.Roy@Sun.COM 290710616SSebastien.Roy@Sun.COM /* 290810616SSebastien.Roy@Sun.COM * Process output packets with outer IPv4 headers. Frees mp and bumps stat on 290910616SSebastien.Roy@Sun.COM * error. 291010616SSebastien.Roy@Sun.COM */ 291110616SSebastien.Roy@Sun.COM static mblk_t * 291210616SSebastien.Roy@Sun.COM iptun_out_process_ipv4(iptun_t *iptun, mblk_t *mp, ipha_t *outer4, 291311042SErik.Nordmark@Sun.COM ipha_t *inner4, ip6_t *inner6, ip_xmit_attr_t *ixa) 291410616SSebastien.Roy@Sun.COM { 291510616SSebastien.Roy@Sun.COM uint8_t *innerptr = (inner4 != NULL ? 291610616SSebastien.Roy@Sun.COM (uint8_t *)inner4 : (uint8_t *)inner6); 291711042SErik.Nordmark@Sun.COM size_t minmtu = iptun->iptun_typeinfo->iti_minmtu; 291810616SSebastien.Roy@Sun.COM 291910616SSebastien.Roy@Sun.COM if (inner4 != NULL) { 292010616SSebastien.Roy@Sun.COM ASSERT(outer4->ipha_protocol == IPPROTO_ENCAP); 292110616SSebastien.Roy@Sun.COM /* 292210616SSebastien.Roy@Sun.COM * Copy the tos from the inner IPv4 header. We mask off ECN 292310616SSebastien.Roy@Sun.COM * bits (bits 6 and 7) because there is currently no 292410616SSebastien.Roy@Sun.COM * tunnel-tunnel communication to determine if both sides 292510616SSebastien.Roy@Sun.COM * support ECN. We opt for the safe choice: don't copy the 292610616SSebastien.Roy@Sun.COM * ECN bits when doing encapsulation. 292710616SSebastien.Roy@Sun.COM */ 292810616SSebastien.Roy@Sun.COM outer4->ipha_type_of_service = 292910616SSebastien.Roy@Sun.COM inner4->ipha_type_of_service & ~0x03; 293010616SSebastien.Roy@Sun.COM } else { 293110616SSebastien.Roy@Sun.COM ASSERT(outer4->ipha_protocol == IPPROTO_IPV6 && 293210616SSebastien.Roy@Sun.COM inner6 != NULL); 293310616SSebastien.Roy@Sun.COM } 293411042SErik.Nordmark@Sun.COM if (ixa->ixa_flags & IXAF_PMTU_IPV4_DF) 293511042SErik.Nordmark@Sun.COM outer4->ipha_fragment_offset_and_flags |= IPH_DF_HTONS; 293611042SErik.Nordmark@Sun.COM else 293711042SErik.Nordmark@Sun.COM outer4->ipha_fragment_offset_and_flags &= ~IPH_DF_HTONS; 293810616SSebastien.Roy@Sun.COM 293910616SSebastien.Roy@Sun.COM /* 294010616SSebastien.Roy@Sun.COM * As described in section 3.2.2 of RFC4213, if the packet payload is 294110616SSebastien.Roy@Sun.COM * less than or equal to the minimum MTU size, then we need to allow 294210616SSebastien.Roy@Sun.COM * IPv4 to fragment the packet. The reason is that even if we end up 294310616SSebastien.Roy@Sun.COM * receiving an ICMP frag-needed, the interface above this tunnel 294410616SSebastien.Roy@Sun.COM * won't be allowed to drop its MTU as a result, since the packet was 294510616SSebastien.Roy@Sun.COM * already smaller than the smallest allowable MTU for that interface. 294610616SSebastien.Roy@Sun.COM */ 294711042SErik.Nordmark@Sun.COM if (mp->b_wptr - innerptr <= minmtu) { 294810616SSebastien.Roy@Sun.COM outer4->ipha_fragment_offset_and_flags = 0; 294911042SErik.Nordmark@Sun.COM ixa->ixa_flags &= ~IXAF_DONTFRAG; 295011042SErik.Nordmark@Sun.COM } else if (!(ixa->ixa_flags & IXAF_PMTU_TOO_SMALL) && 295111042SErik.Nordmark@Sun.COM (iptun->iptun_typeinfo->iti_type != IPTUN_TYPE_6TO4)) { 295211042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_DONTFRAG; 295311042SErik.Nordmark@Sun.COM } 295411042SErik.Nordmark@Sun.COM 295511042SErik.Nordmark@Sun.COM ixa->ixa_ip_hdr_length = IPH_HDR_LENGTH(outer4); 295611042SErik.Nordmark@Sun.COM ixa->ixa_pktlen = msgdsize(mp); 295711042SErik.Nordmark@Sun.COM ixa->ixa_protocol = outer4->ipha_protocol; 295811042SErik.Nordmark@Sun.COM 295911042SErik.Nordmark@Sun.COM outer4->ipha_length = htons(ixa->ixa_pktlen); 296010616SSebastien.Roy@Sun.COM return (mp); 296110616SSebastien.Roy@Sun.COM } 296210616SSebastien.Roy@Sun.COM 296310616SSebastien.Roy@Sun.COM /* 296410616SSebastien.Roy@Sun.COM * Insert an encapsulation limit destination option in the packet provided. 296510616SSebastien.Roy@Sun.COM * Always consumes the mp argument and returns a new mblk pointer. 296610616SSebastien.Roy@Sun.COM */ 296710616SSebastien.Roy@Sun.COM static mblk_t * 296810616SSebastien.Roy@Sun.COM iptun_insert_encaplimit(iptun_t *iptun, mblk_t *mp, ip6_t *outer6, 296910616SSebastien.Roy@Sun.COM uint8_t limit) 297010616SSebastien.Roy@Sun.COM { 297110616SSebastien.Roy@Sun.COM mblk_t *newmp; 297210616SSebastien.Roy@Sun.COM iptun_ipv6hdrs_t *newouter6; 297310616SSebastien.Roy@Sun.COM 297410616SSebastien.Roy@Sun.COM ASSERT(outer6->ip6_nxt == IPPROTO_IPV6); 297510616SSebastien.Roy@Sun.COM ASSERT(mp->b_cont == NULL); 297610616SSebastien.Roy@Sun.COM 297710616SSebastien.Roy@Sun.COM mp->b_rptr += sizeof (ip6_t); 297811042SErik.Nordmark@Sun.COM newmp = allocb(sizeof (iptun_ipv6hdrs_t) + MBLKL(mp), BPRI_MED); 297910616SSebastien.Roy@Sun.COM if (newmp == NULL) { 298010616SSebastien.Roy@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_noxmtbuf); 298110616SSebastien.Roy@Sun.COM return (NULL); 298210616SSebastien.Roy@Sun.COM } 298310616SSebastien.Roy@Sun.COM newmp->b_wptr += sizeof (iptun_ipv6hdrs_t); 298410616SSebastien.Roy@Sun.COM /* Copy the payload (Starting with the inner IPv6 header). */ 298510616SSebastien.Roy@Sun.COM bcopy(mp->b_rptr, newmp->b_wptr, MBLKL(mp)); 298610616SSebastien.Roy@Sun.COM newmp->b_wptr += MBLKL(mp); 298710616SSebastien.Roy@Sun.COM newouter6 = (iptun_ipv6hdrs_t *)newmp->b_rptr; 298810616SSebastien.Roy@Sun.COM /* Now copy the outer IPv6 header. */ 298910616SSebastien.Roy@Sun.COM bcopy(outer6, &newouter6->it6h_ip6h, sizeof (ip6_t)); 299010616SSebastien.Roy@Sun.COM newouter6->it6h_ip6h.ip6_nxt = IPPROTO_DSTOPTS; 299110616SSebastien.Roy@Sun.COM newouter6->it6h_encaplim = iptun_encaplim_init; 299210616SSebastien.Roy@Sun.COM newouter6->it6h_encaplim.iel_destopt.ip6d_nxt = outer6->ip6_nxt; 299310616SSebastien.Roy@Sun.COM newouter6->it6h_encaplim.iel_telopt.ip6ot_encap_limit = limit; 299410616SSebastien.Roy@Sun.COM 299510616SSebastien.Roy@Sun.COM /* 299610616SSebastien.Roy@Sun.COM * The payload length will be set at the end of 299710616SSebastien.Roy@Sun.COM * iptun_out_process_ipv6(). 299810616SSebastien.Roy@Sun.COM */ 299910616SSebastien.Roy@Sun.COM 300010616SSebastien.Roy@Sun.COM freemsg(mp); 300110616SSebastien.Roy@Sun.COM return (newmp); 300210616SSebastien.Roy@Sun.COM } 300310616SSebastien.Roy@Sun.COM 300410616SSebastien.Roy@Sun.COM /* 300510616SSebastien.Roy@Sun.COM * Process output packets with outer IPv6 headers. Frees mp and bumps stats 300610616SSebastien.Roy@Sun.COM * on error. 300710616SSebastien.Roy@Sun.COM */ 300810616SSebastien.Roy@Sun.COM static mblk_t * 300911042SErik.Nordmark@Sun.COM iptun_out_process_ipv6(iptun_t *iptun, mblk_t *mp, ip6_t *outer6, 301011042SErik.Nordmark@Sun.COM ipha_t *inner4, ip6_t *inner6, ip_xmit_attr_t *ixa) 301110616SSebastien.Roy@Sun.COM { 301211042SErik.Nordmark@Sun.COM uint8_t *innerptr = (inner4 != NULL ? 301311042SErik.Nordmark@Sun.COM (uint8_t *)inner4 : (uint8_t *)inner6); 301411042SErik.Nordmark@Sun.COM size_t minmtu = iptun->iptun_typeinfo->iti_minmtu; 301510616SSebastien.Roy@Sun.COM uint8_t *limit, *configlimit; 301610616SSebastien.Roy@Sun.COM uint32_t offset; 301710616SSebastien.Roy@Sun.COM iptun_ipv6hdrs_t *v6hdrs; 301810616SSebastien.Roy@Sun.COM 301910616SSebastien.Roy@Sun.COM if (inner6 != NULL && iptun_find_encaplimit(mp, inner6, &limit)) { 302010616SSebastien.Roy@Sun.COM /* 302110616SSebastien.Roy@Sun.COM * The inner packet is an IPv6 packet which itself contains an 302210616SSebastien.Roy@Sun.COM * encapsulation limit option. The limit variable points to 302310616SSebastien.Roy@Sun.COM * the value in the embedded option. Process the 302410616SSebastien.Roy@Sun.COM * encapsulation limit option as specified in RFC 2473. 302510616SSebastien.Roy@Sun.COM * 302610616SSebastien.Roy@Sun.COM * If limit is 0, then we've exceeded the limit and we need to 302710616SSebastien.Roy@Sun.COM * send back an ICMPv6 parameter problem message. 302810616SSebastien.Roy@Sun.COM * 302910616SSebastien.Roy@Sun.COM * If limit is > 0, then we decrement it by 1 and make sure 303010616SSebastien.Roy@Sun.COM * that the encapsulation limit option in the outer header 303110616SSebastien.Roy@Sun.COM * reflects that (adding an option if one isn't already 303210616SSebastien.Roy@Sun.COM * there). 303310616SSebastien.Roy@Sun.COM */ 303410616SSebastien.Roy@Sun.COM ASSERT(limit > mp->b_rptr && limit < mp->b_wptr); 303510616SSebastien.Roy@Sun.COM if (*limit == 0) { 303610616SSebastien.Roy@Sun.COM mp->b_rptr = (uint8_t *)inner6; 303710616SSebastien.Roy@Sun.COM offset = limit - mp->b_rptr; 303810616SSebastien.Roy@Sun.COM iptun_icmp_error_v6(iptun, inner6, mp, ICMP6_PARAM_PROB, 303911042SErik.Nordmark@Sun.COM 0, offset, ixa->ixa_tsl); 304010616SSebastien.Roy@Sun.COM atomic_inc_64(&iptun->iptun_noxmtbuf); 304110616SSebastien.Roy@Sun.COM return (NULL); 304210616SSebastien.Roy@Sun.COM } 304310616SSebastien.Roy@Sun.COM 304410616SSebastien.Roy@Sun.COM /* 304510616SSebastien.Roy@Sun.COM * The outer header requires an encapsulation limit option. 304610616SSebastien.Roy@Sun.COM * If there isn't one already, add one. 304710616SSebastien.Roy@Sun.COM */ 304810616SSebastien.Roy@Sun.COM if (iptun->iptun_encaplimit == 0) { 304910616SSebastien.Roy@Sun.COM if ((mp = iptun_insert_encaplimit(iptun, mp, outer6, 305010616SSebastien.Roy@Sun.COM (*limit - 1))) == NULL) 305110616SSebastien.Roy@Sun.COM return (NULL); 305211042SErik.Nordmark@Sun.COM v6hdrs = (iptun_ipv6hdrs_t *)mp->b_rptr; 305310616SSebastien.Roy@Sun.COM } else { 305410616SSebastien.Roy@Sun.COM /* 305510616SSebastien.Roy@Sun.COM * There is an existing encapsulation limit option in 305610616SSebastien.Roy@Sun.COM * the outer header. If the inner encapsulation limit 305710616SSebastien.Roy@Sun.COM * is less than the configured encapsulation limit, 305810616SSebastien.Roy@Sun.COM * update the outer encapsulation limit to reflect 305910616SSebastien.Roy@Sun.COM * this lesser value. 306010616SSebastien.Roy@Sun.COM */ 306110616SSebastien.Roy@Sun.COM v6hdrs = (iptun_ipv6hdrs_t *)mp->b_rptr; 306210616SSebastien.Roy@Sun.COM configlimit = 306310616SSebastien.Roy@Sun.COM &v6hdrs->it6h_encaplim.iel_telopt.ip6ot_encap_limit; 306410616SSebastien.Roy@Sun.COM if ((*limit - 1) < *configlimit) 306510616SSebastien.Roy@Sun.COM *configlimit = (*limit - 1); 306610616SSebastien.Roy@Sun.COM } 306711042SErik.Nordmark@Sun.COM ixa->ixa_ip_hdr_length = sizeof (iptun_ipv6hdrs_t); 306811042SErik.Nordmark@Sun.COM ixa->ixa_protocol = v6hdrs->it6h_encaplim.iel_destopt.ip6d_nxt; 306911042SErik.Nordmark@Sun.COM } else { 307011042SErik.Nordmark@Sun.COM ixa->ixa_ip_hdr_length = sizeof (ip6_t); 307111042SErik.Nordmark@Sun.COM ixa->ixa_protocol = outer6->ip6_nxt; 307210616SSebastien.Roy@Sun.COM } 307311042SErik.Nordmark@Sun.COM /* 307411042SErik.Nordmark@Sun.COM * See iptun_output_process_ipv4() why we allow fragmentation for 307511042SErik.Nordmark@Sun.COM * small packets 307611042SErik.Nordmark@Sun.COM */ 307711042SErik.Nordmark@Sun.COM if (mp->b_wptr - innerptr <= minmtu) 307811042SErik.Nordmark@Sun.COM ixa->ixa_flags &= ~IXAF_DONTFRAG; 307911042SErik.Nordmark@Sun.COM else if (!(ixa->ixa_flags & IXAF_PMTU_TOO_SMALL)) 308011042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_DONTFRAG; 308111042SErik.Nordmark@Sun.COM 308211042SErik.Nordmark@Sun.COM ixa->ixa_pktlen = msgdsize(mp); 308311042SErik.Nordmark@Sun.COM outer6->ip6_plen = htons(ixa->ixa_pktlen - sizeof (ip6_t)); 308410616SSebastien.Roy@Sun.COM return (mp); 308510616SSebastien.Roy@Sun.COM } 308610616SSebastien.Roy@Sun.COM 308710616SSebastien.Roy@Sun.COM /* 308810616SSebastien.Roy@Sun.COM * The IP tunneling MAC-type plugins have already done most of the header 308910616SSebastien.Roy@Sun.COM * processing and validity checks. We are simply responsible for multiplexing 309010616SSebastien.Roy@Sun.COM * down to the ip module below us. 309110616SSebastien.Roy@Sun.COM */ 309210616SSebastien.Roy@Sun.COM static void 309310616SSebastien.Roy@Sun.COM iptun_output(iptun_t *iptun, mblk_t *mp) 309410616SSebastien.Roy@Sun.COM { 309510616SSebastien.Roy@Sun.COM conn_t *connp = iptun->iptun_connp; 309610616SSebastien.Roy@Sun.COM mblk_t *newmp; 309711042SErik.Nordmark@Sun.COM int error; 309811042SErik.Nordmark@Sun.COM ip_xmit_attr_t *ixa; 309910616SSebastien.Roy@Sun.COM 310010616SSebastien.Roy@Sun.COM ASSERT(mp->b_datap->db_type == M_DATA); 310110616SSebastien.Roy@Sun.COM 310210616SSebastien.Roy@Sun.COM if (mp->b_cont != NULL) { 310310616SSebastien.Roy@Sun.COM if ((newmp = msgpullup(mp, -1)) == NULL) { 310410616SSebastien.Roy@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_noxmtbuf); 310510616SSebastien.Roy@Sun.COM return; 310610616SSebastien.Roy@Sun.COM } 310710616SSebastien.Roy@Sun.COM freemsg(mp); 310810616SSebastien.Roy@Sun.COM mp = newmp; 310910616SSebastien.Roy@Sun.COM } 311010616SSebastien.Roy@Sun.COM 311111042SErik.Nordmark@Sun.COM if (iptun->iptun_typeinfo->iti_type == IPTUN_TYPE_6TO4) { 311211042SErik.Nordmark@Sun.COM iptun_output_6to4(iptun, mp); 311311042SErik.Nordmark@Sun.COM return; 311411042SErik.Nordmark@Sun.COM } 311511042SErik.Nordmark@Sun.COM 311611042SErik.Nordmark@Sun.COM if (is_system_labeled()) { 311711042SErik.Nordmark@Sun.COM /* 311811042SErik.Nordmark@Sun.COM * Since the label can be different meaning a potentially 311911042SErik.Nordmark@Sun.COM * different IRE,we always use a unique ip_xmit_attr_t. 312011042SErik.Nordmark@Sun.COM */ 312111042SErik.Nordmark@Sun.COM ixa = conn_get_ixa_exclusive(connp); 312211042SErik.Nordmark@Sun.COM } else { 312311042SErik.Nordmark@Sun.COM /* 312411042SErik.Nordmark@Sun.COM * If no other thread is using conn_ixa this just gets a 312511042SErik.Nordmark@Sun.COM * reference to conn_ixa. Otherwise we get a safe copy of 312611042SErik.Nordmark@Sun.COM * conn_ixa. 312711042SErik.Nordmark@Sun.COM */ 312811042SErik.Nordmark@Sun.COM ixa = conn_get_ixa(connp, B_FALSE); 312911042SErik.Nordmark@Sun.COM } 313011042SErik.Nordmark@Sun.COM if (ixa == NULL) { 313111042SErik.Nordmark@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_oerrors); 313211042SErik.Nordmark@Sun.COM return; 313311042SErik.Nordmark@Sun.COM } 313411042SErik.Nordmark@Sun.COM 313511042SErik.Nordmark@Sun.COM /* 313611042SErik.Nordmark@Sun.COM * In case we got a safe copy of conn_ixa, then we need 313711042SErik.Nordmark@Sun.COM * to fill in any pointers in it. 313811042SErik.Nordmark@Sun.COM */ 313911042SErik.Nordmark@Sun.COM if (ixa->ixa_ire == NULL) { 314011042SErik.Nordmark@Sun.COM error = ip_attr_connect(connp, ixa, &connp->conn_saddr_v6, 314111042SErik.Nordmark@Sun.COM &connp->conn_faddr_v6, &connp->conn_faddr_v6, 0, 314211042SErik.Nordmark@Sun.COM NULL, NULL, 0); 314311042SErik.Nordmark@Sun.COM if (error != 0) { 314411042SErik.Nordmark@Sun.COM if (ixa->ixa_ire != NULL && 314511042SErik.Nordmark@Sun.COM (error == EHOSTUNREACH || error == ENETUNREACH)) { 314611042SErik.Nordmark@Sun.COM /* 314711042SErik.Nordmark@Sun.COM * Let conn_ip_output/ire_send_noroute return 314811042SErik.Nordmark@Sun.COM * the error and send any local ICMP error. 314911042SErik.Nordmark@Sun.COM */ 315011042SErik.Nordmark@Sun.COM error = 0; 315111042SErik.Nordmark@Sun.COM } else { 315211042SErik.Nordmark@Sun.COM ixa_refrele(ixa); 315311042SErik.Nordmark@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_oerrors); 315411042SErik.Nordmark@Sun.COM return; 315511042SErik.Nordmark@Sun.COM } 315611042SErik.Nordmark@Sun.COM } 315711042SErik.Nordmark@Sun.COM } 315811042SErik.Nordmark@Sun.COM 315911042SErik.Nordmark@Sun.COM iptun_output_common(iptun, ixa, mp); 316011042SErik.Nordmark@Sun.COM ixa_refrele(ixa); 316111042SErik.Nordmark@Sun.COM } 316211042SErik.Nordmark@Sun.COM 316311042SErik.Nordmark@Sun.COM /* 316411042SErik.Nordmark@Sun.COM * We use an ixa based on the last destination. 316511042SErik.Nordmark@Sun.COM */ 316611042SErik.Nordmark@Sun.COM static void 316711042SErik.Nordmark@Sun.COM iptun_output_6to4(iptun_t *iptun, mblk_t *mp) 316811042SErik.Nordmark@Sun.COM { 316911042SErik.Nordmark@Sun.COM conn_t *connp = iptun->iptun_connp; 317011042SErik.Nordmark@Sun.COM ipha_t *outer4, *inner4; 317111042SErik.Nordmark@Sun.COM ip6_t *outer6, *inner6; 317211042SErik.Nordmark@Sun.COM ip_xmit_attr_t *ixa; 317311042SErik.Nordmark@Sun.COM ip_xmit_attr_t *oldixa; 317411042SErik.Nordmark@Sun.COM int error; 317511042SErik.Nordmark@Sun.COM boolean_t need_connect; 317611042SErik.Nordmark@Sun.COM in6_addr_t v6dst; 317711042SErik.Nordmark@Sun.COM 317811042SErik.Nordmark@Sun.COM ASSERT(mp->b_cont == NULL); /* Verified by iptun_output */ 317911042SErik.Nordmark@Sun.COM 318011042SErik.Nordmark@Sun.COM /* Make sure we set ipha_dst before we look at ipha_dst */ 318111042SErik.Nordmark@Sun.COM 318211042SErik.Nordmark@Sun.COM (void) iptun_find_headers(mp, 0, &outer4, &inner4, &outer6, &inner6); 318311042SErik.Nordmark@Sun.COM ASSERT(outer4 != NULL); 318411042SErik.Nordmark@Sun.COM if (!iptun_out_process_6to4(iptun, outer4, inner6)) { 318511042SErik.Nordmark@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_oerrors); 318611042SErik.Nordmark@Sun.COM return; 318711042SErik.Nordmark@Sun.COM } 318811042SErik.Nordmark@Sun.COM 318911042SErik.Nordmark@Sun.COM if (is_system_labeled()) { 319011042SErik.Nordmark@Sun.COM /* 319111042SErik.Nordmark@Sun.COM * Since the label can be different meaning a potentially 319211042SErik.Nordmark@Sun.COM * different IRE,we always use a unique ip_xmit_attr_t. 319311042SErik.Nordmark@Sun.COM */ 319411042SErik.Nordmark@Sun.COM ixa = conn_get_ixa_exclusive(connp); 319511042SErik.Nordmark@Sun.COM } else { 319611042SErik.Nordmark@Sun.COM /* 319711042SErik.Nordmark@Sun.COM * If no other thread is using conn_ixa this just gets a 319811042SErik.Nordmark@Sun.COM * reference to conn_ixa. Otherwise we get a safe copy of 319911042SErik.Nordmark@Sun.COM * conn_ixa. 320011042SErik.Nordmark@Sun.COM */ 320111042SErik.Nordmark@Sun.COM ixa = conn_get_ixa(connp, B_FALSE); 320211042SErik.Nordmark@Sun.COM } 320311042SErik.Nordmark@Sun.COM if (ixa == NULL) { 320411042SErik.Nordmark@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_oerrors); 320511042SErik.Nordmark@Sun.COM return; 320611042SErik.Nordmark@Sun.COM } 320711042SErik.Nordmark@Sun.COM 320811042SErik.Nordmark@Sun.COM mutex_enter(&connp->conn_lock); 320911042SErik.Nordmark@Sun.COM if (connp->conn_v4lastdst == outer4->ipha_dst) { 321011042SErik.Nordmark@Sun.COM need_connect = (ixa->ixa_ire == NULL); 321111042SErik.Nordmark@Sun.COM } else { 321211042SErik.Nordmark@Sun.COM /* In case previous destination was multirt */ 321311042SErik.Nordmark@Sun.COM ip_attr_newdst(ixa); 321411042SErik.Nordmark@Sun.COM 321511042SErik.Nordmark@Sun.COM /* 321611042SErik.Nordmark@Sun.COM * We later update conn_ixa when we update conn_v4lastdst 321711042SErik.Nordmark@Sun.COM * which enables subsequent packets to avoid redoing 321811042SErik.Nordmark@Sun.COM * ip_attr_connect 321911042SErik.Nordmark@Sun.COM */ 322011042SErik.Nordmark@Sun.COM need_connect = B_TRUE; 322111042SErik.Nordmark@Sun.COM } 322211042SErik.Nordmark@Sun.COM mutex_exit(&connp->conn_lock); 322311042SErik.Nordmark@Sun.COM 322411042SErik.Nordmark@Sun.COM /* 322511042SErik.Nordmark@Sun.COM * In case we got a safe copy of conn_ixa, or otherwise we don't 322611042SErik.Nordmark@Sun.COM * have a current ixa_ire, then we need to fill in any pointers in 322711042SErik.Nordmark@Sun.COM * the ixa. 322811042SErik.Nordmark@Sun.COM */ 322911042SErik.Nordmark@Sun.COM if (need_connect) { 323011042SErik.Nordmark@Sun.COM IN6_IPADDR_TO_V4MAPPED(outer4->ipha_dst, &v6dst); 323111042SErik.Nordmark@Sun.COM 323211042SErik.Nordmark@Sun.COM /* We handle IPsec in iptun_output_common */ 323311042SErik.Nordmark@Sun.COM error = ip_attr_connect(connp, ixa, &connp->conn_saddr_v6, 323411042SErik.Nordmark@Sun.COM &v6dst, &v6dst, 0, NULL, NULL, 0); 323511042SErik.Nordmark@Sun.COM if (error != 0) { 323611042SErik.Nordmark@Sun.COM if (ixa->ixa_ire != NULL && 323711042SErik.Nordmark@Sun.COM (error == EHOSTUNREACH || error == ENETUNREACH)) { 323811042SErik.Nordmark@Sun.COM /* 323911042SErik.Nordmark@Sun.COM * Let conn_ip_output/ire_send_noroute return 324011042SErik.Nordmark@Sun.COM * the error and send any local ICMP error. 324111042SErik.Nordmark@Sun.COM */ 324211042SErik.Nordmark@Sun.COM error = 0; 324311042SErik.Nordmark@Sun.COM } else { 324411042SErik.Nordmark@Sun.COM ixa_refrele(ixa); 324511042SErik.Nordmark@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_oerrors); 324611042SErik.Nordmark@Sun.COM return; 324711042SErik.Nordmark@Sun.COM } 324811042SErik.Nordmark@Sun.COM } 324911042SErik.Nordmark@Sun.COM } 325011042SErik.Nordmark@Sun.COM 325111042SErik.Nordmark@Sun.COM iptun_output_common(iptun, ixa, mp); 325211042SErik.Nordmark@Sun.COM 325311042SErik.Nordmark@Sun.COM /* Atomically replace conn_ixa and conn_v4lastdst */ 325411042SErik.Nordmark@Sun.COM mutex_enter(&connp->conn_lock); 325511042SErik.Nordmark@Sun.COM if (connp->conn_v4lastdst != outer4->ipha_dst) { 325611042SErik.Nordmark@Sun.COM /* Remember the dst which corresponds to conn_ixa */ 325711042SErik.Nordmark@Sun.COM connp->conn_v6lastdst = v6dst; 325811042SErik.Nordmark@Sun.COM oldixa = conn_replace_ixa(connp, ixa); 325911042SErik.Nordmark@Sun.COM } else { 326011042SErik.Nordmark@Sun.COM oldixa = NULL; 326111042SErik.Nordmark@Sun.COM } 326211042SErik.Nordmark@Sun.COM mutex_exit(&connp->conn_lock); 326311042SErik.Nordmark@Sun.COM ixa_refrele(ixa); 326411042SErik.Nordmark@Sun.COM if (oldixa != NULL) 326511042SErik.Nordmark@Sun.COM ixa_refrele(oldixa); 326611042SErik.Nordmark@Sun.COM } 326711042SErik.Nordmark@Sun.COM 326811042SErik.Nordmark@Sun.COM /* 326911042SErik.Nordmark@Sun.COM * Check the destination/label. Modifies *mpp by adding/removing CIPSO. 327011042SErik.Nordmark@Sun.COM * 327111042SErik.Nordmark@Sun.COM * We get the label from the message in order to honor the 327211042SErik.Nordmark@Sun.COM * ULPs/IPs choice of label. This will be NULL for forwarded 327311042SErik.Nordmark@Sun.COM * packets, neighbor discovery packets and some others. 327411042SErik.Nordmark@Sun.COM */ 327511042SErik.Nordmark@Sun.COM static int 327611042SErik.Nordmark@Sun.COM iptun_output_check_label(mblk_t **mpp, ip_xmit_attr_t *ixa) 327711042SErik.Nordmark@Sun.COM { 327811042SErik.Nordmark@Sun.COM cred_t *cr; 327911042SErik.Nordmark@Sun.COM int adjust; 328011042SErik.Nordmark@Sun.COM int iplen; 328111042SErik.Nordmark@Sun.COM int err; 328211042SErik.Nordmark@Sun.COM ts_label_t *effective_tsl = NULL; 328311042SErik.Nordmark@Sun.COM 328411042SErik.Nordmark@Sun.COM 328511042SErik.Nordmark@Sun.COM ASSERT(is_system_labeled()); 328611042SErik.Nordmark@Sun.COM 328711042SErik.Nordmark@Sun.COM cr = msg_getcred(*mpp, NULL); 328811042SErik.Nordmark@Sun.COM if (cr == NULL) 328911042SErik.Nordmark@Sun.COM return (0); 329011042SErik.Nordmark@Sun.COM 329111042SErik.Nordmark@Sun.COM /* 329211042SErik.Nordmark@Sun.COM * We need to start with a label based on the IP/ULP above us 329311042SErik.Nordmark@Sun.COM */ 329411042SErik.Nordmark@Sun.COM ip_xmit_attr_restore_tsl(ixa, cr); 329511042SErik.Nordmark@Sun.COM 329611042SErik.Nordmark@Sun.COM /* 329711042SErik.Nordmark@Sun.COM * Need to update packet with any CIPSO option since 329811042SErik.Nordmark@Sun.COM * conn_ip_output doesn't do that. 329911042SErik.Nordmark@Sun.COM */ 330011042SErik.Nordmark@Sun.COM if (ixa->ixa_flags & IXAF_IS_IPV4) { 330111042SErik.Nordmark@Sun.COM ipha_t *ipha; 330211042SErik.Nordmark@Sun.COM 330311042SErik.Nordmark@Sun.COM ipha = (ipha_t *)(*mpp)->b_rptr; 330411042SErik.Nordmark@Sun.COM iplen = ntohs(ipha->ipha_length); 330511042SErik.Nordmark@Sun.COM err = tsol_check_label_v4(ixa->ixa_tsl, 330611042SErik.Nordmark@Sun.COM ixa->ixa_zoneid, mpp, CONN_MAC_DEFAULT, B_FALSE, 330711042SErik.Nordmark@Sun.COM ixa->ixa_ipst, &effective_tsl); 330811042SErik.Nordmark@Sun.COM if (err != 0) 330911042SErik.Nordmark@Sun.COM return (err); 331011042SErik.Nordmark@Sun.COM 331111042SErik.Nordmark@Sun.COM ipha = (ipha_t *)(*mpp)->b_rptr; 331211042SErik.Nordmark@Sun.COM adjust = (int)ntohs(ipha->ipha_length) - iplen; 331311042SErik.Nordmark@Sun.COM } else { 331411042SErik.Nordmark@Sun.COM ip6_t *ip6h; 331511042SErik.Nordmark@Sun.COM 331611042SErik.Nordmark@Sun.COM ip6h = (ip6_t *)(*mpp)->b_rptr; 331711042SErik.Nordmark@Sun.COM iplen = ntohs(ip6h->ip6_plen); 331811042SErik.Nordmark@Sun.COM 331911042SErik.Nordmark@Sun.COM err = tsol_check_label_v6(ixa->ixa_tsl, 332011042SErik.Nordmark@Sun.COM ixa->ixa_zoneid, mpp, CONN_MAC_DEFAULT, B_FALSE, 332111042SErik.Nordmark@Sun.COM ixa->ixa_ipst, &effective_tsl); 332211042SErik.Nordmark@Sun.COM if (err != 0) 332311042SErik.Nordmark@Sun.COM return (err); 332411042SErik.Nordmark@Sun.COM 332511042SErik.Nordmark@Sun.COM ip6h = (ip6_t *)(*mpp)->b_rptr; 332611042SErik.Nordmark@Sun.COM adjust = (int)ntohs(ip6h->ip6_plen) - iplen; 332711042SErik.Nordmark@Sun.COM } 332811042SErik.Nordmark@Sun.COM 332911042SErik.Nordmark@Sun.COM if (effective_tsl != NULL) { 333011042SErik.Nordmark@Sun.COM /* Update the label */ 333111042SErik.Nordmark@Sun.COM ip_xmit_attr_replace_tsl(ixa, effective_tsl); 333211042SErik.Nordmark@Sun.COM } 333311042SErik.Nordmark@Sun.COM ixa->ixa_pktlen += adjust; 333411042SErik.Nordmark@Sun.COM ixa->ixa_ip_hdr_length += adjust; 333511042SErik.Nordmark@Sun.COM return (0); 333611042SErik.Nordmark@Sun.COM } 333711042SErik.Nordmark@Sun.COM 333811042SErik.Nordmark@Sun.COM 333911042SErik.Nordmark@Sun.COM static void 334011042SErik.Nordmark@Sun.COM iptun_output_common(iptun_t *iptun, ip_xmit_attr_t *ixa, mblk_t *mp) 334111042SErik.Nordmark@Sun.COM { 334211042SErik.Nordmark@Sun.COM ipsec_tun_pol_t *itp = iptun->iptun_itp; 334311042SErik.Nordmark@Sun.COM int outer_hlen; 334411042SErik.Nordmark@Sun.COM mblk_t *newmp; 334511042SErik.Nordmark@Sun.COM ipha_t *outer4, *inner4; 334611042SErik.Nordmark@Sun.COM ip6_t *outer6, *inner6; 334711042SErik.Nordmark@Sun.COM int error; 334811042SErik.Nordmark@Sun.COM boolean_t update_pktlen; 334911042SErik.Nordmark@Sun.COM 335011042SErik.Nordmark@Sun.COM ASSERT(ixa->ixa_ire != NULL); 335111042SErik.Nordmark@Sun.COM 335211042SErik.Nordmark@Sun.COM outer_hlen = iptun_find_headers(mp, 0, &outer4, &inner4, &outer6, 335311042SErik.Nordmark@Sun.COM &inner6); 335410616SSebastien.Roy@Sun.COM if (outer_hlen == 0) { 335510616SSebastien.Roy@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_oerrors); 335610616SSebastien.Roy@Sun.COM return; 335710616SSebastien.Roy@Sun.COM } 335810616SSebastien.Roy@Sun.COM 335911305SPaul.Wernau@Sun.COM /* Save IXAF_DONTFRAG value */ 336011305SPaul.Wernau@Sun.COM iaflags_t dontfrag = ixa->ixa_flags & IXAF_DONTFRAG; 336111305SPaul.Wernau@Sun.COM 336210616SSebastien.Roy@Sun.COM /* Perform header processing. */ 336311042SErik.Nordmark@Sun.COM if (outer4 != NULL) { 336411042SErik.Nordmark@Sun.COM mp = iptun_out_process_ipv4(iptun, mp, outer4, inner4, inner6, 336511042SErik.Nordmark@Sun.COM ixa); 336611042SErik.Nordmark@Sun.COM } else { 336711042SErik.Nordmark@Sun.COM mp = iptun_out_process_ipv6(iptun, mp, outer6, inner4, inner6, 336811042SErik.Nordmark@Sun.COM ixa); 336911042SErik.Nordmark@Sun.COM } 337010616SSebastien.Roy@Sun.COM if (mp == NULL) 337110616SSebastien.Roy@Sun.COM return; 337210616SSebastien.Roy@Sun.COM 337310616SSebastien.Roy@Sun.COM /* 337410616SSebastien.Roy@Sun.COM * Let's hope the compiler optimizes this with "branch taken". 337510616SSebastien.Roy@Sun.COM */ 337610616SSebastien.Roy@Sun.COM if (itp != NULL && (itp->itp_flags & ITPF_P_ACTIVE)) { 337711042SErik.Nordmark@Sun.COM /* This updates the ip_xmit_attr_t */ 337811042SErik.Nordmark@Sun.COM mp = ipsec_tun_outbound(mp, iptun, inner4, inner6, outer4, 337911042SErik.Nordmark@Sun.COM outer6, outer_hlen, ixa); 338011042SErik.Nordmark@Sun.COM if (mp == NULL) { 338110616SSebastien.Roy@Sun.COM atomic_inc_64(&iptun->iptun_oerrors); 338210616SSebastien.Roy@Sun.COM return; 338310616SSebastien.Roy@Sun.COM } 338411042SErik.Nordmark@Sun.COM if (is_system_labeled()) { 338511042SErik.Nordmark@Sun.COM /* 338611042SErik.Nordmark@Sun.COM * Might change the packet by adding/removing CIPSO. 338711042SErik.Nordmark@Sun.COM * After this caller inner* and outer* and outer_hlen 338811042SErik.Nordmark@Sun.COM * might be invalid. 338911042SErik.Nordmark@Sun.COM */ 339011042SErik.Nordmark@Sun.COM error = iptun_output_check_label(&mp, ixa); 339111042SErik.Nordmark@Sun.COM if (error != 0) { 339211042SErik.Nordmark@Sun.COM ip2dbg(("label check failed (%d)\n", error)); 339311042SErik.Nordmark@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_oerrors); 339411042SErik.Nordmark@Sun.COM return; 339511042SErik.Nordmark@Sun.COM } 339611042SErik.Nordmark@Sun.COM } 339711042SErik.Nordmark@Sun.COM 339810616SSebastien.Roy@Sun.COM /* 339910616SSebastien.Roy@Sun.COM * ipsec_tun_outbound() returns a chain of tunneled IP 340010616SSebastien.Roy@Sun.COM * fragments linked with b_next (or a single message if the 340111042SErik.Nordmark@Sun.COM * tunneled packet wasn't a fragment). 340211042SErik.Nordmark@Sun.COM * If fragcache returned a list then we need to update 340311042SErik.Nordmark@Sun.COM * ixa_pktlen for all packets in the list. 340411042SErik.Nordmark@Sun.COM */ 340511042SErik.Nordmark@Sun.COM update_pktlen = (mp->b_next != NULL); 340611042SErik.Nordmark@Sun.COM 340711042SErik.Nordmark@Sun.COM /* 340811042SErik.Nordmark@Sun.COM * Otherwise, we're good to go. The ixa has been updated with 340910616SSebastien.Roy@Sun.COM * instructions for outbound IPsec processing. 341010616SSebastien.Roy@Sun.COM */ 341110616SSebastien.Roy@Sun.COM for (newmp = mp; newmp != NULL; newmp = mp) { 341211305SPaul.Wernau@Sun.COM size_t minmtu = iptun->iptun_typeinfo->iti_minmtu; 341311305SPaul.Wernau@Sun.COM 341410616SSebastien.Roy@Sun.COM atomic_inc_64(&iptun->iptun_opackets); 341511042SErik.Nordmark@Sun.COM atomic_add_64(&iptun->iptun_obytes, ixa->ixa_pktlen); 341610616SSebastien.Roy@Sun.COM mp = mp->b_next; 341710616SSebastien.Roy@Sun.COM newmp->b_next = NULL; 341811042SErik.Nordmark@Sun.COM 341911305SPaul.Wernau@Sun.COM /* 342011305SPaul.Wernau@Sun.COM * The IXAF_DONTFRAG flag is global, but there is 342111305SPaul.Wernau@Sun.COM * a chain here. Check if we're really already 342211305SPaul.Wernau@Sun.COM * smaller than the minimum allowed MTU and reset here 342311305SPaul.Wernau@Sun.COM * appropriately. Otherwise one small packet can kill 342411305SPaul.Wernau@Sun.COM * the whole chain's path mtu discovery. 342511305SPaul.Wernau@Sun.COM * In addition, update the pktlen to the length of 342611305SPaul.Wernau@Sun.COM * the actual packet being processed. 342711305SPaul.Wernau@Sun.COM */ 342811305SPaul.Wernau@Sun.COM if (update_pktlen) { 342911305SPaul.Wernau@Sun.COM ixa->ixa_pktlen = msgdsize(newmp); 343011305SPaul.Wernau@Sun.COM if (ixa->ixa_pktlen <= minmtu) 343111305SPaul.Wernau@Sun.COM ixa->ixa_flags &= ~IXAF_DONTFRAG; 343211305SPaul.Wernau@Sun.COM } 343311042SErik.Nordmark@Sun.COM 343411042SErik.Nordmark@Sun.COM atomic_inc_64(&iptun->iptun_opackets); 343511042SErik.Nordmark@Sun.COM atomic_add_64(&iptun->iptun_obytes, ixa->ixa_pktlen); 343611042SErik.Nordmark@Sun.COM 343711042SErik.Nordmark@Sun.COM error = conn_ip_output(newmp, ixa); 343811305SPaul.Wernau@Sun.COM 343911305SPaul.Wernau@Sun.COM /* Restore IXAF_DONTFRAG value */ 344011305SPaul.Wernau@Sun.COM ixa->ixa_flags |= dontfrag; 344111305SPaul.Wernau@Sun.COM 344211042SErik.Nordmark@Sun.COM if (error == EMSGSIZE) { 344311042SErik.Nordmark@Sun.COM /* IPsec policy might have changed */ 344411042SErik.Nordmark@Sun.COM (void) iptun_update_mtu(iptun, ixa, 0); 344511042SErik.Nordmark@Sun.COM } 344610616SSebastien.Roy@Sun.COM } 344710616SSebastien.Roy@Sun.COM } else { 344810616SSebastien.Roy@Sun.COM /* 344910616SSebastien.Roy@Sun.COM * The ip module will potentially apply global policy to the 345010616SSebastien.Roy@Sun.COM * packet in its output path if there's no active tunnel 345110616SSebastien.Roy@Sun.COM * policy. 345210616SSebastien.Roy@Sun.COM */ 345311042SErik.Nordmark@Sun.COM ASSERT(ixa->ixa_ipsec_policy == NULL); 345411042SErik.Nordmark@Sun.COM mp = ip_output_attach_policy(mp, outer4, outer6, NULL, ixa); 345511042SErik.Nordmark@Sun.COM if (mp == NULL) { 345611042SErik.Nordmark@Sun.COM atomic_inc_64(&iptun->iptun_oerrors); 345711042SErik.Nordmark@Sun.COM return; 345811042SErik.Nordmark@Sun.COM } 345911042SErik.Nordmark@Sun.COM if (is_system_labeled()) { 346011042SErik.Nordmark@Sun.COM /* 346111042SErik.Nordmark@Sun.COM * Might change the packet by adding/removing CIPSO. 346211042SErik.Nordmark@Sun.COM * After this caller inner* and outer* and outer_hlen 346311042SErik.Nordmark@Sun.COM * might be invalid. 346411042SErik.Nordmark@Sun.COM */ 346511042SErik.Nordmark@Sun.COM error = iptun_output_check_label(&mp, ixa); 346611042SErik.Nordmark@Sun.COM if (error != 0) { 346711042SErik.Nordmark@Sun.COM ip2dbg(("label check failed (%d)\n", error)); 346811042SErik.Nordmark@Sun.COM iptun_drop_pkt(mp, &iptun->iptun_oerrors); 346911042SErik.Nordmark@Sun.COM return; 347011042SErik.Nordmark@Sun.COM } 347111042SErik.Nordmark@Sun.COM } 347211042SErik.Nordmark@Sun.COM 347310616SSebastien.Roy@Sun.COM atomic_inc_64(&iptun->iptun_opackets); 347411042SErik.Nordmark@Sun.COM atomic_add_64(&iptun->iptun_obytes, ixa->ixa_pktlen); 347511042SErik.Nordmark@Sun.COM 347611042SErik.Nordmark@Sun.COM error = conn_ip_output(mp, ixa); 347711042SErik.Nordmark@Sun.COM if (error == EMSGSIZE) { 347811042SErik.Nordmark@Sun.COM /* IPsec policy might have changed */ 347911042SErik.Nordmark@Sun.COM (void) iptun_update_mtu(iptun, ixa, 0); 348011042SErik.Nordmark@Sun.COM } 348110616SSebastien.Roy@Sun.COM } 348211042SErik.Nordmark@Sun.COM if (ixa->ixa_flags & IXAF_IPSEC_SECURE) 348311042SErik.Nordmark@Sun.COM ipsec_out_release_refs(ixa); 348410616SSebastien.Roy@Sun.COM } 348510616SSebastien.Roy@Sun.COM 348610616SSebastien.Roy@Sun.COM static mac_callbacks_t iptun_m_callbacks = { 348711878SVenu.Iyer@Sun.COM .mc_callbacks = (MC_SETPROP | MC_GETPROP | MC_PROPINFO), 348810616SSebastien.Roy@Sun.COM .mc_getstat = iptun_m_getstat, 348910616SSebastien.Roy@Sun.COM .mc_start = iptun_m_start, 349010616SSebastien.Roy@Sun.COM .mc_stop = iptun_m_stop, 349110616SSebastien.Roy@Sun.COM .mc_setpromisc = iptun_m_setpromisc, 349210616SSebastien.Roy@Sun.COM .mc_multicst = iptun_m_multicst, 349310616SSebastien.Roy@Sun.COM .mc_unicst = iptun_m_unicst, 349410616SSebastien.Roy@Sun.COM .mc_tx = iptun_m_tx, 349511878SVenu.Iyer@Sun.COM .mc_reserved = NULL, 349610616SSebastien.Roy@Sun.COM .mc_setprop = iptun_m_setprop, 349711878SVenu.Iyer@Sun.COM .mc_getprop = iptun_m_getprop, 349811878SVenu.Iyer@Sun.COM .mc_propinfo = iptun_m_propinfo 349910616SSebastien.Roy@Sun.COM }; 3500