12b24ab6bSSebastien Roy /* 22b24ab6bSSebastien Roy * CDDL HEADER START 32b24ab6bSSebastien Roy * 42b24ab6bSSebastien Roy * The contents of this file are subject to the terms of the 52b24ab6bSSebastien Roy * Common Development and Distribution License (the "License"). 62b24ab6bSSebastien Roy * You may not use this file except in compliance with the License. 72b24ab6bSSebastien Roy * 82b24ab6bSSebastien Roy * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 92b24ab6bSSebastien Roy * or http://www.opensolaris.org/os/licensing. 102b24ab6bSSebastien Roy * See the License for the specific language governing permissions 112b24ab6bSSebastien Roy * and limitations under the License. 122b24ab6bSSebastien Roy * 132b24ab6bSSebastien Roy * When distributing Covered Code, include this CDDL HEADER in each 142b24ab6bSSebastien Roy * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 152b24ab6bSSebastien Roy * If applicable, add the following below this CDDL HEADER, with the 162b24ab6bSSebastien Roy * fields enclosed by brackets "[]" replaced with your own identifying 172b24ab6bSSebastien Roy * information: Portions Copyright [yyyy] [name of copyright owner] 182b24ab6bSSebastien Roy * 192b24ab6bSSebastien Roy * CDDL HEADER END 202b24ab6bSSebastien Roy */ 212b24ab6bSSebastien Roy /* 22*e5e7971fSErik Nordmark * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 232b24ab6bSSebastien Roy */ 242b24ab6bSSebastien Roy 252b24ab6bSSebastien Roy /* 262b24ab6bSSebastien Roy * iptun - IP Tunneling Driver 272b24ab6bSSebastien Roy * 282b24ab6bSSebastien Roy * This module is a GLDv3 driver that implements virtual datalinks over IP 292b24ab6bSSebastien Roy * (a.k.a, IP tunneling). The datalinks are managed through a dld ioctl 302b24ab6bSSebastien Roy * interface (see iptun_ctl.c), and registered with GLDv3 using 312b24ab6bSSebastien Roy * mac_register(). It implements the logic for various forms of IP (IPv4 or 322b24ab6bSSebastien Roy * IPv6) encapsulation within IP (IPv4 or IPv6) by interacting with the ip 332b24ab6bSSebastien Roy * module below it. Each virtual IP tunnel datalink has a conn_t associated 342b24ab6bSSebastien Roy * with it representing the "outer" IP connection. 352b24ab6bSSebastien Roy * 362b24ab6bSSebastien Roy * The module implements the following locking semantics: 372b24ab6bSSebastien Roy * 382b24ab6bSSebastien Roy * Lookups and deletions in iptun_hash are synchronized using iptun_hash_lock. 392b24ab6bSSebastien Roy * See comments above iptun_hash_lock for details. 402b24ab6bSSebastien Roy * 412b24ab6bSSebastien Roy * No locks are ever held while calling up to GLDv3. The general architecture 422b24ab6bSSebastien Roy * of GLDv3 requires this, as the mac perimeter (essentially a lock) for a 432b24ab6bSSebastien Roy * given link will be held while making downcalls (iptun_m_*() callbacks). 442b24ab6bSSebastien Roy * Because we need to hold locks while handling downcalls, holding these locks 452b24ab6bSSebastien Roy * while issuing upcalls results in deadlock scenarios. See the block comment 462b24ab6bSSebastien Roy * above iptun_task_cb() for details on how we safely issue upcalls without 472b24ab6bSSebastien Roy * holding any locks. 482b24ab6bSSebastien Roy * 492b24ab6bSSebastien Roy * The contents of each iptun_t is protected by an iptun_mutex which is held 502b24ab6bSSebastien Roy * in iptun_enter() (called by iptun_enter_by_linkid()), and exited in 512b24ab6bSSebastien Roy * iptun_exit(). 522b24ab6bSSebastien Roy * 532b24ab6bSSebastien Roy * See comments in iptun_delete() and iptun_free() for details on how the 542b24ab6bSSebastien Roy * iptun_t is deleted safely. 552b24ab6bSSebastien Roy */ 562b24ab6bSSebastien Roy 572b24ab6bSSebastien Roy #include <sys/types.h> 582b24ab6bSSebastien Roy #include <sys/kmem.h> 592b24ab6bSSebastien Roy #include <sys/errno.h> 602b24ab6bSSebastien Roy #include <sys/modhash.h> 612b24ab6bSSebastien Roy #include <sys/list.h> 622b24ab6bSSebastien Roy #include <sys/strsun.h> 632b24ab6bSSebastien Roy #include <sys/file.h> 642b24ab6bSSebastien Roy #include <sys/systm.h> 652b24ab6bSSebastien Roy #include <sys/tihdr.h> 662b24ab6bSSebastien Roy #include <sys/param.h> 672b24ab6bSSebastien Roy #include <sys/mac_provider.h> 682b24ab6bSSebastien Roy #include <sys/mac_ipv4.h> 692b24ab6bSSebastien Roy #include <sys/mac_ipv6.h> 702b24ab6bSSebastien Roy #include <sys/mac_6to4.h> 712b24ab6bSSebastien Roy #include <sys/tsol/tnet.h> 722b24ab6bSSebastien Roy #include <sys/sunldi.h> 732b24ab6bSSebastien Roy #include <netinet/in.h> 742b24ab6bSSebastien Roy #include <netinet/ip6.h> 752b24ab6bSSebastien Roy #include <inet/ip.h> 762b24ab6bSSebastien Roy #include <inet/ip_ire.h> 772b24ab6bSSebastien Roy #include <inet/ipsec_impl.h> 78bd670b35SErik Nordmark #include <sys/tsol/label.h> 79bd670b35SErik Nordmark #include <sys/tsol/tnet.h> 802b24ab6bSSebastien Roy #include <inet/iptun.h> 812b24ab6bSSebastien Roy #include "iptun_impl.h" 822b24ab6bSSebastien Roy 832b24ab6bSSebastien Roy /* Do the tunnel type and address family match? */ 842b24ab6bSSebastien Roy #define IPTUN_ADDR_MATCH(iptun_type, family) \ 852b24ab6bSSebastien Roy ((iptun_type == IPTUN_TYPE_IPV4 && family == AF_INET) || \ 862b24ab6bSSebastien Roy (iptun_type == IPTUN_TYPE_IPV6 && family == AF_INET6) || \ 872b24ab6bSSebastien Roy (iptun_type == IPTUN_TYPE_6TO4 && family == AF_INET)) 882b24ab6bSSebastien Roy 892b24ab6bSSebastien Roy #define IPTUN_HASH_KEY(key) ((mod_hash_key_t)(uintptr_t)(key)) 902b24ab6bSSebastien Roy 912b24ab6bSSebastien Roy #define IPTUN_MIN_IPV4_MTU 576 /* ip.h still uses 68 (!) */ 922b24ab6bSSebastien Roy #define IPTUN_MIN_IPV6_MTU IPV6_MIN_MTU 932b24ab6bSSebastien Roy #define IPTUN_MAX_IPV4_MTU (IP_MAXPACKET - sizeof (ipha_t)) 942b24ab6bSSebastien Roy #define IPTUN_MAX_IPV6_MTU (IP_MAXPACKET - sizeof (ip6_t) - \ 952b24ab6bSSebastien Roy sizeof (iptun_encaplim_t)) 962b24ab6bSSebastien Roy 972b24ab6bSSebastien Roy #define IPTUN_MIN_HOPLIMIT 1 982b24ab6bSSebastien Roy #define IPTUN_MAX_HOPLIMIT UINT8_MAX 992b24ab6bSSebastien Roy 1002b24ab6bSSebastien Roy #define IPTUN_MIN_ENCAPLIMIT 0 1012b24ab6bSSebastien Roy #define IPTUN_MAX_ENCAPLIMIT UINT8_MAX 1022b24ab6bSSebastien Roy 1032b24ab6bSSebastien Roy #define IPTUN_IPSEC_REQ_MASK (IPSEC_PREF_REQUIRED | IPSEC_PREF_NEVER) 1042b24ab6bSSebastien Roy 1052b24ab6bSSebastien Roy static iptun_encaplim_t iptun_encaplim_init = { 1062b24ab6bSSebastien Roy { IPPROTO_NONE, 0 }, 1072b24ab6bSSebastien Roy IP6OPT_TUNNEL_LIMIT, 1082b24ab6bSSebastien Roy 1, 1092b24ab6bSSebastien Roy IPTUN_DEFAULT_ENCAPLIMIT, /* filled in with actual value later */ 1102b24ab6bSSebastien Roy IP6OPT_PADN, 1112b24ab6bSSebastien Roy 1, 1122b24ab6bSSebastien Roy 0 1132b24ab6bSSebastien Roy }; 1142b24ab6bSSebastien Roy 115bd670b35SErik Nordmark /* 116bd670b35SErik Nordmark * Table containing per-iptun-type information. 117bd670b35SErik Nordmark * Since IPv6 can run over all of these we have the IPv6 min as the min MTU. 118bd670b35SErik Nordmark */ 1192b24ab6bSSebastien Roy static iptun_typeinfo_t iptun_type_table[] = { 120bd670b35SErik Nordmark { IPTUN_TYPE_IPV4, MAC_PLUGIN_IDENT_IPV4, IPV4_VERSION, 121bd670b35SErik Nordmark IPTUN_MIN_IPV6_MTU, IPTUN_MAX_IPV4_MTU, B_TRUE }, 122bd670b35SErik Nordmark { IPTUN_TYPE_IPV6, MAC_PLUGIN_IDENT_IPV6, IPV6_VERSION, 1232b24ab6bSSebastien Roy IPTUN_MIN_IPV6_MTU, IPTUN_MAX_IPV6_MTU, B_TRUE }, 124bd670b35SErik Nordmark { IPTUN_TYPE_6TO4, MAC_PLUGIN_IDENT_6TO4, IPV4_VERSION, 125bd670b35SErik Nordmark IPTUN_MIN_IPV6_MTU, IPTUN_MAX_IPV4_MTU, B_FALSE }, 126bd670b35SErik Nordmark { IPTUN_TYPE_UNKNOWN, NULL, 0, 0, 0, B_FALSE } 1272b24ab6bSSebastien Roy }; 1282b24ab6bSSebastien Roy 1292b24ab6bSSebastien Roy /* 1302b24ab6bSSebastien Roy * iptun_hash is an iptun_t lookup table by link ID protected by 1312b24ab6bSSebastien Roy * iptun_hash_lock. While the hash table's integrity is maintained via 1322b24ab6bSSebastien Roy * internal locking in the mod_hash_*() functions, we need additional locking 1332b24ab6bSSebastien Roy * so that an iptun_t cannot be deleted after a hash lookup has returned an 1342b24ab6bSSebastien Roy * iptun_t and before iptun_lock has been entered. As such, we use 1352b24ab6bSSebastien Roy * iptun_hash_lock when doing lookups and removals from iptun_hash. 1362b24ab6bSSebastien Roy */ 1372b24ab6bSSebastien Roy mod_hash_t *iptun_hash; 1382b24ab6bSSebastien Roy static kmutex_t iptun_hash_lock; 1392b24ab6bSSebastien Roy 1402b24ab6bSSebastien Roy static uint_t iptun_tunnelcount; /* total for all stacks */ 1412b24ab6bSSebastien Roy kmem_cache_t *iptun_cache; 1422b24ab6bSSebastien Roy ddi_taskq_t *iptun_taskq; 1432b24ab6bSSebastien Roy 1442b24ab6bSSebastien Roy typedef enum { 1452b24ab6bSSebastien Roy IPTUN_TASK_MTU_UPDATE, /* tell mac about new tunnel link MTU */ 1462b24ab6bSSebastien Roy IPTUN_TASK_LADDR_UPDATE, /* tell mac about new local address */ 1472b24ab6bSSebastien Roy IPTUN_TASK_RADDR_UPDATE, /* tell mac about new remote address */ 1482b24ab6bSSebastien Roy IPTUN_TASK_LINK_UPDATE, /* tell mac about new link state */ 1492b24ab6bSSebastien Roy IPTUN_TASK_PDATA_UPDATE /* tell mac about updated plugin data */ 1502b24ab6bSSebastien Roy } iptun_task_t; 1512b24ab6bSSebastien Roy 1522b24ab6bSSebastien Roy typedef struct iptun_task_data_s { 1532b24ab6bSSebastien Roy iptun_task_t itd_task; 1542b24ab6bSSebastien Roy datalink_id_t itd_linkid; 1552b24ab6bSSebastien Roy } iptun_task_data_t; 1562b24ab6bSSebastien Roy 1572b24ab6bSSebastien Roy static void iptun_task_dispatch(iptun_t *, iptun_task_t); 1582b24ab6bSSebastien Roy static int iptun_enter(iptun_t *); 1592b24ab6bSSebastien Roy static void iptun_exit(iptun_t *); 1602b24ab6bSSebastien Roy static void iptun_headergen(iptun_t *, boolean_t); 1612b24ab6bSSebastien Roy static void iptun_drop_pkt(mblk_t *, uint64_t *); 162bd670b35SErik Nordmark static void iptun_input(void *, mblk_t *, void *, ip_recv_attr_t *); 163bd670b35SErik Nordmark static void iptun_input_icmp(void *, mblk_t *, void *, ip_recv_attr_t *); 1642b24ab6bSSebastien Roy static void iptun_output(iptun_t *, mblk_t *); 165bd670b35SErik Nordmark static uint32_t iptun_get_maxmtu(iptun_t *, ip_xmit_attr_t *, uint32_t); 166bd670b35SErik Nordmark static uint32_t iptun_update_mtu(iptun_t *, ip_xmit_attr_t *, uint32_t); 167bd670b35SErik Nordmark static uint32_t iptun_get_dst_pmtu(iptun_t *, ip_xmit_attr_t *); 168bd670b35SErik Nordmark static void iptun_update_dst_pmtu(iptun_t *, ip_xmit_attr_t *); 1692b24ab6bSSebastien Roy static int iptun_setladdr(iptun_t *, const struct sockaddr_storage *); 1702b24ab6bSSebastien Roy 171bd670b35SErik Nordmark static void iptun_output_6to4(iptun_t *, mblk_t *); 172bd670b35SErik Nordmark static void iptun_output_common(iptun_t *, ip_xmit_attr_t *, mblk_t *); 173bd670b35SErik Nordmark static boolean_t iptun_verifyicmp(conn_t *, void *, icmph_t *, icmp6_t *, 174bd670b35SErik Nordmark ip_recv_attr_t *); 175bd670b35SErik Nordmark 176bd670b35SErik Nordmark static void iptun_notify(void *, ip_xmit_attr_t *, ixa_notify_type_t, 177bd670b35SErik Nordmark ixa_notify_arg_t); 178bd670b35SErik Nordmark 1792b24ab6bSSebastien Roy static mac_callbacks_t iptun_m_callbacks; 1802b24ab6bSSebastien Roy 1812b24ab6bSSebastien Roy static int 1822b24ab6bSSebastien Roy iptun_m_getstat(void *arg, uint_t stat, uint64_t *val) 1832b24ab6bSSebastien Roy { 1842b24ab6bSSebastien Roy iptun_t *iptun = arg; 1852b24ab6bSSebastien Roy int err = 0; 1862b24ab6bSSebastien Roy 1872b24ab6bSSebastien Roy switch (stat) { 1882b24ab6bSSebastien Roy case MAC_STAT_IERRORS: 1892b24ab6bSSebastien Roy *val = iptun->iptun_ierrors; 1902b24ab6bSSebastien Roy break; 1912b24ab6bSSebastien Roy case MAC_STAT_OERRORS: 1922b24ab6bSSebastien Roy *val = iptun->iptun_oerrors; 1932b24ab6bSSebastien Roy break; 1942b24ab6bSSebastien Roy case MAC_STAT_RBYTES: 1952b24ab6bSSebastien Roy *val = iptun->iptun_rbytes; 1962b24ab6bSSebastien Roy break; 1972b24ab6bSSebastien Roy case MAC_STAT_IPACKETS: 1982b24ab6bSSebastien Roy *val = iptun->iptun_ipackets; 1992b24ab6bSSebastien Roy break; 2002b24ab6bSSebastien Roy case MAC_STAT_OBYTES: 2012b24ab6bSSebastien Roy *val = iptun->iptun_obytes; 2022b24ab6bSSebastien Roy break; 2032b24ab6bSSebastien Roy case MAC_STAT_OPACKETS: 2042b24ab6bSSebastien Roy *val = iptun->iptun_opackets; 2052b24ab6bSSebastien Roy break; 2062b24ab6bSSebastien Roy case MAC_STAT_NORCVBUF: 2072b24ab6bSSebastien Roy *val = iptun->iptun_norcvbuf; 2082b24ab6bSSebastien Roy break; 2092b24ab6bSSebastien Roy case MAC_STAT_NOXMTBUF: 2102b24ab6bSSebastien Roy *val = iptun->iptun_noxmtbuf; 2112b24ab6bSSebastien Roy break; 2122b24ab6bSSebastien Roy default: 2132b24ab6bSSebastien Roy err = ENOTSUP; 2142b24ab6bSSebastien Roy } 2152b24ab6bSSebastien Roy 2162b24ab6bSSebastien Roy return (err); 2172b24ab6bSSebastien Roy } 2182b24ab6bSSebastien Roy 2192b24ab6bSSebastien Roy static int 2202b24ab6bSSebastien Roy iptun_m_start(void *arg) 2212b24ab6bSSebastien Roy { 2222b24ab6bSSebastien Roy iptun_t *iptun = arg; 2232b24ab6bSSebastien Roy int err; 2242b24ab6bSSebastien Roy 2252b24ab6bSSebastien Roy if ((err = iptun_enter(iptun)) == 0) { 2262b24ab6bSSebastien Roy iptun->iptun_flags |= IPTUN_MAC_STARTED; 2272b24ab6bSSebastien Roy iptun_task_dispatch(iptun, IPTUN_TASK_LINK_UPDATE); 2282b24ab6bSSebastien Roy iptun_exit(iptun); 2292b24ab6bSSebastien Roy } 2302b24ab6bSSebastien Roy return (err); 2312b24ab6bSSebastien Roy } 2322b24ab6bSSebastien Roy 2332b24ab6bSSebastien Roy static void 2342b24ab6bSSebastien Roy iptun_m_stop(void *arg) 2352b24ab6bSSebastien Roy { 2362b24ab6bSSebastien Roy iptun_t *iptun = arg; 2372b24ab6bSSebastien Roy 2382b24ab6bSSebastien Roy if (iptun_enter(iptun) == 0) { 2392b24ab6bSSebastien Roy iptun->iptun_flags &= ~IPTUN_MAC_STARTED; 2402b24ab6bSSebastien Roy iptun_task_dispatch(iptun, IPTUN_TASK_LINK_UPDATE); 2412b24ab6bSSebastien Roy iptun_exit(iptun); 2422b24ab6bSSebastien Roy } 2432b24ab6bSSebastien Roy } 2442b24ab6bSSebastien Roy 2452b24ab6bSSebastien Roy /* 2462b24ab6bSSebastien Roy * iptun_m_setpromisc() does nothing and always succeeds. This is because a 2472b24ab6bSSebastien Roy * tunnel data-link only ever receives packets that are destined exclusively 2482b24ab6bSSebastien Roy * for the local address of the tunnel. 2492b24ab6bSSebastien Roy */ 2502b24ab6bSSebastien Roy /* ARGSUSED */ 2512b24ab6bSSebastien Roy static int 2522b24ab6bSSebastien Roy iptun_m_setpromisc(void *arg, boolean_t on) 2532b24ab6bSSebastien Roy { 2542b24ab6bSSebastien Roy return (0); 2552b24ab6bSSebastien Roy } 2562b24ab6bSSebastien Roy 2572b24ab6bSSebastien Roy /* ARGSUSED */ 2582b24ab6bSSebastien Roy static int 2592b24ab6bSSebastien Roy iptun_m_multicst(void *arg, boolean_t add, const uint8_t *addrp) 2602b24ab6bSSebastien Roy { 2612b24ab6bSSebastien Roy return (ENOTSUP); 2622b24ab6bSSebastien Roy } 2632b24ab6bSSebastien Roy 2642b24ab6bSSebastien Roy /* 2652b24ab6bSSebastien Roy * iptun_m_unicst() sets the local address. 2662b24ab6bSSebastien Roy */ 2672b24ab6bSSebastien Roy /* ARGSUSED */ 2682b24ab6bSSebastien Roy static int 2692b24ab6bSSebastien Roy iptun_m_unicst(void *arg, const uint8_t *addrp) 2702b24ab6bSSebastien Roy { 2712b24ab6bSSebastien Roy iptun_t *iptun = arg; 2722b24ab6bSSebastien Roy int err; 2732b24ab6bSSebastien Roy struct sockaddr_storage ss; 2742b24ab6bSSebastien Roy struct sockaddr_in *sin; 2752b24ab6bSSebastien Roy struct sockaddr_in6 *sin6; 2762b24ab6bSSebastien Roy 2772b24ab6bSSebastien Roy if ((err = iptun_enter(iptun)) == 0) { 2782b24ab6bSSebastien Roy switch (iptun->iptun_typeinfo->iti_ipvers) { 2792b24ab6bSSebastien Roy case IPV4_VERSION: 2802b24ab6bSSebastien Roy sin = (struct sockaddr_in *)&ss; 2812b24ab6bSSebastien Roy sin->sin_family = AF_INET; 2822b24ab6bSSebastien Roy bcopy(addrp, &sin->sin_addr, sizeof (in_addr_t)); 2832b24ab6bSSebastien Roy break; 2842b24ab6bSSebastien Roy case IPV6_VERSION: 2852b24ab6bSSebastien Roy sin6 = (struct sockaddr_in6 *)&ss; 2862b24ab6bSSebastien Roy sin6->sin6_family = AF_INET6; 2872b24ab6bSSebastien Roy bcopy(addrp, &sin6->sin6_addr, sizeof (in6_addr_t)); 2882b24ab6bSSebastien Roy break; 2892b24ab6bSSebastien Roy default: 2902b24ab6bSSebastien Roy ASSERT(0); 2912b24ab6bSSebastien Roy } 2922b24ab6bSSebastien Roy err = iptun_setladdr(iptun, &ss); 2932b24ab6bSSebastien Roy iptun_exit(iptun); 2942b24ab6bSSebastien Roy } 2952b24ab6bSSebastien Roy return (err); 2962b24ab6bSSebastien Roy } 2972b24ab6bSSebastien Roy 2982b24ab6bSSebastien Roy static mblk_t * 2992b24ab6bSSebastien Roy iptun_m_tx(void *arg, mblk_t *mpchain) 3002b24ab6bSSebastien Roy { 3012b24ab6bSSebastien Roy mblk_t *mp, *nmp; 3022b24ab6bSSebastien Roy iptun_t *iptun = arg; 3032b24ab6bSSebastien Roy 3042b24ab6bSSebastien Roy if (!IS_IPTUN_RUNNING(iptun)) { 3052b24ab6bSSebastien Roy iptun_drop_pkt(mpchain, &iptun->iptun_noxmtbuf); 3062b24ab6bSSebastien Roy return (NULL); 3072b24ab6bSSebastien Roy } 3082b24ab6bSSebastien Roy 3092b24ab6bSSebastien Roy for (mp = mpchain; mp != NULL; mp = nmp) { 3102b24ab6bSSebastien Roy nmp = mp->b_next; 3112b24ab6bSSebastien Roy mp->b_next = NULL; 3122b24ab6bSSebastien Roy iptun_output(iptun, mp); 3132b24ab6bSSebastien Roy } 3142b24ab6bSSebastien Roy 3152b24ab6bSSebastien Roy return (NULL); 3162b24ab6bSSebastien Roy } 3172b24ab6bSSebastien Roy 3182b24ab6bSSebastien Roy /* ARGSUSED */ 3192b24ab6bSSebastien Roy static int 3202b24ab6bSSebastien Roy iptun_m_setprop(void *barg, const char *pr_name, mac_prop_id_t pr_num, 3212b24ab6bSSebastien Roy uint_t pr_valsize, const void *pr_val) 3222b24ab6bSSebastien Roy { 3232b24ab6bSSebastien Roy iptun_t *iptun = barg; 3242b24ab6bSSebastien Roy uint32_t value = *(uint32_t *)pr_val; 3252b24ab6bSSebastien Roy int err; 3262b24ab6bSSebastien Roy 3272b24ab6bSSebastien Roy /* 3282b24ab6bSSebastien Roy * We need to enter this iptun_t since we'll be modifying the outer 3292b24ab6bSSebastien Roy * header. 3302b24ab6bSSebastien Roy */ 3312b24ab6bSSebastien Roy if ((err = iptun_enter(iptun)) != 0) 3322b24ab6bSSebastien Roy return (err); 3332b24ab6bSSebastien Roy 3342b24ab6bSSebastien Roy switch (pr_num) { 3352b24ab6bSSebastien Roy case MAC_PROP_IPTUN_HOPLIMIT: 3362b24ab6bSSebastien Roy if (value < IPTUN_MIN_HOPLIMIT || value > IPTUN_MAX_HOPLIMIT) { 3372b24ab6bSSebastien Roy err = EINVAL; 3382b24ab6bSSebastien Roy break; 3392b24ab6bSSebastien Roy } 3402b24ab6bSSebastien Roy if (value != iptun->iptun_hoplimit) { 3412b24ab6bSSebastien Roy iptun->iptun_hoplimit = (uint8_t)value; 3422b24ab6bSSebastien Roy iptun_headergen(iptun, B_TRUE); 3432b24ab6bSSebastien Roy } 3442b24ab6bSSebastien Roy break; 3452b24ab6bSSebastien Roy case MAC_PROP_IPTUN_ENCAPLIMIT: 3462b24ab6bSSebastien Roy if (iptun->iptun_typeinfo->iti_type != IPTUN_TYPE_IPV6 || 3472b24ab6bSSebastien Roy value > IPTUN_MAX_ENCAPLIMIT) { 3482b24ab6bSSebastien Roy err = EINVAL; 3492b24ab6bSSebastien Roy break; 3502b24ab6bSSebastien Roy } 3512b24ab6bSSebastien Roy if (value != iptun->iptun_encaplimit) { 3522b24ab6bSSebastien Roy iptun->iptun_encaplimit = (uint8_t)value; 3532b24ab6bSSebastien Roy iptun_headergen(iptun, B_TRUE); 3542b24ab6bSSebastien Roy } 3552b24ab6bSSebastien Roy break; 3562b24ab6bSSebastien Roy case MAC_PROP_MTU: { 357bd670b35SErik Nordmark uint32_t maxmtu = iptun_get_maxmtu(iptun, NULL, 0); 3582b24ab6bSSebastien Roy 3592b24ab6bSSebastien Roy if (value < iptun->iptun_typeinfo->iti_minmtu || 3602b24ab6bSSebastien Roy value > maxmtu) { 3612b24ab6bSSebastien Roy err = EINVAL; 3622b24ab6bSSebastien Roy break; 3632b24ab6bSSebastien Roy } 3642b24ab6bSSebastien Roy iptun->iptun_flags |= IPTUN_FIXED_MTU; 3652b24ab6bSSebastien Roy if (value != iptun->iptun_mtu) { 3662b24ab6bSSebastien Roy iptun->iptun_mtu = value; 3672b24ab6bSSebastien Roy iptun_task_dispatch(iptun, IPTUN_TASK_MTU_UPDATE); 3682b24ab6bSSebastien Roy } 3692b24ab6bSSebastien Roy break; 3702b24ab6bSSebastien Roy } 3712b24ab6bSSebastien Roy default: 3722b24ab6bSSebastien Roy err = EINVAL; 3732b24ab6bSSebastien Roy } 3742b24ab6bSSebastien Roy iptun_exit(iptun); 3752b24ab6bSSebastien Roy return (err); 3762b24ab6bSSebastien Roy } 3772b24ab6bSSebastien Roy 3782b24ab6bSSebastien Roy /* ARGSUSED */ 3792b24ab6bSSebastien Roy static int 3802b24ab6bSSebastien Roy iptun_m_getprop(void *barg, const char *pr_name, mac_prop_id_t pr_num, 3810dc2366fSVenugopal Iyer uint_t pr_valsize, void *pr_val) 3822b24ab6bSSebastien Roy { 3832b24ab6bSSebastien Roy iptun_t *iptun = barg; 3842b24ab6bSSebastien Roy int err; 3852b24ab6bSSebastien Roy 3862b24ab6bSSebastien Roy if ((err = iptun_enter(iptun)) != 0) 3872b24ab6bSSebastien Roy return (err); 3882b24ab6bSSebastien Roy 3892b24ab6bSSebastien Roy switch (pr_num) { 3902b24ab6bSSebastien Roy case MAC_PROP_IPTUN_HOPLIMIT: 3910dc2366fSVenugopal Iyer ASSERT(pr_valsize >= sizeof (uint32_t)); 3922b24ab6bSSebastien Roy *(uint32_t *)pr_val = iptun->iptun_hoplimit; 3932b24ab6bSSebastien Roy break; 3942b24ab6bSSebastien Roy 3950dc2366fSVenugopal Iyer case MAC_PROP_IPTUN_ENCAPLIMIT: 3960dc2366fSVenugopal Iyer *(uint32_t *)pr_val = iptun->iptun_encaplimit; 3972b24ab6bSSebastien Roy break; 3982b24ab6bSSebastien Roy default: 3990dc2366fSVenugopal Iyer err = ENOTSUP; 4002b24ab6bSSebastien Roy } 4012b24ab6bSSebastien Roy done: 4022b24ab6bSSebastien Roy iptun_exit(iptun); 4032b24ab6bSSebastien Roy return (err); 4042b24ab6bSSebastien Roy } 4052b24ab6bSSebastien Roy 4060dc2366fSVenugopal Iyer /* ARGSUSED */ 4070dc2366fSVenugopal Iyer static void 4080dc2366fSVenugopal Iyer iptun_m_propinfo(void *barg, const char *pr_name, mac_prop_id_t pr_num, 4090dc2366fSVenugopal Iyer mac_prop_info_handle_t prh) 4100dc2366fSVenugopal Iyer { 4110dc2366fSVenugopal Iyer iptun_t *iptun = barg; 4120dc2366fSVenugopal Iyer 4130dc2366fSVenugopal Iyer switch (pr_num) { 4140dc2366fSVenugopal Iyer case MAC_PROP_IPTUN_HOPLIMIT: 4150dc2366fSVenugopal Iyer mac_prop_info_set_range_uint32(prh, 4160dc2366fSVenugopal Iyer IPTUN_MIN_HOPLIMIT, IPTUN_MAX_HOPLIMIT); 4170dc2366fSVenugopal Iyer mac_prop_info_set_default_uint32(prh, IPTUN_DEFAULT_HOPLIMIT); 4180dc2366fSVenugopal Iyer break; 4190dc2366fSVenugopal Iyer 4200dc2366fSVenugopal Iyer case MAC_PROP_IPTUN_ENCAPLIMIT: 4210dc2366fSVenugopal Iyer if (iptun->iptun_typeinfo->iti_type != IPTUN_TYPE_IPV6) 4220dc2366fSVenugopal Iyer break; 4230dc2366fSVenugopal Iyer mac_prop_info_set_range_uint32(prh, 4240dc2366fSVenugopal Iyer IPTUN_MIN_ENCAPLIMIT, IPTUN_MAX_ENCAPLIMIT); 4250dc2366fSVenugopal Iyer mac_prop_info_set_default_uint32(prh, IPTUN_DEFAULT_ENCAPLIMIT); 4260dc2366fSVenugopal Iyer break; 4270dc2366fSVenugopal Iyer case MAC_PROP_MTU: 4280dc2366fSVenugopal Iyer mac_prop_info_set_range_uint32(prh, 4290dc2366fSVenugopal Iyer iptun->iptun_typeinfo->iti_minmtu, 4300dc2366fSVenugopal Iyer iptun_get_maxmtu(iptun, NULL, 0)); 4310dc2366fSVenugopal Iyer break; 4320dc2366fSVenugopal Iyer } 4330dc2366fSVenugopal Iyer } 4340dc2366fSVenugopal Iyer 4352b24ab6bSSebastien Roy uint_t 4362b24ab6bSSebastien Roy iptun_count(void) 4372b24ab6bSSebastien Roy { 4382b24ab6bSSebastien Roy return (iptun_tunnelcount); 4392b24ab6bSSebastien Roy } 4402b24ab6bSSebastien Roy 4412b24ab6bSSebastien Roy /* 4422b24ab6bSSebastien Roy * Enter an iptun_t exclusively. This is essentially just a mutex, but we 4432b24ab6bSSebastien Roy * don't allow iptun_enter() to succeed on a tunnel if it's in the process of 4442b24ab6bSSebastien Roy * being deleted. 4452b24ab6bSSebastien Roy */ 4462b24ab6bSSebastien Roy static int 4472b24ab6bSSebastien Roy iptun_enter(iptun_t *iptun) 4482b24ab6bSSebastien Roy { 4492b24ab6bSSebastien Roy mutex_enter(&iptun->iptun_lock); 4502b24ab6bSSebastien Roy while (iptun->iptun_flags & IPTUN_DELETE_PENDING) 4512b24ab6bSSebastien Roy cv_wait(&iptun->iptun_enter_cv, &iptun->iptun_lock); 4522b24ab6bSSebastien Roy if (iptun->iptun_flags & IPTUN_CONDEMNED) { 4532b24ab6bSSebastien Roy mutex_exit(&iptun->iptun_lock); 4542b24ab6bSSebastien Roy return (ENOENT); 4552b24ab6bSSebastien Roy } 4562b24ab6bSSebastien Roy return (0); 4572b24ab6bSSebastien Roy } 4582b24ab6bSSebastien Roy 4592b24ab6bSSebastien Roy /* 4602b24ab6bSSebastien Roy * Exit the tunnel entered in iptun_enter(). 4612b24ab6bSSebastien Roy */ 4622b24ab6bSSebastien Roy static void 4632b24ab6bSSebastien Roy iptun_exit(iptun_t *iptun) 4642b24ab6bSSebastien Roy { 4652b24ab6bSSebastien Roy mutex_exit(&iptun->iptun_lock); 4662b24ab6bSSebastien Roy } 4672b24ab6bSSebastien Roy 4682b24ab6bSSebastien Roy /* 4692b24ab6bSSebastien Roy * Enter the IP tunnel instance by datalink ID. 4702b24ab6bSSebastien Roy */ 4712b24ab6bSSebastien Roy static int 4722b24ab6bSSebastien Roy iptun_enter_by_linkid(datalink_id_t linkid, iptun_t **iptun) 4732b24ab6bSSebastien Roy { 4742b24ab6bSSebastien Roy int err; 4752b24ab6bSSebastien Roy 4762b24ab6bSSebastien Roy mutex_enter(&iptun_hash_lock); 4772b24ab6bSSebastien Roy if (mod_hash_find(iptun_hash, IPTUN_HASH_KEY(linkid), 4782b24ab6bSSebastien Roy (mod_hash_val_t *)iptun) == 0) 4792b24ab6bSSebastien Roy err = iptun_enter(*iptun); 4802b24ab6bSSebastien Roy else 4812b24ab6bSSebastien Roy err = ENOENT; 4822b24ab6bSSebastien Roy if (err != 0) 4832b24ab6bSSebastien Roy *iptun = NULL; 4842b24ab6bSSebastien Roy mutex_exit(&iptun_hash_lock); 4852b24ab6bSSebastien Roy return (err); 4862b24ab6bSSebastien Roy } 4872b24ab6bSSebastien Roy 4882b24ab6bSSebastien Roy /* 489bd670b35SErik Nordmark * Handle tasks that were deferred through the iptun_taskq because they require 490bd670b35SErik Nordmark * calling up to the mac module, and we can't call up to the mac module while 491bd670b35SErik Nordmark * holding locks. 4922b24ab6bSSebastien Roy * 493bd670b35SErik Nordmark * This is tricky to get right without introducing race conditions and 4942b24ab6bSSebastien Roy * deadlocks with the mac module, as we cannot issue an upcall while in the 4952b24ab6bSSebastien Roy * iptun_t. The reason is that upcalls may try and enter the mac perimeter, 4962b24ab6bSSebastien Roy * while iptun callbacks (such as iptun_m_setprop()) called from the mac 4972b24ab6bSSebastien Roy * module will already have the perimeter held, and will then try and enter 4982b24ab6bSSebastien Roy * the iptun_t. You can see the lock ordering problem with this; this will 4992b24ab6bSSebastien Roy * deadlock. 5002b24ab6bSSebastien Roy * 5012b24ab6bSSebastien Roy * The safe way to do this is to enter the iptun_t in question and copy the 5022b24ab6bSSebastien Roy * information we need out of it so that we can exit it and know that the 5032b24ab6bSSebastien Roy * information being passed up to the upcalls won't be subject to modification 5042b24ab6bSSebastien Roy * by other threads. The problem now is that we need to exit it prior to 5052b24ab6bSSebastien Roy * issuing the upcall, but once we do this, a thread could come along and 5062b24ab6bSSebastien Roy * delete the iptun_t and thus the mac handle required to issue the upcall. 5072b24ab6bSSebastien Roy * To prevent this, we set the IPTUN_UPCALL_PENDING flag prior to exiting the 5082b24ab6bSSebastien Roy * iptun_t. This flag is the condition associated with iptun_upcall_cv, which 5092b24ab6bSSebastien Roy * iptun_delete() will cv_wait() on. When the upcall completes, we clear 5102b24ab6bSSebastien Roy * IPTUN_UPCALL_PENDING and cv_signal() any potentially waiting 5112b24ab6bSSebastien Roy * iptun_delete(). We can thus still safely use iptun->iptun_mh after having 5122b24ab6bSSebastien Roy * exited the iptun_t. 5132b24ab6bSSebastien Roy */ 5142b24ab6bSSebastien Roy static void 5152b24ab6bSSebastien Roy iptun_task_cb(void *arg) 5162b24ab6bSSebastien Roy { 5172b24ab6bSSebastien Roy iptun_task_data_t *itd = arg; 5182b24ab6bSSebastien Roy iptun_task_t task = itd->itd_task; 5192b24ab6bSSebastien Roy datalink_id_t linkid = itd->itd_linkid; 5202b24ab6bSSebastien Roy iptun_t *iptun; 5212b24ab6bSSebastien Roy uint32_t mtu; 5222b24ab6bSSebastien Roy iptun_addr_t addr; 5232b24ab6bSSebastien Roy link_state_t linkstate; 5242b24ab6bSSebastien Roy size_t header_size; 5252b24ab6bSSebastien Roy iptun_header_t header; 5262b24ab6bSSebastien Roy 5272b24ab6bSSebastien Roy kmem_free(itd, sizeof (*itd)); 5282b24ab6bSSebastien Roy 5292b24ab6bSSebastien Roy /* 5302b24ab6bSSebastien Roy * Note that if the lookup fails, it's because the tunnel was deleted 5312b24ab6bSSebastien Roy * between the time the task was dispatched and now. That isn't an 5322b24ab6bSSebastien Roy * error. 5332b24ab6bSSebastien Roy */ 5342b24ab6bSSebastien Roy if (iptun_enter_by_linkid(linkid, &iptun) != 0) 5352b24ab6bSSebastien Roy return; 5362b24ab6bSSebastien Roy 5372b24ab6bSSebastien Roy iptun->iptun_flags |= IPTUN_UPCALL_PENDING; 5382b24ab6bSSebastien Roy 5392b24ab6bSSebastien Roy switch (task) { 5402b24ab6bSSebastien Roy case IPTUN_TASK_MTU_UPDATE: 5412b24ab6bSSebastien Roy mtu = iptun->iptun_mtu; 5422b24ab6bSSebastien Roy break; 5432b24ab6bSSebastien Roy case IPTUN_TASK_LADDR_UPDATE: 5442b24ab6bSSebastien Roy addr = iptun->iptun_laddr; 5452b24ab6bSSebastien Roy break; 5462b24ab6bSSebastien Roy case IPTUN_TASK_RADDR_UPDATE: 5472b24ab6bSSebastien Roy addr = iptun->iptun_raddr; 5482b24ab6bSSebastien Roy break; 5492b24ab6bSSebastien Roy case IPTUN_TASK_LINK_UPDATE: 5502b24ab6bSSebastien Roy linkstate = IS_IPTUN_RUNNING(iptun) ? 5512b24ab6bSSebastien Roy LINK_STATE_UP : LINK_STATE_DOWN; 5522b24ab6bSSebastien Roy break; 5532b24ab6bSSebastien Roy case IPTUN_TASK_PDATA_UPDATE: 5542b24ab6bSSebastien Roy header_size = iptun->iptun_header_size; 5552b24ab6bSSebastien Roy header = iptun->iptun_header; 5562b24ab6bSSebastien Roy break; 5572b24ab6bSSebastien Roy default: 5582b24ab6bSSebastien Roy ASSERT(0); 5592b24ab6bSSebastien Roy } 5602b24ab6bSSebastien Roy 5612b24ab6bSSebastien Roy iptun_exit(iptun); 5622b24ab6bSSebastien Roy 5632b24ab6bSSebastien Roy switch (task) { 5642b24ab6bSSebastien Roy case IPTUN_TASK_MTU_UPDATE: 5652b24ab6bSSebastien Roy (void) mac_maxsdu_update(iptun->iptun_mh, mtu); 5662b24ab6bSSebastien Roy break; 5672b24ab6bSSebastien Roy case IPTUN_TASK_LADDR_UPDATE: 5682b24ab6bSSebastien Roy mac_unicst_update(iptun->iptun_mh, (uint8_t *)&addr.ia_addr); 5692b24ab6bSSebastien Roy break; 5702b24ab6bSSebastien Roy case IPTUN_TASK_RADDR_UPDATE: 5712b24ab6bSSebastien Roy mac_dst_update(iptun->iptun_mh, (uint8_t *)&addr.ia_addr); 5722b24ab6bSSebastien Roy break; 5732b24ab6bSSebastien Roy case IPTUN_TASK_LINK_UPDATE: 5742b24ab6bSSebastien Roy mac_link_update(iptun->iptun_mh, linkstate); 5752b24ab6bSSebastien Roy break; 5762b24ab6bSSebastien Roy case IPTUN_TASK_PDATA_UPDATE: 5772b24ab6bSSebastien Roy if (mac_pdata_update(iptun->iptun_mh, 5782b24ab6bSSebastien Roy header_size == 0 ? NULL : &header, header_size) != 0) 5792b24ab6bSSebastien Roy atomic_inc_64(&iptun->iptun_taskq_fail); 5802b24ab6bSSebastien Roy break; 5812b24ab6bSSebastien Roy } 5822b24ab6bSSebastien Roy 5832b24ab6bSSebastien Roy mutex_enter(&iptun->iptun_lock); 5842b24ab6bSSebastien Roy iptun->iptun_flags &= ~IPTUN_UPCALL_PENDING; 5852b24ab6bSSebastien Roy cv_signal(&iptun->iptun_upcall_cv); 5862b24ab6bSSebastien Roy mutex_exit(&iptun->iptun_lock); 5872b24ab6bSSebastien Roy } 5882b24ab6bSSebastien Roy 5892b24ab6bSSebastien Roy static void 5902b24ab6bSSebastien Roy iptun_task_dispatch(iptun_t *iptun, iptun_task_t iptun_task) 5912b24ab6bSSebastien Roy { 5922b24ab6bSSebastien Roy iptun_task_data_t *itd; 5932b24ab6bSSebastien Roy 5942b24ab6bSSebastien Roy itd = kmem_alloc(sizeof (*itd), KM_NOSLEEP); 5952b24ab6bSSebastien Roy if (itd == NULL) { 5962b24ab6bSSebastien Roy atomic_inc_64(&iptun->iptun_taskq_fail); 5972b24ab6bSSebastien Roy return; 5982b24ab6bSSebastien Roy } 5992b24ab6bSSebastien Roy itd->itd_task = iptun_task; 6002b24ab6bSSebastien Roy itd->itd_linkid = iptun->iptun_linkid; 6012b24ab6bSSebastien Roy if (ddi_taskq_dispatch(iptun_taskq, iptun_task_cb, itd, DDI_NOSLEEP)) { 6022b24ab6bSSebastien Roy atomic_inc_64(&iptun->iptun_taskq_fail); 6032b24ab6bSSebastien Roy kmem_free(itd, sizeof (*itd)); 6042b24ab6bSSebastien Roy } 6052b24ab6bSSebastien Roy } 6062b24ab6bSSebastien Roy 6072b24ab6bSSebastien Roy /* 6082b24ab6bSSebastien Roy * Convert an iptun_addr_t to sockaddr_storage. 6092b24ab6bSSebastien Roy */ 6102b24ab6bSSebastien Roy static void 6112b24ab6bSSebastien Roy iptun_getaddr(iptun_addr_t *iptun_addr, struct sockaddr_storage *ss) 6122b24ab6bSSebastien Roy { 6132b24ab6bSSebastien Roy struct sockaddr_in *sin; 6142b24ab6bSSebastien Roy struct sockaddr_in6 *sin6; 6152b24ab6bSSebastien Roy 6162b24ab6bSSebastien Roy bzero(ss, sizeof (*ss)); 6172b24ab6bSSebastien Roy switch (iptun_addr->ia_family) { 6182b24ab6bSSebastien Roy case AF_INET: 6192b24ab6bSSebastien Roy sin = (struct sockaddr_in *)ss; 6202b24ab6bSSebastien Roy sin->sin_addr.s_addr = iptun_addr->ia_addr.iau_addr4; 6212b24ab6bSSebastien Roy break; 6222b24ab6bSSebastien Roy case AF_INET6: 6232b24ab6bSSebastien Roy sin6 = (struct sockaddr_in6 *)ss; 6242b24ab6bSSebastien Roy sin6->sin6_addr = iptun_addr->ia_addr.iau_addr6; 6252b24ab6bSSebastien Roy break; 6262b24ab6bSSebastien Roy default: 6272b24ab6bSSebastien Roy ASSERT(0); 6282b24ab6bSSebastien Roy } 6292b24ab6bSSebastien Roy ss->ss_family = iptun_addr->ia_family; 6302b24ab6bSSebastien Roy } 6312b24ab6bSSebastien Roy 6322b24ab6bSSebastien Roy /* 6332b24ab6bSSebastien Roy * General purpose function to set an IP tunnel source or destination address. 6342b24ab6bSSebastien Roy */ 6352b24ab6bSSebastien Roy static int 6362b24ab6bSSebastien Roy iptun_setaddr(iptun_type_t iptun_type, iptun_addr_t *iptun_addr, 6372b24ab6bSSebastien Roy const struct sockaddr_storage *ss) 6382b24ab6bSSebastien Roy { 6392b24ab6bSSebastien Roy if (!IPTUN_ADDR_MATCH(iptun_type, ss->ss_family)) 6402b24ab6bSSebastien Roy return (EINVAL); 6412b24ab6bSSebastien Roy 6422b24ab6bSSebastien Roy switch (ss->ss_family) { 6432b24ab6bSSebastien Roy case AF_INET: { 6442b24ab6bSSebastien Roy struct sockaddr_in *sin = (struct sockaddr_in *)ss; 6452b24ab6bSSebastien Roy 6462b24ab6bSSebastien Roy if ((sin->sin_addr.s_addr == INADDR_ANY) || 6472b24ab6bSSebastien Roy (sin->sin_addr.s_addr == INADDR_BROADCAST) || 6482b24ab6bSSebastien Roy CLASSD(sin->sin_addr.s_addr)) { 6492b24ab6bSSebastien Roy return (EADDRNOTAVAIL); 6502b24ab6bSSebastien Roy } 6512b24ab6bSSebastien Roy iptun_addr->ia_addr.iau_addr4 = sin->sin_addr.s_addr; 6522b24ab6bSSebastien Roy break; 6532b24ab6bSSebastien Roy } 6542b24ab6bSSebastien Roy case AF_INET6: { 6552b24ab6bSSebastien Roy struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ss; 6562b24ab6bSSebastien Roy 6572b24ab6bSSebastien Roy if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) || 6582b24ab6bSSebastien Roy IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr) || 6592b24ab6bSSebastien Roy IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { 6602b24ab6bSSebastien Roy return (EADDRNOTAVAIL); 6612b24ab6bSSebastien Roy } 6622b24ab6bSSebastien Roy iptun_addr->ia_addr.iau_addr6 = sin6->sin6_addr; 6632b24ab6bSSebastien Roy break; 6642b24ab6bSSebastien Roy } 6652b24ab6bSSebastien Roy default: 6662b24ab6bSSebastien Roy return (EAFNOSUPPORT); 6672b24ab6bSSebastien Roy } 6682b24ab6bSSebastien Roy iptun_addr->ia_family = ss->ss_family; 6692b24ab6bSSebastien Roy return (0); 6702b24ab6bSSebastien Roy } 6712b24ab6bSSebastien Roy 6722b24ab6bSSebastien Roy static int 6732b24ab6bSSebastien Roy iptun_setladdr(iptun_t *iptun, const struct sockaddr_storage *laddr) 6742b24ab6bSSebastien Roy { 6752b24ab6bSSebastien Roy return (iptun_setaddr(iptun->iptun_typeinfo->iti_type, 6762b24ab6bSSebastien Roy &iptun->iptun_laddr, laddr)); 6772b24ab6bSSebastien Roy } 6782b24ab6bSSebastien Roy 6792b24ab6bSSebastien Roy static int 6802b24ab6bSSebastien Roy iptun_setraddr(iptun_t *iptun, const struct sockaddr_storage *raddr) 6812b24ab6bSSebastien Roy { 6822b24ab6bSSebastien Roy if (!(iptun->iptun_typeinfo->iti_hasraddr)) 6832b24ab6bSSebastien Roy return (EINVAL); 6842b24ab6bSSebastien Roy return (iptun_setaddr(iptun->iptun_typeinfo->iti_type, 6852b24ab6bSSebastien Roy &iptun->iptun_raddr, raddr)); 6862b24ab6bSSebastien Roy } 6872b24ab6bSSebastien Roy 6882b24ab6bSSebastien Roy static boolean_t 6892b24ab6bSSebastien Roy iptun_canbind(iptun_t *iptun) 6902b24ab6bSSebastien Roy { 6912b24ab6bSSebastien Roy /* 6922b24ab6bSSebastien Roy * A tunnel may bind when its source address has been set, and if its 6932b24ab6bSSebastien Roy * tunnel type requires one, also its destination address. 6942b24ab6bSSebastien Roy */ 6952b24ab6bSSebastien Roy return ((iptun->iptun_flags & IPTUN_LADDR) && 6962b24ab6bSSebastien Roy ((iptun->iptun_flags & IPTUN_RADDR) || 6972b24ab6bSSebastien Roy !(iptun->iptun_typeinfo->iti_hasraddr))); 6982b24ab6bSSebastien Roy } 6992b24ab6bSSebastien Roy 700bd670b35SErik Nordmark /* 701bd670b35SErik Nordmark * Verify that the local address is valid, and insert in the fanout 702bd670b35SErik Nordmark */ 7032b24ab6bSSebastien Roy static int 7042b24ab6bSSebastien Roy iptun_bind(iptun_t *iptun) 7052b24ab6bSSebastien Roy { 7062b24ab6bSSebastien Roy conn_t *connp = iptun->iptun_connp; 707bd670b35SErik Nordmark int error = 0; 708bd670b35SErik Nordmark ip_xmit_attr_t *ixa; 709*e5e7971fSErik Nordmark ip_xmit_attr_t *oldixa; 710bd670b35SErik Nordmark iulp_t uinfo; 711bd670b35SErik Nordmark ip_stack_t *ipst = connp->conn_netstack->netstack_ip; 712bd670b35SErik Nordmark 713*e5e7971fSErik Nordmark /* 714*e5e7971fSErik Nordmark * Get an exclusive ixa for this thread. 715*e5e7971fSErik Nordmark * We defer updating conn_ixa until later to handle any concurrent 716*e5e7971fSErik Nordmark * conn_ixa_cleanup thread. 717*e5e7971fSErik Nordmark */ 718*e5e7971fSErik Nordmark ixa = conn_get_ixa(connp, B_FALSE); 719bd670b35SErik Nordmark if (ixa == NULL) 720bd670b35SErik Nordmark return (ENOMEM); 721bd670b35SErik Nordmark 722bd670b35SErik Nordmark /* We create PMTU state including for 6to4 */ 723bd670b35SErik Nordmark ixa->ixa_flags |= IXAF_PMTU_DISCOVERY; 7242b24ab6bSSebastien Roy 7252b24ab6bSSebastien Roy ASSERT(iptun_canbind(iptun)); 7262b24ab6bSSebastien Roy 727bd670b35SErik Nordmark mutex_enter(&connp->conn_lock); 728bd670b35SErik Nordmark /* 729bd670b35SErik Nordmark * Note that conn_proto can't be set since the upper protocol 730bd670b35SErik Nordmark * can be both 41 and 4 when IPv6 and IPv4 are over the same tunnel. 731bd670b35SErik Nordmark * ipcl_iptun_classify doesn't use conn_proto. 732bd670b35SErik Nordmark */ 733bd670b35SErik Nordmark connp->conn_ipversion = iptun->iptun_typeinfo->iti_ipvers; 734bd670b35SErik Nordmark 7352b24ab6bSSebastien Roy switch (iptun->iptun_typeinfo->iti_type) { 7362b24ab6bSSebastien Roy case IPTUN_TYPE_IPV4: 737bd670b35SErik Nordmark IN6_IPADDR_TO_V4MAPPED(iptun->iptun_laddr4, 738bd670b35SErik Nordmark &connp->conn_laddr_v6); 739bd670b35SErik Nordmark IN6_IPADDR_TO_V4MAPPED(iptun->iptun_raddr4, 740bd670b35SErik Nordmark &connp->conn_faddr_v6); 741bd670b35SErik Nordmark ixa->ixa_flags |= IXAF_IS_IPV4; 742bd670b35SErik Nordmark if (ip_laddr_verify_v4(iptun->iptun_laddr4, IPCL_ZONEID(connp), 743bd670b35SErik Nordmark ipst, B_FALSE) != IPVL_UNICAST_UP) { 744bd670b35SErik Nordmark mutex_exit(&connp->conn_lock); 745bd670b35SErik Nordmark error = EADDRNOTAVAIL; 746bd670b35SErik Nordmark goto done; 747bd670b35SErik Nordmark } 7482b24ab6bSSebastien Roy break; 7492b24ab6bSSebastien Roy case IPTUN_TYPE_IPV6: 750bd670b35SErik Nordmark connp->conn_laddr_v6 = iptun->iptun_laddr6; 751bd670b35SErik Nordmark connp->conn_faddr_v6 = iptun->iptun_raddr6; 752bd670b35SErik Nordmark ixa->ixa_flags &= ~IXAF_IS_IPV4; 753bd670b35SErik Nordmark /* We use a zero scopeid for now */ 754bd670b35SErik Nordmark if (ip_laddr_verify_v6(&iptun->iptun_laddr6, IPCL_ZONEID(connp), 755bd670b35SErik Nordmark ipst, B_FALSE, 0) != IPVL_UNICAST_UP) { 756bd670b35SErik Nordmark mutex_exit(&connp->conn_lock); 757bd670b35SErik Nordmark error = EADDRNOTAVAIL; 758bd670b35SErik Nordmark goto done; 759bd670b35SErik Nordmark } 7602b24ab6bSSebastien Roy break; 7612b24ab6bSSebastien Roy case IPTUN_TYPE_6TO4: 762bd670b35SErik Nordmark IN6_IPADDR_TO_V4MAPPED(iptun->iptun_laddr4, 763bd670b35SErik Nordmark &connp->conn_laddr_v6); 764bd670b35SErik Nordmark IN6_IPADDR_TO_V4MAPPED(INADDR_ANY, &connp->conn_faddr_v6); 765bd670b35SErik Nordmark ixa->ixa_flags |= IXAF_IS_IPV4; 766bd670b35SErik Nordmark mutex_exit(&connp->conn_lock); 767bd670b35SErik Nordmark 768bd670b35SErik Nordmark switch (ip_laddr_verify_v4(iptun->iptun_laddr4, 769bd670b35SErik Nordmark IPCL_ZONEID(connp), ipst, B_FALSE)) { 770bd670b35SErik Nordmark case IPVL_UNICAST_UP: 771bd670b35SErik Nordmark case IPVL_UNICAST_DOWN: 7722b24ab6bSSebastien Roy break; 773bd670b35SErik Nordmark default: 774bd670b35SErik Nordmark error = EADDRNOTAVAIL; 775bd670b35SErik Nordmark goto done; 776bd670b35SErik Nordmark } 777bd670b35SErik Nordmark goto insert; 7782b24ab6bSSebastien Roy } 7792b24ab6bSSebastien Roy 780bd670b35SErik Nordmark /* In case previous destination was multirt */ 781bd670b35SErik Nordmark ip_attr_newdst(ixa); 7822b24ab6bSSebastien Roy 7832b24ab6bSSebastien Roy /* 784bd670b35SErik Nordmark * When we set a tunnel's destination address, we do not 785bd670b35SErik Nordmark * care if the destination is reachable. Transient routing 786bd670b35SErik Nordmark * issues should not inhibit the creation of a tunnel 787bd670b35SErik Nordmark * interface, for example. Thus we pass B_FALSE here. 7882b24ab6bSSebastien Roy */ 789bd670b35SErik Nordmark connp->conn_saddr_v6 = connp->conn_laddr_v6; 790bd670b35SErik Nordmark mutex_exit(&connp->conn_lock); 791bd670b35SErik Nordmark 792bd670b35SErik Nordmark /* As long as the MTU is large we avoid fragmentation */ 793bd670b35SErik Nordmark ixa->ixa_flags |= IXAF_DONTFRAG | IXAF_PMTU_IPV4_DF; 794bd670b35SErik Nordmark 795bd670b35SErik Nordmark /* We handle IPsec in iptun_output_common */ 796bd670b35SErik Nordmark error = ip_attr_connect(connp, ixa, &connp->conn_saddr_v6, 797bd670b35SErik Nordmark &connp->conn_faddr_v6, &connp->conn_faddr_v6, 0, 798bd670b35SErik Nordmark &connp->conn_saddr_v6, &uinfo, 0); 799bd670b35SErik Nordmark 800bd670b35SErik Nordmark if (error != 0) 801bd670b35SErik Nordmark goto done; 802bd670b35SErik Nordmark 803bd670b35SErik Nordmark /* saddr shouldn't change since it was already set */ 804bd670b35SErik Nordmark ASSERT(IN6_ARE_ADDR_EQUAL(&connp->conn_laddr_v6, 805bd670b35SErik Nordmark &connp->conn_saddr_v6)); 806bd670b35SErik Nordmark 807bd670b35SErik Nordmark /* We set IXAF_VERIFY_PMTU to catch PMTU increases */ 808bd670b35SErik Nordmark ixa->ixa_flags |= IXAF_VERIFY_PMTU; 809bd670b35SErik Nordmark ASSERT(uinfo.iulp_mtu != 0); 810bd670b35SErik Nordmark 811bd670b35SErik Nordmark /* 812bd670b35SErik Nordmark * Allow setting new policies. 813bd670b35SErik Nordmark * The addresses/ports are already set, thus the IPsec policy calls 814bd670b35SErik Nordmark * can handle their passed-in conn's. 815bd670b35SErik Nordmark */ 816bd670b35SErik Nordmark connp->conn_policy_cached = B_FALSE; 817bd670b35SErik Nordmark 818bd670b35SErik Nordmark insert: 819bd670b35SErik Nordmark error = ipcl_conn_insert(connp); 820bd670b35SErik Nordmark if (error != 0) 821bd670b35SErik Nordmark goto done; 822bd670b35SErik Nordmark 823*e5e7971fSErik Nordmark /* Atomically update v6lastdst and conn_ixa */ 824*e5e7971fSErik Nordmark mutex_enter(&connp->conn_lock); 825bd670b35SErik Nordmark /* Record this as the "last" send even though we haven't sent any */ 826bd670b35SErik Nordmark connp->conn_v6lastdst = connp->conn_faddr_v6; 827bd670b35SErik Nordmark 828bd670b35SErik Nordmark iptun->iptun_flags |= IPTUN_BOUND; 829*e5e7971fSErik Nordmark 830*e5e7971fSErik Nordmark oldixa = conn_replace_ixa(connp, ixa); 831*e5e7971fSErik Nordmark /* Done with conn_t */ 832*e5e7971fSErik Nordmark mutex_exit(&connp->conn_lock); 833*e5e7971fSErik Nordmark ixa_refrele(oldixa); 834*e5e7971fSErik Nordmark 835bd670b35SErik Nordmark /* 836bd670b35SErik Nordmark * Now that we're bound with ip below us, this is a good 837bd670b35SErik Nordmark * time to initialize the destination path MTU and to 838bd670b35SErik Nordmark * re-calculate the tunnel's link MTU. 839bd670b35SErik Nordmark */ 840bd670b35SErik Nordmark (void) iptun_update_mtu(iptun, ixa, 0); 8412b24ab6bSSebastien Roy 8422b24ab6bSSebastien Roy if (IS_IPTUN_RUNNING(iptun)) 8432b24ab6bSSebastien Roy iptun_task_dispatch(iptun, IPTUN_TASK_LINK_UPDATE); 844bd670b35SErik Nordmark 845bd670b35SErik Nordmark done: 846bd670b35SErik Nordmark ixa_refrele(ixa); 847bd670b35SErik Nordmark return (error); 8482b24ab6bSSebastien Roy } 8492b24ab6bSSebastien Roy 8502b24ab6bSSebastien Roy static void 8512b24ab6bSSebastien Roy iptun_unbind(iptun_t *iptun) 8522b24ab6bSSebastien Roy { 8532b24ab6bSSebastien Roy ASSERT(iptun->iptun_flags & IPTUN_BOUND); 8542b24ab6bSSebastien Roy ASSERT(mutex_owned(&iptun->iptun_lock) || 8552b24ab6bSSebastien Roy (iptun->iptun_flags & IPTUN_CONDEMNED)); 8562b24ab6bSSebastien Roy ip_unbind(iptun->iptun_connp); 8572b24ab6bSSebastien Roy iptun->iptun_flags &= ~IPTUN_BOUND; 8582b24ab6bSSebastien Roy if (!(iptun->iptun_flags & IPTUN_CONDEMNED)) 8592b24ab6bSSebastien Roy iptun_task_dispatch(iptun, IPTUN_TASK_LINK_UPDATE); 8602b24ab6bSSebastien Roy } 8612b24ab6bSSebastien Roy 8622b24ab6bSSebastien Roy /* 8632b24ab6bSSebastien Roy * Re-generate the template data-link header for a given IP tunnel given the 8642b24ab6bSSebastien Roy * tunnel's current parameters. 8652b24ab6bSSebastien Roy */ 8662b24ab6bSSebastien Roy static void 8672b24ab6bSSebastien Roy iptun_headergen(iptun_t *iptun, boolean_t update_mac) 8682b24ab6bSSebastien Roy { 8692b24ab6bSSebastien Roy switch (iptun->iptun_typeinfo->iti_ipvers) { 8702b24ab6bSSebastien Roy case IPV4_VERSION: 8712b24ab6bSSebastien Roy /* 8722b24ab6bSSebastien Roy * We only need to use a custom IP header if the administrator 8732b24ab6bSSebastien Roy * has supplied a non-default hoplimit. 8742b24ab6bSSebastien Roy */ 8752b24ab6bSSebastien Roy if (iptun->iptun_hoplimit == IPTUN_DEFAULT_HOPLIMIT) { 8762b24ab6bSSebastien Roy iptun->iptun_header_size = 0; 8772b24ab6bSSebastien Roy break; 8782b24ab6bSSebastien Roy } 8792b24ab6bSSebastien Roy iptun->iptun_header_size = sizeof (ipha_t); 8802b24ab6bSSebastien Roy iptun->iptun_header4.ipha_version_and_hdr_length = 8812b24ab6bSSebastien Roy IP_SIMPLE_HDR_VERSION; 8822b24ab6bSSebastien Roy iptun->iptun_header4.ipha_fragment_offset_and_flags = 8832b24ab6bSSebastien Roy htons(IPH_DF); 8842b24ab6bSSebastien Roy iptun->iptun_header4.ipha_ttl = iptun->iptun_hoplimit; 8852b24ab6bSSebastien Roy break; 8862b24ab6bSSebastien Roy case IPV6_VERSION: { 8872b24ab6bSSebastien Roy ip6_t *ip6hp = &iptun->iptun_header6.it6h_ip6h; 8882b24ab6bSSebastien Roy 8892b24ab6bSSebastien Roy /* 8902b24ab6bSSebastien Roy * We only need to use a custom IPv6 header if either the 8912b24ab6bSSebastien Roy * administrator has supplied a non-default hoplimit, or we 8922b24ab6bSSebastien Roy * need to include an encapsulation limit option in the outer 8932b24ab6bSSebastien Roy * header. 8942b24ab6bSSebastien Roy */ 8952b24ab6bSSebastien Roy if (iptun->iptun_hoplimit == IPTUN_DEFAULT_HOPLIMIT && 8962b24ab6bSSebastien Roy iptun->iptun_encaplimit == 0) { 8972b24ab6bSSebastien Roy iptun->iptun_header_size = 0; 8982b24ab6bSSebastien Roy break; 8992b24ab6bSSebastien Roy } 9002b24ab6bSSebastien Roy 9012b24ab6bSSebastien Roy (void) memset(ip6hp, 0, sizeof (*ip6hp)); 9022b24ab6bSSebastien Roy if (iptun->iptun_encaplimit == 0) { 9032b24ab6bSSebastien Roy iptun->iptun_header_size = sizeof (ip6_t); 9042b24ab6bSSebastien Roy ip6hp->ip6_nxt = IPPROTO_NONE; 9052b24ab6bSSebastien Roy } else { 9062b24ab6bSSebastien Roy iptun_encaplim_t *iel; 9072b24ab6bSSebastien Roy 9082b24ab6bSSebastien Roy iptun->iptun_header_size = sizeof (iptun_ipv6hdrs_t); 9092b24ab6bSSebastien Roy /* 9102b24ab6bSSebastien Roy * The mac_ipv6 plugin requires ip6_plen to be in host 9112b24ab6bSSebastien Roy * byte order and reflect the extension headers 9122b24ab6bSSebastien Roy * present in the template. The actual network byte 9132b24ab6bSSebastien Roy * order ip6_plen will be set on a per-packet basis on 9142b24ab6bSSebastien Roy * transmit. 9152b24ab6bSSebastien Roy */ 9162b24ab6bSSebastien Roy ip6hp->ip6_plen = sizeof (*iel); 9172b24ab6bSSebastien Roy ip6hp->ip6_nxt = IPPROTO_DSTOPTS; 9182b24ab6bSSebastien Roy iel = &iptun->iptun_header6.it6h_encaplim; 9192b24ab6bSSebastien Roy *iel = iptun_encaplim_init; 9202b24ab6bSSebastien Roy iel->iel_telopt.ip6ot_encap_limit = 9212b24ab6bSSebastien Roy iptun->iptun_encaplimit; 9222b24ab6bSSebastien Roy } 9232b24ab6bSSebastien Roy 9242b24ab6bSSebastien Roy ip6hp->ip6_hlim = iptun->iptun_hoplimit; 9252b24ab6bSSebastien Roy break; 9262b24ab6bSSebastien Roy } 9272b24ab6bSSebastien Roy } 9282b24ab6bSSebastien Roy 9292b24ab6bSSebastien Roy if (update_mac) 9302b24ab6bSSebastien Roy iptun_task_dispatch(iptun, IPTUN_TASK_PDATA_UPDATE); 9312b24ab6bSSebastien Roy } 9322b24ab6bSSebastien Roy 9332b24ab6bSSebastien Roy /* 9342b24ab6bSSebastien Roy * Insert inbound and outbound IPv4 and IPv6 policy into the given policy 9352b24ab6bSSebastien Roy * head. 9362b24ab6bSSebastien Roy */ 9372b24ab6bSSebastien Roy static boolean_t 9382b24ab6bSSebastien Roy iptun_insert_simple_policies(ipsec_policy_head_t *ph, ipsec_act_t *actp, 9392b24ab6bSSebastien Roy uint_t n, netstack_t *ns) 9402b24ab6bSSebastien Roy { 9412b24ab6bSSebastien Roy int f = IPSEC_AF_V4; 9422b24ab6bSSebastien Roy 9432b24ab6bSSebastien Roy if (!ipsec_polhead_insert(ph, actp, n, f, IPSEC_TYPE_INBOUND, ns) || 9442b24ab6bSSebastien Roy !ipsec_polhead_insert(ph, actp, n, f, IPSEC_TYPE_OUTBOUND, ns)) 9452b24ab6bSSebastien Roy return (B_FALSE); 9462b24ab6bSSebastien Roy 9472b24ab6bSSebastien Roy f = IPSEC_AF_V6; 9482b24ab6bSSebastien Roy return (ipsec_polhead_insert(ph, actp, n, f, IPSEC_TYPE_INBOUND, ns) && 9492b24ab6bSSebastien Roy ipsec_polhead_insert(ph, actp, n, f, IPSEC_TYPE_OUTBOUND, ns)); 9502b24ab6bSSebastien Roy } 9512b24ab6bSSebastien Roy 9522b24ab6bSSebastien Roy /* 9532b24ab6bSSebastien Roy * Used to set IPsec policy when policy is set through the IPTUN_CREATE or 9542b24ab6bSSebastien Roy * IPTUN_MODIFY ioctls. 9552b24ab6bSSebastien Roy */ 9562b24ab6bSSebastien Roy static int 9572b24ab6bSSebastien Roy iptun_set_sec_simple(iptun_t *iptun, const ipsec_req_t *ipsr) 9582b24ab6bSSebastien Roy { 9592b24ab6bSSebastien Roy int rc = 0; 9602b24ab6bSSebastien Roy uint_t nact; 9612b24ab6bSSebastien Roy ipsec_act_t *actp = NULL; 9622b24ab6bSSebastien Roy boolean_t clear_all, old_policy = B_FALSE; 9632b24ab6bSSebastien Roy ipsec_tun_pol_t *itp; 9642b24ab6bSSebastien Roy char name[MAXLINKNAMELEN]; 9652b24ab6bSSebastien Roy uint64_t gen; 9662b24ab6bSSebastien Roy netstack_t *ns = iptun->iptun_ns; 9672b24ab6bSSebastien Roy 9682b24ab6bSSebastien Roy /* Can't specify self-encap on a tunnel. */ 9692b24ab6bSSebastien Roy if (ipsr->ipsr_self_encap_req != 0) 9702b24ab6bSSebastien Roy return (EINVAL); 9712b24ab6bSSebastien Roy 9722b24ab6bSSebastien Roy /* 9732b24ab6bSSebastien Roy * If it's a "clear-all" entry, unset the security flags and resume 9742b24ab6bSSebastien Roy * normal cleartext (or inherit-from-global) policy. 9752b24ab6bSSebastien Roy */ 9762b24ab6bSSebastien Roy clear_all = ((ipsr->ipsr_ah_req & IPTUN_IPSEC_REQ_MASK) == 0 && 9772b24ab6bSSebastien Roy (ipsr->ipsr_esp_req & IPTUN_IPSEC_REQ_MASK) == 0); 9782b24ab6bSSebastien Roy 9792b24ab6bSSebastien Roy ASSERT(mutex_owned(&iptun->iptun_lock)); 9802b24ab6bSSebastien Roy itp = iptun->iptun_itp; 9812b24ab6bSSebastien Roy if (itp == NULL) { 9822b24ab6bSSebastien Roy if (clear_all) 9832b24ab6bSSebastien Roy goto bail; 9842b24ab6bSSebastien Roy if ((rc = dls_mgmt_get_linkinfo(iptun->iptun_linkid, name, NULL, 9852b24ab6bSSebastien Roy NULL, NULL)) != 0) 9862b24ab6bSSebastien Roy goto bail; 9872b24ab6bSSebastien Roy ASSERT(name[0] != '\0'); 9882b24ab6bSSebastien Roy if ((itp = create_tunnel_policy(name, &rc, &gen, ns)) == NULL) 9892b24ab6bSSebastien Roy goto bail; 9902b24ab6bSSebastien Roy iptun->iptun_itp = itp; 9912b24ab6bSSebastien Roy } 9922b24ab6bSSebastien Roy 9932b24ab6bSSebastien Roy /* Allocate the actvec now, before holding itp or polhead locks. */ 9942b24ab6bSSebastien Roy ipsec_actvec_from_req(ipsr, &actp, &nact, ns); 9952b24ab6bSSebastien Roy if (actp == NULL) { 9962b24ab6bSSebastien Roy rc = ENOMEM; 9972b24ab6bSSebastien Roy goto bail; 9982b24ab6bSSebastien Roy } 9992b24ab6bSSebastien Roy 10002b24ab6bSSebastien Roy /* 10012b24ab6bSSebastien Roy * Just write on the active polhead. Save the primary/secondary stuff 10022b24ab6bSSebastien Roy * for spdsock operations. 10032b24ab6bSSebastien Roy * 10042b24ab6bSSebastien Roy * Mutex because we need to write to the polhead AND flags atomically. 10052b24ab6bSSebastien Roy * Other threads will acquire the polhead lock as a reader if the 10062b24ab6bSSebastien Roy * (unprotected) flag is set. 10072b24ab6bSSebastien Roy */ 10082b24ab6bSSebastien Roy mutex_enter(&itp->itp_lock); 10092b24ab6bSSebastien Roy if (itp->itp_flags & ITPF_P_TUNNEL) { 10102b24ab6bSSebastien Roy /* Oops, we lost a race. Let's get out of here. */ 10112b24ab6bSSebastien Roy rc = EBUSY; 10122b24ab6bSSebastien Roy goto mutex_bail; 10132b24ab6bSSebastien Roy } 10142b24ab6bSSebastien Roy old_policy = ((itp->itp_flags & ITPF_P_ACTIVE) != 0); 10152b24ab6bSSebastien Roy 10162b24ab6bSSebastien Roy if (old_policy) { 10172b24ab6bSSebastien Roy ITPF_CLONE(itp->itp_flags); 10182b24ab6bSSebastien Roy rc = ipsec_copy_polhead(itp->itp_policy, itp->itp_inactive, ns); 10192b24ab6bSSebastien Roy if (rc != 0) { 10202b24ab6bSSebastien Roy /* inactive has already been cleared. */ 10212b24ab6bSSebastien Roy itp->itp_flags &= ~ITPF_IFLAGS; 10222b24ab6bSSebastien Roy goto mutex_bail; 10232b24ab6bSSebastien Roy } 10242b24ab6bSSebastien Roy rw_enter(&itp->itp_policy->iph_lock, RW_WRITER); 10252b24ab6bSSebastien Roy ipsec_polhead_flush(itp->itp_policy, ns); 10262b24ab6bSSebastien Roy } else { 10272b24ab6bSSebastien Roy /* Else assume itp->itp_policy is already flushed. */ 10282b24ab6bSSebastien Roy rw_enter(&itp->itp_policy->iph_lock, RW_WRITER); 10292b24ab6bSSebastien Roy } 10302b24ab6bSSebastien Roy 10312b24ab6bSSebastien Roy if (clear_all) { 10322b24ab6bSSebastien Roy ASSERT(avl_numnodes(&itp->itp_policy->iph_rulebyid) == 0); 10332b24ab6bSSebastien Roy itp->itp_flags &= ~ITPF_PFLAGS; 10342b24ab6bSSebastien Roy rw_exit(&itp->itp_policy->iph_lock); 10352b24ab6bSSebastien Roy old_policy = B_FALSE; /* Clear out the inactive one too. */ 10362b24ab6bSSebastien Roy goto recover_bail; 10372b24ab6bSSebastien Roy } 10382b24ab6bSSebastien Roy 10392b24ab6bSSebastien Roy if (iptun_insert_simple_policies(itp->itp_policy, actp, nact, ns)) { 10402b24ab6bSSebastien Roy rw_exit(&itp->itp_policy->iph_lock); 10412b24ab6bSSebastien Roy /* 10422b24ab6bSSebastien Roy * Adjust MTU and make sure the DL side knows what's up. 10432b24ab6bSSebastien Roy */ 10442b24ab6bSSebastien Roy itp->itp_flags = ITPF_P_ACTIVE; 1045bd670b35SErik Nordmark (void) iptun_update_mtu(iptun, NULL, 0); 10462b24ab6bSSebastien Roy old_policy = B_FALSE; /* Blank out inactive - we succeeded */ 10472b24ab6bSSebastien Roy } else { 10482b24ab6bSSebastien Roy rw_exit(&itp->itp_policy->iph_lock); 10492b24ab6bSSebastien Roy rc = ENOMEM; 10502b24ab6bSSebastien Roy } 10512b24ab6bSSebastien Roy 10522b24ab6bSSebastien Roy recover_bail: 10532b24ab6bSSebastien Roy if (old_policy) { 10542b24ab6bSSebastien Roy /* Recover policy in in active polhead. */ 10552b24ab6bSSebastien Roy ipsec_swap_policy(itp->itp_policy, itp->itp_inactive, ns); 10562b24ab6bSSebastien Roy ITPF_SWAP(itp->itp_flags); 10572b24ab6bSSebastien Roy } 10582b24ab6bSSebastien Roy 10592b24ab6bSSebastien Roy /* Clear policy in inactive polhead. */ 10602b24ab6bSSebastien Roy itp->itp_flags &= ~ITPF_IFLAGS; 10612b24ab6bSSebastien Roy rw_enter(&itp->itp_inactive->iph_lock, RW_WRITER); 10622b24ab6bSSebastien Roy ipsec_polhead_flush(itp->itp_inactive, ns); 10632b24ab6bSSebastien Roy rw_exit(&itp->itp_inactive->iph_lock); 10642b24ab6bSSebastien Roy 10652b24ab6bSSebastien Roy mutex_bail: 10662b24ab6bSSebastien Roy mutex_exit(&itp->itp_lock); 10672b24ab6bSSebastien Roy 10682b24ab6bSSebastien Roy bail: 10692b24ab6bSSebastien Roy if (actp != NULL) 10702b24ab6bSSebastien Roy ipsec_actvec_free(actp, nact); 10712b24ab6bSSebastien Roy 10722b24ab6bSSebastien Roy return (rc); 10732b24ab6bSSebastien Roy } 10742b24ab6bSSebastien Roy 10752b24ab6bSSebastien Roy static iptun_typeinfo_t * 10762b24ab6bSSebastien Roy iptun_gettypeinfo(iptun_type_t type) 10772b24ab6bSSebastien Roy { 10782b24ab6bSSebastien Roy int i; 10792b24ab6bSSebastien Roy 10802b24ab6bSSebastien Roy for (i = 0; iptun_type_table[i].iti_type != IPTUN_TYPE_UNKNOWN; i++) { 10812b24ab6bSSebastien Roy if (iptun_type_table[i].iti_type == type) 10822b24ab6bSSebastien Roy break; 10832b24ab6bSSebastien Roy } 10842b24ab6bSSebastien Roy return (&iptun_type_table[i]); 10852b24ab6bSSebastien Roy } 10862b24ab6bSSebastien Roy 10872b24ab6bSSebastien Roy /* 10882b24ab6bSSebastien Roy * Set the parameters included in ik on the tunnel iptun. Parameters that can 10892b24ab6bSSebastien Roy * only be set at creation time are set in iptun_create(). 10902b24ab6bSSebastien Roy */ 10912b24ab6bSSebastien Roy static int 10922b24ab6bSSebastien Roy iptun_setparams(iptun_t *iptun, const iptun_kparams_t *ik) 10932b24ab6bSSebastien Roy { 10942b24ab6bSSebastien Roy int err = 0; 10952b24ab6bSSebastien Roy netstack_t *ns = iptun->iptun_ns; 10962b24ab6bSSebastien Roy iptun_addr_t orig_laddr, orig_raddr; 10972b24ab6bSSebastien Roy uint_t orig_flags = iptun->iptun_flags; 10982b24ab6bSSebastien Roy 10992b24ab6bSSebastien Roy if (ik->iptun_kparam_flags & IPTUN_KPARAM_LADDR) { 11002b24ab6bSSebastien Roy if (orig_flags & IPTUN_LADDR) 11012b24ab6bSSebastien Roy orig_laddr = iptun->iptun_laddr; 11022b24ab6bSSebastien Roy if ((err = iptun_setladdr(iptun, &ik->iptun_kparam_laddr)) != 0) 11032b24ab6bSSebastien Roy return (err); 11042b24ab6bSSebastien Roy iptun->iptun_flags |= IPTUN_LADDR; 11052b24ab6bSSebastien Roy } 11062b24ab6bSSebastien Roy 11072b24ab6bSSebastien Roy if (ik->iptun_kparam_flags & IPTUN_KPARAM_RADDR) { 11082b24ab6bSSebastien Roy if (orig_flags & IPTUN_RADDR) 11092b24ab6bSSebastien Roy orig_raddr = iptun->iptun_raddr; 11102b24ab6bSSebastien Roy if ((err = iptun_setraddr(iptun, &ik->iptun_kparam_raddr)) != 0) 11112b24ab6bSSebastien Roy goto done; 11122b24ab6bSSebastien Roy iptun->iptun_flags |= IPTUN_RADDR; 11132b24ab6bSSebastien Roy } 11142b24ab6bSSebastien Roy 11152b24ab6bSSebastien Roy if (ik->iptun_kparam_flags & IPTUN_KPARAM_SECINFO) { 11162b24ab6bSSebastien Roy /* 11172b24ab6bSSebastien Roy * Set IPsec policy originating from the ifconfig(1M) command 11182b24ab6bSSebastien Roy * line. This is traditionally called "simple" policy because 11192b24ab6bSSebastien Roy * the ipsec_req_t (iptun_kparam_secinfo) can only describe a 11202b24ab6bSSebastien Roy * simple policy of "do ESP on everything" and/or "do AH on 11212b24ab6bSSebastien Roy * everything" (as opposed to the rich policy that can be 11222b24ab6bSSebastien Roy * defined with ipsecconf(1M)). 11232b24ab6bSSebastien Roy */ 11242b24ab6bSSebastien Roy if (iptun->iptun_typeinfo->iti_type == IPTUN_TYPE_6TO4) { 11252b24ab6bSSebastien Roy /* 11262b24ab6bSSebastien Roy * Can't set security properties for automatic 11272b24ab6bSSebastien Roy * tunnels. 11282b24ab6bSSebastien Roy */ 11292b24ab6bSSebastien Roy err = EINVAL; 11302b24ab6bSSebastien Roy goto done; 11312b24ab6bSSebastien Roy } 11322b24ab6bSSebastien Roy 11332b24ab6bSSebastien Roy if (!ipsec_loaded(ns->netstack_ipsec)) { 11342b24ab6bSSebastien Roy /* If IPsec can be loaded, try and load it now. */ 11352b24ab6bSSebastien Roy if (ipsec_failed(ns->netstack_ipsec)) { 11362b24ab6bSSebastien Roy err = EPROTONOSUPPORT; 11372b24ab6bSSebastien Roy goto done; 11382b24ab6bSSebastien Roy } 11392b24ab6bSSebastien Roy ipsec_loader_loadnow(ns->netstack_ipsec); 11402b24ab6bSSebastien Roy /* 11412b24ab6bSSebastien Roy * ipsec_loader_loadnow() returns while IPsec is 11422b24ab6bSSebastien Roy * loaded asynchronously. While a method exists to 11432b24ab6bSSebastien Roy * wait for IPsec to load (ipsec_loader_wait()), it 11442b24ab6bSSebastien Roy * requires use of a STREAMS queue to do a qwait(). 11452b24ab6bSSebastien Roy * We're not in STREAMS context here, and so we can't 11462b24ab6bSSebastien Roy * use it. This is not a problem in practice because 11472b24ab6bSSebastien Roy * in the vast majority of cases, key management and 11482b24ab6bSSebastien Roy * global policy will have loaded before any tunnels 11492b24ab6bSSebastien Roy * are plumbed, and so IPsec will already have been 11502b24ab6bSSebastien Roy * loaded. 11512b24ab6bSSebastien Roy */ 11522b24ab6bSSebastien Roy err = EAGAIN; 11532b24ab6bSSebastien Roy goto done; 11542b24ab6bSSebastien Roy } 11552b24ab6bSSebastien Roy 11562b24ab6bSSebastien Roy err = iptun_set_sec_simple(iptun, &ik->iptun_kparam_secinfo); 11572b24ab6bSSebastien Roy if (err == 0) { 11582b24ab6bSSebastien Roy iptun->iptun_flags |= IPTUN_SIMPLE_POLICY; 11592b24ab6bSSebastien Roy iptun->iptun_simple_policy = ik->iptun_kparam_secinfo; 11602b24ab6bSSebastien Roy } 11612b24ab6bSSebastien Roy } 11622b24ab6bSSebastien Roy done: 11632b24ab6bSSebastien Roy if (err != 0) { 11642b24ab6bSSebastien Roy /* Restore original source and destination. */ 11652b24ab6bSSebastien Roy if (ik->iptun_kparam_flags & IPTUN_KPARAM_LADDR && 11662b24ab6bSSebastien Roy (orig_flags & IPTUN_LADDR)) 11672b24ab6bSSebastien Roy iptun->iptun_laddr = orig_laddr; 11682b24ab6bSSebastien Roy if ((ik->iptun_kparam_flags & IPTUN_KPARAM_RADDR) && 11692b24ab6bSSebastien Roy (orig_flags & IPTUN_RADDR)) 11702b24ab6bSSebastien Roy iptun->iptun_raddr = orig_raddr; 11712b24ab6bSSebastien Roy iptun->iptun_flags = orig_flags; 11722b24ab6bSSebastien Roy } 11732b24ab6bSSebastien Roy return (err); 11742b24ab6bSSebastien Roy } 11752b24ab6bSSebastien Roy 11762b24ab6bSSebastien Roy static int 11772b24ab6bSSebastien Roy iptun_register(iptun_t *iptun) 11782b24ab6bSSebastien Roy { 11792b24ab6bSSebastien Roy mac_register_t *mac; 11802b24ab6bSSebastien Roy int err; 11812b24ab6bSSebastien Roy 11822b24ab6bSSebastien Roy ASSERT(!(iptun->iptun_flags & IPTUN_MAC_REGISTERED)); 11832b24ab6bSSebastien Roy 11842b24ab6bSSebastien Roy if ((mac = mac_alloc(MAC_VERSION)) == NULL) 11852b24ab6bSSebastien Roy return (EINVAL); 11862b24ab6bSSebastien Roy 11872b24ab6bSSebastien Roy mac->m_type_ident = iptun->iptun_typeinfo->iti_ident; 11882b24ab6bSSebastien Roy mac->m_driver = iptun; 11892b24ab6bSSebastien Roy mac->m_dip = iptun_dip; 11902b24ab6bSSebastien Roy mac->m_instance = (uint_t)-1; 11912b24ab6bSSebastien Roy mac->m_src_addr = (uint8_t *)&iptun->iptun_laddr.ia_addr; 11922b24ab6bSSebastien Roy mac->m_dst_addr = iptun->iptun_typeinfo->iti_hasraddr ? 11932b24ab6bSSebastien Roy (uint8_t *)&iptun->iptun_raddr.ia_addr : NULL; 11942b24ab6bSSebastien Roy mac->m_callbacks = &iptun_m_callbacks; 11952b24ab6bSSebastien Roy mac->m_min_sdu = iptun->iptun_typeinfo->iti_minmtu; 11962b24ab6bSSebastien Roy mac->m_max_sdu = iptun->iptun_mtu; 11972b24ab6bSSebastien Roy if (iptun->iptun_header_size != 0) { 11982b24ab6bSSebastien Roy mac->m_pdata = &iptun->iptun_header; 11992b24ab6bSSebastien Roy mac->m_pdata_size = iptun->iptun_header_size; 12002b24ab6bSSebastien Roy } 12012b24ab6bSSebastien Roy if ((err = mac_register(mac, &iptun->iptun_mh)) == 0) 12022b24ab6bSSebastien Roy iptun->iptun_flags |= IPTUN_MAC_REGISTERED; 12032b24ab6bSSebastien Roy mac_free(mac); 12042b24ab6bSSebastien Roy return (err); 12052b24ab6bSSebastien Roy } 12062b24ab6bSSebastien Roy 12072b24ab6bSSebastien Roy static int 12082b24ab6bSSebastien Roy iptun_unregister(iptun_t *iptun) 12092b24ab6bSSebastien Roy { 12102b24ab6bSSebastien Roy int err; 12112b24ab6bSSebastien Roy 12122b24ab6bSSebastien Roy ASSERT(iptun->iptun_flags & IPTUN_MAC_REGISTERED); 12132b24ab6bSSebastien Roy if ((err = mac_unregister(iptun->iptun_mh)) == 0) 12142b24ab6bSSebastien Roy iptun->iptun_flags &= ~IPTUN_MAC_REGISTERED; 12152b24ab6bSSebastien Roy return (err); 12162b24ab6bSSebastien Roy } 12172b24ab6bSSebastien Roy 12182b24ab6bSSebastien Roy static conn_t * 12192b24ab6bSSebastien Roy iptun_conn_create(iptun_t *iptun, netstack_t *ns, cred_t *credp) 12202b24ab6bSSebastien Roy { 12212b24ab6bSSebastien Roy conn_t *connp; 12222b24ab6bSSebastien Roy 12232b24ab6bSSebastien Roy if ((connp = ipcl_conn_create(IPCL_IPCCONN, KM_NOSLEEP, ns)) == NULL) 12242b24ab6bSSebastien Roy return (NULL); 12252b24ab6bSSebastien Roy 12262b24ab6bSSebastien Roy connp->conn_flags |= IPCL_IPTUN; 12272b24ab6bSSebastien Roy connp->conn_iptun = iptun; 12282b24ab6bSSebastien Roy connp->conn_recv = iptun_input; 1229bd670b35SErik Nordmark connp->conn_recvicmp = iptun_input_icmp; 1230bd670b35SErik Nordmark connp->conn_verifyicmp = iptun_verifyicmp; 1231bd670b35SErik Nordmark 1232bd670b35SErik Nordmark /* 1233bd670b35SErik Nordmark * Register iptun_notify to listen to capability changes detected by IP. 1234bd670b35SErik Nordmark * This upcall is made in the context of the call to conn_ip_output. 1235bd670b35SErik Nordmark */ 1236bd670b35SErik Nordmark connp->conn_ixa->ixa_notify = iptun_notify; 1237bd670b35SErik Nordmark connp->conn_ixa->ixa_notify_cookie = iptun; 1238bd670b35SErik Nordmark 12392b24ab6bSSebastien Roy /* 12402b24ab6bSSebastien Roy * For exclusive stacks we set conn_zoneid to GLOBAL_ZONEID as is done 12412b24ab6bSSebastien Roy * for all other conn_t's. 12422b24ab6bSSebastien Roy * 12432b24ab6bSSebastien Roy * Note that there's an important distinction between iptun_zoneid and 12442b24ab6bSSebastien Roy * conn_zoneid. The conn_zoneid is set to GLOBAL_ZONEID in non-global 12452b24ab6bSSebastien Roy * exclusive stack zones to make the ip module believe that the 12462b24ab6bSSebastien Roy * non-global zone is actually a global zone. Therefore, when 12472b24ab6bSSebastien Roy * interacting with the ip module, we must always use conn_zoneid. 12482b24ab6bSSebastien Roy */ 12492b24ab6bSSebastien Roy connp->conn_zoneid = (ns->netstack_stackid == GLOBAL_NETSTACKID) ? 12502b24ab6bSSebastien Roy crgetzoneid(credp) : GLOBAL_ZONEID; 12512b24ab6bSSebastien Roy connp->conn_cred = credp; 12522b24ab6bSSebastien Roy /* crfree() is done in ipcl_conn_destroy(), called by CONN_DEC_REF() */ 12532b24ab6bSSebastien Roy crhold(connp->conn_cred); 1254bd670b35SErik Nordmark connp->conn_cpid = NOPID; 12552b24ab6bSSebastien Roy 1256bd670b35SErik Nordmark /* conn_allzones can not be set this early, hence no IPCL_ZONEID */ 1257bd670b35SErik Nordmark connp->conn_ixa->ixa_zoneid = connp->conn_zoneid; 12582b24ab6bSSebastien Roy ASSERT(connp->conn_ref == 1); 12592b24ab6bSSebastien Roy 1260bd670b35SErik Nordmark /* Cache things in ixa without an extra refhold */ 1261be4c8f74SErik Nordmark ASSERT(!(connp->conn_ixa->ixa_free_flags & IXA_FREE_CRED)); 1262bd670b35SErik Nordmark connp->conn_ixa->ixa_cred = connp->conn_cred; 1263bd670b35SErik Nordmark connp->conn_ixa->ixa_cpid = connp->conn_cpid; 1264bd670b35SErik Nordmark if (is_system_labeled()) 1265bd670b35SErik Nordmark connp->conn_ixa->ixa_tsl = crgetlabel(connp->conn_cred); 1266bd670b35SErik Nordmark 1267bd670b35SErik Nordmark /* 1268bd670b35SErik Nordmark * Have conn_ip_output drop packets should our outer source 1269bd670b35SErik Nordmark * go invalid 1270bd670b35SErik Nordmark */ 1271bd670b35SErik Nordmark connp->conn_ixa->ixa_flags |= IXAF_VERIFY_SOURCE; 1272bd670b35SErik Nordmark 1273bd670b35SErik Nordmark switch (iptun->iptun_typeinfo->iti_ipvers) { 1274bd670b35SErik Nordmark case IPV4_VERSION: 1275bd670b35SErik Nordmark connp->conn_family = AF_INET6; 1276bd670b35SErik Nordmark break; 1277bd670b35SErik Nordmark case IPV6_VERSION: 1278bd670b35SErik Nordmark connp->conn_family = AF_INET; 1279bd670b35SErik Nordmark break; 1280bd670b35SErik Nordmark } 12812b24ab6bSSebastien Roy mutex_enter(&connp->conn_lock); 12822b24ab6bSSebastien Roy connp->conn_state_flags &= ~CONN_INCIPIENT; 12832b24ab6bSSebastien Roy mutex_exit(&connp->conn_lock); 12842b24ab6bSSebastien Roy return (connp); 12852b24ab6bSSebastien Roy } 12862b24ab6bSSebastien Roy 12872b24ab6bSSebastien Roy static void 12882b24ab6bSSebastien Roy iptun_conn_destroy(conn_t *connp) 12892b24ab6bSSebastien Roy { 12902b24ab6bSSebastien Roy ip_quiesce_conn(connp); 12912b24ab6bSSebastien Roy connp->conn_iptun = NULL; 12922b24ab6bSSebastien Roy ASSERT(connp->conn_ref == 1); 12932b24ab6bSSebastien Roy CONN_DEC_REF(connp); 12942b24ab6bSSebastien Roy } 12952b24ab6bSSebastien Roy 12962b24ab6bSSebastien Roy static iptun_t * 12972b24ab6bSSebastien Roy iptun_alloc(void) 12982b24ab6bSSebastien Roy { 12992b24ab6bSSebastien Roy iptun_t *iptun; 13002b24ab6bSSebastien Roy 13012b24ab6bSSebastien Roy if ((iptun = kmem_cache_alloc(iptun_cache, KM_NOSLEEP)) != NULL) { 13022b24ab6bSSebastien Roy bzero(iptun, sizeof (*iptun)); 13032b24ab6bSSebastien Roy atomic_inc_32(&iptun_tunnelcount); 13042b24ab6bSSebastien Roy } 13052b24ab6bSSebastien Roy return (iptun); 13062b24ab6bSSebastien Roy } 13072b24ab6bSSebastien Roy 13082b24ab6bSSebastien Roy static void 13092b24ab6bSSebastien Roy iptun_free(iptun_t *iptun) 13102b24ab6bSSebastien Roy { 13112b24ab6bSSebastien Roy ASSERT(iptun->iptun_flags & IPTUN_CONDEMNED); 13122b24ab6bSSebastien Roy 13132b24ab6bSSebastien Roy if (iptun->iptun_flags & IPTUN_HASH_INSERTED) { 13142b24ab6bSSebastien Roy iptun_stack_t *iptuns = iptun->iptun_iptuns; 13152b24ab6bSSebastien Roy 13162b24ab6bSSebastien Roy mutex_enter(&iptun_hash_lock); 13172b24ab6bSSebastien Roy VERIFY(mod_hash_remove(iptun_hash, 13182b24ab6bSSebastien Roy IPTUN_HASH_KEY(iptun->iptun_linkid), 13192b24ab6bSSebastien Roy (mod_hash_val_t *)&iptun) == 0); 13202b24ab6bSSebastien Roy mutex_exit(&iptun_hash_lock); 13212b24ab6bSSebastien Roy iptun->iptun_flags &= ~IPTUN_HASH_INSERTED; 13222b24ab6bSSebastien Roy mutex_enter(&iptuns->iptuns_lock); 13232b24ab6bSSebastien Roy list_remove(&iptuns->iptuns_iptunlist, iptun); 13242b24ab6bSSebastien Roy mutex_exit(&iptuns->iptuns_lock); 13252b24ab6bSSebastien Roy } 13262b24ab6bSSebastien Roy 13272b24ab6bSSebastien Roy if (iptun->iptun_flags & IPTUN_BOUND) 13282b24ab6bSSebastien Roy iptun_unbind(iptun); 13292b24ab6bSSebastien Roy 13302b24ab6bSSebastien Roy /* 13312b24ab6bSSebastien Roy * After iptun_unregister(), there will be no threads executing a 13322b24ab6bSSebastien Roy * downcall from the mac module, including in the tx datapath. 13332b24ab6bSSebastien Roy */ 13342b24ab6bSSebastien Roy if (iptun->iptun_flags & IPTUN_MAC_REGISTERED) 13352b24ab6bSSebastien Roy VERIFY(iptun_unregister(iptun) == 0); 13362b24ab6bSSebastien Roy 13372b24ab6bSSebastien Roy if (iptun->iptun_itp != NULL) { 13382b24ab6bSSebastien Roy /* 13392b24ab6bSSebastien Roy * Remove from the AVL tree, AND release the reference iptun_t 13402b24ab6bSSebastien Roy * itself holds on the ITP. 13412b24ab6bSSebastien Roy */ 13422b24ab6bSSebastien Roy itp_unlink(iptun->iptun_itp, iptun->iptun_ns); 13432b24ab6bSSebastien Roy ITP_REFRELE(iptun->iptun_itp, iptun->iptun_ns); 13442b24ab6bSSebastien Roy iptun->iptun_itp = NULL; 13452b24ab6bSSebastien Roy iptun->iptun_flags &= ~IPTUN_SIMPLE_POLICY; 13462b24ab6bSSebastien Roy } 13472b24ab6bSSebastien Roy 13482b24ab6bSSebastien Roy /* 13492b24ab6bSSebastien Roy * After ipcl_conn_destroy(), there will be no threads executing an 13502b24ab6bSSebastien Roy * upcall from ip (i.e., iptun_input()), and it is then safe to free 13512b24ab6bSSebastien Roy * the iptun_t. 13522b24ab6bSSebastien Roy */ 13532b24ab6bSSebastien Roy if (iptun->iptun_connp != NULL) { 13542b24ab6bSSebastien Roy iptun_conn_destroy(iptun->iptun_connp); 13552b24ab6bSSebastien Roy iptun->iptun_connp = NULL; 13562b24ab6bSSebastien Roy } 13572b24ab6bSSebastien Roy 13582b24ab6bSSebastien Roy kmem_cache_free(iptun_cache, iptun); 13592b24ab6bSSebastien Roy atomic_dec_32(&iptun_tunnelcount); 13602b24ab6bSSebastien Roy } 13612b24ab6bSSebastien Roy 13622b24ab6bSSebastien Roy int 13632b24ab6bSSebastien Roy iptun_create(iptun_kparams_t *ik, cred_t *credp) 13642b24ab6bSSebastien Roy { 13652b24ab6bSSebastien Roy iptun_t *iptun = NULL; 13662b24ab6bSSebastien Roy int err = 0, mherr; 13672b24ab6bSSebastien Roy char linkname[MAXLINKNAMELEN]; 13682b24ab6bSSebastien Roy ipsec_tun_pol_t *itp; 13692b24ab6bSSebastien Roy netstack_t *ns = NULL; 13702b24ab6bSSebastien Roy iptun_stack_t *iptuns; 13712b24ab6bSSebastien Roy datalink_id_t tmpid; 13722b24ab6bSSebastien Roy zoneid_t zoneid = crgetzoneid(credp); 13732b24ab6bSSebastien Roy boolean_t link_created = B_FALSE; 13742b24ab6bSSebastien Roy 13752b24ab6bSSebastien Roy /* The tunnel type is mandatory */ 13762b24ab6bSSebastien Roy if (!(ik->iptun_kparam_flags & IPTUN_KPARAM_TYPE)) 13772b24ab6bSSebastien Roy return (EINVAL); 13782b24ab6bSSebastien Roy 13792b24ab6bSSebastien Roy /* 13802b24ab6bSSebastien Roy * Is the linkid that the caller wishes to associate with this new 13812b24ab6bSSebastien Roy * tunnel assigned to this zone? 13822b24ab6bSSebastien Roy */ 13832b24ab6bSSebastien Roy if (zone_check_datalink(&zoneid, ik->iptun_kparam_linkid) != 0) { 13842b24ab6bSSebastien Roy if (zoneid != GLOBAL_ZONEID) 13852b24ab6bSSebastien Roy return (EINVAL); 13862b24ab6bSSebastien Roy } else if (zoneid == GLOBAL_ZONEID) { 13872b24ab6bSSebastien Roy return (EINVAL); 13882b24ab6bSSebastien Roy } 13892b24ab6bSSebastien Roy 13902b24ab6bSSebastien Roy /* 13912b24ab6bSSebastien Roy * Make sure that we're not trying to create a tunnel that has already 13922b24ab6bSSebastien Roy * been created. 13932b24ab6bSSebastien Roy */ 13942b24ab6bSSebastien Roy if (iptun_enter_by_linkid(ik->iptun_kparam_linkid, &iptun) == 0) { 13952b24ab6bSSebastien Roy iptun_exit(iptun); 13962b24ab6bSSebastien Roy iptun = NULL; 13972b24ab6bSSebastien Roy err = EEXIST; 13982b24ab6bSSebastien Roy goto done; 13992b24ab6bSSebastien Roy } 14002b24ab6bSSebastien Roy 14012b24ab6bSSebastien Roy ns = netstack_find_by_cred(credp); 14022b24ab6bSSebastien Roy iptuns = ns->netstack_iptun; 14032b24ab6bSSebastien Roy 14042b24ab6bSSebastien Roy if ((iptun = iptun_alloc()) == NULL) { 14052b24ab6bSSebastien Roy err = ENOMEM; 14062b24ab6bSSebastien Roy goto done; 14072b24ab6bSSebastien Roy } 14082b24ab6bSSebastien Roy 14092b24ab6bSSebastien Roy iptun->iptun_linkid = ik->iptun_kparam_linkid; 14102b24ab6bSSebastien Roy iptun->iptun_zoneid = zoneid; 14112b24ab6bSSebastien Roy iptun->iptun_ns = ns; 14122b24ab6bSSebastien Roy 14132b24ab6bSSebastien Roy iptun->iptun_typeinfo = iptun_gettypeinfo(ik->iptun_kparam_type); 14142b24ab6bSSebastien Roy if (iptun->iptun_typeinfo->iti_type == IPTUN_TYPE_UNKNOWN) { 14152b24ab6bSSebastien Roy err = EINVAL; 14162b24ab6bSSebastien Roy goto done; 14172b24ab6bSSebastien Roy } 14182b24ab6bSSebastien Roy 14192b24ab6bSSebastien Roy if (ik->iptun_kparam_flags & IPTUN_KPARAM_IMPLICIT) 14202b24ab6bSSebastien Roy iptun->iptun_flags |= IPTUN_IMPLICIT; 14212b24ab6bSSebastien Roy 14222b24ab6bSSebastien Roy if ((err = iptun_setparams(iptun, ik)) != 0) 14232b24ab6bSSebastien Roy goto done; 14242b24ab6bSSebastien Roy 14252b24ab6bSSebastien Roy iptun->iptun_hoplimit = IPTUN_DEFAULT_HOPLIMIT; 14262b24ab6bSSebastien Roy if (iptun->iptun_typeinfo->iti_type == IPTUN_TYPE_IPV6) 14272b24ab6bSSebastien Roy iptun->iptun_encaplimit = IPTUN_DEFAULT_ENCAPLIMIT; 14282b24ab6bSSebastien Roy 14292b24ab6bSSebastien Roy iptun_headergen(iptun, B_FALSE); 14302b24ab6bSSebastien Roy 14312b24ab6bSSebastien Roy iptun->iptun_connp = iptun_conn_create(iptun, ns, credp); 14322b24ab6bSSebastien Roy if (iptun->iptun_connp == NULL) { 14332b24ab6bSSebastien Roy err = ENOMEM; 14342b24ab6bSSebastien Roy goto done; 14352b24ab6bSSebastien Roy } 14362b24ab6bSSebastien Roy 14372b24ab6bSSebastien Roy iptun->iptun_mtu = iptun->iptun_typeinfo->iti_maxmtu; 14382b24ab6bSSebastien Roy iptun->iptun_dpmtu = iptun->iptun_mtu; 14392b24ab6bSSebastien Roy 14402b24ab6bSSebastien Roy /* 14412b24ab6bSSebastien Roy * Find an ITP based on linkname. If we have parms already set via 14422b24ab6bSSebastien Roy * the iptun_setparams() call above, it may have created an ITP for 14432b24ab6bSSebastien Roy * us. We always try get_tunnel_policy() for DEBUG correctness 14442b24ab6bSSebastien Roy * checks, and we may wish to refactor this to only check when 14452b24ab6bSSebastien Roy * iptun_itp is NULL. 14462b24ab6bSSebastien Roy */ 14472b24ab6bSSebastien Roy if ((err = dls_mgmt_get_linkinfo(iptun->iptun_linkid, linkname, NULL, 14482b24ab6bSSebastien Roy NULL, NULL)) != 0) 14492b24ab6bSSebastien Roy goto done; 14502b24ab6bSSebastien Roy if ((itp = get_tunnel_policy(linkname, ns)) != NULL) 14512b24ab6bSSebastien Roy iptun->iptun_itp = itp; 14522b24ab6bSSebastien Roy 14532b24ab6bSSebastien Roy /* 14542b24ab6bSSebastien Roy * See if we have the necessary IP addresses assigned to this tunnel 14552b24ab6bSSebastien Roy * to try and bind them with ip underneath us. If we're not ready to 14562b24ab6bSSebastien Roy * bind yet, then we'll defer the bind operation until the addresses 14572b24ab6bSSebastien Roy * are modified. 14582b24ab6bSSebastien Roy */ 14592b24ab6bSSebastien Roy if (iptun_canbind(iptun) && ((err = iptun_bind(iptun)) != 0)) 14602b24ab6bSSebastien Roy goto done; 14612b24ab6bSSebastien Roy 14622b24ab6bSSebastien Roy if ((err = iptun_register(iptun)) != 0) 14632b24ab6bSSebastien Roy goto done; 14642b24ab6bSSebastien Roy 14652b24ab6bSSebastien Roy err = dls_devnet_create(iptun->iptun_mh, iptun->iptun_linkid, 14662b24ab6bSSebastien Roy iptun->iptun_zoneid); 14672b24ab6bSSebastien Roy if (err != 0) 14682b24ab6bSSebastien Roy goto done; 14692b24ab6bSSebastien Roy link_created = B_TRUE; 14702b24ab6bSSebastien Roy 14712b24ab6bSSebastien Roy /* 14722b24ab6bSSebastien Roy * We hash by link-id as that is the key used by all other iptun 14732b24ab6bSSebastien Roy * interfaces (modify, delete, etc.). 14742b24ab6bSSebastien Roy */ 14752b24ab6bSSebastien Roy if ((mherr = mod_hash_insert(iptun_hash, 14762b24ab6bSSebastien Roy IPTUN_HASH_KEY(iptun->iptun_linkid), (mod_hash_val_t)iptun)) == 0) { 14772b24ab6bSSebastien Roy mutex_enter(&iptuns->iptuns_lock); 14782b24ab6bSSebastien Roy list_insert_head(&iptuns->iptuns_iptunlist, iptun); 14792b24ab6bSSebastien Roy mutex_exit(&iptuns->iptuns_lock); 14802b24ab6bSSebastien Roy iptun->iptun_flags |= IPTUN_HASH_INSERTED; 14812b24ab6bSSebastien Roy } else if (mherr == MH_ERR_NOMEM) { 14822b24ab6bSSebastien Roy err = ENOMEM; 14832b24ab6bSSebastien Roy } else if (mherr == MH_ERR_DUPLICATE) { 14842b24ab6bSSebastien Roy err = EEXIST; 14852b24ab6bSSebastien Roy } else { 14862b24ab6bSSebastien Roy err = EINVAL; 14872b24ab6bSSebastien Roy } 14882b24ab6bSSebastien Roy 14892b24ab6bSSebastien Roy done: 14902b24ab6bSSebastien Roy if (iptun == NULL && ns != NULL) 14912b24ab6bSSebastien Roy netstack_rele(ns); 14922b24ab6bSSebastien Roy if (err != 0 && iptun != NULL) { 14932b24ab6bSSebastien Roy if (link_created) { 14942b24ab6bSSebastien Roy (void) dls_devnet_destroy(iptun->iptun_mh, &tmpid, 14952b24ab6bSSebastien Roy B_TRUE); 14962b24ab6bSSebastien Roy } 14972b24ab6bSSebastien Roy iptun->iptun_flags |= IPTUN_CONDEMNED; 14982b24ab6bSSebastien Roy iptun_free(iptun); 14992b24ab6bSSebastien Roy } 15002b24ab6bSSebastien Roy return (err); 15012b24ab6bSSebastien Roy } 15022b24ab6bSSebastien Roy 15032b24ab6bSSebastien Roy int 15042b24ab6bSSebastien Roy iptun_delete(datalink_id_t linkid, cred_t *credp) 15052b24ab6bSSebastien Roy { 15062b24ab6bSSebastien Roy int err; 15072b24ab6bSSebastien Roy iptun_t *iptun = NULL; 15082b24ab6bSSebastien Roy 15092b24ab6bSSebastien Roy if ((err = iptun_enter_by_linkid(linkid, &iptun)) != 0) 15102b24ab6bSSebastien Roy return (err); 15112b24ab6bSSebastien Roy 15122b24ab6bSSebastien Roy /* One cannot delete a tunnel that belongs to another zone. */ 15132b24ab6bSSebastien Roy if (iptun->iptun_zoneid != crgetzoneid(credp)) { 15142b24ab6bSSebastien Roy iptun_exit(iptun); 15152b24ab6bSSebastien Roy return (EACCES); 15162b24ab6bSSebastien Roy } 15172b24ab6bSSebastien Roy 15182b24ab6bSSebastien Roy /* 15192b24ab6bSSebastien Roy * We need to exit iptun in order to issue calls up the stack such as 15202b24ab6bSSebastien Roy * dls_devnet_destroy(). If we call up while still in iptun, deadlock 15212b24ab6bSSebastien Roy * with calls coming down the stack is possible. We prevent other 15222b24ab6bSSebastien Roy * threads from entering this iptun after we've exited it by setting 15232b24ab6bSSebastien Roy * the IPTUN_DELETE_PENDING flag. This will cause callers of 15242b24ab6bSSebastien Roy * iptun_enter() to block waiting on iptun_enter_cv. The assumption 15252b24ab6bSSebastien Roy * here is that the functions we're calling while IPTUN_DELETE_PENDING 15262b24ab6bSSebastien Roy * is set dont resuult in an iptun_enter() call, as that would result 15272b24ab6bSSebastien Roy * in deadlock. 15282b24ab6bSSebastien Roy */ 15292b24ab6bSSebastien Roy iptun->iptun_flags |= IPTUN_DELETE_PENDING; 15302b24ab6bSSebastien Roy 15312b24ab6bSSebastien Roy /* Wait for any pending upcall to the mac module to complete. */ 15322b24ab6bSSebastien Roy while (iptun->iptun_flags & IPTUN_UPCALL_PENDING) 15332b24ab6bSSebastien Roy cv_wait(&iptun->iptun_upcall_cv, &iptun->iptun_lock); 15342b24ab6bSSebastien Roy 15352b24ab6bSSebastien Roy iptun_exit(iptun); 15362b24ab6bSSebastien Roy 15372b24ab6bSSebastien Roy if ((err = dls_devnet_destroy(iptun->iptun_mh, &linkid, B_TRUE)) == 0) { 15382b24ab6bSSebastien Roy /* 15392b24ab6bSSebastien Roy * mac_disable() will fail with EBUSY if there are references 15402b24ab6bSSebastien Roy * to the iptun MAC. If there are none, then mac_disable() 15412b24ab6bSSebastien Roy * will assure that none can be acquired until the MAC is 15422b24ab6bSSebastien Roy * unregistered. 15432b24ab6bSSebastien Roy * 15442b24ab6bSSebastien Roy * XXX CR 6791335 prevents us from calling mac_disable() prior 15452b24ab6bSSebastien Roy * to dls_devnet_destroy(), so we unfortunately need to 15462b24ab6bSSebastien Roy * attempt to re-create the devnet node if mac_disable() 15472b24ab6bSSebastien Roy * fails. 15482b24ab6bSSebastien Roy */ 15492b24ab6bSSebastien Roy if ((err = mac_disable(iptun->iptun_mh)) != 0) { 15502b24ab6bSSebastien Roy (void) dls_devnet_create(iptun->iptun_mh, linkid, 15512b24ab6bSSebastien Roy iptun->iptun_zoneid); 15522b24ab6bSSebastien Roy } 15532b24ab6bSSebastien Roy } 15542b24ab6bSSebastien Roy 15552b24ab6bSSebastien Roy /* 15562b24ab6bSSebastien Roy * Now that we know the fate of this iptun_t, we need to clear 15572b24ab6bSSebastien Roy * IPTUN_DELETE_PENDING, and set IPTUN_CONDEMNED if the iptun_t is 15582b24ab6bSSebastien Roy * slated to be freed. Either way, we need to signal the threads 15592b24ab6bSSebastien Roy * waiting in iptun_enter() so that they can either fail if 15602b24ab6bSSebastien Roy * IPTUN_CONDEMNED is set, or continue if it's not. 15612b24ab6bSSebastien Roy */ 15622b24ab6bSSebastien Roy mutex_enter(&iptun->iptun_lock); 15632b24ab6bSSebastien Roy iptun->iptun_flags &= ~IPTUN_DELETE_PENDING; 15642b24ab6bSSebastien Roy if (err == 0) 15652b24ab6bSSebastien Roy iptun->iptun_flags |= IPTUN_CONDEMNED; 15662b24ab6bSSebastien Roy cv_broadcast(&iptun->iptun_enter_cv); 15672b24ab6bSSebastien Roy mutex_exit(&iptun->iptun_lock); 15682b24ab6bSSebastien Roy 15692b24ab6bSSebastien Roy /* 15702b24ab6bSSebastien Roy * Note that there is no danger in calling iptun_free() after having 15712b24ab6bSSebastien Roy * dropped the iptun_lock since callers of iptun_enter() at this point 15722b24ab6bSSebastien Roy * are doing so from iptun_enter_by_linkid() (mac_disable() got rid of 15732b24ab6bSSebastien Roy * threads entering from mac callbacks which call iptun_enter() 15742b24ab6bSSebastien Roy * directly) which holds iptun_hash_lock, and iptun_free() grabs this 15752b24ab6bSSebastien Roy * lock in order to remove the iptun_t from the hash table. 15762b24ab6bSSebastien Roy */ 15772b24ab6bSSebastien Roy if (err == 0) 15782b24ab6bSSebastien Roy iptun_free(iptun); 15792b24ab6bSSebastien Roy 15802b24ab6bSSebastien Roy return (err); 15812b24ab6bSSebastien Roy } 15822b24ab6bSSebastien Roy 15832b24ab6bSSebastien Roy int 15842b24ab6bSSebastien Roy iptun_modify(const iptun_kparams_t *ik, cred_t *credp) 15852b24ab6bSSebastien Roy { 15862b24ab6bSSebastien Roy iptun_t *iptun; 15872b24ab6bSSebastien Roy boolean_t laddr_change = B_FALSE, raddr_change = B_FALSE; 15882b24ab6bSSebastien Roy int err; 15892b24ab6bSSebastien Roy 15902b24ab6bSSebastien Roy if ((err = iptun_enter_by_linkid(ik->iptun_kparam_linkid, &iptun)) != 0) 15912b24ab6bSSebastien Roy return (err); 15922b24ab6bSSebastien Roy 15932b24ab6bSSebastien Roy /* One cannot modify a tunnel that belongs to another zone. */ 15942b24ab6bSSebastien Roy if (iptun->iptun_zoneid != crgetzoneid(credp)) { 15952b24ab6bSSebastien Roy err = EACCES; 15962b24ab6bSSebastien Roy goto done; 15972b24ab6bSSebastien Roy } 15982b24ab6bSSebastien Roy 15992b24ab6bSSebastien Roy /* The tunnel type cannot be changed */ 16002b24ab6bSSebastien Roy if (ik->iptun_kparam_flags & IPTUN_KPARAM_TYPE) { 16012b24ab6bSSebastien Roy err = EINVAL; 16022b24ab6bSSebastien Roy goto done; 16032b24ab6bSSebastien Roy } 16042b24ab6bSSebastien Roy 16052b24ab6bSSebastien Roy if ((err = iptun_setparams(iptun, ik)) != 0) 16062b24ab6bSSebastien Roy goto done; 16072b24ab6bSSebastien Roy iptun_headergen(iptun, B_FALSE); 16082b24ab6bSSebastien Roy 16092b24ab6bSSebastien Roy /* 16102b24ab6bSSebastien Roy * If any of the tunnel's addresses has been modified and the tunnel 16112b24ab6bSSebastien Roy * has the necessary addresses assigned to it, we need to try to bind 16122b24ab6bSSebastien Roy * with ip underneath us. If we're not ready to bind yet, then we'll 16132b24ab6bSSebastien Roy * try again when the addresses are modified later. 16142b24ab6bSSebastien Roy */ 16152b24ab6bSSebastien Roy laddr_change = (ik->iptun_kparam_flags & IPTUN_KPARAM_LADDR); 16162b24ab6bSSebastien Roy raddr_change = (ik->iptun_kparam_flags & IPTUN_KPARAM_RADDR); 16172b24ab6bSSebastien Roy if (laddr_change || raddr_change) { 16182b24ab6bSSebastien Roy if (iptun->iptun_flags & IPTUN_BOUND) 16192b24ab6bSSebastien Roy iptun_unbind(iptun); 16202b24ab6bSSebastien Roy if (iptun_canbind(iptun) && (err = iptun_bind(iptun)) != 0) { 16212b24ab6bSSebastien Roy if (laddr_change) 16222b24ab6bSSebastien Roy iptun->iptun_flags &= ~IPTUN_LADDR; 16232b24ab6bSSebastien Roy if (raddr_change) 16242b24ab6bSSebastien Roy iptun->iptun_flags &= ~IPTUN_RADDR; 16252b24ab6bSSebastien Roy goto done; 16262b24ab6bSSebastien Roy } 16272b24ab6bSSebastien Roy } 16282b24ab6bSSebastien Roy 16292b24ab6bSSebastien Roy if (laddr_change) 16302b24ab6bSSebastien Roy iptun_task_dispatch(iptun, IPTUN_TASK_LADDR_UPDATE); 16312b24ab6bSSebastien Roy if (raddr_change) 16322b24ab6bSSebastien Roy iptun_task_dispatch(iptun, IPTUN_TASK_RADDR_UPDATE); 16332b24ab6bSSebastien Roy 16342b24ab6bSSebastien Roy done: 16352b24ab6bSSebastien Roy iptun_exit(iptun); 16362b24ab6bSSebastien Roy return (err); 16372b24ab6bSSebastien Roy } 16382b24ab6bSSebastien Roy 16392b24ab6bSSebastien Roy /* Given an IP tunnel's datalink id, fill in its parameters. */ 16402b24ab6bSSebastien Roy int 16412b24ab6bSSebastien Roy iptun_info(iptun_kparams_t *ik, cred_t *credp) 16422b24ab6bSSebastien Roy { 16432b24ab6bSSebastien Roy iptun_t *iptun; 16442b24ab6bSSebastien Roy int err; 16452b24ab6bSSebastien Roy 16462b24ab6bSSebastien Roy /* Is the tunnel link visible from the caller's zone? */ 16472b24ab6bSSebastien Roy if (!dls_devnet_islinkvisible(ik->iptun_kparam_linkid, 16482b24ab6bSSebastien Roy crgetzoneid(credp))) 16492b24ab6bSSebastien Roy return (ENOENT); 16502b24ab6bSSebastien Roy 16512b24ab6bSSebastien Roy if ((err = iptun_enter_by_linkid(ik->iptun_kparam_linkid, &iptun)) != 0) 16522b24ab6bSSebastien Roy return (err); 16532b24ab6bSSebastien Roy 16542b24ab6bSSebastien Roy bzero(ik, sizeof (iptun_kparams_t)); 16552b24ab6bSSebastien Roy 16562b24ab6bSSebastien Roy ik->iptun_kparam_linkid = iptun->iptun_linkid; 16572b24ab6bSSebastien Roy ik->iptun_kparam_type = iptun->iptun_typeinfo->iti_type; 16582b24ab6bSSebastien Roy ik->iptun_kparam_flags |= IPTUN_KPARAM_TYPE; 16592b24ab6bSSebastien Roy 16602b24ab6bSSebastien Roy if (iptun->iptun_flags & IPTUN_LADDR) { 16612b24ab6bSSebastien Roy iptun_getaddr(&iptun->iptun_laddr, &ik->iptun_kparam_laddr); 16622b24ab6bSSebastien Roy ik->iptun_kparam_flags |= IPTUN_KPARAM_LADDR; 16632b24ab6bSSebastien Roy } 16642b24ab6bSSebastien Roy if (iptun->iptun_flags & IPTUN_RADDR) { 16652b24ab6bSSebastien Roy iptun_getaddr(&iptun->iptun_raddr, &ik->iptun_kparam_raddr); 16662b24ab6bSSebastien Roy ik->iptun_kparam_flags |= IPTUN_KPARAM_RADDR; 16672b24ab6bSSebastien Roy } 16682b24ab6bSSebastien Roy 16692b24ab6bSSebastien Roy if (iptun->iptun_flags & IPTUN_IMPLICIT) 16702b24ab6bSSebastien Roy ik->iptun_kparam_flags |= IPTUN_KPARAM_IMPLICIT; 16712b24ab6bSSebastien Roy 16722b24ab6bSSebastien Roy if (iptun->iptun_itp != NULL) { 16732b24ab6bSSebastien Roy mutex_enter(&iptun->iptun_itp->itp_lock); 16742b24ab6bSSebastien Roy if (iptun->iptun_itp->itp_flags & ITPF_P_ACTIVE) { 16752b24ab6bSSebastien Roy ik->iptun_kparam_flags |= IPTUN_KPARAM_IPSECPOL; 16762b24ab6bSSebastien Roy if (iptun->iptun_flags & IPTUN_SIMPLE_POLICY) { 16772b24ab6bSSebastien Roy ik->iptun_kparam_flags |= IPTUN_KPARAM_SECINFO; 16782b24ab6bSSebastien Roy ik->iptun_kparam_secinfo = 16792b24ab6bSSebastien Roy iptun->iptun_simple_policy; 16802b24ab6bSSebastien Roy } 16812b24ab6bSSebastien Roy } 16822b24ab6bSSebastien Roy mutex_exit(&iptun->iptun_itp->itp_lock); 16832b24ab6bSSebastien Roy } 16842b24ab6bSSebastien Roy 16852b24ab6bSSebastien Roy done: 16862b24ab6bSSebastien Roy iptun_exit(iptun); 16872b24ab6bSSebastien Roy return (err); 16882b24ab6bSSebastien Roy } 16892b24ab6bSSebastien Roy 16902b24ab6bSSebastien Roy int 16912b24ab6bSSebastien Roy iptun_set_6to4relay(netstack_t *ns, ipaddr_t relay_addr) 16922b24ab6bSSebastien Roy { 16932b24ab6bSSebastien Roy if (relay_addr == INADDR_BROADCAST || CLASSD(relay_addr)) 16942b24ab6bSSebastien Roy return (EADDRNOTAVAIL); 16952b24ab6bSSebastien Roy ns->netstack_iptun->iptuns_relay_rtr_addr = relay_addr; 16962b24ab6bSSebastien Roy return (0); 16972b24ab6bSSebastien Roy } 16982b24ab6bSSebastien Roy 16992b24ab6bSSebastien Roy void 17002b24ab6bSSebastien Roy iptun_get_6to4relay(netstack_t *ns, ipaddr_t *relay_addr) 17012b24ab6bSSebastien Roy { 17022b24ab6bSSebastien Roy *relay_addr = ns->netstack_iptun->iptuns_relay_rtr_addr; 17032b24ab6bSSebastien Roy } 17042b24ab6bSSebastien Roy 17052b24ab6bSSebastien Roy void 17062b24ab6bSSebastien Roy iptun_set_policy(datalink_id_t linkid, ipsec_tun_pol_t *itp) 17072b24ab6bSSebastien Roy { 17082b24ab6bSSebastien Roy iptun_t *iptun; 17092b24ab6bSSebastien Roy 17102b24ab6bSSebastien Roy if (iptun_enter_by_linkid(linkid, &iptun) != 0) 17112b24ab6bSSebastien Roy return; 17122b24ab6bSSebastien Roy if (iptun->iptun_itp != itp) { 17132b24ab6bSSebastien Roy ASSERT(iptun->iptun_itp == NULL); 17142b24ab6bSSebastien Roy ITP_REFHOLD(itp); 17152b24ab6bSSebastien Roy iptun->iptun_itp = itp; 17162b24ab6bSSebastien Roy } 171701ac885fSDan McDonald /* 171801ac885fSDan McDonald * IPsec policy means IPsec overhead, which means lower MTU. 171901ac885fSDan McDonald * Refresh the MTU for this tunnel. 172001ac885fSDan McDonald */ 172101ac885fSDan McDonald (void) iptun_update_mtu(iptun, NULL, 0); 17222b24ab6bSSebastien Roy iptun_exit(iptun); 17232b24ab6bSSebastien Roy } 17242b24ab6bSSebastien Roy 17252b24ab6bSSebastien Roy /* 17262b24ab6bSSebastien Roy * Obtain the path MTU to the tunnel destination. 1727bd670b35SErik Nordmark * Can return zero in some cases. 17282b24ab6bSSebastien Roy */ 17292b24ab6bSSebastien Roy static uint32_t 1730bd670b35SErik Nordmark iptun_get_dst_pmtu(iptun_t *iptun, ip_xmit_attr_t *ixa) 17312b24ab6bSSebastien Roy { 17322b24ab6bSSebastien Roy uint32_t pmtu = 0; 1733bd670b35SErik Nordmark conn_t *connp = iptun->iptun_connp; 1734bd670b35SErik Nordmark boolean_t need_rele = B_FALSE; 17352b24ab6bSSebastien Roy 17362b24ab6bSSebastien Roy /* 1737bd670b35SErik Nordmark * We only obtain the pmtu for tunnels that have a remote tunnel 1738bd670b35SErik Nordmark * address. 17392b24ab6bSSebastien Roy */ 17402b24ab6bSSebastien Roy if (!(iptun->iptun_flags & IPTUN_RADDR)) 17412b24ab6bSSebastien Roy return (0); 17422b24ab6bSSebastien Roy 1743bd670b35SErik Nordmark if (ixa == NULL) { 1744bd670b35SErik Nordmark ixa = conn_get_ixa(connp, B_FALSE); 1745bd670b35SErik Nordmark if (ixa == NULL) 1746bd670b35SErik Nordmark return (0); 1747bd670b35SErik Nordmark need_rele = B_TRUE; 1748bd670b35SErik Nordmark } 1749bd670b35SErik Nordmark /* 1750bd670b35SErik Nordmark * Guard against ICMP errors before we have sent, as well as against 1751bd670b35SErik Nordmark * and a thread which held conn_ixa. 1752bd670b35SErik Nordmark */ 1753bd670b35SErik Nordmark if (ixa->ixa_ire != NULL) { 1754bd670b35SErik Nordmark pmtu = ip_get_pmtu(ixa); 1755bd670b35SErik Nordmark 1756bd670b35SErik Nordmark /* 1757bd670b35SErik Nordmark * For both IPv4 and IPv6 we can have indication that the outer 1758bd670b35SErik Nordmark * header needs fragmentation. 1759bd670b35SErik Nordmark */ 1760bd670b35SErik Nordmark if (ixa->ixa_flags & IXAF_PMTU_TOO_SMALL) { 1761bd670b35SErik Nordmark /* Must allow fragmentation in ip_output */ 1762bd670b35SErik Nordmark ixa->ixa_flags &= ~IXAF_DONTFRAG; 1763bd670b35SErik Nordmark } else if (iptun->iptun_typeinfo->iti_type != IPTUN_TYPE_6TO4) { 1764bd670b35SErik Nordmark ixa->ixa_flags |= IXAF_DONTFRAG; 1765bd670b35SErik Nordmark } else { 1766bd670b35SErik Nordmark /* ip_get_pmtu might have set this - we don't want it */ 1767bd670b35SErik Nordmark ixa->ixa_flags &= ~IXAF_PMTU_IPV4_DF; 1768bd670b35SErik Nordmark } 17692b24ab6bSSebastien Roy } 17702b24ab6bSSebastien Roy 1771bd670b35SErik Nordmark if (need_rele) 1772bd670b35SErik Nordmark ixa_refrele(ixa); 17732b24ab6bSSebastien Roy return (pmtu); 17742b24ab6bSSebastien Roy } 17752b24ab6bSSebastien Roy 17762b24ab6bSSebastien Roy /* 1777bd670b35SErik Nordmark * Update the ip_xmit_attr_t to capture the current lower path mtu as known 1778bd670b35SErik Nordmark * by ip. 1779bd670b35SErik Nordmark */ 1780bd670b35SErik Nordmark static void 1781bd670b35SErik Nordmark iptun_update_dst_pmtu(iptun_t *iptun, ip_xmit_attr_t *ixa) 1782bd670b35SErik Nordmark { 1783bd670b35SErik Nordmark uint32_t pmtu; 1784bd670b35SErik Nordmark conn_t *connp = iptun->iptun_connp; 1785bd670b35SErik Nordmark boolean_t need_rele = B_FALSE; 1786bd670b35SErik Nordmark 1787bd670b35SErik Nordmark /* IXAF_VERIFY_PMTU is not set if we don't have a fixed destination */ 1788bd670b35SErik Nordmark if (!(iptun->iptun_flags & IPTUN_RADDR)) 1789bd670b35SErik Nordmark return; 1790bd670b35SErik Nordmark 1791bd670b35SErik Nordmark if (ixa == NULL) { 1792bd670b35SErik Nordmark ixa = conn_get_ixa(connp, B_FALSE); 1793bd670b35SErik Nordmark if (ixa == NULL) 1794bd670b35SErik Nordmark return; 1795bd670b35SErik Nordmark need_rele = B_TRUE; 1796bd670b35SErik Nordmark } 1797bd670b35SErik Nordmark /* 1798bd670b35SErik Nordmark * Guard against ICMP errors before we have sent, as well as against 1799bd670b35SErik Nordmark * and a thread which held conn_ixa. 1800bd670b35SErik Nordmark */ 1801bd670b35SErik Nordmark if (ixa->ixa_ire != NULL) { 1802bd670b35SErik Nordmark pmtu = ip_get_pmtu(ixa); 1803bd670b35SErik Nordmark /* 1804bd670b35SErik Nordmark * Update ixa_fragsize and ixa_pmtu. 1805bd670b35SErik Nordmark */ 1806bd670b35SErik Nordmark ixa->ixa_fragsize = ixa->ixa_pmtu = pmtu; 1807bd670b35SErik Nordmark 1808bd670b35SErik Nordmark /* 1809bd670b35SErik Nordmark * For both IPv4 and IPv6 we can have indication that the outer 1810bd670b35SErik Nordmark * header needs fragmentation. 1811bd670b35SErik Nordmark */ 1812bd670b35SErik Nordmark if (ixa->ixa_flags & IXAF_PMTU_TOO_SMALL) { 1813bd670b35SErik Nordmark /* Must allow fragmentation in ip_output */ 1814bd670b35SErik Nordmark ixa->ixa_flags &= ~IXAF_DONTFRAG; 1815bd670b35SErik Nordmark } else if (iptun->iptun_typeinfo->iti_type != IPTUN_TYPE_6TO4) { 1816bd670b35SErik Nordmark ixa->ixa_flags |= IXAF_DONTFRAG; 1817bd670b35SErik Nordmark } else { 1818bd670b35SErik Nordmark /* ip_get_pmtu might have set this - we don't want it */ 1819bd670b35SErik Nordmark ixa->ixa_flags &= ~IXAF_PMTU_IPV4_DF; 1820bd670b35SErik Nordmark } 1821bd670b35SErik Nordmark } 1822bd670b35SErik Nordmark 1823bd670b35SErik Nordmark if (need_rele) 1824bd670b35SErik Nordmark ixa_refrele(ixa); 1825bd670b35SErik Nordmark } 1826bd670b35SErik Nordmark 1827bd670b35SErik Nordmark /* 1828bd670b35SErik Nordmark * There is nothing that iptun can verify in addition to IP having 1829bd670b35SErik Nordmark * verified the IP addresses in the fanout. 1830bd670b35SErik Nordmark */ 1831bd670b35SErik Nordmark /* ARGSUSED */ 1832bd670b35SErik Nordmark static boolean_t 1833bd670b35SErik Nordmark iptun_verifyicmp(conn_t *connp, void *arg2, icmph_t *icmph, icmp6_t *icmp6, 1834bd670b35SErik Nordmark ip_recv_attr_t *ira) 1835bd670b35SErik Nordmark { 1836bd670b35SErik Nordmark return (B_TRUE); 1837bd670b35SErik Nordmark } 1838bd670b35SErik Nordmark 1839bd670b35SErik Nordmark /* 1840bd670b35SErik Nordmark * Notify function registered with ip_xmit_attr_t. 1841bd670b35SErik Nordmark */ 1842bd670b35SErik Nordmark static void 1843bd670b35SErik Nordmark iptun_notify(void *arg, ip_xmit_attr_t *ixa, ixa_notify_type_t ntype, 1844bd670b35SErik Nordmark ixa_notify_arg_t narg) 1845bd670b35SErik Nordmark { 1846bd670b35SErik Nordmark iptun_t *iptun = (iptun_t *)arg; 1847bd670b35SErik Nordmark 1848bd670b35SErik Nordmark switch (ntype) { 1849bd670b35SErik Nordmark case IXAN_PMTU: 1850bd670b35SErik Nordmark (void) iptun_update_mtu(iptun, ixa, narg); 1851bd670b35SErik Nordmark break; 1852bd670b35SErik Nordmark } 1853bd670b35SErik Nordmark } 1854bd670b35SErik Nordmark 1855bd670b35SErik Nordmark /* 18562b24ab6bSSebastien Roy * Returns the max of old_ovhd and the overhead associated with pol. 18572b24ab6bSSebastien Roy */ 18582b24ab6bSSebastien Roy static uint32_t 18592b24ab6bSSebastien Roy iptun_max_policy_overhead(ipsec_policy_t *pol, uint32_t old_ovhd) 18602b24ab6bSSebastien Roy { 18612b24ab6bSSebastien Roy uint32_t new_ovhd = old_ovhd; 18622b24ab6bSSebastien Roy 18632b24ab6bSSebastien Roy while (pol != NULL) { 18642b24ab6bSSebastien Roy new_ovhd = max(new_ovhd, 18652b24ab6bSSebastien Roy ipsec_act_ovhd(&pol->ipsp_act->ipa_act)); 18662b24ab6bSSebastien Roy pol = pol->ipsp_hash.hash_next; 18672b24ab6bSSebastien Roy } 18682b24ab6bSSebastien Roy return (new_ovhd); 18692b24ab6bSSebastien Roy } 18702b24ab6bSSebastien Roy 18712b24ab6bSSebastien Roy static uint32_t 18722b24ab6bSSebastien Roy iptun_get_ipsec_overhead(iptun_t *iptun) 18732b24ab6bSSebastien Roy { 18742b24ab6bSSebastien Roy ipsec_policy_root_t *ipr; 18752b24ab6bSSebastien Roy ipsec_policy_head_t *iph; 18762b24ab6bSSebastien Roy ipsec_policy_t *pol; 18772b24ab6bSSebastien Roy ipsec_selector_t sel; 18782b24ab6bSSebastien Roy int i; 18792b24ab6bSSebastien Roy uint32_t ipsec_ovhd = 0; 18802b24ab6bSSebastien Roy ipsec_tun_pol_t *itp = iptun->iptun_itp; 18812b24ab6bSSebastien Roy netstack_t *ns = iptun->iptun_ns; 18822b24ab6bSSebastien Roy 18832b24ab6bSSebastien Roy if (itp == NULL || !(itp->itp_flags & ITPF_P_ACTIVE)) { 18842b24ab6bSSebastien Roy /* 18852b24ab6bSSebastien Roy * Consult global policy, just in case. This will only work 18862b24ab6bSSebastien Roy * if we have both source and destination addresses to work 18872b24ab6bSSebastien Roy * with. 18882b24ab6bSSebastien Roy */ 18892b24ab6bSSebastien Roy if ((iptun->iptun_flags & (IPTUN_LADDR|IPTUN_RADDR)) != 18902b24ab6bSSebastien Roy (IPTUN_LADDR|IPTUN_RADDR)) 18912b24ab6bSSebastien Roy return (0); 18922b24ab6bSSebastien Roy 18932b24ab6bSSebastien Roy iph = ipsec_system_policy(ns); 18942b24ab6bSSebastien Roy bzero(&sel, sizeof (sel)); 18952b24ab6bSSebastien Roy sel.ips_isv4 = 18962b24ab6bSSebastien Roy (iptun->iptun_typeinfo->iti_ipvers == IPV4_VERSION); 18972b24ab6bSSebastien Roy switch (iptun->iptun_typeinfo->iti_ipvers) { 18982b24ab6bSSebastien Roy case IPV4_VERSION: 18992b24ab6bSSebastien Roy sel.ips_local_addr_v4 = iptun->iptun_laddr4; 19002b24ab6bSSebastien Roy sel.ips_remote_addr_v4 = iptun->iptun_raddr4; 19012b24ab6bSSebastien Roy break; 19022b24ab6bSSebastien Roy case IPV6_VERSION: 19032b24ab6bSSebastien Roy sel.ips_local_addr_v6 = iptun->iptun_laddr6; 19042b24ab6bSSebastien Roy sel.ips_remote_addr_v6 = iptun->iptun_raddr6; 19052b24ab6bSSebastien Roy break; 19062b24ab6bSSebastien Roy } 19072b24ab6bSSebastien Roy /* Check for both IPv4 and IPv6. */ 19082b24ab6bSSebastien Roy sel.ips_protocol = IPPROTO_ENCAP; 19092b24ab6bSSebastien Roy pol = ipsec_find_policy_head(NULL, iph, IPSEC_TYPE_OUTBOUND, 1910bd670b35SErik Nordmark &sel); 19112b24ab6bSSebastien Roy if (pol != NULL) { 19122b24ab6bSSebastien Roy ipsec_ovhd = ipsec_act_ovhd(&pol->ipsp_act->ipa_act); 1913bd670b35SErik Nordmark IPPOL_REFRELE(pol); 19142b24ab6bSSebastien Roy } 19152b24ab6bSSebastien Roy sel.ips_protocol = IPPROTO_IPV6; 19162b24ab6bSSebastien Roy pol = ipsec_find_policy_head(NULL, iph, IPSEC_TYPE_OUTBOUND, 1917bd670b35SErik Nordmark &sel); 19182b24ab6bSSebastien Roy if (pol != NULL) { 19192b24ab6bSSebastien Roy ipsec_ovhd = max(ipsec_ovhd, 19202b24ab6bSSebastien Roy ipsec_act_ovhd(&pol->ipsp_act->ipa_act)); 1921bd670b35SErik Nordmark IPPOL_REFRELE(pol); 19222b24ab6bSSebastien Roy } 19232b24ab6bSSebastien Roy IPPH_REFRELE(iph, ns); 19242b24ab6bSSebastien Roy } else { 19252b24ab6bSSebastien Roy /* 19262b24ab6bSSebastien Roy * Look through all of the possible IPsec actions for the 19272b24ab6bSSebastien Roy * tunnel, and find the largest potential IPsec overhead. 19282b24ab6bSSebastien Roy */ 19292b24ab6bSSebastien Roy iph = itp->itp_policy; 19302b24ab6bSSebastien Roy rw_enter(&iph->iph_lock, RW_READER); 19312b24ab6bSSebastien Roy ipr = &(iph->iph_root[IPSEC_TYPE_OUTBOUND]); 19322b24ab6bSSebastien Roy ipsec_ovhd = iptun_max_policy_overhead( 19332b24ab6bSSebastien Roy ipr->ipr_nonhash[IPSEC_AF_V4], 0); 19342b24ab6bSSebastien Roy ipsec_ovhd = iptun_max_policy_overhead( 19352b24ab6bSSebastien Roy ipr->ipr_nonhash[IPSEC_AF_V6], ipsec_ovhd); 19362b24ab6bSSebastien Roy for (i = 0; i < ipr->ipr_nchains; i++) { 19372b24ab6bSSebastien Roy ipsec_ovhd = iptun_max_policy_overhead( 19382b24ab6bSSebastien Roy ipr->ipr_hash[i].hash_head, ipsec_ovhd); 19392b24ab6bSSebastien Roy } 19402b24ab6bSSebastien Roy rw_exit(&iph->iph_lock); 19412b24ab6bSSebastien Roy } 19422b24ab6bSSebastien Roy 19432b24ab6bSSebastien Roy return (ipsec_ovhd); 19442b24ab6bSSebastien Roy } 19452b24ab6bSSebastien Roy 19462b24ab6bSSebastien Roy /* 1947bd670b35SErik Nordmark * Calculate and return the maximum possible upper MTU for the given tunnel. 1948bd670b35SErik Nordmark * 1949bd670b35SErik Nordmark * If new_pmtu is set then we also need to update the lower path MTU information 1950bd670b35SErik Nordmark * in the ip_xmit_attr_t. That is needed since we set IXAF_VERIFY_PMTU so that 1951bd670b35SErik Nordmark * we are notified by conn_ip_output() when the path MTU increases. 19522b24ab6bSSebastien Roy */ 19532b24ab6bSSebastien Roy static uint32_t 1954bd670b35SErik Nordmark iptun_get_maxmtu(iptun_t *iptun, ip_xmit_attr_t *ixa, uint32_t new_pmtu) 19552b24ab6bSSebastien Roy { 19562b24ab6bSSebastien Roy size_t header_size, ipsec_overhead; 19572b24ab6bSSebastien Roy uint32_t maxmtu, pmtu; 19582b24ab6bSSebastien Roy 19592b24ab6bSSebastien Roy /* 19602b24ab6bSSebastien Roy * Start with the path-MTU to the remote address, which is either 19612b24ab6bSSebastien Roy * provided as the new_pmtu argument, or obtained using 19622b24ab6bSSebastien Roy * iptun_get_dst_pmtu(). 19632b24ab6bSSebastien Roy */ 19642b24ab6bSSebastien Roy if (new_pmtu != 0) { 1965bd670b35SErik Nordmark if (iptun->iptun_flags & IPTUN_RADDR) 19662b24ab6bSSebastien Roy iptun->iptun_dpmtu = new_pmtu; 19672b24ab6bSSebastien Roy pmtu = new_pmtu; 19682b24ab6bSSebastien Roy } else if (iptun->iptun_flags & IPTUN_RADDR) { 1969bd670b35SErik Nordmark if ((pmtu = iptun_get_dst_pmtu(iptun, ixa)) == 0) { 19702b24ab6bSSebastien Roy /* 19712b24ab6bSSebastien Roy * We weren't able to obtain the path-MTU of the 19722b24ab6bSSebastien Roy * destination. Use the previous value. 19732b24ab6bSSebastien Roy */ 19742b24ab6bSSebastien Roy pmtu = iptun->iptun_dpmtu; 19752b24ab6bSSebastien Roy } else { 19762b24ab6bSSebastien Roy iptun->iptun_dpmtu = pmtu; 19772b24ab6bSSebastien Roy } 19782b24ab6bSSebastien Roy } else { 19792b24ab6bSSebastien Roy /* 19802b24ab6bSSebastien Roy * We have no path-MTU information to go on, use the maximum 19812b24ab6bSSebastien Roy * possible value. 19822b24ab6bSSebastien Roy */ 19832b24ab6bSSebastien Roy pmtu = iptun->iptun_typeinfo->iti_maxmtu; 19842b24ab6bSSebastien Roy } 19852b24ab6bSSebastien Roy 19862b24ab6bSSebastien Roy /* 19872b24ab6bSSebastien Roy * Now calculate tunneling overhead and subtract that from the 19882b24ab6bSSebastien Roy * path-MTU information obtained above. 19892b24ab6bSSebastien Roy */ 19902b24ab6bSSebastien Roy if (iptun->iptun_header_size != 0) { 19912b24ab6bSSebastien Roy header_size = iptun->iptun_header_size; 19922b24ab6bSSebastien Roy } else { 19932b24ab6bSSebastien Roy switch (iptun->iptun_typeinfo->iti_ipvers) { 19942b24ab6bSSebastien Roy case IPV4_VERSION: 19952b24ab6bSSebastien Roy header_size = sizeof (ipha_t); 19965d3b8cb7SBill Sommerfeld if (is_system_labeled()) 19975d3b8cb7SBill Sommerfeld header_size += IP_MAX_OPT_LENGTH; 19982b24ab6bSSebastien Roy break; 19992b24ab6bSSebastien Roy case IPV6_VERSION: 20002b24ab6bSSebastien Roy header_size = sizeof (iptun_ipv6hdrs_t); 20012b24ab6bSSebastien Roy break; 20022b24ab6bSSebastien Roy } 20032b24ab6bSSebastien Roy } 20042b24ab6bSSebastien Roy 20052b24ab6bSSebastien Roy ipsec_overhead = iptun_get_ipsec_overhead(iptun); 20062b24ab6bSSebastien Roy 20072b24ab6bSSebastien Roy maxmtu = pmtu - (header_size + ipsec_overhead); 20082b24ab6bSSebastien Roy return (max(maxmtu, iptun->iptun_typeinfo->iti_minmtu)); 20092b24ab6bSSebastien Roy } 20102b24ab6bSSebastien Roy 20112b24ab6bSSebastien Roy /* 2012bd670b35SErik Nordmark * Re-calculate the tunnel's MTU as seen from above and notify the MAC layer 2013bd670b35SErik Nordmark * of any change in MTU. The new_pmtu argument is the new lower path MTU to 2014bd670b35SErik Nordmark * the tunnel destination to be used in the tunnel MTU calculation. Passing 2015bd670b35SErik Nordmark * in 0 for new_pmtu causes the lower path MTU to be dynamically updated using 2016bd670b35SErik Nordmark * ip_get_pmtu(). 20172b24ab6bSSebastien Roy * 20182b24ab6bSSebastien Roy * If the calculated tunnel MTU is different than its previous value, then we 20192b24ab6bSSebastien Roy * notify the MAC layer above us of this change using mac_maxsdu_update(). 20202b24ab6bSSebastien Roy */ 20212b24ab6bSSebastien Roy static uint32_t 2022bd670b35SErik Nordmark iptun_update_mtu(iptun_t *iptun, ip_xmit_attr_t *ixa, uint32_t new_pmtu) 20232b24ab6bSSebastien Roy { 20242b24ab6bSSebastien Roy uint32_t newmtu; 20252b24ab6bSSebastien Roy 2026bd670b35SErik Nordmark /* We always update the ixa since we might have set IXAF_VERIFY_PMTU */ 2027bd670b35SErik Nordmark iptun_update_dst_pmtu(iptun, ixa); 2028bd670b35SErik Nordmark 20292b24ab6bSSebastien Roy /* 20302b24ab6bSSebastien Roy * We return the current MTU without updating it if it was pegged to a 20312b24ab6bSSebastien Roy * static value using the MAC_PROP_MTU link property. 20322b24ab6bSSebastien Roy */ 20332b24ab6bSSebastien Roy if (iptun->iptun_flags & IPTUN_FIXED_MTU) 20342b24ab6bSSebastien Roy return (iptun->iptun_mtu); 20352b24ab6bSSebastien Roy 20362b24ab6bSSebastien Roy /* If the MTU isn't fixed, then use the maximum possible value. */ 2037bd670b35SErik Nordmark newmtu = iptun_get_maxmtu(iptun, ixa, new_pmtu); 20382b24ab6bSSebastien Roy /* 20392b24ab6bSSebastien Roy * We only dynamically adjust the tunnel MTU for tunnels with 20402b24ab6bSSebastien Roy * destinations because dynamic MTU calculations are based on the 20412b24ab6bSSebastien Roy * destination path-MTU. 20422b24ab6bSSebastien Roy */ 20432b24ab6bSSebastien Roy if ((iptun->iptun_flags & IPTUN_RADDR) && newmtu != iptun->iptun_mtu) { 20442b24ab6bSSebastien Roy iptun->iptun_mtu = newmtu; 20452b24ab6bSSebastien Roy if (iptun->iptun_flags & IPTUN_MAC_REGISTERED) 20462b24ab6bSSebastien Roy iptun_task_dispatch(iptun, IPTUN_TASK_MTU_UPDATE); 20472b24ab6bSSebastien Roy } 20482b24ab6bSSebastien Roy 20492b24ab6bSSebastien Roy return (newmtu); 20502b24ab6bSSebastien Roy } 20512b24ab6bSSebastien Roy 20522b24ab6bSSebastien Roy /* 20532b24ab6bSSebastien Roy * Frees a packet or packet chain and bumps stat for each freed packet. 20542b24ab6bSSebastien Roy */ 20552b24ab6bSSebastien Roy static void 20562b24ab6bSSebastien Roy iptun_drop_pkt(mblk_t *mp, uint64_t *stat) 20572b24ab6bSSebastien Roy { 20582b24ab6bSSebastien Roy mblk_t *pktmp; 20592b24ab6bSSebastien Roy 20602b24ab6bSSebastien Roy for (pktmp = mp; pktmp != NULL; pktmp = mp) { 20612b24ab6bSSebastien Roy mp = mp->b_next; 20622b24ab6bSSebastien Roy pktmp->b_next = NULL; 20632b24ab6bSSebastien Roy if (stat != NULL) 20642b24ab6bSSebastien Roy atomic_inc_64(stat); 20652b24ab6bSSebastien Roy freemsg(pktmp); 20662b24ab6bSSebastien Roy } 20672b24ab6bSSebastien Roy } 20682b24ab6bSSebastien Roy 20692b24ab6bSSebastien Roy /* 20702b24ab6bSSebastien Roy * Allocate and return a new mblk to hold an IP and ICMP header, and chain the 20712b24ab6bSSebastien Roy * original packet to its b_cont. Returns NULL on failure. 20722b24ab6bSSebastien Roy */ 20732b24ab6bSSebastien Roy static mblk_t * 20742b24ab6bSSebastien Roy iptun_build_icmperr(size_t hdrs_size, mblk_t *orig_pkt) 20752b24ab6bSSebastien Roy { 20762b24ab6bSSebastien Roy mblk_t *icmperr_mp; 20772b24ab6bSSebastien Roy 2078bd670b35SErik Nordmark if ((icmperr_mp = allocb(hdrs_size, BPRI_MED)) != NULL) { 20792b24ab6bSSebastien Roy icmperr_mp->b_wptr += hdrs_size; 20802b24ab6bSSebastien Roy /* tack on the offending packet */ 20812b24ab6bSSebastien Roy icmperr_mp->b_cont = orig_pkt; 20822b24ab6bSSebastien Roy } 20832b24ab6bSSebastien Roy return (icmperr_mp); 20842b24ab6bSSebastien Roy } 20852b24ab6bSSebastien Roy 20862b24ab6bSSebastien Roy /* 20872b24ab6bSSebastien Roy * Transmit an ICMP error. mp->b_rptr points at the packet to be included in 20882b24ab6bSSebastien Roy * the ICMP error. 20892b24ab6bSSebastien Roy */ 20902b24ab6bSSebastien Roy static void 2091bd670b35SErik Nordmark iptun_sendicmp_v4(iptun_t *iptun, icmph_t *icmp, ipha_t *orig_ipha, mblk_t *mp, 2092bd670b35SErik Nordmark ts_label_t *tsl) 20932b24ab6bSSebastien Roy { 20942b24ab6bSSebastien Roy size_t orig_pktsize, hdrs_size; 20952b24ab6bSSebastien Roy mblk_t *icmperr_mp; 20962b24ab6bSSebastien Roy ipha_t *new_ipha; 20972b24ab6bSSebastien Roy icmph_t *new_icmp; 2098bd670b35SErik Nordmark ip_xmit_attr_t ixas; 2099bd670b35SErik Nordmark conn_t *connp = iptun->iptun_connp; 21002b24ab6bSSebastien Roy 21012b24ab6bSSebastien Roy orig_pktsize = msgdsize(mp); 21022b24ab6bSSebastien Roy hdrs_size = sizeof (ipha_t) + sizeof (icmph_t); 21032b24ab6bSSebastien Roy if ((icmperr_mp = iptun_build_icmperr(hdrs_size, mp)) == NULL) { 21042b24ab6bSSebastien Roy iptun_drop_pkt(mp, &iptun->iptun_noxmtbuf); 21052b24ab6bSSebastien Roy return; 21062b24ab6bSSebastien Roy } 21072b24ab6bSSebastien Roy 21082b24ab6bSSebastien Roy new_ipha = (ipha_t *)icmperr_mp->b_rptr; 21092b24ab6bSSebastien Roy new_icmp = (icmph_t *)(new_ipha + 1); 21102b24ab6bSSebastien Roy 21112b24ab6bSSebastien Roy new_ipha->ipha_version_and_hdr_length = IP_SIMPLE_HDR_VERSION; 21122b24ab6bSSebastien Roy new_ipha->ipha_type_of_service = 0; 21132b24ab6bSSebastien Roy new_ipha->ipha_ident = 0; 21142b24ab6bSSebastien Roy new_ipha->ipha_fragment_offset_and_flags = 0; 21152b24ab6bSSebastien Roy new_ipha->ipha_ttl = orig_ipha->ipha_ttl; 21162b24ab6bSSebastien Roy new_ipha->ipha_protocol = IPPROTO_ICMP; 21172b24ab6bSSebastien Roy new_ipha->ipha_src = orig_ipha->ipha_dst; 21182b24ab6bSSebastien Roy new_ipha->ipha_dst = orig_ipha->ipha_src; 21192b24ab6bSSebastien Roy new_ipha->ipha_hdr_checksum = 0; /* will be computed by ip */ 21202b24ab6bSSebastien Roy new_ipha->ipha_length = htons(hdrs_size + orig_pktsize); 21212b24ab6bSSebastien Roy 21222b24ab6bSSebastien Roy *new_icmp = *icmp; 21232b24ab6bSSebastien Roy new_icmp->icmph_checksum = 0; 21242b24ab6bSSebastien Roy new_icmp->icmph_checksum = IP_CSUM(icmperr_mp, sizeof (ipha_t), 0); 21252b24ab6bSSebastien Roy 2126bd670b35SErik Nordmark bzero(&ixas, sizeof (ixas)); 2127bd670b35SErik Nordmark ixas.ixa_flags = IXAF_BASIC_SIMPLE_V4; 21283e87ae12SSowmini Varadhan if (new_ipha->ipha_src == INADDR_ANY) { 21293e87ae12SSowmini Varadhan ixas.ixa_flags &= ~IXAF_VERIFY_SOURCE; 2130bd670b35SErik Nordmark ixas.ixa_flags |= IXAF_SET_SOURCE; 21313e87ae12SSowmini Varadhan } 2132bd670b35SErik Nordmark 2133bd670b35SErik Nordmark ixas.ixa_zoneid = IPCL_ZONEID(connp); 2134bd670b35SErik Nordmark ixas.ixa_ipst = connp->conn_netstack->netstack_ip; 2135bd670b35SErik Nordmark ixas.ixa_cred = connp->conn_cred; 2136bd670b35SErik Nordmark ixas.ixa_cpid = NOPID; 2137bd670b35SErik Nordmark if (is_system_labeled()) 2138bd670b35SErik Nordmark ixas.ixa_tsl = tsl; 2139bd670b35SErik Nordmark 2140bd670b35SErik Nordmark ixas.ixa_ifindex = 0; 2141bd670b35SErik Nordmark ixas.ixa_multicast_ttl = IP_DEFAULT_MULTICAST_TTL; 2142bd670b35SErik Nordmark 2143bd670b35SErik Nordmark (void) ip_output_simple(icmperr_mp, &ixas); 2144bd670b35SErik Nordmark ixa_cleanup(&ixas); 21452b24ab6bSSebastien Roy } 21462b24ab6bSSebastien Roy 21472b24ab6bSSebastien Roy static void 2148bd670b35SErik Nordmark iptun_sendicmp_v6(iptun_t *iptun, icmp6_t *icmp6, ip6_t *orig_ip6h, mblk_t *mp, 2149bd670b35SErik Nordmark ts_label_t *tsl) 21502b24ab6bSSebastien Roy { 21512b24ab6bSSebastien Roy size_t orig_pktsize, hdrs_size; 21522b24ab6bSSebastien Roy mblk_t *icmp6err_mp; 21532b24ab6bSSebastien Roy ip6_t *new_ip6h; 21542b24ab6bSSebastien Roy icmp6_t *new_icmp6; 2155bd670b35SErik Nordmark ip_xmit_attr_t ixas; 2156bd670b35SErik Nordmark conn_t *connp = iptun->iptun_connp; 21572b24ab6bSSebastien Roy 21582b24ab6bSSebastien Roy orig_pktsize = msgdsize(mp); 21592b24ab6bSSebastien Roy hdrs_size = sizeof (ip6_t) + sizeof (icmp6_t); 21602b24ab6bSSebastien Roy if ((icmp6err_mp = iptun_build_icmperr(hdrs_size, mp)) == NULL) { 21612b24ab6bSSebastien Roy iptun_drop_pkt(mp, &iptun->iptun_noxmtbuf); 21622b24ab6bSSebastien Roy return; 21632b24ab6bSSebastien Roy } 21642b24ab6bSSebastien Roy 21652b24ab6bSSebastien Roy new_ip6h = (ip6_t *)icmp6err_mp->b_rptr; 21662b24ab6bSSebastien Roy new_icmp6 = (icmp6_t *)(new_ip6h + 1); 21672b24ab6bSSebastien Roy 21682b24ab6bSSebastien Roy new_ip6h->ip6_vcf = orig_ip6h->ip6_vcf; 21692b24ab6bSSebastien Roy new_ip6h->ip6_plen = htons(sizeof (icmp6_t) + orig_pktsize); 21702b24ab6bSSebastien Roy new_ip6h->ip6_hops = orig_ip6h->ip6_hops; 21712b24ab6bSSebastien Roy new_ip6h->ip6_nxt = IPPROTO_ICMPV6; 21722b24ab6bSSebastien Roy new_ip6h->ip6_src = orig_ip6h->ip6_dst; 21732b24ab6bSSebastien Roy new_ip6h->ip6_dst = orig_ip6h->ip6_src; 21742b24ab6bSSebastien Roy 21752b24ab6bSSebastien Roy *new_icmp6 = *icmp6; 2176bd670b35SErik Nordmark /* The checksum is calculated in ip_output_simple and friends. */ 21772b24ab6bSSebastien Roy new_icmp6->icmp6_cksum = new_ip6h->ip6_plen; 21782b24ab6bSSebastien Roy 2179bd670b35SErik Nordmark bzero(&ixas, sizeof (ixas)); 2180bd670b35SErik Nordmark ixas.ixa_flags = IXAF_BASIC_SIMPLE_V6; 21813e87ae12SSowmini Varadhan if (IN6_IS_ADDR_UNSPECIFIED(&new_ip6h->ip6_src)) { 21823e87ae12SSowmini Varadhan ixas.ixa_flags &= ~IXAF_VERIFY_SOURCE; 2183bd670b35SErik Nordmark ixas.ixa_flags |= IXAF_SET_SOURCE; 21843e87ae12SSowmini Varadhan } 2185bd670b35SErik Nordmark 2186bd670b35SErik Nordmark ixas.ixa_zoneid = IPCL_ZONEID(connp); 2187bd670b35SErik Nordmark ixas.ixa_ipst = connp->conn_netstack->netstack_ip; 2188bd670b35SErik Nordmark ixas.ixa_cred = connp->conn_cred; 2189bd670b35SErik Nordmark ixas.ixa_cpid = NOPID; 2190bd670b35SErik Nordmark if (is_system_labeled()) 2191bd670b35SErik Nordmark ixas.ixa_tsl = tsl; 2192bd670b35SErik Nordmark 2193bd670b35SErik Nordmark ixas.ixa_ifindex = 0; 2194bd670b35SErik Nordmark ixas.ixa_multicast_ttl = IP_DEFAULT_MULTICAST_TTL; 2195bd670b35SErik Nordmark 2196bd670b35SErik Nordmark (void) ip_output_simple(icmp6err_mp, &ixas); 2197bd670b35SErik Nordmark ixa_cleanup(&ixas); 21982b24ab6bSSebastien Roy } 21992b24ab6bSSebastien Roy 22002b24ab6bSSebastien Roy static void 22012b24ab6bSSebastien Roy iptun_icmp_error_v4(iptun_t *iptun, ipha_t *orig_ipha, mblk_t *mp, 2202bd670b35SErik Nordmark uint8_t type, uint8_t code, ts_label_t *tsl) 22032b24ab6bSSebastien Roy { 22042b24ab6bSSebastien Roy icmph_t icmp; 22052b24ab6bSSebastien Roy 22062b24ab6bSSebastien Roy bzero(&icmp, sizeof (icmp)); 22072b24ab6bSSebastien Roy icmp.icmph_type = type; 22082b24ab6bSSebastien Roy icmp.icmph_code = code; 22092b24ab6bSSebastien Roy 2210bd670b35SErik Nordmark iptun_sendicmp_v4(iptun, &icmp, orig_ipha, mp, tsl); 22112b24ab6bSSebastien Roy } 22122b24ab6bSSebastien Roy 22132b24ab6bSSebastien Roy static void 22142b24ab6bSSebastien Roy iptun_icmp_fragneeded_v4(iptun_t *iptun, uint32_t newmtu, ipha_t *orig_ipha, 2215bd670b35SErik Nordmark mblk_t *mp, ts_label_t *tsl) 22162b24ab6bSSebastien Roy { 22172b24ab6bSSebastien Roy icmph_t icmp; 22182b24ab6bSSebastien Roy 22192b24ab6bSSebastien Roy icmp.icmph_type = ICMP_DEST_UNREACHABLE; 22202b24ab6bSSebastien Roy icmp.icmph_code = ICMP_FRAGMENTATION_NEEDED; 22212b24ab6bSSebastien Roy icmp.icmph_du_zero = 0; 22222b24ab6bSSebastien Roy icmp.icmph_du_mtu = htons(newmtu); 22232b24ab6bSSebastien Roy 2224bd670b35SErik Nordmark iptun_sendicmp_v4(iptun, &icmp, orig_ipha, mp, tsl); 22252b24ab6bSSebastien Roy } 22262b24ab6bSSebastien Roy 22272b24ab6bSSebastien Roy static void 22282b24ab6bSSebastien Roy iptun_icmp_error_v6(iptun_t *iptun, ip6_t *orig_ip6h, mblk_t *mp, 2229bd670b35SErik Nordmark uint8_t type, uint8_t code, uint32_t offset, ts_label_t *tsl) 22302b24ab6bSSebastien Roy { 22312b24ab6bSSebastien Roy icmp6_t icmp6; 22322b24ab6bSSebastien Roy 22332b24ab6bSSebastien Roy bzero(&icmp6, sizeof (icmp6)); 22342b24ab6bSSebastien Roy icmp6.icmp6_type = type; 22352b24ab6bSSebastien Roy icmp6.icmp6_code = code; 22362b24ab6bSSebastien Roy if (type == ICMP6_PARAM_PROB) 22372b24ab6bSSebastien Roy icmp6.icmp6_pptr = htonl(offset); 22382b24ab6bSSebastien Roy 2239bd670b35SErik Nordmark iptun_sendicmp_v6(iptun, &icmp6, orig_ip6h, mp, tsl); 22402b24ab6bSSebastien Roy } 22412b24ab6bSSebastien Roy 22422b24ab6bSSebastien Roy static void 22432b24ab6bSSebastien Roy iptun_icmp_toobig_v6(iptun_t *iptun, uint32_t newmtu, ip6_t *orig_ip6h, 2244bd670b35SErik Nordmark mblk_t *mp, ts_label_t *tsl) 22452b24ab6bSSebastien Roy { 22462b24ab6bSSebastien Roy icmp6_t icmp6; 22472b24ab6bSSebastien Roy 22482b24ab6bSSebastien Roy icmp6.icmp6_type = ICMP6_PACKET_TOO_BIG; 22492b24ab6bSSebastien Roy icmp6.icmp6_code = 0; 22502b24ab6bSSebastien Roy icmp6.icmp6_mtu = htonl(newmtu); 22512b24ab6bSSebastien Roy 2252bd670b35SErik Nordmark iptun_sendicmp_v6(iptun, &icmp6, orig_ip6h, mp, tsl); 22532b24ab6bSSebastien Roy } 22542b24ab6bSSebastien Roy 22552b24ab6bSSebastien Roy /* 22562b24ab6bSSebastien Roy * Determines if the packet pointed to by ipha or ip6h is an ICMP error. The 22572b24ab6bSSebastien Roy * mp argument is only used to do bounds checking. 22582b24ab6bSSebastien Roy */ 22592b24ab6bSSebastien Roy static boolean_t 22602b24ab6bSSebastien Roy is_icmp_error(mblk_t *mp, ipha_t *ipha, ip6_t *ip6h) 22612b24ab6bSSebastien Roy { 22622b24ab6bSSebastien Roy uint16_t hlen; 22632b24ab6bSSebastien Roy 22642b24ab6bSSebastien Roy if (ipha != NULL) { 22652b24ab6bSSebastien Roy icmph_t *icmph; 22662b24ab6bSSebastien Roy 22672b24ab6bSSebastien Roy ASSERT(ip6h == NULL); 22682b24ab6bSSebastien Roy if (ipha->ipha_protocol != IPPROTO_ICMP) 22692b24ab6bSSebastien Roy return (B_FALSE); 22702b24ab6bSSebastien Roy 22712b24ab6bSSebastien Roy hlen = IPH_HDR_LENGTH(ipha); 22722b24ab6bSSebastien Roy icmph = (icmph_t *)((uint8_t *)ipha + hlen); 22732b24ab6bSSebastien Roy return (ICMP_IS_ERROR(icmph->icmph_type) || 22742b24ab6bSSebastien Roy icmph->icmph_type == ICMP_REDIRECT); 22752b24ab6bSSebastien Roy } else { 22762b24ab6bSSebastien Roy icmp6_t *icmp6; 22772b24ab6bSSebastien Roy uint8_t *nexthdrp; 22782b24ab6bSSebastien Roy 22792b24ab6bSSebastien Roy ASSERT(ip6h != NULL); 22802b24ab6bSSebastien Roy if (!ip_hdr_length_nexthdr_v6(mp, ip6h, &hlen, &nexthdrp) || 22812b24ab6bSSebastien Roy *nexthdrp != IPPROTO_ICMPV6) { 22822b24ab6bSSebastien Roy return (B_FALSE); 22832b24ab6bSSebastien Roy } 22842b24ab6bSSebastien Roy 22852b24ab6bSSebastien Roy icmp6 = (icmp6_t *)((uint8_t *)ip6h + hlen); 22862b24ab6bSSebastien Roy return (ICMP6_IS_ERROR(icmp6->icmp6_type) || 22872b24ab6bSSebastien Roy icmp6->icmp6_type == ND_REDIRECT); 22882b24ab6bSSebastien Roy } 22892b24ab6bSSebastien Roy } 22902b24ab6bSSebastien Roy 22912b24ab6bSSebastien Roy /* 22922b24ab6bSSebastien Roy * Find inner and outer IP headers from a tunneled packet as setup for calls 22932b24ab6bSSebastien Roy * into ipsec_tun_{in,out}bound(). 2294bd670b35SErik Nordmark * Note that we need to allow the outer header to be in a separate mblk from 2295bd670b35SErik Nordmark * the inner header. 2296bd670b35SErik Nordmark * If the caller knows the outer_hlen, the caller passes it in. Otherwise zero. 22972b24ab6bSSebastien Roy */ 22982b24ab6bSSebastien Roy static size_t 2299bd670b35SErik Nordmark iptun_find_headers(mblk_t *mp, size_t outer_hlen, ipha_t **outer4, 2300bd670b35SErik Nordmark ipha_t **inner4, ip6_t **outer6, ip6_t **inner6) 23012b24ab6bSSebastien Roy { 23022b24ab6bSSebastien Roy ipha_t *ipha; 23032b24ab6bSSebastien Roy size_t first_mblkl = MBLKL(mp); 23042b24ab6bSSebastien Roy mblk_t *inner_mp; 23052b24ab6bSSebastien Roy 23062b24ab6bSSebastien Roy /* 23072b24ab6bSSebastien Roy * Don't bother handling packets that don't have a full IP header in 23082b24ab6bSSebastien Roy * the fist mblk. For the input path, the ip module ensures that this 23092b24ab6bSSebastien Roy * won't happen, and on the output path, the IP tunneling MAC-type 23102b24ab6bSSebastien Roy * plugins ensure that this also won't happen. 23112b24ab6bSSebastien Roy */ 23122b24ab6bSSebastien Roy if (first_mblkl < sizeof (ipha_t)) 23132b24ab6bSSebastien Roy return (0); 23142b24ab6bSSebastien Roy ipha = (ipha_t *)(mp->b_rptr); 23152b24ab6bSSebastien Roy switch (IPH_HDR_VERSION(ipha)) { 23162b24ab6bSSebastien Roy case IPV4_VERSION: 23172b24ab6bSSebastien Roy *outer4 = ipha; 23182b24ab6bSSebastien Roy *outer6 = NULL; 2319bd670b35SErik Nordmark if (outer_hlen == 0) 23202b24ab6bSSebastien Roy outer_hlen = IPH_HDR_LENGTH(ipha); 23212b24ab6bSSebastien Roy break; 23222b24ab6bSSebastien Roy case IPV6_VERSION: 23232b24ab6bSSebastien Roy *outer4 = NULL; 23242b24ab6bSSebastien Roy *outer6 = (ip6_t *)ipha; 2325bd670b35SErik Nordmark if (outer_hlen == 0) 23262b24ab6bSSebastien Roy outer_hlen = ip_hdr_length_v6(mp, (ip6_t *)ipha); 23272b24ab6bSSebastien Roy break; 23282b24ab6bSSebastien Roy default: 23292b24ab6bSSebastien Roy return (0); 23302b24ab6bSSebastien Roy } 23312b24ab6bSSebastien Roy 23322b24ab6bSSebastien Roy if (first_mblkl < outer_hlen || 23332b24ab6bSSebastien Roy (first_mblkl == outer_hlen && mp->b_cont == NULL)) 23342b24ab6bSSebastien Roy return (0); 23352b24ab6bSSebastien Roy 23362b24ab6bSSebastien Roy /* 23372b24ab6bSSebastien Roy * We don't bother doing a pullup here since the outer header will 23382b24ab6bSSebastien Roy * just get stripped off soon on input anyway. We just want to ensure 23392b24ab6bSSebastien Roy * that the inner* pointer points to a full header. 23402b24ab6bSSebastien Roy */ 23412b24ab6bSSebastien Roy if (first_mblkl == outer_hlen) { 23422b24ab6bSSebastien Roy inner_mp = mp->b_cont; 23432b24ab6bSSebastien Roy ipha = (ipha_t *)inner_mp->b_rptr; 23442b24ab6bSSebastien Roy } else { 23452b24ab6bSSebastien Roy inner_mp = mp; 23462b24ab6bSSebastien Roy ipha = (ipha_t *)(mp->b_rptr + outer_hlen); 23472b24ab6bSSebastien Roy } 23482b24ab6bSSebastien Roy switch (IPH_HDR_VERSION(ipha)) { 23492b24ab6bSSebastien Roy case IPV4_VERSION: 23502b24ab6bSSebastien Roy if (inner_mp->b_wptr - (uint8_t *)ipha < sizeof (ipha_t)) 23512b24ab6bSSebastien Roy return (0); 23522b24ab6bSSebastien Roy *inner4 = ipha; 23532b24ab6bSSebastien Roy *inner6 = NULL; 23542b24ab6bSSebastien Roy break; 23552b24ab6bSSebastien Roy case IPV6_VERSION: 23562b24ab6bSSebastien Roy if (inner_mp->b_wptr - (uint8_t *)ipha < sizeof (ip6_t)) 23572b24ab6bSSebastien Roy return (0); 23582b24ab6bSSebastien Roy *inner4 = NULL; 23592b24ab6bSSebastien Roy *inner6 = (ip6_t *)ipha; 23602b24ab6bSSebastien Roy break; 23612b24ab6bSSebastien Roy default: 23622b24ab6bSSebastien Roy return (0); 23632b24ab6bSSebastien Roy } 23642b24ab6bSSebastien Roy 23652b24ab6bSSebastien Roy return (outer_hlen); 23662b24ab6bSSebastien Roy } 23672b24ab6bSSebastien Roy 23682b24ab6bSSebastien Roy /* 23692b24ab6bSSebastien Roy * Received ICMP error in response to an X over IPv4 packet that we 23702b24ab6bSSebastien Roy * transmitted. 23712b24ab6bSSebastien Roy * 23722b24ab6bSSebastien Roy * NOTE: "outer" refers to what's inside the ICMP payload. We will get one of 23732b24ab6bSSebastien Roy * the following: 23742b24ab6bSSebastien Roy * 23752b24ab6bSSebastien Roy * [IPv4(0)][ICMPv4][IPv4(1)][IPv4(2)][ULP] 23762b24ab6bSSebastien Roy * 23772b24ab6bSSebastien Roy * or 23782b24ab6bSSebastien Roy * 23792b24ab6bSSebastien Roy * [IPv4(0)][ICMPv4][IPv4(1)][IPv6][ULP] 23802b24ab6bSSebastien Roy * 23812b24ab6bSSebastien Roy * And "outer4" will get set to IPv4(1), and inner[46] will correspond to 23822b24ab6bSSebastien Roy * whatever the very-inner packet is (IPv4(2) or IPv6). 23832b24ab6bSSebastien Roy */ 23842b24ab6bSSebastien Roy static void 2385bd670b35SErik Nordmark iptun_input_icmp_v4(iptun_t *iptun, mblk_t *data_mp, icmph_t *icmph, 2386bd670b35SErik Nordmark ip_recv_attr_t *ira) 23872b24ab6bSSebastien Roy { 23882b24ab6bSSebastien Roy uint8_t *orig; 23892b24ab6bSSebastien Roy ipha_t *outer4, *inner4; 23902b24ab6bSSebastien Roy ip6_t *outer6, *inner6; 23912b24ab6bSSebastien Roy int outer_hlen; 23922b24ab6bSSebastien Roy uint8_t type, code; 23932b24ab6bSSebastien Roy 23942b24ab6bSSebastien Roy ASSERT(data_mp->b_cont == NULL); 23952b24ab6bSSebastien Roy /* 23962b24ab6bSSebastien Roy * Temporarily move b_rptr forward so that iptun_find_headers() can 23972b24ab6bSSebastien Roy * find headers in the ICMP packet payload. 23982b24ab6bSSebastien Roy */ 23992b24ab6bSSebastien Roy orig = data_mp->b_rptr; 24002b24ab6bSSebastien Roy data_mp->b_rptr = (uint8_t *)(icmph + 1); 24012b24ab6bSSebastien Roy /* 24022b24ab6bSSebastien Roy * The ip module ensures that ICMP errors contain at least the 24032b24ab6bSSebastien Roy * original IP header (otherwise, the error would never have made it 24042b24ab6bSSebastien Roy * here). 24052b24ab6bSSebastien Roy */ 24062b24ab6bSSebastien Roy ASSERT(MBLKL(data_mp) >= 0); 2407bd670b35SErik Nordmark outer_hlen = iptun_find_headers(data_mp, 0, &outer4, &inner4, &outer6, 24082b24ab6bSSebastien Roy &inner6); 24092b24ab6bSSebastien Roy ASSERT(outer6 == NULL); 24102b24ab6bSSebastien Roy data_mp->b_rptr = orig; 24112b24ab6bSSebastien Roy if (outer_hlen == 0) { 2412bd670b35SErik Nordmark iptun_drop_pkt(data_mp, &iptun->iptun_ierrors); 24132b24ab6bSSebastien Roy return; 24142b24ab6bSSebastien Roy } 24152b24ab6bSSebastien Roy 24162b24ab6bSSebastien Roy /* Only ICMP errors due to tunneled packets should reach here. */ 24172b24ab6bSSebastien Roy ASSERT(outer4->ipha_protocol == IPPROTO_ENCAP || 24182b24ab6bSSebastien Roy outer4->ipha_protocol == IPPROTO_IPV6); 24192b24ab6bSSebastien Roy 2420bd670b35SErik Nordmark data_mp = ipsec_tun_inbound(ira, data_mp, iptun->iptun_itp, 2421bd670b35SErik Nordmark inner4, inner6, outer4, outer6, -outer_hlen, iptun->iptun_ns); 2422bd670b35SErik Nordmark if (data_mp == NULL) { 24232b24ab6bSSebastien Roy /* Callee did all of the freeing. */ 24242b24ab6bSSebastien Roy atomic_inc_64(&iptun->iptun_ierrors); 24252b24ab6bSSebastien Roy return; 24262b24ab6bSSebastien Roy } 24272b24ab6bSSebastien Roy /* We should never see reassembled fragment here. */ 24282b24ab6bSSebastien Roy ASSERT(data_mp->b_next == NULL); 24292b24ab6bSSebastien Roy 24302b24ab6bSSebastien Roy data_mp->b_rptr = (uint8_t *)outer4 + outer_hlen; 24312b24ab6bSSebastien Roy 24322b24ab6bSSebastien Roy /* 24332b24ab6bSSebastien Roy * If the original packet being transmitted was itself an ICMP error, 24342b24ab6bSSebastien Roy * then drop this packet. We don't want to generate an ICMP error in 24352b24ab6bSSebastien Roy * response to an ICMP error. 24362b24ab6bSSebastien Roy */ 24372b24ab6bSSebastien Roy if (is_icmp_error(data_mp, inner4, inner6)) { 24382b24ab6bSSebastien Roy iptun_drop_pkt(data_mp, &iptun->iptun_norcvbuf); 24392b24ab6bSSebastien Roy return; 24402b24ab6bSSebastien Roy } 24412b24ab6bSSebastien Roy 24422b24ab6bSSebastien Roy switch (icmph->icmph_type) { 24432b24ab6bSSebastien Roy case ICMP_DEST_UNREACHABLE: 24442b24ab6bSSebastien Roy type = (inner4 != NULL ? icmph->icmph_type : ICMP6_DST_UNREACH); 24452b24ab6bSSebastien Roy switch (icmph->icmph_code) { 24462b24ab6bSSebastien Roy case ICMP_FRAGMENTATION_NEEDED: { 24472b24ab6bSSebastien Roy uint32_t newmtu; 24482b24ab6bSSebastien Roy 24492b24ab6bSSebastien Roy /* 24502b24ab6bSSebastien Roy * We reconcile this with the fact that the tunnel may 24512b24ab6bSSebastien Roy * also have IPsec policy by letting iptun_update_mtu 24522b24ab6bSSebastien Roy * take care of it. 24532b24ab6bSSebastien Roy */ 2454bd670b35SErik Nordmark newmtu = iptun_update_mtu(iptun, NULL, 2455bd670b35SErik Nordmark ntohs(icmph->icmph_du_mtu)); 24562b24ab6bSSebastien Roy 24572b24ab6bSSebastien Roy if (inner4 != NULL) { 24582b24ab6bSSebastien Roy iptun_icmp_fragneeded_v4(iptun, newmtu, inner4, 2459bd670b35SErik Nordmark data_mp, ira->ira_tsl); 24602b24ab6bSSebastien Roy } else { 24612b24ab6bSSebastien Roy iptun_icmp_toobig_v6(iptun, newmtu, inner6, 2462bd670b35SErik Nordmark data_mp, ira->ira_tsl); 24632b24ab6bSSebastien Roy } 24642b24ab6bSSebastien Roy return; 24652b24ab6bSSebastien Roy } 24662b24ab6bSSebastien Roy case ICMP_DEST_NET_UNREACH_ADMIN: 24672b24ab6bSSebastien Roy case ICMP_DEST_HOST_UNREACH_ADMIN: 24682b24ab6bSSebastien Roy code = (inner4 != NULL ? ICMP_DEST_NET_UNREACH_ADMIN : 24692b24ab6bSSebastien Roy ICMP6_DST_UNREACH_ADMIN); 24702b24ab6bSSebastien Roy break; 24712b24ab6bSSebastien Roy default: 24722b24ab6bSSebastien Roy code = (inner4 != NULL ? ICMP_HOST_UNREACHABLE : 24732b24ab6bSSebastien Roy ICMP6_DST_UNREACH_ADDR); 24742b24ab6bSSebastien Roy break; 24752b24ab6bSSebastien Roy } 24762b24ab6bSSebastien Roy break; 24772b24ab6bSSebastien Roy case ICMP_TIME_EXCEEDED: 24782b24ab6bSSebastien Roy if (inner6 != NULL) { 24792b24ab6bSSebastien Roy type = ICMP6_TIME_EXCEEDED; 24802b24ab6bSSebastien Roy code = 0; 24812b24ab6bSSebastien Roy } /* else we're already set. */ 24822b24ab6bSSebastien Roy break; 24832b24ab6bSSebastien Roy case ICMP_PARAM_PROBLEM: 24842b24ab6bSSebastien Roy /* 24852b24ab6bSSebastien Roy * This is a problem with the outer header we transmitted. 24862b24ab6bSSebastien Roy * Treat this as an output error. 24872b24ab6bSSebastien Roy */ 24882b24ab6bSSebastien Roy iptun_drop_pkt(data_mp, &iptun->iptun_oerrors); 24892b24ab6bSSebastien Roy return; 24902b24ab6bSSebastien Roy default: 24912b24ab6bSSebastien Roy iptun_drop_pkt(data_mp, &iptun->iptun_norcvbuf); 24922b24ab6bSSebastien Roy return; 24932b24ab6bSSebastien Roy } 24942b24ab6bSSebastien Roy 2495bd670b35SErik Nordmark if (inner4 != NULL) { 2496bd670b35SErik Nordmark iptun_icmp_error_v4(iptun, inner4, data_mp, type, code, 2497bd670b35SErik Nordmark ira->ira_tsl); 2498bd670b35SErik Nordmark } else { 2499bd670b35SErik Nordmark iptun_icmp_error_v6(iptun, inner6, data_mp, type, code, 0, 2500bd670b35SErik Nordmark ira->ira_tsl); 2501bd670b35SErik Nordmark } 25022b24ab6bSSebastien Roy } 25032b24ab6bSSebastien Roy 25042b24ab6bSSebastien Roy /* 25052b24ab6bSSebastien Roy * Return B_TRUE if the IPv6 packet pointed to by ip6h contains a Tunnel 25062b24ab6bSSebastien Roy * Encapsulation Limit destination option. If there is one, set encaplim_ptr 25072b24ab6bSSebastien Roy * to point to the option value. 25082b24ab6bSSebastien Roy */ 25092b24ab6bSSebastien Roy static boolean_t 25102b24ab6bSSebastien Roy iptun_find_encaplimit(mblk_t *mp, ip6_t *ip6h, uint8_t **encaplim_ptr) 25112b24ab6bSSebastien Roy { 2512bd670b35SErik Nordmark ip_pkt_t pkt; 25132b24ab6bSSebastien Roy uint8_t *endptr; 25142b24ab6bSSebastien Roy ip6_dest_t *destp; 25152b24ab6bSSebastien Roy struct ip6_opt *optp; 25162b24ab6bSSebastien Roy 25172b24ab6bSSebastien Roy pkt.ipp_fields = 0; /* must be initialized */ 2518bd670b35SErik Nordmark (void) ip_find_hdr_v6(mp, ip6h, B_FALSE, &pkt, NULL); 25192b24ab6bSSebastien Roy if ((pkt.ipp_fields & IPPF_DSTOPTS) != 0) { 25202b24ab6bSSebastien Roy destp = pkt.ipp_dstopts; 2521bd670b35SErik Nordmark } else if ((pkt.ipp_fields & IPPF_RTHDRDSTOPTS) != 0) { 2522bd670b35SErik Nordmark destp = pkt.ipp_rthdrdstopts; 25232b24ab6bSSebastien Roy } else { 25242b24ab6bSSebastien Roy return (B_FALSE); 25252b24ab6bSSebastien Roy } 25262b24ab6bSSebastien Roy 25272b24ab6bSSebastien Roy endptr = (uint8_t *)destp + 8 * (destp->ip6d_len + 1); 25282b24ab6bSSebastien Roy optp = (struct ip6_opt *)(destp + 1); 25292b24ab6bSSebastien Roy while (endptr - (uint8_t *)optp > sizeof (*optp)) { 25302b24ab6bSSebastien Roy if (optp->ip6o_type == IP6OPT_TUNNEL_LIMIT) { 25312b24ab6bSSebastien Roy if ((uint8_t *)(optp + 1) >= endptr) 25322b24ab6bSSebastien Roy return (B_FALSE); 25332b24ab6bSSebastien Roy *encaplim_ptr = (uint8_t *)&optp[1]; 25342b24ab6bSSebastien Roy return (B_TRUE); 25352b24ab6bSSebastien Roy } 25362b24ab6bSSebastien Roy optp = (struct ip6_opt *)((uint8_t *)optp + optp->ip6o_len + 2); 25372b24ab6bSSebastien Roy } 25382b24ab6bSSebastien Roy return (B_FALSE); 25392b24ab6bSSebastien Roy } 25402b24ab6bSSebastien Roy 25412b24ab6bSSebastien Roy /* 25422b24ab6bSSebastien Roy * Received ICMPv6 error in response to an X over IPv6 packet that we 25432b24ab6bSSebastien Roy * transmitted. 25442b24ab6bSSebastien Roy * 25452b24ab6bSSebastien Roy * NOTE: "outer" refers to what's inside the ICMP payload. We will get one of 25462b24ab6bSSebastien Roy * the following: 25472b24ab6bSSebastien Roy * 25482b24ab6bSSebastien Roy * [IPv6(0)][ICMPv6][IPv6(1)][IPv4][ULP] 25492b24ab6bSSebastien Roy * 25502b24ab6bSSebastien Roy * or 25512b24ab6bSSebastien Roy * 25522b24ab6bSSebastien Roy * [IPv6(0)][ICMPv6][IPv6(1)][IPv6(2)][ULP] 25532b24ab6bSSebastien Roy * 25542b24ab6bSSebastien Roy * And "outer6" will get set to IPv6(1), and inner[46] will correspond to 25552b24ab6bSSebastien Roy * whatever the very-inner packet is (IPv4 or IPv6(2)). 25562b24ab6bSSebastien Roy */ 25572b24ab6bSSebastien Roy static void 2558bd670b35SErik Nordmark iptun_input_icmp_v6(iptun_t *iptun, mblk_t *data_mp, icmp6_t *icmp6h, 2559bd670b35SErik Nordmark ip_recv_attr_t *ira) 25602b24ab6bSSebastien Roy { 25612b24ab6bSSebastien Roy uint8_t *orig; 25622b24ab6bSSebastien Roy ipha_t *outer4, *inner4; 25632b24ab6bSSebastien Roy ip6_t *outer6, *inner6; 25642b24ab6bSSebastien Roy int outer_hlen; 25652b24ab6bSSebastien Roy uint8_t type, code; 25662b24ab6bSSebastien Roy 25672b24ab6bSSebastien Roy ASSERT(data_mp->b_cont == NULL); 25682b24ab6bSSebastien Roy 25692b24ab6bSSebastien Roy /* 25702b24ab6bSSebastien Roy * Temporarily move b_rptr forward so that iptun_find_headers() can 25712b24ab6bSSebastien Roy * find IP headers in the ICMP packet payload. 25722b24ab6bSSebastien Roy */ 25732b24ab6bSSebastien Roy orig = data_mp->b_rptr; 25742b24ab6bSSebastien Roy data_mp->b_rptr = (uint8_t *)(icmp6h + 1); 25752b24ab6bSSebastien Roy /* 25762b24ab6bSSebastien Roy * The ip module ensures that ICMP errors contain at least the 25772b24ab6bSSebastien Roy * original IP header (otherwise, the error would never have made it 25782b24ab6bSSebastien Roy * here). 25792b24ab6bSSebastien Roy */ 25802b24ab6bSSebastien Roy ASSERT(MBLKL(data_mp) >= 0); 2581bd670b35SErik Nordmark outer_hlen = iptun_find_headers(data_mp, 0, &outer4, &inner4, &outer6, 25822b24ab6bSSebastien Roy &inner6); 25832b24ab6bSSebastien Roy ASSERT(outer4 == NULL); 25842b24ab6bSSebastien Roy data_mp->b_rptr = orig; /* Restore r_ptr */ 25852b24ab6bSSebastien Roy if (outer_hlen == 0) { 2586bd670b35SErik Nordmark iptun_drop_pkt(data_mp, &iptun->iptun_ierrors); 25872b24ab6bSSebastien Roy return; 25882b24ab6bSSebastien Roy } 25892b24ab6bSSebastien Roy 2590bd670b35SErik Nordmark data_mp = ipsec_tun_inbound(ira, data_mp, iptun->iptun_itp, 2591bd670b35SErik Nordmark inner4, inner6, outer4, outer6, -outer_hlen, iptun->iptun_ns); 2592bd670b35SErik Nordmark if (data_mp == NULL) { 25932b24ab6bSSebastien Roy /* Callee did all of the freeing. */ 25942b24ab6bSSebastien Roy atomic_inc_64(&iptun->iptun_ierrors); 25952b24ab6bSSebastien Roy return; 25962b24ab6bSSebastien Roy } 25972b24ab6bSSebastien Roy /* We should never see reassembled fragment here. */ 25982b24ab6bSSebastien Roy ASSERT(data_mp->b_next == NULL); 25992b24ab6bSSebastien Roy 26002b24ab6bSSebastien Roy data_mp->b_rptr = (uint8_t *)outer6 + outer_hlen; 26012b24ab6bSSebastien Roy 26022b24ab6bSSebastien Roy /* 26032b24ab6bSSebastien Roy * If the original packet being transmitted was itself an ICMP error, 26042b24ab6bSSebastien Roy * then drop this packet. We don't want to generate an ICMP error in 26052b24ab6bSSebastien Roy * response to an ICMP error. 26062b24ab6bSSebastien Roy */ 26072b24ab6bSSebastien Roy if (is_icmp_error(data_mp, inner4, inner6)) { 26082b24ab6bSSebastien Roy iptun_drop_pkt(data_mp, &iptun->iptun_norcvbuf); 26092b24ab6bSSebastien Roy return; 26102b24ab6bSSebastien Roy } 26112b24ab6bSSebastien Roy 26122b24ab6bSSebastien Roy switch (icmp6h->icmp6_type) { 26132b24ab6bSSebastien Roy case ICMP6_PARAM_PROB: { 26142b24ab6bSSebastien Roy uint8_t *encaplim_ptr; 26152b24ab6bSSebastien Roy 26162b24ab6bSSebastien Roy /* 26172b24ab6bSSebastien Roy * If the ICMPv6 error points to a valid Tunnel Encapsulation 26182b24ab6bSSebastien Roy * Limit option and the limit value is 0, then fall through 26192b24ab6bSSebastien Roy * and send a host unreachable message. Otherwise, treat the 26202b24ab6bSSebastien Roy * error as an output error, as there must have been a problem 26212b24ab6bSSebastien Roy * with a packet we sent. 26222b24ab6bSSebastien Roy */ 26232b24ab6bSSebastien Roy if (!iptun_find_encaplimit(data_mp, outer6, &encaplim_ptr) || 26242b24ab6bSSebastien Roy (icmp6h->icmp6_pptr != 26252b24ab6bSSebastien Roy ((ptrdiff_t)encaplim_ptr - (ptrdiff_t)outer6)) || 26262b24ab6bSSebastien Roy *encaplim_ptr != 0) { 26272b24ab6bSSebastien Roy iptun_drop_pkt(data_mp, &iptun->iptun_oerrors); 26282b24ab6bSSebastien Roy return; 26292b24ab6bSSebastien Roy } 26302b24ab6bSSebastien Roy /* FALLTHRU */ 26312b24ab6bSSebastien Roy } 26322b24ab6bSSebastien Roy case ICMP6_TIME_EXCEEDED: 26332b24ab6bSSebastien Roy case ICMP6_DST_UNREACH: 26342b24ab6bSSebastien Roy type = (inner4 != NULL ? ICMP_DEST_UNREACHABLE : 26352b24ab6bSSebastien Roy ICMP6_DST_UNREACH); 26362b24ab6bSSebastien Roy code = (inner4 != NULL ? ICMP_HOST_UNREACHABLE : 26372b24ab6bSSebastien Roy ICMP6_DST_UNREACH_ADDR); 26382b24ab6bSSebastien Roy break; 26392b24ab6bSSebastien Roy case ICMP6_PACKET_TOO_BIG: { 26402b24ab6bSSebastien Roy uint32_t newmtu; 26412b24ab6bSSebastien Roy 26422b24ab6bSSebastien Roy /* 26432b24ab6bSSebastien Roy * We reconcile this with the fact that the tunnel may also 26442b24ab6bSSebastien Roy * have IPsec policy by letting iptun_update_mtu take care of 26452b24ab6bSSebastien Roy * it. 26462b24ab6bSSebastien Roy */ 2647bd670b35SErik Nordmark newmtu = iptun_update_mtu(iptun, NULL, 2648bd670b35SErik Nordmark ntohl(icmp6h->icmp6_mtu)); 26492b24ab6bSSebastien Roy 26502b24ab6bSSebastien Roy if (inner4 != NULL) { 26512b24ab6bSSebastien Roy iptun_icmp_fragneeded_v4(iptun, newmtu, inner4, 2652bd670b35SErik Nordmark data_mp, ira->ira_tsl); 26532b24ab6bSSebastien Roy } else { 2654bd670b35SErik Nordmark iptun_icmp_toobig_v6(iptun, newmtu, inner6, data_mp, 2655bd670b35SErik Nordmark ira->ira_tsl); 26562b24ab6bSSebastien Roy } 26572b24ab6bSSebastien Roy return; 26582b24ab6bSSebastien Roy } 26592b24ab6bSSebastien Roy default: 26602b24ab6bSSebastien Roy iptun_drop_pkt(data_mp, &iptun->iptun_norcvbuf); 26612b24ab6bSSebastien Roy return; 26622b24ab6bSSebastien Roy } 26632b24ab6bSSebastien Roy 2664bd670b35SErik Nordmark if (inner4 != NULL) { 2665bd670b35SErik Nordmark iptun_icmp_error_v4(iptun, inner4, data_mp, type, code, 2666bd670b35SErik Nordmark ira->ira_tsl); 2667bd670b35SErik Nordmark } else { 2668bd670b35SErik Nordmark iptun_icmp_error_v6(iptun, inner6, data_mp, type, code, 0, 2669bd670b35SErik Nordmark ira->ira_tsl); 2670bd670b35SErik Nordmark } 26712b24ab6bSSebastien Roy } 26722b24ab6bSSebastien Roy 2673bd670b35SErik Nordmark /* 2674bd670b35SErik Nordmark * Called as conn_recvicmp from IP for ICMP errors. 2675bd670b35SErik Nordmark */ 2676bd670b35SErik Nordmark /* ARGSUSED2 */ 26772b24ab6bSSebastien Roy static void 2678bd670b35SErik Nordmark iptun_input_icmp(void *arg, mblk_t *mp, void *arg2, ip_recv_attr_t *ira) 26792b24ab6bSSebastien Roy { 2680bd670b35SErik Nordmark conn_t *connp = arg; 2681bd670b35SErik Nordmark iptun_t *iptun = connp->conn_iptun; 26822b24ab6bSSebastien Roy mblk_t *tmpmp; 26832b24ab6bSSebastien Roy size_t hlen; 26842b24ab6bSSebastien Roy 2685bd670b35SErik Nordmark ASSERT(IPCL_IS_IPTUN(connp)); 2686bd670b35SErik Nordmark 2687bd670b35SErik Nordmark if (mp->b_cont != NULL) { 26882b24ab6bSSebastien Roy /* 26892b24ab6bSSebastien Roy * Since ICMP error processing necessitates access to bits 26902b24ab6bSSebastien Roy * that are within the ICMP error payload (the original packet 26912b24ab6bSSebastien Roy * that caused the error), pull everything up into a single 26922b24ab6bSSebastien Roy * block for convenience. 26932b24ab6bSSebastien Roy */ 2694bd670b35SErik Nordmark if ((tmpmp = msgpullup(mp, -1)) == NULL) { 2695bd670b35SErik Nordmark iptun_drop_pkt(mp, &iptun->iptun_norcvbuf); 26962b24ab6bSSebastien Roy return; 26972b24ab6bSSebastien Roy } 2698bd670b35SErik Nordmark freemsg(mp); 2699bd670b35SErik Nordmark mp = tmpmp; 27002b24ab6bSSebastien Roy } 27012b24ab6bSSebastien Roy 2702bd670b35SErik Nordmark hlen = ira->ira_ip_hdr_length; 27032b24ab6bSSebastien Roy switch (iptun->iptun_typeinfo->iti_ipvers) { 27042b24ab6bSSebastien Roy case IPV4_VERSION: 27052b24ab6bSSebastien Roy /* 27062b24ab6bSSebastien Roy * The outer IP header coming up from IP is always ipha_t 27072b24ab6bSSebastien Roy * alligned (otherwise, we would have crashed in ip). 27082b24ab6bSSebastien Roy */ 2709bd670b35SErik Nordmark iptun_input_icmp_v4(iptun, mp, (icmph_t *)(mp->b_rptr + hlen), 2710bd670b35SErik Nordmark ira); 27112b24ab6bSSebastien Roy break; 27122b24ab6bSSebastien Roy case IPV6_VERSION: 2713bd670b35SErik Nordmark iptun_input_icmp_v6(iptun, mp, (icmp6_t *)(mp->b_rptr + hlen), 2714bd670b35SErik Nordmark ira); 27152b24ab6bSSebastien Roy break; 27162b24ab6bSSebastien Roy } 27172b24ab6bSSebastien Roy } 27182b24ab6bSSebastien Roy 27192b24ab6bSSebastien Roy static boolean_t 27202b24ab6bSSebastien Roy iptun_in_6to4_ok(iptun_t *iptun, ipha_t *outer4, ip6_t *inner6) 27212b24ab6bSSebastien Roy { 27222b24ab6bSSebastien Roy ipaddr_t v4addr; 27232b24ab6bSSebastien Roy 27242b24ab6bSSebastien Roy /* 2725d501bbfeSSebastien Roy * It's possible that someone sent us an IPv4-in-IPv4 packet with the 2726d501bbfeSSebastien Roy * IPv4 address of a 6to4 tunnel as the destination. 2727d501bbfeSSebastien Roy */ 2728d501bbfeSSebastien Roy if (inner6 == NULL) 2729d501bbfeSSebastien Roy return (B_FALSE); 2730d501bbfeSSebastien Roy 2731d501bbfeSSebastien Roy /* 27322b24ab6bSSebastien Roy * Make sure that the IPv6 destination is within the site that this 27332b24ab6bSSebastien Roy * 6to4 tunnel is routing for. We don't want people bouncing random 27342b24ab6bSSebastien Roy * tunneled IPv6 packets through this 6to4 router. 27352b24ab6bSSebastien Roy */ 27362b24ab6bSSebastien Roy IN6_6TO4_TO_V4ADDR(&inner6->ip6_dst, (struct in_addr *)&v4addr); 27372b24ab6bSSebastien Roy if (outer4->ipha_dst != v4addr) 27382b24ab6bSSebastien Roy return (B_FALSE); 27392b24ab6bSSebastien Roy 27402b24ab6bSSebastien Roy if (IN6_IS_ADDR_6TO4(&inner6->ip6_src)) { 27412b24ab6bSSebastien Roy /* 27422b24ab6bSSebastien Roy * Section 9 of RFC 3056 (security considerations) suggests 27432b24ab6bSSebastien Roy * that when a packet is from a 6to4 site (i.e., it's not a 27442b24ab6bSSebastien Roy * global address being forwarded froma relay router), make 27452b24ab6bSSebastien Roy * sure that the packet was tunneled by that site's 6to4 27462b24ab6bSSebastien Roy * router. 27472b24ab6bSSebastien Roy */ 27482b24ab6bSSebastien Roy IN6_6TO4_TO_V4ADDR(&inner6->ip6_src, (struct in_addr *)&v4addr); 27492b24ab6bSSebastien Roy if (outer4->ipha_src != v4addr) 27502b24ab6bSSebastien Roy return (B_FALSE); 27512b24ab6bSSebastien Roy } else { 27522b24ab6bSSebastien Roy /* 27532b24ab6bSSebastien Roy * Only accept packets from a relay router if we've configured 27542b24ab6bSSebastien Roy * outbound relay router functionality. 27552b24ab6bSSebastien Roy */ 27562b24ab6bSSebastien Roy if (iptun->iptun_iptuns->iptuns_relay_rtr_addr == INADDR_ANY) 27572b24ab6bSSebastien Roy return (B_FALSE); 27582b24ab6bSSebastien Roy } 27592b24ab6bSSebastien Roy 27602b24ab6bSSebastien Roy return (B_TRUE); 27612b24ab6bSSebastien Roy } 27622b24ab6bSSebastien Roy 27632b24ab6bSSebastien Roy /* 27642b24ab6bSSebastien Roy * Input function for everything that comes up from the ip module below us. 27652b24ab6bSSebastien Roy * This is called directly from the ip module via connp->conn_recv(). 27662b24ab6bSSebastien Roy * 2767bd670b35SErik Nordmark * We receive M_DATA messages with IP-in-IP tunneled packets. 27682b24ab6bSSebastien Roy */ 2769bd670b35SErik Nordmark /* ARGSUSED2 */ 27702b24ab6bSSebastien Roy static void 2771bd670b35SErik Nordmark iptun_input(void *arg, mblk_t *data_mp, void *arg2, ip_recv_attr_t *ira) 27722b24ab6bSSebastien Roy { 27732b24ab6bSSebastien Roy conn_t *connp = arg; 27742b24ab6bSSebastien Roy iptun_t *iptun = connp->conn_iptun; 27752b24ab6bSSebastien Roy int outer_hlen; 27762b24ab6bSSebastien Roy ipha_t *outer4, *inner4; 27772b24ab6bSSebastien Roy ip6_t *outer6, *inner6; 27782b24ab6bSSebastien Roy 27792b24ab6bSSebastien Roy ASSERT(IPCL_IS_IPTUN(connp)); 2780bd670b35SErik Nordmark ASSERT(DB_TYPE(data_mp) == M_DATA); 27812b24ab6bSSebastien Roy 2782bd670b35SErik Nordmark outer_hlen = iptun_find_headers(data_mp, ira->ira_ip_hdr_length, 2783bd670b35SErik Nordmark &outer4, &inner4, &outer6, &inner6); 2784bd670b35SErik Nordmark if (outer_hlen == 0) 27852b24ab6bSSebastien Roy goto drop; 27862b24ab6bSSebastien Roy 27872b24ab6bSSebastien Roy /* 27882b24ab6bSSebastien Roy * If the system is labeled, we call tsol_check_dest() on the packet 27892b24ab6bSSebastien Roy * destination (our local tunnel address) to ensure that the packet as 27902b24ab6bSSebastien Roy * labeled should be allowed to be sent to us. We don't need to call 27912b24ab6bSSebastien Roy * the more involved tsol_receive_local() since the tunnel link itself 27922b24ab6bSSebastien Roy * cannot be assigned to shared-stack non-global zones. 27932b24ab6bSSebastien Roy */ 2794bd670b35SErik Nordmark if (ira->ira_flags & IRAF_SYSTEM_LABELED) { 2795bd670b35SErik Nordmark if (ira->ira_tsl == NULL) 27962b24ab6bSSebastien Roy goto drop; 2797bd670b35SErik Nordmark if (tsol_check_dest(ira->ira_tsl, (outer4 != NULL ? 27982b24ab6bSSebastien Roy (void *)&outer4->ipha_dst : (void *)&outer6->ip6_dst), 27992b24ab6bSSebastien Roy (outer4 != NULL ? IPV4_VERSION : IPV6_VERSION), 2800bd670b35SErik Nordmark CONN_MAC_DEFAULT, B_FALSE, NULL) != 0) 28012b24ab6bSSebastien Roy goto drop; 28022b24ab6bSSebastien Roy } 28032b24ab6bSSebastien Roy 2804bd670b35SErik Nordmark data_mp = ipsec_tun_inbound(ira, data_mp, iptun->iptun_itp, 2805bd670b35SErik Nordmark inner4, inner6, outer4, outer6, outer_hlen, iptun->iptun_ns); 2806bd670b35SErik Nordmark if (data_mp == NULL) { 28072b24ab6bSSebastien Roy /* Callee did all of the freeing. */ 28082b24ab6bSSebastien Roy return; 28092b24ab6bSSebastien Roy } 28102b24ab6bSSebastien Roy 28112b24ab6bSSebastien Roy if (iptun->iptun_typeinfo->iti_type == IPTUN_TYPE_6TO4 && 28122b24ab6bSSebastien Roy !iptun_in_6to4_ok(iptun, outer4, inner6)) 28132b24ab6bSSebastien Roy goto drop; 28142b24ab6bSSebastien Roy 28152b24ab6bSSebastien Roy /* 28162b24ab6bSSebastien Roy * We need to statistically account for each packet individually, so 28172b24ab6bSSebastien Roy * we might as well split up any b_next chains here. 28182b24ab6bSSebastien Roy */ 28192b24ab6bSSebastien Roy do { 2820bd670b35SErik Nordmark mblk_t *mp; 2821bd670b35SErik Nordmark 28222b24ab6bSSebastien Roy mp = data_mp->b_next; 28232b24ab6bSSebastien Roy data_mp->b_next = NULL; 28242b24ab6bSSebastien Roy 28252b24ab6bSSebastien Roy atomic_inc_64(&iptun->iptun_ipackets); 28262b24ab6bSSebastien Roy atomic_add_64(&iptun->iptun_rbytes, msgdsize(data_mp)); 28272b24ab6bSSebastien Roy mac_rx(iptun->iptun_mh, NULL, data_mp); 28282b24ab6bSSebastien Roy 28292b24ab6bSSebastien Roy data_mp = mp; 28302b24ab6bSSebastien Roy } while (data_mp != NULL); 28312b24ab6bSSebastien Roy return; 28322b24ab6bSSebastien Roy drop: 2833bd670b35SErik Nordmark iptun_drop_pkt(data_mp, &iptun->iptun_ierrors); 28342b24ab6bSSebastien Roy } 28352b24ab6bSSebastien Roy 28362b24ab6bSSebastien Roy /* 28372b24ab6bSSebastien Roy * Do 6to4-specific header-processing on output. Return B_TRUE if the packet 28382b24ab6bSSebastien Roy * was processed without issue, or B_FALSE if the packet had issues and should 28392b24ab6bSSebastien Roy * be dropped. 28402b24ab6bSSebastien Roy */ 28412b24ab6bSSebastien Roy static boolean_t 28422b24ab6bSSebastien Roy iptun_out_process_6to4(iptun_t *iptun, ipha_t *outer4, ip6_t *inner6) 28432b24ab6bSSebastien Roy { 28442b24ab6bSSebastien Roy ipaddr_t v4addr; 28452b24ab6bSSebastien Roy 28462b24ab6bSSebastien Roy /* 28472b24ab6bSSebastien Roy * IPv6 source must be a 6to4 address. This is because a conscious 28482b24ab6bSSebastien Roy * decision was made to not allow a Solaris system to be used as a 28492b24ab6bSSebastien Roy * relay router (for security reasons) when 6to4 was initially 28502b24ab6bSSebastien Roy * integrated. If this decision is ever reversed, the following check 28512b24ab6bSSebastien Roy * can be removed. 28522b24ab6bSSebastien Roy */ 28532b24ab6bSSebastien Roy if (!IN6_IS_ADDR_6TO4(&inner6->ip6_src)) 28542b24ab6bSSebastien Roy return (B_FALSE); 28552b24ab6bSSebastien Roy 28562b24ab6bSSebastien Roy /* 28572b24ab6bSSebastien Roy * RFC3056 mandates that the IPv4 source MUST be set to the IPv4 28582b24ab6bSSebastien Roy * portion of the 6to4 IPv6 source address. In other words, make sure 28592b24ab6bSSebastien Roy * that we're tunneling packets from our own 6to4 site. 28602b24ab6bSSebastien Roy */ 28612b24ab6bSSebastien Roy IN6_6TO4_TO_V4ADDR(&inner6->ip6_src, (struct in_addr *)&v4addr); 28622b24ab6bSSebastien Roy if (outer4->ipha_src != v4addr) 28632b24ab6bSSebastien Roy return (B_FALSE); 28642b24ab6bSSebastien Roy 28652b24ab6bSSebastien Roy /* 28662b24ab6bSSebastien Roy * Automatically set the destination of the outer IPv4 header as 28672b24ab6bSSebastien Roy * described in RFC3056. There are two possibilities: 28682b24ab6bSSebastien Roy * 28692b24ab6bSSebastien Roy * a. If the IPv6 destination is a 6to4 address, set the IPv4 address 28702b24ab6bSSebastien Roy * to the IPv4 portion of the 6to4 address. 28712b24ab6bSSebastien Roy * b. If the IPv6 destination is a native IPv6 address, set the IPv4 28722b24ab6bSSebastien Roy * destination to the address of a relay router. 28732b24ab6bSSebastien Roy * 28742b24ab6bSSebastien Roy * Design Note: b shouldn't be necessary here, and this is a flaw in 28752b24ab6bSSebastien Roy * the design of the 6to4relay command. Instead of setting a 6to4 28762b24ab6bSSebastien Roy * relay address in this module via an ioctl, the 6to4relay command 28772b24ab6bSSebastien Roy * could simply add a IPv6 route for native IPv6 addresses (such as a 28782b24ab6bSSebastien Roy * default route) in the forwarding table that uses a 6to4 destination 28792b24ab6bSSebastien Roy * as its next hop, and the IPv4 portion of that address could be a 28802b24ab6bSSebastien Roy * 6to4 relay address. In order for this to work, IP would have to 28812b24ab6bSSebastien Roy * resolve the next hop address, which would necessitate a link-layer 28822b24ab6bSSebastien Roy * address resolver for 6to4 links, which doesn't exist today. 28832b24ab6bSSebastien Roy * 28842b24ab6bSSebastien Roy * In fact, if a resolver existed for 6to4 links, then setting the 28852b24ab6bSSebastien Roy * IPv4 destination in the outer header could be done as part of 28862b24ab6bSSebastien Roy * link-layer address resolution and fast-path header generation, and 28872b24ab6bSSebastien Roy * not here. 28882b24ab6bSSebastien Roy */ 28892b24ab6bSSebastien Roy if (IN6_IS_ADDR_6TO4(&inner6->ip6_dst)) { 28902b24ab6bSSebastien Roy /* destination is a 6to4 router */ 28912b24ab6bSSebastien Roy IN6_6TO4_TO_V4ADDR(&inner6->ip6_dst, 28922b24ab6bSSebastien Roy (struct in_addr *)&outer4->ipha_dst); 2893bd670b35SErik Nordmark 2894bd670b35SErik Nordmark /* Reject attempts to send to INADDR_ANY */ 2895bd670b35SErik Nordmark if (outer4->ipha_dst == INADDR_ANY) 2896bd670b35SErik Nordmark return (B_FALSE); 28972b24ab6bSSebastien Roy } else { 28982b24ab6bSSebastien Roy /* 28992b24ab6bSSebastien Roy * The destination is a native IPv6 address. If output to a 29002b24ab6bSSebastien Roy * relay-router is enabled, use the relay-router's IPv4 29012b24ab6bSSebastien Roy * address as the destination. 29022b24ab6bSSebastien Roy */ 29032b24ab6bSSebastien Roy if (iptun->iptun_iptuns->iptuns_relay_rtr_addr == INADDR_ANY) 29042b24ab6bSSebastien Roy return (B_FALSE); 29052b24ab6bSSebastien Roy outer4->ipha_dst = iptun->iptun_iptuns->iptuns_relay_rtr_addr; 29062b24ab6bSSebastien Roy } 29072b24ab6bSSebastien Roy 29082b24ab6bSSebastien Roy /* 29092b24ab6bSSebastien Roy * If the outer source and destination are equal, this means that the 29102b24ab6bSSebastien Roy * 6to4 router somehow forwarded an IPv6 packet destined for its own 29112b24ab6bSSebastien Roy * 6to4 site to its 6to4 tunnel interface, which will result in this 29122b24ab6bSSebastien Roy * packet infinitely bouncing between ip and iptun. 29132b24ab6bSSebastien Roy */ 29142b24ab6bSSebastien Roy return (outer4->ipha_src != outer4->ipha_dst); 29152b24ab6bSSebastien Roy } 29162b24ab6bSSebastien Roy 29172b24ab6bSSebastien Roy /* 29182b24ab6bSSebastien Roy * Process output packets with outer IPv4 headers. Frees mp and bumps stat on 29192b24ab6bSSebastien Roy * error. 29202b24ab6bSSebastien Roy */ 29212b24ab6bSSebastien Roy static mblk_t * 29222b24ab6bSSebastien Roy iptun_out_process_ipv4(iptun_t *iptun, mblk_t *mp, ipha_t *outer4, 2923bd670b35SErik Nordmark ipha_t *inner4, ip6_t *inner6, ip_xmit_attr_t *ixa) 29242b24ab6bSSebastien Roy { 29252b24ab6bSSebastien Roy uint8_t *innerptr = (inner4 != NULL ? 29262b24ab6bSSebastien Roy (uint8_t *)inner4 : (uint8_t *)inner6); 2927bd670b35SErik Nordmark size_t minmtu = iptun->iptun_typeinfo->iti_minmtu; 29282b24ab6bSSebastien Roy 29292b24ab6bSSebastien Roy if (inner4 != NULL) { 29302b24ab6bSSebastien Roy ASSERT(outer4->ipha_protocol == IPPROTO_ENCAP); 29312b24ab6bSSebastien Roy /* 29322b24ab6bSSebastien Roy * Copy the tos from the inner IPv4 header. We mask off ECN 29332b24ab6bSSebastien Roy * bits (bits 6 and 7) because there is currently no 29342b24ab6bSSebastien Roy * tunnel-tunnel communication to determine if both sides 29352b24ab6bSSebastien Roy * support ECN. We opt for the safe choice: don't copy the 29362b24ab6bSSebastien Roy * ECN bits when doing encapsulation. 29372b24ab6bSSebastien Roy */ 29382b24ab6bSSebastien Roy outer4->ipha_type_of_service = 29392b24ab6bSSebastien Roy inner4->ipha_type_of_service & ~0x03; 29402b24ab6bSSebastien Roy } else { 29412b24ab6bSSebastien Roy ASSERT(outer4->ipha_protocol == IPPROTO_IPV6 && 29422b24ab6bSSebastien Roy inner6 != NULL); 29432b24ab6bSSebastien Roy } 2944bd670b35SErik Nordmark if (ixa->ixa_flags & IXAF_PMTU_IPV4_DF) 2945bd670b35SErik Nordmark outer4->ipha_fragment_offset_and_flags |= IPH_DF_HTONS; 2946bd670b35SErik Nordmark else 2947bd670b35SErik Nordmark outer4->ipha_fragment_offset_and_flags &= ~IPH_DF_HTONS; 29482b24ab6bSSebastien Roy 29492b24ab6bSSebastien Roy /* 29502b24ab6bSSebastien Roy * As described in section 3.2.2 of RFC4213, if the packet payload is 29512b24ab6bSSebastien Roy * less than or equal to the minimum MTU size, then we need to allow 29522b24ab6bSSebastien Roy * IPv4 to fragment the packet. The reason is that even if we end up 29532b24ab6bSSebastien Roy * receiving an ICMP frag-needed, the interface above this tunnel 29542b24ab6bSSebastien Roy * won't be allowed to drop its MTU as a result, since the packet was 29552b24ab6bSSebastien Roy * already smaller than the smallest allowable MTU for that interface. 29562b24ab6bSSebastien Roy */ 2957bd670b35SErik Nordmark if (mp->b_wptr - innerptr <= minmtu) { 29582b24ab6bSSebastien Roy outer4->ipha_fragment_offset_and_flags = 0; 2959bd670b35SErik Nordmark ixa->ixa_flags &= ~IXAF_DONTFRAG; 2960bd670b35SErik Nordmark } else if (!(ixa->ixa_flags & IXAF_PMTU_TOO_SMALL) && 2961bd670b35SErik Nordmark (iptun->iptun_typeinfo->iti_type != IPTUN_TYPE_6TO4)) { 2962bd670b35SErik Nordmark ixa->ixa_flags |= IXAF_DONTFRAG; 2963bd670b35SErik Nordmark } 29642b24ab6bSSebastien Roy 2965bd670b35SErik Nordmark ixa->ixa_ip_hdr_length = IPH_HDR_LENGTH(outer4); 2966bd670b35SErik Nordmark ixa->ixa_pktlen = msgdsize(mp); 2967bd670b35SErik Nordmark ixa->ixa_protocol = outer4->ipha_protocol; 29682b24ab6bSSebastien Roy 2969bd670b35SErik Nordmark outer4->ipha_length = htons(ixa->ixa_pktlen); 29702b24ab6bSSebastien Roy return (mp); 29712b24ab6bSSebastien Roy } 29722b24ab6bSSebastien Roy 29732b24ab6bSSebastien Roy /* 29742b24ab6bSSebastien Roy * Insert an encapsulation limit destination option in the packet provided. 29752b24ab6bSSebastien Roy * Always consumes the mp argument and returns a new mblk pointer. 29762b24ab6bSSebastien Roy */ 29772b24ab6bSSebastien Roy static mblk_t * 29782b24ab6bSSebastien Roy iptun_insert_encaplimit(iptun_t *iptun, mblk_t *mp, ip6_t *outer6, 29792b24ab6bSSebastien Roy uint8_t limit) 29802b24ab6bSSebastien Roy { 29812b24ab6bSSebastien Roy mblk_t *newmp; 29822b24ab6bSSebastien Roy iptun_ipv6hdrs_t *newouter6; 29832b24ab6bSSebastien Roy 29842b24ab6bSSebastien Roy ASSERT(outer6->ip6_nxt == IPPROTO_IPV6); 29852b24ab6bSSebastien Roy ASSERT(mp->b_cont == NULL); 29862b24ab6bSSebastien Roy 29872b24ab6bSSebastien Roy mp->b_rptr += sizeof (ip6_t); 2988bd670b35SErik Nordmark newmp = allocb(sizeof (iptun_ipv6hdrs_t) + MBLKL(mp), BPRI_MED); 29892b24ab6bSSebastien Roy if (newmp == NULL) { 29902b24ab6bSSebastien Roy iptun_drop_pkt(mp, &iptun->iptun_noxmtbuf); 29912b24ab6bSSebastien Roy return (NULL); 29922b24ab6bSSebastien Roy } 29932b24ab6bSSebastien Roy newmp->b_wptr += sizeof (iptun_ipv6hdrs_t); 29942b24ab6bSSebastien Roy /* Copy the payload (Starting with the inner IPv6 header). */ 29952b24ab6bSSebastien Roy bcopy(mp->b_rptr, newmp->b_wptr, MBLKL(mp)); 29962b24ab6bSSebastien Roy newmp->b_wptr += MBLKL(mp); 29972b24ab6bSSebastien Roy newouter6 = (iptun_ipv6hdrs_t *)newmp->b_rptr; 29982b24ab6bSSebastien Roy /* Now copy the outer IPv6 header. */ 29992b24ab6bSSebastien Roy bcopy(outer6, &newouter6->it6h_ip6h, sizeof (ip6_t)); 30002b24ab6bSSebastien Roy newouter6->it6h_ip6h.ip6_nxt = IPPROTO_DSTOPTS; 30012b24ab6bSSebastien Roy newouter6->it6h_encaplim = iptun_encaplim_init; 30022b24ab6bSSebastien Roy newouter6->it6h_encaplim.iel_destopt.ip6d_nxt = outer6->ip6_nxt; 30032b24ab6bSSebastien Roy newouter6->it6h_encaplim.iel_telopt.ip6ot_encap_limit = limit; 30042b24ab6bSSebastien Roy 30052b24ab6bSSebastien Roy /* 30062b24ab6bSSebastien Roy * The payload length will be set at the end of 30072b24ab6bSSebastien Roy * iptun_out_process_ipv6(). 30082b24ab6bSSebastien Roy */ 30092b24ab6bSSebastien Roy 30102b24ab6bSSebastien Roy freemsg(mp); 30112b24ab6bSSebastien Roy return (newmp); 30122b24ab6bSSebastien Roy } 30132b24ab6bSSebastien Roy 30142b24ab6bSSebastien Roy /* 30152b24ab6bSSebastien Roy * Process output packets with outer IPv6 headers. Frees mp and bumps stats 30162b24ab6bSSebastien Roy * on error. 30172b24ab6bSSebastien Roy */ 30182b24ab6bSSebastien Roy static mblk_t * 3019bd670b35SErik Nordmark iptun_out_process_ipv6(iptun_t *iptun, mblk_t *mp, ip6_t *outer6, 3020bd670b35SErik Nordmark ipha_t *inner4, ip6_t *inner6, ip_xmit_attr_t *ixa) 30212b24ab6bSSebastien Roy { 3022bd670b35SErik Nordmark uint8_t *innerptr = (inner4 != NULL ? 3023bd670b35SErik Nordmark (uint8_t *)inner4 : (uint8_t *)inner6); 3024bd670b35SErik Nordmark size_t minmtu = iptun->iptun_typeinfo->iti_minmtu; 30252b24ab6bSSebastien Roy uint8_t *limit, *configlimit; 30262b24ab6bSSebastien Roy uint32_t offset; 30272b24ab6bSSebastien Roy iptun_ipv6hdrs_t *v6hdrs; 30282b24ab6bSSebastien Roy 30292b24ab6bSSebastien Roy if (inner6 != NULL && iptun_find_encaplimit(mp, inner6, &limit)) { 30302b24ab6bSSebastien Roy /* 30312b24ab6bSSebastien Roy * The inner packet is an IPv6 packet which itself contains an 30322b24ab6bSSebastien Roy * encapsulation limit option. The limit variable points to 30332b24ab6bSSebastien Roy * the value in the embedded option. Process the 30342b24ab6bSSebastien Roy * encapsulation limit option as specified in RFC 2473. 30352b24ab6bSSebastien Roy * 30362b24ab6bSSebastien Roy * If limit is 0, then we've exceeded the limit and we need to 30372b24ab6bSSebastien Roy * send back an ICMPv6 parameter problem message. 30382b24ab6bSSebastien Roy * 30392b24ab6bSSebastien Roy * If limit is > 0, then we decrement it by 1 and make sure 30402b24ab6bSSebastien Roy * that the encapsulation limit option in the outer header 30412b24ab6bSSebastien Roy * reflects that (adding an option if one isn't already 30422b24ab6bSSebastien Roy * there). 30432b24ab6bSSebastien Roy */ 30442b24ab6bSSebastien Roy ASSERT(limit > mp->b_rptr && limit < mp->b_wptr); 30452b24ab6bSSebastien Roy if (*limit == 0) { 30462b24ab6bSSebastien Roy mp->b_rptr = (uint8_t *)inner6; 30472b24ab6bSSebastien Roy offset = limit - mp->b_rptr; 30482b24ab6bSSebastien Roy iptun_icmp_error_v6(iptun, inner6, mp, ICMP6_PARAM_PROB, 3049bd670b35SErik Nordmark 0, offset, ixa->ixa_tsl); 30502b24ab6bSSebastien Roy atomic_inc_64(&iptun->iptun_noxmtbuf); 30512b24ab6bSSebastien Roy return (NULL); 30522b24ab6bSSebastien Roy } 30532b24ab6bSSebastien Roy 30542b24ab6bSSebastien Roy /* 30552b24ab6bSSebastien Roy * The outer header requires an encapsulation limit option. 30562b24ab6bSSebastien Roy * If there isn't one already, add one. 30572b24ab6bSSebastien Roy */ 30582b24ab6bSSebastien Roy if (iptun->iptun_encaplimit == 0) { 30592b24ab6bSSebastien Roy if ((mp = iptun_insert_encaplimit(iptun, mp, outer6, 30602b24ab6bSSebastien Roy (*limit - 1))) == NULL) 30612b24ab6bSSebastien Roy return (NULL); 3062bd670b35SErik Nordmark v6hdrs = (iptun_ipv6hdrs_t *)mp->b_rptr; 30632b24ab6bSSebastien Roy } else { 30642b24ab6bSSebastien Roy /* 30652b24ab6bSSebastien Roy * There is an existing encapsulation limit option in 30662b24ab6bSSebastien Roy * the outer header. If the inner encapsulation limit 30672b24ab6bSSebastien Roy * is less than the configured encapsulation limit, 30682b24ab6bSSebastien Roy * update the outer encapsulation limit to reflect 30692b24ab6bSSebastien Roy * this lesser value. 30702b24ab6bSSebastien Roy */ 30712b24ab6bSSebastien Roy v6hdrs = (iptun_ipv6hdrs_t *)mp->b_rptr; 30722b24ab6bSSebastien Roy configlimit = 30732b24ab6bSSebastien Roy &v6hdrs->it6h_encaplim.iel_telopt.ip6ot_encap_limit; 30742b24ab6bSSebastien Roy if ((*limit - 1) < *configlimit) 30752b24ab6bSSebastien Roy *configlimit = (*limit - 1); 30762b24ab6bSSebastien Roy } 3077bd670b35SErik Nordmark ixa->ixa_ip_hdr_length = sizeof (iptun_ipv6hdrs_t); 3078bd670b35SErik Nordmark ixa->ixa_protocol = v6hdrs->it6h_encaplim.iel_destopt.ip6d_nxt; 3079bd670b35SErik Nordmark } else { 3080bd670b35SErik Nordmark ixa->ixa_ip_hdr_length = sizeof (ip6_t); 3081bd670b35SErik Nordmark ixa->ixa_protocol = outer6->ip6_nxt; 30822b24ab6bSSebastien Roy } 3083bd670b35SErik Nordmark /* 3084bd670b35SErik Nordmark * See iptun_output_process_ipv4() why we allow fragmentation for 3085bd670b35SErik Nordmark * small packets 3086bd670b35SErik Nordmark */ 3087bd670b35SErik Nordmark if (mp->b_wptr - innerptr <= minmtu) 3088bd670b35SErik Nordmark ixa->ixa_flags &= ~IXAF_DONTFRAG; 3089bd670b35SErik Nordmark else if (!(ixa->ixa_flags & IXAF_PMTU_TOO_SMALL)) 3090bd670b35SErik Nordmark ixa->ixa_flags |= IXAF_DONTFRAG; 30912b24ab6bSSebastien Roy 3092bd670b35SErik Nordmark ixa->ixa_pktlen = msgdsize(mp); 3093bd670b35SErik Nordmark outer6->ip6_plen = htons(ixa->ixa_pktlen - sizeof (ip6_t)); 30942b24ab6bSSebastien Roy return (mp); 30952b24ab6bSSebastien Roy } 30962b24ab6bSSebastien Roy 30972b24ab6bSSebastien Roy /* 30982b24ab6bSSebastien Roy * The IP tunneling MAC-type plugins have already done most of the header 30992b24ab6bSSebastien Roy * processing and validity checks. We are simply responsible for multiplexing 31002b24ab6bSSebastien Roy * down to the ip module below us. 31012b24ab6bSSebastien Roy */ 31022b24ab6bSSebastien Roy static void 31032b24ab6bSSebastien Roy iptun_output(iptun_t *iptun, mblk_t *mp) 31042b24ab6bSSebastien Roy { 31052b24ab6bSSebastien Roy conn_t *connp = iptun->iptun_connp; 31062b24ab6bSSebastien Roy mblk_t *newmp; 3107bd670b35SErik Nordmark int error; 3108bd670b35SErik Nordmark ip_xmit_attr_t *ixa; 31092b24ab6bSSebastien Roy 31102b24ab6bSSebastien Roy ASSERT(mp->b_datap->db_type == M_DATA); 31112b24ab6bSSebastien Roy 31122b24ab6bSSebastien Roy if (mp->b_cont != NULL) { 31132b24ab6bSSebastien Roy if ((newmp = msgpullup(mp, -1)) == NULL) { 31142b24ab6bSSebastien Roy iptun_drop_pkt(mp, &iptun->iptun_noxmtbuf); 31152b24ab6bSSebastien Roy return; 31162b24ab6bSSebastien Roy } 31172b24ab6bSSebastien Roy freemsg(mp); 31182b24ab6bSSebastien Roy mp = newmp; 31192b24ab6bSSebastien Roy } 31202b24ab6bSSebastien Roy 3121bd670b35SErik Nordmark if (iptun->iptun_typeinfo->iti_type == IPTUN_TYPE_6TO4) { 3122bd670b35SErik Nordmark iptun_output_6to4(iptun, mp); 3123bd670b35SErik Nordmark return; 3124bd670b35SErik Nordmark } 3125bd670b35SErik Nordmark 3126bd670b35SErik Nordmark if (is_system_labeled()) { 3127bd670b35SErik Nordmark /* 3128bd670b35SErik Nordmark * Since the label can be different meaning a potentially 3129bd670b35SErik Nordmark * different IRE,we always use a unique ip_xmit_attr_t. 3130bd670b35SErik Nordmark */ 3131bd670b35SErik Nordmark ixa = conn_get_ixa_exclusive(connp); 3132bd670b35SErik Nordmark } else { 3133bd670b35SErik Nordmark /* 3134bd670b35SErik Nordmark * If no other thread is using conn_ixa this just gets a 3135bd670b35SErik Nordmark * reference to conn_ixa. Otherwise we get a safe copy of 3136bd670b35SErik Nordmark * conn_ixa. 3137bd670b35SErik Nordmark */ 3138bd670b35SErik Nordmark ixa = conn_get_ixa(connp, B_FALSE); 3139bd670b35SErik Nordmark } 3140bd670b35SErik Nordmark if (ixa == NULL) { 3141bd670b35SErik Nordmark iptun_drop_pkt(mp, &iptun->iptun_oerrors); 3142bd670b35SErik Nordmark return; 3143bd670b35SErik Nordmark } 3144bd670b35SErik Nordmark 3145bd670b35SErik Nordmark /* 3146bd670b35SErik Nordmark * In case we got a safe copy of conn_ixa, then we need 3147bd670b35SErik Nordmark * to fill in any pointers in it. 3148bd670b35SErik Nordmark */ 3149bd670b35SErik Nordmark if (ixa->ixa_ire == NULL) { 3150bd670b35SErik Nordmark error = ip_attr_connect(connp, ixa, &connp->conn_saddr_v6, 3151bd670b35SErik Nordmark &connp->conn_faddr_v6, &connp->conn_faddr_v6, 0, 3152bd670b35SErik Nordmark NULL, NULL, 0); 3153bd670b35SErik Nordmark if (error != 0) { 3154bd670b35SErik Nordmark if (ixa->ixa_ire != NULL && 3155bd670b35SErik Nordmark (error == EHOSTUNREACH || error == ENETUNREACH)) { 3156bd670b35SErik Nordmark /* 3157bd670b35SErik Nordmark * Let conn_ip_output/ire_send_noroute return 3158bd670b35SErik Nordmark * the error and send any local ICMP error. 3159bd670b35SErik Nordmark */ 3160bd670b35SErik Nordmark error = 0; 3161bd670b35SErik Nordmark } else { 3162bd670b35SErik Nordmark ixa_refrele(ixa); 3163bd670b35SErik Nordmark iptun_drop_pkt(mp, &iptun->iptun_oerrors); 3164bd670b35SErik Nordmark return; 3165bd670b35SErik Nordmark } 3166bd670b35SErik Nordmark } 3167bd670b35SErik Nordmark } 3168bd670b35SErik Nordmark 3169bd670b35SErik Nordmark iptun_output_common(iptun, ixa, mp); 3170bd670b35SErik Nordmark ixa_refrele(ixa); 3171bd670b35SErik Nordmark } 3172bd670b35SErik Nordmark 3173bd670b35SErik Nordmark /* 3174bd670b35SErik Nordmark * We use an ixa based on the last destination. 3175bd670b35SErik Nordmark */ 3176bd670b35SErik Nordmark static void 3177bd670b35SErik Nordmark iptun_output_6to4(iptun_t *iptun, mblk_t *mp) 3178bd670b35SErik Nordmark { 3179bd670b35SErik Nordmark conn_t *connp = iptun->iptun_connp; 3180bd670b35SErik Nordmark ipha_t *outer4, *inner4; 3181bd670b35SErik Nordmark ip6_t *outer6, *inner6; 3182bd670b35SErik Nordmark ip_xmit_attr_t *ixa; 3183bd670b35SErik Nordmark ip_xmit_attr_t *oldixa; 3184bd670b35SErik Nordmark int error; 3185bd670b35SErik Nordmark boolean_t need_connect; 3186bd670b35SErik Nordmark in6_addr_t v6dst; 3187bd670b35SErik Nordmark 3188bd670b35SErik Nordmark ASSERT(mp->b_cont == NULL); /* Verified by iptun_output */ 3189bd670b35SErik Nordmark 3190bd670b35SErik Nordmark /* Make sure we set ipha_dst before we look at ipha_dst */ 3191bd670b35SErik Nordmark 3192bd670b35SErik Nordmark (void) iptun_find_headers(mp, 0, &outer4, &inner4, &outer6, &inner6); 3193bd670b35SErik Nordmark ASSERT(outer4 != NULL); 3194bd670b35SErik Nordmark if (!iptun_out_process_6to4(iptun, outer4, inner6)) { 3195bd670b35SErik Nordmark iptun_drop_pkt(mp, &iptun->iptun_oerrors); 3196bd670b35SErik Nordmark return; 3197bd670b35SErik Nordmark } 3198bd670b35SErik Nordmark 3199bd670b35SErik Nordmark if (is_system_labeled()) { 3200bd670b35SErik Nordmark /* 3201bd670b35SErik Nordmark * Since the label can be different meaning a potentially 3202bd670b35SErik Nordmark * different IRE,we always use a unique ip_xmit_attr_t. 3203bd670b35SErik Nordmark */ 3204bd670b35SErik Nordmark ixa = conn_get_ixa_exclusive(connp); 3205bd670b35SErik Nordmark } else { 3206bd670b35SErik Nordmark /* 3207bd670b35SErik Nordmark * If no other thread is using conn_ixa this just gets a 3208bd670b35SErik Nordmark * reference to conn_ixa. Otherwise we get a safe copy of 3209bd670b35SErik Nordmark * conn_ixa. 3210bd670b35SErik Nordmark */ 3211bd670b35SErik Nordmark ixa = conn_get_ixa(connp, B_FALSE); 3212bd670b35SErik Nordmark } 3213bd670b35SErik Nordmark if (ixa == NULL) { 3214bd670b35SErik Nordmark iptun_drop_pkt(mp, &iptun->iptun_oerrors); 3215bd670b35SErik Nordmark return; 3216bd670b35SErik Nordmark } 3217bd670b35SErik Nordmark 3218bd670b35SErik Nordmark mutex_enter(&connp->conn_lock); 3219bd670b35SErik Nordmark if (connp->conn_v4lastdst == outer4->ipha_dst) { 3220bd670b35SErik Nordmark need_connect = (ixa->ixa_ire == NULL); 3221bd670b35SErik Nordmark } else { 3222bd670b35SErik Nordmark /* In case previous destination was multirt */ 3223bd670b35SErik Nordmark ip_attr_newdst(ixa); 3224bd670b35SErik Nordmark 3225bd670b35SErik Nordmark /* 3226bd670b35SErik Nordmark * We later update conn_ixa when we update conn_v4lastdst 3227bd670b35SErik Nordmark * which enables subsequent packets to avoid redoing 3228bd670b35SErik Nordmark * ip_attr_connect 3229bd670b35SErik Nordmark */ 3230bd670b35SErik Nordmark need_connect = B_TRUE; 3231bd670b35SErik Nordmark } 3232bd670b35SErik Nordmark mutex_exit(&connp->conn_lock); 3233bd670b35SErik Nordmark 3234bd670b35SErik Nordmark /* 3235bd670b35SErik Nordmark * In case we got a safe copy of conn_ixa, or otherwise we don't 3236bd670b35SErik Nordmark * have a current ixa_ire, then we need to fill in any pointers in 3237bd670b35SErik Nordmark * the ixa. 3238bd670b35SErik Nordmark */ 3239bd670b35SErik Nordmark if (need_connect) { 3240bd670b35SErik Nordmark IN6_IPADDR_TO_V4MAPPED(outer4->ipha_dst, &v6dst); 3241bd670b35SErik Nordmark 3242bd670b35SErik Nordmark /* We handle IPsec in iptun_output_common */ 3243bd670b35SErik Nordmark error = ip_attr_connect(connp, ixa, &connp->conn_saddr_v6, 3244bd670b35SErik Nordmark &v6dst, &v6dst, 0, NULL, NULL, 0); 3245bd670b35SErik Nordmark if (error != 0) { 3246bd670b35SErik Nordmark if (ixa->ixa_ire != NULL && 3247bd670b35SErik Nordmark (error == EHOSTUNREACH || error == ENETUNREACH)) { 3248bd670b35SErik Nordmark /* 3249bd670b35SErik Nordmark * Let conn_ip_output/ire_send_noroute return 3250bd670b35SErik Nordmark * the error and send any local ICMP error. 3251bd670b35SErik Nordmark */ 3252bd670b35SErik Nordmark error = 0; 3253bd670b35SErik Nordmark } else { 3254bd670b35SErik Nordmark ixa_refrele(ixa); 3255bd670b35SErik Nordmark iptun_drop_pkt(mp, &iptun->iptun_oerrors); 3256bd670b35SErik Nordmark return; 3257bd670b35SErik Nordmark } 3258bd670b35SErik Nordmark } 3259bd670b35SErik Nordmark } 3260bd670b35SErik Nordmark 3261bd670b35SErik Nordmark iptun_output_common(iptun, ixa, mp); 3262bd670b35SErik Nordmark 3263bd670b35SErik Nordmark /* Atomically replace conn_ixa and conn_v4lastdst */ 3264bd670b35SErik Nordmark mutex_enter(&connp->conn_lock); 3265bd670b35SErik Nordmark if (connp->conn_v4lastdst != outer4->ipha_dst) { 3266bd670b35SErik Nordmark /* Remember the dst which corresponds to conn_ixa */ 3267bd670b35SErik Nordmark connp->conn_v6lastdst = v6dst; 3268bd670b35SErik Nordmark oldixa = conn_replace_ixa(connp, ixa); 3269bd670b35SErik Nordmark } else { 3270bd670b35SErik Nordmark oldixa = NULL; 3271bd670b35SErik Nordmark } 3272bd670b35SErik Nordmark mutex_exit(&connp->conn_lock); 3273bd670b35SErik Nordmark ixa_refrele(ixa); 3274bd670b35SErik Nordmark if (oldixa != NULL) 3275bd670b35SErik Nordmark ixa_refrele(oldixa); 3276bd670b35SErik Nordmark } 3277bd670b35SErik Nordmark 3278bd670b35SErik Nordmark /* 3279bd670b35SErik Nordmark * Check the destination/label. Modifies *mpp by adding/removing CIPSO. 3280bd670b35SErik Nordmark * 3281bd670b35SErik Nordmark * We get the label from the message in order to honor the 3282bd670b35SErik Nordmark * ULPs/IPs choice of label. This will be NULL for forwarded 3283bd670b35SErik Nordmark * packets, neighbor discovery packets and some others. 3284bd670b35SErik Nordmark */ 3285bd670b35SErik Nordmark static int 3286bd670b35SErik Nordmark iptun_output_check_label(mblk_t **mpp, ip_xmit_attr_t *ixa) 3287bd670b35SErik Nordmark { 3288bd670b35SErik Nordmark cred_t *cr; 3289bd670b35SErik Nordmark int adjust; 3290bd670b35SErik Nordmark int iplen; 3291bd670b35SErik Nordmark int err; 3292bd670b35SErik Nordmark ts_label_t *effective_tsl = NULL; 3293bd670b35SErik Nordmark 3294bd670b35SErik Nordmark 3295bd670b35SErik Nordmark ASSERT(is_system_labeled()); 3296bd670b35SErik Nordmark 3297bd670b35SErik Nordmark cr = msg_getcred(*mpp, NULL); 3298bd670b35SErik Nordmark if (cr == NULL) 3299bd670b35SErik Nordmark return (0); 3300bd670b35SErik Nordmark 3301bd670b35SErik Nordmark /* 3302bd670b35SErik Nordmark * We need to start with a label based on the IP/ULP above us 3303bd670b35SErik Nordmark */ 3304bd670b35SErik Nordmark ip_xmit_attr_restore_tsl(ixa, cr); 3305bd670b35SErik Nordmark 3306bd670b35SErik Nordmark /* 3307bd670b35SErik Nordmark * Need to update packet with any CIPSO option since 3308bd670b35SErik Nordmark * conn_ip_output doesn't do that. 3309bd670b35SErik Nordmark */ 3310bd670b35SErik Nordmark if (ixa->ixa_flags & IXAF_IS_IPV4) { 3311bd670b35SErik Nordmark ipha_t *ipha; 3312bd670b35SErik Nordmark 3313bd670b35SErik Nordmark ipha = (ipha_t *)(*mpp)->b_rptr; 3314bd670b35SErik Nordmark iplen = ntohs(ipha->ipha_length); 3315bd670b35SErik Nordmark err = tsol_check_label_v4(ixa->ixa_tsl, 3316bd670b35SErik Nordmark ixa->ixa_zoneid, mpp, CONN_MAC_DEFAULT, B_FALSE, 3317bd670b35SErik Nordmark ixa->ixa_ipst, &effective_tsl); 3318bd670b35SErik Nordmark if (err != 0) 3319bd670b35SErik Nordmark return (err); 3320bd670b35SErik Nordmark 3321bd670b35SErik Nordmark ipha = (ipha_t *)(*mpp)->b_rptr; 3322bd670b35SErik Nordmark adjust = (int)ntohs(ipha->ipha_length) - iplen; 3323bd670b35SErik Nordmark } else { 3324bd670b35SErik Nordmark ip6_t *ip6h; 3325bd670b35SErik Nordmark 3326bd670b35SErik Nordmark ip6h = (ip6_t *)(*mpp)->b_rptr; 3327bd670b35SErik Nordmark iplen = ntohs(ip6h->ip6_plen); 3328bd670b35SErik Nordmark 3329bd670b35SErik Nordmark err = tsol_check_label_v6(ixa->ixa_tsl, 3330bd670b35SErik Nordmark ixa->ixa_zoneid, mpp, CONN_MAC_DEFAULT, B_FALSE, 3331bd670b35SErik Nordmark ixa->ixa_ipst, &effective_tsl); 3332bd670b35SErik Nordmark if (err != 0) 3333bd670b35SErik Nordmark return (err); 3334bd670b35SErik Nordmark 3335bd670b35SErik Nordmark ip6h = (ip6_t *)(*mpp)->b_rptr; 3336bd670b35SErik Nordmark adjust = (int)ntohs(ip6h->ip6_plen) - iplen; 3337bd670b35SErik Nordmark } 3338bd670b35SErik Nordmark 3339bd670b35SErik Nordmark if (effective_tsl != NULL) { 3340bd670b35SErik Nordmark /* Update the label */ 3341bd670b35SErik Nordmark ip_xmit_attr_replace_tsl(ixa, effective_tsl); 3342bd670b35SErik Nordmark } 3343bd670b35SErik Nordmark ixa->ixa_pktlen += adjust; 3344bd670b35SErik Nordmark ixa->ixa_ip_hdr_length += adjust; 3345bd670b35SErik Nordmark return (0); 3346bd670b35SErik Nordmark } 3347bd670b35SErik Nordmark 3348bd670b35SErik Nordmark 3349bd670b35SErik Nordmark static void 3350bd670b35SErik Nordmark iptun_output_common(iptun_t *iptun, ip_xmit_attr_t *ixa, mblk_t *mp) 3351bd670b35SErik Nordmark { 3352bd670b35SErik Nordmark ipsec_tun_pol_t *itp = iptun->iptun_itp; 3353bd670b35SErik Nordmark int outer_hlen; 3354bd670b35SErik Nordmark mblk_t *newmp; 3355bd670b35SErik Nordmark ipha_t *outer4, *inner4; 3356bd670b35SErik Nordmark ip6_t *outer6, *inner6; 3357bd670b35SErik Nordmark int error; 3358bd670b35SErik Nordmark boolean_t update_pktlen; 3359bd670b35SErik Nordmark 3360bd670b35SErik Nordmark ASSERT(ixa->ixa_ire != NULL); 3361bd670b35SErik Nordmark 3362bd670b35SErik Nordmark outer_hlen = iptun_find_headers(mp, 0, &outer4, &inner4, &outer6, 3363bd670b35SErik Nordmark &inner6); 33642b24ab6bSSebastien Roy if (outer_hlen == 0) { 33652b24ab6bSSebastien Roy iptun_drop_pkt(mp, &iptun->iptun_oerrors); 33662b24ab6bSSebastien Roy return; 33672b24ab6bSSebastien Roy } 33682b24ab6bSSebastien Roy 3369d1a98e54SPaul Wernau /* Save IXAF_DONTFRAG value */ 3370d1a98e54SPaul Wernau iaflags_t dontfrag = ixa->ixa_flags & IXAF_DONTFRAG; 3371d1a98e54SPaul Wernau 33722b24ab6bSSebastien Roy /* Perform header processing. */ 3373bd670b35SErik Nordmark if (outer4 != NULL) { 3374bd670b35SErik Nordmark mp = iptun_out_process_ipv4(iptun, mp, outer4, inner4, inner6, 3375bd670b35SErik Nordmark ixa); 3376bd670b35SErik Nordmark } else { 3377bd670b35SErik Nordmark mp = iptun_out_process_ipv6(iptun, mp, outer6, inner4, inner6, 3378bd670b35SErik Nordmark ixa); 3379bd670b35SErik Nordmark } 33802b24ab6bSSebastien Roy if (mp == NULL) 33812b24ab6bSSebastien Roy return; 33822b24ab6bSSebastien Roy 33832b24ab6bSSebastien Roy /* 33842b24ab6bSSebastien Roy * Let's hope the compiler optimizes this with "branch taken". 33852b24ab6bSSebastien Roy */ 33862b24ab6bSSebastien Roy if (itp != NULL && (itp->itp_flags & ITPF_P_ACTIVE)) { 3387bd670b35SErik Nordmark /* This updates the ip_xmit_attr_t */ 3388bd670b35SErik Nordmark mp = ipsec_tun_outbound(mp, iptun, inner4, inner6, outer4, 3389bd670b35SErik Nordmark outer6, outer_hlen, ixa); 3390bd670b35SErik Nordmark if (mp == NULL) { 33912b24ab6bSSebastien Roy atomic_inc_64(&iptun->iptun_oerrors); 33922b24ab6bSSebastien Roy return; 33932b24ab6bSSebastien Roy } 3394bd670b35SErik Nordmark if (is_system_labeled()) { 3395bd670b35SErik Nordmark /* 3396bd670b35SErik Nordmark * Might change the packet by adding/removing CIPSO. 3397bd670b35SErik Nordmark * After this caller inner* and outer* and outer_hlen 3398bd670b35SErik Nordmark * might be invalid. 3399bd670b35SErik Nordmark */ 3400bd670b35SErik Nordmark error = iptun_output_check_label(&mp, ixa); 3401bd670b35SErik Nordmark if (error != 0) { 3402bd670b35SErik Nordmark ip2dbg(("label check failed (%d)\n", error)); 3403bd670b35SErik Nordmark iptun_drop_pkt(mp, &iptun->iptun_oerrors); 3404bd670b35SErik Nordmark return; 3405bd670b35SErik Nordmark } 3406bd670b35SErik Nordmark } 3407bd670b35SErik Nordmark 34082b24ab6bSSebastien Roy /* 34092b24ab6bSSebastien Roy * ipsec_tun_outbound() returns a chain of tunneled IP 34102b24ab6bSSebastien Roy * fragments linked with b_next (or a single message if the 3411bd670b35SErik Nordmark * tunneled packet wasn't a fragment). 3412bd670b35SErik Nordmark * If fragcache returned a list then we need to update 3413bd670b35SErik Nordmark * ixa_pktlen for all packets in the list. 3414bd670b35SErik Nordmark */ 3415bd670b35SErik Nordmark update_pktlen = (mp->b_next != NULL); 3416bd670b35SErik Nordmark 3417bd670b35SErik Nordmark /* 3418bd670b35SErik Nordmark * Otherwise, we're good to go. The ixa has been updated with 34192b24ab6bSSebastien Roy * instructions for outbound IPsec processing. 34202b24ab6bSSebastien Roy */ 34212b24ab6bSSebastien Roy for (newmp = mp; newmp != NULL; newmp = mp) { 3422d1a98e54SPaul Wernau size_t minmtu = iptun->iptun_typeinfo->iti_minmtu; 3423d1a98e54SPaul Wernau 34242b24ab6bSSebastien Roy atomic_inc_64(&iptun->iptun_opackets); 3425bd670b35SErik Nordmark atomic_add_64(&iptun->iptun_obytes, ixa->ixa_pktlen); 34262b24ab6bSSebastien Roy mp = mp->b_next; 34272b24ab6bSSebastien Roy newmp->b_next = NULL; 3428bd670b35SErik Nordmark 3429d1a98e54SPaul Wernau /* 3430d1a98e54SPaul Wernau * The IXAF_DONTFRAG flag is global, but there is 3431d1a98e54SPaul Wernau * a chain here. Check if we're really already 3432d1a98e54SPaul Wernau * smaller than the minimum allowed MTU and reset here 3433d1a98e54SPaul Wernau * appropriately. Otherwise one small packet can kill 3434d1a98e54SPaul Wernau * the whole chain's path mtu discovery. 3435d1a98e54SPaul Wernau * In addition, update the pktlen to the length of 3436d1a98e54SPaul Wernau * the actual packet being processed. 3437d1a98e54SPaul Wernau */ 3438d1a98e54SPaul Wernau if (update_pktlen) { 3439d1a98e54SPaul Wernau ixa->ixa_pktlen = msgdsize(newmp); 3440d1a98e54SPaul Wernau if (ixa->ixa_pktlen <= minmtu) 3441d1a98e54SPaul Wernau ixa->ixa_flags &= ~IXAF_DONTFRAG; 3442d1a98e54SPaul Wernau } 3443bd670b35SErik Nordmark 3444bd670b35SErik Nordmark atomic_inc_64(&iptun->iptun_opackets); 3445bd670b35SErik Nordmark atomic_add_64(&iptun->iptun_obytes, ixa->ixa_pktlen); 3446bd670b35SErik Nordmark 3447bd670b35SErik Nordmark error = conn_ip_output(newmp, ixa); 3448d1a98e54SPaul Wernau 3449d1a98e54SPaul Wernau /* Restore IXAF_DONTFRAG value */ 3450d1a98e54SPaul Wernau ixa->ixa_flags |= dontfrag; 3451d1a98e54SPaul Wernau 3452bd670b35SErik Nordmark if (error == EMSGSIZE) { 3453bd670b35SErik Nordmark /* IPsec policy might have changed */ 3454bd670b35SErik Nordmark (void) iptun_update_mtu(iptun, ixa, 0); 3455bd670b35SErik Nordmark } 34562b24ab6bSSebastien Roy } 34572b24ab6bSSebastien Roy } else { 34582b24ab6bSSebastien Roy /* 34592b24ab6bSSebastien Roy * The ip module will potentially apply global policy to the 34602b24ab6bSSebastien Roy * packet in its output path if there's no active tunnel 34612b24ab6bSSebastien Roy * policy. 34622b24ab6bSSebastien Roy */ 3463bd670b35SErik Nordmark ASSERT(ixa->ixa_ipsec_policy == NULL); 3464bd670b35SErik Nordmark mp = ip_output_attach_policy(mp, outer4, outer6, NULL, ixa); 3465bd670b35SErik Nordmark if (mp == NULL) { 3466bd670b35SErik Nordmark atomic_inc_64(&iptun->iptun_oerrors); 3467bd670b35SErik Nordmark return; 34682b24ab6bSSebastien Roy } 3469bd670b35SErik Nordmark if (is_system_labeled()) { 34702b24ab6bSSebastien Roy /* 3471bd670b35SErik Nordmark * Might change the packet by adding/removing CIPSO. 3472bd670b35SErik Nordmark * After this caller inner* and outer* and outer_hlen 3473bd670b35SErik Nordmark * might be invalid. 34742b24ab6bSSebastien Roy */ 3475bd670b35SErik Nordmark error = iptun_output_check_label(&mp, ixa); 3476bd670b35SErik Nordmark if (error != 0) { 3477bd670b35SErik Nordmark ip2dbg(("label check failed (%d)\n", error)); 3478bd670b35SErik Nordmark iptun_drop_pkt(mp, &iptun->iptun_oerrors); 3479bd670b35SErik Nordmark return; 3480bd670b35SErik Nordmark } 34812b24ab6bSSebastien Roy } 34822b24ab6bSSebastien Roy 3483bd670b35SErik Nordmark atomic_inc_64(&iptun->iptun_opackets); 3484bd670b35SErik Nordmark atomic_add_64(&iptun->iptun_obytes, ixa->ixa_pktlen); 3485bd670b35SErik Nordmark 3486bd670b35SErik Nordmark error = conn_ip_output(mp, ixa); 3487bd670b35SErik Nordmark if (error == EMSGSIZE) { 3488bd670b35SErik Nordmark /* IPsec policy might have changed */ 3489bd670b35SErik Nordmark (void) iptun_update_mtu(iptun, ixa, 0); 3490bd670b35SErik Nordmark } 3491bd670b35SErik Nordmark } 3492bd670b35SErik Nordmark if (ixa->ixa_flags & IXAF_IPSEC_SECURE) 3493bd670b35SErik Nordmark ipsec_out_release_refs(ixa); 34942b24ab6bSSebastien Roy } 34952b24ab6bSSebastien Roy 34962b24ab6bSSebastien Roy static mac_callbacks_t iptun_m_callbacks = { 34970dc2366fSVenugopal Iyer .mc_callbacks = (MC_SETPROP | MC_GETPROP | MC_PROPINFO), 34982b24ab6bSSebastien Roy .mc_getstat = iptun_m_getstat, 34992b24ab6bSSebastien Roy .mc_start = iptun_m_start, 35002b24ab6bSSebastien Roy .mc_stop = iptun_m_stop, 35012b24ab6bSSebastien Roy .mc_setpromisc = iptun_m_setpromisc, 35022b24ab6bSSebastien Roy .mc_multicst = iptun_m_multicst, 35032b24ab6bSSebastien Roy .mc_unicst = iptun_m_unicst, 35042b24ab6bSSebastien Roy .mc_tx = iptun_m_tx, 35050dc2366fSVenugopal Iyer .mc_reserved = NULL, 35062b24ab6bSSebastien Roy .mc_setprop = iptun_m_setprop, 35070dc2366fSVenugopal Iyer .mc_getprop = iptun_m_getprop, 35080dc2366fSVenugopal Iyer .mc_propinfo = iptun_m_propinfo 35092b24ab6bSSebastien Roy }; 3510