xref: /illumos-gate/usr/src/uts/common/inet/ip/ipsecesp.c (revision 4132743a134e253014b777399c8f8275cf71636c)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
57845d282Sdanmcd  * Common Development and Distribution License (the "License").
67845d282Sdanmcd  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22930af642SDan McDonald  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
24b7daf799SDan McDonald  * Copyright (c) 2012 Nexenta Systems, Inc. All rights reserved.
25b7daf799SDan McDonald  * Copyright (c) 2017 Joyent, Inc.
267c478bd9Sstevel@tonic-gate  */
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate #include <sys/types.h>
297c478bd9Sstevel@tonic-gate #include <sys/stream.h>
307c478bd9Sstevel@tonic-gate #include <sys/stropts.h>
317c478bd9Sstevel@tonic-gate #include <sys/errno.h>
327c478bd9Sstevel@tonic-gate #include <sys/strlog.h>
337c478bd9Sstevel@tonic-gate #include <sys/tihdr.h>
347c478bd9Sstevel@tonic-gate #include <sys/socket.h>
357c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
367c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
377c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
38f4b3ec61Sdh155122 #include <sys/zone.h>
397c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
407c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
417c478bd9Sstevel@tonic-gate #include <sys/vtrace.h>
427c478bd9Sstevel@tonic-gate #include <sys/debug.h>
437c478bd9Sstevel@tonic-gate #include <sys/atomic.h>
447c478bd9Sstevel@tonic-gate #include <sys/strsun.h>
457c478bd9Sstevel@tonic-gate #include <sys/random.h>
467c478bd9Sstevel@tonic-gate #include <netinet/in.h>
477c478bd9Sstevel@tonic-gate #include <net/if.h>
487c478bd9Sstevel@tonic-gate #include <netinet/ip6.h>
497c478bd9Sstevel@tonic-gate #include <net/pfkeyv2.h>
50628b0c67SMark Fenwick #include <net/pfpolicy.h>
517c478bd9Sstevel@tonic-gate 
527c478bd9Sstevel@tonic-gate #include <inet/common.h>
537c478bd9Sstevel@tonic-gate #include <inet/mi.h>
547c478bd9Sstevel@tonic-gate #include <inet/nd.h>
557c478bd9Sstevel@tonic-gate #include <inet/ip.h>
56437220cdSdanmcd #include <inet/ip_impl.h>
577c478bd9Sstevel@tonic-gate #include <inet/ip6.h>
58bd670b35SErik Nordmark #include <inet/ip_if.h>
59bd670b35SErik Nordmark #include <inet/ip_ndp.h>
607c478bd9Sstevel@tonic-gate #include <inet/sadb.h>
617c478bd9Sstevel@tonic-gate #include <inet/ipsec_info.h>
627c478bd9Sstevel@tonic-gate #include <inet/ipsec_impl.h>
637c478bd9Sstevel@tonic-gate #include <inet/ipsecesp.h>
647c478bd9Sstevel@tonic-gate #include <inet/ipdrop.h>
657c478bd9Sstevel@tonic-gate #include <inet/tcp.h>
667c478bd9Sstevel@tonic-gate #include <sys/kstat.h>
677c478bd9Sstevel@tonic-gate #include <sys/policy.h>
687c478bd9Sstevel@tonic-gate #include <sys/strsun.h>
695d3b8cb7SBill Sommerfeld #include <sys/strsubr.h>
707c478bd9Sstevel@tonic-gate #include <inet/udp_impl.h>
717c478bd9Sstevel@tonic-gate #include <sys/taskq.h>
72f4b3ec61Sdh155122 #include <sys/note.h>
737c478bd9Sstevel@tonic-gate 
745d3b8cb7SBill Sommerfeld #include <sys/tsol/tnet.h>
755d3b8cb7SBill Sommerfeld 
767c478bd9Sstevel@tonic-gate /*
777c478bd9Sstevel@tonic-gate  * Table of ND variables supported by ipsecesp. These are loaded into
787c478bd9Sstevel@tonic-gate  * ipsecesp_g_nd in ipsecesp_init_nd.
797c478bd9Sstevel@tonic-gate  * All of these are alterable, within the min/max values given, at run time.
807c478bd9Sstevel@tonic-gate  */
81f4b3ec61Sdh155122 static	ipsecespparam_t	lcl_param_arr[] = {
827c478bd9Sstevel@tonic-gate 	/* min	max			value	name */
837c478bd9Sstevel@tonic-gate 	{ 0,	3,			0,	"ipsecesp_debug"},
847c478bd9Sstevel@tonic-gate 	{ 125,	32000, SADB_AGE_INTERVAL_DEFAULT, "ipsecesp_age_interval"},
857c478bd9Sstevel@tonic-gate 	{ 1,	10,			1,	"ipsecesp_reap_delay"},
867c478bd9Sstevel@tonic-gate 	{ 1,	SADB_MAX_REPLAY,	64,	"ipsecesp_replay_size"},
877c478bd9Sstevel@tonic-gate 	{ 1,	300,			15,	"ipsecesp_acquire_timeout"},
887c478bd9Sstevel@tonic-gate 	{ 1,	1800,			90,	"ipsecesp_larval_timeout"},
897c478bd9Sstevel@tonic-gate 	/* Default lifetime values for ACQUIRE messages. */
907c478bd9Sstevel@tonic-gate 	{ 0,	0xffffffffU,	0,	"ipsecesp_default_soft_bytes"},
917c478bd9Sstevel@tonic-gate 	{ 0,	0xffffffffU,	0,	"ipsecesp_default_hard_bytes"},
927c478bd9Sstevel@tonic-gate 	{ 0,	0xffffffffU,	24000,	"ipsecesp_default_soft_addtime"},
937c478bd9Sstevel@tonic-gate 	{ 0,	0xffffffffU,	28800,	"ipsecesp_default_hard_addtime"},
947c478bd9Sstevel@tonic-gate 	{ 0,	0xffffffffU,	0,	"ipsecesp_default_soft_usetime"},
957c478bd9Sstevel@tonic-gate 	{ 0,	0xffffffffU,	0,	"ipsecesp_default_hard_usetime"},
967c478bd9Sstevel@tonic-gate 	{ 0,	1,		0,	"ipsecesp_log_unknown_spi"},
977c478bd9Sstevel@tonic-gate 	{ 0,	2,		1,	"ipsecesp_padding_check"},
98437220cdSdanmcd 	{ 0,	600,		20,	"ipsecesp_nat_keepalive_interval"},
997c478bd9Sstevel@tonic-gate };
100437220cdSdanmcd /* For ipsecesp_nat_keepalive_interval, see ipsecesp.h. */
1017c478bd9Sstevel@tonic-gate 
1027c478bd9Sstevel@tonic-gate #define	esp0dbg(a)	printf a
1037c478bd9Sstevel@tonic-gate /* NOTE:  != 0 instead of > 0 so lint doesn't complain. */
104f4b3ec61Sdh155122 #define	esp1dbg(espstack, a)	if (espstack->ipsecesp_debug != 0) printf a
105f4b3ec61Sdh155122 #define	esp2dbg(espstack, a)	if (espstack->ipsecesp_debug > 1) printf a
106f4b3ec61Sdh155122 #define	esp3dbg(espstack, a)	if (espstack->ipsecesp_debug > 2) printf a
1077c478bd9Sstevel@tonic-gate 
1087c478bd9Sstevel@tonic-gate static int ipsecesp_open(queue_t *, dev_t *, int, int, cred_t *);
1095e1743f0SToomas Soome static int ipsecesp_close(queue_t *, int, cred_t *);
1109c451ec7SToomas Soome static int ipsecesp_rput(queue_t *, mblk_t *);
1119c451ec7SToomas Soome static int ipsecesp_wput(queue_t *, mblk_t *);
112f4b3ec61Sdh155122 static void	*ipsecesp_stack_init(netstackid_t stackid, netstack_t *ns);
113f4b3ec61Sdh155122 static void	ipsecesp_stack_fini(netstackid_t stackid, void *arg);
1147c478bd9Sstevel@tonic-gate 
115437220cdSdanmcd static void esp_prepare_udp(netstack_t *, mblk_t *, ipha_t *);
116bd670b35SErik Nordmark static void esp_outbound_finish(mblk_t *, ip_xmit_attr_t *);
117bd670b35SErik Nordmark static void esp_inbound_restart(mblk_t *, ip_recv_attr_t *);
1187c478bd9Sstevel@tonic-gate 
119f4b3ec61Sdh155122 static boolean_t esp_register_out(uint32_t, uint32_t, uint_t,
120bd670b35SErik Nordmark     ipsecesp_stack_t *, cred_t *);
1217c478bd9Sstevel@tonic-gate static boolean_t esp_strip_header(mblk_t *, boolean_t, uint32_t,
122f4b3ec61Sdh155122     kstat_named_t **, ipsecesp_stack_t *);
123bd670b35SErik Nordmark static mblk_t *esp_submit_req_inbound(mblk_t *, ip_recv_attr_t *,
124bd670b35SErik Nordmark     ipsa_t *, uint_t);
125bd670b35SErik Nordmark static mblk_t *esp_submit_req_outbound(mblk_t *, ip_xmit_attr_t *,
126bd670b35SErik Nordmark     ipsa_t *, uchar_t *, uint_t);
1279c2c14abSThejaswini Singarajipura 
128f4b3ec61Sdh155122 /* Setable in /etc/system */
129f4b3ec61Sdh155122 uint32_t esp_hash_size = IPSEC_DEFAULT_HASH_SIZE;
130f4b3ec61Sdh155122 
1317c478bd9Sstevel@tonic-gate static struct module_info info = {
1327c478bd9Sstevel@tonic-gate 	5137, "ipsecesp", 0, INFPSZ, 65536, 1024
1337c478bd9Sstevel@tonic-gate };
1347c478bd9Sstevel@tonic-gate 
1357c478bd9Sstevel@tonic-gate static struct qinit rinit = {
1369c451ec7SToomas Soome 	ipsecesp_rput, NULL, ipsecesp_open, ipsecesp_close, NULL, &info,
1377c478bd9Sstevel@tonic-gate 	NULL
1387c478bd9Sstevel@tonic-gate };
1397c478bd9Sstevel@tonic-gate 
1407c478bd9Sstevel@tonic-gate static struct qinit winit = {
1419c451ec7SToomas Soome 	ipsecesp_wput, NULL, ipsecesp_open, ipsecesp_close, NULL, &info,
1427c478bd9Sstevel@tonic-gate 	NULL
1437c478bd9Sstevel@tonic-gate };
1447c478bd9Sstevel@tonic-gate 
1457c478bd9Sstevel@tonic-gate struct streamtab ipsecespinfo = {
1467c478bd9Sstevel@tonic-gate 	&rinit, &winit, NULL, NULL
1477c478bd9Sstevel@tonic-gate };
1487c478bd9Sstevel@tonic-gate 
1497c478bd9Sstevel@tonic-gate static taskq_t *esp_taskq;
1507c478bd9Sstevel@tonic-gate 
1517c478bd9Sstevel@tonic-gate /*
1527c478bd9Sstevel@tonic-gate  * OTOH, this one is set at open/close, and I'm D_MTQPAIR for now.
1537c478bd9Sstevel@tonic-gate  *
1547c478bd9Sstevel@tonic-gate  * Question:	Do I need this, given that all instance's esps->esps_wq point
1557c478bd9Sstevel@tonic-gate  *		to IP?
1567c478bd9Sstevel@tonic-gate  *
1577c478bd9Sstevel@tonic-gate  * Answer:	Yes, because I need to know which queue is BOUND to
1587c478bd9Sstevel@tonic-gate  *		IPPROTO_ESP
1597c478bd9Sstevel@tonic-gate  */
1607c478bd9Sstevel@tonic-gate 
1617c478bd9Sstevel@tonic-gate static int	esp_kstat_update(kstat_t *, int);
1627c478bd9Sstevel@tonic-gate 
1637c478bd9Sstevel@tonic-gate static boolean_t
esp_kstat_init(ipsecesp_stack_t * espstack,netstackid_t stackid)164f4b3ec61Sdh155122 esp_kstat_init(ipsecesp_stack_t *espstack, netstackid_t stackid)
1657c478bd9Sstevel@tonic-gate {
166f4b3ec61Sdh155122 	espstack->esp_ksp = kstat_create_netstack("ipsecesp", 0, "esp_stat",
167f4b3ec61Sdh155122 	    "net", KSTAT_TYPE_NAMED,
168281819e5SDan McDonald 	    sizeof (esp_kstats_t) / sizeof (kstat_named_t), 0, stackid);
1697c478bd9Sstevel@tonic-gate 
170f4b3ec61Sdh155122 	if (espstack->esp_ksp == NULL || espstack->esp_ksp->ks_data == NULL)
1717c478bd9Sstevel@tonic-gate 		return (B_FALSE);
1727c478bd9Sstevel@tonic-gate 
173f4b3ec61Sdh155122 	espstack->esp_kstats = espstack->esp_ksp->ks_data;
1747c478bd9Sstevel@tonic-gate 
175f4b3ec61Sdh155122 	espstack->esp_ksp->ks_update = esp_kstat_update;
176f4b3ec61Sdh155122 	espstack->esp_ksp->ks_private = (void *)(uintptr_t)stackid;
1777c478bd9Sstevel@tonic-gate 
1787c478bd9Sstevel@tonic-gate #define	K64 KSTAT_DATA_UINT64
179f4b3ec61Sdh155122 #define	KI(x) kstat_named_init(&(espstack->esp_kstats->esp_stat_##x), #x, K64)
1807c478bd9Sstevel@tonic-gate 
1817c478bd9Sstevel@tonic-gate 	KI(num_aalgs);
1827c478bd9Sstevel@tonic-gate 	KI(num_ealgs);
1837c478bd9Sstevel@tonic-gate 	KI(good_auth);
1847c478bd9Sstevel@tonic-gate 	KI(bad_auth);
1857c478bd9Sstevel@tonic-gate 	KI(bad_padding);
1867c478bd9Sstevel@tonic-gate 	KI(replay_failures);
1877c478bd9Sstevel@tonic-gate 	KI(replay_early_failures);
1887c478bd9Sstevel@tonic-gate 	KI(keysock_in);
1897c478bd9Sstevel@tonic-gate 	KI(out_requests);
1907c478bd9Sstevel@tonic-gate 	KI(acquire_requests);
1917c478bd9Sstevel@tonic-gate 	KI(bytes_expired);
1927c478bd9Sstevel@tonic-gate 	KI(out_discards);
1937c478bd9Sstevel@tonic-gate 	KI(crypto_sync);
1947c478bd9Sstevel@tonic-gate 	KI(crypto_async);
1957c478bd9Sstevel@tonic-gate 	KI(crypto_failures);
1967c478bd9Sstevel@tonic-gate 	KI(bad_decrypt);
1974a179720Sdanmcd 	KI(sa_port_renumbers);
1987c478bd9Sstevel@tonic-gate 
1997c478bd9Sstevel@tonic-gate #undef KI
2007c478bd9Sstevel@tonic-gate #undef K64
2017c478bd9Sstevel@tonic-gate 
202f4b3ec61Sdh155122 	kstat_install(espstack->esp_ksp);
2037c478bd9Sstevel@tonic-gate 
2047c478bd9Sstevel@tonic-gate 	return (B_TRUE);
2057c478bd9Sstevel@tonic-gate }
2067c478bd9Sstevel@tonic-gate 
2077c478bd9Sstevel@tonic-gate static int
esp_kstat_update(kstat_t * kp,int rw)2087c478bd9Sstevel@tonic-gate esp_kstat_update(kstat_t *kp, int rw)
2097c478bd9Sstevel@tonic-gate {
2107c478bd9Sstevel@tonic-gate 	esp_kstats_t *ekp;
211a23b3b1bSToomas Soome 	netstackid_t	stackid;
212f4b3ec61Sdh155122 	netstack_t	*ns;
213f4b3ec61Sdh155122 	ipsec_stack_t	*ipss;
2147c478bd9Sstevel@tonic-gate 
2157c478bd9Sstevel@tonic-gate 	if ((kp == NULL) || (kp->ks_data == NULL))
2167c478bd9Sstevel@tonic-gate 		return (EIO);
2177c478bd9Sstevel@tonic-gate 
2187c478bd9Sstevel@tonic-gate 	if (rw == KSTAT_WRITE)
2197c478bd9Sstevel@tonic-gate 		return (EACCES);
2207c478bd9Sstevel@tonic-gate 
221a23b3b1bSToomas Soome 	stackid = (zoneid_t)(uintptr_t)kp->ks_private;
222f4b3ec61Sdh155122 	ns = netstack_find_by_stackid(stackid);
223f4b3ec61Sdh155122 	if (ns == NULL)
224f4b3ec61Sdh155122 		return (-1);
225f4b3ec61Sdh155122 	ipss = ns->netstack_ipsec;
226f4b3ec61Sdh155122 	if (ipss == NULL) {
227f4b3ec61Sdh155122 		netstack_rele(ns);
228f4b3ec61Sdh155122 		return (-1);
229f4b3ec61Sdh155122 	}
2307c478bd9Sstevel@tonic-gate 	ekp = (esp_kstats_t *)kp->ks_data;
2317c478bd9Sstevel@tonic-gate 
23269e71331SBayard Bell 	rw_enter(&ipss->ipsec_alg_lock, RW_READER);
233f4b3ec61Sdh155122 	ekp->esp_stat_num_aalgs.value.ui64 =
234f4b3ec61Sdh155122 	    ipss->ipsec_nalgs[IPSEC_ALG_AUTH];
235f4b3ec61Sdh155122 	ekp->esp_stat_num_ealgs.value.ui64 =
236f4b3ec61Sdh155122 	    ipss->ipsec_nalgs[IPSEC_ALG_ENCR];
23769e71331SBayard Bell 	rw_exit(&ipss->ipsec_alg_lock);
2387c478bd9Sstevel@tonic-gate 
239f4b3ec61Sdh155122 	netstack_rele(ns);
2407c478bd9Sstevel@tonic-gate 	return (0);
2417c478bd9Sstevel@tonic-gate }
2427c478bd9Sstevel@tonic-gate 
2437c478bd9Sstevel@tonic-gate #ifdef DEBUG
2447c478bd9Sstevel@tonic-gate /*
2457c478bd9Sstevel@tonic-gate  * Debug routine, useful to see pre-encryption data.
2467c478bd9Sstevel@tonic-gate  */
2477c478bd9Sstevel@tonic-gate static char *
dump_msg(mblk_t * mp)2487c478bd9Sstevel@tonic-gate dump_msg(mblk_t *mp)
2497c478bd9Sstevel@tonic-gate {
2507c478bd9Sstevel@tonic-gate 	char tmp_str[3], tmp_line[256];
2517c478bd9Sstevel@tonic-gate 
2527c478bd9Sstevel@tonic-gate 	while (mp != NULL) {
2537c478bd9Sstevel@tonic-gate 		unsigned char *ptr;
2547c478bd9Sstevel@tonic-gate 
2557c478bd9Sstevel@tonic-gate 		printf("mblk address 0x%p, length %ld, db_ref %d "
2567c478bd9Sstevel@tonic-gate 		    "type %d, base 0x%p, lim 0x%p\n",
2577c478bd9Sstevel@tonic-gate 		    (void *) mp, (long)(mp->b_wptr - mp->b_rptr),
2587c478bd9Sstevel@tonic-gate 		    mp->b_datap->db_ref, mp->b_datap->db_type,
2597c478bd9Sstevel@tonic-gate 		    (void *)mp->b_datap->db_base, (void *)mp->b_datap->db_lim);
2607c478bd9Sstevel@tonic-gate 		ptr = mp->b_rptr;
2617c478bd9Sstevel@tonic-gate 
2627c478bd9Sstevel@tonic-gate 		tmp_line[0] = '\0';
2637c478bd9Sstevel@tonic-gate 		while (ptr < mp->b_wptr) {
2647c478bd9Sstevel@tonic-gate 			uint_t diff;
2657c478bd9Sstevel@tonic-gate 
2667c478bd9Sstevel@tonic-gate 			diff = (ptr - mp->b_rptr);
2677c478bd9Sstevel@tonic-gate 			if (!(diff & 0x1f)) {
2687c478bd9Sstevel@tonic-gate 				if (strlen(tmp_line) > 0) {
2697c478bd9Sstevel@tonic-gate 					printf("bytes: %s\n", tmp_line);
2707c478bd9Sstevel@tonic-gate 					tmp_line[0] = '\0';
2717c478bd9Sstevel@tonic-gate 				}
2727c478bd9Sstevel@tonic-gate 			}
2737c478bd9Sstevel@tonic-gate 			if (!(diff & 0x3))
2747c478bd9Sstevel@tonic-gate 				(void) strcat(tmp_line, " ");
2757c478bd9Sstevel@tonic-gate 			(void) sprintf(tmp_str, "%02x", *ptr);
2767c478bd9Sstevel@tonic-gate 			(void) strcat(tmp_line, tmp_str);
2777c478bd9Sstevel@tonic-gate 			ptr++;
2787c478bd9Sstevel@tonic-gate 		}
2797c478bd9Sstevel@tonic-gate 		if (strlen(tmp_line) > 0)
2807c478bd9Sstevel@tonic-gate 			printf("bytes: %s\n", tmp_line);
2817c478bd9Sstevel@tonic-gate 
2827c478bd9Sstevel@tonic-gate 		mp = mp->b_cont;
2837c478bd9Sstevel@tonic-gate 	}
2847c478bd9Sstevel@tonic-gate 
2857c478bd9Sstevel@tonic-gate 	return ("\n");
2867c478bd9Sstevel@tonic-gate }
2877c478bd9Sstevel@tonic-gate 
2887c478bd9Sstevel@tonic-gate #else /* DEBUG */
2897c478bd9Sstevel@tonic-gate static char *
dump_msg(mblk_t * mp)2907c478bd9Sstevel@tonic-gate dump_msg(mblk_t *mp)
2917c478bd9Sstevel@tonic-gate {
2927c478bd9Sstevel@tonic-gate 	printf("Find value of mp %p.\n", mp);
2937c478bd9Sstevel@tonic-gate 	return ("\n");
2947c478bd9Sstevel@tonic-gate }
2957c478bd9Sstevel@tonic-gate #endif /* DEBUG */
2967c478bd9Sstevel@tonic-gate 
2977c478bd9Sstevel@tonic-gate /*
2987c478bd9Sstevel@tonic-gate  * Don't have to lock age_interval, as only one thread will access it at
2997c478bd9Sstevel@tonic-gate  * a time, because I control the one function that does with timeout().
3007c478bd9Sstevel@tonic-gate  */
3017c478bd9Sstevel@tonic-gate static void
esp_ager(void * arg)302f4b3ec61Sdh155122 esp_ager(void *arg)
3037c478bd9Sstevel@tonic-gate {
304f4b3ec61Sdh155122 	ipsecesp_stack_t *espstack = (ipsecesp_stack_t *)arg;
305f4b3ec61Sdh155122 	netstack_t	*ns = espstack->ipsecesp_netstack;
3067c478bd9Sstevel@tonic-gate 	hrtime_t begin = gethrtime();
3077c478bd9Sstevel@tonic-gate 
308f4b3ec61Sdh155122 	sadb_ager(&espstack->esp_sadb.s_v4, espstack->esp_pfkey_q,
309bd670b35SErik Nordmark 	    espstack->ipsecesp_reap_delay, ns);
310f4b3ec61Sdh155122 	sadb_ager(&espstack->esp_sadb.s_v6, espstack->esp_pfkey_q,
311bd670b35SErik Nordmark 	    espstack->ipsecesp_reap_delay, ns);
3127c478bd9Sstevel@tonic-gate 
313f4b3ec61Sdh155122 	espstack->esp_event = sadb_retimeout(begin, espstack->esp_pfkey_q,
314f4b3ec61Sdh155122 	    esp_ager, espstack,
315f4b3ec61Sdh155122 	    &espstack->ipsecesp_age_interval, espstack->ipsecesp_age_int_max,
316f4b3ec61Sdh155122 	    info.mi_idnum);
3177c478bd9Sstevel@tonic-gate }
3187c478bd9Sstevel@tonic-gate 
3197c478bd9Sstevel@tonic-gate /*
3207c478bd9Sstevel@tonic-gate  * Get an ESP NDD parameter.
3217c478bd9Sstevel@tonic-gate  */
3227c478bd9Sstevel@tonic-gate /* ARGSUSED */
3237c478bd9Sstevel@tonic-gate static int
ipsecesp_param_get(queue_t * q,mblk_t * mp,caddr_t cp,cred_t * cr)32469e71331SBayard Bell ipsecesp_param_get(
32569e71331SBayard Bell     queue_t	*q,
32669e71331SBayard Bell     mblk_t	*mp,
32769e71331SBayard Bell     caddr_t	cp,
32869e71331SBayard Bell     cred_t *cr)
3297c478bd9Sstevel@tonic-gate {
3307c478bd9Sstevel@tonic-gate 	ipsecespparam_t	*ipsecesppa = (ipsecespparam_t *)cp;
3317c478bd9Sstevel@tonic-gate 	uint_t value;
332f4b3ec61Sdh155122 	ipsecesp_stack_t	*espstack = (ipsecesp_stack_t *)q->q_ptr;
3337c478bd9Sstevel@tonic-gate 
334f4b3ec61Sdh155122 	mutex_enter(&espstack->ipsecesp_param_lock);
3357c478bd9Sstevel@tonic-gate 	value = ipsecesppa->ipsecesp_param_value;
336f4b3ec61Sdh155122 	mutex_exit(&espstack->ipsecesp_param_lock);
3377c478bd9Sstevel@tonic-gate 
3387c478bd9Sstevel@tonic-gate 	(void) mi_mpprintf(mp, "%u", value);
3397c478bd9Sstevel@tonic-gate 	return (0);
3407c478bd9Sstevel@tonic-gate }
3417c478bd9Sstevel@tonic-gate 
3427c478bd9Sstevel@tonic-gate /*
3437c478bd9Sstevel@tonic-gate  * This routine sets an NDD variable in a ipsecespparam_t structure.
3447c478bd9Sstevel@tonic-gate  */
3457c478bd9Sstevel@tonic-gate /* ARGSUSED */
3467c478bd9Sstevel@tonic-gate static int
ipsecesp_param_set(queue_t * q,mblk_t * mp,char * value,caddr_t cp,cred_t * cr)34769e71331SBayard Bell ipsecesp_param_set(
34869e71331SBayard Bell     queue_t	*q,
34969e71331SBayard Bell     mblk_t	*mp,
35069e71331SBayard Bell     char	*value,
35169e71331SBayard Bell     caddr_t	cp,
35269e71331SBayard Bell     cred_t *cr)
3537c478bd9Sstevel@tonic-gate {
3547c478bd9Sstevel@tonic-gate 	ulong_t	new_value;
3557c478bd9Sstevel@tonic-gate 	ipsecespparam_t	*ipsecesppa = (ipsecespparam_t *)cp;
356f4b3ec61Sdh155122 	ipsecesp_stack_t	*espstack = (ipsecesp_stack_t *)q->q_ptr;
3577c478bd9Sstevel@tonic-gate 
3587c478bd9Sstevel@tonic-gate 	/*
3597c478bd9Sstevel@tonic-gate 	 * Fail the request if the new value does not lie within the
3607c478bd9Sstevel@tonic-gate 	 * required bounds.
3617c478bd9Sstevel@tonic-gate 	 */
3627c478bd9Sstevel@tonic-gate 	if (ddi_strtoul(value, NULL, 10, &new_value) != 0 ||
3637c478bd9Sstevel@tonic-gate 	    new_value < ipsecesppa->ipsecesp_param_min ||
3647c478bd9Sstevel@tonic-gate 	    new_value > ipsecesppa->ipsecesp_param_max) {
3657c478bd9Sstevel@tonic-gate 		return (EINVAL);
3667c478bd9Sstevel@tonic-gate 	}
3677c478bd9Sstevel@tonic-gate 
3687c478bd9Sstevel@tonic-gate 	/* Set the new value */
369f4b3ec61Sdh155122 	mutex_enter(&espstack->ipsecesp_param_lock);
3707c478bd9Sstevel@tonic-gate 	ipsecesppa->ipsecesp_param_value = new_value;
371f4b3ec61Sdh155122 	mutex_exit(&espstack->ipsecesp_param_lock);
3727c478bd9Sstevel@tonic-gate 	return (0);
3737c478bd9Sstevel@tonic-gate }
3747c478bd9Sstevel@tonic-gate 
3757c478bd9Sstevel@tonic-gate /*
3767c478bd9Sstevel@tonic-gate  * Using lifetime NDD variables, fill in an extended combination's
3777c478bd9Sstevel@tonic-gate  * lifetime information.
3787c478bd9Sstevel@tonic-gate  */
3797c478bd9Sstevel@tonic-gate void
ipsecesp_fill_defs(sadb_x_ecomb_t * ecomb,netstack_t * ns)380f4b3ec61Sdh155122 ipsecesp_fill_defs(sadb_x_ecomb_t *ecomb, netstack_t *ns)
3817c478bd9Sstevel@tonic-gate {
382f4b3ec61Sdh155122 	ipsecesp_stack_t	*espstack = ns->netstack_ipsecesp;
383f4b3ec61Sdh155122 
384f4b3ec61Sdh155122 	ecomb->sadb_x_ecomb_soft_bytes = espstack->ipsecesp_default_soft_bytes;
385f4b3ec61Sdh155122 	ecomb->sadb_x_ecomb_hard_bytes = espstack->ipsecesp_default_hard_bytes;
386f4b3ec61Sdh155122 	ecomb->sadb_x_ecomb_soft_addtime =
387f4b3ec61Sdh155122 	    espstack->ipsecesp_default_soft_addtime;
388f4b3ec61Sdh155122 	ecomb->sadb_x_ecomb_hard_addtime =
389f4b3ec61Sdh155122 	    espstack->ipsecesp_default_hard_addtime;
390f4b3ec61Sdh155122 	ecomb->sadb_x_ecomb_soft_usetime =
391f4b3ec61Sdh155122 	    espstack->ipsecesp_default_soft_usetime;
392f4b3ec61Sdh155122 	ecomb->sadb_x_ecomb_hard_usetime =
393f4b3ec61Sdh155122 	    espstack->ipsecesp_default_hard_usetime;
3947c478bd9Sstevel@tonic-gate }
3957c478bd9Sstevel@tonic-gate 
3967c478bd9Sstevel@tonic-gate /*
3977c478bd9Sstevel@tonic-gate  * Initialize things for ESP at module load time.
3987c478bd9Sstevel@tonic-gate  */
3997c478bd9Sstevel@tonic-gate boolean_t
ipsecesp_ddi_init(void)4007c478bd9Sstevel@tonic-gate ipsecesp_ddi_init(void)
4017c478bd9Sstevel@tonic-gate {
4027c478bd9Sstevel@tonic-gate 	esp_taskq = taskq_create("esp_taskq", 1, minclsyspri,
4037c478bd9Sstevel@tonic-gate 	    IPSEC_TASKQ_MIN, IPSEC_TASKQ_MAX, 0);
4047c478bd9Sstevel@tonic-gate 
405f4b3ec61Sdh155122 	/*
406f4b3ec61Sdh155122 	 * We want to be informed each time a stack is created or
407f4b3ec61Sdh155122 	 * destroyed in the kernel, so we can maintain the
408f4b3ec61Sdh155122 	 * set of ipsecesp_stack_t's.
409f4b3ec61Sdh155122 	 */
410f4b3ec61Sdh155122 	netstack_register(NS_IPSECESP, ipsecesp_stack_init, NULL,
411f4b3ec61Sdh155122 	    ipsecesp_stack_fini);
4127c478bd9Sstevel@tonic-gate 
4137c478bd9Sstevel@tonic-gate 	return (B_TRUE);
4147c478bd9Sstevel@tonic-gate }
4157c478bd9Sstevel@tonic-gate 
4167c478bd9Sstevel@tonic-gate /*
417f4b3ec61Sdh155122  * Walk through the param array specified registering each element with the
418f4b3ec61Sdh155122  * named dispatch handler.
419f4b3ec61Sdh155122  */
420f4b3ec61Sdh155122 static boolean_t
ipsecesp_param_register(IDP * ndp,ipsecespparam_t * espp,int cnt)421f4b3ec61Sdh155122 ipsecesp_param_register(IDP *ndp, ipsecespparam_t *espp, int cnt)
422f4b3ec61Sdh155122 {
423f4b3ec61Sdh155122 	for (; cnt-- > 0; espp++) {
424f4b3ec61Sdh155122 		if (espp->ipsecesp_param_name != NULL &&
425f4b3ec61Sdh155122 		    espp->ipsecesp_param_name[0]) {
426f4b3ec61Sdh155122 			if (!nd_load(ndp,
427f4b3ec61Sdh155122 			    espp->ipsecesp_param_name,
428f4b3ec61Sdh155122 			    ipsecesp_param_get, ipsecesp_param_set,
429f4b3ec61Sdh155122 			    (caddr_t)espp)) {
430f4b3ec61Sdh155122 				nd_free(ndp);
431f4b3ec61Sdh155122 				return (B_FALSE);
432f4b3ec61Sdh155122 			}
433f4b3ec61Sdh155122 		}
434f4b3ec61Sdh155122 	}
435f4b3ec61Sdh155122 	return (B_TRUE);
436f4b3ec61Sdh155122 }
437b7daf799SDan McDonald 
438f4b3ec61Sdh155122 /*
439f4b3ec61Sdh155122  * Initialize things for ESP for each stack instance
440f4b3ec61Sdh155122  */
441f4b3ec61Sdh155122 static void *
ipsecesp_stack_init(netstackid_t stackid,netstack_t * ns)442f4b3ec61Sdh155122 ipsecesp_stack_init(netstackid_t stackid, netstack_t *ns)
443f4b3ec61Sdh155122 {
444f4b3ec61Sdh155122 	ipsecesp_stack_t	*espstack;
445f4b3ec61Sdh155122 	ipsecespparam_t		*espp;
446f4b3ec61Sdh155122 
447f4b3ec61Sdh155122 	espstack = (ipsecesp_stack_t *)kmem_zalloc(sizeof (*espstack),
448f4b3ec61Sdh155122 	    KM_SLEEP);
449f4b3ec61Sdh155122 	espstack->ipsecesp_netstack = ns;
450f4b3ec61Sdh155122 
451f4b3ec61Sdh155122 	espp = (ipsecespparam_t *)kmem_alloc(sizeof (lcl_param_arr), KM_SLEEP);
452f4b3ec61Sdh155122 	espstack->ipsecesp_params = espp;
453f4b3ec61Sdh155122 	bcopy(lcl_param_arr, espp, sizeof (lcl_param_arr));
454f4b3ec61Sdh155122 
455f4b3ec61Sdh155122 	(void) ipsecesp_param_register(&espstack->ipsecesp_g_nd, espp,
456f4b3ec61Sdh155122 	    A_CNT(lcl_param_arr));
457f4b3ec61Sdh155122 
458f4b3ec61Sdh155122 	(void) esp_kstat_init(espstack, stackid);
459f4b3ec61Sdh155122 
460f4b3ec61Sdh155122 	espstack->esp_sadb.s_acquire_timeout =
461f4b3ec61Sdh155122 	    &espstack->ipsecesp_acquire_timeout;
462f4b3ec61Sdh155122 	sadbp_init("ESP", &espstack->esp_sadb, SADB_SATYPE_ESP, esp_hash_size,
463f4b3ec61Sdh155122 	    espstack->ipsecesp_netstack);
464f4b3ec61Sdh155122 
465f4b3ec61Sdh155122 	mutex_init(&espstack->ipsecesp_param_lock, NULL, MUTEX_DEFAULT, 0);
466f4b3ec61Sdh155122 
467f4b3ec61Sdh155122 	ip_drop_register(&espstack->esp_dropper, "IPsec ESP");
468f4b3ec61Sdh155122 	return (espstack);
469f4b3ec61Sdh155122 }
470f4b3ec61Sdh155122 
471f4b3ec61Sdh155122 /*
4727c478bd9Sstevel@tonic-gate  * Destroy things for ESP at module unload time.
4737c478bd9Sstevel@tonic-gate  */
4747c478bd9Sstevel@tonic-gate void
ipsecesp_ddi_destroy(void)4757c478bd9Sstevel@tonic-gate ipsecesp_ddi_destroy(void)
4767c478bd9Sstevel@tonic-gate {
477f4b3ec61Sdh155122 	netstack_unregister(NS_IPSECESP);
4787c478bd9Sstevel@tonic-gate 	taskq_destroy(esp_taskq);
479f4b3ec61Sdh155122 }
480f4b3ec61Sdh155122 
481f4b3ec61Sdh155122 /*
482f4b3ec61Sdh155122  * Destroy things for ESP for one stack instance
483f4b3ec61Sdh155122  */
484f4b3ec61Sdh155122 static void
ipsecesp_stack_fini(netstackid_t stackid,void * arg)485f4b3ec61Sdh155122 ipsecesp_stack_fini(netstackid_t stackid, void *arg)
486f4b3ec61Sdh155122 {
487f4b3ec61Sdh155122 	ipsecesp_stack_t *espstack = (ipsecesp_stack_t *)arg;
488f4b3ec61Sdh155122 
489f4b3ec61Sdh155122 	if (espstack->esp_pfkey_q != NULL) {
490f4b3ec61Sdh155122 		(void) quntimeout(espstack->esp_pfkey_q, espstack->esp_event);
491f4b3ec61Sdh155122 	}
492f4b3ec61Sdh155122 	espstack->esp_sadb.s_acquire_timeout = NULL;
493f4b3ec61Sdh155122 	sadbp_destroy(&espstack->esp_sadb, espstack->ipsecesp_netstack);
494f4b3ec61Sdh155122 	ip_drop_unregister(&espstack->esp_dropper);
495f4b3ec61Sdh155122 	mutex_destroy(&espstack->ipsecesp_param_lock);
496f4b3ec61Sdh155122 	nd_free(&espstack->ipsecesp_g_nd);
497f4b3ec61Sdh155122 
498f4b3ec61Sdh155122 	kmem_free(espstack->ipsecesp_params, sizeof (lcl_param_arr));
499f4b3ec61Sdh155122 	espstack->ipsecesp_params = NULL;
500f4b3ec61Sdh155122 	kstat_delete_netstack(espstack->esp_ksp, stackid);
501f4b3ec61Sdh155122 	espstack->esp_ksp = NULL;
502f4b3ec61Sdh155122 	espstack->esp_kstats = NULL;
503f4b3ec61Sdh155122 	kmem_free(espstack, sizeof (*espstack));
5047c478bd9Sstevel@tonic-gate }
5057c478bd9Sstevel@tonic-gate 
5067c478bd9Sstevel@tonic-gate /*
507bd670b35SErik Nordmark  * ESP module open routine, which is here for keysock plumbing.
508bd670b35SErik Nordmark  * Keysock is pushed over {AH,ESP} which is an artifact from the Bad Old
509bd670b35SErik Nordmark  * Days of export control, and fears that ESP would not be allowed
510bd670b35SErik Nordmark  * to be shipped at all by default.  Eventually, keysock should
511bd670b35SErik Nordmark  * either access AH and ESP via modstubs or krtld dependencies, or
512bd670b35SErik Nordmark  * perhaps be folded in with AH and ESP into a single IPsec/netsec
513bd670b35SErik Nordmark  * module ("netsec" if PF_KEY provides more than AH/ESP keying tables).
5147c478bd9Sstevel@tonic-gate  */
5157c478bd9Sstevel@tonic-gate /* ARGSUSED */
5167c478bd9Sstevel@tonic-gate static int
ipsecesp_open(queue_t * q,dev_t * devp,int flag,int sflag,cred_t * credp)5177c478bd9Sstevel@tonic-gate ipsecesp_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp)
5187c478bd9Sstevel@tonic-gate {
519f4b3ec61Sdh155122 	netstack_t		*ns;
520f4b3ec61Sdh155122 	ipsecesp_stack_t	*espstack;
521f4b3ec61Sdh155122 
522d2370ffeSsommerfe 	if (secpolicy_ip_config(credp, B_FALSE) != 0)
5237c478bd9Sstevel@tonic-gate 		return (EPERM);
5247c478bd9Sstevel@tonic-gate 
5257c478bd9Sstevel@tonic-gate 	if (q->q_ptr != NULL)
5267c478bd9Sstevel@tonic-gate 		return (0);  /* Re-open of an already open instance. */
5277c478bd9Sstevel@tonic-gate 
5287c478bd9Sstevel@tonic-gate 	if (sflag != MODOPEN)
5297c478bd9Sstevel@tonic-gate 		return (EINVAL);
5307c478bd9Sstevel@tonic-gate 
531f4b3ec61Sdh155122 	ns = netstack_find_by_cred(credp);
532f4b3ec61Sdh155122 	ASSERT(ns != NULL);
533f4b3ec61Sdh155122 	espstack = ns->netstack_ipsecesp;
534f4b3ec61Sdh155122 	ASSERT(espstack != NULL);
535f4b3ec61Sdh155122 
536f4b3ec61Sdh155122 	q->q_ptr = espstack;
537f4b3ec61Sdh155122 	WR(q)->q_ptr = q->q_ptr;
5387c478bd9Sstevel@tonic-gate 
5397c478bd9Sstevel@tonic-gate 	qprocson(q);
5407c478bd9Sstevel@tonic-gate 	return (0);
5417c478bd9Sstevel@tonic-gate }
5427c478bd9Sstevel@tonic-gate 
5437c478bd9Sstevel@tonic-gate /*
5447c478bd9Sstevel@tonic-gate  * ESP module close routine.
5457c478bd9Sstevel@tonic-gate  */
5465e1743f0SToomas Soome /* ARGSUSED */
5477c478bd9Sstevel@tonic-gate static int
ipsecesp_close(queue_t * q,int flags __unused,cred_t * credp __unused)5485e1743f0SToomas Soome ipsecesp_close(queue_t *q, int flags __unused, cred_t *credp __unused)
5497c478bd9Sstevel@tonic-gate {
550f4b3ec61Sdh155122 	ipsecesp_stack_t	*espstack = (ipsecesp_stack_t *)q->q_ptr;
551f4b3ec61Sdh155122 
5527c478bd9Sstevel@tonic-gate 	/*
5537c478bd9Sstevel@tonic-gate 	 * Clean up q_ptr, if needed.
5547c478bd9Sstevel@tonic-gate 	 */
5557c478bd9Sstevel@tonic-gate 	qprocsoff(q);
5567c478bd9Sstevel@tonic-gate 
5577c478bd9Sstevel@tonic-gate 	/* Keysock queue check is safe, because of OCEXCL perimeter. */
5587c478bd9Sstevel@tonic-gate 
559f4b3ec61Sdh155122 	if (q == espstack->esp_pfkey_q) {
560f4b3ec61Sdh155122 		esp1dbg(espstack,
561f4b3ec61Sdh155122 		    ("ipsecesp_close:  Ummm... keysock is closing ESP.\n"));
562f4b3ec61Sdh155122 		espstack->esp_pfkey_q = NULL;
5637c478bd9Sstevel@tonic-gate 		/* Detach qtimeouts. */
564f4b3ec61Sdh155122 		(void) quntimeout(q, espstack->esp_event);
5657c478bd9Sstevel@tonic-gate 	}
5667c478bd9Sstevel@tonic-gate 
567f4b3ec61Sdh155122 	netstack_rele(espstack->ipsecesp_netstack);
5687c478bd9Sstevel@tonic-gate 	return (0);
5697c478bd9Sstevel@tonic-gate }
5707c478bd9Sstevel@tonic-gate 
5717c478bd9Sstevel@tonic-gate /*
5727c478bd9Sstevel@tonic-gate  * Add a number of bytes to what the SA has protected so far.  Return
5737c478bd9Sstevel@tonic-gate  * B_TRUE if the SA can still protect that many bytes.
5747c478bd9Sstevel@tonic-gate  *
5757c478bd9Sstevel@tonic-gate  * Caller must REFRELE the passed-in assoc.  This function must REFRELE
5767c478bd9Sstevel@tonic-gate  * any obtained peer SA.
5777c478bd9Sstevel@tonic-gate  */
5787c478bd9Sstevel@tonic-gate static boolean_t
esp_age_bytes(ipsa_t * assoc,uint64_t bytes,boolean_t inbound)5797c478bd9Sstevel@tonic-gate esp_age_bytes(ipsa_t *assoc, uint64_t bytes, boolean_t inbound)
5807c478bd9Sstevel@tonic-gate {
5817c478bd9Sstevel@tonic-gate 	ipsa_t *inassoc, *outassoc;
5827c478bd9Sstevel@tonic-gate 	isaf_t *bucket;
5837c478bd9Sstevel@tonic-gate 	boolean_t inrc, outrc, isv6;
5847c478bd9Sstevel@tonic-gate 	sadb_t *sp;
5857c478bd9Sstevel@tonic-gate 	int outhash;
586f4b3ec61Sdh155122 	netstack_t		*ns = assoc->ipsa_netstack;
587f4b3ec61Sdh155122 	ipsecesp_stack_t	*espstack = ns->netstack_ipsecesp;
5887c478bd9Sstevel@tonic-gate 
5897c478bd9Sstevel@tonic-gate 	/* No peer?  No problem! */
5907c478bd9Sstevel@tonic-gate 	if (!assoc->ipsa_haspeer) {
591f4b3ec61Sdh155122 		return (sadb_age_bytes(espstack->esp_pfkey_q, assoc, bytes,
5927c478bd9Sstevel@tonic-gate 		    B_TRUE));
5937c478bd9Sstevel@tonic-gate 	}
5947c478bd9Sstevel@tonic-gate 
5957c478bd9Sstevel@tonic-gate 	/*
5967c478bd9Sstevel@tonic-gate 	 * Otherwise, we want to grab both the original assoc and its peer.
5977c478bd9Sstevel@tonic-gate 	 * There might be a race for this, but if it's a real race, two
5987c478bd9Sstevel@tonic-gate 	 * expire messages may occur.  We limit this by only sending the
5997c478bd9Sstevel@tonic-gate 	 * expire message on one of the peers, we'll pick the inbound
6007c478bd9Sstevel@tonic-gate 	 * arbitrarily.
6017c478bd9Sstevel@tonic-gate 	 *
6027c478bd9Sstevel@tonic-gate 	 * If we need tight synchronization on the peer SA, then we need to
6037c478bd9Sstevel@tonic-gate 	 * reconsider.
6047c478bd9Sstevel@tonic-gate 	 */
6057c478bd9Sstevel@tonic-gate 
6067c478bd9Sstevel@tonic-gate 	/* Use address length to select IPv6/IPv4 */
6077c478bd9Sstevel@tonic-gate 	isv6 = (assoc->ipsa_addrfam == AF_INET6);
608f4b3ec61Sdh155122 	sp = isv6 ? &espstack->esp_sadb.s_v6 : &espstack->esp_sadb.s_v4;
6097c478bd9Sstevel@tonic-gate 
6107c478bd9Sstevel@tonic-gate 	if (inbound) {
6117c478bd9Sstevel@tonic-gate 		inassoc = assoc;
6127c478bd9Sstevel@tonic-gate 		if (isv6) {
613fb87b5d2Ssommerfe 			outhash = OUTBOUND_HASH_V6(sp, *((in6_addr_t *)
6147c478bd9Sstevel@tonic-gate 			    &inassoc->ipsa_dstaddr));
6157c478bd9Sstevel@tonic-gate 		} else {
616fb87b5d2Ssommerfe 			outhash = OUTBOUND_HASH_V4(sp, *((ipaddr_t *)
6177c478bd9Sstevel@tonic-gate 			    &inassoc->ipsa_dstaddr));
6187c478bd9Sstevel@tonic-gate 		}
6197c478bd9Sstevel@tonic-gate 		bucket = &sp->sdb_of[outhash];
6207c478bd9Sstevel@tonic-gate 		mutex_enter(&bucket->isaf_lock);
6217c478bd9Sstevel@tonic-gate 		outassoc = ipsec_getassocbyspi(bucket, inassoc->ipsa_spi,
6227c478bd9Sstevel@tonic-gate 		    inassoc->ipsa_srcaddr, inassoc->ipsa_dstaddr,
6237c478bd9Sstevel@tonic-gate 		    inassoc->ipsa_addrfam);
6247c478bd9Sstevel@tonic-gate 		mutex_exit(&bucket->isaf_lock);
6257c478bd9Sstevel@tonic-gate 		if (outassoc == NULL) {
6267c478bd9Sstevel@tonic-gate 			/* Q: Do we wish to set haspeer == B_FALSE? */
6277c478bd9Sstevel@tonic-gate 			esp0dbg(("esp_age_bytes: "
6287c478bd9Sstevel@tonic-gate 			    "can't find peer for inbound.\n"));
629f4b3ec61Sdh155122 			return (sadb_age_bytes(espstack->esp_pfkey_q, inassoc,
6307c478bd9Sstevel@tonic-gate 			    bytes, B_TRUE));
6317c478bd9Sstevel@tonic-gate 		}
6327c478bd9Sstevel@tonic-gate 	} else {
6337c478bd9Sstevel@tonic-gate 		outassoc = assoc;
634fb87b5d2Ssommerfe 		bucket = INBOUND_BUCKET(sp, outassoc->ipsa_spi);
6357c478bd9Sstevel@tonic-gate 		mutex_enter(&bucket->isaf_lock);
6367c478bd9Sstevel@tonic-gate 		inassoc = ipsec_getassocbyspi(bucket, outassoc->ipsa_spi,
6377c478bd9Sstevel@tonic-gate 		    outassoc->ipsa_srcaddr, outassoc->ipsa_dstaddr,
6387c478bd9Sstevel@tonic-gate 		    outassoc->ipsa_addrfam);
6397c478bd9Sstevel@tonic-gate 		mutex_exit(&bucket->isaf_lock);
6407c478bd9Sstevel@tonic-gate 		if (inassoc == NULL) {
6417c478bd9Sstevel@tonic-gate 			/* Q: Do we wish to set haspeer == B_FALSE? */
6427c478bd9Sstevel@tonic-gate 			esp0dbg(("esp_age_bytes: "
6437c478bd9Sstevel@tonic-gate 			    "can't find peer for outbound.\n"));
644f4b3ec61Sdh155122 			return (sadb_age_bytes(espstack->esp_pfkey_q, outassoc,
6457c478bd9Sstevel@tonic-gate 			    bytes, B_TRUE));
6467c478bd9Sstevel@tonic-gate 		}
6477c478bd9Sstevel@tonic-gate 	}
6487c478bd9Sstevel@tonic-gate 
649f4b3ec61Sdh155122 	inrc = sadb_age_bytes(espstack->esp_pfkey_q, inassoc, bytes, B_TRUE);
650f4b3ec61Sdh155122 	outrc = sadb_age_bytes(espstack->esp_pfkey_q, outassoc, bytes, B_FALSE);
6517c478bd9Sstevel@tonic-gate 
6527c478bd9Sstevel@tonic-gate 	/*
6537c478bd9Sstevel@tonic-gate 	 * REFRELE any peer SA.
6547c478bd9Sstevel@tonic-gate 	 *
6557c478bd9Sstevel@tonic-gate 	 * Because of the multi-line macro nature of IPSA_REFRELE, keep
6567c478bd9Sstevel@tonic-gate 	 * them in { }.
6577c478bd9Sstevel@tonic-gate 	 */
6587c478bd9Sstevel@tonic-gate 	if (inbound) {
6597c478bd9Sstevel@tonic-gate 		IPSA_REFRELE(outassoc);
6607c478bd9Sstevel@tonic-gate 	} else {
6617c478bd9Sstevel@tonic-gate 		IPSA_REFRELE(inassoc);
6627c478bd9Sstevel@tonic-gate 	}
6637c478bd9Sstevel@tonic-gate 
6647c478bd9Sstevel@tonic-gate 	return (inrc && outrc);
6657c478bd9Sstevel@tonic-gate }
6667c478bd9Sstevel@tonic-gate 
6677c478bd9Sstevel@tonic-gate /*
6687c478bd9Sstevel@tonic-gate  * Do incoming NAT-T manipulations for packet.
669bd670b35SErik Nordmark  * Returns NULL if the mblk chain is consumed.
6707c478bd9Sstevel@tonic-gate  */
671bd670b35SErik Nordmark static mblk_t *
esp_fix_natt_checksums(mblk_t * data_mp,ipsa_t * assoc)6727c478bd9Sstevel@tonic-gate esp_fix_natt_checksums(mblk_t *data_mp, ipsa_t *assoc)
6737c478bd9Sstevel@tonic-gate {
6747c478bd9Sstevel@tonic-gate 	ipha_t *ipha = (ipha_t *)data_mp->b_rptr;
675bd670b35SErik Nordmark 	tcpha_t *tcpha;
6767c478bd9Sstevel@tonic-gate 	udpha_t *udpha;
6777c478bd9Sstevel@tonic-gate 	/* Initialize to our inbound cksum adjustment... */
6787c478bd9Sstevel@tonic-gate 	uint32_t sum = assoc->ipsa_inbound_cksum;
6797c478bd9Sstevel@tonic-gate 
6807c478bd9Sstevel@tonic-gate 	switch (ipha->ipha_protocol) {
6817c478bd9Sstevel@tonic-gate 	case IPPROTO_TCP:
682bd670b35SErik Nordmark 		tcpha = (tcpha_t *)(data_mp->b_rptr +
6837c478bd9Sstevel@tonic-gate 		    IPH_HDR_LENGTH(ipha));
6847c478bd9Sstevel@tonic-gate 
6857c478bd9Sstevel@tonic-gate #define	DOWN_SUM(x) (x) = ((x) & 0xFFFF) +	 ((x) >> 16)
686bd670b35SErik Nordmark 		sum += ~ntohs(tcpha->tha_sum) & 0xFFFF;
6877c478bd9Sstevel@tonic-gate 		DOWN_SUM(sum);
6887c478bd9Sstevel@tonic-gate 		DOWN_SUM(sum);
689bd670b35SErik Nordmark 		tcpha->tha_sum = ~htons(sum);
6907c478bd9Sstevel@tonic-gate 		break;
6917c478bd9Sstevel@tonic-gate 	case IPPROTO_UDP:
6927c478bd9Sstevel@tonic-gate 		udpha = (udpha_t *)(data_mp->b_rptr + IPH_HDR_LENGTH(ipha));
6937c478bd9Sstevel@tonic-gate 
6947c478bd9Sstevel@tonic-gate 		if (udpha->uha_checksum != 0) {
6957c478bd9Sstevel@tonic-gate 			/* Adujst if the inbound one was not zero. */
6967c478bd9Sstevel@tonic-gate 			sum += ~ntohs(udpha->uha_checksum) & 0xFFFF;
6977c478bd9Sstevel@tonic-gate 			DOWN_SUM(sum);
6987c478bd9Sstevel@tonic-gate 			DOWN_SUM(sum);
6997c478bd9Sstevel@tonic-gate 			udpha->uha_checksum = ~htons(sum);
7007c478bd9Sstevel@tonic-gate 			if (udpha->uha_checksum == 0)
7017c478bd9Sstevel@tonic-gate 				udpha->uha_checksum = 0xFFFF;
7027c478bd9Sstevel@tonic-gate 		}
7037c478bd9Sstevel@tonic-gate #undef DOWN_SUM
7047c478bd9Sstevel@tonic-gate 		break;
7057c478bd9Sstevel@tonic-gate 	case IPPROTO_IP:
7067c478bd9Sstevel@tonic-gate 		/*
7077c478bd9Sstevel@tonic-gate 		 * This case is only an issue for self-encapsulated
7087c478bd9Sstevel@tonic-gate 		 * packets.  So for now, fall through.
7097c478bd9Sstevel@tonic-gate 		 */
7107c478bd9Sstevel@tonic-gate 		break;
7117c478bd9Sstevel@tonic-gate 	}
712bd670b35SErik Nordmark 	return (data_mp);
7137c478bd9Sstevel@tonic-gate }
7147c478bd9Sstevel@tonic-gate 
7157c478bd9Sstevel@tonic-gate 
7167c478bd9Sstevel@tonic-gate /*
71732350c00Sdanmcd  * Strip ESP header, check padding, and fix IP header.
7187c478bd9Sstevel@tonic-gate  * Returns B_TRUE on success, B_FALSE if an error occured.
7197c478bd9Sstevel@tonic-gate  */
7207c478bd9Sstevel@tonic-gate static boolean_t
esp_strip_header(mblk_t * data_mp,boolean_t isv4,uint32_t ivlen,kstat_named_t ** counter,ipsecesp_stack_t * espstack)7217c478bd9Sstevel@tonic-gate esp_strip_header(mblk_t *data_mp, boolean_t isv4, uint32_t ivlen,
722f4b3ec61Sdh155122     kstat_named_t **counter, ipsecesp_stack_t *espstack)
7237c478bd9Sstevel@tonic-gate {
7247c478bd9Sstevel@tonic-gate 	ipha_t *ipha;
7257c478bd9Sstevel@tonic-gate 	ip6_t *ip6h;
7267c478bd9Sstevel@tonic-gate 	uint_t divpoint;
7277c478bd9Sstevel@tonic-gate 	mblk_t *scratch;
7287c478bd9Sstevel@tonic-gate 	uint8_t nexthdr, padlen;
7297c478bd9Sstevel@tonic-gate 	uint8_t lastpad;
730f4b3ec61Sdh155122 	ipsec_stack_t	*ipss = espstack->ipsecesp_netstack->netstack_ipsec;
73132350c00Sdanmcd 	uint8_t *lastbyte;
7327c478bd9Sstevel@tonic-gate 
7337c478bd9Sstevel@tonic-gate 	/*
7347c478bd9Sstevel@tonic-gate 	 * Strip ESP data and fix IP header.
7357c478bd9Sstevel@tonic-gate 	 *
7367c478bd9Sstevel@tonic-gate 	 * XXX In case the beginning of esp_inbound() changes to not do a
7377c478bd9Sstevel@tonic-gate 	 * pullup, this part of the code can remain unchanged.
7387c478bd9Sstevel@tonic-gate 	 */
7397c478bd9Sstevel@tonic-gate 	if (isv4) {
7407c478bd9Sstevel@tonic-gate 		ASSERT((data_mp->b_wptr - data_mp->b_rptr) >= sizeof (ipha_t));
7417c478bd9Sstevel@tonic-gate 		ipha = (ipha_t *)data_mp->b_rptr;
7427c478bd9Sstevel@tonic-gate 		ASSERT((data_mp->b_wptr - data_mp->b_rptr) >= sizeof (esph_t) +
7437c478bd9Sstevel@tonic-gate 		    IPH_HDR_LENGTH(ipha));
7447c478bd9Sstevel@tonic-gate 		divpoint = IPH_HDR_LENGTH(ipha);
7457c478bd9Sstevel@tonic-gate 	} else {
7467c478bd9Sstevel@tonic-gate 		ASSERT((data_mp->b_wptr - data_mp->b_rptr) >= sizeof (ip6_t));
7477c478bd9Sstevel@tonic-gate 		ip6h = (ip6_t *)data_mp->b_rptr;
7487c478bd9Sstevel@tonic-gate 		divpoint = ip_hdr_length_v6(data_mp, ip6h);
7497c478bd9Sstevel@tonic-gate 	}
7507c478bd9Sstevel@tonic-gate 
7517c478bd9Sstevel@tonic-gate 	scratch = data_mp;
7527c478bd9Sstevel@tonic-gate 	while (scratch->b_cont != NULL)
7537c478bd9Sstevel@tonic-gate 		scratch = scratch->b_cont;
7547c478bd9Sstevel@tonic-gate 
7557c478bd9Sstevel@tonic-gate 	ASSERT((scratch->b_wptr - scratch->b_rptr) >= 3);
7567c478bd9Sstevel@tonic-gate 
7577c478bd9Sstevel@tonic-gate 	/*
7587c478bd9Sstevel@tonic-gate 	 * "Next header" and padding length are the last two bytes in the
7597c478bd9Sstevel@tonic-gate 	 * ESP-protected datagram, thus the explicit - 1 and - 2.
7607c478bd9Sstevel@tonic-gate 	 * lastpad is the last byte of the padding, which can be used for
7617c478bd9Sstevel@tonic-gate 	 * a quick check to see if the padding is correct.
7627c478bd9Sstevel@tonic-gate 	 */
76332350c00Sdanmcd 	lastbyte = scratch->b_wptr - 1;
76432350c00Sdanmcd 	nexthdr = *lastbyte--;
76532350c00Sdanmcd 	padlen = *lastbyte--;
7667c478bd9Sstevel@tonic-gate 
7677c478bd9Sstevel@tonic-gate 	if (isv4) {
7687c478bd9Sstevel@tonic-gate 		/* Fix part of the IP header. */
7697c478bd9Sstevel@tonic-gate 		ipha->ipha_protocol = nexthdr;
7707c478bd9Sstevel@tonic-gate 		/*
7717c478bd9Sstevel@tonic-gate 		 * Reality check the padlen.  The explicit - 2 is for the
7727c478bd9Sstevel@tonic-gate 		 * padding length and the next-header bytes.
7737c478bd9Sstevel@tonic-gate 		 */
7747c478bd9Sstevel@tonic-gate 		if (padlen >= ntohs(ipha->ipha_length) - sizeof (ipha_t) - 2 -
7757c478bd9Sstevel@tonic-gate 		    sizeof (esph_t) - ivlen) {
776f4b3ec61Sdh155122 			ESP_BUMP_STAT(espstack, bad_decrypt);
777f4b3ec61Sdh155122 			ipsec_rl_strlog(espstack->ipsecesp_netstack,
778f4b3ec61Sdh155122 			    info.mi_idnum, 0, 0,
779f4b3ec61Sdh155122 			    SL_ERROR | SL_WARN,
78032350c00Sdanmcd 			    "Corrupt ESP packet (padlen too big).\n");
781f4b3ec61Sdh155122 			esp1dbg(espstack, ("padlen (%d) is greater than:\n",
782f4b3ec61Sdh155122 			    padlen));
783f4b3ec61Sdh155122 			esp1dbg(espstack, ("pkt len(%d) - ip hdr - esp "
784f4b3ec61Sdh155122 			    "hdr - ivlen(%d) = %d.\n",
785f4b3ec61Sdh155122 			    ntohs(ipha->ipha_length), ivlen,
7867c478bd9Sstevel@tonic-gate 			    (int)(ntohs(ipha->ipha_length) - sizeof (ipha_t) -
7877c478bd9Sstevel@tonic-gate 			    2 - sizeof (esph_t) - ivlen)));
788f4b3ec61Sdh155122 			*counter = DROPPER(ipss, ipds_esp_bad_padlen);
7897c478bd9Sstevel@tonic-gate 			return (B_FALSE);
7907c478bd9Sstevel@tonic-gate 		}
7917c478bd9Sstevel@tonic-gate 
7927c478bd9Sstevel@tonic-gate 		/*
7937c478bd9Sstevel@tonic-gate 		 * Fix the rest of the header.  The explicit - 2 is for the
7947c478bd9Sstevel@tonic-gate 		 * padding length and the next-header bytes.
7957c478bd9Sstevel@tonic-gate 		 */
7967c478bd9Sstevel@tonic-gate 		ipha->ipha_length = htons(ntohs(ipha->ipha_length) - padlen -
7977c478bd9Sstevel@tonic-gate 		    2 - sizeof (esph_t) - ivlen);
7987c478bd9Sstevel@tonic-gate 		ipha->ipha_hdr_checksum = 0;
7997c478bd9Sstevel@tonic-gate 		ipha->ipha_hdr_checksum = (uint16_t)ip_csum_hdr(ipha);
8007c478bd9Sstevel@tonic-gate 	} else {
8017c478bd9Sstevel@tonic-gate 		if (ip6h->ip6_nxt == IPPROTO_ESP) {
8027c478bd9Sstevel@tonic-gate 			ip6h->ip6_nxt = nexthdr;
8037c478bd9Sstevel@tonic-gate 		} else {
804bd670b35SErik Nordmark 			ip_pkt_t ipp;
8057c478bd9Sstevel@tonic-gate 
8067c478bd9Sstevel@tonic-gate 			bzero(&ipp, sizeof (ipp));
807bd670b35SErik Nordmark 			(void) ip_find_hdr_v6(data_mp, ip6h, B_FALSE, &ipp,
808bd670b35SErik Nordmark 			    NULL);
8097c478bd9Sstevel@tonic-gate 			if (ipp.ipp_dstopts != NULL) {
8107c478bd9Sstevel@tonic-gate 				ipp.ipp_dstopts->ip6d_nxt = nexthdr;
8117c478bd9Sstevel@tonic-gate 			} else if (ipp.ipp_rthdr != NULL) {
8127c478bd9Sstevel@tonic-gate 				ipp.ipp_rthdr->ip6r_nxt = nexthdr;
8137c478bd9Sstevel@tonic-gate 			} else if (ipp.ipp_hopopts != NULL) {
8147c478bd9Sstevel@tonic-gate 				ipp.ipp_hopopts->ip6h_nxt = nexthdr;
8157c478bd9Sstevel@tonic-gate 			} else {
8167c478bd9Sstevel@tonic-gate 				/* Panic a DEBUG kernel. */
8177c478bd9Sstevel@tonic-gate 				ASSERT(ipp.ipp_hopopts != NULL);
8187c478bd9Sstevel@tonic-gate 				/* Otherwise, pretend it's IP + ESP. */
8197c478bd9Sstevel@tonic-gate 				cmn_err(CE_WARN, "ESP IPv6 headers wrong.\n");
8207c478bd9Sstevel@tonic-gate 				ip6h->ip6_nxt = nexthdr;
8217c478bd9Sstevel@tonic-gate 			}
8227c478bd9Sstevel@tonic-gate 		}
8237c478bd9Sstevel@tonic-gate 
8247c478bd9Sstevel@tonic-gate 		if (padlen >= ntohs(ip6h->ip6_plen) - 2 - sizeof (esph_t) -
8257c478bd9Sstevel@tonic-gate 		    ivlen) {
826f4b3ec61Sdh155122 			ESP_BUMP_STAT(espstack, bad_decrypt);
827f4b3ec61Sdh155122 			ipsec_rl_strlog(espstack->ipsecesp_netstack,
828f4b3ec61Sdh155122 			    info.mi_idnum, 0, 0,
829f4b3ec61Sdh155122 			    SL_ERROR | SL_WARN,
83032350c00Sdanmcd 			    "Corrupt ESP packet (v6 padlen too big).\n");
831f4b3ec61Sdh155122 			esp1dbg(espstack, ("padlen (%d) is greater than:\n",
832f4b3ec61Sdh155122 			    padlen));
833437220cdSdanmcd 			esp1dbg(espstack,
834437220cdSdanmcd 			    ("pkt len(%u) - ip hdr - esp hdr - ivlen(%d) = "
835437220cdSdanmcd 			    "%u.\n", (unsigned)(ntohs(ip6h->ip6_plen)
8367c478bd9Sstevel@tonic-gate 			    + sizeof (ip6_t)), ivlen,
8377c478bd9Sstevel@tonic-gate 			    (unsigned)(ntohs(ip6h->ip6_plen) - 2 -
8387c478bd9Sstevel@tonic-gate 			    sizeof (esph_t) - ivlen)));
839f4b3ec61Sdh155122 			*counter = DROPPER(ipss, ipds_esp_bad_padlen);
8407c478bd9Sstevel@tonic-gate 			return (B_FALSE);
8417c478bd9Sstevel@tonic-gate 		}
8427c478bd9Sstevel@tonic-gate 
8437c478bd9Sstevel@tonic-gate 
8447c478bd9Sstevel@tonic-gate 		/*
8457c478bd9Sstevel@tonic-gate 		 * Fix the rest of the header.  The explicit - 2 is for the
8467c478bd9Sstevel@tonic-gate 		 * padding length and the next-header bytes.  IPv6 is nice,
8477c478bd9Sstevel@tonic-gate 		 * because there's no hdr checksum!
8487c478bd9Sstevel@tonic-gate 		 */
8497c478bd9Sstevel@tonic-gate 		ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) - padlen -
8507c478bd9Sstevel@tonic-gate 		    2 - sizeof (esph_t) - ivlen);
8517c478bd9Sstevel@tonic-gate 	}
8527c478bd9Sstevel@tonic-gate 
853f4b3ec61Sdh155122 	if (espstack->ipsecesp_padding_check > 0 && padlen > 0) {
85432350c00Sdanmcd 		/*
85532350c00Sdanmcd 		 * Weak padding check: compare last-byte to length, they
85632350c00Sdanmcd 		 * should be equal.
85732350c00Sdanmcd 		 */
85832350c00Sdanmcd 		lastpad = *lastbyte--;
85932350c00Sdanmcd 
86032350c00Sdanmcd 		if (padlen != lastpad) {
861f4b3ec61Sdh155122 			ipsec_rl_strlog(espstack->ipsecesp_netstack,
862f4b3ec61Sdh155122 			    info.mi_idnum, 0, 0, SL_ERROR | SL_WARN,
86332350c00Sdanmcd 			    "Corrupt ESP packet (lastpad != padlen).\n");
864f4b3ec61Sdh155122 			esp1dbg(espstack,
865f4b3ec61Sdh155122 			    ("lastpad (%d) not equal to padlen (%d):\n",
8667c478bd9Sstevel@tonic-gate 			    lastpad, padlen));
867f4b3ec61Sdh155122 			ESP_BUMP_STAT(espstack, bad_padding);
868f4b3ec61Sdh155122 			*counter = DROPPER(ipss, ipds_esp_bad_padding);
8697c478bd9Sstevel@tonic-gate 			return (B_FALSE);
8707c478bd9Sstevel@tonic-gate 		}
8717c478bd9Sstevel@tonic-gate 
87232350c00Sdanmcd 		/*
87332350c00Sdanmcd 		 * Strong padding check: Check all pad bytes to see that
87432350c00Sdanmcd 		 * they're ascending.  Go backwards using a descending counter
87532350c00Sdanmcd 		 * to verify.  padlen == 1 is checked by previous block, so
87632350c00Sdanmcd 		 * only bother if we've more than 1 byte of padding.
87732350c00Sdanmcd 		 * Consequently, start the check one byte before the location
87832350c00Sdanmcd 		 * of "lastpad".
87932350c00Sdanmcd 		 */
880f4b3ec61Sdh155122 		if (espstack->ipsecesp_padding_check > 1) {
88132350c00Sdanmcd 			/*
88232350c00Sdanmcd 			 * This assert may have to become an if and a pullup
88332350c00Sdanmcd 			 * if we start accepting multi-dblk mblks. For now,
88432350c00Sdanmcd 			 * though, any packet here will have been pulled up in
88532350c00Sdanmcd 			 * esp_inbound.
88632350c00Sdanmcd 			 */
88732350c00Sdanmcd 			ASSERT(MBLKL(scratch) >= lastpad + 3);
8887c478bd9Sstevel@tonic-gate 
8897c478bd9Sstevel@tonic-gate 			/*
89032350c00Sdanmcd 			 * Use "--lastpad" because we already checked the very
89132350c00Sdanmcd 			 * last pad byte previously.
8927c478bd9Sstevel@tonic-gate 			 */
89332350c00Sdanmcd 			while (--lastpad != 0) {
89432350c00Sdanmcd 				if (lastpad != *lastbyte) {
895f4b3ec61Sdh155122 					ipsec_rl_strlog(
896f4b3ec61Sdh155122 					    espstack->ipsecesp_netstack,
897f4b3ec61Sdh155122 					    info.mi_idnum, 0, 0,
89832350c00Sdanmcd 					    SL_ERROR | SL_WARN, "Corrupt ESP "
89932350c00Sdanmcd 					    "packet (bad padding).\n");
900f4b3ec61Sdh155122 					esp1dbg(espstack,
901f4b3ec61Sdh155122 					    ("padding not in correct"
9027c478bd9Sstevel@tonic-gate 					    " format:\n"));
903f4b3ec61Sdh155122 					ESP_BUMP_STAT(espstack, bad_padding);
904f4b3ec61Sdh155122 					*counter = DROPPER(ipss,
905f4b3ec61Sdh155122 					    ipds_esp_bad_padding);
9067c478bd9Sstevel@tonic-gate 					return (B_FALSE);
9077c478bd9Sstevel@tonic-gate 				}
90832350c00Sdanmcd 				lastbyte--;
90932350c00Sdanmcd 			}
9107c478bd9Sstevel@tonic-gate 		}
9117c478bd9Sstevel@tonic-gate 	}
9127c478bd9Sstevel@tonic-gate 
9137c478bd9Sstevel@tonic-gate 	/* Trim off the padding. */
9147c478bd9Sstevel@tonic-gate 	ASSERT(data_mp->b_cont == NULL);
9157c478bd9Sstevel@tonic-gate 	data_mp->b_wptr -= (padlen + 2);
9167c478bd9Sstevel@tonic-gate 
9177c478bd9Sstevel@tonic-gate 	/*
9187c478bd9Sstevel@tonic-gate 	 * Remove the ESP header.
9197c478bd9Sstevel@tonic-gate 	 *
9207c478bd9Sstevel@tonic-gate 	 * The above assertions about data_mp's size will make this work.
9217c478bd9Sstevel@tonic-gate 	 *
9227c478bd9Sstevel@tonic-gate 	 * XXX  Question:  If I send up and get back a contiguous mblk,
9237c478bd9Sstevel@tonic-gate 	 * would it be quicker to bcopy over, or keep doing the dupb stuff?
9247c478bd9Sstevel@tonic-gate 	 * I go with copying for now.
9257c478bd9Sstevel@tonic-gate 	 */
9267c478bd9Sstevel@tonic-gate 
9277c478bd9Sstevel@tonic-gate 	if (IS_P2ALIGNED(data_mp->b_rptr, sizeof (uint32_t)) &&
9287c478bd9Sstevel@tonic-gate 	    IS_P2ALIGNED(ivlen, sizeof (uint32_t))) {
9297c478bd9Sstevel@tonic-gate 		uint8_t *start = data_mp->b_rptr;
9307c478bd9Sstevel@tonic-gate 		uint32_t *src, *dst;
9317c478bd9Sstevel@tonic-gate 
9327c478bd9Sstevel@tonic-gate 		src = (uint32_t *)(start + divpoint);
9337c478bd9Sstevel@tonic-gate 		dst = (uint32_t *)(start + divpoint + sizeof (esph_t) + ivlen);
9347c478bd9Sstevel@tonic-gate 
9357c478bd9Sstevel@tonic-gate 		ASSERT(IS_P2ALIGNED(dst, sizeof (uint32_t)) &&
9367c478bd9Sstevel@tonic-gate 		    IS_P2ALIGNED(src, sizeof (uint32_t)));
9377c478bd9Sstevel@tonic-gate 
9387c478bd9Sstevel@tonic-gate 		do {
9397c478bd9Sstevel@tonic-gate 			src--;
9407c478bd9Sstevel@tonic-gate 			dst--;
9417c478bd9Sstevel@tonic-gate 			*dst = *src;
9427c478bd9Sstevel@tonic-gate 		} while (src != (uint32_t *)start);
9437c478bd9Sstevel@tonic-gate 
9447c478bd9Sstevel@tonic-gate 		data_mp->b_rptr = (uchar_t *)dst;
9457c478bd9Sstevel@tonic-gate 	} else {
9467c478bd9Sstevel@tonic-gate 		uint8_t *start = data_mp->b_rptr;
9477c478bd9Sstevel@tonic-gate 		uint8_t *src, *dst;
9487c478bd9Sstevel@tonic-gate 
9497c478bd9Sstevel@tonic-gate 		src = start + divpoint;
9507c478bd9Sstevel@tonic-gate 		dst = src + sizeof (esph_t) + ivlen;
9517c478bd9Sstevel@tonic-gate 
9527c478bd9Sstevel@tonic-gate 		do {
9537c478bd9Sstevel@tonic-gate 			src--;
9547c478bd9Sstevel@tonic-gate 			dst--;
9557c478bd9Sstevel@tonic-gate 			*dst = *src;
9567c478bd9Sstevel@tonic-gate 		} while (src != start);
9577c478bd9Sstevel@tonic-gate 
9587c478bd9Sstevel@tonic-gate 		data_mp->b_rptr = dst;
9597c478bd9Sstevel@tonic-gate 	}
9607c478bd9Sstevel@tonic-gate 
961f4b3ec61Sdh155122 	esp2dbg(espstack, ("data_mp after inbound ESP adjustment:\n"));
962f4b3ec61Sdh155122 	esp2dbg(espstack, (dump_msg(data_mp)));
9637c478bd9Sstevel@tonic-gate 
9647c478bd9Sstevel@tonic-gate 	return (B_TRUE);
9657c478bd9Sstevel@tonic-gate }
9667c478bd9Sstevel@tonic-gate 
9677c478bd9Sstevel@tonic-gate /*
9687c478bd9Sstevel@tonic-gate  * Updating use times can be tricky business if the ipsa_haspeer flag is
9697c478bd9Sstevel@tonic-gate  * set.  This function is called once in an SA's lifetime.
9707c478bd9Sstevel@tonic-gate  *
9717c478bd9Sstevel@tonic-gate  * Caller has to REFRELE "assoc" which is passed in.  This function has
9727c478bd9Sstevel@tonic-gate  * to REFRELE any peer SA that is obtained.
9737c478bd9Sstevel@tonic-gate  */
9747c478bd9Sstevel@tonic-gate static void
esp_set_usetime(ipsa_t * assoc,boolean_t inbound)9757c478bd9Sstevel@tonic-gate esp_set_usetime(ipsa_t *assoc, boolean_t inbound)
9767c478bd9Sstevel@tonic-gate {
9777c478bd9Sstevel@tonic-gate 	ipsa_t *inassoc, *outassoc;
9787c478bd9Sstevel@tonic-gate 	isaf_t *bucket;
9797c478bd9Sstevel@tonic-gate 	sadb_t *sp;
9807c478bd9Sstevel@tonic-gate 	int outhash;
9817c478bd9Sstevel@tonic-gate 	boolean_t isv6;
982f4b3ec61Sdh155122 	netstack_t		*ns = assoc->ipsa_netstack;
983f4b3ec61Sdh155122 	ipsecesp_stack_t	*espstack = ns->netstack_ipsecesp;
9847c478bd9Sstevel@tonic-gate 
9857c478bd9Sstevel@tonic-gate 	/* No peer?  No problem! */
9867c478bd9Sstevel@tonic-gate 	if (!assoc->ipsa_haspeer) {
9877c478bd9Sstevel@tonic-gate 		sadb_set_usetime(assoc);
9887c478bd9Sstevel@tonic-gate 		return;
9897c478bd9Sstevel@tonic-gate 	}
9907c478bd9Sstevel@tonic-gate 
9917c478bd9Sstevel@tonic-gate 	/*
9927c478bd9Sstevel@tonic-gate 	 * Otherwise, we want to grab both the original assoc and its peer.
9937c478bd9Sstevel@tonic-gate 	 * There might be a race for this, but if it's a real race, the times
9947c478bd9Sstevel@tonic-gate 	 * will be out-of-synch by at most a second, and since our time
9957c478bd9Sstevel@tonic-gate 	 * granularity is a second, this won't be a problem.
9967c478bd9Sstevel@tonic-gate 	 *
9977c478bd9Sstevel@tonic-gate 	 * If we need tight synchronization on the peer SA, then we need to
9987c478bd9Sstevel@tonic-gate 	 * reconsider.
9997c478bd9Sstevel@tonic-gate 	 */
10007c478bd9Sstevel@tonic-gate 
10017c478bd9Sstevel@tonic-gate 	/* Use address length to select IPv6/IPv4 */
10027c478bd9Sstevel@tonic-gate 	isv6 = (assoc->ipsa_addrfam == AF_INET6);
1003f4b3ec61Sdh155122 	sp = isv6 ? &espstack->esp_sadb.s_v6 : &espstack->esp_sadb.s_v4;
10047c478bd9Sstevel@tonic-gate 
10057c478bd9Sstevel@tonic-gate 	if (inbound) {
10067c478bd9Sstevel@tonic-gate 		inassoc = assoc;
10077c478bd9Sstevel@tonic-gate 		if (isv6) {
1008fb87b5d2Ssommerfe 			outhash = OUTBOUND_HASH_V6(sp, *((in6_addr_t *)
10097c478bd9Sstevel@tonic-gate 			    &inassoc->ipsa_dstaddr));
10107c478bd9Sstevel@tonic-gate 		} else {
1011fb87b5d2Ssommerfe 			outhash = OUTBOUND_HASH_V4(sp, *((ipaddr_t *)
10127c478bd9Sstevel@tonic-gate 			    &inassoc->ipsa_dstaddr));
10137c478bd9Sstevel@tonic-gate 		}
10147c478bd9Sstevel@tonic-gate 		bucket = &sp->sdb_of[outhash];
10157c478bd9Sstevel@tonic-gate 		mutex_enter(&bucket->isaf_lock);
10167c478bd9Sstevel@tonic-gate 		outassoc = ipsec_getassocbyspi(bucket, inassoc->ipsa_spi,
10177c478bd9Sstevel@tonic-gate 		    inassoc->ipsa_srcaddr, inassoc->ipsa_dstaddr,
10187c478bd9Sstevel@tonic-gate 		    inassoc->ipsa_addrfam);
10197c478bd9Sstevel@tonic-gate 		mutex_exit(&bucket->isaf_lock);
10207c478bd9Sstevel@tonic-gate 		if (outassoc == NULL) {
10217c478bd9Sstevel@tonic-gate 			/* Q: Do we wish to set haspeer == B_FALSE? */
10227c478bd9Sstevel@tonic-gate 			esp0dbg(("esp_set_usetime: "
10237c478bd9Sstevel@tonic-gate 			    "can't find peer for inbound.\n"));
10247c478bd9Sstevel@tonic-gate 			sadb_set_usetime(inassoc);
10257c478bd9Sstevel@tonic-gate 			return;
10267c478bd9Sstevel@tonic-gate 		}
10277c478bd9Sstevel@tonic-gate 	} else {
10287c478bd9Sstevel@tonic-gate 		outassoc = assoc;
1029fb87b5d2Ssommerfe 		bucket = INBOUND_BUCKET(sp, outassoc->ipsa_spi);
10307c478bd9Sstevel@tonic-gate 		mutex_enter(&bucket->isaf_lock);
10317c478bd9Sstevel@tonic-gate 		inassoc = ipsec_getassocbyspi(bucket, outassoc->ipsa_spi,
10327c478bd9Sstevel@tonic-gate 		    outassoc->ipsa_srcaddr, outassoc->ipsa_dstaddr,
10337c478bd9Sstevel@tonic-gate 		    outassoc->ipsa_addrfam);
10347c478bd9Sstevel@tonic-gate 		mutex_exit(&bucket->isaf_lock);
10357c478bd9Sstevel@tonic-gate 		if (inassoc == NULL) {
10367c478bd9Sstevel@tonic-gate 			/* Q: Do we wish to set haspeer == B_FALSE? */
10377c478bd9Sstevel@tonic-gate 			esp0dbg(("esp_set_usetime: "
10387c478bd9Sstevel@tonic-gate 			    "can't find peer for outbound.\n"));
10397c478bd9Sstevel@tonic-gate 			sadb_set_usetime(outassoc);
10407c478bd9Sstevel@tonic-gate 			return;
10417c478bd9Sstevel@tonic-gate 		}
10427c478bd9Sstevel@tonic-gate 	}
10437c478bd9Sstevel@tonic-gate 
10447c478bd9Sstevel@tonic-gate 	/* Update usetime on both. */
10457c478bd9Sstevel@tonic-gate 	sadb_set_usetime(inassoc);
10467c478bd9Sstevel@tonic-gate 	sadb_set_usetime(outassoc);
10477c478bd9Sstevel@tonic-gate 
10487c478bd9Sstevel@tonic-gate 	/*
10497c478bd9Sstevel@tonic-gate 	 * REFRELE any peer SA.
10507c478bd9Sstevel@tonic-gate 	 *
10517c478bd9Sstevel@tonic-gate 	 * Because of the multi-line macro nature of IPSA_REFRELE, keep
10527c478bd9Sstevel@tonic-gate 	 * them in { }.
10537c478bd9Sstevel@tonic-gate 	 */
10547c478bd9Sstevel@tonic-gate 	if (inbound) {
10557c478bd9Sstevel@tonic-gate 		IPSA_REFRELE(outassoc);
10567c478bd9Sstevel@tonic-gate 	} else {
10577c478bd9Sstevel@tonic-gate 		IPSA_REFRELE(inassoc);
10587c478bd9Sstevel@tonic-gate 	}
10597c478bd9Sstevel@tonic-gate }
10607c478bd9Sstevel@tonic-gate 
10617c478bd9Sstevel@tonic-gate /*
10627c478bd9Sstevel@tonic-gate  * Handle ESP inbound data for IPv4 and IPv6.
10637c478bd9Sstevel@tonic-gate  * On success returns B_TRUE, on failure returns B_FALSE and frees the
1064bd670b35SErik Nordmark  * mblk chain data_mp.
10657c478bd9Sstevel@tonic-gate  */
1066bd670b35SErik Nordmark mblk_t *
esp_inbound(mblk_t * data_mp,void * arg,ip_recv_attr_t * ira)1067bd670b35SErik Nordmark esp_inbound(mblk_t *data_mp, void *arg, ip_recv_attr_t *ira)
10687c478bd9Sstevel@tonic-gate {
10697c478bd9Sstevel@tonic-gate 	esph_t *esph = (esph_t *)arg;
1070bd670b35SErik Nordmark 	ipsa_t *ipsa = ira->ira_ipsec_esp_sa;
1071bd670b35SErik Nordmark 	netstack_t	*ns = ira->ira_ill->ill_ipst->ips_netstack;
1072f4b3ec61Sdh155122 	ipsecesp_stack_t *espstack = ns->netstack_ipsecesp;
1073f4b3ec61Sdh155122 	ipsec_stack_t	*ipss = ns->netstack_ipsec;
10747c478bd9Sstevel@tonic-gate 
10757c478bd9Sstevel@tonic-gate 	/*
10767c478bd9Sstevel@tonic-gate 	 * We may wish to check replay in-range-only here as an optimization.
10777c478bd9Sstevel@tonic-gate 	 * Include the reality check of ipsa->ipsa_replay >
10787c478bd9Sstevel@tonic-gate 	 * ipsa->ipsa_replay_wsize for times when it's the first N packets,
10797c478bd9Sstevel@tonic-gate 	 * where N == ipsa->ipsa_replay_wsize.
10807c478bd9Sstevel@tonic-gate 	 *
10817c478bd9Sstevel@tonic-gate 	 * Another check that may come here later is the "collision" check.
10827c478bd9Sstevel@tonic-gate 	 * If legitimate packets flow quickly enough, this won't be a problem,
10837c478bd9Sstevel@tonic-gate 	 * but collisions may cause authentication algorithm crunching to
10847c478bd9Sstevel@tonic-gate 	 * take place when it doesn't need to.
10857c478bd9Sstevel@tonic-gate 	 */
10867c478bd9Sstevel@tonic-gate 	if (!sadb_replay_peek(ipsa, esph->esph_replay)) {
1087f4b3ec61Sdh155122 		ESP_BUMP_STAT(espstack, replay_early_failures);
1088f4b3ec61Sdh155122 		IP_ESP_BUMP_STAT(ipss, in_discards);
1089bd670b35SErik Nordmark 		ip_drop_packet(data_mp, B_TRUE, ira->ira_ill,
1090f4b3ec61Sdh155122 		    DROPPER(ipss, ipds_esp_early_replay),
1091f4b3ec61Sdh155122 		    &espstack->esp_dropper);
1092bd670b35SErik Nordmark 		BUMP_MIB(ira->ira_ill->ill_ip_mib, ipIfStatsInDiscards);
1093bd670b35SErik Nordmark 		return (NULL);
10947c478bd9Sstevel@tonic-gate 	}
10957c478bd9Sstevel@tonic-gate 
10967c478bd9Sstevel@tonic-gate 	/*
10977c478bd9Sstevel@tonic-gate 	 * Adjust the IP header's payload length to reflect the removal
10987c478bd9Sstevel@tonic-gate 	 * of the ICV.
10997c478bd9Sstevel@tonic-gate 	 */
1100bd670b35SErik Nordmark 	if (!(ira->ira_flags & IRAF_IS_IPV4)) {
11017c478bd9Sstevel@tonic-gate 		ip6_t *ip6h = (ip6_t *)data_mp->b_rptr;
11027c478bd9Sstevel@tonic-gate 		ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) -
11037c478bd9Sstevel@tonic-gate 		    ipsa->ipsa_mac_len);
11047c478bd9Sstevel@tonic-gate 	} else {
11057c478bd9Sstevel@tonic-gate 		ipha_t *ipha = (ipha_t *)data_mp->b_rptr;
11067c478bd9Sstevel@tonic-gate 		ipha->ipha_length = htons(ntohs(ipha->ipha_length) -
11077c478bd9Sstevel@tonic-gate 		    ipsa->ipsa_mac_len);
11087c478bd9Sstevel@tonic-gate 	}
11097c478bd9Sstevel@tonic-gate 
11107c478bd9Sstevel@tonic-gate 	/* submit the request to the crypto framework */
1111bd670b35SErik Nordmark 	return (esp_submit_req_inbound(data_mp, ira, ipsa,
11127c478bd9Sstevel@tonic-gate 	    (uint8_t *)esph - data_mp->b_rptr));
11137c478bd9Sstevel@tonic-gate }
11147c478bd9Sstevel@tonic-gate 
11155d3b8cb7SBill Sommerfeld /* XXX refactor me */
11167c478bd9Sstevel@tonic-gate /*
11177c478bd9Sstevel@tonic-gate  * Handle the SADB_GETSPI message.  Create a larval SA.
11187c478bd9Sstevel@tonic-gate  */
11197c478bd9Sstevel@tonic-gate static void
esp_getspi(mblk_t * mp,keysock_in_t * ksi,ipsecesp_stack_t * espstack)1120f4b3ec61Sdh155122 esp_getspi(mblk_t *mp, keysock_in_t *ksi, ipsecesp_stack_t *espstack)
11217c478bd9Sstevel@tonic-gate {
11227c478bd9Sstevel@tonic-gate 	ipsa_t *newbie, *target;
11237c478bd9Sstevel@tonic-gate 	isaf_t *outbound, *inbound;
11247c478bd9Sstevel@tonic-gate 	int rc, diagnostic;
11257c478bd9Sstevel@tonic-gate 	sadb_sa_t *assoc;
11267c478bd9Sstevel@tonic-gate 	keysock_out_t *kso;
11277c478bd9Sstevel@tonic-gate 	uint32_t newspi;
11287c478bd9Sstevel@tonic-gate 
11297c478bd9Sstevel@tonic-gate 	/*
11307c478bd9Sstevel@tonic-gate 	 * Randomly generate a proposed SPI value
11317c478bd9Sstevel@tonic-gate 	 */
11329c2c14abSThejaswini Singarajipura 	if (cl_inet_getspi != NULL) {
11338e4b770fSLu Huafeng 		cl_inet_getspi(espstack->ipsecesp_netstack->netstack_stackid,
11348e4b770fSLu Huafeng 		    IPPROTO_ESP, (uint8_t *)&newspi, sizeof (uint32_t), NULL);
11359c2c14abSThejaswini Singarajipura 	} else {
11369c2c14abSThejaswini Singarajipura 		(void) random_get_pseudo_bytes((uint8_t *)&newspi,
11379c2c14abSThejaswini Singarajipura 		    sizeof (uint32_t));
11389c2c14abSThejaswini Singarajipura 	}
1139f4b3ec61Sdh155122 	newbie = sadb_getspi(ksi, newspi, &diagnostic,
11409c2c14abSThejaswini Singarajipura 	    espstack->ipsecesp_netstack, IPPROTO_ESP);
11417c478bd9Sstevel@tonic-gate 
11427c478bd9Sstevel@tonic-gate 	if (newbie == NULL) {
1143f4b3ec61Sdh155122 		sadb_pfkey_error(espstack->esp_pfkey_q, mp, ENOMEM, diagnostic,
11447c478bd9Sstevel@tonic-gate 		    ksi->ks_in_serial);
11457c478bd9Sstevel@tonic-gate 		return;
11467c478bd9Sstevel@tonic-gate 	} else if (newbie == (ipsa_t *)-1) {
1147f4b3ec61Sdh155122 		sadb_pfkey_error(espstack->esp_pfkey_q, mp, EINVAL, diagnostic,
11487c478bd9Sstevel@tonic-gate 		    ksi->ks_in_serial);
11497c478bd9Sstevel@tonic-gate 		return;
11507c478bd9Sstevel@tonic-gate 	}
11517c478bd9Sstevel@tonic-gate 
11527c478bd9Sstevel@tonic-gate 	/*
11537c478bd9Sstevel@tonic-gate 	 * XXX - We may randomly collide.  We really should recover from this.
11547c478bd9Sstevel@tonic-gate 	 *	 Unfortunately, that could require spending way-too-much-time
11557c478bd9Sstevel@tonic-gate 	 *	 in here.  For now, let the user retry.
11567c478bd9Sstevel@tonic-gate 	 */
11577c478bd9Sstevel@tonic-gate 
11587c478bd9Sstevel@tonic-gate 	if (newbie->ipsa_addrfam == AF_INET6) {
1159f4b3ec61Sdh155122 		outbound = OUTBOUND_BUCKET_V6(&espstack->esp_sadb.s_v6,
1160fb87b5d2Ssommerfe 		    *(uint32_t *)(newbie->ipsa_dstaddr));
1161f4b3ec61Sdh155122 		inbound = INBOUND_BUCKET(&espstack->esp_sadb.s_v6,
1162f4b3ec61Sdh155122 		    newbie->ipsa_spi);
11637c478bd9Sstevel@tonic-gate 	} else {
11647c478bd9Sstevel@tonic-gate 		ASSERT(newbie->ipsa_addrfam == AF_INET);
1165f4b3ec61Sdh155122 		outbound = OUTBOUND_BUCKET_V4(&espstack->esp_sadb.s_v4,
1166fb87b5d2Ssommerfe 		    *(uint32_t *)(newbie->ipsa_dstaddr));
1167f4b3ec61Sdh155122 		inbound = INBOUND_BUCKET(&espstack->esp_sadb.s_v4,
1168f4b3ec61Sdh155122 		    newbie->ipsa_spi);
11697c478bd9Sstevel@tonic-gate 	}
11707c478bd9Sstevel@tonic-gate 
11717c478bd9Sstevel@tonic-gate 	mutex_enter(&outbound->isaf_lock);
11727c478bd9Sstevel@tonic-gate 	mutex_enter(&inbound->isaf_lock);
11737c478bd9Sstevel@tonic-gate 
11747c478bd9Sstevel@tonic-gate 	/*
11757c478bd9Sstevel@tonic-gate 	 * Check for collisions (i.e. did sadb_getspi() return with something
11767c478bd9Sstevel@tonic-gate 	 * that already exists?).
11777c478bd9Sstevel@tonic-gate 	 *
11787c478bd9Sstevel@tonic-gate 	 * Try outbound first.  Even though SADB_GETSPI is traditionally
11797c478bd9Sstevel@tonic-gate 	 * for inbound SAs, you never know what a user might do.
11807c478bd9Sstevel@tonic-gate 	 */
11817c478bd9Sstevel@tonic-gate 	target = ipsec_getassocbyspi(outbound, newbie->ipsa_spi,
11827c478bd9Sstevel@tonic-gate 	    newbie->ipsa_srcaddr, newbie->ipsa_dstaddr, newbie->ipsa_addrfam);
11837c478bd9Sstevel@tonic-gate 	if (target == NULL) {
11847c478bd9Sstevel@tonic-gate 		target = ipsec_getassocbyspi(inbound, newbie->ipsa_spi,
11857c478bd9Sstevel@tonic-gate 		    newbie->ipsa_srcaddr, newbie->ipsa_dstaddr,
11867c478bd9Sstevel@tonic-gate 		    newbie->ipsa_addrfam);
11877c478bd9Sstevel@tonic-gate 	}
11887c478bd9Sstevel@tonic-gate 
11897c478bd9Sstevel@tonic-gate 	/*
11907c478bd9Sstevel@tonic-gate 	 * I don't have collisions elsewhere!
11917c478bd9Sstevel@tonic-gate 	 * (Nor will I because I'm still holding inbound/outbound locks.)
11927c478bd9Sstevel@tonic-gate 	 */
11937c478bd9Sstevel@tonic-gate 
11947c478bd9Sstevel@tonic-gate 	if (target != NULL) {
11957c478bd9Sstevel@tonic-gate 		rc = EEXIST;
11967c478bd9Sstevel@tonic-gate 		IPSA_REFRELE(target);
11977c478bd9Sstevel@tonic-gate 	} else {
11987c478bd9Sstevel@tonic-gate 		/*
11997c478bd9Sstevel@tonic-gate 		 * sadb_insertassoc() also checks for collisions, so
12007c478bd9Sstevel@tonic-gate 		 * if there's a colliding entry, rc will be set
12017c478bd9Sstevel@tonic-gate 		 * to EEXIST.
12027c478bd9Sstevel@tonic-gate 		 */
12037c478bd9Sstevel@tonic-gate 		rc = sadb_insertassoc(newbie, inbound);
1204437220cdSdanmcd 		newbie->ipsa_hardexpiretime = gethrestime_sec();
1205f4b3ec61Sdh155122 		newbie->ipsa_hardexpiretime +=
1206f4b3ec61Sdh155122 		    espstack->ipsecesp_larval_timeout;
12077c478bd9Sstevel@tonic-gate 	}
12087c478bd9Sstevel@tonic-gate 
12097c478bd9Sstevel@tonic-gate 	/*
12107c478bd9Sstevel@tonic-gate 	 * Can exit outbound mutex.  Hold inbound until we're done
12117c478bd9Sstevel@tonic-gate 	 * with newbie.
12127c478bd9Sstevel@tonic-gate 	 */
12137c478bd9Sstevel@tonic-gate 	mutex_exit(&outbound->isaf_lock);
12147c478bd9Sstevel@tonic-gate 
12157c478bd9Sstevel@tonic-gate 	if (rc != 0) {
12167c478bd9Sstevel@tonic-gate 		mutex_exit(&inbound->isaf_lock);
12177c478bd9Sstevel@tonic-gate 		IPSA_REFRELE(newbie);
1218f4b3ec61Sdh155122 		sadb_pfkey_error(espstack->esp_pfkey_q, mp, rc,
1219f4b3ec61Sdh155122 		    SADB_X_DIAGNOSTIC_NONE, ksi->ks_in_serial);
12207c478bd9Sstevel@tonic-gate 		return;
12217c478bd9Sstevel@tonic-gate 	}
12227c478bd9Sstevel@tonic-gate 
12237c478bd9Sstevel@tonic-gate 
12247c478bd9Sstevel@tonic-gate 	/* Can write here because I'm still holding the bucket lock. */
12257c478bd9Sstevel@tonic-gate 	newbie->ipsa_type = SADB_SATYPE_ESP;
12267c478bd9Sstevel@tonic-gate 
12277c478bd9Sstevel@tonic-gate 	/*
12287c478bd9Sstevel@tonic-gate 	 * Construct successful return message. We have one thing going
12297c478bd9Sstevel@tonic-gate 	 * for us in PF_KEY v2.  That's the fact that
12307c478bd9Sstevel@tonic-gate 	 *	sizeof (sadb_spirange_t) == sizeof (sadb_sa_t)
12317c478bd9Sstevel@tonic-gate 	 */
12327c478bd9Sstevel@tonic-gate 	assoc = (sadb_sa_t *)ksi->ks_in_extv[SADB_EXT_SPIRANGE];
12337c478bd9Sstevel@tonic-gate 	assoc->sadb_sa_exttype = SADB_EXT_SA;
12347c478bd9Sstevel@tonic-gate 	assoc->sadb_sa_spi = newbie->ipsa_spi;
12357c478bd9Sstevel@tonic-gate 	*((uint64_t *)(&assoc->sadb_sa_replay)) = 0;
12367c478bd9Sstevel@tonic-gate 	mutex_exit(&inbound->isaf_lock);
12377c478bd9Sstevel@tonic-gate 
12387c478bd9Sstevel@tonic-gate 	/* Convert KEYSOCK_IN to KEYSOCK_OUT. */
12397c478bd9Sstevel@tonic-gate 	kso = (keysock_out_t *)ksi;
12407c478bd9Sstevel@tonic-gate 	kso->ks_out_len = sizeof (*kso);
12417c478bd9Sstevel@tonic-gate 	kso->ks_out_serial = ksi->ks_in_serial;
12427c478bd9Sstevel@tonic-gate 	kso->ks_out_type = KEYSOCK_OUT;
12437c478bd9Sstevel@tonic-gate 
12447c478bd9Sstevel@tonic-gate 	/*
12457c478bd9Sstevel@tonic-gate 	 * Can safely putnext() to esp_pfkey_q, because this is a turnaround
12467c478bd9Sstevel@tonic-gate 	 * from the esp_pfkey_q.
12477c478bd9Sstevel@tonic-gate 	 */
1248f4b3ec61Sdh155122 	putnext(espstack->esp_pfkey_q, mp);
12497c478bd9Sstevel@tonic-gate }
12507c478bd9Sstevel@tonic-gate 
12517c478bd9Sstevel@tonic-gate /*
12527c478bd9Sstevel@tonic-gate  * Insert the ESP header into a packet.  Duplicate an mblk, and insert a newly
12537c478bd9Sstevel@tonic-gate  * allocated mblk with the ESP header in between the two.
12547c478bd9Sstevel@tonic-gate  */
12557c478bd9Sstevel@tonic-gate static boolean_t
esp_insert_esp(mblk_t * mp,mblk_t * esp_mp,uint_t divpoint,ipsecesp_stack_t * espstack)1256f4b3ec61Sdh155122 esp_insert_esp(mblk_t *mp, mblk_t *esp_mp, uint_t divpoint,
1257f4b3ec61Sdh155122     ipsecesp_stack_t *espstack)
12587c478bd9Sstevel@tonic-gate {
12597c478bd9Sstevel@tonic-gate 	mblk_t *split_mp = mp;
12607c478bd9Sstevel@tonic-gate 	uint_t wheretodiv = divpoint;
12617c478bd9Sstevel@tonic-gate 
12627c478bd9Sstevel@tonic-gate 	while ((split_mp->b_wptr - split_mp->b_rptr) < wheretodiv) {
12637c478bd9Sstevel@tonic-gate 		wheretodiv -= (split_mp->b_wptr - split_mp->b_rptr);
12647c478bd9Sstevel@tonic-gate 		split_mp = split_mp->b_cont;
12657c478bd9Sstevel@tonic-gate 		ASSERT(split_mp != NULL);
12667c478bd9Sstevel@tonic-gate 	}
12677c478bd9Sstevel@tonic-gate 
12687c478bd9Sstevel@tonic-gate 	if (split_mp->b_wptr - split_mp->b_rptr != wheretodiv) {
12697c478bd9Sstevel@tonic-gate 		mblk_t *scratch;
12707c478bd9Sstevel@tonic-gate 
12717c478bd9Sstevel@tonic-gate 		/* "scratch" is the 2nd half, split_mp is the first. */
12727c478bd9Sstevel@tonic-gate 		scratch = dupb(split_mp);
12737c478bd9Sstevel@tonic-gate 		if (scratch == NULL) {
1274f4b3ec61Sdh155122 			esp1dbg(espstack,
1275f4b3ec61Sdh155122 			    ("esp_insert_esp: can't allocate scratch.\n"));
12767c478bd9Sstevel@tonic-gate 			return (B_FALSE);
12777c478bd9Sstevel@tonic-gate 		}
12787c478bd9Sstevel@tonic-gate 		/* NOTE:  dupb() doesn't set b_cont appropriately. */
12797c478bd9Sstevel@tonic-gate 		scratch->b_cont = split_mp->b_cont;
12807c478bd9Sstevel@tonic-gate 		scratch->b_rptr += wheretodiv;
12817c478bd9Sstevel@tonic-gate 		split_mp->b_wptr = split_mp->b_rptr + wheretodiv;
12827c478bd9Sstevel@tonic-gate 		split_mp->b_cont = scratch;
12837c478bd9Sstevel@tonic-gate 	}
12847c478bd9Sstevel@tonic-gate 	/*
12857c478bd9Sstevel@tonic-gate 	 * At this point, split_mp is exactly "wheretodiv" bytes long, and
12867c478bd9Sstevel@tonic-gate 	 * holds the end of the pre-ESP part of the datagram.
12877c478bd9Sstevel@tonic-gate 	 */
12887c478bd9Sstevel@tonic-gate 	esp_mp->b_cont = split_mp->b_cont;
12897c478bd9Sstevel@tonic-gate 	split_mp->b_cont = esp_mp;
12907c478bd9Sstevel@tonic-gate 
12917c478bd9Sstevel@tonic-gate 	return (B_TRUE);
12927c478bd9Sstevel@tonic-gate }
12937c478bd9Sstevel@tonic-gate 
12947c478bd9Sstevel@tonic-gate /*
12954a179720Sdanmcd  * Section 7 of RFC 3947 says:
12964a179720Sdanmcd  *
12974a179720Sdanmcd  * 7.  Recovering from the Expiring NAT Mappings
12984a179720Sdanmcd  *
12994a179720Sdanmcd  *    There are cases where NAT box decides to remove mappings that are still
13004a179720Sdanmcd  *    alive (for example, when the keepalive interval is too long, or when the
13014a179720Sdanmcd  *    NAT box is rebooted).  To recover from this, ends that are NOT behind
13024a179720Sdanmcd  *    NAT SHOULD use the last valid UDP encapsulated IKE or IPsec packet from
13034a179720Sdanmcd  *    the other end to determine which IP and port addresses should be used.
13044a179720Sdanmcd  *    The host behind dynamic NAT MUST NOT do this, as otherwise it opens a
13054a179720Sdanmcd  *    DoS attack possibility because the IP address or port of the other host
13064a179720Sdanmcd  *    will not change (it is not behind NAT).
13074a179720Sdanmcd  *
13084a179720Sdanmcd  *    Keepalives cannot be used for these purposes, as they are not
13094a179720Sdanmcd  *    authenticated, but any IKE authenticated IKE packet or ESP packet can be
13104a179720Sdanmcd  *    used to detect whether the IP address or the port has changed.
13114a179720Sdanmcd  *
13124a179720Sdanmcd  * The following function will check an SA and its explicitly-set pair to see
13134a179720Sdanmcd  * if the NAT-T remote port matches the received packet (which must have
13144a179720Sdanmcd  * passed ESP authentication, see esp_in_done() for the caller context).  If
13154a179720Sdanmcd  * there is a mismatch, the SAs are updated.  It is not important if we race
13164a179720Sdanmcd  * with a transmitting thread, as if there is a transmitting thread, it will
13174a179720Sdanmcd  * merely emit a packet that will most-likely be dropped.
13184a179720Sdanmcd  *
13194a179720Sdanmcd  * "ports" are ordered src,dst, and assoc is an inbound SA, where src should
13204a179720Sdanmcd  * match ipsa_remote_nat_port and dst should match ipsa_local_nat_port.
13214a179720Sdanmcd  */
13224a179720Sdanmcd #ifdef _LITTLE_ENDIAN
13234a179720Sdanmcd #define	FIRST_16(x) ((x) & 0xFFFF)
13244a179720Sdanmcd #define	NEXT_16(x) (((x) >> 16) & 0xFFFF)
13254a179720Sdanmcd #else
13264a179720Sdanmcd #define	FIRST_16(x) (((x) >> 16) & 0xFFFF)
13274a179720Sdanmcd #define	NEXT_16(x) ((x) & 0xFFFF)
13284a179720Sdanmcd #endif
13294a179720Sdanmcd static void
esp_port_freshness(uint32_t ports,ipsa_t * assoc)13304a179720Sdanmcd esp_port_freshness(uint32_t ports, ipsa_t *assoc)
13314a179720Sdanmcd {
13324a179720Sdanmcd 	uint16_t remote = FIRST_16(ports);
13334a179720Sdanmcd 	uint16_t local = NEXT_16(ports);
13344a179720Sdanmcd 	ipsa_t *outbound_peer;
13354a179720Sdanmcd 	isaf_t *bucket;
13364a179720Sdanmcd 	ipsecesp_stack_t *espstack = assoc->ipsa_netstack->netstack_ipsecesp;
13374a179720Sdanmcd 
13384a179720Sdanmcd 	/* We found a conn_t, therefore local != 0. */
13394a179720Sdanmcd 	ASSERT(local != 0);
13404a179720Sdanmcd 	/* Assume an IPv4 SA. */
13414a179720Sdanmcd 	ASSERT(assoc->ipsa_addrfam == AF_INET);
13424a179720Sdanmcd 
13434a179720Sdanmcd 	/*
13444a179720Sdanmcd 	 * On-the-wire rport == 0 means something's very wrong.
13454a179720Sdanmcd 	 * An unpaired SA is also useless to us.
13464a179720Sdanmcd 	 * If we are behind the NAT, don't bother.
13474a179720Sdanmcd 	 * A zero local NAT port defaults to 4500, so check that too.
13484a179720Sdanmcd 	 * And, of course, if the ports already match, we don't need to
13494a179720Sdanmcd 	 * bother.
13504a179720Sdanmcd 	 */
13514a179720Sdanmcd 	if (remote == 0 || assoc->ipsa_otherspi == 0 ||
13524a179720Sdanmcd 	    (assoc->ipsa_flags & IPSA_F_BEHIND_NAT) ||
13534a179720Sdanmcd 	    (assoc->ipsa_remote_nat_port == 0 &&
13544a179720Sdanmcd 	    remote == htons(IPPORT_IKE_NATT)) ||
13554a179720Sdanmcd 	    remote == assoc->ipsa_remote_nat_port)
13564a179720Sdanmcd 		return;
13574a179720Sdanmcd 
13584a179720Sdanmcd 	/* Try and snag the peer.   NOTE:  Assume IPv4 for now. */
13594a179720Sdanmcd 	bucket = OUTBOUND_BUCKET_V4(&(espstack->esp_sadb.s_v4),
13604a179720Sdanmcd 	    assoc->ipsa_srcaddr[0]);
13614a179720Sdanmcd 	mutex_enter(&bucket->isaf_lock);
13624a179720Sdanmcd 	outbound_peer = ipsec_getassocbyspi(bucket, assoc->ipsa_otherspi,
13634a179720Sdanmcd 	    assoc->ipsa_dstaddr, assoc->ipsa_srcaddr, AF_INET);
13644a179720Sdanmcd 	mutex_exit(&bucket->isaf_lock);
13654a179720Sdanmcd 
13664a179720Sdanmcd 	/* We probably lost a race to a deleting or expiring thread. */
13674a179720Sdanmcd 	if (outbound_peer == NULL)
13684a179720Sdanmcd 		return;
13694a179720Sdanmcd 
13704a179720Sdanmcd 	/*
13714a179720Sdanmcd 	 * Hold the mutexes for both SAs so we don't race another inbound
13724a179720Sdanmcd 	 * thread.  A lock-entry order shouldn't matter, since all other
13734a179720Sdanmcd 	 * per-ipsa locks are individually held-then-released.
13744a179720Sdanmcd 	 *
13754a179720Sdanmcd 	 * Luckily, this has nothing to do with the remote-NAT address,
13764a179720Sdanmcd 	 * so we don't have to re-scribble the cached-checksum differential.
13774a179720Sdanmcd 	 */
13784a179720Sdanmcd 	mutex_enter(&outbound_peer->ipsa_lock);
13794a179720Sdanmcd 	mutex_enter(&assoc->ipsa_lock);
13804a179720Sdanmcd 	outbound_peer->ipsa_remote_nat_port = assoc->ipsa_remote_nat_port =
13814a179720Sdanmcd 	    remote;
13824a179720Sdanmcd 	mutex_exit(&assoc->ipsa_lock);
13834a179720Sdanmcd 	mutex_exit(&outbound_peer->ipsa_lock);
13844a179720Sdanmcd 	IPSA_REFRELE(outbound_peer);
13854a179720Sdanmcd 	ESP_BUMP_STAT(espstack, sa_port_renumbers);
13864a179720Sdanmcd }
13874a179720Sdanmcd /*
13887c478bd9Sstevel@tonic-gate  * Finish processing of an inbound ESP packet after processing by the
13897c478bd9Sstevel@tonic-gate  * crypto framework.
13907c478bd9Sstevel@tonic-gate  * - Remove the ESP header.
13917c478bd9Sstevel@tonic-gate  * - Send packet back to IP.
13927c478bd9Sstevel@tonic-gate  * If authentication was performed on the packet, this function is called
13937c478bd9Sstevel@tonic-gate  * only if the authentication succeeded.
13947c478bd9Sstevel@tonic-gate  * On success returns B_TRUE, on failure returns B_FALSE and frees the
1395bd670b35SErik Nordmark  * mblk chain data_mp.
13967c478bd9Sstevel@tonic-gate  */
1397bd670b35SErik Nordmark static mblk_t *
esp_in_done(mblk_t * data_mp,ip_recv_attr_t * ira,ipsec_crypto_t * ic)1398bd670b35SErik Nordmark esp_in_done(mblk_t *data_mp, ip_recv_attr_t *ira, ipsec_crypto_t *ic)
13997c478bd9Sstevel@tonic-gate {
14007c478bd9Sstevel@tonic-gate 	ipsa_t *assoc;
14017c478bd9Sstevel@tonic-gate 	uint_t espstart;
14027c478bd9Sstevel@tonic-gate 	uint32_t ivlen = 0;
14037c478bd9Sstevel@tonic-gate 	uint_t processed_len;
14047c478bd9Sstevel@tonic-gate 	esph_t *esph;
14057c478bd9Sstevel@tonic-gate 	kstat_named_t *counter;
14067c478bd9Sstevel@tonic-gate 	boolean_t is_natt;
1407bd670b35SErik Nordmark 	netstack_t	*ns = ira->ira_ill->ill_ipst->ips_netstack;
1408f4b3ec61Sdh155122 	ipsecesp_stack_t *espstack = ns->netstack_ipsecesp;
1409f4b3ec61Sdh155122 	ipsec_stack_t	*ipss = ns->netstack_ipsec;
14107c478bd9Sstevel@tonic-gate 
1411bd670b35SErik Nordmark 	assoc = ira->ira_ipsec_esp_sa;
14127c478bd9Sstevel@tonic-gate 	ASSERT(assoc != NULL);
14137c478bd9Sstevel@tonic-gate 
14147c478bd9Sstevel@tonic-gate 	is_natt = ((assoc->ipsa_flags & IPSA_F_NATT) != 0);
14157c478bd9Sstevel@tonic-gate 
14167c478bd9Sstevel@tonic-gate 	/* get the pointer to the ESP header */
14177c478bd9Sstevel@tonic-gate 	if (assoc->ipsa_encr_alg == SADB_EALG_NULL) {
14187c478bd9Sstevel@tonic-gate 		/* authentication-only ESP */
1419bd670b35SErik Nordmark 		espstart = ic->ic_crypto_data.cd_offset;
1420bd670b35SErik Nordmark 		processed_len = ic->ic_crypto_data.cd_length;
14217c478bd9Sstevel@tonic-gate 	} else {
14227c478bd9Sstevel@tonic-gate 		/* encryption present */
14237c478bd9Sstevel@tonic-gate 		ivlen = assoc->ipsa_iv_len;
14247c478bd9Sstevel@tonic-gate 		if (assoc->ipsa_auth_alg == SADB_AALG_NONE) {
14257c478bd9Sstevel@tonic-gate 			/* encryption-only ESP */
1426bd670b35SErik Nordmark 			espstart = ic->ic_crypto_data.cd_offset -
14277c478bd9Sstevel@tonic-gate 			    sizeof (esph_t) - assoc->ipsa_iv_len;
1428bd670b35SErik Nordmark 			processed_len = ic->ic_crypto_data.cd_length +
14297c478bd9Sstevel@tonic-gate 			    ivlen;
14307c478bd9Sstevel@tonic-gate 		} else {
14317c478bd9Sstevel@tonic-gate 			/* encryption with authentication */
1432bd670b35SErik Nordmark 			espstart = ic->ic_crypto_dual_data.dd_offset1;
1433bd670b35SErik Nordmark 			processed_len = ic->ic_crypto_dual_data.dd_len2 +
14347c478bd9Sstevel@tonic-gate 			    ivlen;
14357c478bd9Sstevel@tonic-gate 		}
14367c478bd9Sstevel@tonic-gate 	}
14377c478bd9Sstevel@tonic-gate 
14387c478bd9Sstevel@tonic-gate 	esph = (esph_t *)(data_mp->b_rptr + espstart);
14397c478bd9Sstevel@tonic-gate 
1440628b0c67SMark Fenwick 	if (assoc->ipsa_auth_alg != IPSA_AALG_NONE ||
1441628b0c67SMark Fenwick 	    (assoc->ipsa_flags & IPSA_F_COMBINED)) {
1442628b0c67SMark Fenwick 		/*
1443628b0c67SMark Fenwick 		 * Authentication passed if we reach this point.
1444628b0c67SMark Fenwick 		 * Packets with authentication will have the ICV
1445628b0c67SMark Fenwick 		 * after the crypto data. Adjust b_wptr before
1446628b0c67SMark Fenwick 		 * making padlen checks.
1447628b0c67SMark Fenwick 		 */
1448f4b3ec61Sdh155122 		ESP_BUMP_STAT(espstack, good_auth);
14497c478bd9Sstevel@tonic-gate 		data_mp->b_wptr -= assoc->ipsa_mac_len;
14507c478bd9Sstevel@tonic-gate 
14517c478bd9Sstevel@tonic-gate 		/*
14527c478bd9Sstevel@tonic-gate 		 * Check replay window here!
14537c478bd9Sstevel@tonic-gate 		 * For right now, assume keysock will set the replay window
14547c478bd9Sstevel@tonic-gate 		 * size to zero for SAs that have an unspecified sender.
14557c478bd9Sstevel@tonic-gate 		 * This may change...
14567c478bd9Sstevel@tonic-gate 		 */
14577c478bd9Sstevel@tonic-gate 
14587c478bd9Sstevel@tonic-gate 		if (!sadb_replay_check(assoc, esph->esph_replay)) {
14597c478bd9Sstevel@tonic-gate 			/*
14607c478bd9Sstevel@tonic-gate 			 * Log the event. As of now we print out an event.
14617c478bd9Sstevel@tonic-gate 			 * Do not print the replay failure number, or else
14627c478bd9Sstevel@tonic-gate 			 * syslog cannot collate the error messages.  Printing
14637c478bd9Sstevel@tonic-gate 			 * the replay number that failed opens a denial-of-
14647c478bd9Sstevel@tonic-gate 			 * service attack.
14657c478bd9Sstevel@tonic-gate 			 */
14667c478bd9Sstevel@tonic-gate 			ipsec_assocfailure(info.mi_idnum, 0, 0,
14677c478bd9Sstevel@tonic-gate 			    SL_ERROR | SL_WARN,
14687c478bd9Sstevel@tonic-gate 			    "Replay failed for ESP spi 0x%x, dst %s.\n",
14697c478bd9Sstevel@tonic-gate 			    assoc->ipsa_spi, assoc->ipsa_dstaddr,
1470f4b3ec61Sdh155122 			    assoc->ipsa_addrfam, espstack->ipsecesp_netstack);
1471f4b3ec61Sdh155122 			ESP_BUMP_STAT(espstack, replay_failures);
1472f4b3ec61Sdh155122 			counter = DROPPER(ipss, ipds_esp_replay);
14737c478bd9Sstevel@tonic-gate 			goto drop_and_bail;
14747c478bd9Sstevel@tonic-gate 		}
14754a179720Sdanmcd 
1476bd670b35SErik Nordmark 		if (is_natt) {
1477bd670b35SErik Nordmark 			ASSERT(ira->ira_flags & IRAF_ESP_UDP_PORTS);
1478bd670b35SErik Nordmark 			ASSERT(ira->ira_esp_udp_ports != 0);
1479bd670b35SErik Nordmark 			esp_port_freshness(ira->ira_esp_udp_ports, assoc);
1480bd670b35SErik Nordmark 		}
14817c478bd9Sstevel@tonic-gate 	}
14827c478bd9Sstevel@tonic-gate 
1483437220cdSdanmcd 	esp_set_usetime(assoc, B_TRUE);
1484437220cdSdanmcd 
14857c478bd9Sstevel@tonic-gate 	if (!esp_age_bytes(assoc, processed_len, B_TRUE)) {
14867c478bd9Sstevel@tonic-gate 		/* The ipsa has hit hard expiration, LOG and AUDIT. */
14877c478bd9Sstevel@tonic-gate 		ipsec_assocfailure(info.mi_idnum, 0, 0,
14887c478bd9Sstevel@tonic-gate 		    SL_ERROR | SL_WARN,
14897c478bd9Sstevel@tonic-gate 		    "ESP association 0x%x, dst %s had bytes expire.\n",
1490f4b3ec61Sdh155122 		    assoc->ipsa_spi, assoc->ipsa_dstaddr, assoc->ipsa_addrfam,
1491f4b3ec61Sdh155122 		    espstack->ipsecesp_netstack);
1492f4b3ec61Sdh155122 		ESP_BUMP_STAT(espstack, bytes_expired);
1493f4b3ec61Sdh155122 		counter = DROPPER(ipss, ipds_esp_bytes_expire);
14947c478bd9Sstevel@tonic-gate 		goto drop_and_bail;
14957c478bd9Sstevel@tonic-gate 	}
14967c478bd9Sstevel@tonic-gate 
14977c478bd9Sstevel@tonic-gate 	/*
14987c478bd9Sstevel@tonic-gate 	 * Remove ESP header and padding from packet.  I hope the compiler
14997c478bd9Sstevel@tonic-gate 	 * spews "branch, predict taken" code for this.
15007c478bd9Sstevel@tonic-gate 	 */
15017c478bd9Sstevel@tonic-gate 
1502bd670b35SErik Nordmark 	if (esp_strip_header(data_mp, (ira->ira_flags & IRAF_IS_IPV4),
1503bd670b35SErik Nordmark 	    ivlen, &counter, espstack)) {
15045d3b8cb7SBill Sommerfeld 
1505bd670b35SErik Nordmark 		if (is_system_labeled() && assoc->ipsa_tsl != NULL) {
1506bd670b35SErik Nordmark 			if (!ip_recv_attr_replace_label(ira, assoc->ipsa_tsl)) {
1507bd670b35SErik Nordmark 				ip_drop_packet(data_mp, B_TRUE, ira->ira_ill,
1508bd670b35SErik Nordmark 				    DROPPER(ipss, ipds_ah_nomem),
1509bd670b35SErik Nordmark 				    &espstack->esp_dropper);
1510bd670b35SErik Nordmark 				BUMP_MIB(ira->ira_ill->ill_ip_mib,
1511bd670b35SErik Nordmark 				    ipIfStatsInDiscards);
1512bd670b35SErik Nordmark 				return (NULL);
15135d3b8cb7SBill Sommerfeld 			}
15145d3b8cb7SBill Sommerfeld 		}
15157c478bd9Sstevel@tonic-gate 		if (is_natt)
15167c478bd9Sstevel@tonic-gate 			return (esp_fix_natt_checksums(data_mp, assoc));
1517d74f5ecaSDan McDonald 
1518d74f5ecaSDan McDonald 		if (assoc->ipsa_state == IPSA_STATE_IDLE) {
1519d74f5ecaSDan McDonald 			/*
1520d74f5ecaSDan McDonald 			 * Cluster buffering case.  Tell caller that we're
1521d74f5ecaSDan McDonald 			 * handling the packet.
1522d74f5ecaSDan McDonald 			 */
1523bd670b35SErik Nordmark 			sadb_buf_pkt(assoc, data_mp, ira);
1524bd670b35SErik Nordmark 			return (NULL);
1525d74f5ecaSDan McDonald 		}
1526d74f5ecaSDan McDonald 
1527bd670b35SErik Nordmark 		return (data_mp);
15287c478bd9Sstevel@tonic-gate 	}
15297c478bd9Sstevel@tonic-gate 
1530f4b3ec61Sdh155122 	esp1dbg(espstack, ("esp_in_done: esp_strip_header() failed\n"));
15317c478bd9Sstevel@tonic-gate drop_and_bail:
1532f4b3ec61Sdh155122 	IP_ESP_BUMP_STAT(ipss, in_discards);
1533bd670b35SErik Nordmark 	ip_drop_packet(data_mp, B_TRUE, ira->ira_ill, counter,
1534f4b3ec61Sdh155122 	    &espstack->esp_dropper);
1535bd670b35SErik Nordmark 	BUMP_MIB(ira->ira_ill->ill_ip_mib, ipIfStatsInDiscards);
1536bd670b35SErik Nordmark 	return (NULL);
15377c478bd9Sstevel@tonic-gate }
15387c478bd9Sstevel@tonic-gate 
15397c478bd9Sstevel@tonic-gate /*
15407c478bd9Sstevel@tonic-gate  * Called upon failing the inbound ICV check. The message passed as
15417c478bd9Sstevel@tonic-gate  * argument is freed.
15427c478bd9Sstevel@tonic-gate  */
15437c478bd9Sstevel@tonic-gate static void
esp_log_bad_auth(mblk_t * mp,ip_recv_attr_t * ira)1544bd670b35SErik Nordmark esp_log_bad_auth(mblk_t *mp, ip_recv_attr_t *ira)
15457c478bd9Sstevel@tonic-gate {
1546bd670b35SErik Nordmark 	ipsa_t		*assoc = ira->ira_ipsec_esp_sa;
1547bd670b35SErik Nordmark 	netstack_t	*ns = ira->ira_ill->ill_ipst->ips_netstack;
1548f4b3ec61Sdh155122 	ipsecesp_stack_t *espstack = ns->netstack_ipsecesp;
1549f4b3ec61Sdh155122 	ipsec_stack_t	*ipss = ns->netstack_ipsec;
15507c478bd9Sstevel@tonic-gate 
15517c478bd9Sstevel@tonic-gate 	/*
15527c478bd9Sstevel@tonic-gate 	 * Log the event. Don't print to the console, block
15537c478bd9Sstevel@tonic-gate 	 * potential denial-of-service attack.
15547c478bd9Sstevel@tonic-gate 	 */
1555f4b3ec61Sdh155122 	ESP_BUMP_STAT(espstack, bad_auth);
15567c478bd9Sstevel@tonic-gate 
15577c478bd9Sstevel@tonic-gate 	ipsec_assocfailure(info.mi_idnum, 0, 0, SL_ERROR | SL_WARN,
15587c478bd9Sstevel@tonic-gate 	    "ESP Authentication failed for spi 0x%x, dst %s.\n",
1559f4b3ec61Sdh155122 	    assoc->ipsa_spi, assoc->ipsa_dstaddr, assoc->ipsa_addrfam,
1560f4b3ec61Sdh155122 	    espstack->ipsecesp_netstack);
15617c478bd9Sstevel@tonic-gate 
1562f4b3ec61Sdh155122 	IP_ESP_BUMP_STAT(ipss, in_discards);
1563bd670b35SErik Nordmark 	ip_drop_packet(mp, B_TRUE, ira->ira_ill,
1564f4b3ec61Sdh155122 	    DROPPER(ipss, ipds_esp_bad_auth),
1565f4b3ec61Sdh155122 	    &espstack->esp_dropper);
15667c478bd9Sstevel@tonic-gate }
15677c478bd9Sstevel@tonic-gate 
15687c478bd9Sstevel@tonic-gate 
15697c478bd9Sstevel@tonic-gate /*
15707c478bd9Sstevel@tonic-gate  * Invoked for outbound packets after ESP processing. If the packet
15717c478bd9Sstevel@tonic-gate  * also requires AH, performs the AH SA selection and AH processing.
1572bd670b35SErik Nordmark  *
1573b7daf799SDan McDonald  * Returns data_mp (possibly with AH added) unless data_mp was consumed
1574b7daf799SDan McDonald  * due to an error, or queued due to async. crypto or an ACQUIRE trigger.
15757c478bd9Sstevel@tonic-gate  */
1576bd670b35SErik Nordmark static mblk_t *
esp_do_outbound_ah(mblk_t * data_mp,ip_xmit_attr_t * ixa)1577bd670b35SErik Nordmark esp_do_outbound_ah(mblk_t *data_mp, ip_xmit_attr_t *ixa)
15787c478bd9Sstevel@tonic-gate {
15797c478bd9Sstevel@tonic-gate 	ipsec_action_t *ap;
15807c478bd9Sstevel@tonic-gate 
1581bd670b35SErik Nordmark 	ap = ixa->ixa_ipsec_action;
15827c478bd9Sstevel@tonic-gate 	if (ap == NULL) {
1583bd670b35SErik Nordmark 		ipsec_policy_t *pp = ixa->ixa_ipsec_policy;
15847c478bd9Sstevel@tonic-gate 		ap = pp->ipsp_act;
15857c478bd9Sstevel@tonic-gate 	}
15867c478bd9Sstevel@tonic-gate 
15877c478bd9Sstevel@tonic-gate 	if (!ap->ipa_want_ah)
1588bd670b35SErik Nordmark 		return (data_mp);
15897c478bd9Sstevel@tonic-gate 
1590bd670b35SErik Nordmark 	/*
1591bd670b35SErik Nordmark 	 * Normally the AH SA would have already been put in place
1592bd670b35SErik Nordmark 	 * but it could have been flushed so we need to look for it.
1593bd670b35SErik Nordmark 	 */
1594bd670b35SErik Nordmark 	if (ixa->ixa_ipsec_ah_sa == NULL) {
1595bd670b35SErik Nordmark 		if (!ipsec_outbound_sa(data_mp, ixa, IPPROTO_AH)) {
1596bd670b35SErik Nordmark 			sadb_acquire(data_mp, ixa, B_TRUE, B_FALSE);
1597bd670b35SErik Nordmark 			return (NULL);
15987c478bd9Sstevel@tonic-gate 		}
15997c478bd9Sstevel@tonic-gate 	}
1600bd670b35SErik Nordmark 	ASSERT(ixa->ixa_ipsec_ah_sa != NULL);
16017c478bd9Sstevel@tonic-gate 
1602bd670b35SErik Nordmark 	data_mp = ixa->ixa_ipsec_ah_sa->ipsa_output_func(data_mp, ixa);
1603bd670b35SErik Nordmark 	return (data_mp);
16047c478bd9Sstevel@tonic-gate }
16057c478bd9Sstevel@tonic-gate 
16067c478bd9Sstevel@tonic-gate 
16077c478bd9Sstevel@tonic-gate /*
16087c478bd9Sstevel@tonic-gate  * Kernel crypto framework callback invoked after completion of async
1609bd670b35SErik Nordmark  * crypto requests for outbound packets.
16107c478bd9Sstevel@tonic-gate  */
16117c478bd9Sstevel@tonic-gate static void
esp_kcf_callback_outbound(void * arg,int status)1612bd670b35SErik Nordmark esp_kcf_callback_outbound(void *arg, int status)
16137c478bd9Sstevel@tonic-gate {
1614bd670b35SErik Nordmark 	mblk_t		*mp = (mblk_t *)arg;
1615bd670b35SErik Nordmark 	mblk_t		*async_mp;
1616bd670b35SErik Nordmark 	netstack_t	*ns;
1617f4b3ec61Sdh155122 	ipsec_stack_t	*ipss;
1618bd670b35SErik Nordmark 	ipsecesp_stack_t *espstack;
1619bd670b35SErik Nordmark 	mblk_t		*data_mp;
1620bd670b35SErik Nordmark 	ip_xmit_attr_t	ixas;
1621bd670b35SErik Nordmark 	ipsec_crypto_t	*ic;
1622bd670b35SErik Nordmark 	ill_t		*ill;
1623f4b3ec61Sdh155122 
1624f4b3ec61Sdh155122 	/*
1625bd670b35SErik Nordmark 	 * First remove the ipsec_crypto_t mblk
1626bd670b35SErik Nordmark 	 * Note that we need to ipsec_free_crypto_data(mp) once done with ic.
1627f4b3ec61Sdh155122 	 */
1628bd670b35SErik Nordmark 	async_mp = ipsec_remove_crypto_data(mp, &ic);
1629bd670b35SErik Nordmark 	ASSERT(async_mp != NULL);
1630f4b3ec61Sdh155122 
1631bd670b35SErik Nordmark 	/*
1632bd670b35SErik Nordmark 	 * Extract the ip_xmit_attr_t from the first mblk.
1633bd670b35SErik Nordmark 	 * Verifies that the netstack and ill is still around; could
1634bd670b35SErik Nordmark 	 * have vanished while kEf was doing its work.
1635bd670b35SErik Nordmark 	 * On succesful return we have a nce_t and the ill/ipst can't
1636bd670b35SErik Nordmark 	 * disappear until we do the nce_refrele in ixa_cleanup.
1637bd670b35SErik Nordmark 	 */
1638bd670b35SErik Nordmark 	data_mp = async_mp->b_cont;
1639bd670b35SErik Nordmark 	async_mp->b_cont = NULL;
1640bd670b35SErik Nordmark 	if (!ip_xmit_attr_from_mblk(async_mp, &ixas)) {
1641bd670b35SErik Nordmark 		/* Disappeared on us - no ill/ipst for MIB */
1642bd670b35SErik Nordmark 		/* We have nowhere to do stats since ixa_ipst could be NULL */
1643bd670b35SErik Nordmark 		if (ixas.ixa_nce != NULL) {
1644bd670b35SErik Nordmark 			ill = ixas.ixa_nce->nce_ill;
1645bd670b35SErik Nordmark 			BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
1646bd670b35SErik Nordmark 			ip_drop_output("ipIfStatsOutDiscards", data_mp, ill);
1647bd670b35SErik Nordmark 		}
1648bd670b35SErik Nordmark 		freemsg(data_mp);
1649bd670b35SErik Nordmark 		goto done;
1650bd670b35SErik Nordmark 	}
1651bd670b35SErik Nordmark 	ns = ixas.ixa_ipst->ips_netstack;
1652f4b3ec61Sdh155122 	espstack = ns->netstack_ipsecesp;
1653f4b3ec61Sdh155122 	ipss = ns->netstack_ipsec;
1654bd670b35SErik Nordmark 	ill = ixas.ixa_nce->nce_ill;
1655f4b3ec61Sdh155122 
16567c478bd9Sstevel@tonic-gate 	if (status == CRYPTO_SUCCESS) {
16577c478bd9Sstevel@tonic-gate 		/*
16587c478bd9Sstevel@tonic-gate 		 * If a ICV was computed, it was stored by the
16597c478bd9Sstevel@tonic-gate 		 * crypto framework at the end of the packet.
16607c478bd9Sstevel@tonic-gate 		 */
1661bd670b35SErik Nordmark 		ipha_t *ipha = (ipha_t *)data_mp->b_rptr;
16627c478bd9Sstevel@tonic-gate 
1663bd670b35SErik Nordmark 		esp_set_usetime(ixas.ixa_ipsec_esp_sa, B_FALSE);
1664437220cdSdanmcd 		/* NAT-T packet. */
1665bd670b35SErik Nordmark 		if (IPH_HDR_VERSION(ipha) == IP_VERSION &&
1666bd670b35SErik Nordmark 		    ipha->ipha_protocol == IPPROTO_UDP)
1667bd670b35SErik Nordmark 			esp_prepare_udp(ns, data_mp, ipha);
1668437220cdSdanmcd 
16697c478bd9Sstevel@tonic-gate 		/* do AH processing if needed */
1670bd670b35SErik Nordmark 		data_mp = esp_do_outbound_ah(data_mp, &ixas);
1671bd670b35SErik Nordmark 		if (data_mp == NULL)
1672bd670b35SErik Nordmark 			goto done;
1673bd670b35SErik Nordmark 
1674bd670b35SErik Nordmark 		(void) ip_output_post_ipsec(data_mp, &ixas);
16757c478bd9Sstevel@tonic-gate 	} else {
1676bd670b35SErik Nordmark 		/* Outbound shouldn't see invalid MAC */
1677bd670b35SErik Nordmark 		ASSERT(status != CRYPTO_INVALID_MAC);
1678bd670b35SErik Nordmark 
1679bd670b35SErik Nordmark 		esp1dbg(espstack,
1680bd670b35SErik Nordmark 		    ("esp_kcf_callback_outbound: crypto failed with 0x%x\n",
1681bd670b35SErik Nordmark 		    status));
1682bd670b35SErik Nordmark 		ESP_BUMP_STAT(espstack, crypto_failures);
1683bd670b35SErik Nordmark 		ESP_BUMP_STAT(espstack, out_discards);
1684bd670b35SErik Nordmark 		ip_drop_packet(data_mp, B_FALSE, ill,
1685bd670b35SErik Nordmark 		    DROPPER(ipss, ipds_esp_crypto_failed),
1686bd670b35SErik Nordmark 		    &espstack->esp_dropper);
1687bd670b35SErik Nordmark 		BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
16887c478bd9Sstevel@tonic-gate 	}
1689bd670b35SErik Nordmark done:
1690bd670b35SErik Nordmark 	ixa_cleanup(&ixas);
1691bd670b35SErik Nordmark 	(void) ipsec_free_crypto_data(mp);
16927c478bd9Sstevel@tonic-gate }
16937c478bd9Sstevel@tonic-gate 
1694bd670b35SErik Nordmark /*
1695bd670b35SErik Nordmark  * Kernel crypto framework callback invoked after completion of async
1696bd670b35SErik Nordmark  * crypto requests for inbound packets.
1697bd670b35SErik Nordmark  */
1698bd670b35SErik Nordmark static void
esp_kcf_callback_inbound(void * arg,int status)1699bd670b35SErik Nordmark esp_kcf_callback_inbound(void *arg, int status)
1700bd670b35SErik Nordmark {
1701bd670b35SErik Nordmark 	mblk_t		*mp = (mblk_t *)arg;
1702bd670b35SErik Nordmark 	mblk_t		*async_mp;
1703bd670b35SErik Nordmark 	netstack_t	*ns;
1704bd670b35SErik Nordmark 	ipsecesp_stack_t *espstack;
1705bd670b35SErik Nordmark 	ipsec_stack_t	*ipss;
1706bd670b35SErik Nordmark 	mblk_t		*data_mp;
1707bd670b35SErik Nordmark 	ip_recv_attr_t	iras;
1708bd670b35SErik Nordmark 	ipsec_crypto_t	*ic;
1709bd670b35SErik Nordmark 
1710bd670b35SErik Nordmark 	/*
1711bd670b35SErik Nordmark 	 * First remove the ipsec_crypto_t mblk
1712bd670b35SErik Nordmark 	 * Note that we need to ipsec_free_crypto_data(mp) once done with ic.
1713bd670b35SErik Nordmark 	 */
1714bd670b35SErik Nordmark 	async_mp = ipsec_remove_crypto_data(mp, &ic);
1715bd670b35SErik Nordmark 	ASSERT(async_mp != NULL);
1716bd670b35SErik Nordmark 
1717bd670b35SErik Nordmark 	/*
1718bd670b35SErik Nordmark 	 * Extract the ip_recv_attr_t from the first mblk.
1719bd670b35SErik Nordmark 	 * Verifies that the netstack and ill is still around; could
1720bd670b35SErik Nordmark 	 * have vanished while kEf was doing its work.
1721bd670b35SErik Nordmark 	 */
1722bd670b35SErik Nordmark 	data_mp = async_mp->b_cont;
1723bd670b35SErik Nordmark 	async_mp->b_cont = NULL;
1724bd670b35SErik Nordmark 	if (!ip_recv_attr_from_mblk(async_mp, &iras)) {
1725bd670b35SErik Nordmark 		/* The ill or ip_stack_t disappeared on us */
1726bd670b35SErik Nordmark 		ip_drop_input("ip_recv_attr_from_mblk", data_mp, NULL);
1727bd670b35SErik Nordmark 		freemsg(data_mp);
1728bd670b35SErik Nordmark 		goto done;
1729bd670b35SErik Nordmark 	}
1730bd670b35SErik Nordmark 
1731bd670b35SErik Nordmark 	ns = iras.ira_ill->ill_ipst->ips_netstack;
1732bd670b35SErik Nordmark 	espstack = ns->netstack_ipsecesp;
1733bd670b35SErik Nordmark 	ipss = ns->netstack_ipsec;
1734bd670b35SErik Nordmark 
1735bd670b35SErik Nordmark 	if (status == CRYPTO_SUCCESS) {
1736bd670b35SErik Nordmark 		data_mp = esp_in_done(data_mp, &iras, ic);
1737bd670b35SErik Nordmark 		if (data_mp == NULL)
1738bd670b35SErik Nordmark 			goto done;
1739bd670b35SErik Nordmark 
1740bd670b35SErik Nordmark 		/* finish IPsec processing */
1741bd670b35SErik Nordmark 		ip_input_post_ipsec(data_mp, &iras);
17427c478bd9Sstevel@tonic-gate 	} else if (status == CRYPTO_INVALID_MAC) {
1743bd670b35SErik Nordmark 		esp_log_bad_auth(data_mp, &iras);
17447c478bd9Sstevel@tonic-gate 	} else {
1745f4b3ec61Sdh155122 		esp1dbg(espstack,
1746f4b3ec61Sdh155122 		    ("esp_kcf_callback: crypto failed with 0x%x\n",
17477c478bd9Sstevel@tonic-gate 		    status));
1748f4b3ec61Sdh155122 		ESP_BUMP_STAT(espstack, crypto_failures);
1749f4b3ec61Sdh155122 		IP_ESP_BUMP_STAT(ipss, in_discards);
1750bd670b35SErik Nordmark 		ip_drop_packet(data_mp, B_TRUE, iras.ira_ill,
1751f4b3ec61Sdh155122 		    DROPPER(ipss, ipds_esp_crypto_failed),
1752f4b3ec61Sdh155122 		    &espstack->esp_dropper);
1753bd670b35SErik Nordmark 		BUMP_MIB(iras.ira_ill->ill_ip_mib, ipIfStatsInDiscards);
17547c478bd9Sstevel@tonic-gate 	}
1755bd670b35SErik Nordmark done:
1756bd670b35SErik Nordmark 	ira_cleanup(&iras, B_TRUE);
1757bd670b35SErik Nordmark 	(void) ipsec_free_crypto_data(mp);
17587c478bd9Sstevel@tonic-gate }
17597c478bd9Sstevel@tonic-gate 
17607c478bd9Sstevel@tonic-gate /*
17617c478bd9Sstevel@tonic-gate  * Invoked on crypto framework failure during inbound and outbound processing.
17627c478bd9Sstevel@tonic-gate  */
17637c478bd9Sstevel@tonic-gate static void
esp_crypto_failed(mblk_t * data_mp,boolean_t is_inbound,int kef_rc,ill_t * ill,ipsecesp_stack_t * espstack)1764bd670b35SErik Nordmark esp_crypto_failed(mblk_t *data_mp, boolean_t is_inbound, int kef_rc,
1765bd670b35SErik Nordmark     ill_t *ill, ipsecesp_stack_t *espstack)
17667c478bd9Sstevel@tonic-gate {
1767f4b3ec61Sdh155122 	ipsec_stack_t	*ipss = espstack->ipsecesp_netstack->netstack_ipsec;
1768f4b3ec61Sdh155122 
1769f4b3ec61Sdh155122 	esp1dbg(espstack, ("crypto failed for %s ESP with 0x%x\n",
17707c478bd9Sstevel@tonic-gate 	    is_inbound ? "inbound" : "outbound", kef_rc));
1771bd670b35SErik Nordmark 	ip_drop_packet(data_mp, is_inbound, ill,
1772f4b3ec61Sdh155122 	    DROPPER(ipss, ipds_esp_crypto_failed),
1773f4b3ec61Sdh155122 	    &espstack->esp_dropper);
1774f4b3ec61Sdh155122 	ESP_BUMP_STAT(espstack, crypto_failures);
17757c478bd9Sstevel@tonic-gate 	if (is_inbound)
1776f4b3ec61Sdh155122 		IP_ESP_BUMP_STAT(ipss, in_discards);
17777c478bd9Sstevel@tonic-gate 	else
1778f4b3ec61Sdh155122 		ESP_BUMP_STAT(espstack, out_discards);
17797c478bd9Sstevel@tonic-gate }
17807c478bd9Sstevel@tonic-gate 
1781bd670b35SErik Nordmark /*
1782bd670b35SErik Nordmark  * A statement-equivalent macro, _cr MUST point to a modifiable
1783bd670b35SErik Nordmark  * crypto_call_req_t.
1784bd670b35SErik Nordmark  */
1785bd670b35SErik Nordmark #define	ESP_INIT_CALLREQ(_cr, _mp, _callback)				\
1786bd670b35SErik Nordmark 	(_cr)->cr_flag = CRYPTO_SKIP_REQID|CRYPTO_ALWAYS_QUEUE;	\
1787bd670b35SErik Nordmark 	(_cr)->cr_callback_arg = (_mp);				\
1788bd670b35SErik Nordmark 	(_cr)->cr_callback_func = (_callback)
17897c478bd9Sstevel@tonic-gate 
17907c478bd9Sstevel@tonic-gate #define	ESP_INIT_CRYPTO_MAC(mac, icvlen, icvbuf) {			\
17917c478bd9Sstevel@tonic-gate 	(mac)->cd_format = CRYPTO_DATA_RAW;				\
17927c478bd9Sstevel@tonic-gate 	(mac)->cd_offset = 0;						\
17937c478bd9Sstevel@tonic-gate 	(mac)->cd_length = icvlen;					\
17947c478bd9Sstevel@tonic-gate 	(mac)->cd_raw.iov_base = (char *)icvbuf;			\
17957c478bd9Sstevel@tonic-gate 	(mac)->cd_raw.iov_len = icvlen;					\
17967c478bd9Sstevel@tonic-gate }
17977c478bd9Sstevel@tonic-gate 
17987c478bd9Sstevel@tonic-gate #define	ESP_INIT_CRYPTO_DATA(data, mp, off, len) {			\
17997c478bd9Sstevel@tonic-gate 	if (MBLKL(mp) >= (len) + (off)) {				\
18007c478bd9Sstevel@tonic-gate 		(data)->cd_format = CRYPTO_DATA_RAW;			\
18017c478bd9Sstevel@tonic-gate 		(data)->cd_raw.iov_base = (char *)(mp)->b_rptr;		\
18027c478bd9Sstevel@tonic-gate 		(data)->cd_raw.iov_len = MBLKL(mp);			\
18037c478bd9Sstevel@tonic-gate 		(data)->cd_offset = off;				\
18047c478bd9Sstevel@tonic-gate 	} else {							\
18057c478bd9Sstevel@tonic-gate 		(data)->cd_format = CRYPTO_DATA_MBLK;			\
18067c478bd9Sstevel@tonic-gate 		(data)->cd_mp = mp;					\
18077c478bd9Sstevel@tonic-gate 		(data)->cd_offset = off;				\
18087c478bd9Sstevel@tonic-gate 	}								\
18097c478bd9Sstevel@tonic-gate 	(data)->cd_length = len;					\
18107c478bd9Sstevel@tonic-gate }
18117c478bd9Sstevel@tonic-gate 
18127c478bd9Sstevel@tonic-gate #define	ESP_INIT_CRYPTO_DUAL_DATA(data, mp, off1, len1, off2, len2) {	\
18137c478bd9Sstevel@tonic-gate 	(data)->dd_format = CRYPTO_DATA_MBLK;				\
18147c478bd9Sstevel@tonic-gate 	(data)->dd_mp = mp;						\
18157c478bd9Sstevel@tonic-gate 	(data)->dd_len1 = len1;						\
18167c478bd9Sstevel@tonic-gate 	(data)->dd_offset1 = off1;					\
18177c478bd9Sstevel@tonic-gate 	(data)->dd_len2 = len2;						\
18187c478bd9Sstevel@tonic-gate 	(data)->dd_offset2 = off2;					\
18197c478bd9Sstevel@tonic-gate }
18207c478bd9Sstevel@tonic-gate 
1821bd670b35SErik Nordmark /*
1822bd670b35SErik Nordmark  * Returns data_mp if successfully completed the request. Returns
1823bd670b35SErik Nordmark  * NULL if it failed (and increments InDiscards) or if it is pending.
1824bd670b35SErik Nordmark  */
1825bd670b35SErik Nordmark static mblk_t *
esp_submit_req_inbound(mblk_t * esp_mp,ip_recv_attr_t * ira,ipsa_t * assoc,uint_t esph_offset)1826bd670b35SErik Nordmark esp_submit_req_inbound(mblk_t *esp_mp, ip_recv_attr_t *ira,
1827bd670b35SErik Nordmark     ipsa_t *assoc, uint_t esph_offset)
18287c478bd9Sstevel@tonic-gate {
18297c478bd9Sstevel@tonic-gate 	uint_t auth_offset, msg_len, auth_len;
1830bd670b35SErik Nordmark 	crypto_call_req_t call_req, *callrp;
1831bd670b35SErik Nordmark 	mblk_t *mp;
1832628b0c67SMark Fenwick 	esph_t *esph_ptr;
1833bd670b35SErik Nordmark 	int kef_rc;
18347c478bd9Sstevel@tonic-gate 	uint_t icv_len = assoc->ipsa_mac_len;
18357c478bd9Sstevel@tonic-gate 	crypto_ctx_template_t auth_ctx_tmpl;
1836bd670b35SErik Nordmark 	boolean_t do_auth, do_encr, force;
18377c478bd9Sstevel@tonic-gate 	uint_t encr_offset, encr_len;
18387c478bd9Sstevel@tonic-gate 	uint_t iv_len = assoc->ipsa_iv_len;
18397c478bd9Sstevel@tonic-gate 	crypto_ctx_template_t encr_ctx_tmpl;
1840bd670b35SErik Nordmark 	ipsec_crypto_t	*ic, icstack;
1841628b0c67SMark Fenwick 	uchar_t *iv_ptr;
1842bd670b35SErik Nordmark 	netstack_t *ns = ira->ira_ill->ill_ipst->ips_netstack;
1843bd670b35SErik Nordmark 	ipsec_stack_t *ipss = ns->netstack_ipsec;
1844bd670b35SErik Nordmark 	ipsecesp_stack_t *espstack = ns->netstack_ipsecesp;
1845f4b3ec61Sdh155122 
1846*4132743aSToomas Soome 	mp = NULL;
18477c478bd9Sstevel@tonic-gate 	do_auth = assoc->ipsa_auth_alg != SADB_AALG_NONE;
18487c478bd9Sstevel@tonic-gate 	do_encr = assoc->ipsa_encr_alg != SADB_EALG_NULL;
1849bd670b35SErik Nordmark 	force = (assoc->ipsa_flags & IPSA_F_ASYNC);
1850bd670b35SErik Nordmark 
1851bd670b35SErik Nordmark #ifdef IPSEC_LATENCY_TEST
1852bd670b35SErik Nordmark 	kef_rc = CRYPTO_SUCCESS;
1853bd670b35SErik Nordmark #else
1854bd670b35SErik Nordmark 	kef_rc = CRYPTO_FAILED;
1855bd670b35SErik Nordmark #endif
18567c478bd9Sstevel@tonic-gate 
18577c478bd9Sstevel@tonic-gate 	/*
18587c478bd9Sstevel@tonic-gate 	 * An inbound packet is of the form:
1859bd670b35SErik Nordmark 	 * [IP,options,ESP,IV,data,ICV,pad]
18607c478bd9Sstevel@tonic-gate 	 */
1861628b0c67SMark Fenwick 	esph_ptr = (esph_t *)(esp_mp->b_rptr + esph_offset);
1862628b0c67SMark Fenwick 	iv_ptr = (uchar_t *)(esph_ptr + 1);
1863628b0c67SMark Fenwick 	/* Packet length starting at IP header ending after ESP ICV. */
18647c478bd9Sstevel@tonic-gate 	msg_len = MBLKL(esp_mp);
18657c478bd9Sstevel@tonic-gate 
1866628b0c67SMark Fenwick 	encr_offset = esph_offset + sizeof (esph_t) + iv_len;
1867628b0c67SMark Fenwick 	encr_len = msg_len - encr_offset;
1868628b0c67SMark Fenwick 
1869628b0c67SMark Fenwick 	/*
1870628b0c67SMark Fenwick 	 * Counter mode algs need a nonce. This is setup in sadb_common_add().
1871628b0c67SMark Fenwick 	 * If for some reason we are using a SA which does not have a nonce
1872628b0c67SMark Fenwick 	 * then we must fail here.
1873628b0c67SMark Fenwick 	 */
1874628b0c67SMark Fenwick 	if ((assoc->ipsa_flags & IPSA_F_COUNTERMODE) &&
1875628b0c67SMark Fenwick 	    (assoc->ipsa_nonce == NULL)) {
1876bd670b35SErik Nordmark 		ip_drop_packet(esp_mp, B_TRUE, ira->ira_ill,
1877628b0c67SMark Fenwick 		    DROPPER(ipss, ipds_esp_nomem), &espstack->esp_dropper);
1878bd670b35SErik Nordmark 		return (NULL);
1879bd670b35SErik Nordmark 	}
1880bd670b35SErik Nordmark 
1881bd670b35SErik Nordmark 	if (force) {
1882bd670b35SErik Nordmark 		/* We are doing asynch; allocate mblks to hold state */
1883bd670b35SErik Nordmark 		if ((mp = ip_recv_attr_to_mblk(ira)) == NULL ||
1884bd670b35SErik Nordmark 		    (mp = ipsec_add_crypto_data(mp, &ic)) == NULL) {
1885bd670b35SErik Nordmark 			BUMP_MIB(ira->ira_ill->ill_ip_mib, ipIfStatsInDiscards);
1886bd670b35SErik Nordmark 			ip_drop_input("ipIfStatsInDiscards", esp_mp,
1887bd670b35SErik Nordmark 			    ira->ira_ill);
1888bd670b35SErik Nordmark 			return (NULL);
1889bd670b35SErik Nordmark 		}
1890bd670b35SErik Nordmark 		linkb(mp, esp_mp);
1891bd670b35SErik Nordmark 		callrp = &call_req;
1892bd670b35SErik Nordmark 		ESP_INIT_CALLREQ(callrp, mp, esp_kcf_callback_inbound);
1893bd670b35SErik Nordmark 	} else {
1894bd670b35SErik Nordmark 		/*
1895bd670b35SErik Nordmark 		 * If we know we are going to do sync then ipsec_crypto_t
1896bd670b35SErik Nordmark 		 * should be on the stack.
1897bd670b35SErik Nordmark 		 */
1898bd670b35SErik Nordmark 		ic = &icstack;
1899bd670b35SErik Nordmark 		bzero(ic, sizeof (*ic));
1900bd670b35SErik Nordmark 		callrp = NULL;
1901628b0c67SMark Fenwick 	}
1902628b0c67SMark Fenwick 
19037c478bd9Sstevel@tonic-gate 	if (do_auth) {
19047c478bd9Sstevel@tonic-gate 		/* authentication context template */
19057c478bd9Sstevel@tonic-gate 		IPSEC_CTX_TMPL(assoc, ipsa_authtmpl, IPSEC_ALG_AUTH,
19067c478bd9Sstevel@tonic-gate 		    auth_ctx_tmpl);
19077c478bd9Sstevel@tonic-gate 
19087c478bd9Sstevel@tonic-gate 		/* ICV to be verified */
1909bd670b35SErik Nordmark 		ESP_INIT_CRYPTO_MAC(&ic->ic_crypto_mac,
19107c478bd9Sstevel@tonic-gate 		    icv_len, esp_mp->b_wptr - icv_len);
19117c478bd9Sstevel@tonic-gate 
19127c478bd9Sstevel@tonic-gate 		/* authentication starts at the ESP header */
19137c478bd9Sstevel@tonic-gate 		auth_offset = esph_offset;
19147c478bd9Sstevel@tonic-gate 		auth_len = msg_len - auth_offset - icv_len;
19157c478bd9Sstevel@tonic-gate 		if (!do_encr) {
19167c478bd9Sstevel@tonic-gate 			/* authentication only */
19177c478bd9Sstevel@tonic-gate 			/* initialize input data argument */
1918bd670b35SErik Nordmark 			ESP_INIT_CRYPTO_DATA(&ic->ic_crypto_data,
19197c478bd9Sstevel@tonic-gate 			    esp_mp, auth_offset, auth_len);
19207c478bd9Sstevel@tonic-gate 
19217c478bd9Sstevel@tonic-gate 			/* call the crypto framework */
19227c478bd9Sstevel@tonic-gate 			kef_rc = crypto_mac_verify(&assoc->ipsa_amech,
1923bd670b35SErik Nordmark 			    &ic->ic_crypto_data,
19247c478bd9Sstevel@tonic-gate 			    &assoc->ipsa_kcfauthkey, auth_ctx_tmpl,
1925bd670b35SErik Nordmark 			    &ic->ic_crypto_mac, callrp);
19267c478bd9Sstevel@tonic-gate 		}
19277c478bd9Sstevel@tonic-gate 	}
19287c478bd9Sstevel@tonic-gate 
19297c478bd9Sstevel@tonic-gate 	if (do_encr) {
19307c478bd9Sstevel@tonic-gate 		/* encryption template */
19317c478bd9Sstevel@tonic-gate 		IPSEC_CTX_TMPL(assoc, ipsa_encrtmpl, IPSEC_ALG_ENCR,
19327c478bd9Sstevel@tonic-gate 		    encr_ctx_tmpl);
19337c478bd9Sstevel@tonic-gate 
1934628b0c67SMark Fenwick 		/* Call the nonce update function. Also passes in IV */
1935628b0c67SMark Fenwick 		(assoc->ipsa_noncefunc)(assoc, (uchar_t *)esph_ptr, encr_len,
1936bd670b35SErik Nordmark 		    iv_ptr, &ic->ic_cmm, &ic->ic_crypto_data);
19377c478bd9Sstevel@tonic-gate 
19387c478bd9Sstevel@tonic-gate 		if (!do_auth) {
19397c478bd9Sstevel@tonic-gate 			/* decryption only */
19407c478bd9Sstevel@tonic-gate 			/* initialize input data argument */
1941bd670b35SErik Nordmark 			ESP_INIT_CRYPTO_DATA(&ic->ic_crypto_data,
19427c478bd9Sstevel@tonic-gate 			    esp_mp, encr_offset, encr_len);
19437c478bd9Sstevel@tonic-gate 
19447c478bd9Sstevel@tonic-gate 			/* call the crypto framework */
1945628b0c67SMark Fenwick 			kef_rc = crypto_decrypt((crypto_mechanism_t *)
1946bd670b35SErik Nordmark 			    &ic->ic_cmm, &ic->ic_crypto_data,
19477c478bd9Sstevel@tonic-gate 			    &assoc->ipsa_kcfencrkey, encr_ctx_tmpl,
1948bd670b35SErik Nordmark 			    NULL, callrp);
19497c478bd9Sstevel@tonic-gate 		}
19507c478bd9Sstevel@tonic-gate 	}
19517c478bd9Sstevel@tonic-gate 
19527c478bd9Sstevel@tonic-gate 	if (do_auth && do_encr) {
19537c478bd9Sstevel@tonic-gate 		/* dual operation */
19547c478bd9Sstevel@tonic-gate 		/* initialize input data argument */
1955bd670b35SErik Nordmark 		ESP_INIT_CRYPTO_DUAL_DATA(&ic->ic_crypto_dual_data,
19567c478bd9Sstevel@tonic-gate 		    esp_mp, auth_offset, auth_len,
19577c478bd9Sstevel@tonic-gate 		    encr_offset, encr_len - icv_len);
19587c478bd9Sstevel@tonic-gate 
19597c478bd9Sstevel@tonic-gate 		/* specify IV */
1960bd670b35SErik Nordmark 		ic->ic_crypto_dual_data.dd_miscdata = (char *)iv_ptr;
19617c478bd9Sstevel@tonic-gate 
19627c478bd9Sstevel@tonic-gate 		/* call the framework */
19637c478bd9Sstevel@tonic-gate 		kef_rc = crypto_mac_verify_decrypt(&assoc->ipsa_amech,
1964bd670b35SErik Nordmark 		    &assoc->ipsa_emech, &ic->ic_crypto_dual_data,
19657c478bd9Sstevel@tonic-gate 		    &assoc->ipsa_kcfauthkey, &assoc->ipsa_kcfencrkey,
1966bd670b35SErik Nordmark 		    auth_ctx_tmpl, encr_ctx_tmpl, &ic->ic_crypto_mac,
1967bd670b35SErik Nordmark 		    NULL, callrp);
19687c478bd9Sstevel@tonic-gate 	}
19697c478bd9Sstevel@tonic-gate 
19707c478bd9Sstevel@tonic-gate 	switch (kef_rc) {
19717c478bd9Sstevel@tonic-gate 	case CRYPTO_SUCCESS:
1972f4b3ec61Sdh155122 		ESP_BUMP_STAT(espstack, crypto_sync);
1973bd670b35SErik Nordmark 		esp_mp = esp_in_done(esp_mp, ira, ic);
1974bd670b35SErik Nordmark 		if (force) {
1975bd670b35SErik Nordmark 			/* Free mp after we are done with ic */
1976bd670b35SErik Nordmark 			mp = ipsec_free_crypto_data(mp);
1977bd670b35SErik Nordmark 			(void) ip_recv_attr_free_mblk(mp);
1978bd670b35SErik Nordmark 		}
1979bd670b35SErik Nordmark 		return (esp_mp);
19807c478bd9Sstevel@tonic-gate 	case CRYPTO_QUEUED:
1981bd670b35SErik Nordmark 		/* esp_kcf_callback_inbound() will be invoked on completion */
1982f4b3ec61Sdh155122 		ESP_BUMP_STAT(espstack, crypto_async);
1983bd670b35SErik Nordmark 		return (NULL);
19847c478bd9Sstevel@tonic-gate 	case CRYPTO_INVALID_MAC:
1985bd670b35SErik Nordmark 		if (force) {
1986bd670b35SErik Nordmark 			mp = ipsec_free_crypto_data(mp);
1987bd670b35SErik Nordmark 			esp_mp = ip_recv_attr_free_mblk(mp);
1988bd670b35SErik Nordmark 		}
1989f4b3ec61Sdh155122 		ESP_BUMP_STAT(espstack, crypto_sync);
1990bd670b35SErik Nordmark 		BUMP_MIB(ira->ira_ill->ill_ip_mib, ipIfStatsInDiscards);
1991bd670b35SErik Nordmark 		esp_log_bad_auth(esp_mp, ira);
1992bd670b35SErik Nordmark 		/* esp_mp was passed to ip_drop_packet */
1993bd670b35SErik Nordmark 		return (NULL);
19947c478bd9Sstevel@tonic-gate 	}
19957c478bd9Sstevel@tonic-gate 
1996bd984e57SDan McDonald 	if (force) {
1997bd670b35SErik Nordmark 		mp = ipsec_free_crypto_data(mp);
1998bd670b35SErik Nordmark 		esp_mp = ip_recv_attr_free_mblk(mp);
1999bd984e57SDan McDonald 	}
2000bd670b35SErik Nordmark 	BUMP_MIB(ira->ira_ill->ill_ip_mib, ipIfStatsInDiscards);
2001bd670b35SErik Nordmark 	esp_crypto_failed(esp_mp, B_TRUE, kef_rc, ira->ira_ill, espstack);
2002bd670b35SErik Nordmark 	/* esp_mp was passed to ip_drop_packet */
2003bd670b35SErik Nordmark 	return (NULL);
20047c478bd9Sstevel@tonic-gate }
20057c478bd9Sstevel@tonic-gate 
2006437220cdSdanmcd /*
2007437220cdSdanmcd  * Compute the IP and UDP checksums -- common code for both keepalives and
2008437220cdSdanmcd  * actual ESP-in-UDP packets.  Be flexible with multiple mblks because ESP
2009437220cdSdanmcd  * uses mblk-insertion to insert the UDP header.
2010437220cdSdanmcd  * TODO - If there is an easy way to prep a packet for HW checksums, make
2011437220cdSdanmcd  * it happen here.
2012bd670b35SErik Nordmark  * Note that this is used before both before calling ip_output_simple and
2013bd670b35SErik Nordmark  * in the esp datapath. The former could use IXAF_SET_ULP_CKSUM but not the
2014bd670b35SErik Nordmark  * latter.
2015437220cdSdanmcd  */
2016437220cdSdanmcd static void
esp_prepare_udp(netstack_t * ns,mblk_t * mp,ipha_t * ipha)2017437220cdSdanmcd esp_prepare_udp(netstack_t *ns, mblk_t *mp, ipha_t *ipha)
2018437220cdSdanmcd {
2019437220cdSdanmcd 	int offset;
2020437220cdSdanmcd 	uint32_t cksum;
2021437220cdSdanmcd 	uint16_t *arr;
2022437220cdSdanmcd 	mblk_t *udpmp = mp;
202387e10ffaSwy83408 	uint_t hlen = IPH_HDR_LENGTH(ipha);
2024437220cdSdanmcd 
2025437220cdSdanmcd 	ASSERT(MBLKL(mp) >= sizeof (ipha_t));
2026437220cdSdanmcd 
2027437220cdSdanmcd 	ipha->ipha_hdr_checksum = 0;
2028437220cdSdanmcd 	ipha->ipha_hdr_checksum = ip_csum_hdr(ipha);
2029437220cdSdanmcd 
2030437220cdSdanmcd 	if (ns->netstack_udp->us_do_checksum) {
2031437220cdSdanmcd 		ASSERT(MBLKL(udpmp) >= sizeof (udpha_t));
2032437220cdSdanmcd 		/* arr points to the IP header. */
2033437220cdSdanmcd 		arr = (uint16_t *)ipha;
2034437220cdSdanmcd 		IP_STAT(ns->netstack_ip, ip_out_sw_cksum);
2035bd670b35SErik Nordmark 		IP_STAT_UPDATE(ns->netstack_ip, ip_out_sw_cksum_bytes,
203687e10ffaSwy83408 		    ntohs(htons(ipha->ipha_length) - hlen));
2037437220cdSdanmcd 		/* arr[6-9] are the IP addresses. */
2038437220cdSdanmcd 		cksum = IP_UDP_CSUM_COMP + arr[6] + arr[7] + arr[8] + arr[9] +
203987e10ffaSwy83408 		    ntohs(htons(ipha->ipha_length) - hlen);
204087e10ffaSwy83408 		cksum = IP_CSUM(mp, hlen, cksum);
204187e10ffaSwy83408 		offset = hlen + UDP_CHECKSUM_OFFSET;
2042437220cdSdanmcd 		while (offset >= MBLKL(udpmp)) {
2043437220cdSdanmcd 			offset -= MBLKL(udpmp);
2044437220cdSdanmcd 			udpmp = udpmp->b_cont;
2045437220cdSdanmcd 		}
2046437220cdSdanmcd 		/* arr points to the UDP header's checksum field. */
2047437220cdSdanmcd 		arr = (uint16_t *)(udpmp->b_rptr + offset);
2048437220cdSdanmcd 		*arr = cksum;
2049437220cdSdanmcd 	}
2050437220cdSdanmcd }
2051437220cdSdanmcd 
2052437220cdSdanmcd /*
205373184bc7SDan McDonald  * taskq handler so we can send the NAT-T keepalive on a separate thread.
205473184bc7SDan McDonald  */
205573184bc7SDan McDonald static void
actually_send_keepalive(void * arg)205673184bc7SDan McDonald actually_send_keepalive(void *arg)
205773184bc7SDan McDonald {
2058bd670b35SErik Nordmark 	mblk_t *mp = (mblk_t *)arg;
2059bd670b35SErik Nordmark 	ip_xmit_attr_t ixas;
206073184bc7SDan McDonald 	netstack_t	*ns;
2061bd670b35SErik Nordmark 	netstackid_t	stackid;
206273184bc7SDan McDonald 
2063bd670b35SErik Nordmark 	stackid = (netstackid_t)(uintptr_t)mp->b_prev;
2064bd670b35SErik Nordmark 	mp->b_prev = NULL;
2065bd670b35SErik Nordmark 	ns = netstack_find_by_stackid(stackid);
2066bd670b35SErik Nordmark 	if (ns == NULL) {
2067bd670b35SErik Nordmark 		/* Disappeared */
2068bd670b35SErik Nordmark 		ip_drop_output("ipIfStatsOutDiscards", mp, NULL);
2069bd670b35SErik Nordmark 		freemsg(mp);
207073184bc7SDan McDonald 		return;
207173184bc7SDan McDonald 	}
207273184bc7SDan McDonald 
2073bd670b35SErik Nordmark 	bzero(&ixas, sizeof (ixas));
2074bd670b35SErik Nordmark 	ixas.ixa_zoneid = ALL_ZONES;
2075bd670b35SErik Nordmark 	ixas.ixa_cred = kcred;
2076bd670b35SErik Nordmark 	ixas.ixa_cpid = NOPID;
2077bd670b35SErik Nordmark 	ixas.ixa_tsl = NULL;
2078bd670b35SErik Nordmark 	ixas.ixa_ipst = ns->netstack_ip;
2079bd670b35SErik Nordmark 	/* No ULP checksum; done by esp_prepare_udp */
208044b099c4SSowmini Varadhan 	ixas.ixa_flags = (IXAF_IS_IPV4 | IXAF_NO_IPSEC | IXAF_VERIFY_SOURCE);
2081bd670b35SErik Nordmark 
2082bd670b35SErik Nordmark 	(void) ip_output_simple(mp, &ixas);
2083bd670b35SErik Nordmark 	ixa_cleanup(&ixas);
208473184bc7SDan McDonald 	netstack_rele(ns);
208573184bc7SDan McDonald }
208673184bc7SDan McDonald 
208773184bc7SDan McDonald /*
2088bd670b35SErik Nordmark  * Send a one-byte UDP NAT-T keepalive.
2089437220cdSdanmcd  */
2090437220cdSdanmcd void
ipsecesp_send_keepalive(ipsa_t * assoc)2091437220cdSdanmcd ipsecesp_send_keepalive(ipsa_t *assoc)
2092437220cdSdanmcd {
2093bd670b35SErik Nordmark 	mblk_t		*mp;
2094437220cdSdanmcd 	ipha_t		*ipha;
2095437220cdSdanmcd 	udpha_t		*udpha;
2096bd670b35SErik Nordmark 	netstack_t	*ns = assoc->ipsa_netstack;
2097437220cdSdanmcd 
20980c0328cdSBill Sommerfeld 	ASSERT(MUTEX_NOT_HELD(&assoc->ipsa_lock));
2099437220cdSdanmcd 
2100437220cdSdanmcd 	mp = allocb(sizeof (ipha_t) + sizeof (udpha_t) + 1, BPRI_HI);
2101437220cdSdanmcd 	if (mp == NULL)
2102437220cdSdanmcd 		return;
2103437220cdSdanmcd 	ipha = (ipha_t *)mp->b_rptr;
2104437220cdSdanmcd 	ipha->ipha_version_and_hdr_length = IP_SIMPLE_HDR_VERSION;
2105437220cdSdanmcd 	ipha->ipha_type_of_service = 0;
2106437220cdSdanmcd 	ipha->ipha_length = htons(sizeof (ipha_t) + sizeof (udpha_t) + 1);
2107437220cdSdanmcd 	/* Use the low-16 of the SPI so we have some clue where it came from. */
2108437220cdSdanmcd 	ipha->ipha_ident = *(((uint16_t *)(&assoc->ipsa_spi)) + 1);
2109437220cdSdanmcd 	ipha->ipha_fragment_offset_and_flags = 0;  /* Too small to fragment! */
2110437220cdSdanmcd 	ipha->ipha_ttl = 0xFF;
2111437220cdSdanmcd 	ipha->ipha_protocol = IPPROTO_UDP;
2112437220cdSdanmcd 	ipha->ipha_hdr_checksum = 0;
2113437220cdSdanmcd 	ipha->ipha_src = assoc->ipsa_srcaddr[0];
2114437220cdSdanmcd 	ipha->ipha_dst = assoc->ipsa_dstaddr[0];
2115437220cdSdanmcd 	udpha = (udpha_t *)(ipha + 1);
2116437220cdSdanmcd 	udpha->uha_src_port = (assoc->ipsa_local_nat_port != 0) ?
2117437220cdSdanmcd 	    assoc->ipsa_local_nat_port : htons(IPPORT_IKE_NATT);
2118437220cdSdanmcd 	udpha->uha_dst_port = (assoc->ipsa_remote_nat_port != 0) ?
2119437220cdSdanmcd 	    assoc->ipsa_remote_nat_port : htons(IPPORT_IKE_NATT);
2120437220cdSdanmcd 	udpha->uha_length = htons(sizeof (udpha_t) + 1);
2121437220cdSdanmcd 	udpha->uha_checksum = 0;
2122437220cdSdanmcd 	mp->b_wptr = (uint8_t *)(udpha + 1);
2123437220cdSdanmcd 	*(mp->b_wptr++) = 0xFF;
2124437220cdSdanmcd 
2125bd670b35SErik Nordmark 	esp_prepare_udp(ns, mp, ipha);
2126437220cdSdanmcd 
212773184bc7SDan McDonald 	/*
212873184bc7SDan McDonald 	 * We're holding an isaf_t bucket lock, so pawn off the actual
212973184bc7SDan McDonald 	 * packet transmission to another thread.  Just in case syncq
213073184bc7SDan McDonald 	 * processing causes a same-bucket packet to be processed.
213173184bc7SDan McDonald 	 */
2132bd670b35SErik Nordmark 	mp->b_prev = (mblk_t *)(uintptr_t)ns->netstack_stackid;
2133bd670b35SErik Nordmark 
2134bd670b35SErik Nordmark 	if (taskq_dispatch(esp_taskq, actually_send_keepalive, mp,
2135fc8ae2ecSToomas Soome 	    TQ_NOSLEEP) == TASKQID_INVALID) {
213673184bc7SDan McDonald 		/* Assume no memory if taskq_dispatch() fails. */
2137bd670b35SErik Nordmark 		mp->b_prev = NULL;
2138bd670b35SErik Nordmark 		ip_drop_packet(mp, B_FALSE, NULL,
2139bd670b35SErik Nordmark 		    DROPPER(ns->netstack_ipsec, ipds_esp_nomem),
2140bd670b35SErik Nordmark 		    &ns->netstack_ipsecesp->esp_dropper);
214173184bc7SDan McDonald 	}
2142437220cdSdanmcd }
2143437220cdSdanmcd 
2144bd670b35SErik Nordmark /*
2145bd670b35SErik Nordmark  * Returns mp if successfully completed the request. Returns
2146bd670b35SErik Nordmark  * NULL if it failed (and increments InDiscards) or if it is pending.
2147bd670b35SErik Nordmark  */
2148bd670b35SErik Nordmark static mblk_t *
esp_submit_req_outbound(mblk_t * data_mp,ip_xmit_attr_t * ixa,ipsa_t * assoc,uchar_t * icv_buf,uint_t payload_len)2149bd670b35SErik Nordmark esp_submit_req_outbound(mblk_t *data_mp, ip_xmit_attr_t *ixa, ipsa_t *assoc,
2150bd670b35SErik Nordmark     uchar_t *icv_buf, uint_t payload_len)
21517c478bd9Sstevel@tonic-gate {
21527c478bd9Sstevel@tonic-gate 	uint_t auth_len;
2153bd670b35SErik Nordmark 	crypto_call_req_t call_req, *callrp;
2154bd670b35SErik Nordmark 	mblk_t *esp_mp;
2155628b0c67SMark Fenwick 	esph_t *esph_ptr;
2156bd670b35SErik Nordmark 	mblk_t *mp;
21577c478bd9Sstevel@tonic-gate 	int kef_rc = CRYPTO_FAILED;
21587c478bd9Sstevel@tonic-gate 	uint_t icv_len = assoc->ipsa_mac_len;
21597c478bd9Sstevel@tonic-gate 	crypto_ctx_template_t auth_ctx_tmpl;
2160bd670b35SErik Nordmark 	boolean_t do_auth, do_encr, force;
21617c478bd9Sstevel@tonic-gate 	uint_t iv_len = assoc->ipsa_iv_len;
21627c478bd9Sstevel@tonic-gate 	crypto_ctx_template_t encr_ctx_tmpl;
21637c478bd9Sstevel@tonic-gate 	boolean_t is_natt = ((assoc->ipsa_flags & IPSA_F_NATT) != 0);
21647c478bd9Sstevel@tonic-gate 	size_t esph_offset = (is_natt ? UDPH_SIZE : 0);
2165bd670b35SErik Nordmark 	netstack_t	*ns = ixa->ixa_ipst->ips_netstack;
2166f4b3ec61Sdh155122 	ipsecesp_stack_t *espstack = ns->netstack_ipsecesp;
2167bd670b35SErik Nordmark 	ipsec_crypto_t	*ic, icstack;
2168628b0c67SMark Fenwick 	uchar_t		*iv_ptr;
2169628b0c67SMark Fenwick 	crypto_data_t	*cd_ptr = NULL;
2170bd670b35SErik Nordmark 	ill_t		*ill = ixa->ixa_nce->nce_ill;
2171bd670b35SErik Nordmark 	ipsec_stack_t	*ipss = ns->netstack_ipsec;
21727c478bd9Sstevel@tonic-gate 
2173f4b3ec61Sdh155122 	esp3dbg(espstack, ("esp_submit_req_outbound:%s",
2174f4b3ec61Sdh155122 	    is_natt ? "natt" : "not natt"));
21757c478bd9Sstevel@tonic-gate 
2176*4132743aSToomas Soome 	mp = NULL;
21777c478bd9Sstevel@tonic-gate 	do_encr = assoc->ipsa_encr_alg != SADB_EALG_NULL;
21787c478bd9Sstevel@tonic-gate 	do_auth = assoc->ipsa_auth_alg != SADB_AALG_NONE;
2179bd670b35SErik Nordmark 	force = (assoc->ipsa_flags & IPSA_F_ASYNC);
2180bd670b35SErik Nordmark 
2181bd670b35SErik Nordmark #ifdef IPSEC_LATENCY_TEST
2182bd670b35SErik Nordmark 	kef_rc = CRYPTO_SUCCESS;
2183bd670b35SErik Nordmark #else
2184bd670b35SErik Nordmark 	kef_rc = CRYPTO_FAILED;
2185bd670b35SErik Nordmark #endif
21867c478bd9Sstevel@tonic-gate 
21877c478bd9Sstevel@tonic-gate 	/*
21887c478bd9Sstevel@tonic-gate 	 * Outbound IPsec packets are of the form:
2189bd670b35SErik Nordmark 	 * [IP,options] -> [ESP,IV] -> [data] -> [pad,ICV]
21907c478bd9Sstevel@tonic-gate 	 * unless it's NATT, then it's
2191bd670b35SErik Nordmark 	 * [IP,options] -> [udp][ESP,IV] -> [data] -> [pad,ICV]
21927c478bd9Sstevel@tonic-gate 	 * Get a pointer to the mblk containing the ESP header.
21937c478bd9Sstevel@tonic-gate 	 */
2194bd670b35SErik Nordmark 	ASSERT(data_mp->b_cont != NULL);
2195bd670b35SErik Nordmark 	esp_mp = data_mp->b_cont;
2196628b0c67SMark Fenwick 	esph_ptr = (esph_t *)(esp_mp->b_rptr + esph_offset);
2197628b0c67SMark Fenwick 	iv_ptr = (uchar_t *)(esph_ptr + 1);
2198628b0c67SMark Fenwick 
2199628b0c67SMark Fenwick 	/*
2200628b0c67SMark Fenwick 	 * Combined mode algs need a nonce. This is setup in sadb_common_add().
2201628b0c67SMark Fenwick 	 * If for some reason we are using a SA which does not have a nonce
2202628b0c67SMark Fenwick 	 * then we must fail here.
2203628b0c67SMark Fenwick 	 */
2204628b0c67SMark Fenwick 	if ((assoc->ipsa_flags & IPSA_F_COUNTERMODE) &&
2205628b0c67SMark Fenwick 	    (assoc->ipsa_nonce == NULL)) {
2206bd670b35SErik Nordmark 		ip_drop_packet(data_mp, B_FALSE, NULL,
2207628b0c67SMark Fenwick 		    DROPPER(ipss, ipds_esp_nomem), &espstack->esp_dropper);
2208bd670b35SErik Nordmark 		return (NULL);
2209628b0c67SMark Fenwick 	}
22107c478bd9Sstevel@tonic-gate 
2211bd670b35SErik Nordmark 	if (force) {
2212bd670b35SErik Nordmark 		/* We are doing asynch; allocate mblks to hold state */
2213bd670b35SErik Nordmark 		if ((mp = ip_xmit_attr_to_mblk(ixa)) == NULL ||
2214bd670b35SErik Nordmark 		    (mp = ipsec_add_crypto_data(mp, &ic)) == NULL) {
2215bd670b35SErik Nordmark 			BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
2216bd670b35SErik Nordmark 			ip_drop_output("ipIfStatsOutDiscards", data_mp, ill);
2217bd670b35SErik Nordmark 			freemsg(data_mp);
2218bd670b35SErik Nordmark 			return (NULL);
2219bd670b35SErik Nordmark 		}
2220bd670b35SErik Nordmark 
2221bd670b35SErik Nordmark 		linkb(mp, data_mp);
2222bd670b35SErik Nordmark 		callrp = &call_req;
2223bd670b35SErik Nordmark 		ESP_INIT_CALLREQ(callrp, mp, esp_kcf_callback_outbound);
2224bd670b35SErik Nordmark 	} else {
2225bd670b35SErik Nordmark 		/*
2226bd670b35SErik Nordmark 		 * If we know we are going to do sync then ipsec_crypto_t
2227bd670b35SErik Nordmark 		 * should be on the stack.
2228bd670b35SErik Nordmark 		 */
2229bd670b35SErik Nordmark 		ic = &icstack;
2230bd670b35SErik Nordmark 		bzero(ic, sizeof (*ic));
2231bd670b35SErik Nordmark 		callrp = NULL;
2232bd670b35SErik Nordmark 	}
2233bd670b35SErik Nordmark 
22347c478bd9Sstevel@tonic-gate 
22357c478bd9Sstevel@tonic-gate 	if (do_auth) {
22367c478bd9Sstevel@tonic-gate 		/* authentication context template */
22377c478bd9Sstevel@tonic-gate 		IPSEC_CTX_TMPL(assoc, ipsa_authtmpl, IPSEC_ALG_AUTH,
22387c478bd9Sstevel@tonic-gate 		    auth_ctx_tmpl);
22397c478bd9Sstevel@tonic-gate 
22407c478bd9Sstevel@tonic-gate 		/* where to store the computed mac */
2241bd670b35SErik Nordmark 		ESP_INIT_CRYPTO_MAC(&ic->ic_crypto_mac,
22427c478bd9Sstevel@tonic-gate 		    icv_len, icv_buf);
22437c478bd9Sstevel@tonic-gate 
22447c478bd9Sstevel@tonic-gate 		/* authentication starts at the ESP header */
2245a86080f9Sdanmcd 		auth_len = payload_len + iv_len + sizeof (esph_t);
22467c478bd9Sstevel@tonic-gate 		if (!do_encr) {
22477c478bd9Sstevel@tonic-gate 			/* authentication only */
22487c478bd9Sstevel@tonic-gate 			/* initialize input data argument */
2249bd670b35SErik Nordmark 			ESP_INIT_CRYPTO_DATA(&ic->ic_crypto_data,
22507c478bd9Sstevel@tonic-gate 			    esp_mp, esph_offset, auth_len);
22517c478bd9Sstevel@tonic-gate 
22527c478bd9Sstevel@tonic-gate 			/* call the crypto framework */
22537c478bd9Sstevel@tonic-gate 			kef_rc = crypto_mac(&assoc->ipsa_amech,
2254bd670b35SErik Nordmark 			    &ic->ic_crypto_data,
22557c478bd9Sstevel@tonic-gate 			    &assoc->ipsa_kcfauthkey, auth_ctx_tmpl,
2256bd670b35SErik Nordmark 			    &ic->ic_crypto_mac, callrp);
22577c478bd9Sstevel@tonic-gate 		}
22587c478bd9Sstevel@tonic-gate 	}
22597c478bd9Sstevel@tonic-gate 
22607c478bd9Sstevel@tonic-gate 	if (do_encr) {
22617c478bd9Sstevel@tonic-gate 		/* encryption context template */
22627c478bd9Sstevel@tonic-gate 		IPSEC_CTX_TMPL(assoc, ipsa_encrtmpl, IPSEC_ALG_ENCR,
22637c478bd9Sstevel@tonic-gate 		    encr_ctx_tmpl);
2264628b0c67SMark Fenwick 		/* Call the nonce update function. */
2265628b0c67SMark Fenwick 		(assoc->ipsa_noncefunc)(assoc, (uchar_t *)esph_ptr, payload_len,
2266bd670b35SErik Nordmark 		    iv_ptr, &ic->ic_cmm, &ic->ic_crypto_data);
22677c478bd9Sstevel@tonic-gate 
22687c478bd9Sstevel@tonic-gate 		if (!do_auth) {
22697c478bd9Sstevel@tonic-gate 			/* encryption only, skip mblk that contains ESP hdr */
22707c478bd9Sstevel@tonic-gate 			/* initialize input data argument */
2271bd670b35SErik Nordmark 			ESP_INIT_CRYPTO_DATA(&ic->ic_crypto_data,
2272bd670b35SErik Nordmark 			    esp_mp->b_cont, 0, payload_len);
22737c478bd9Sstevel@tonic-gate 
2274628b0c67SMark Fenwick 			/*
2275628b0c67SMark Fenwick 			 * For combined mode ciphers, the ciphertext is the same
2276628b0c67SMark Fenwick 			 * size as the clear text, the ICV should follow the
2277628b0c67SMark Fenwick 			 * ciphertext. To convince the kcf to allow in-line
2278628b0c67SMark Fenwick 			 * encryption, with an ICV, use ipsec_out_crypto_mac
2279628b0c67SMark Fenwick 			 * to point to the same buffer as the data. The calling
2280628b0c67SMark Fenwick 			 * function need to ensure the buffer is large enough to
2281628b0c67SMark Fenwick 			 * include the ICV.
2282628b0c67SMark Fenwick 			 *
2283628b0c67SMark Fenwick 			 * The IV is already written to the packet buffer, the
2284628b0c67SMark Fenwick 			 * nonce setup function copied it to the params struct
2285628b0c67SMark Fenwick 			 * for the cipher to use.
2286628b0c67SMark Fenwick 			 */
2287628b0c67SMark Fenwick 			if (assoc->ipsa_flags & IPSA_F_COMBINED) {
2288bd670b35SErik Nordmark 				bcopy(&ic->ic_crypto_data,
2289bd670b35SErik Nordmark 				    &ic->ic_crypto_mac,
2290628b0c67SMark Fenwick 				    sizeof (crypto_data_t));
2291bd670b35SErik Nordmark 				ic->ic_crypto_mac.cd_length =
2292628b0c67SMark Fenwick 				    payload_len + icv_len;
2293bd670b35SErik Nordmark 				cd_ptr = &ic->ic_crypto_mac;
2294628b0c67SMark Fenwick 			}
22957c478bd9Sstevel@tonic-gate 
22967c478bd9Sstevel@tonic-gate 			/* call the crypto framework */
2297628b0c67SMark Fenwick 			kef_rc = crypto_encrypt((crypto_mechanism_t *)
2298bd670b35SErik Nordmark 			    &ic->ic_cmm, &ic->ic_crypto_data,
22997c478bd9Sstevel@tonic-gate 			    &assoc->ipsa_kcfencrkey, encr_ctx_tmpl,
2300bd670b35SErik Nordmark 			    cd_ptr, callrp);
2301628b0c67SMark Fenwick 
23027c478bd9Sstevel@tonic-gate 		}
23037c478bd9Sstevel@tonic-gate 	}
23047c478bd9Sstevel@tonic-gate 
23057c478bd9Sstevel@tonic-gate 	if (do_auth && do_encr) {
23067c478bd9Sstevel@tonic-gate 		/*
23077c478bd9Sstevel@tonic-gate 		 * Encryption and authentication:
23087c478bd9Sstevel@tonic-gate 		 * Pass the pointer to the mblk chain starting at the ESP
23097c478bd9Sstevel@tonic-gate 		 * header to the framework. Skip the ESP header mblk
23107c478bd9Sstevel@tonic-gate 		 * for encryption, which is reflected by an encryption
23117c478bd9Sstevel@tonic-gate 		 * offset equal to the length of that mblk. Start
23127c478bd9Sstevel@tonic-gate 		 * the authentication at the ESP header, i.e. use an
23137c478bd9Sstevel@tonic-gate 		 * authentication offset of zero.
23147c478bd9Sstevel@tonic-gate 		 */
2315bd670b35SErik Nordmark 		ESP_INIT_CRYPTO_DUAL_DATA(&ic->ic_crypto_dual_data,
23167c478bd9Sstevel@tonic-gate 		    esp_mp, MBLKL(esp_mp), payload_len, esph_offset, auth_len);
23177c478bd9Sstevel@tonic-gate 
23187c478bd9Sstevel@tonic-gate 		/* specify IV */
2319bd670b35SErik Nordmark 		ic->ic_crypto_dual_data.dd_miscdata = (char *)iv_ptr;
23207c478bd9Sstevel@tonic-gate 
23217c478bd9Sstevel@tonic-gate 		/* call the framework */
23227c478bd9Sstevel@tonic-gate 		kef_rc = crypto_encrypt_mac(&assoc->ipsa_emech,
23237c478bd9Sstevel@tonic-gate 		    &assoc->ipsa_amech, NULL,
23247c478bd9Sstevel@tonic-gate 		    &assoc->ipsa_kcfencrkey, &assoc->ipsa_kcfauthkey,
23257c478bd9Sstevel@tonic-gate 		    encr_ctx_tmpl, auth_ctx_tmpl,
2326bd670b35SErik Nordmark 		    &ic->ic_crypto_dual_data,
2327bd670b35SErik Nordmark 		    &ic->ic_crypto_mac, callrp);
23287c478bd9Sstevel@tonic-gate 	}
23297c478bd9Sstevel@tonic-gate 
23307c478bd9Sstevel@tonic-gate 	switch (kef_rc) {
23317c478bd9Sstevel@tonic-gate 	case CRYPTO_SUCCESS:
2332f4b3ec61Sdh155122 		ESP_BUMP_STAT(espstack, crypto_sync);
2333437220cdSdanmcd 		esp_set_usetime(assoc, B_FALSE);
2334bd670b35SErik Nordmark 		if (force) {
2335bd670b35SErik Nordmark 			mp = ipsec_free_crypto_data(mp);
2336bd670b35SErik Nordmark 			data_mp = ip_xmit_attr_free_mblk(mp);
2337bd670b35SErik Nordmark 		}
2338437220cdSdanmcd 		if (is_natt)
2339bd670b35SErik Nordmark 			esp_prepare_udp(ns, data_mp, (ipha_t *)data_mp->b_rptr);
2340bd670b35SErik Nordmark 		return (data_mp);
23417c478bd9Sstevel@tonic-gate 	case CRYPTO_QUEUED:
2342bd670b35SErik Nordmark 		/* esp_kcf_callback_outbound() will be invoked on completion */
2343f4b3ec61Sdh155122 		ESP_BUMP_STAT(espstack, crypto_async);
2344bd670b35SErik Nordmark 		return (NULL);
23457c478bd9Sstevel@tonic-gate 	}
23467c478bd9Sstevel@tonic-gate 
2347bd670b35SErik Nordmark 	if (force) {
2348bd670b35SErik Nordmark 		mp = ipsec_free_crypto_data(mp);
2349bd670b35SErik Nordmark 		data_mp = ip_xmit_attr_free_mblk(mp);
2350bd670b35SErik Nordmark 	}
2351bd670b35SErik Nordmark 	BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
2352bd670b35SErik Nordmark 	esp_crypto_failed(data_mp, B_FALSE, kef_rc, NULL, espstack);
2353bd670b35SErik Nordmark 	/* data_mp was passed to ip_drop_packet */
2354bd670b35SErik Nordmark 	return (NULL);
23557c478bd9Sstevel@tonic-gate }
23567c478bd9Sstevel@tonic-gate 
23577c478bd9Sstevel@tonic-gate /*
23587c478bd9Sstevel@tonic-gate  * Handle outbound IPsec processing for IPv4 and IPv6
2359bd670b35SErik Nordmark  *
2360bd670b35SErik Nordmark  * Returns data_mp if successfully completed the request. Returns
2361bd670b35SErik Nordmark  * NULL if it failed (and increments InDiscards) or if it is pending.
23627c478bd9Sstevel@tonic-gate  */
2363bd670b35SErik Nordmark static mblk_t *
esp_outbound(mblk_t * data_mp,ip_xmit_attr_t * ixa)2364bd670b35SErik Nordmark esp_outbound(mblk_t *data_mp, ip_xmit_attr_t *ixa)
23657c478bd9Sstevel@tonic-gate {
2366bd670b35SErik Nordmark 	mblk_t *espmp, *tailmp;
23677c478bd9Sstevel@tonic-gate 	ipha_t *ipha;
23687c478bd9Sstevel@tonic-gate 	ip6_t *ip6h;
2369628b0c67SMark Fenwick 	esph_t *esph_ptr, *iv_ptr;
23707c478bd9Sstevel@tonic-gate 	uint_t af;
23717c478bd9Sstevel@tonic-gate 	uint8_t *nhp;
23727c478bd9Sstevel@tonic-gate 	uintptr_t divpoint, datalen, adj, padlen, i, alloclen;
23737c478bd9Sstevel@tonic-gate 	uintptr_t esplen = sizeof (esph_t);
23747c478bd9Sstevel@tonic-gate 	uint8_t protocol;
23757c478bd9Sstevel@tonic-gate 	ipsa_t *assoc;
2376628b0c67SMark Fenwick 	uint_t iv_len, block_size, mac_len = 0;
23777c478bd9Sstevel@tonic-gate 	uchar_t *icv_buf;
23787c478bd9Sstevel@tonic-gate 	udpha_t *udpha;
23797c478bd9Sstevel@tonic-gate 	boolean_t is_natt = B_FALSE;
2380bd670b35SErik Nordmark 	netstack_t	*ns = ixa->ixa_ipst->ips_netstack;
2381bd670b35SErik Nordmark 	ipsecesp_stack_t *espstack = ns->netstack_ipsecesp;
2382bd670b35SErik Nordmark 	ipsec_stack_t	*ipss = ns->netstack_ipsec;
2383bd670b35SErik Nordmark 	ill_t		*ill = ixa->ixa_nce->nce_ill;
2384bd670b35SErik Nordmark 	boolean_t	need_refrele = B_FALSE;
2385f4b3ec61Sdh155122 
2386f4b3ec61Sdh155122 	ESP_BUMP_STAT(espstack, out_requests);
2387f4b3ec61Sdh155122 
23887c478bd9Sstevel@tonic-gate 	/*
23897c478bd9Sstevel@tonic-gate 	 * <sigh> We have to copy the message here, because TCP (for example)
23907c478bd9Sstevel@tonic-gate 	 * keeps a dupb() of the message lying around for retransmission.
23917c478bd9Sstevel@tonic-gate 	 * Since ESP changes the whole of the datagram, we have to create our
23927c478bd9Sstevel@tonic-gate 	 * own copy lest we clobber TCP's data.  Since we have to copy anyway,
23937c478bd9Sstevel@tonic-gate 	 * we might as well make use of msgpullup() and get the mblk into one
23947c478bd9Sstevel@tonic-gate 	 * contiguous piece!
23957c478bd9Sstevel@tonic-gate 	 */
2396bd670b35SErik Nordmark 	tailmp = msgpullup(data_mp, -1);
2397bd670b35SErik Nordmark 	if (tailmp == NULL) {
23987c478bd9Sstevel@tonic-gate 		esp0dbg(("esp_outbound: msgpullup() failed, "
23997c478bd9Sstevel@tonic-gate 		    "dropping packet.\n"));
2400bd670b35SErik Nordmark 		ip_drop_packet(data_mp, B_FALSE, ill,
2401f4b3ec61Sdh155122 		    DROPPER(ipss, ipds_esp_nomem),
2402f4b3ec61Sdh155122 		    &espstack->esp_dropper);
2403bd670b35SErik Nordmark 		BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
2404bd670b35SErik Nordmark 		return (NULL);
24057c478bd9Sstevel@tonic-gate 	}
2406bd670b35SErik Nordmark 	freemsg(data_mp);
2407bd670b35SErik Nordmark 	data_mp = tailmp;
24087c478bd9Sstevel@tonic-gate 
2409bd670b35SErik Nordmark 	assoc = ixa->ixa_ipsec_esp_sa;
24105d3b8cb7SBill Sommerfeld 	ASSERT(assoc != NULL);
24115d3b8cb7SBill Sommerfeld 
24125d3b8cb7SBill Sommerfeld 	/*
24135d3b8cb7SBill Sommerfeld 	 * Get the outer IP header in shape to escape this system..
24145d3b8cb7SBill Sommerfeld 	 */
2415bd670b35SErik Nordmark 	if (is_system_labeled() && (assoc->ipsa_otsl != NULL)) {
2416bd670b35SErik Nordmark 		/*
2417bd670b35SErik Nordmark 		 * Need to update packet with any CIPSO option and update
2418bd670b35SErik Nordmark 		 * ixa_tsl to capture the new label.
2419bd670b35SErik Nordmark 		 * We allocate a separate ixa for that purpose.
2420bd670b35SErik Nordmark 		 */
2421bd670b35SErik Nordmark 		ixa = ip_xmit_attr_duplicate(ixa);
2422bd670b35SErik Nordmark 		if (ixa == NULL) {
2423bd670b35SErik Nordmark 			ip_drop_packet(data_mp, B_FALSE, ill,
2424bd670b35SErik Nordmark 			    DROPPER(ipss, ipds_esp_nomem),
24255d3b8cb7SBill Sommerfeld 			    &espstack->esp_dropper);
2426bd670b35SErik Nordmark 			return (NULL);
24275d3b8cb7SBill Sommerfeld 		}
2428bd670b35SErik Nordmark 		need_refrele = B_TRUE;
24295d3b8cb7SBill Sommerfeld 
2430bd670b35SErik Nordmark 		label_hold(assoc->ipsa_otsl);
2431bd670b35SErik Nordmark 		ip_xmit_attr_replace_tsl(ixa, assoc->ipsa_otsl);
2432bd670b35SErik Nordmark 
2433bd670b35SErik Nordmark 		data_mp = sadb_whack_label(data_mp, assoc, ixa,
2434bd670b35SErik Nordmark 		    DROPPER(ipss, ipds_esp_nomem), &espstack->esp_dropper);
2435bd670b35SErik Nordmark 		if (data_mp == NULL) {
2436bd670b35SErik Nordmark 			/* Packet dropped by sadb_whack_label */
2437bd670b35SErik Nordmark 			ixa_refrele(ixa);
2438bd670b35SErik Nordmark 			return (NULL);
2439bd670b35SErik Nordmark 		}
2440bd670b35SErik Nordmark 	}
24415d3b8cb7SBill Sommerfeld 
24427c478bd9Sstevel@tonic-gate 	/*
24437c478bd9Sstevel@tonic-gate 	 * Reality check....
24447c478bd9Sstevel@tonic-gate 	 */
24457c478bd9Sstevel@tonic-gate 	ipha = (ipha_t *)data_mp->b_rptr;  /* So we can call esp_acquire(). */
2446*4132743aSToomas Soome 	ip6h = (ip6_t *)ipha;
24477c478bd9Sstevel@tonic-gate 
2448bd670b35SErik Nordmark 	if (ixa->ixa_flags & IXAF_IS_IPV4) {
2449bd670b35SErik Nordmark 		ASSERT(IPH_HDR_VERSION(ipha) == IPV4_VERSION);
2450bd670b35SErik Nordmark 
24517c478bd9Sstevel@tonic-gate 		af = AF_INET;
24527c478bd9Sstevel@tonic-gate 		divpoint = IPH_HDR_LENGTH(ipha);
24537c478bd9Sstevel@tonic-gate 		datalen = ntohs(ipha->ipha_length) - divpoint;
24547c478bd9Sstevel@tonic-gate 		nhp = (uint8_t *)&ipha->ipha_protocol;
24557c478bd9Sstevel@tonic-gate 	} else {
2456bd670b35SErik Nordmark 		ip_pkt_t ipp;
2457bd670b35SErik Nordmark 
2458bd670b35SErik Nordmark 		ASSERT(IPH_HDR_VERSION(ipha) == IPV6_VERSION);
24597c478bd9Sstevel@tonic-gate 
24607c478bd9Sstevel@tonic-gate 		af = AF_INET6;
24617c478bd9Sstevel@tonic-gate 		bzero(&ipp, sizeof (ipp));
2462bd670b35SErik Nordmark 		divpoint = ip_find_hdr_v6(data_mp, ip6h, B_FALSE, &ipp, NULL);
24637c478bd9Sstevel@tonic-gate 		if (ipp.ipp_dstopts != NULL &&
24647c478bd9Sstevel@tonic-gate 		    ipp.ipp_dstopts->ip6d_nxt != IPPROTO_ROUTING) {
24657c478bd9Sstevel@tonic-gate 			/*
24667c478bd9Sstevel@tonic-gate 			 * Destination options are tricky.  If we get in here,
24677c478bd9Sstevel@tonic-gate 			 * then we have a terminal header following the
24687c478bd9Sstevel@tonic-gate 			 * destination options.  We need to adjust backwards
24697c478bd9Sstevel@tonic-gate 			 * so we insert ESP BEFORE the destination options
24707c478bd9Sstevel@tonic-gate 			 * bag.  (So that the dstopts get encrypted!)
24717c478bd9Sstevel@tonic-gate 			 *
24727c478bd9Sstevel@tonic-gate 			 * Since this is for outbound packets only, we know
24737c478bd9Sstevel@tonic-gate 			 * that non-terminal destination options only precede
24747c478bd9Sstevel@tonic-gate 			 * routing headers.
24757c478bd9Sstevel@tonic-gate 			 */
24767c478bd9Sstevel@tonic-gate 			divpoint -= ipp.ipp_dstoptslen;
24777c478bd9Sstevel@tonic-gate 		}
24787c478bd9Sstevel@tonic-gate 		datalen = ntohs(ip6h->ip6_plen) + sizeof (ip6_t) - divpoint;
24797c478bd9Sstevel@tonic-gate 
24807c478bd9Sstevel@tonic-gate 		if (ipp.ipp_rthdr != NULL) {
24817c478bd9Sstevel@tonic-gate 			nhp = &ipp.ipp_rthdr->ip6r_nxt;
24827c478bd9Sstevel@tonic-gate 		} else if (ipp.ipp_hopopts != NULL) {
24837c478bd9Sstevel@tonic-gate 			nhp = &ipp.ipp_hopopts->ip6h_nxt;
24847c478bd9Sstevel@tonic-gate 		} else {
24857c478bd9Sstevel@tonic-gate 			ASSERT(divpoint == sizeof (ip6_t));
24867c478bd9Sstevel@tonic-gate 			/* It's probably IP + ESP. */
24877c478bd9Sstevel@tonic-gate 			nhp = &ip6h->ip6_nxt;
24887c478bd9Sstevel@tonic-gate 		}
24897c478bd9Sstevel@tonic-gate 	}
24907c478bd9Sstevel@tonic-gate 
24917c478bd9Sstevel@tonic-gate 	mac_len = assoc->ipsa_mac_len;
24927c478bd9Sstevel@tonic-gate 
24937c478bd9Sstevel@tonic-gate 	if (assoc->ipsa_flags & IPSA_F_NATT) {
24945d3b8cb7SBill Sommerfeld 		/* wedge in UDP header */
24957c478bd9Sstevel@tonic-gate 		is_natt = B_TRUE;
24967c478bd9Sstevel@tonic-gate 		esplen += UDPH_SIZE;
24977c478bd9Sstevel@tonic-gate 	}
24987c478bd9Sstevel@tonic-gate 
24997c478bd9Sstevel@tonic-gate 	/*
25007c478bd9Sstevel@tonic-gate 	 * Set up ESP header and encryption padding for ENCR PI request.
25017c478bd9Sstevel@tonic-gate 	 */
25027c478bd9Sstevel@tonic-gate 
250332350c00Sdanmcd 	/* Determine the padding length.  Pad to 4-bytes for no-encryption. */
25047c478bd9Sstevel@tonic-gate 	if (assoc->ipsa_encr_alg != SADB_EALG_NULL) {
250532350c00Sdanmcd 		iv_len = assoc->ipsa_iv_len;
2506628b0c67SMark Fenwick 		block_size = assoc->ipsa_datalen;
250732350c00Sdanmcd 
250832350c00Sdanmcd 		/*
2509628b0c67SMark Fenwick 		 * Pad the data to the length of the cipher block size.
251032350c00Sdanmcd 		 * Include the two additional bytes (hence the - 2) for the
251132350c00Sdanmcd 		 * padding length and the next header.  Take this into account
251232350c00Sdanmcd 		 * when calculating the actual length of the padding.
251332350c00Sdanmcd 		 */
251432350c00Sdanmcd 		ASSERT(ISP2(iv_len));
2515628b0c67SMark Fenwick 		padlen = ((unsigned)(block_size - datalen - 2)) &
2516628b0c67SMark Fenwick 		    (block_size - 1);
25177c478bd9Sstevel@tonic-gate 	} else {
251832350c00Sdanmcd 		iv_len = 0;
251932350c00Sdanmcd 		padlen = ((unsigned)(sizeof (uint32_t) - datalen - 2)) &
252032350c00Sdanmcd 		    (sizeof (uint32_t) - 1);
25217c478bd9Sstevel@tonic-gate 	}
25227c478bd9Sstevel@tonic-gate 
25237c478bd9Sstevel@tonic-gate 	/* Allocate ESP header and IV. */
25247c478bd9Sstevel@tonic-gate 	esplen += iv_len;
25257c478bd9Sstevel@tonic-gate 
25267c478bd9Sstevel@tonic-gate 	/*
25277c478bd9Sstevel@tonic-gate 	 * Update association byte-count lifetimes.  Don't forget to take
25287c478bd9Sstevel@tonic-gate 	 * into account the padding length and next-header (hence the + 2).
2529a86080f9Sdanmcd 	 *
25307c478bd9Sstevel@tonic-gate 	 * Use the amount of data fed into the "encryption algorithm".  This
25317c478bd9Sstevel@tonic-gate 	 * is the IV, the data length, the padding length, and the final two
25327c478bd9Sstevel@tonic-gate 	 * bytes (padlen, and next-header).
25337c478bd9Sstevel@tonic-gate 	 *
25347c478bd9Sstevel@tonic-gate 	 */
25357c478bd9Sstevel@tonic-gate 
2536a86080f9Sdanmcd 	if (!esp_age_bytes(assoc, datalen + padlen + iv_len + 2, B_FALSE)) {
2537bd670b35SErik Nordmark 		ip_drop_packet(data_mp, B_FALSE, ill,
2538f4b3ec61Sdh155122 		    DROPPER(ipss, ipds_esp_bytes_expire),
2539f4b3ec61Sdh155122 		    &espstack->esp_dropper);
2540bd670b35SErik Nordmark 		BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
2541bd670b35SErik Nordmark 		if (need_refrele)
2542bd670b35SErik Nordmark 			ixa_refrele(ixa);
2543bd670b35SErik Nordmark 		return (NULL);
25447c478bd9Sstevel@tonic-gate 	}
25457c478bd9Sstevel@tonic-gate 
25467c478bd9Sstevel@tonic-gate 	espmp = allocb(esplen, BPRI_HI);
25477c478bd9Sstevel@tonic-gate 	if (espmp == NULL) {
2548f4b3ec61Sdh155122 		ESP_BUMP_STAT(espstack, out_discards);
2549f4b3ec61Sdh155122 		esp1dbg(espstack, ("esp_outbound: can't allocate espmp.\n"));
2550bd670b35SErik Nordmark 		ip_drop_packet(data_mp, B_FALSE, ill,
2551f4b3ec61Sdh155122 		    DROPPER(ipss, ipds_esp_nomem),
2552f4b3ec61Sdh155122 		    &espstack->esp_dropper);
2553bd670b35SErik Nordmark 		BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
2554bd670b35SErik Nordmark 		if (need_refrele)
2555bd670b35SErik Nordmark 			ixa_refrele(ixa);
2556bd670b35SErik Nordmark 		return (NULL);
25577c478bd9Sstevel@tonic-gate 	}
25587c478bd9Sstevel@tonic-gate 	espmp->b_wptr += esplen;
2559628b0c67SMark Fenwick 	esph_ptr = (esph_t *)espmp->b_rptr;
25607c478bd9Sstevel@tonic-gate 
25617c478bd9Sstevel@tonic-gate 	if (is_natt) {
2562f4b3ec61Sdh155122 		esp3dbg(espstack, ("esp_outbound: NATT"));
25637c478bd9Sstevel@tonic-gate 
25647c478bd9Sstevel@tonic-gate 		udpha = (udpha_t *)espmp->b_rptr;
2565437220cdSdanmcd 		udpha->uha_src_port = (assoc->ipsa_local_nat_port != 0) ?
2566437220cdSdanmcd 		    assoc->ipsa_local_nat_port : htons(IPPORT_IKE_NATT);
2567437220cdSdanmcd 		udpha->uha_dst_port = (assoc->ipsa_remote_nat_port != 0) ?
2568437220cdSdanmcd 		    assoc->ipsa_remote_nat_port : htons(IPPORT_IKE_NATT);
25697c478bd9Sstevel@tonic-gate 		/*
2570437220cdSdanmcd 		 * Set the checksum to 0, so that the esp_prepare_udp() call
25717c478bd9Sstevel@tonic-gate 		 * can do the right thing.
25727c478bd9Sstevel@tonic-gate 		 */
25737c478bd9Sstevel@tonic-gate 		udpha->uha_checksum = 0;
2574628b0c67SMark Fenwick 		esph_ptr = (esph_t *)(udpha + 1);
25757c478bd9Sstevel@tonic-gate 	}
25767c478bd9Sstevel@tonic-gate 
2577628b0c67SMark Fenwick 	esph_ptr->esph_spi = assoc->ipsa_spi;
25787c478bd9Sstevel@tonic-gate 
25791a5e258fSJosef 'Jeff' Sipek 	esph_ptr->esph_replay = htonl(atomic_inc_32_nv(&assoc->ipsa_replay));
2580628b0c67SMark Fenwick 	if (esph_ptr->esph_replay == 0 && assoc->ipsa_replay_wsize != 0) {
25817c478bd9Sstevel@tonic-gate 		/*
25827c478bd9Sstevel@tonic-gate 		 * XXX We have replay counter wrapping.
25837c478bd9Sstevel@tonic-gate 		 * We probably want to nuke this SA (and its peer).
25847c478bd9Sstevel@tonic-gate 		 */
25857c478bd9Sstevel@tonic-gate 		ipsec_assocfailure(info.mi_idnum, 0, 0,
25867c478bd9Sstevel@tonic-gate 		    SL_ERROR | SL_CONSOLE | SL_WARN,
25877c478bd9Sstevel@tonic-gate 		    "Outbound ESP SA (0x%x, %s) has wrapped sequence.\n",
2588628b0c67SMark Fenwick 		    esph_ptr->esph_spi, assoc->ipsa_dstaddr, af,
2589f4b3ec61Sdh155122 		    espstack->ipsecesp_netstack);
25907c478bd9Sstevel@tonic-gate 
2591f4b3ec61Sdh155122 		ESP_BUMP_STAT(espstack, out_discards);
25927c478bd9Sstevel@tonic-gate 		sadb_replay_delete(assoc);
2593bd670b35SErik Nordmark 		ip_drop_packet(data_mp, B_FALSE, ill,
2594f4b3ec61Sdh155122 		    DROPPER(ipss, ipds_esp_replay),
2595f4b3ec61Sdh155122 		    &espstack->esp_dropper);
2596bd670b35SErik Nordmark 		BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
2597bd670b35SErik Nordmark 		if (need_refrele)
2598bd670b35SErik Nordmark 			ixa_refrele(ixa);
2599bd670b35SErik Nordmark 		return (NULL);
26007c478bd9Sstevel@tonic-gate 	}
26017c478bd9Sstevel@tonic-gate 
2602628b0c67SMark Fenwick 	iv_ptr = (esph_ptr + 1);
26037c478bd9Sstevel@tonic-gate 	/*
2604628b0c67SMark Fenwick 	 * iv_ptr points to the mblk which will contain the IV once we have
2605628b0c67SMark Fenwick 	 * written it there. This mblk will be part of a mblk chain that
2606628b0c67SMark Fenwick 	 * will make up the packet.
2607628b0c67SMark Fenwick 	 *
2608628b0c67SMark Fenwick 	 * For counter mode algorithms, the IV is a 64 bit quantity, it
2609628b0c67SMark Fenwick 	 * must NEVER repeat in the lifetime of the SA, otherwise an
2610628b0c67SMark Fenwick 	 * attacker who had recorded enough packets might be able to
2611628b0c67SMark Fenwick 	 * determine some clear text.
2612628b0c67SMark Fenwick 	 *
2613628b0c67SMark Fenwick 	 * To ensure this does not happen, the IV is stored in the SA and
2614628b0c67SMark Fenwick 	 * incremented for each packet, the IV is then copied into the
2615628b0c67SMark Fenwick 	 * "packet" for transmission to the receiving system. The IV will
2616628b0c67SMark Fenwick 	 * also be copied into the nonce, when the packet is encrypted.
2617628b0c67SMark Fenwick 	 *
2618628b0c67SMark Fenwick 	 * CBC mode algorithms use a random IV for each packet. We do not
2619628b0c67SMark Fenwick 	 * require the highest quality random bits, but for best security
2620628b0c67SMark Fenwick 	 * with CBC mode ciphers, the value must be unlikely to repeat and
2621628b0c67SMark Fenwick 	 * must not be known in advance to an adversary capable of influencing
2622628b0c67SMark Fenwick 	 * the clear text.
26237c478bd9Sstevel@tonic-gate 	 */
2624628b0c67SMark Fenwick 	if (!update_iv((uint8_t *)iv_ptr, espstack->esp_pfkey_q, assoc,
2625628b0c67SMark Fenwick 	    espstack)) {
2626bd670b35SErik Nordmark 		ip_drop_packet(data_mp, B_FALSE, ill,
2627628b0c67SMark Fenwick 		    DROPPER(ipss, ipds_esp_iv_wrap), &espstack->esp_dropper);
2628bd670b35SErik Nordmark 		if (need_refrele)
2629bd670b35SErik Nordmark 			ixa_refrele(ixa);
2630bd670b35SErik Nordmark 		return (NULL);
2631628b0c67SMark Fenwick 	}
26327c478bd9Sstevel@tonic-gate 
26337c478bd9Sstevel@tonic-gate 	/* Fix the IP header. */
26347c478bd9Sstevel@tonic-gate 	alloclen = padlen + 2 + mac_len;
26357c478bd9Sstevel@tonic-gate 	adj = alloclen + (espmp->b_wptr - espmp->b_rptr);
26367c478bd9Sstevel@tonic-gate 
26377c478bd9Sstevel@tonic-gate 	protocol = *nhp;
26387c478bd9Sstevel@tonic-gate 
2639bd670b35SErik Nordmark 	if (ixa->ixa_flags & IXAF_IS_IPV4) {
26407c478bd9Sstevel@tonic-gate 		ipha->ipha_length = htons(ntohs(ipha->ipha_length) + adj);
26417c478bd9Sstevel@tonic-gate 		if (is_natt) {
26427c478bd9Sstevel@tonic-gate 			*nhp = IPPROTO_UDP;
26437c478bd9Sstevel@tonic-gate 			udpha->uha_length = htons(ntohs(ipha->ipha_length) -
26447c478bd9Sstevel@tonic-gate 			    IPH_HDR_LENGTH(ipha));
26457c478bd9Sstevel@tonic-gate 		} else {
26467c478bd9Sstevel@tonic-gate 			*nhp = IPPROTO_ESP;
26477c478bd9Sstevel@tonic-gate 		}
26487c478bd9Sstevel@tonic-gate 		ipha->ipha_hdr_checksum = 0;
26497c478bd9Sstevel@tonic-gate 		ipha->ipha_hdr_checksum = (uint16_t)ip_csum_hdr(ipha);
26507c478bd9Sstevel@tonic-gate 	} else {
26517c478bd9Sstevel@tonic-gate 		ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) + adj);
26527c478bd9Sstevel@tonic-gate 		*nhp = IPPROTO_ESP;
26537c478bd9Sstevel@tonic-gate 	}
26547c478bd9Sstevel@tonic-gate 
26557c478bd9Sstevel@tonic-gate 	/* I've got the two ESP mblks, now insert them. */
26567c478bd9Sstevel@tonic-gate 
2657f4b3ec61Sdh155122 	esp2dbg(espstack, ("data_mp before outbound ESP adjustment:\n"));
2658f4b3ec61Sdh155122 	esp2dbg(espstack, (dump_msg(data_mp)));
26597c478bd9Sstevel@tonic-gate 
2660f4b3ec61Sdh155122 	if (!esp_insert_esp(data_mp, espmp, divpoint, espstack)) {
2661f4b3ec61Sdh155122 		ESP_BUMP_STAT(espstack, out_discards);
26627c478bd9Sstevel@tonic-gate 		/* NOTE:  esp_insert_esp() only fails if there's no memory. */
2663bd670b35SErik Nordmark 		ip_drop_packet(data_mp, B_FALSE, ill,
2664f4b3ec61Sdh155122 		    DROPPER(ipss, ipds_esp_nomem),
2665f4b3ec61Sdh155122 		    &espstack->esp_dropper);
26667c478bd9Sstevel@tonic-gate 		freeb(espmp);
2667bd670b35SErik Nordmark 		BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
2668bd670b35SErik Nordmark 		if (need_refrele)
2669bd670b35SErik Nordmark 			ixa_refrele(ixa);
2670bd670b35SErik Nordmark 		return (NULL);
26717c478bd9Sstevel@tonic-gate 	}
26727c478bd9Sstevel@tonic-gate 
26737c478bd9Sstevel@tonic-gate 	/* Append padding (and leave room for ICV). */
26747c478bd9Sstevel@tonic-gate 	for (tailmp = data_mp; tailmp->b_cont != NULL; tailmp = tailmp->b_cont)
26757c478bd9Sstevel@tonic-gate 		;
26767c478bd9Sstevel@tonic-gate 	if (tailmp->b_wptr + alloclen > tailmp->b_datap->db_lim) {
26777c478bd9Sstevel@tonic-gate 		tailmp->b_cont = allocb(alloclen, BPRI_HI);
26787c478bd9Sstevel@tonic-gate 		if (tailmp->b_cont == NULL) {
2679f4b3ec61Sdh155122 			ESP_BUMP_STAT(espstack, out_discards);
26807c478bd9Sstevel@tonic-gate 			esp0dbg(("esp_outbound:  Can't allocate tailmp.\n"));
2681bd670b35SErik Nordmark 			ip_drop_packet(data_mp, B_FALSE, ill,
2682f4b3ec61Sdh155122 			    DROPPER(ipss, ipds_esp_nomem),
2683f4b3ec61Sdh155122 			    &espstack->esp_dropper);
2684bd670b35SErik Nordmark 			BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
2685bd670b35SErik Nordmark 			if (need_refrele)
2686bd670b35SErik Nordmark 				ixa_refrele(ixa);
2687bd670b35SErik Nordmark 			return (NULL);
26887c478bd9Sstevel@tonic-gate 		}
26897c478bd9Sstevel@tonic-gate 		tailmp = tailmp->b_cont;
26907c478bd9Sstevel@tonic-gate 	}
26917c478bd9Sstevel@tonic-gate 
26927c478bd9Sstevel@tonic-gate 	/*
26937c478bd9Sstevel@tonic-gate 	 * If there's padding, N bytes of padding must be of the form 0x1,
26947c478bd9Sstevel@tonic-gate 	 * 0x2, 0x3... 0xN.
26957c478bd9Sstevel@tonic-gate 	 */
26967c478bd9Sstevel@tonic-gate 	for (i = 0; i < padlen; ) {
26977c478bd9Sstevel@tonic-gate 		i++;
26987c478bd9Sstevel@tonic-gate 		*tailmp->b_wptr++ = i;
26997c478bd9Sstevel@tonic-gate 	}
27007c478bd9Sstevel@tonic-gate 	*tailmp->b_wptr++ = i;
27017c478bd9Sstevel@tonic-gate 	*tailmp->b_wptr++ = protocol;
27027c478bd9Sstevel@tonic-gate 
2703f4b3ec61Sdh155122 	esp2dbg(espstack, ("data_Mp before encryption:\n"));
2704f4b3ec61Sdh155122 	esp2dbg(espstack, (dump_msg(data_mp)));
27057c478bd9Sstevel@tonic-gate 
27067c478bd9Sstevel@tonic-gate 	/*
27077c478bd9Sstevel@tonic-gate 	 * Okay.  I've set up the pre-encryption ESP.  Let's do it!
27087c478bd9Sstevel@tonic-gate 	 */
27097c478bd9Sstevel@tonic-gate 
27107c478bd9Sstevel@tonic-gate 	if (mac_len > 0) {
27117c478bd9Sstevel@tonic-gate 		ASSERT(tailmp->b_wptr + mac_len <= tailmp->b_datap->db_lim);
27127c478bd9Sstevel@tonic-gate 		icv_buf = tailmp->b_wptr;
27137c478bd9Sstevel@tonic-gate 		tailmp->b_wptr += mac_len;
27147c478bd9Sstevel@tonic-gate 	} else {
27157c478bd9Sstevel@tonic-gate 		icv_buf = NULL;
27167c478bd9Sstevel@tonic-gate 	}
27177c478bd9Sstevel@tonic-gate 
2718bd670b35SErik Nordmark 	data_mp = esp_submit_req_outbound(data_mp, ixa, assoc, icv_buf,
2719bd670b35SErik Nordmark 	    datalen + padlen + 2);
2720bd670b35SErik Nordmark 	if (need_refrele)
2721bd670b35SErik Nordmark 		ixa_refrele(ixa);
2722bd670b35SErik Nordmark 	return (data_mp);
27237c478bd9Sstevel@tonic-gate }
27247c478bd9Sstevel@tonic-gate 
27257c478bd9Sstevel@tonic-gate /*
27267c478bd9Sstevel@tonic-gate  * IP calls this to validate the ICMP errors that
27277c478bd9Sstevel@tonic-gate  * we got from the network.
27287c478bd9Sstevel@tonic-gate  */
2729bd670b35SErik Nordmark mblk_t *
ipsecesp_icmp_error(mblk_t * data_mp,ip_recv_attr_t * ira)2730bd670b35SErik Nordmark ipsecesp_icmp_error(mblk_t *data_mp, ip_recv_attr_t *ira)
27317c478bd9Sstevel@tonic-gate {
2732bd670b35SErik Nordmark 	netstack_t	*ns = ira->ira_ill->ill_ipst->ips_netstack;
2733bd670b35SErik Nordmark 	ipsecesp_stack_t *espstack = ns->netstack_ipsecesp;
2734bd670b35SErik Nordmark 	ipsec_stack_t	*ipss = ns->netstack_ipsec;
2735f4b3ec61Sdh155122 
27367c478bd9Sstevel@tonic-gate 	/*
27377c478bd9Sstevel@tonic-gate 	 * Unless we get an entire packet back, this function is useless.
27387c478bd9Sstevel@tonic-gate 	 * Why?
27397c478bd9Sstevel@tonic-gate 	 *
27407c478bd9Sstevel@tonic-gate 	 * 1.)	Partial packets are useless, because the "next header"
27417c478bd9Sstevel@tonic-gate 	 *	is at the end of the decrypted ESP packet.  Without the
27427c478bd9Sstevel@tonic-gate 	 *	whole packet, this is useless.
27437c478bd9Sstevel@tonic-gate 	 *
27447c478bd9Sstevel@tonic-gate 	 * 2.)	If we every use a stateful cipher, such as a stream or a
27457c478bd9Sstevel@tonic-gate 	 *	one-time pad, we can't do anything.
27467c478bd9Sstevel@tonic-gate 	 *
27477c478bd9Sstevel@tonic-gate 	 * Since the chances of us getting an entire packet back are very
27487c478bd9Sstevel@tonic-gate 	 * very small, we discard here.
27497c478bd9Sstevel@tonic-gate 	 */
2750f4b3ec61Sdh155122 	IP_ESP_BUMP_STAT(ipss, in_discards);
2751bd670b35SErik Nordmark 	ip_drop_packet(data_mp, B_TRUE, ira->ira_ill,
2752f4b3ec61Sdh155122 	    DROPPER(ipss, ipds_esp_icmp),
2753f4b3ec61Sdh155122 	    &espstack->esp_dropper);
2754bd670b35SErik Nordmark 	return (NULL);
27557c478bd9Sstevel@tonic-gate }
27567c478bd9Sstevel@tonic-gate 
27577c478bd9Sstevel@tonic-gate /*
27587c478bd9Sstevel@tonic-gate  * Construct an SADB_REGISTER message with the current algorithms.
2759628b0c67SMark Fenwick  * This function gets called when 'ipsecalgs -s' is run or when
2760628b0c67SMark Fenwick  * in.iked (or other KMD) starts.
27617c478bd9Sstevel@tonic-gate  */
27627c478bd9Sstevel@tonic-gate static boolean_t
esp_register_out(uint32_t sequence,uint32_t pid,uint_t serial,ipsecesp_stack_t * espstack,cred_t * cr)2763f4b3ec61Sdh155122 esp_register_out(uint32_t sequence, uint32_t pid, uint_t serial,
2764bd670b35SErik Nordmark     ipsecesp_stack_t *espstack, cred_t *cr)
27657c478bd9Sstevel@tonic-gate {
27667c478bd9Sstevel@tonic-gate 	mblk_t *pfkey_msg_mp, *keysock_out_mp;
27677c478bd9Sstevel@tonic-gate 	sadb_msg_t *samsg;
27687c478bd9Sstevel@tonic-gate 	sadb_supported_t *sasupp_auth = NULL;
27697c478bd9Sstevel@tonic-gate 	sadb_supported_t *sasupp_encr = NULL;
27707c478bd9Sstevel@tonic-gate 	sadb_alg_t *saalg;
27717c478bd9Sstevel@tonic-gate 	uint_t allocsize = sizeof (*samsg);
27727c478bd9Sstevel@tonic-gate 	uint_t i, numalgs_snap;
27737c478bd9Sstevel@tonic-gate 	int current_aalgs;
27747c478bd9Sstevel@tonic-gate 	ipsec_alginfo_t **authalgs;
27757c478bd9Sstevel@tonic-gate 	uint_t num_aalgs;
27767c478bd9Sstevel@tonic-gate 	int current_ealgs;
27777c478bd9Sstevel@tonic-gate 	ipsec_alginfo_t **encralgs;
27787c478bd9Sstevel@tonic-gate 	uint_t num_ealgs;
2779f4b3ec61Sdh155122 	ipsec_stack_t	*ipss = espstack->ipsecesp_netstack->netstack_ipsec;
27805d3b8cb7SBill Sommerfeld 	sadb_sens_t *sens;
27815d3b8cb7SBill Sommerfeld 	size_t sens_len = 0;
27825d3b8cb7SBill Sommerfeld 	sadb_ext_t *nextext;
2783bd670b35SErik Nordmark 	ts_label_t *sens_tsl = NULL;
27847c478bd9Sstevel@tonic-gate 
27857c478bd9Sstevel@tonic-gate 	/* Allocate the KEYSOCK_OUT. */
27867c478bd9Sstevel@tonic-gate 	keysock_out_mp = sadb_keysock_out(serial);
27877c478bd9Sstevel@tonic-gate 	if (keysock_out_mp == NULL) {
27887c478bd9Sstevel@tonic-gate 		esp0dbg(("esp_register_out: couldn't allocate mblk.\n"));
27897c478bd9Sstevel@tonic-gate 		return (B_FALSE);
27907c478bd9Sstevel@tonic-gate 	}
27917c478bd9Sstevel@tonic-gate 
2792bd670b35SErik Nordmark 	if (is_system_labeled() && (cr != NULL)) {
2793bd670b35SErik Nordmark 		sens_tsl = crgetlabel(cr);
2794bd670b35SErik Nordmark 		if (sens_tsl != NULL) {
2795bd670b35SErik Nordmark 			sens_len = sadb_sens_len_from_label(sens_tsl);
27965d3b8cb7SBill Sommerfeld 			allocsize += sens_len;
27975d3b8cb7SBill Sommerfeld 		}
27985d3b8cb7SBill Sommerfeld 	}
27995d3b8cb7SBill Sommerfeld 
28007c478bd9Sstevel@tonic-gate 	/*
28017c478bd9Sstevel@tonic-gate 	 * Allocate the PF_KEY message that follows KEYSOCK_OUT.
28027c478bd9Sstevel@tonic-gate 	 */
28037c478bd9Sstevel@tonic-gate 
280469e71331SBayard Bell 	rw_enter(&ipss->ipsec_alg_lock, RW_READER);
28057c478bd9Sstevel@tonic-gate 	/*
28067c478bd9Sstevel@tonic-gate 	 * Fill SADB_REGISTER message's algorithm descriptors.  Hold
28077c478bd9Sstevel@tonic-gate 	 * down the lock while filling it.
28087c478bd9Sstevel@tonic-gate 	 *
28097c478bd9Sstevel@tonic-gate 	 * Return only valid algorithms, so the number of algorithms
28107c478bd9Sstevel@tonic-gate 	 * to send up may be less than the number of algorithm entries
28117c478bd9Sstevel@tonic-gate 	 * in the table.
28127c478bd9Sstevel@tonic-gate 	 */
2813f4b3ec61Sdh155122 	authalgs = ipss->ipsec_alglists[IPSEC_ALG_AUTH];
28147c478bd9Sstevel@tonic-gate 	for (num_aalgs = 0, i = 0; i < IPSEC_MAX_ALGS; i++)
28157c478bd9Sstevel@tonic-gate 		if (authalgs[i] != NULL && ALG_VALID(authalgs[i]))
28167c478bd9Sstevel@tonic-gate 			num_aalgs++;
28177c478bd9Sstevel@tonic-gate 
28187c478bd9Sstevel@tonic-gate 	if (num_aalgs != 0) {
28197c478bd9Sstevel@tonic-gate 		allocsize += (num_aalgs * sizeof (*saalg));
28207c478bd9Sstevel@tonic-gate 		allocsize += sizeof (*sasupp_auth);
28217c478bd9Sstevel@tonic-gate 	}
2822f4b3ec61Sdh155122 	encralgs = ipss->ipsec_alglists[IPSEC_ALG_ENCR];
28237c478bd9Sstevel@tonic-gate 	for (num_ealgs = 0, i = 0; i < IPSEC_MAX_ALGS; i++)
28247c478bd9Sstevel@tonic-gate 		if (encralgs[i] != NULL && ALG_VALID(encralgs[i]))
28257c478bd9Sstevel@tonic-gate 			num_ealgs++;
28267c478bd9Sstevel@tonic-gate 
28277c478bd9Sstevel@tonic-gate 	if (num_ealgs != 0) {
28287c478bd9Sstevel@tonic-gate 		allocsize += (num_ealgs * sizeof (*saalg));
28297c478bd9Sstevel@tonic-gate 		allocsize += sizeof (*sasupp_encr);
28307c478bd9Sstevel@tonic-gate 	}
28317c478bd9Sstevel@tonic-gate 	keysock_out_mp->b_cont = allocb(allocsize, BPRI_HI);
28327c478bd9Sstevel@tonic-gate 	if (keysock_out_mp->b_cont == NULL) {
283369e71331SBayard Bell 		rw_exit(&ipss->ipsec_alg_lock);
28347c478bd9Sstevel@tonic-gate 		freemsg(keysock_out_mp);
28357c478bd9Sstevel@tonic-gate 		return (B_FALSE);
28367c478bd9Sstevel@tonic-gate 	}
28377c478bd9Sstevel@tonic-gate 	pfkey_msg_mp = keysock_out_mp->b_cont;
28387c478bd9Sstevel@tonic-gate 	pfkey_msg_mp->b_wptr += allocsize;
28395d3b8cb7SBill Sommerfeld 
28405d3b8cb7SBill Sommerfeld 	nextext = (sadb_ext_t *)(pfkey_msg_mp->b_rptr + sizeof (*samsg));
28415d3b8cb7SBill Sommerfeld 
28427c478bd9Sstevel@tonic-gate 	if (num_aalgs != 0) {
28435d3b8cb7SBill Sommerfeld 		sasupp_auth = (sadb_supported_t *)nextext;
28447c478bd9Sstevel@tonic-gate 		saalg = (sadb_alg_t *)(sasupp_auth + 1);
28457c478bd9Sstevel@tonic-gate 
28467c478bd9Sstevel@tonic-gate 		ASSERT(((ulong_t)saalg & 0x7) == 0);
28477c478bd9Sstevel@tonic-gate 
28487c478bd9Sstevel@tonic-gate 		numalgs_snap = 0;
28497c478bd9Sstevel@tonic-gate 		for (i = 0;
2850f4b3ec61Sdh155122 		    ((i < IPSEC_MAX_ALGS) && (numalgs_snap < num_aalgs));
2851f4b3ec61Sdh155122 		    i++) {
28527c478bd9Sstevel@tonic-gate 			if (authalgs[i] == NULL || !ALG_VALID(authalgs[i]))
28537c478bd9Sstevel@tonic-gate 				continue;
28547c478bd9Sstevel@tonic-gate 
28557c478bd9Sstevel@tonic-gate 			saalg->sadb_alg_id = authalgs[i]->alg_id;
28567c478bd9Sstevel@tonic-gate 			saalg->sadb_alg_ivlen = 0;
28577c478bd9Sstevel@tonic-gate 			saalg->sadb_alg_minbits	= authalgs[i]->alg_ef_minbits;
28587c478bd9Sstevel@tonic-gate 			saalg->sadb_alg_maxbits	= authalgs[i]->alg_ef_maxbits;
28597c478bd9Sstevel@tonic-gate 			saalg->sadb_x_alg_increment =
28607c478bd9Sstevel@tonic-gate 			    authalgs[i]->alg_increment;
2861628b0c67SMark Fenwick 			saalg->sadb_x_alg_saltbits = SADB_8TO1(
2862628b0c67SMark Fenwick 			    authalgs[i]->alg_saltlen);
28637c478bd9Sstevel@tonic-gate 			numalgs_snap++;
28647c478bd9Sstevel@tonic-gate 			saalg++;
28657c478bd9Sstevel@tonic-gate 		}
28667c478bd9Sstevel@tonic-gate 		ASSERT(numalgs_snap == num_aalgs);
28677c478bd9Sstevel@tonic-gate #ifdef DEBUG
28687c478bd9Sstevel@tonic-gate 		/*
28697c478bd9Sstevel@tonic-gate 		 * Reality check to make sure I snagged all of the
28707c478bd9Sstevel@tonic-gate 		 * algorithms.
28717c478bd9Sstevel@tonic-gate 		 */
28727c478bd9Sstevel@tonic-gate 		for (; i < IPSEC_MAX_ALGS; i++) {
28737c478bd9Sstevel@tonic-gate 			if (authalgs[i] != NULL && ALG_VALID(authalgs[i])) {
28747c478bd9Sstevel@tonic-gate 				cmn_err(CE_PANIC, "esp_register_out()! "
28757c478bd9Sstevel@tonic-gate 				    "Missed aalg #%d.\n", i);
28767c478bd9Sstevel@tonic-gate 			}
28777c478bd9Sstevel@tonic-gate 		}
28787c478bd9Sstevel@tonic-gate #endif /* DEBUG */
28795d3b8cb7SBill Sommerfeld 		nextext = (sadb_ext_t *)saalg;
28807c478bd9Sstevel@tonic-gate 	}
28817c478bd9Sstevel@tonic-gate 
28827c478bd9Sstevel@tonic-gate 	if (num_ealgs != 0) {
28835d3b8cb7SBill Sommerfeld 		sasupp_encr = (sadb_supported_t *)nextext;
28847c478bd9Sstevel@tonic-gate 		saalg = (sadb_alg_t *)(sasupp_encr + 1);
28857c478bd9Sstevel@tonic-gate 
28867c478bd9Sstevel@tonic-gate 		numalgs_snap = 0;
28877c478bd9Sstevel@tonic-gate 		for (i = 0;
28887c478bd9Sstevel@tonic-gate 		    ((i < IPSEC_MAX_ALGS) && (numalgs_snap < num_ealgs)); i++) {
28897c478bd9Sstevel@tonic-gate 			if (encralgs[i] == NULL || !ALG_VALID(encralgs[i]))
28907c478bd9Sstevel@tonic-gate 				continue;
28917c478bd9Sstevel@tonic-gate 			saalg->sadb_alg_id = encralgs[i]->alg_id;
2892628b0c67SMark Fenwick 			saalg->sadb_alg_ivlen = encralgs[i]->alg_ivlen;
28937c478bd9Sstevel@tonic-gate 			saalg->sadb_alg_minbits	= encralgs[i]->alg_ef_minbits;
28947c478bd9Sstevel@tonic-gate 			saalg->sadb_alg_maxbits	= encralgs[i]->alg_ef_maxbits;
2895628b0c67SMark Fenwick 			/*
2896628b0c67SMark Fenwick 			 * We could advertise the ICV length, except there
2897628b0c67SMark Fenwick 			 * is not a value in sadb_x_algb to do this.
2898628b0c67SMark Fenwick 			 * saalg->sadb_alg_maclen = encralgs[i]->alg_maclen;
2899628b0c67SMark Fenwick 			 */
29007c478bd9Sstevel@tonic-gate 			saalg->sadb_x_alg_increment =
29017c478bd9Sstevel@tonic-gate 			    encralgs[i]->alg_increment;
2902628b0c67SMark Fenwick 			saalg->sadb_x_alg_saltbits =
2903628b0c67SMark Fenwick 			    SADB_8TO1(encralgs[i]->alg_saltlen);
2904628b0c67SMark Fenwick 
29057c478bd9Sstevel@tonic-gate 			numalgs_snap++;
29067c478bd9Sstevel@tonic-gate 			saalg++;
29077c478bd9Sstevel@tonic-gate 		}
29087c478bd9Sstevel@tonic-gate 		ASSERT(numalgs_snap == num_ealgs);
29097c478bd9Sstevel@tonic-gate #ifdef DEBUG
29107c478bd9Sstevel@tonic-gate 		/*
29117c478bd9Sstevel@tonic-gate 		 * Reality check to make sure I snagged all of the
29127c478bd9Sstevel@tonic-gate 		 * algorithms.
29137c478bd9Sstevel@tonic-gate 		 */
29147c478bd9Sstevel@tonic-gate 		for (; i < IPSEC_MAX_ALGS; i++) {
29157c478bd9Sstevel@tonic-gate 			if (encralgs[i] != NULL && ALG_VALID(encralgs[i])) {
29167c478bd9Sstevel@tonic-gate 				cmn_err(CE_PANIC, "esp_register_out()! "
29177c478bd9Sstevel@tonic-gate 				    "Missed ealg #%d.\n", i);
29187c478bd9Sstevel@tonic-gate 			}
29197c478bd9Sstevel@tonic-gate 		}
29207c478bd9Sstevel@tonic-gate #endif /* DEBUG */
29215d3b8cb7SBill Sommerfeld 		nextext = (sadb_ext_t *)saalg;
29227c478bd9Sstevel@tonic-gate 	}
29237c478bd9Sstevel@tonic-gate 
29247c478bd9Sstevel@tonic-gate 	current_aalgs = num_aalgs;
29257c478bd9Sstevel@tonic-gate 	current_ealgs = num_ealgs;
29267c478bd9Sstevel@tonic-gate 
292769e71331SBayard Bell 	rw_exit(&ipss->ipsec_alg_lock);
29287c478bd9Sstevel@tonic-gate 
2929bd670b35SErik Nordmark 	if (sens_tsl != NULL) {
29305d3b8cb7SBill Sommerfeld 		sens = (sadb_sens_t *)nextext;
2931bd670b35SErik Nordmark 		sadb_sens_from_label(sens, SADB_EXT_SENSITIVITY,
2932bd670b35SErik Nordmark 		    sens_tsl, sens_len);
29335d3b8cb7SBill Sommerfeld 
29345d3b8cb7SBill Sommerfeld 		nextext = (sadb_ext_t *)(((uint8_t *)sens) + sens_len);
29355d3b8cb7SBill Sommerfeld 	}
29365d3b8cb7SBill Sommerfeld 
29377c478bd9Sstevel@tonic-gate 	/* Now fill the rest of the SADB_REGISTER message. */
29387c478bd9Sstevel@tonic-gate 
29397c478bd9Sstevel@tonic-gate 	samsg = (sadb_msg_t *)pfkey_msg_mp->b_rptr;
29407c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_version = PF_KEY_V2;
29417c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_type = SADB_REGISTER;
29427c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_errno = 0;
29437c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_satype = SADB_SATYPE_ESP;
29447c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_len = SADB_8TO64(allocsize);
29457c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_reserved = 0;
29467c478bd9Sstevel@tonic-gate 	/*
29477c478bd9Sstevel@tonic-gate 	 * Assume caller has sufficient sequence/pid number info.  If it's one
29487c478bd9Sstevel@tonic-gate 	 * from me over a new alg., I could give two hoots about sequence.
29497c478bd9Sstevel@tonic-gate 	 */
29507c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_seq = sequence;
29517c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_pid = pid;
29527c478bd9Sstevel@tonic-gate 
29537c478bd9Sstevel@tonic-gate 	if (sasupp_auth != NULL) {
2954437220cdSdanmcd 		sasupp_auth->sadb_supported_len = SADB_8TO64(
2955437220cdSdanmcd 		    sizeof (*sasupp_auth) + sizeof (*saalg) * current_aalgs);
29567c478bd9Sstevel@tonic-gate 		sasupp_auth->sadb_supported_exttype = SADB_EXT_SUPPORTED_AUTH;
29577c478bd9Sstevel@tonic-gate 		sasupp_auth->sadb_supported_reserved = 0;
29587c478bd9Sstevel@tonic-gate 	}
29597c478bd9Sstevel@tonic-gate 
29607c478bd9Sstevel@tonic-gate 	if (sasupp_encr != NULL) {
2961437220cdSdanmcd 		sasupp_encr->sadb_supported_len = SADB_8TO64(
2962437220cdSdanmcd 		    sizeof (*sasupp_encr) + sizeof (*saalg) * current_ealgs);
29637c478bd9Sstevel@tonic-gate 		sasupp_encr->sadb_supported_exttype =
29647c478bd9Sstevel@tonic-gate 		    SADB_EXT_SUPPORTED_ENCRYPT;
29657c478bd9Sstevel@tonic-gate 		sasupp_encr->sadb_supported_reserved = 0;
29667c478bd9Sstevel@tonic-gate 	}
29677c478bd9Sstevel@tonic-gate 
2968f4b3ec61Sdh155122 	if (espstack->esp_pfkey_q != NULL)
2969f4b3ec61Sdh155122 		putnext(espstack->esp_pfkey_q, keysock_out_mp);
29707c478bd9Sstevel@tonic-gate 	else {
29717c478bd9Sstevel@tonic-gate 		freemsg(keysock_out_mp);
29727c478bd9Sstevel@tonic-gate 		return (B_FALSE);
29737c478bd9Sstevel@tonic-gate 	}
29747c478bd9Sstevel@tonic-gate 
29757c478bd9Sstevel@tonic-gate 	return (B_TRUE);
29767c478bd9Sstevel@tonic-gate }
29777c478bd9Sstevel@tonic-gate 
29787c478bd9Sstevel@tonic-gate /*
29797c478bd9Sstevel@tonic-gate  * Invoked when the algorithm table changes. Causes SADB_REGISTER
29807c478bd9Sstevel@tonic-gate  * messages continaining the current list of algorithms to be
29817c478bd9Sstevel@tonic-gate  * sent up to the ESP listeners.
29827c478bd9Sstevel@tonic-gate  */
29837c478bd9Sstevel@tonic-gate void
ipsecesp_algs_changed(netstack_t * ns)2984f4b3ec61Sdh155122 ipsecesp_algs_changed(netstack_t *ns)
29857c478bd9Sstevel@tonic-gate {
2986f4b3ec61Sdh155122 	ipsecesp_stack_t	*espstack = ns->netstack_ipsecesp;
2987f4b3ec61Sdh155122 
29887c478bd9Sstevel@tonic-gate 	/*
29897c478bd9Sstevel@tonic-gate 	 * Time to send a PF_KEY SADB_REGISTER message to ESP listeners
29907c478bd9Sstevel@tonic-gate 	 * everywhere.  (The function itself checks for NULL esp_pfkey_q.)
29917c478bd9Sstevel@tonic-gate 	 */
29925d3b8cb7SBill Sommerfeld 	(void) esp_register_out(0, 0, 0, espstack, NULL);
29937c478bd9Sstevel@tonic-gate }
29947c478bd9Sstevel@tonic-gate 
29957c478bd9Sstevel@tonic-gate /*
299673184bc7SDan McDonald  * Stub function that taskq_dispatch() invokes to take the mblk (in arg)
2997bd670b35SErik Nordmark  * and send() it into ESP and IP again.
29987c478bd9Sstevel@tonic-gate  */
29997c478bd9Sstevel@tonic-gate static void
inbound_task(void * arg)30007c478bd9Sstevel@tonic-gate inbound_task(void *arg)
30017c478bd9Sstevel@tonic-gate {
30027c478bd9Sstevel@tonic-gate 	mblk_t		*mp = (mblk_t *)arg;
3003bd670b35SErik Nordmark 	mblk_t		*async_mp;
3004bd670b35SErik Nordmark 	ip_recv_attr_t	iras;
30057c478bd9Sstevel@tonic-gate 
3006bd670b35SErik Nordmark 	async_mp = mp;
3007bd670b35SErik Nordmark 	mp = async_mp->b_cont;
3008bd670b35SErik Nordmark 	async_mp->b_cont = NULL;
3009bd670b35SErik Nordmark 	if (!ip_recv_attr_from_mblk(async_mp, &iras)) {
3010bd670b35SErik Nordmark 		/* The ill or ip_stack_t disappeared on us */
3011bd670b35SErik Nordmark 		ip_drop_input("ip_recv_attr_from_mblk", mp, NULL);
301273184bc7SDan McDonald 		freemsg(mp);
3013bd670b35SErik Nordmark 		goto done;
301473184bc7SDan McDonald 	}
301573184bc7SDan McDonald 
3016bd670b35SErik Nordmark 	esp_inbound_restart(mp, &iras);
3017bd670b35SErik Nordmark done:
3018bd670b35SErik Nordmark 	ira_cleanup(&iras, B_TRUE);
3019bd670b35SErik Nordmark }
3020bd670b35SErik Nordmark 
3021bd670b35SErik Nordmark /*
3022bd670b35SErik Nordmark  * Restart ESP after the SA has been added.
3023bd670b35SErik Nordmark  */
3024bd670b35SErik Nordmark static void
esp_inbound_restart(mblk_t * mp,ip_recv_attr_t * ira)3025bd670b35SErik Nordmark esp_inbound_restart(mblk_t *mp, ip_recv_attr_t *ira)
3026bd670b35SErik Nordmark {
3027bd670b35SErik Nordmark 	esph_t		*esph;
3028bd670b35SErik Nordmark 	netstack_t	*ns = ira->ira_ill->ill_ipst->ips_netstack;
3029bd670b35SErik Nordmark 	ipsecesp_stack_t *espstack = ns->netstack_ipsecesp;
303073184bc7SDan McDonald 
3031f4b3ec61Sdh155122 	esp2dbg(espstack, ("in ESP inbound_task"));
3032f4b3ec61Sdh155122 	ASSERT(espstack != NULL);
30337c478bd9Sstevel@tonic-gate 
3034bd670b35SErik Nordmark 	mp = ipsec_inbound_esp_sa(mp, ira, &esph);
3035bd670b35SErik Nordmark 	if (mp == NULL)
3036bd670b35SErik Nordmark 		return;
3037bd670b35SErik Nordmark 
3038bd670b35SErik Nordmark 	ASSERT(esph != NULL);
3039bd670b35SErik Nordmark 	ASSERT(ira->ira_flags & IRAF_IPSEC_SECURE);
3040bd670b35SErik Nordmark 	ASSERT(ira->ira_ipsec_esp_sa != NULL);
3041bd670b35SErik Nordmark 
3042bd670b35SErik Nordmark 	mp = ira->ira_ipsec_esp_sa->ipsa_input_func(mp, esph, ira);
3043bd670b35SErik Nordmark 	if (mp == NULL) {
3044bd670b35SErik Nordmark 		/*
3045bd670b35SErik Nordmark 		 * Either it failed or is pending. In the former case
3046bd670b35SErik Nordmark 		 * ipIfStatsInDiscards was increased.
3047bd670b35SErik Nordmark 		 */
3048bd670b35SErik Nordmark 		return;
30497c478bd9Sstevel@tonic-gate 	}
3050bd670b35SErik Nordmark 
3051bd670b35SErik Nordmark 	ip_input_post_ipsec(mp, ira);
305273184bc7SDan McDonald }
30537c478bd9Sstevel@tonic-gate 
30547c478bd9Sstevel@tonic-gate /*
30557c478bd9Sstevel@tonic-gate  * Now that weak-key passed, actually ADD the security association, and
30567c478bd9Sstevel@tonic-gate  * send back a reply ADD message.
30577c478bd9Sstevel@tonic-gate  */
30587c478bd9Sstevel@tonic-gate static int
esp_add_sa_finish(mblk_t * mp,sadb_msg_t * samsg,keysock_in_t * ksi,int * diagnostic,ipsecesp_stack_t * espstack)30598810c16bSdanmcd esp_add_sa_finish(mblk_t *mp, sadb_msg_t *samsg, keysock_in_t *ksi,
3060f4b3ec61Sdh155122     int *diagnostic, ipsecesp_stack_t *espstack)
30617c478bd9Sstevel@tonic-gate {
30625d3b8cb7SBill Sommerfeld 	isaf_t *primary = NULL, *secondary;
30635d3b8cb7SBill Sommerfeld 	boolean_t clone = B_FALSE, is_inbound = B_FALSE;
30647c478bd9Sstevel@tonic-gate 	ipsa_t *larval = NULL;
30657c478bd9Sstevel@tonic-gate 	ipsacq_t *acqrec;
30667c478bd9Sstevel@tonic-gate 	iacqf_t *acq_bucket;
30677c478bd9Sstevel@tonic-gate 	mblk_t *acq_msgs = NULL;
30687c478bd9Sstevel@tonic-gate 	int rc;
30697c478bd9Sstevel@tonic-gate 	mblk_t *lpkt;
30705d3b8cb7SBill Sommerfeld 	int error;
30715d3b8cb7SBill Sommerfeld 	ipsa_query_t sq;
3072f4b3ec61Sdh155122 	ipsec_stack_t	*ipss = espstack->ipsecesp_netstack->netstack_ipsec;
30737c478bd9Sstevel@tonic-gate 
30747c478bd9Sstevel@tonic-gate 	/*
30757c478bd9Sstevel@tonic-gate 	 * Locate the appropriate table(s).
30767c478bd9Sstevel@tonic-gate 	 */
30775d3b8cb7SBill Sommerfeld 	sq.spp = &espstack->esp_sadb;	/* XXX */
30785d3b8cb7SBill Sommerfeld 	error = sadb_form_query(ksi, IPSA_Q_SA|IPSA_Q_DST,
30795d3b8cb7SBill Sommerfeld 	    IPSA_Q_SA|IPSA_Q_DST|IPSA_Q_INBOUND|IPSA_Q_OUTBOUND,
30805d3b8cb7SBill Sommerfeld 	    &sq, diagnostic);
30815d3b8cb7SBill Sommerfeld 	if (error)
30825d3b8cb7SBill Sommerfeld 		return (error);
308307b56925Ssommerfe 
308438d95a78Smarkfen 	/*
308538d95a78Smarkfen 	 * Use the direction flags provided by the KMD to determine
308638d95a78Smarkfen 	 * if the inbound or outbound table should be the primary
308738d95a78Smarkfen 	 * for this SA. If these flags were absent then make this
308838d95a78Smarkfen 	 * decision based on the addresses.
308938d95a78Smarkfen 	 */
30905d3b8cb7SBill Sommerfeld 	if (sq.assoc->sadb_sa_flags & IPSA_F_INBOUND) {
30915d3b8cb7SBill Sommerfeld 		primary = sq.inbound;
30925d3b8cb7SBill Sommerfeld 		secondary = sq.outbound;
309338d95a78Smarkfen 		is_inbound = B_TRUE;
30945d3b8cb7SBill Sommerfeld 		if (sq.assoc->sadb_sa_flags & IPSA_F_OUTBOUND)
309538d95a78Smarkfen 			clone = B_TRUE;
30965d3b8cb7SBill Sommerfeld 	} else if (sq.assoc->sadb_sa_flags & IPSA_F_OUTBOUND) {
30975d3b8cb7SBill Sommerfeld 		primary = sq.outbound;
30985d3b8cb7SBill Sommerfeld 		secondary = sq.inbound;
309938d95a78Smarkfen 	}
310038d95a78Smarkfen 
310138d95a78Smarkfen 	if (primary == NULL) {
310238d95a78Smarkfen 		/*
310338d95a78Smarkfen 		 * The KMD did not set a direction flag, determine which
310438d95a78Smarkfen 		 * table to insert the SA into based on addresses.
310538d95a78Smarkfen 		 */
31067c478bd9Sstevel@tonic-gate 		switch (ksi->ks_in_dsttype) {
31077c478bd9Sstevel@tonic-gate 		case KS_IN_ADDR_MBCAST:
31087c478bd9Sstevel@tonic-gate 			clone = B_TRUE;	/* All mcast SAs can be bidirectional */
31095d3b8cb7SBill Sommerfeld 			sq.assoc->sadb_sa_flags |= IPSA_F_OUTBOUND;
31107c478bd9Sstevel@tonic-gate 			/* FALLTHRU */
31117c478bd9Sstevel@tonic-gate 		/*
31127c478bd9Sstevel@tonic-gate 		 * If the source address is either one of mine, or unspecified
31137c478bd9Sstevel@tonic-gate 		 * (which is best summed up by saying "not 'not mine'"),
31147c478bd9Sstevel@tonic-gate 		 * then the association is potentially bi-directional,
31157c478bd9Sstevel@tonic-gate 		 * in that it can be used for inbound traffic and outbound
31167c478bd9Sstevel@tonic-gate 		 * traffic.  The best example of such an SA is a multicast
31177c478bd9Sstevel@tonic-gate 		 * SA (which allows me to receive the outbound traffic).
31187c478bd9Sstevel@tonic-gate 		 */
311938d95a78Smarkfen 		case KS_IN_ADDR_ME:
31205d3b8cb7SBill Sommerfeld 			sq.assoc->sadb_sa_flags |= IPSA_F_INBOUND;
31215d3b8cb7SBill Sommerfeld 			primary = sq.inbound;
31225d3b8cb7SBill Sommerfeld 			secondary = sq.outbound;
31237c478bd9Sstevel@tonic-gate 			if (ksi->ks_in_srctype != KS_IN_ADDR_NOTME)
31247c478bd9Sstevel@tonic-gate 				clone = B_TRUE;
31257c478bd9Sstevel@tonic-gate 			is_inbound = B_TRUE;
31267c478bd9Sstevel@tonic-gate 			break;
31277c478bd9Sstevel@tonic-gate 		/*
31287c478bd9Sstevel@tonic-gate 		 * If the source address literally not mine (either
31297c478bd9Sstevel@tonic-gate 		 * unspecified or not mine), then this SA may have an
31307c478bd9Sstevel@tonic-gate 		 * address that WILL be mine after some configuration.
31317c478bd9Sstevel@tonic-gate 		 * We pay the price for this by making it a bi-directional
31327c478bd9Sstevel@tonic-gate 		 * SA.
31337c478bd9Sstevel@tonic-gate 		 */
313438d95a78Smarkfen 		case KS_IN_ADDR_NOTME:
31355d3b8cb7SBill Sommerfeld 			sq.assoc->sadb_sa_flags |= IPSA_F_OUTBOUND;
31365d3b8cb7SBill Sommerfeld 			primary = sq.outbound;
31375d3b8cb7SBill Sommerfeld 			secondary = sq.inbound;
313838d95a78Smarkfen 			if (ksi->ks_in_srctype != KS_IN_ADDR_ME) {
31395d3b8cb7SBill Sommerfeld 				sq.assoc->sadb_sa_flags |= IPSA_F_INBOUND;
31407c478bd9Sstevel@tonic-gate 				clone = B_TRUE;
314138d95a78Smarkfen 			}
31427c478bd9Sstevel@tonic-gate 			break;
31437c478bd9Sstevel@tonic-gate 		default:
31448810c16bSdanmcd 			*diagnostic = SADB_X_DIAGNOSTIC_BAD_DST;
31457c478bd9Sstevel@tonic-gate 			return (EINVAL);
31467c478bd9Sstevel@tonic-gate 		}
314738d95a78Smarkfen 	}
31487c478bd9Sstevel@tonic-gate 
31497c478bd9Sstevel@tonic-gate 	/*
31507c478bd9Sstevel@tonic-gate 	 * Find a ACQUIRE list entry if possible.  If we've added an SA that
31517c478bd9Sstevel@tonic-gate 	 * suits the needs of an ACQUIRE list entry, we can eliminate the
31527c478bd9Sstevel@tonic-gate 	 * ACQUIRE list entry and transmit the enqueued packets.  Use the
31537c478bd9Sstevel@tonic-gate 	 * high-bit of the sequence number to queue it.  Key off destination
31547c478bd9Sstevel@tonic-gate 	 * addr, and change acqrec's state.
31557c478bd9Sstevel@tonic-gate 	 */
31567c478bd9Sstevel@tonic-gate 
31577c478bd9Sstevel@tonic-gate 	if (samsg->sadb_msg_seq & IACQF_LOWEST_SEQ) {
31585d3b8cb7SBill Sommerfeld 		acq_bucket = &(sq.sp->sdb_acq[sq.outhash]);
31597c478bd9Sstevel@tonic-gate 		mutex_enter(&acq_bucket->iacqf_lock);
31607c478bd9Sstevel@tonic-gate 		for (acqrec = acq_bucket->iacqf_ipsacq; acqrec != NULL;
31617c478bd9Sstevel@tonic-gate 		    acqrec = acqrec->ipsacq_next) {
31627c478bd9Sstevel@tonic-gate 			mutex_enter(&acqrec->ipsacq_lock);
31637c478bd9Sstevel@tonic-gate 			/*
31647c478bd9Sstevel@tonic-gate 			 * Q:  I only check sequence.  Should I check dst?
31657c478bd9Sstevel@tonic-gate 			 * A: Yes, check dest because those are the packets
31667c478bd9Sstevel@tonic-gate 			 *    that are queued up.
31677c478bd9Sstevel@tonic-gate 			 */
31687c478bd9Sstevel@tonic-gate 			if (acqrec->ipsacq_seq == samsg->sadb_msg_seq &&
31695d3b8cb7SBill Sommerfeld 			    IPSA_ARE_ADDR_EQUAL(sq.dstaddr,
31707c478bd9Sstevel@tonic-gate 			    acqrec->ipsacq_dstaddr, acqrec->ipsacq_addrfam))
31717c478bd9Sstevel@tonic-gate 				break;
31727c478bd9Sstevel@tonic-gate 			mutex_exit(&acqrec->ipsacq_lock);
31737c478bd9Sstevel@tonic-gate 		}
31747c478bd9Sstevel@tonic-gate 		if (acqrec != NULL) {
31757c478bd9Sstevel@tonic-gate 			/*
31767c478bd9Sstevel@tonic-gate 			 * AHA!  I found an ACQUIRE record for this SA.
31777c478bd9Sstevel@tonic-gate 			 * Grab the msg list, and free the acquire record.
31787c478bd9Sstevel@tonic-gate 			 * I already am holding the lock for this record,
31797c478bd9Sstevel@tonic-gate 			 * so all I have to do is free it.
31807c478bd9Sstevel@tonic-gate 			 */
31817c478bd9Sstevel@tonic-gate 			acq_msgs = acqrec->ipsacq_mp;
31827c478bd9Sstevel@tonic-gate 			acqrec->ipsacq_mp = NULL;
31837c478bd9Sstevel@tonic-gate 			mutex_exit(&acqrec->ipsacq_lock);
3184f4b3ec61Sdh155122 			sadb_destroy_acquire(acqrec,
3185f4b3ec61Sdh155122 			    espstack->ipsecesp_netstack);
31867c478bd9Sstevel@tonic-gate 		}
31877c478bd9Sstevel@tonic-gate 		mutex_exit(&acq_bucket->iacqf_lock);
31887c478bd9Sstevel@tonic-gate 	}
31897c478bd9Sstevel@tonic-gate 
31907c478bd9Sstevel@tonic-gate 	/*
31917c478bd9Sstevel@tonic-gate 	 * Find PF_KEY message, and see if I'm an update.  If so, find entry
31927c478bd9Sstevel@tonic-gate 	 * in larval list (if there).
31937c478bd9Sstevel@tonic-gate 	 */
31947c478bd9Sstevel@tonic-gate 	if (samsg->sadb_msg_type == SADB_UPDATE) {
31955d3b8cb7SBill Sommerfeld 		mutex_enter(&sq.inbound->isaf_lock);
31965d3b8cb7SBill Sommerfeld 		larval = ipsec_getassocbyspi(sq.inbound, sq.assoc->sadb_sa_spi,
31975d3b8cb7SBill Sommerfeld 		    ALL_ZEROES_PTR, sq.dstaddr, sq.dst->sin_family);
31985d3b8cb7SBill Sommerfeld 		mutex_exit(&sq.inbound->isaf_lock);
31997c478bd9Sstevel@tonic-gate 
320072bd9b6bSdanmcd 		if ((larval == NULL) ||
320172bd9b6bSdanmcd 		    (larval->ipsa_state != IPSA_STATE_LARVAL)) {
320238d95a78Smarkfen 			*diagnostic = SADB_X_DIAGNOSTIC_SA_NOTFOUND;
320372bd9b6bSdanmcd 			if (larval != NULL) {
320472bd9b6bSdanmcd 				IPSA_REFRELE(larval);
320572bd9b6bSdanmcd 			}
32067c478bd9Sstevel@tonic-gate 			esp0dbg(("Larval update, but larval disappeared.\n"));
32077c478bd9Sstevel@tonic-gate 			return (ESRCH);
32087c478bd9Sstevel@tonic-gate 		} /* Else sadb_common_add unlinks it for me! */
32097c478bd9Sstevel@tonic-gate 	}
32107c478bd9Sstevel@tonic-gate 
3211930af642SDan McDonald 	if (larval != NULL) {
3212930af642SDan McDonald 		/*
3213930af642SDan McDonald 		 * Hold again, because sadb_common_add() consumes a reference,
3214930af642SDan McDonald 		 * and we don't want to clear_lpkt() without a reference.
3215930af642SDan McDonald 		 */
3216930af642SDan McDonald 		IPSA_REFHOLD(larval);
3217930af642SDan McDonald 	}
32187c478bd9Sstevel@tonic-gate 
3219bd670b35SErik Nordmark 	rc = sadb_common_add(espstack->esp_pfkey_q,
3220f4b3ec61Sdh155122 	    mp, samsg, ksi, primary, secondary, larval, clone, is_inbound,
322138d95a78Smarkfen 	    diagnostic, espstack->ipsecesp_netstack, &espstack->esp_sadb);
32227c478bd9Sstevel@tonic-gate 
3223930af642SDan McDonald 	if (larval != NULL) {
3224bd670b35SErik Nordmark 		if (rc == 0) {
3225930af642SDan McDonald 			lpkt = sadb_clear_lpkt(larval);
3226930af642SDan McDonald 			if (lpkt != NULL) {
3227fc8ae2ecSToomas Soome 				rc = taskq_dispatch(esp_taskq, inbound_task,
3228fc8ae2ecSToomas Soome 				    lpkt, TQ_NOSLEEP) == TASKQID_INVALID;
3229bd670b35SErik Nordmark 			}
32307c478bd9Sstevel@tonic-gate 		}
3231930af642SDan McDonald 		IPSA_REFRELE(larval);
3232bd670b35SErik Nordmark 	}
32337c478bd9Sstevel@tonic-gate 
32347c478bd9Sstevel@tonic-gate 	/*
32357c478bd9Sstevel@tonic-gate 	 * How much more stack will I create with all of these
32367c478bd9Sstevel@tonic-gate 	 * esp_outbound() calls?
32377c478bd9Sstevel@tonic-gate 	 */
32387c478bd9Sstevel@tonic-gate 
3239bd670b35SErik Nordmark 	/* Handle the packets queued waiting for the SA */
32407c478bd9Sstevel@tonic-gate 	while (acq_msgs != NULL) {
3241bd670b35SErik Nordmark 		mblk_t		*asyncmp;
3242bd670b35SErik Nordmark 		mblk_t		*data_mp;
3243bd670b35SErik Nordmark 		ip_xmit_attr_t	ixas;
3244bd670b35SErik Nordmark 		ill_t		*ill;
32457c478bd9Sstevel@tonic-gate 
3246bd670b35SErik Nordmark 		asyncmp = acq_msgs;
32477c478bd9Sstevel@tonic-gate 		acq_msgs = acq_msgs->b_next;
3248bd670b35SErik Nordmark 		asyncmp->b_next = NULL;
32497c478bd9Sstevel@tonic-gate 
3250bd670b35SErik Nordmark 		/*
3251bd670b35SErik Nordmark 		 * Extract the ip_xmit_attr_t from the first mblk.
3252bd670b35SErik Nordmark 		 * Verifies that the netstack and ill is still around; could
3253bd670b35SErik Nordmark 		 * have vanished while iked was doing its work.
3254bd670b35SErik Nordmark 		 * On succesful return we have a nce_t and the ill/ipst can't
3255bd670b35SErik Nordmark 		 * disappear until we do the nce_refrele in ixa_cleanup.
3256bd670b35SErik Nordmark 		 */
3257bd670b35SErik Nordmark 		data_mp = asyncmp->b_cont;
3258bd670b35SErik Nordmark 		asyncmp->b_cont = NULL;
3259bd670b35SErik Nordmark 		if (!ip_xmit_attr_from_mblk(asyncmp, &ixas)) {
3260f4b3ec61Sdh155122 			ESP_BUMP_STAT(espstack, out_discards);
3261bd670b35SErik Nordmark 			ip_drop_packet(data_mp, B_FALSE, NULL,
3262f4b3ec61Sdh155122 			    DROPPER(ipss, ipds_sadb_acquire_timeout),
3263f4b3ec61Sdh155122 			    &espstack->esp_dropper);
3264bd670b35SErik Nordmark 		} else if (rc != 0) {
3265bd670b35SErik Nordmark 			ill = ixas.ixa_nce->nce_ill;
3266bd670b35SErik Nordmark 			ESP_BUMP_STAT(espstack, out_discards);
3267bd670b35SErik Nordmark 			ip_drop_packet(data_mp, B_FALSE, ill,
3268bd670b35SErik Nordmark 			    DROPPER(ipss, ipds_sadb_acquire_timeout),
3269bd670b35SErik Nordmark 			    &espstack->esp_dropper);
3270bd670b35SErik Nordmark 			BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
3271bd670b35SErik Nordmark 		} else {
3272bd670b35SErik Nordmark 			esp_outbound_finish(data_mp, &ixas);
3273bd670b35SErik Nordmark 		}
3274bd670b35SErik Nordmark 		ixa_cleanup(&ixas);
32757c478bd9Sstevel@tonic-gate 	}
32767c478bd9Sstevel@tonic-gate 
32777c478bd9Sstevel@tonic-gate 	return (rc);
32787c478bd9Sstevel@tonic-gate }
32797c478bd9Sstevel@tonic-gate 
32807c478bd9Sstevel@tonic-gate /*
3281bd670b35SErik Nordmark  * Process one of the queued messages (from ipsacq_mp) once the SA
3282bd670b35SErik Nordmark  * has been added.
3283bd670b35SErik Nordmark  */
3284bd670b35SErik Nordmark static void
esp_outbound_finish(mblk_t * data_mp,ip_xmit_attr_t * ixa)3285bd670b35SErik Nordmark esp_outbound_finish(mblk_t *data_mp, ip_xmit_attr_t *ixa)
3286bd670b35SErik Nordmark {
3287bd670b35SErik Nordmark 	netstack_t	*ns = ixa->ixa_ipst->ips_netstack;
3288bd670b35SErik Nordmark 	ipsecesp_stack_t *espstack = ns->netstack_ipsecesp;
3289bd670b35SErik Nordmark 	ipsec_stack_t	*ipss = ns->netstack_ipsec;
3290bd670b35SErik Nordmark 	ill_t		*ill = ixa->ixa_nce->nce_ill;
3291bd670b35SErik Nordmark 
3292bd670b35SErik Nordmark 	if (!ipsec_outbound_sa(data_mp, ixa, IPPROTO_ESP)) {
3293bd670b35SErik Nordmark 		ESP_BUMP_STAT(espstack, out_discards);
3294bd670b35SErik Nordmark 		ip_drop_packet(data_mp, B_FALSE, ill,
3295bd670b35SErik Nordmark 		    DROPPER(ipss, ipds_sadb_acquire_timeout),
3296bd670b35SErik Nordmark 		    &espstack->esp_dropper);
3297bd670b35SErik Nordmark 		BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
3298bd670b35SErik Nordmark 		return;
3299bd670b35SErik Nordmark 	}
3300bd670b35SErik Nordmark 
3301bd670b35SErik Nordmark 	data_mp = esp_outbound(data_mp, ixa);
3302bd670b35SErik Nordmark 	if (data_mp == NULL)
3303bd670b35SErik Nordmark 		return;
3304bd670b35SErik Nordmark 
3305bd670b35SErik Nordmark 	/* do AH processing if needed */
3306bd670b35SErik Nordmark 	data_mp = esp_do_outbound_ah(data_mp, ixa);
3307bd670b35SErik Nordmark 	if (data_mp == NULL)
3308bd670b35SErik Nordmark 		return;
3309bd670b35SErik Nordmark 
3310bd670b35SErik Nordmark 	(void) ip_output_post_ipsec(data_mp, ixa);
3311bd670b35SErik Nordmark }
3312bd670b35SErik Nordmark 
3313bd670b35SErik Nordmark /*
33147c478bd9Sstevel@tonic-gate  * Add new ESP security association.  This may become a generic AH/ESP
33157c478bd9Sstevel@tonic-gate  * routine eventually.
33167c478bd9Sstevel@tonic-gate  */
33177c478bd9Sstevel@tonic-gate static int
esp_add_sa(mblk_t * mp,keysock_in_t * ksi,int * diagnostic,netstack_t * ns)3318f4b3ec61Sdh155122 esp_add_sa(mblk_t *mp, keysock_in_t *ksi, int *diagnostic, netstack_t *ns)
33197c478bd9Sstevel@tonic-gate {
33207c478bd9Sstevel@tonic-gate 	sadb_sa_t *assoc = (sadb_sa_t *)ksi->ks_in_extv[SADB_EXT_SA];
33217c478bd9Sstevel@tonic-gate 	sadb_address_t *srcext =
33227c478bd9Sstevel@tonic-gate 	    (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_SRC];
33237c478bd9Sstevel@tonic-gate 	sadb_address_t *dstext =
33247c478bd9Sstevel@tonic-gate 	    (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_DST];
33258810c16bSdanmcd 	sadb_address_t *isrcext =
33268810c16bSdanmcd 	    (sadb_address_t *)ksi->ks_in_extv[SADB_X_EXT_ADDRESS_INNER_SRC];
33278810c16bSdanmcd 	sadb_address_t *idstext =
33288810c16bSdanmcd 	    (sadb_address_t *)ksi->ks_in_extv[SADB_X_EXT_ADDRESS_INNER_DST];
33297c478bd9Sstevel@tonic-gate 	sadb_address_t *nttext_loc =
33307c478bd9Sstevel@tonic-gate 	    (sadb_address_t *)ksi->ks_in_extv[SADB_X_EXT_ADDRESS_NATT_LOC];
33317c478bd9Sstevel@tonic-gate 	sadb_address_t *nttext_rem =
33327c478bd9Sstevel@tonic-gate 	    (sadb_address_t *)ksi->ks_in_extv[SADB_X_EXT_ADDRESS_NATT_REM];
33337c478bd9Sstevel@tonic-gate 	sadb_key_t *akey = (sadb_key_t *)ksi->ks_in_extv[SADB_EXT_KEY_AUTH];
33347c478bd9Sstevel@tonic-gate 	sadb_key_t *ekey = (sadb_key_t *)ksi->ks_in_extv[SADB_EXT_KEY_ENCRYPT];
33357c478bd9Sstevel@tonic-gate 	struct sockaddr_in *src, *dst;
33367c478bd9Sstevel@tonic-gate 	struct sockaddr_in *natt_loc, *natt_rem;
33377c478bd9Sstevel@tonic-gate 	struct sockaddr_in6 *natt_loc6, *natt_rem6;
33387c478bd9Sstevel@tonic-gate 	sadb_lifetime_t *soft =
33397c478bd9Sstevel@tonic-gate 	    (sadb_lifetime_t *)ksi->ks_in_extv[SADB_EXT_LIFETIME_SOFT];
33407c478bd9Sstevel@tonic-gate 	sadb_lifetime_t *hard =
33417c478bd9Sstevel@tonic-gate 	    (sadb_lifetime_t *)ksi->ks_in_extv[SADB_EXT_LIFETIME_HARD];
33429c2c14abSThejaswini Singarajipura 	sadb_lifetime_t *idle =
33439c2c14abSThejaswini Singarajipura 	    (sadb_lifetime_t *)ksi->ks_in_extv[SADB_X_EXT_LIFETIME_IDLE];
3344f4b3ec61Sdh155122 	ipsecesp_stack_t *espstack = ns->netstack_ipsecesp;
3345f4b3ec61Sdh155122 	ipsec_stack_t	*ipss = ns->netstack_ipsec;
33467c478bd9Sstevel@tonic-gate 
33475d3b8cb7SBill Sommerfeld 
33485d3b8cb7SBill Sommerfeld 
33497c478bd9Sstevel@tonic-gate 	/* I need certain extensions present for an ADD message. */
33507c478bd9Sstevel@tonic-gate 	if (srcext == NULL) {
33517c478bd9Sstevel@tonic-gate 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_SRC;
33527c478bd9Sstevel@tonic-gate 		return (EINVAL);
33537c478bd9Sstevel@tonic-gate 	}
33547c478bd9Sstevel@tonic-gate 	if (dstext == NULL) {
33557c478bd9Sstevel@tonic-gate 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_DST;
33567c478bd9Sstevel@tonic-gate 		return (EINVAL);
33577c478bd9Sstevel@tonic-gate 	}
33588810c16bSdanmcd 	if (isrcext == NULL && idstext != NULL) {
33598810c16bSdanmcd 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_INNER_SRC;
33608810c16bSdanmcd 		return (EINVAL);
33618810c16bSdanmcd 	}
33628810c16bSdanmcd 	if (isrcext != NULL && idstext == NULL) {
33638810c16bSdanmcd 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_INNER_DST;
33648810c16bSdanmcd 		return (EINVAL);
33658810c16bSdanmcd 	}
33667c478bd9Sstevel@tonic-gate 	if (assoc == NULL) {
33677c478bd9Sstevel@tonic-gate 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_SA;
33687c478bd9Sstevel@tonic-gate 		return (EINVAL);
33697c478bd9Sstevel@tonic-gate 	}
33707c478bd9Sstevel@tonic-gate 	if (ekey == NULL && assoc->sadb_sa_encrypt != SADB_EALG_NULL) {
33717c478bd9Sstevel@tonic-gate 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_EKEY;
33727c478bd9Sstevel@tonic-gate 		return (EINVAL);
33737c478bd9Sstevel@tonic-gate 	}
33747c478bd9Sstevel@tonic-gate 
33757c478bd9Sstevel@tonic-gate 	src = (struct sockaddr_in *)(srcext + 1);
33767c478bd9Sstevel@tonic-gate 	dst = (struct sockaddr_in *)(dstext + 1);
33777c478bd9Sstevel@tonic-gate 	natt_loc = (struct sockaddr_in *)(nttext_loc + 1);
33787c478bd9Sstevel@tonic-gate 	natt_loc6 = (struct sockaddr_in6 *)(nttext_loc + 1);
33797c478bd9Sstevel@tonic-gate 	natt_rem = (struct sockaddr_in *)(nttext_rem + 1);
33807c478bd9Sstevel@tonic-gate 	natt_rem6 = (struct sockaddr_in6 *)(nttext_rem + 1);
33817c478bd9Sstevel@tonic-gate 
33827c478bd9Sstevel@tonic-gate 	/* Sundry ADD-specific reality checks. */
33837c478bd9Sstevel@tonic-gate 	/* XXX STATS :  Logging/stats here? */
33849c2c14abSThejaswini Singarajipura 
33859c2c14abSThejaswini Singarajipura 	if ((assoc->sadb_sa_state != SADB_SASTATE_MATURE) &&
33869c2c14abSThejaswini Singarajipura 	    (assoc->sadb_sa_state != SADB_X_SASTATE_ACTIVE_ELSEWHERE)) {
33877c478bd9Sstevel@tonic-gate 		*diagnostic = SADB_X_DIAGNOSTIC_BAD_SASTATE;
33887c478bd9Sstevel@tonic-gate 		return (EINVAL);
33897c478bd9Sstevel@tonic-gate 	}
33907c478bd9Sstevel@tonic-gate 	if (assoc->sadb_sa_encrypt == SADB_EALG_NONE) {
33917c478bd9Sstevel@tonic-gate 		*diagnostic = SADB_X_DIAGNOSTIC_BAD_EALG;
33927c478bd9Sstevel@tonic-gate 		return (EINVAL);
33937c478bd9Sstevel@tonic-gate 	}
33947c478bd9Sstevel@tonic-gate 
3395bd670b35SErik Nordmark #ifndef IPSEC_LATENCY_TEST
33967c478bd9Sstevel@tonic-gate 	if (assoc->sadb_sa_encrypt == SADB_EALG_NULL &&
33977c478bd9Sstevel@tonic-gate 	    assoc->sadb_sa_auth == SADB_AALG_NONE) {
33987c478bd9Sstevel@tonic-gate 		*diagnostic = SADB_X_DIAGNOSTIC_BAD_AALG;
33997c478bd9Sstevel@tonic-gate 		return (EINVAL);
34007c478bd9Sstevel@tonic-gate 	}
3401bd670b35SErik Nordmark #endif
34027c478bd9Sstevel@tonic-gate 
340372bd9b6bSdanmcd 	if (assoc->sadb_sa_flags & ~espstack->esp_sadb.s_addflags) {
34047c478bd9Sstevel@tonic-gate 		*diagnostic = SADB_X_DIAGNOSTIC_BAD_SAFLAGS;
34057c478bd9Sstevel@tonic-gate 		return (EINVAL);
34067c478bd9Sstevel@tonic-gate 	}
34077c478bd9Sstevel@tonic-gate 
34089c2c14abSThejaswini Singarajipura 	if ((*diagnostic = sadb_hardsoftchk(hard, soft, idle)) != 0) {
34097c478bd9Sstevel@tonic-gate 		return (EINVAL);
34107c478bd9Sstevel@tonic-gate 	}
34118810c16bSdanmcd 	ASSERT(src->sin_family == dst->sin_family);
34127c478bd9Sstevel@tonic-gate 
34137c478bd9Sstevel@tonic-gate 	if (assoc->sadb_sa_flags & SADB_X_SAFLAGS_NATT_LOC) {
34147c478bd9Sstevel@tonic-gate 		if (nttext_loc == NULL) {
34157c478bd9Sstevel@tonic-gate 			*diagnostic = SADB_X_DIAGNOSTIC_MISSING_NATT_LOC;
34167c478bd9Sstevel@tonic-gate 			return (EINVAL);
34177c478bd9Sstevel@tonic-gate 		}
34187c478bd9Sstevel@tonic-gate 
34197c478bd9Sstevel@tonic-gate 		if (natt_loc->sin_family == AF_INET6 &&
34207c478bd9Sstevel@tonic-gate 		    !IN6_IS_ADDR_V4MAPPED(&natt_loc6->sin6_addr)) {
34217c478bd9Sstevel@tonic-gate 			*diagnostic = SADB_X_DIAGNOSTIC_MALFORMED_NATT_LOC;
34227c478bd9Sstevel@tonic-gate 			return (EINVAL);
34237c478bd9Sstevel@tonic-gate 		}
34247c478bd9Sstevel@tonic-gate 	}
34257c478bd9Sstevel@tonic-gate 
34267c478bd9Sstevel@tonic-gate 	if (assoc->sadb_sa_flags & SADB_X_SAFLAGS_NATT_REM) {
34277c478bd9Sstevel@tonic-gate 		if (nttext_rem == NULL) {
34287c478bd9Sstevel@tonic-gate 			*diagnostic = SADB_X_DIAGNOSTIC_MISSING_NATT_REM;
34297c478bd9Sstevel@tonic-gate 			return (EINVAL);
34307c478bd9Sstevel@tonic-gate 		}
34317c478bd9Sstevel@tonic-gate 		if (natt_rem->sin_family == AF_INET6 &&
34327c478bd9Sstevel@tonic-gate 		    !IN6_IS_ADDR_V4MAPPED(&natt_rem6->sin6_addr)) {
34337c478bd9Sstevel@tonic-gate 			*diagnostic = SADB_X_DIAGNOSTIC_MALFORMED_NATT_REM;
34347c478bd9Sstevel@tonic-gate 			return (EINVAL);
34357c478bd9Sstevel@tonic-gate 		}
34367c478bd9Sstevel@tonic-gate 	}
34377c478bd9Sstevel@tonic-gate 
34387c478bd9Sstevel@tonic-gate 
34397c478bd9Sstevel@tonic-gate 	/* Stuff I don't support, for now.  XXX Diagnostic? */
34405d3b8cb7SBill Sommerfeld 	if (ksi->ks_in_extv[SADB_EXT_LIFETIME_CURRENT] != NULL)
34417c478bd9Sstevel@tonic-gate 		return (EOPNOTSUPP);
34427c478bd9Sstevel@tonic-gate 
34435d3b8cb7SBill Sommerfeld 	if ((*diagnostic = sadb_labelchk(ksi)) != 0)
34445d3b8cb7SBill Sommerfeld 		return (EINVAL);
34455d3b8cb7SBill Sommerfeld 
34467c478bd9Sstevel@tonic-gate 	/*
34475d3b8cb7SBill Sommerfeld 	 * XXX Policy :  I'm not checking identities at this time,
34485d3b8cb7SBill Sommerfeld 	 * but if I did, I'd do them here, before I sent
34497c478bd9Sstevel@tonic-gate 	 * the weak key check up to the algorithm.
34507c478bd9Sstevel@tonic-gate 	 */
34517c478bd9Sstevel@tonic-gate 
345269e71331SBayard Bell 	rw_enter(&ipss->ipsec_alg_lock, RW_READER);
34537c478bd9Sstevel@tonic-gate 
34547c478bd9Sstevel@tonic-gate 	/*
34557c478bd9Sstevel@tonic-gate 	 * First locate the authentication algorithm.
34567c478bd9Sstevel@tonic-gate 	 */
3457bd670b35SErik Nordmark #ifdef IPSEC_LATENCY_TEST
3458bd670b35SErik Nordmark 	if (akey != NULL && assoc->sadb_sa_auth != SADB_AALG_NONE) {
3459bd670b35SErik Nordmark #else
34607c478bd9Sstevel@tonic-gate 	if (akey != NULL) {
3461bd670b35SErik Nordmark #endif
34627c478bd9Sstevel@tonic-gate 		ipsec_alginfo_t *aalg;
34637c478bd9Sstevel@tonic-gate 
3464f4b3ec61Sdh155122 		aalg = ipss->ipsec_alglists[IPSEC_ALG_AUTH]
3465f4b3ec61Sdh155122 		    [assoc->sadb_sa_auth];
34667c478bd9Sstevel@tonic-gate 		if (aalg == NULL || !ALG_VALID(aalg)) {
346769e71331SBayard Bell 			rw_exit(&ipss->ipsec_alg_lock);
3468f4b3ec61Sdh155122 			esp1dbg(espstack, ("Couldn't find auth alg #%d.\n",
34697c478bd9Sstevel@tonic-gate 			    assoc->sadb_sa_auth));
34707c478bd9Sstevel@tonic-gate 			*diagnostic = SADB_X_DIAGNOSTIC_BAD_AALG;
34717c478bd9Sstevel@tonic-gate 			return (EINVAL);
34727c478bd9Sstevel@tonic-gate 		}
34737c478bd9Sstevel@tonic-gate 
347446c444baSmarkfen 		/*
347546c444baSmarkfen 		 * Sanity check key sizes.
347646c444baSmarkfen 		 * Note: It's not possible to use SADB_AALG_NONE because
347746c444baSmarkfen 		 * this auth_alg is not defined with ALG_FLAG_VALID. If this
347846c444baSmarkfen 		 * ever changes, the same check for SADB_AALG_NONE and
347946c444baSmarkfen 		 * a auth_key != NULL should be made here ( see below).
348046c444baSmarkfen 		 */
34817c478bd9Sstevel@tonic-gate 		if (!ipsec_valid_key_size(akey->sadb_key_bits, aalg)) {
348269e71331SBayard Bell 			rw_exit(&ipss->ipsec_alg_lock);
34837c478bd9Sstevel@tonic-gate 			*diagnostic = SADB_X_DIAGNOSTIC_BAD_AKEYBITS;
34847c478bd9Sstevel@tonic-gate 			return (EINVAL);
34857c478bd9Sstevel@tonic-gate 		}
348646c444baSmarkfen 		ASSERT(aalg->alg_mech_type != CRYPTO_MECHANISM_INVALID);
34877c478bd9Sstevel@tonic-gate 
34887c478bd9Sstevel@tonic-gate 		/* check key and fix parity if needed */
34897c478bd9Sstevel@tonic-gate 		if (ipsec_check_key(aalg->alg_mech_type, akey, B_TRUE,
34907c478bd9Sstevel@tonic-gate 		    diagnostic) != 0) {
349169e71331SBayard Bell 			rw_exit(&ipss->ipsec_alg_lock);
34927c478bd9Sstevel@tonic-gate 			return (EINVAL);
34937c478bd9Sstevel@tonic-gate 		}
34947c478bd9Sstevel@tonic-gate 	}
34957c478bd9Sstevel@tonic-gate 
34967c478bd9Sstevel@tonic-gate 	/*
34977c478bd9Sstevel@tonic-gate 	 * Then locate the encryption algorithm.
34987c478bd9Sstevel@tonic-gate 	 */
34997c478bd9Sstevel@tonic-gate 	if (ekey != NULL) {
3500628b0c67SMark Fenwick 		uint_t keybits;
35017c478bd9Sstevel@tonic-gate 		ipsec_alginfo_t *ealg;
35027c478bd9Sstevel@tonic-gate 
3503f4b3ec61Sdh155122 		ealg = ipss->ipsec_alglists[IPSEC_ALG_ENCR]
3504f4b3ec61Sdh155122 		    [assoc->sadb_sa_encrypt];
35057c478bd9Sstevel@tonic-gate 		if (ealg == NULL || !ALG_VALID(ealg)) {
350669e71331SBayard Bell 			rw_exit(&ipss->ipsec_alg_lock);
3507f4b3ec61Sdh155122 			esp1dbg(espstack, ("Couldn't find encr alg #%d.\n",
35087c478bd9Sstevel@tonic-gate 			    assoc->sadb_sa_encrypt));
35097c478bd9Sstevel@tonic-gate 			*diagnostic = SADB_X_DIAGNOSTIC_BAD_EALG;
35107c478bd9Sstevel@tonic-gate 			return (EINVAL);
35117c478bd9Sstevel@tonic-gate 		}
35127c478bd9Sstevel@tonic-gate 
351346c444baSmarkfen 		/*
351446c444baSmarkfen 		 * Sanity check key sizes. If the encryption algorithm is
351546c444baSmarkfen 		 * SADB_EALG_NULL but the encryption key is NOT
351646c444baSmarkfen 		 * NULL then complain.
3517628b0c67SMark Fenwick 		 *
3518628b0c67SMark Fenwick 		 * The keying material includes salt bits if required by
3519628b0c67SMark Fenwick 		 * algorithm and optionally the Initial IV, check the
3520628b0c67SMark Fenwick 		 * length of whats left.
352146c444baSmarkfen 		 */
3522628b0c67SMark Fenwick 		keybits = ekey->sadb_key_bits;
3523628b0c67SMark Fenwick 		keybits -= ekey->sadb_key_reserved;
3524628b0c67SMark Fenwick 		keybits -= SADB_8TO1(ealg->alg_saltlen);
352546c444baSmarkfen 		if ((assoc->sadb_sa_encrypt == SADB_EALG_NULL) ||
3526628b0c67SMark Fenwick 		    (!ipsec_valid_key_size(keybits, ealg))) {
352769e71331SBayard Bell 			rw_exit(&ipss->ipsec_alg_lock);
35287c478bd9Sstevel@tonic-gate 			*diagnostic = SADB_X_DIAGNOSTIC_BAD_EKEYBITS;
35297c478bd9Sstevel@tonic-gate 			return (EINVAL);
35307c478bd9Sstevel@tonic-gate 		}
353146c444baSmarkfen 		ASSERT(ealg->alg_mech_type != CRYPTO_MECHANISM_INVALID);
35327c478bd9Sstevel@tonic-gate 
35337c478bd9Sstevel@tonic-gate 		/* check key */
35347c478bd9Sstevel@tonic-gate 		if (ipsec_check_key(ealg->alg_mech_type, ekey, B_FALSE,
35357c478bd9Sstevel@tonic-gate 		    diagnostic) != 0) {
353669e71331SBayard Bell 			rw_exit(&ipss->ipsec_alg_lock);
35377c478bd9Sstevel@tonic-gate 			return (EINVAL);
35387c478bd9Sstevel@tonic-gate 		}
35397c478bd9Sstevel@tonic-gate 	}
354069e71331SBayard Bell 	rw_exit(&ipss->ipsec_alg_lock);
35417c478bd9Sstevel@tonic-gate 
35428810c16bSdanmcd 	return (esp_add_sa_finish(mp, (sadb_msg_t *)mp->b_cont->b_rptr, ksi,
3543f4b3ec61Sdh155122 	    diagnostic, espstack));
35447c478bd9Sstevel@tonic-gate }
35457c478bd9Sstevel@tonic-gate 
35467c478bd9Sstevel@tonic-gate /*
35477c478bd9Sstevel@tonic-gate  * Update a security association.  Updates come in two varieties.  The first
35487c478bd9Sstevel@tonic-gate  * is an update of lifetimes on a non-larval SA.  The second is an update of
35497c478bd9Sstevel@tonic-gate  * a larval SA, which ends up looking a lot more like an add.
35507c478bd9Sstevel@tonic-gate  */
35517c478bd9Sstevel@tonic-gate static int
3552f4b3ec61Sdh155122 esp_update_sa(mblk_t *mp, keysock_in_t *ksi, int *diagnostic,
355338d95a78Smarkfen     ipsecesp_stack_t *espstack, uint8_t sadb_msg_type)
35547c478bd9Sstevel@tonic-gate {
35559c2c14abSThejaswini Singarajipura 	sadb_sa_t *assoc = (sadb_sa_t *)ksi->ks_in_extv[SADB_EXT_SA];
35569c2c14abSThejaswini Singarajipura 	mblk_t    *buf_pkt;
35579c2c14abSThejaswini Singarajipura 	int rcode;
35589c2c14abSThejaswini Singarajipura 
35597c478bd9Sstevel@tonic-gate 	sadb_address_t *dstext =
35607c478bd9Sstevel@tonic-gate 	    (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_DST];
35617c478bd9Sstevel@tonic-gate 
35627c478bd9Sstevel@tonic-gate 	if (dstext == NULL) {
35637c478bd9Sstevel@tonic-gate 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_DST;
35647c478bd9Sstevel@tonic-gate 		return (EINVAL);
35657c478bd9Sstevel@tonic-gate 	}
35667c478bd9Sstevel@tonic-gate 
35679c2c14abSThejaswini Singarajipura 	rcode = sadb_update_sa(mp, ksi, &buf_pkt, &espstack->esp_sadb,
35689c2c14abSThejaswini Singarajipura 	    diagnostic, espstack->esp_pfkey_q, esp_add_sa,
35699c2c14abSThejaswini Singarajipura 	    espstack->ipsecesp_netstack, sadb_msg_type);
35709c2c14abSThejaswini Singarajipura 
35719c2c14abSThejaswini Singarajipura 	if ((assoc->sadb_sa_state != SADB_X_SASTATE_ACTIVE) ||
35729c2c14abSThejaswini Singarajipura 	    (rcode != 0)) {
35739c2c14abSThejaswini Singarajipura 		return (rcode);
35749c2c14abSThejaswini Singarajipura 	}
35759c2c14abSThejaswini Singarajipura 
357673184bc7SDan McDonald 	HANDLE_BUF_PKT(esp_taskq, espstack->ipsecesp_netstack->netstack_ipsec,
35779c2c14abSThejaswini Singarajipura 	    espstack->esp_dropper, buf_pkt);
35789c2c14abSThejaswini Singarajipura 
35799c2c14abSThejaswini Singarajipura 	return (rcode);
35807c478bd9Sstevel@tonic-gate }
35817c478bd9Sstevel@tonic-gate 
35825d3b8cb7SBill Sommerfeld /* XXX refactor me */
35837c478bd9Sstevel@tonic-gate /*
35847c478bd9Sstevel@tonic-gate  * Delete a security association.  This is REALLY likely to be code common to
35857c478bd9Sstevel@tonic-gate  * both AH and ESP.  Find the association, then unlink it.
35867c478bd9Sstevel@tonic-gate  */
35877c478bd9Sstevel@tonic-gate static int
3588f4b3ec61Sdh155122 esp_del_sa(mblk_t *mp, keysock_in_t *ksi, int *diagnostic,
358938d95a78Smarkfen     ipsecesp_stack_t *espstack, uint8_t sadb_msg_type)
35907c478bd9Sstevel@tonic-gate {
35917c478bd9Sstevel@tonic-gate 	sadb_sa_t *assoc = (sadb_sa_t *)ksi->ks_in_extv[SADB_EXT_SA];
35927c478bd9Sstevel@tonic-gate 	sadb_address_t *dstext =
35937c478bd9Sstevel@tonic-gate 	    (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_DST];
35947c478bd9Sstevel@tonic-gate 	sadb_address_t *srcext =
35957c478bd9Sstevel@tonic-gate 	    (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_SRC];
35967c478bd9Sstevel@tonic-gate 	struct sockaddr_in *sin;
35977c478bd9Sstevel@tonic-gate 
35987c478bd9Sstevel@tonic-gate 	if (assoc == NULL) {
35997c478bd9Sstevel@tonic-gate 		if (dstext != NULL) {
36007c478bd9Sstevel@tonic-gate 			sin = (struct sockaddr_in *)(dstext + 1);
36017c478bd9Sstevel@tonic-gate 		} else if (srcext != NULL) {
36027c478bd9Sstevel@tonic-gate 			sin = (struct sockaddr_in *)(srcext + 1);
36037c478bd9Sstevel@tonic-gate 		} else {
36047c478bd9Sstevel@tonic-gate 			*diagnostic = SADB_X_DIAGNOSTIC_MISSING_SA;
36057c478bd9Sstevel@tonic-gate 			return (EINVAL);
36067c478bd9Sstevel@tonic-gate 		}
36078810c16bSdanmcd 		return (sadb_purge_sa(mp, ksi,
3608f4b3ec61Sdh155122 		    (sin->sin_family == AF_INET6) ? &espstack->esp_sadb.s_v6 :
36095d3b8cb7SBill Sommerfeld 		    &espstack->esp_sadb.s_v4, diagnostic,
3610bd670b35SErik Nordmark 		    espstack->esp_pfkey_q));
36117c478bd9Sstevel@tonic-gate 	}
36127c478bd9Sstevel@tonic-gate 
361338d95a78Smarkfen 	return (sadb_delget_sa(mp, ksi, &espstack->esp_sadb, diagnostic,
361438d95a78Smarkfen 	    espstack->esp_pfkey_q, sadb_msg_type));
36157c478bd9Sstevel@tonic-gate }
36167c478bd9Sstevel@tonic-gate 
36175d3b8cb7SBill Sommerfeld /* XXX refactor me */
36187c478bd9Sstevel@tonic-gate /*
36197c478bd9Sstevel@tonic-gate  * Convert the entire contents of all of ESP's SA tables into PF_KEY SADB_DUMP
36207c478bd9Sstevel@tonic-gate  * messages.
36217c478bd9Sstevel@tonic-gate  */
36227c478bd9Sstevel@tonic-gate static void
3623f4b3ec61Sdh155122 esp_dump(mblk_t *mp, keysock_in_t *ksi, ipsecesp_stack_t *espstack)
36247c478bd9Sstevel@tonic-gate {
36257c478bd9Sstevel@tonic-gate 	int error;
36267c478bd9Sstevel@tonic-gate 	sadb_msg_t *samsg;
36277c478bd9Sstevel@tonic-gate 
36287c478bd9Sstevel@tonic-gate 	/*
36297c478bd9Sstevel@tonic-gate 	 * Dump each fanout, bailing if error is non-zero.
36307c478bd9Sstevel@tonic-gate 	 */
36317c478bd9Sstevel@tonic-gate 
36329c2c14abSThejaswini Singarajipura 	error = sadb_dump(espstack->esp_pfkey_q, mp, ksi,
3633f4b3ec61Sdh155122 	    &espstack->esp_sadb.s_v4);
36347c478bd9Sstevel@tonic-gate 	if (error != 0)
36357c478bd9Sstevel@tonic-gate 		goto bail;
36367c478bd9Sstevel@tonic-gate 
36379c2c14abSThejaswini Singarajipura 	error = sadb_dump(espstack->esp_pfkey_q, mp, ksi,
3638f4b3ec61Sdh155122 	    &espstack->esp_sadb.s_v6);
36397c478bd9Sstevel@tonic-gate bail:
36407c478bd9Sstevel@tonic-gate 	ASSERT(mp->b_cont != NULL);
36417c478bd9Sstevel@tonic-gate 	samsg = (sadb_msg_t *)mp->b_cont->b_rptr;
36427c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_errno = (uint8_t)error;
3643f4b3ec61Sdh155122 	sadb_pfkey_echo(espstack->esp_pfkey_q, mp,
3644f4b3ec61Sdh155122 	    (sadb_msg_t *)mp->b_cont->b_rptr, ksi, NULL);
36457c478bd9Sstevel@tonic-gate }
36467c478bd9Sstevel@tonic-gate 
36477c478bd9Sstevel@tonic-gate /*
36488810c16bSdanmcd  * First-cut reality check for an inbound PF_KEY message.
36498810c16bSdanmcd  */
36508810c16bSdanmcd static boolean_t
3651f4b3ec61Sdh155122 esp_pfkey_reality_failures(mblk_t *mp, keysock_in_t *ksi,
3652f4b3ec61Sdh155122     ipsecesp_stack_t *espstack)
36538810c16bSdanmcd {
36548810c16bSdanmcd 	int diagnostic;
36558810c16bSdanmcd 
36568810c16bSdanmcd 	if (ksi->ks_in_extv[SADB_EXT_PROPOSAL] != NULL) {
36578810c16bSdanmcd 		diagnostic = SADB_X_DIAGNOSTIC_PROP_PRESENT;
36588810c16bSdanmcd 		goto badmsg;
36598810c16bSdanmcd 	}
36608810c16bSdanmcd 	if (ksi->ks_in_extv[SADB_EXT_SUPPORTED_AUTH] != NULL ||
36618810c16bSdanmcd 	    ksi->ks_in_extv[SADB_EXT_SUPPORTED_ENCRYPT] != NULL) {
36628810c16bSdanmcd 		diagnostic = SADB_X_DIAGNOSTIC_SUPP_PRESENT;
36638810c16bSdanmcd 		goto badmsg;
36648810c16bSdanmcd 	}
36658810c16bSdanmcd 	return (B_FALSE);	/* False ==> no failures */
36668810c16bSdanmcd 
36678810c16bSdanmcd badmsg:
3668f4b3ec61Sdh155122 	sadb_pfkey_error(espstack->esp_pfkey_q, mp, EINVAL, diagnostic,
36698810c16bSdanmcd 	    ksi->ks_in_serial);
36708810c16bSdanmcd 	return (B_TRUE);	/* True ==> failures */
36718810c16bSdanmcd }
36728810c16bSdanmcd 
36738810c16bSdanmcd /*
36747c478bd9Sstevel@tonic-gate  * ESP parsing of PF_KEY messages.  Keysock did most of the really silly
36757c478bd9Sstevel@tonic-gate  * error cases.  What I receive is a fully-formed, syntactically legal
36767c478bd9Sstevel@tonic-gate  * PF_KEY message.  I then need to check semantics...
36777c478bd9Sstevel@tonic-gate  *
36787c478bd9Sstevel@tonic-gate  * This code may become common to AH and ESP.  Stay tuned.
36797c478bd9Sstevel@tonic-gate  *
36807c478bd9Sstevel@tonic-gate  * I also make the assumption that db_ref's are cool.  If this assumption
36817c478bd9Sstevel@tonic-gate  * is wrong, this means that someone other than keysock or me has been
36827c478bd9Sstevel@tonic-gate  * mucking with PF_KEY messages.
36837c478bd9Sstevel@tonic-gate  */
36847c478bd9Sstevel@tonic-gate static void
3685f4b3ec61Sdh155122 esp_parse_pfkey(mblk_t *mp, ipsecesp_stack_t *espstack)
36867c478bd9Sstevel@tonic-gate {
36877c478bd9Sstevel@tonic-gate 	mblk_t *msg = mp->b_cont;
36887c478bd9Sstevel@tonic-gate 	sadb_msg_t *samsg;
36897c478bd9Sstevel@tonic-gate 	keysock_in_t *ksi;
36907c478bd9Sstevel@tonic-gate 	int error;
36917c478bd9Sstevel@tonic-gate 	int diagnostic = SADB_X_DIAGNOSTIC_NONE;
36927c478bd9Sstevel@tonic-gate 
36937c478bd9Sstevel@tonic-gate 	ASSERT(msg != NULL);
3694f4b3ec61Sdh155122 
36957c478bd9Sstevel@tonic-gate 	samsg = (sadb_msg_t *)msg->b_rptr;
36967c478bd9Sstevel@tonic-gate 	ksi = (keysock_in_t *)mp->b_rptr;
36977c478bd9Sstevel@tonic-gate 
36987c478bd9Sstevel@tonic-gate 	/*
36997c478bd9Sstevel@tonic-gate 	 * If applicable, convert unspecified AF_INET6 to unspecified
37008810c16bSdanmcd 	 * AF_INET.  And do other address reality checks.
37017c478bd9Sstevel@tonic-gate 	 */
3702f4b3ec61Sdh155122 	if (!sadb_addrfix(ksi, espstack->esp_pfkey_q, mp,
3703f4b3ec61Sdh155122 	    espstack->ipsecesp_netstack) ||
3704f4b3ec61Sdh155122 	    esp_pfkey_reality_failures(mp, ksi, espstack)) {
37058810c16bSdanmcd 		return;
37068810c16bSdanmcd 	}
37077c478bd9Sstevel@tonic-gate 
37087c478bd9Sstevel@tonic-gate 	switch (samsg->sadb_msg_type) {
37097c478bd9Sstevel@tonic-gate 	case SADB_ADD:
3710f4b3ec61Sdh155122 		error = esp_add_sa(mp, ksi, &diagnostic,
3711f4b3ec61Sdh155122 		    espstack->ipsecesp_netstack);
37127c478bd9Sstevel@tonic-gate 		if (error != 0) {
3713f4b3ec61Sdh155122 			sadb_pfkey_error(espstack->esp_pfkey_q, mp, error,
3714f4b3ec61Sdh155122 			    diagnostic, ksi->ks_in_serial);
37157c478bd9Sstevel@tonic-gate 		}
37167c478bd9Sstevel@tonic-gate 		/* else esp_add_sa() took care of things. */
37177c478bd9Sstevel@tonic-gate 		break;
37187c478bd9Sstevel@tonic-gate 	case SADB_DELETE:
371938d95a78Smarkfen 	case SADB_X_DELPAIR:
37209c2c14abSThejaswini Singarajipura 	case SADB_X_DELPAIR_STATE:
372138d95a78Smarkfen 		error = esp_del_sa(mp, ksi, &diagnostic, espstack,
372238d95a78Smarkfen 		    samsg->sadb_msg_type);
37237c478bd9Sstevel@tonic-gate 		if (error != 0) {
3724f4b3ec61Sdh155122 			sadb_pfkey_error(espstack->esp_pfkey_q, mp, error,
3725f4b3ec61Sdh155122 			    diagnostic, ksi->ks_in_serial);
37267c478bd9Sstevel@tonic-gate 		}
37277c478bd9Sstevel@tonic-gate 		/* Else esp_del_sa() took care of things. */
37287c478bd9Sstevel@tonic-gate 		break;
37297c478bd9Sstevel@tonic-gate 	case SADB_GET:
373038d95a78Smarkfen 		error = sadb_delget_sa(mp, ksi, &espstack->esp_sadb,
373138d95a78Smarkfen 		    &diagnostic, espstack->esp_pfkey_q, samsg->sadb_msg_type);
37327c478bd9Sstevel@tonic-gate 		if (error != 0) {
3733f4b3ec61Sdh155122 			sadb_pfkey_error(espstack->esp_pfkey_q, mp, error,
3734f4b3ec61Sdh155122 			    diagnostic, ksi->ks_in_serial);
37357c478bd9Sstevel@tonic-gate 		}
37367c478bd9Sstevel@tonic-gate 		/* Else sadb_get_sa() took care of things. */
37377c478bd9Sstevel@tonic-gate 		break;
37387c478bd9Sstevel@tonic-gate 	case SADB_FLUSH:
3739f4b3ec61Sdh155122 		sadbp_flush(&espstack->esp_sadb, espstack->ipsecesp_netstack);
3740f4b3ec61Sdh155122 		sadb_pfkey_echo(espstack->esp_pfkey_q, mp, samsg, ksi, NULL);
37417c478bd9Sstevel@tonic-gate 		break;
37427c478bd9Sstevel@tonic-gate 	case SADB_REGISTER:
37437c478bd9Sstevel@tonic-gate 		/*
37447c478bd9Sstevel@tonic-gate 		 * Hmmm, let's do it!  Check for extensions (there should
37457c478bd9Sstevel@tonic-gate 		 * be none), extract the fields, call esp_register_out(),
37467c478bd9Sstevel@tonic-gate 		 * then either free or report an error.
37477c478bd9Sstevel@tonic-gate 		 *
37487c478bd9Sstevel@tonic-gate 		 * Keysock takes care of the PF_KEY bookkeeping for this.
37497c478bd9Sstevel@tonic-gate 		 */
37507c478bd9Sstevel@tonic-gate 		if (esp_register_out(samsg->sadb_msg_seq, samsg->sadb_msg_pid,
3751bd670b35SErik Nordmark 		    ksi->ks_in_serial, espstack, msg_getcred(mp, NULL))) {
37527c478bd9Sstevel@tonic-gate 			freemsg(mp);
37537c478bd9Sstevel@tonic-gate 		} else {
37547c478bd9Sstevel@tonic-gate 			/*
37557c478bd9Sstevel@tonic-gate 			 * Only way this path hits is if there is a memory
37567c478bd9Sstevel@tonic-gate 			 * failure.  It will not return B_FALSE because of
37577c478bd9Sstevel@tonic-gate 			 * lack of esp_pfkey_q if I am in wput().
37587c478bd9Sstevel@tonic-gate 			 */
3759f4b3ec61Sdh155122 			sadb_pfkey_error(espstack->esp_pfkey_q, mp, ENOMEM,
3760f4b3ec61Sdh155122 			    diagnostic, ksi->ks_in_serial);
37617c478bd9Sstevel@tonic-gate 		}
37627c478bd9Sstevel@tonic-gate 		break;
37637c478bd9Sstevel@tonic-gate 	case SADB_UPDATE:
376438d95a78Smarkfen 	case SADB_X_UPDATEPAIR:
37657c478bd9Sstevel@tonic-gate 		/*
37667c478bd9Sstevel@tonic-gate 		 * Find a larval, if not there, find a full one and get
37677c478bd9Sstevel@tonic-gate 		 * strict.
37687c478bd9Sstevel@tonic-gate 		 */
376938d95a78Smarkfen 		error = esp_update_sa(mp, ksi, &diagnostic, espstack,
377038d95a78Smarkfen 		    samsg->sadb_msg_type);
37717c478bd9Sstevel@tonic-gate 		if (error != 0) {
3772f4b3ec61Sdh155122 			sadb_pfkey_error(espstack->esp_pfkey_q, mp, error,
3773f4b3ec61Sdh155122 			    diagnostic, ksi->ks_in_serial);
37747c478bd9Sstevel@tonic-gate 		}
37757c478bd9Sstevel@tonic-gate 		/* else esp_update_sa() took care of things. */
37767c478bd9Sstevel@tonic-gate 		break;
37777c478bd9Sstevel@tonic-gate 	case SADB_GETSPI:
37787c478bd9Sstevel@tonic-gate 		/*
37797c478bd9Sstevel@tonic-gate 		 * Reserve a new larval entry.
37807c478bd9Sstevel@tonic-gate 		 */
3781f4b3ec61Sdh155122 		esp_getspi(mp, ksi, espstack);
37827c478bd9Sstevel@tonic-gate 		break;
37837c478bd9Sstevel@tonic-gate 	case SADB_ACQUIRE:
37847c478bd9Sstevel@tonic-gate 		/*
37857c478bd9Sstevel@tonic-gate 		 * Find larval and/or ACQUIRE record and kill it (them), I'm
37867c478bd9Sstevel@tonic-gate 		 * most likely an error.  Inbound ACQUIRE messages should only
37877c478bd9Sstevel@tonic-gate 		 * have the base header.
37887c478bd9Sstevel@tonic-gate 		 */
3789f4b3ec61Sdh155122 		sadb_in_acquire(samsg, &espstack->esp_sadb,
3790f4b3ec61Sdh155122 		    espstack->esp_pfkey_q, espstack->ipsecesp_netstack);
37917c478bd9Sstevel@tonic-gate 		freemsg(mp);
37927c478bd9Sstevel@tonic-gate 		break;
37937c478bd9Sstevel@tonic-gate 	case SADB_DUMP:
37947c478bd9Sstevel@tonic-gate 		/*
37957c478bd9Sstevel@tonic-gate 		 * Dump all entries.
37967c478bd9Sstevel@tonic-gate 		 */
3797f4b3ec61Sdh155122 		esp_dump(mp, ksi, espstack);
37987c478bd9Sstevel@tonic-gate 		/* esp_dump will take care of the return message, etc. */
37997c478bd9Sstevel@tonic-gate 		break;
38007c478bd9Sstevel@tonic-gate 	case SADB_EXPIRE:
38017c478bd9Sstevel@tonic-gate 		/* Should never reach me. */
3802f4b3ec61Sdh155122 		sadb_pfkey_error(espstack->esp_pfkey_q, mp, EOPNOTSUPP,
3803f4b3ec61Sdh155122 		    diagnostic, ksi->ks_in_serial);
38047c478bd9Sstevel@tonic-gate 		break;
38057c478bd9Sstevel@tonic-gate 	default:
3806f4b3ec61Sdh155122 		sadb_pfkey_error(espstack->esp_pfkey_q, mp, EINVAL,
38077c478bd9Sstevel@tonic-gate 		    SADB_X_DIAGNOSTIC_UNKNOWN_MSG, ksi->ks_in_serial);
38087c478bd9Sstevel@tonic-gate 		break;
38097c478bd9Sstevel@tonic-gate 	}
38107c478bd9Sstevel@tonic-gate }
38117c478bd9Sstevel@tonic-gate 
38127c478bd9Sstevel@tonic-gate /*
38137c478bd9Sstevel@tonic-gate  * Handle case where PF_KEY says it can't find a keysock for one of my
38147c478bd9Sstevel@tonic-gate  * ACQUIRE messages.
38157c478bd9Sstevel@tonic-gate  */
38167c478bd9Sstevel@tonic-gate static void
3817f4b3ec61Sdh155122 esp_keysock_no_socket(mblk_t *mp, ipsecesp_stack_t *espstack)
38187c478bd9Sstevel@tonic-gate {
38197c478bd9Sstevel@tonic-gate 	sadb_msg_t *samsg;
38207c478bd9Sstevel@tonic-gate 	keysock_out_err_t *kse = (keysock_out_err_t *)mp->b_rptr;
38217c478bd9Sstevel@tonic-gate 
38227c478bd9Sstevel@tonic-gate 	if (mp->b_cont == NULL) {
38237c478bd9Sstevel@tonic-gate 		freemsg(mp);
38247c478bd9Sstevel@tonic-gate 		return;
38257c478bd9Sstevel@tonic-gate 	}
38267c478bd9Sstevel@tonic-gate 	samsg = (sadb_msg_t *)mp->b_cont->b_rptr;
38277c478bd9Sstevel@tonic-gate 
38287c478bd9Sstevel@tonic-gate 	/*
38297c478bd9Sstevel@tonic-gate 	 * If keysock can't find any registered, delete the acquire record
38307c478bd9Sstevel@tonic-gate 	 * immediately, and handle errors.
38317c478bd9Sstevel@tonic-gate 	 */
38327c478bd9Sstevel@tonic-gate 	if (samsg->sadb_msg_type == SADB_ACQUIRE) {
38337c478bd9Sstevel@tonic-gate 		samsg->sadb_msg_errno = kse->ks_err_errno;
38347c478bd9Sstevel@tonic-gate 		samsg->sadb_msg_len = SADB_8TO64(sizeof (*samsg));
38357c478bd9Sstevel@tonic-gate 		/*
3836bd670b35SErik Nordmark 		 * Use the write-side of the esp_pfkey_q
38377c478bd9Sstevel@tonic-gate 		 */
3838f4b3ec61Sdh155122 		sadb_in_acquire(samsg, &espstack->esp_sadb,
3839f4b3ec61Sdh155122 		    WR(espstack->esp_pfkey_q), espstack->ipsecesp_netstack);
38407c478bd9Sstevel@tonic-gate 	}
38417c478bd9Sstevel@tonic-gate 
38427c478bd9Sstevel@tonic-gate 	freemsg(mp);
38437c478bd9Sstevel@tonic-gate }
38447c478bd9Sstevel@tonic-gate 
38457c478bd9Sstevel@tonic-gate /*
38469c451ec7SToomas Soome  * ESP module read put routine.
38479c451ec7SToomas Soome  */
38489c451ec7SToomas Soome static int
38499c451ec7SToomas Soome ipsecesp_rput(queue_t *q, mblk_t *mp)
38509c451ec7SToomas Soome {
38519c451ec7SToomas Soome 	putnext(q, mp);
38529c451ec7SToomas Soome 	return (0);
38539c451ec7SToomas Soome }
38549c451ec7SToomas Soome 
38559c451ec7SToomas Soome /*
38567c478bd9Sstevel@tonic-gate  * ESP module write put routine.
38577c478bd9Sstevel@tonic-gate  */
38589c451ec7SToomas Soome static int
38597c478bd9Sstevel@tonic-gate ipsecesp_wput(queue_t *q, mblk_t *mp)
38607c478bd9Sstevel@tonic-gate {
38617c478bd9Sstevel@tonic-gate 	ipsec_info_t *ii;
38627c478bd9Sstevel@tonic-gate 	struct iocblk *iocp;
3863f4b3ec61Sdh155122 	ipsecesp_stack_t	*espstack = (ipsecesp_stack_t *)q->q_ptr;
38647c478bd9Sstevel@tonic-gate 
3865f4b3ec61Sdh155122 	esp3dbg(espstack, ("In esp_wput().\n"));
38667c478bd9Sstevel@tonic-gate 
38677c478bd9Sstevel@tonic-gate 	/* NOTE: Each case must take care of freeing or passing mp. */
38687c478bd9Sstevel@tonic-gate 	switch (mp->b_datap->db_type) {
38697c478bd9Sstevel@tonic-gate 	case M_CTL:
38707c478bd9Sstevel@tonic-gate 		if ((mp->b_wptr - mp->b_rptr) < sizeof (ipsec_info_t)) {
38717c478bd9Sstevel@tonic-gate 			/* Not big enough message. */
38727c478bd9Sstevel@tonic-gate 			freemsg(mp);
38737c478bd9Sstevel@tonic-gate 			break;
38747c478bd9Sstevel@tonic-gate 		}
38757c478bd9Sstevel@tonic-gate 		ii = (ipsec_info_t *)mp->b_rptr;
38767c478bd9Sstevel@tonic-gate 
38777c478bd9Sstevel@tonic-gate 		switch (ii->ipsec_info_type) {
38787c478bd9Sstevel@tonic-gate 		case KEYSOCK_OUT_ERR:
3879f4b3ec61Sdh155122 			esp1dbg(espstack, ("Got KEYSOCK_OUT_ERR message.\n"));
3880f4b3ec61Sdh155122 			esp_keysock_no_socket(mp, espstack);
38817c478bd9Sstevel@tonic-gate 			break;
38827c478bd9Sstevel@tonic-gate 		case KEYSOCK_IN:
3883f4b3ec61Sdh155122 			ESP_BUMP_STAT(espstack, keysock_in);
3884f4b3ec61Sdh155122 			esp3dbg(espstack, ("Got KEYSOCK_IN message.\n"));
38857c478bd9Sstevel@tonic-gate 
38868810c16bSdanmcd 			/* Parse the message. */
3887f4b3ec61Sdh155122 			esp_parse_pfkey(mp, espstack);
38887c478bd9Sstevel@tonic-gate 			break;
38897c478bd9Sstevel@tonic-gate 		case KEYSOCK_HELLO:
3890f4b3ec61Sdh155122 			sadb_keysock_hello(&espstack->esp_pfkey_q, q, mp,
3891f4b3ec61Sdh155122 			    esp_ager, (void *)espstack, &espstack->esp_event,
3892f4b3ec61Sdh155122 			    SADB_SATYPE_ESP);
38937c478bd9Sstevel@tonic-gate 			break;
38947c478bd9Sstevel@tonic-gate 		default:
3895f4b3ec61Sdh155122 			esp2dbg(espstack, ("Got M_CTL from above of 0x%x.\n",
38967c478bd9Sstevel@tonic-gate 			    ii->ipsec_info_type));
38977c478bd9Sstevel@tonic-gate 			freemsg(mp);
38987c478bd9Sstevel@tonic-gate 			break;
38997c478bd9Sstevel@tonic-gate 		}
39007c478bd9Sstevel@tonic-gate 		break;
39017c478bd9Sstevel@tonic-gate 	case M_IOCTL:
39027c478bd9Sstevel@tonic-gate 		iocp = (struct iocblk *)mp->b_rptr;
39037c478bd9Sstevel@tonic-gate 		switch (iocp->ioc_cmd) {
39047c478bd9Sstevel@tonic-gate 		case ND_SET:
39057c478bd9Sstevel@tonic-gate 		case ND_GET:
3906f4b3ec61Sdh155122 			if (nd_getset(q, espstack->ipsecesp_g_nd, mp)) {
39077c478bd9Sstevel@tonic-gate 				qreply(q, mp);
39089c451ec7SToomas Soome 				return (0);
39097c478bd9Sstevel@tonic-gate 			} else {
39107c478bd9Sstevel@tonic-gate 				iocp->ioc_error = ENOENT;
39117c478bd9Sstevel@tonic-gate 			}
39127c478bd9Sstevel@tonic-gate 			/* FALLTHRU */
39137c478bd9Sstevel@tonic-gate 		default:
39147c478bd9Sstevel@tonic-gate 			/* We really don't support any other ioctls, do we? */
39157c478bd9Sstevel@tonic-gate 
39167c478bd9Sstevel@tonic-gate 			/* Return EINVAL */
39177c478bd9Sstevel@tonic-gate 			if (iocp->ioc_error != ENOENT)
39187c478bd9Sstevel@tonic-gate 				iocp->ioc_error = EINVAL;
39197c478bd9Sstevel@tonic-gate 			iocp->ioc_count = 0;
39207c478bd9Sstevel@tonic-gate 			mp->b_datap->db_type = M_IOCACK;
39217c478bd9Sstevel@tonic-gate 			qreply(q, mp);
39229c451ec7SToomas Soome 			return (0);
39237c478bd9Sstevel@tonic-gate 		}
39247c478bd9Sstevel@tonic-gate 	default:
3925f4b3ec61Sdh155122 		esp3dbg(espstack,
3926f4b3ec61Sdh155122 		    ("Got default message, type %d, passing to IP.\n",
39277c478bd9Sstevel@tonic-gate 		    mp->b_datap->db_type));
39287c478bd9Sstevel@tonic-gate 		putnext(q, mp);
39297c478bd9Sstevel@tonic-gate 	}
39309c451ec7SToomas Soome 	return (0);
39317c478bd9Sstevel@tonic-gate }
39327c478bd9Sstevel@tonic-gate 
39337c478bd9Sstevel@tonic-gate /*
39347c478bd9Sstevel@tonic-gate  * Wrapper to allow IP to trigger an ESP association failure message
39357c478bd9Sstevel@tonic-gate  * during inbound SA selection.
39367c478bd9Sstevel@tonic-gate  */
39377c478bd9Sstevel@tonic-gate void
39387c478bd9Sstevel@tonic-gate ipsecesp_in_assocfailure(mblk_t *mp, char level, ushort_t sl, char *fmt,
3939bd670b35SErik Nordmark     uint32_t spi, void *addr, int af, ip_recv_attr_t *ira)
39407c478bd9Sstevel@tonic-gate {
3941bd670b35SErik Nordmark 	netstack_t	*ns = ira->ira_ill->ill_ipst->ips_netstack;
3942bd670b35SErik Nordmark 	ipsecesp_stack_t *espstack = ns->netstack_ipsecesp;
3943bd670b35SErik Nordmark 	ipsec_stack_t	*ipss = ns->netstack_ipsec;
3944f4b3ec61Sdh155122 
3945f4b3ec61Sdh155122 	if (espstack->ipsecesp_log_unknown_spi) {
39467c478bd9Sstevel@tonic-gate 		ipsec_assocfailure(info.mi_idnum, 0, level, sl, fmt, spi,
3947f4b3ec61Sdh155122 		    addr, af, espstack->ipsecesp_netstack);
39487c478bd9Sstevel@tonic-gate 	}
39497c478bd9Sstevel@tonic-gate 
3950bd670b35SErik Nordmark 	ip_drop_packet(mp, B_TRUE, ira->ira_ill,
3951f4b3ec61Sdh155122 	    DROPPER(ipss, ipds_esp_no_sa),
3952f4b3ec61Sdh155122 	    &espstack->esp_dropper);
39537c478bd9Sstevel@tonic-gate }
39547c478bd9Sstevel@tonic-gate 
39557c478bd9Sstevel@tonic-gate /*
39567c478bd9Sstevel@tonic-gate  * Initialize the ESP input and output processing functions.
39577c478bd9Sstevel@tonic-gate  */
39587c478bd9Sstevel@tonic-gate void
39597c478bd9Sstevel@tonic-gate ipsecesp_init_funcs(ipsa_t *sa)
39607c478bd9Sstevel@tonic-gate {
39617c478bd9Sstevel@tonic-gate 	if (sa->ipsa_output_func == NULL)
39627c478bd9Sstevel@tonic-gate 		sa->ipsa_output_func = esp_outbound;
39637c478bd9Sstevel@tonic-gate 	if (sa->ipsa_input_func == NULL)
39647c478bd9Sstevel@tonic-gate 		sa->ipsa_input_func = esp_inbound;
39657c478bd9Sstevel@tonic-gate }
3966