xref: /titanic_51/usr/src/uts/common/inet/iptun/iptun.c (revision 567a25d3ca327069b2629ff9fd3fb921e6cccbf4)
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