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