xref: /titanic_51/usr/src/uts/common/inet/ip/ipsecah.c (revision 1a5e258f5471356ca102c7176637cdce45bac147)
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
5bffb04cfSmarkfen  * Common Development and Distribution License (the "License").
6bffb04cfSmarkfen  * 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.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate #include <sys/types.h>
277c478bd9Sstevel@tonic-gate #include <sys/stream.h>
287c478bd9Sstevel@tonic-gate #include <sys/stropts.h>
297c478bd9Sstevel@tonic-gate #include <sys/errno.h>
307c478bd9Sstevel@tonic-gate #include <sys/strlog.h>
317c478bd9Sstevel@tonic-gate #include <sys/tihdr.h>
327c478bd9Sstevel@tonic-gate #include <sys/socket.h>
337c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
347c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
359c2c14abSThejaswini Singarajipura #include <sys/mkdev.h>
367c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
37f4b3ec61Sdh155122 #include <sys/zone.h>
3832350c00Sdanmcd #include <sys/sysmacros.h>
397c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
407c478bd9Sstevel@tonic-gate #include <sys/vtrace.h>
417c478bd9Sstevel@tonic-gate #include <sys/debug.h>
427c478bd9Sstevel@tonic-gate #include <sys/atomic.h>
437c478bd9Sstevel@tonic-gate #include <sys/strsun.h>
447c478bd9Sstevel@tonic-gate #include <sys/random.h>
457c478bd9Sstevel@tonic-gate #include <netinet/in.h>
467c478bd9Sstevel@tonic-gate #include <net/if.h>
477c478bd9Sstevel@tonic-gate #include <netinet/ip6.h>
487c478bd9Sstevel@tonic-gate #include <netinet/icmp6.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/ip.h>
557c478bd9Sstevel@tonic-gate #include <inet/ip6.h>
567c478bd9Sstevel@tonic-gate #include <inet/nd.h>
57bd670b35SErik Nordmark #include <inet/ip_if.h>
58bd670b35SErik Nordmark #include <inet/ip_ndp.h>
597c478bd9Sstevel@tonic-gate #include <inet/ipsec_info.h>
607c478bd9Sstevel@tonic-gate #include <inet/ipsec_impl.h>
617c478bd9Sstevel@tonic-gate #include <inet/sadb.h>
627c478bd9Sstevel@tonic-gate #include <inet/ipsecah.h>
637c478bd9Sstevel@tonic-gate #include <inet/ipsec_impl.h>
647c478bd9Sstevel@tonic-gate #include <inet/ipdrop.h>
657c478bd9Sstevel@tonic-gate #include <sys/taskq.h>
667c478bd9Sstevel@tonic-gate #include <sys/policy.h>
677c478bd9Sstevel@tonic-gate #include <sys/strsun.h>
687c478bd9Sstevel@tonic-gate 
697c478bd9Sstevel@tonic-gate #include <sys/crypto/common.h>
707c478bd9Sstevel@tonic-gate #include <sys/crypto/api.h>
717c478bd9Sstevel@tonic-gate #include <sys/kstat.h>
7245916cd2Sjpk #include <sys/strsubr.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 ipsecah. These are loaded into
787c478bd9Sstevel@tonic-gate  * ipsecah_g_nd in ipsecah_init_nd.
797c478bd9Sstevel@tonic-gate  * All of these are alterable, within the min/max values given, at run time.
807c478bd9Sstevel@tonic-gate  */
81f4b3ec61Sdh155122 static	ipsecahparam_t	lcl_param_arr[] = {
827c478bd9Sstevel@tonic-gate 	/* min	max			value	name */
837c478bd9Sstevel@tonic-gate 	{ 0,	3,			0,	"ipsecah_debug"},
847c478bd9Sstevel@tonic-gate 	{ 125,	32000, SADB_AGE_INTERVAL_DEFAULT,	"ipsecah_age_interval"},
857c478bd9Sstevel@tonic-gate 	{ 1,	10,			1,	"ipsecah_reap_delay"},
867c478bd9Sstevel@tonic-gate 	{ 1,	SADB_MAX_REPLAY,	64,	"ipsecah_replay_size"},
877c478bd9Sstevel@tonic-gate 	{ 1,	300,			15,	"ipsecah_acquire_timeout"},
887c478bd9Sstevel@tonic-gate 	{ 1,	1800,			90,	"ipsecah_larval_timeout"},
897c478bd9Sstevel@tonic-gate 	/* Default lifetime values for ACQUIRE messages. */
907c478bd9Sstevel@tonic-gate 	{ 0,	0xffffffffU,		0,	"ipsecah_default_soft_bytes"},
917c478bd9Sstevel@tonic-gate 	{ 0,	0xffffffffU,		0,	"ipsecah_default_hard_bytes"},
927c478bd9Sstevel@tonic-gate 	{ 0,	0xffffffffU,		24000,	"ipsecah_default_soft_addtime"},
937c478bd9Sstevel@tonic-gate 	{ 0,	0xffffffffU,		28800,	"ipsecah_default_hard_addtime"},
947c478bd9Sstevel@tonic-gate 	{ 0,	0xffffffffU,		0,	"ipsecah_default_soft_usetime"},
957c478bd9Sstevel@tonic-gate 	{ 0,	0xffffffffU,		0,	"ipsecah_default_hard_usetime"},
967c478bd9Sstevel@tonic-gate 	{ 0,	1,			0,	"ipsecah_log_unknown_spi"},
977c478bd9Sstevel@tonic-gate };
98f4b3ec61Sdh155122 #define	ipsecah_debug			ipsecah_params[0].ipsecah_param_value
99f4b3ec61Sdh155122 #define	ipsecah_age_interval		ipsecah_params[1].ipsecah_param_value
100f4b3ec61Sdh155122 #define	ipsecah_age_int_max		ipsecah_params[1].ipsecah_param_max
101f4b3ec61Sdh155122 #define	ipsecah_reap_delay		ipsecah_params[2].ipsecah_param_value
102f4b3ec61Sdh155122 #define	ipsecah_replay_size		ipsecah_params[3].ipsecah_param_value
103f4b3ec61Sdh155122 #define	ipsecah_acquire_timeout		ipsecah_params[4].ipsecah_param_value
104f4b3ec61Sdh155122 #define	ipsecah_larval_timeout		ipsecah_params[5].ipsecah_param_value
105f4b3ec61Sdh155122 #define	ipsecah_default_soft_bytes	ipsecah_params[6].ipsecah_param_value
106f4b3ec61Sdh155122 #define	ipsecah_default_hard_bytes	ipsecah_params[7].ipsecah_param_value
107f4b3ec61Sdh155122 #define	ipsecah_default_soft_addtime	ipsecah_params[8].ipsecah_param_value
108f4b3ec61Sdh155122 #define	ipsecah_default_hard_addtime	ipsecah_params[9].ipsecah_param_value
109f4b3ec61Sdh155122 #define	ipsecah_default_soft_usetime	ipsecah_params[10].ipsecah_param_value
110f4b3ec61Sdh155122 #define	ipsecah_default_hard_usetime	ipsecah_params[11].ipsecah_param_value
111f4b3ec61Sdh155122 #define	ipsecah_log_unknown_spi		ipsecah_params[12].ipsecah_param_value
1127c478bd9Sstevel@tonic-gate 
1137c478bd9Sstevel@tonic-gate #define	ah0dbg(a)	printf a
1147c478bd9Sstevel@tonic-gate /* NOTE:  != 0 instead of > 0 so lint doesn't complain. */
115f4b3ec61Sdh155122 #define	ah1dbg(ahstack, a)	if (ahstack->ipsecah_debug != 0) printf a
116f4b3ec61Sdh155122 #define	ah2dbg(ahstack, a)	if (ahstack->ipsecah_debug > 1) printf a
117f4b3ec61Sdh155122 #define	ah3dbg(ahstack, a)	if (ahstack->ipsecah_debug > 2) printf a
1187c478bd9Sstevel@tonic-gate 
1197c478bd9Sstevel@tonic-gate /*
1207c478bd9Sstevel@tonic-gate  * XXX This is broken. Padding should be determined dynamically
1217c478bd9Sstevel@tonic-gate  * depending on the ICV size and IP version number so that the
1227c478bd9Sstevel@tonic-gate  * total AH header size is a multiple of 32 bits or 64 bits
1237c478bd9Sstevel@tonic-gate  * for V4 and V6 respectively. For 96bit ICVs we have no problems.
1247c478bd9Sstevel@tonic-gate  * Anything different from that, we need to fix our code.
1257c478bd9Sstevel@tonic-gate  */
1267c478bd9Sstevel@tonic-gate #define	IPV4_PADDING_ALIGN	0x04	/* Multiple of 32 bits */
1277c478bd9Sstevel@tonic-gate #define	IPV6_PADDING_ALIGN	0x04	/* Multiple of 32 bits */
1287c478bd9Sstevel@tonic-gate 
1297c478bd9Sstevel@tonic-gate /*
1307c478bd9Sstevel@tonic-gate  * Helper macro. Avoids a call to msgdsize if there is only one
1317c478bd9Sstevel@tonic-gate  * mblk in the chain.
1327c478bd9Sstevel@tonic-gate  */
1337c478bd9Sstevel@tonic-gate #define	AH_MSGSIZE(mp) ((mp)->b_cont != NULL ? msgdsize(mp) : MBLKL(mp))
1347c478bd9Sstevel@tonic-gate 
135f4b3ec61Sdh155122 
136bd670b35SErik Nordmark static mblk_t *ah_auth_out_done(mblk_t *, ip_xmit_attr_t *, ipsec_crypto_t *);
137bd670b35SErik Nordmark static mblk_t *ah_auth_in_done(mblk_t *, ip_recv_attr_t *, ipsec_crypto_t *);
1387c478bd9Sstevel@tonic-gate static mblk_t *ah_process_ip_options_v4(mblk_t *, ipsa_t *, int *, uint_t,
139f4b3ec61Sdh155122     boolean_t, ipsecah_stack_t *);
1407c478bd9Sstevel@tonic-gate static mblk_t *ah_process_ip_options_v6(mblk_t *, ipsa_t *, int *, uint_t,
141f4b3ec61Sdh155122     boolean_t, ipsecah_stack_t *);
142f4b3ec61Sdh155122 static void ah_getspi(mblk_t *, keysock_in_t *, ipsecah_stack_t *);
143bd670b35SErik Nordmark static void ah_inbound_restart(mblk_t *, ip_recv_attr_t *);
144bd670b35SErik Nordmark 
145bd670b35SErik Nordmark static mblk_t *ah_outbound(mblk_t *, ip_xmit_attr_t *);
146bd670b35SErik Nordmark static void ah_outbound_finish(mblk_t *, ip_xmit_attr_t *);
1477c478bd9Sstevel@tonic-gate 
1487c478bd9Sstevel@tonic-gate static int ipsecah_open(queue_t *, dev_t *, int, int, cred_t *);
1497c478bd9Sstevel@tonic-gate static int ipsecah_close(queue_t *);
1507c478bd9Sstevel@tonic-gate static void ipsecah_wput(queue_t *, mblk_t *);
151f4b3ec61Sdh155122 static void ah_send_acquire(ipsacq_t *, mblk_t *, netstack_t *);
1525d3b8cb7SBill Sommerfeld static boolean_t ah_register_out(uint32_t, uint32_t, uint_t, ipsecah_stack_t *,
153bd670b35SErik Nordmark     cred_t *);
154f4b3ec61Sdh155122 static void	*ipsecah_stack_init(netstackid_t stackid, netstack_t *ns);
155f4b3ec61Sdh155122 static void	ipsecah_stack_fini(netstackid_t stackid, void *arg);
156f4b3ec61Sdh155122 
157f4b3ec61Sdh155122 /* Setable in /etc/system */
158f4b3ec61Sdh155122 uint32_t ah_hash_size = IPSEC_DEFAULT_HASH_SIZE;
159f4b3ec61Sdh155122 
160f4b3ec61Sdh155122 static taskq_t *ah_taskq;
1617c478bd9Sstevel@tonic-gate 
1627c478bd9Sstevel@tonic-gate static struct module_info info = {
1637c478bd9Sstevel@tonic-gate 	5136, "ipsecah", 0, INFPSZ, 65536, 1024
1647c478bd9Sstevel@tonic-gate };
1657c478bd9Sstevel@tonic-gate 
1667c478bd9Sstevel@tonic-gate static struct qinit rinit = {
167bd670b35SErik Nordmark 	(pfi_t)putnext, NULL, ipsecah_open, ipsecah_close, NULL, &info,
1687c478bd9Sstevel@tonic-gate 	NULL
1697c478bd9Sstevel@tonic-gate };
1707c478bd9Sstevel@tonic-gate 
1717c478bd9Sstevel@tonic-gate static struct qinit winit = {
1727c478bd9Sstevel@tonic-gate 	(pfi_t)ipsecah_wput, NULL, ipsecah_open, ipsecah_close, NULL, &info,
1737c478bd9Sstevel@tonic-gate 	NULL
1747c478bd9Sstevel@tonic-gate };
1757c478bd9Sstevel@tonic-gate 
1767c478bd9Sstevel@tonic-gate struct streamtab ipsecahinfo = {
1777c478bd9Sstevel@tonic-gate 	&rinit, &winit, NULL, NULL
1787c478bd9Sstevel@tonic-gate };
1797c478bd9Sstevel@tonic-gate 
1807c478bd9Sstevel@tonic-gate static int ah_kstat_update(kstat_t *, int);
1817c478bd9Sstevel@tonic-gate 
182bffb04cfSmarkfen uint64_t ipsacq_maxpackets = IPSACQ_MAXPACKETS;
183bffb04cfSmarkfen 
1847c478bd9Sstevel@tonic-gate static boolean_t
185f4b3ec61Sdh155122 ah_kstat_init(ipsecah_stack_t *ahstack, netstackid_t stackid)
1867c478bd9Sstevel@tonic-gate {
187f4b3ec61Sdh155122 	ipsec_stack_t	*ipss = ahstack->ipsecah_netstack->netstack_ipsec;
1887c478bd9Sstevel@tonic-gate 
189f4b3ec61Sdh155122 	ahstack->ah_ksp = kstat_create_netstack("ipsecah", 0, "ah_stat", "net",
190f4b3ec61Sdh155122 	    KSTAT_TYPE_NAMED, sizeof (ah_kstats_t) / sizeof (kstat_named_t),
191f4b3ec61Sdh155122 	    KSTAT_FLAG_PERSISTENT, stackid);
1927c478bd9Sstevel@tonic-gate 
193f4b3ec61Sdh155122 	if (ahstack->ah_ksp == NULL || ahstack->ah_ksp->ks_data == NULL)
1947c478bd9Sstevel@tonic-gate 		return (B_FALSE);
1957c478bd9Sstevel@tonic-gate 
196f4b3ec61Sdh155122 	ahstack->ah_kstats = ahstack->ah_ksp->ks_data;
1977c478bd9Sstevel@tonic-gate 
198f4b3ec61Sdh155122 	ahstack->ah_ksp->ks_update = ah_kstat_update;
199f4b3ec61Sdh155122 	ahstack->ah_ksp->ks_private = (void *)(uintptr_t)stackid;
2007c478bd9Sstevel@tonic-gate 
2017c478bd9Sstevel@tonic-gate #define	K64 KSTAT_DATA_UINT64
202f4b3ec61Sdh155122 #define	KI(x) kstat_named_init(&(ahstack->ah_kstats->ah_stat_##x), #x, K64)
2037c478bd9Sstevel@tonic-gate 
2047c478bd9Sstevel@tonic-gate 	KI(num_aalgs);
2057c478bd9Sstevel@tonic-gate 	KI(good_auth);
2067c478bd9Sstevel@tonic-gate 	KI(bad_auth);
2077c478bd9Sstevel@tonic-gate 	KI(replay_failures);
2087c478bd9Sstevel@tonic-gate 	KI(replay_early_failures);
2097c478bd9Sstevel@tonic-gate 	KI(keysock_in);
2107c478bd9Sstevel@tonic-gate 	KI(out_requests);
2117c478bd9Sstevel@tonic-gate 	KI(acquire_requests);
2127c478bd9Sstevel@tonic-gate 	KI(bytes_expired);
2137c478bd9Sstevel@tonic-gate 	KI(out_discards);
2147c478bd9Sstevel@tonic-gate 	KI(crypto_sync);
2157c478bd9Sstevel@tonic-gate 	KI(crypto_async);
2167c478bd9Sstevel@tonic-gate 	KI(crypto_failures);
2177c478bd9Sstevel@tonic-gate 
2187c478bd9Sstevel@tonic-gate #undef KI
2197c478bd9Sstevel@tonic-gate #undef K64
2207c478bd9Sstevel@tonic-gate 
221f4b3ec61Sdh155122 	kstat_install(ahstack->ah_ksp);
222f4b3ec61Sdh155122 	IP_ACQUIRE_STAT(ipss, maxpackets, ipsacq_maxpackets);
2237c478bd9Sstevel@tonic-gate 	return (B_TRUE);
2247c478bd9Sstevel@tonic-gate }
2257c478bd9Sstevel@tonic-gate 
2267c478bd9Sstevel@tonic-gate static int
2277c478bd9Sstevel@tonic-gate ah_kstat_update(kstat_t *kp, int rw)
2287c478bd9Sstevel@tonic-gate {
2297c478bd9Sstevel@tonic-gate 	ah_kstats_t	*ekp;
230f4b3ec61Sdh155122 	netstackid_t	stackid = (netstackid_t)(uintptr_t)kp->ks_private;
231f4b3ec61Sdh155122 	netstack_t	*ns;
232f4b3ec61Sdh155122 	ipsec_stack_t	*ipss;
2337c478bd9Sstevel@tonic-gate 
2347c478bd9Sstevel@tonic-gate 	if ((kp == NULL) || (kp->ks_data == NULL))
2357c478bd9Sstevel@tonic-gate 		return (EIO);
2367c478bd9Sstevel@tonic-gate 
2377c478bd9Sstevel@tonic-gate 	if (rw == KSTAT_WRITE)
2387c478bd9Sstevel@tonic-gate 		return (EACCES);
2397c478bd9Sstevel@tonic-gate 
240f4b3ec61Sdh155122 	ns = netstack_find_by_stackid(stackid);
241f4b3ec61Sdh155122 	if (ns == NULL)
242f4b3ec61Sdh155122 		return (-1);
243f4b3ec61Sdh155122 	ipss = ns->netstack_ipsec;
244f4b3ec61Sdh155122 	if (ipss == NULL) {
245f4b3ec61Sdh155122 		netstack_rele(ns);
246f4b3ec61Sdh155122 		return (-1);
247f4b3ec61Sdh155122 	}
2487c478bd9Sstevel@tonic-gate 	ekp = (ah_kstats_t *)kp->ks_data;
2497c478bd9Sstevel@tonic-gate 
250f4b3ec61Sdh155122 	mutex_enter(&ipss->ipsec_alg_lock);
251f4b3ec61Sdh155122 	ekp->ah_stat_num_aalgs.value.ui64 = ipss->ipsec_nalgs[IPSEC_ALG_AUTH];
252f4b3ec61Sdh155122 	mutex_exit(&ipss->ipsec_alg_lock);
2537c478bd9Sstevel@tonic-gate 
254f4b3ec61Sdh155122 	netstack_rele(ns);
2557c478bd9Sstevel@tonic-gate 	return (0);
2567c478bd9Sstevel@tonic-gate }
2577c478bd9Sstevel@tonic-gate 
2587c478bd9Sstevel@tonic-gate /*
2597c478bd9Sstevel@tonic-gate  * Don't have to lock ipsec_age_interval, as only one thread will access it at
2607c478bd9Sstevel@tonic-gate  * a time, because I control the one function that does a qtimeout() on
2617c478bd9Sstevel@tonic-gate  * ah_pfkey_q.
2627c478bd9Sstevel@tonic-gate  */
2637c478bd9Sstevel@tonic-gate static void
264f4b3ec61Sdh155122 ah_ager(void *arg)
2657c478bd9Sstevel@tonic-gate {
266f4b3ec61Sdh155122 	ipsecah_stack_t *ahstack = (ipsecah_stack_t *)arg;
267f4b3ec61Sdh155122 	netstack_t	*ns = ahstack->ipsecah_netstack;
2687c478bd9Sstevel@tonic-gate 	hrtime_t begin = gethrtime();
2697c478bd9Sstevel@tonic-gate 
270f4b3ec61Sdh155122 	sadb_ager(&ahstack->ah_sadb.s_v4, ahstack->ah_pfkey_q,
271bd670b35SErik Nordmark 	    ahstack->ipsecah_reap_delay, ns);
272f4b3ec61Sdh155122 	sadb_ager(&ahstack->ah_sadb.s_v6, ahstack->ah_pfkey_q,
273bd670b35SErik Nordmark 	    ahstack->ipsecah_reap_delay, ns);
2747c478bd9Sstevel@tonic-gate 
275f4b3ec61Sdh155122 	ahstack->ah_event = sadb_retimeout(begin, ahstack->ah_pfkey_q,
276f4b3ec61Sdh155122 	    ah_ager, ahstack,
277f4b3ec61Sdh155122 	    &ahstack->ipsecah_age_interval, ahstack->ipsecah_age_int_max,
278f4b3ec61Sdh155122 	    info.mi_idnum);
2797c478bd9Sstevel@tonic-gate }
2807c478bd9Sstevel@tonic-gate 
2817c478bd9Sstevel@tonic-gate /*
2827c478bd9Sstevel@tonic-gate  * Get an AH NDD parameter.
2837c478bd9Sstevel@tonic-gate  */
2847c478bd9Sstevel@tonic-gate /* ARGSUSED */
2857c478bd9Sstevel@tonic-gate static int
2867c478bd9Sstevel@tonic-gate ipsecah_param_get(q, mp, cp, cr)
2877c478bd9Sstevel@tonic-gate 	queue_t	*q;
2887c478bd9Sstevel@tonic-gate 	mblk_t	*mp;
2897c478bd9Sstevel@tonic-gate 	caddr_t	cp;
2907c478bd9Sstevel@tonic-gate 	cred_t *cr;
2917c478bd9Sstevel@tonic-gate {
2927c478bd9Sstevel@tonic-gate 	ipsecahparam_t	*ipsecahpa = (ipsecahparam_t *)cp;
2937c478bd9Sstevel@tonic-gate 	uint_t value;
294f4b3ec61Sdh155122 	ipsecah_stack_t	*ahstack = (ipsecah_stack_t *)q->q_ptr;
2957c478bd9Sstevel@tonic-gate 
296f4b3ec61Sdh155122 	mutex_enter(&ahstack->ipsecah_param_lock);
2977c478bd9Sstevel@tonic-gate 	value = ipsecahpa->ipsecah_param_value;
298f4b3ec61Sdh155122 	mutex_exit(&ahstack->ipsecah_param_lock);
2997c478bd9Sstevel@tonic-gate 
3007c478bd9Sstevel@tonic-gate 	(void) mi_mpprintf(mp, "%u", value);
3017c478bd9Sstevel@tonic-gate 	return (0);
3027c478bd9Sstevel@tonic-gate }
3037c478bd9Sstevel@tonic-gate 
3047c478bd9Sstevel@tonic-gate /*
3057c478bd9Sstevel@tonic-gate  * This routine sets an NDD variable in a ipsecahparam_t structure.
3067c478bd9Sstevel@tonic-gate  */
3077c478bd9Sstevel@tonic-gate /* ARGSUSED */
3087c478bd9Sstevel@tonic-gate static int
3097c478bd9Sstevel@tonic-gate ipsecah_param_set(q, mp, value, cp, cr)
3107c478bd9Sstevel@tonic-gate 	queue_t	*q;
3117c478bd9Sstevel@tonic-gate 	mblk_t	*mp;
3127c478bd9Sstevel@tonic-gate 	char	*value;
3137c478bd9Sstevel@tonic-gate 	caddr_t	cp;
3147c478bd9Sstevel@tonic-gate 	cred_t *cr;
3157c478bd9Sstevel@tonic-gate {
3167c478bd9Sstevel@tonic-gate 	ulong_t	new_value;
3177c478bd9Sstevel@tonic-gate 	ipsecahparam_t	*ipsecahpa = (ipsecahparam_t *)cp;
318f4b3ec61Sdh155122 	ipsecah_stack_t	*ahstack = (ipsecah_stack_t *)q->q_ptr;
3197c478bd9Sstevel@tonic-gate 
3207c478bd9Sstevel@tonic-gate 	/*
3217c478bd9Sstevel@tonic-gate 	 * Fail the request if the new value does not lie within the
3227c478bd9Sstevel@tonic-gate 	 * required bounds.
3237c478bd9Sstevel@tonic-gate 	 */
3247c478bd9Sstevel@tonic-gate 	if (ddi_strtoul(value, NULL, 10, &new_value) != 0 ||
3257c478bd9Sstevel@tonic-gate 	    new_value < ipsecahpa->ipsecah_param_min ||
3267c478bd9Sstevel@tonic-gate 	    new_value > ipsecahpa->ipsecah_param_max) {
3277c478bd9Sstevel@tonic-gate 		    return (EINVAL);
3287c478bd9Sstevel@tonic-gate 	}
3297c478bd9Sstevel@tonic-gate 
3307c478bd9Sstevel@tonic-gate 	/* Set the new value */
331f4b3ec61Sdh155122 	mutex_enter(&ahstack->ipsecah_param_lock);
3327c478bd9Sstevel@tonic-gate 	ipsecahpa->ipsecah_param_value = new_value;
333f4b3ec61Sdh155122 	mutex_exit(&ahstack->ipsecah_param_lock);
3347c478bd9Sstevel@tonic-gate 	return (0);
3357c478bd9Sstevel@tonic-gate }
3367c478bd9Sstevel@tonic-gate 
3377c478bd9Sstevel@tonic-gate /*
3387c478bd9Sstevel@tonic-gate  * Using lifetime NDD variables, fill in an extended combination's
3397c478bd9Sstevel@tonic-gate  * lifetime information.
3407c478bd9Sstevel@tonic-gate  */
3417c478bd9Sstevel@tonic-gate void
342f4b3ec61Sdh155122 ipsecah_fill_defs(sadb_x_ecomb_t *ecomb, netstack_t *ns)
3437c478bd9Sstevel@tonic-gate {
344f4b3ec61Sdh155122 	ipsecah_stack_t	*ahstack = ns->netstack_ipsecah;
345f4b3ec61Sdh155122 
346f4b3ec61Sdh155122 	ecomb->sadb_x_ecomb_soft_bytes = ahstack->ipsecah_default_soft_bytes;
347f4b3ec61Sdh155122 	ecomb->sadb_x_ecomb_hard_bytes = ahstack->ipsecah_default_hard_bytes;
348f4b3ec61Sdh155122 	ecomb->sadb_x_ecomb_soft_addtime =
349f4b3ec61Sdh155122 	    ahstack->ipsecah_default_soft_addtime;
350f4b3ec61Sdh155122 	ecomb->sadb_x_ecomb_hard_addtime =
351f4b3ec61Sdh155122 	    ahstack->ipsecah_default_hard_addtime;
352f4b3ec61Sdh155122 	ecomb->sadb_x_ecomb_soft_usetime =
353f4b3ec61Sdh155122 	    ahstack->ipsecah_default_soft_usetime;
354f4b3ec61Sdh155122 	ecomb->sadb_x_ecomb_hard_usetime =
355f4b3ec61Sdh155122 	    ahstack->ipsecah_default_hard_usetime;
3567c478bd9Sstevel@tonic-gate }
3577c478bd9Sstevel@tonic-gate 
3587c478bd9Sstevel@tonic-gate /*
3597c478bd9Sstevel@tonic-gate  * Initialize things for AH at module load time.
3607c478bd9Sstevel@tonic-gate  */
3617c478bd9Sstevel@tonic-gate boolean_t
3627c478bd9Sstevel@tonic-gate ipsecah_ddi_init(void)
3637c478bd9Sstevel@tonic-gate {
3647c478bd9Sstevel@tonic-gate 	ah_taskq = taskq_create("ah_taskq", 1, minclsyspri,
3657c478bd9Sstevel@tonic-gate 	    IPSEC_TASKQ_MIN, IPSEC_TASKQ_MAX, 0);
3667c478bd9Sstevel@tonic-gate 
367f4b3ec61Sdh155122 	/*
368f4b3ec61Sdh155122 	 * We want to be informed each time a stack is created or
369f4b3ec61Sdh155122 	 * destroyed in the kernel, so we can maintain the
370f4b3ec61Sdh155122 	 * set of ipsecah_stack_t's.
371f4b3ec61Sdh155122 	 */
372f4b3ec61Sdh155122 	netstack_register(NS_IPSECAH, ipsecah_stack_init, NULL,
373f4b3ec61Sdh155122 	    ipsecah_stack_fini);
3747c478bd9Sstevel@tonic-gate 
3757c478bd9Sstevel@tonic-gate 	return (B_TRUE);
3767c478bd9Sstevel@tonic-gate }
3777c478bd9Sstevel@tonic-gate 
3787c478bd9Sstevel@tonic-gate /*
379f4b3ec61Sdh155122  * Walk through the param array specified registering each element with the
380f4b3ec61Sdh155122  * named dispatch handler.
381f4b3ec61Sdh155122  */
382f4b3ec61Sdh155122 static boolean_t
383f4b3ec61Sdh155122 ipsecah_param_register(IDP *ndp, ipsecahparam_t *ahp, int cnt)
384f4b3ec61Sdh155122 {
385f4b3ec61Sdh155122 	for (; cnt-- > 0; ahp++) {
386f4b3ec61Sdh155122 		if (ahp->ipsecah_param_name != NULL &&
387f4b3ec61Sdh155122 		    ahp->ipsecah_param_name[0]) {
388f4b3ec61Sdh155122 			if (!nd_load(ndp,
389f4b3ec61Sdh155122 			    ahp->ipsecah_param_name,
390f4b3ec61Sdh155122 			    ipsecah_param_get, ipsecah_param_set,
391f4b3ec61Sdh155122 			    (caddr_t)ahp)) {
392f4b3ec61Sdh155122 				nd_free(ndp);
393f4b3ec61Sdh155122 				return (B_FALSE);
394f4b3ec61Sdh155122 			}
395f4b3ec61Sdh155122 		}
396f4b3ec61Sdh155122 	}
397f4b3ec61Sdh155122 	return (B_TRUE);
398f4b3ec61Sdh155122 }
399f4b3ec61Sdh155122 
400f4b3ec61Sdh155122 /*
401f4b3ec61Sdh155122  * Initialize things for AH for each stack instance
402f4b3ec61Sdh155122  */
403f4b3ec61Sdh155122 static void *
404f4b3ec61Sdh155122 ipsecah_stack_init(netstackid_t stackid, netstack_t *ns)
405f4b3ec61Sdh155122 {
406f4b3ec61Sdh155122 	ipsecah_stack_t	*ahstack;
407f4b3ec61Sdh155122 	ipsecahparam_t	*ahp;
408f4b3ec61Sdh155122 
409f4b3ec61Sdh155122 	ahstack = (ipsecah_stack_t *)kmem_zalloc(sizeof (*ahstack), KM_SLEEP);
410f4b3ec61Sdh155122 	ahstack->ipsecah_netstack = ns;
411f4b3ec61Sdh155122 
412f4b3ec61Sdh155122 	ahp = (ipsecahparam_t *)kmem_alloc(sizeof (lcl_param_arr), KM_SLEEP);
413f4b3ec61Sdh155122 	ahstack->ipsecah_params = ahp;
414f4b3ec61Sdh155122 	bcopy(lcl_param_arr, ahp, sizeof (lcl_param_arr));
415f4b3ec61Sdh155122 
416f4b3ec61Sdh155122 	(void) ipsecah_param_register(&ahstack->ipsecah_g_nd, ahp,
417f4b3ec61Sdh155122 	    A_CNT(lcl_param_arr));
418f4b3ec61Sdh155122 
419f4b3ec61Sdh155122 	(void) ah_kstat_init(ahstack, stackid);
420f4b3ec61Sdh155122 
421f4b3ec61Sdh155122 	ahstack->ah_sadb.s_acquire_timeout = &ahstack->ipsecah_acquire_timeout;
422f4b3ec61Sdh155122 	ahstack->ah_sadb.s_acqfn = ah_send_acquire;
423f4b3ec61Sdh155122 	sadbp_init("AH", &ahstack->ah_sadb, SADB_SATYPE_AH, ah_hash_size,
424f4b3ec61Sdh155122 	    ahstack->ipsecah_netstack);
425f4b3ec61Sdh155122 
426f4b3ec61Sdh155122 	mutex_init(&ahstack->ipsecah_param_lock, NULL, MUTEX_DEFAULT, 0);
427f4b3ec61Sdh155122 
428f4b3ec61Sdh155122 	ip_drop_register(&ahstack->ah_dropper, "IPsec AH");
429f4b3ec61Sdh155122 	return (ahstack);
430f4b3ec61Sdh155122 }
431f4b3ec61Sdh155122 
432f4b3ec61Sdh155122 /*
4337c478bd9Sstevel@tonic-gate  * Destroy things for AH at module unload time.
4347c478bd9Sstevel@tonic-gate  */
4357c478bd9Sstevel@tonic-gate void
4367c478bd9Sstevel@tonic-gate ipsecah_ddi_destroy(void)
4377c478bd9Sstevel@tonic-gate {
438f4b3ec61Sdh155122 	netstack_unregister(NS_IPSECAH);
4397c478bd9Sstevel@tonic-gate 	taskq_destroy(ah_taskq);
440f4b3ec61Sdh155122 }
4417c478bd9Sstevel@tonic-gate 
442f4b3ec61Sdh155122 /*
443f4b3ec61Sdh155122  * Destroy things for AH for one stack... Never called?
444f4b3ec61Sdh155122  */
445f4b3ec61Sdh155122 static void
446f4b3ec61Sdh155122 ipsecah_stack_fini(netstackid_t stackid, void *arg)
447f4b3ec61Sdh155122 {
448f4b3ec61Sdh155122 	ipsecah_stack_t *ahstack = (ipsecah_stack_t *)arg;
449f4b3ec61Sdh155122 
450f4b3ec61Sdh155122 	if (ahstack->ah_pfkey_q != NULL) {
451f4b3ec61Sdh155122 		(void) quntimeout(ahstack->ah_pfkey_q, ahstack->ah_event);
452f4b3ec61Sdh155122 	}
453f4b3ec61Sdh155122 	ahstack->ah_sadb.s_acqfn = NULL;
454f4b3ec61Sdh155122 	ahstack->ah_sadb.s_acquire_timeout = NULL;
455f4b3ec61Sdh155122 	sadbp_destroy(&ahstack->ah_sadb, ahstack->ipsecah_netstack);
456f4b3ec61Sdh155122 	ip_drop_unregister(&ahstack->ah_dropper);
457f4b3ec61Sdh155122 	mutex_destroy(&ahstack->ipsecah_param_lock);
458f4b3ec61Sdh155122 	nd_free(&ahstack->ipsecah_g_nd);
459f4b3ec61Sdh155122 
460f4b3ec61Sdh155122 	kmem_free(ahstack->ipsecah_params, sizeof (lcl_param_arr));
461f4b3ec61Sdh155122 	ahstack->ipsecah_params = NULL;
462f4b3ec61Sdh155122 	kstat_delete_netstack(ahstack->ah_ksp, stackid);
463f4b3ec61Sdh155122 	ahstack->ah_ksp = NULL;
464f4b3ec61Sdh155122 	ahstack->ah_kstats = NULL;
465f4b3ec61Sdh155122 
466f4b3ec61Sdh155122 	kmem_free(ahstack, sizeof (*ahstack));
4677c478bd9Sstevel@tonic-gate }
4687c478bd9Sstevel@tonic-gate 
4697c478bd9Sstevel@tonic-gate /*
470bd670b35SErik Nordmark  * AH module open routine, which is here for keysock plumbing.
471bd670b35SErik Nordmark  * Keysock is pushed over {AH,ESP} which is an artifact from the Bad Old
472bd670b35SErik Nordmark  * Days of export control, and fears that ESP would not be allowed
473bd670b35SErik Nordmark  * to be shipped at all by default.  Eventually, keysock should
474bd670b35SErik Nordmark  * either access AH and ESP via modstubs or krtld dependencies, or
475bd670b35SErik Nordmark  * perhaps be folded in with AH and ESP into a single IPsec/netsec
476bd670b35SErik Nordmark  * module ("netsec" if PF_KEY provides more than AH/ESP keying tables).
4777c478bd9Sstevel@tonic-gate  */
4787c478bd9Sstevel@tonic-gate /* ARGSUSED */
4797c478bd9Sstevel@tonic-gate static int
4807c478bd9Sstevel@tonic-gate ipsecah_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp)
4817c478bd9Sstevel@tonic-gate {
482f4b3ec61Sdh155122 	netstack_t	*ns;
483f4b3ec61Sdh155122 	ipsecah_stack_t	*ahstack;
484f4b3ec61Sdh155122 
485d2370ffeSsommerfe 	if (secpolicy_ip_config(credp, B_FALSE) != 0)
4867c478bd9Sstevel@tonic-gate 		return (EPERM);
4877c478bd9Sstevel@tonic-gate 
4887c478bd9Sstevel@tonic-gate 	if (q->q_ptr != NULL)
4897c478bd9Sstevel@tonic-gate 		return (0);  /* Re-open of an already open instance. */
4907c478bd9Sstevel@tonic-gate 
4917c478bd9Sstevel@tonic-gate 	if (sflag != MODOPEN)
4927c478bd9Sstevel@tonic-gate 		return (EINVAL);
4937c478bd9Sstevel@tonic-gate 
494f4b3ec61Sdh155122 	ns = netstack_find_by_cred(credp);
495f4b3ec61Sdh155122 	ASSERT(ns != NULL);
496f4b3ec61Sdh155122 	ahstack = ns->netstack_ipsecah;
497f4b3ec61Sdh155122 	ASSERT(ahstack != NULL);
498f4b3ec61Sdh155122 
499f4b3ec61Sdh155122 	q->q_ptr = ahstack;
500f4b3ec61Sdh155122 	WR(q)->q_ptr = q->q_ptr;
5017c478bd9Sstevel@tonic-gate 
5027c478bd9Sstevel@tonic-gate 	qprocson(q);
5037c478bd9Sstevel@tonic-gate 	return (0);
5047c478bd9Sstevel@tonic-gate }
5057c478bd9Sstevel@tonic-gate 
5067c478bd9Sstevel@tonic-gate /*
5077c478bd9Sstevel@tonic-gate  * AH module close routine.
5087c478bd9Sstevel@tonic-gate  */
5097c478bd9Sstevel@tonic-gate static int
5107c478bd9Sstevel@tonic-gate ipsecah_close(queue_t *q)
5117c478bd9Sstevel@tonic-gate {
512f4b3ec61Sdh155122 	ipsecah_stack_t	*ahstack = (ipsecah_stack_t *)q->q_ptr;
513f4b3ec61Sdh155122 
5147c478bd9Sstevel@tonic-gate 	/*
5157c478bd9Sstevel@tonic-gate 	 * Clean up q_ptr, if needed.
5167c478bd9Sstevel@tonic-gate 	 */
5177c478bd9Sstevel@tonic-gate 	qprocsoff(q);
5187c478bd9Sstevel@tonic-gate 
5197c478bd9Sstevel@tonic-gate 	/* Keysock queue check is safe, because of OCEXCL perimeter. */
5207c478bd9Sstevel@tonic-gate 
521f4b3ec61Sdh155122 	if (q == ahstack->ah_pfkey_q) {
522f4b3ec61Sdh155122 		ah1dbg(ahstack,
523f4b3ec61Sdh155122 		    ("ipsecah_close:  Ummm... keysock is closing AH.\n"));
524f4b3ec61Sdh155122 		ahstack->ah_pfkey_q = NULL;
5257c478bd9Sstevel@tonic-gate 		/* Detach qtimeouts. */
526f4b3ec61Sdh155122 		(void) quntimeout(q, ahstack->ah_event);
5277c478bd9Sstevel@tonic-gate 	}
5287c478bd9Sstevel@tonic-gate 
529f4b3ec61Sdh155122 	netstack_rele(ahstack->ipsecah_netstack);
5307c478bd9Sstevel@tonic-gate 	return (0);
5317c478bd9Sstevel@tonic-gate }
5327c478bd9Sstevel@tonic-gate 
5337c478bd9Sstevel@tonic-gate /*
5347c478bd9Sstevel@tonic-gate  * Construct an SADB_REGISTER message with the current algorithms.
5357c478bd9Sstevel@tonic-gate  */
5367c478bd9Sstevel@tonic-gate static boolean_t
537f4b3ec61Sdh155122 ah_register_out(uint32_t sequence, uint32_t pid, uint_t serial,
538bd670b35SErik Nordmark     ipsecah_stack_t *ahstack, cred_t *cr)
5397c478bd9Sstevel@tonic-gate {
5407c478bd9Sstevel@tonic-gate 	mblk_t *mp;
5417c478bd9Sstevel@tonic-gate 	boolean_t rc = B_TRUE;
5427c478bd9Sstevel@tonic-gate 	sadb_msg_t *samsg;
5437c478bd9Sstevel@tonic-gate 	sadb_supported_t *sasupp;
5447c478bd9Sstevel@tonic-gate 	sadb_alg_t *saalg;
5457c478bd9Sstevel@tonic-gate 	uint_t allocsize = sizeof (*samsg);
5467c478bd9Sstevel@tonic-gate 	uint_t i, numalgs_snap;
5477c478bd9Sstevel@tonic-gate 	ipsec_alginfo_t **authalgs;
5487c478bd9Sstevel@tonic-gate 	uint_t num_aalgs;
549f4b3ec61Sdh155122 	ipsec_stack_t	*ipss = ahstack->ipsecah_netstack->netstack_ipsec;
5505d3b8cb7SBill Sommerfeld 	sadb_sens_t *sens;
5515d3b8cb7SBill Sommerfeld 	size_t sens_len = 0;
5525d3b8cb7SBill Sommerfeld 	sadb_ext_t *nextext;
553bd670b35SErik Nordmark 	ts_label_t *sens_tsl = NULL;
5547c478bd9Sstevel@tonic-gate 
5557c478bd9Sstevel@tonic-gate 	/* Allocate the KEYSOCK_OUT. */
5567c478bd9Sstevel@tonic-gate 	mp = sadb_keysock_out(serial);
5577c478bd9Sstevel@tonic-gate 	if (mp == NULL) {
5587c478bd9Sstevel@tonic-gate 		ah0dbg(("ah_register_out: couldn't allocate mblk.\n"));
5597c478bd9Sstevel@tonic-gate 		return (B_FALSE);
5607c478bd9Sstevel@tonic-gate 	}
5617c478bd9Sstevel@tonic-gate 
562bd670b35SErik Nordmark 	if (is_system_labeled() && (cr != NULL)) {
563bd670b35SErik Nordmark 		sens_tsl = crgetlabel(cr);
564bd670b35SErik Nordmark 		if (sens_tsl != NULL) {
565bd670b35SErik Nordmark 			sens_len = sadb_sens_len_from_label(sens_tsl);
5665d3b8cb7SBill Sommerfeld 			allocsize += sens_len;
5675d3b8cb7SBill Sommerfeld 		}
5685d3b8cb7SBill Sommerfeld 	}
5695d3b8cb7SBill Sommerfeld 
5707c478bd9Sstevel@tonic-gate 	/*
5717c478bd9Sstevel@tonic-gate 	 * Allocate the PF_KEY message that follows KEYSOCK_OUT.
5727c478bd9Sstevel@tonic-gate 	 * The alg reader lock needs to be held while allocating
5737c478bd9Sstevel@tonic-gate 	 * the variable part (i.e. the algorithms) of the message.
5747c478bd9Sstevel@tonic-gate 	 */
5757c478bd9Sstevel@tonic-gate 
576f4b3ec61Sdh155122 	mutex_enter(&ipss->ipsec_alg_lock);
5777c478bd9Sstevel@tonic-gate 
5787c478bd9Sstevel@tonic-gate 	/*
5797c478bd9Sstevel@tonic-gate 	 * Return only valid algorithms, so the number of algorithms
5807c478bd9Sstevel@tonic-gate 	 * to send up may be less than the number of algorithm entries
5817c478bd9Sstevel@tonic-gate 	 * in the table.
5827c478bd9Sstevel@tonic-gate 	 */
583f4b3ec61Sdh155122 	authalgs = ipss->ipsec_alglists[IPSEC_ALG_AUTH];
5847c478bd9Sstevel@tonic-gate 	for (num_aalgs = 0, i = 0; i < IPSEC_MAX_ALGS; i++)
5857c478bd9Sstevel@tonic-gate 		if (authalgs[i] != NULL && ALG_VALID(authalgs[i]))
5867c478bd9Sstevel@tonic-gate 			num_aalgs++;
5877c478bd9Sstevel@tonic-gate 
5887c478bd9Sstevel@tonic-gate 	/*
5897c478bd9Sstevel@tonic-gate 	 * Fill SADB_REGISTER message's algorithm descriptors.  Hold
5907c478bd9Sstevel@tonic-gate 	 * down the lock while filling it.
5917c478bd9Sstevel@tonic-gate 	 */
5927c478bd9Sstevel@tonic-gate 	if (num_aalgs != 0) {
5937c478bd9Sstevel@tonic-gate 		allocsize += (num_aalgs * sizeof (*saalg));
5947c478bd9Sstevel@tonic-gate 		allocsize += sizeof (*sasupp);
5957c478bd9Sstevel@tonic-gate 	}
5967c478bd9Sstevel@tonic-gate 	mp->b_cont = allocb(allocsize, BPRI_HI);
5977c478bd9Sstevel@tonic-gate 	if (mp->b_cont == NULL) {
598f4b3ec61Sdh155122 		mutex_exit(&ipss->ipsec_alg_lock);
5997c478bd9Sstevel@tonic-gate 		freemsg(mp);
6007c478bd9Sstevel@tonic-gate 		return (B_FALSE);
6017c478bd9Sstevel@tonic-gate 	}
6027c478bd9Sstevel@tonic-gate 
6037c478bd9Sstevel@tonic-gate 	mp->b_cont->b_wptr += allocsize;
6045d3b8cb7SBill Sommerfeld 	nextext = (sadb_ext_t *)(mp->b_cont->b_rptr + sizeof (*samsg));
6055d3b8cb7SBill Sommerfeld 
6067c478bd9Sstevel@tonic-gate 	if (num_aalgs != 0) {
6077c478bd9Sstevel@tonic-gate 
6085d3b8cb7SBill Sommerfeld 		saalg = (sadb_alg_t *)(((uint8_t *)nextext) + sizeof (*sasupp));
6097c478bd9Sstevel@tonic-gate 		ASSERT(((ulong_t)saalg & 0x7) == 0);
6107c478bd9Sstevel@tonic-gate 
6117c478bd9Sstevel@tonic-gate 		numalgs_snap = 0;
6127c478bd9Sstevel@tonic-gate 		for (i = 0;
613f4b3ec61Sdh155122 		    ((i < IPSEC_MAX_ALGS) && (numalgs_snap < num_aalgs));
614f4b3ec61Sdh155122 		    i++) {
6157c478bd9Sstevel@tonic-gate 			if (authalgs[i] == NULL || !ALG_VALID(authalgs[i]))
6167c478bd9Sstevel@tonic-gate 				continue;
6177c478bd9Sstevel@tonic-gate 
6187c478bd9Sstevel@tonic-gate 			saalg->sadb_alg_id = authalgs[i]->alg_id;
6197c478bd9Sstevel@tonic-gate 			saalg->sadb_alg_ivlen = 0;
6207c478bd9Sstevel@tonic-gate 			saalg->sadb_alg_minbits = authalgs[i]->alg_ef_minbits;
6217c478bd9Sstevel@tonic-gate 			saalg->sadb_alg_maxbits = authalgs[i]->alg_ef_maxbits;
6227c478bd9Sstevel@tonic-gate 			saalg->sadb_x_alg_increment =
6237c478bd9Sstevel@tonic-gate 			    authalgs[i]->alg_increment;
624628b0c67SMark Fenwick 			/* For now, salt is meaningless in AH. */
625628b0c67SMark Fenwick 			ASSERT(authalgs[i]->alg_saltlen == 0);
626628b0c67SMark Fenwick 			saalg->sadb_x_alg_saltbits =
627628b0c67SMark Fenwick 			    SADB_8TO1(authalgs[i]->alg_saltlen);
6287c478bd9Sstevel@tonic-gate 			numalgs_snap++;
6297c478bd9Sstevel@tonic-gate 			saalg++;
6307c478bd9Sstevel@tonic-gate 		}
6317c478bd9Sstevel@tonic-gate 		ASSERT(numalgs_snap == num_aalgs);
6327c478bd9Sstevel@tonic-gate #ifdef DEBUG
6337c478bd9Sstevel@tonic-gate 		/*
6347c478bd9Sstevel@tonic-gate 		 * Reality check to make sure I snagged all of the
6357c478bd9Sstevel@tonic-gate 		 * algorithms.
6367c478bd9Sstevel@tonic-gate 		 */
6377c478bd9Sstevel@tonic-gate 		for (; i < IPSEC_MAX_ALGS; i++)
6387c478bd9Sstevel@tonic-gate 			if (authalgs[i] != NULL && ALG_VALID(authalgs[i]))
6397c478bd9Sstevel@tonic-gate 				cmn_err(CE_PANIC,
6407c478bd9Sstevel@tonic-gate 				    "ah_register_out()!  Missed #%d.\n", i);
6417c478bd9Sstevel@tonic-gate #endif /* DEBUG */
6425d3b8cb7SBill Sommerfeld 		nextext = (sadb_ext_t *)saalg;
6437c478bd9Sstevel@tonic-gate 	}
6447c478bd9Sstevel@tonic-gate 
645f4b3ec61Sdh155122 	mutex_exit(&ipss->ipsec_alg_lock);
6467c478bd9Sstevel@tonic-gate 
647bd670b35SErik Nordmark 	if (sens_tsl != NULL) {
6485d3b8cb7SBill Sommerfeld 		sens = (sadb_sens_t *)nextext;
649bd670b35SErik Nordmark 		sadb_sens_from_label(sens, SADB_EXT_SENSITIVITY,
650bd670b35SErik Nordmark 		    sens_tsl, sens_len);
6515d3b8cb7SBill Sommerfeld 
6525d3b8cb7SBill Sommerfeld 		nextext = (sadb_ext_t *)(((uint8_t *)sens) + sens_len);
6535d3b8cb7SBill Sommerfeld 	}
6545d3b8cb7SBill Sommerfeld 
6557c478bd9Sstevel@tonic-gate 	/* Now fill the restof the SADB_REGISTER message. */
6567c478bd9Sstevel@tonic-gate 
6577c478bd9Sstevel@tonic-gate 	samsg = (sadb_msg_t *)mp->b_cont->b_rptr;
6587c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_version = PF_KEY_V2;
6597c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_type = SADB_REGISTER;
6607c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_errno = 0;
6617c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_satype = SADB_SATYPE_AH;
6627c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_len = SADB_8TO64(allocsize);
6637c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_reserved = 0;
6647c478bd9Sstevel@tonic-gate 	/*
6657c478bd9Sstevel@tonic-gate 	 * Assume caller has sufficient sequence/pid number info.  If it's one
6667c478bd9Sstevel@tonic-gate 	 * from me over a new alg., I could give two hoots about sequence.
6677c478bd9Sstevel@tonic-gate 	 */
6687c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_seq = sequence;
6697c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_pid = pid;
6707c478bd9Sstevel@tonic-gate 
6715d3b8cb7SBill Sommerfeld 	if (num_aalgs != 0) {
6727c478bd9Sstevel@tonic-gate 		sasupp = (sadb_supported_t *)(samsg + 1);
6735d3b8cb7SBill Sommerfeld 		sasupp->sadb_supported_len = SADB_8TO64(
6745d3b8cb7SBill Sommerfeld 		    sizeof (*sasupp) + sizeof (*saalg) * num_aalgs);
6757c478bd9Sstevel@tonic-gate 		sasupp->sadb_supported_exttype = SADB_EXT_SUPPORTED_AUTH;
6767c478bd9Sstevel@tonic-gate 		sasupp->sadb_supported_reserved = 0;
6777c478bd9Sstevel@tonic-gate 	}
6787c478bd9Sstevel@tonic-gate 
679f4b3ec61Sdh155122 	if (ahstack->ah_pfkey_q != NULL)
680f4b3ec61Sdh155122 		putnext(ahstack->ah_pfkey_q, mp);
6817c478bd9Sstevel@tonic-gate 	else {
6827c478bd9Sstevel@tonic-gate 		rc = B_FALSE;
6837c478bd9Sstevel@tonic-gate 		freemsg(mp);
6847c478bd9Sstevel@tonic-gate 	}
6857c478bd9Sstevel@tonic-gate 
6867c478bd9Sstevel@tonic-gate 	return (rc);
6877c478bd9Sstevel@tonic-gate }
6887c478bd9Sstevel@tonic-gate 
6897c478bd9Sstevel@tonic-gate /*
6907c478bd9Sstevel@tonic-gate  * Invoked when the algorithm table changes. Causes SADB_REGISTER
6917c478bd9Sstevel@tonic-gate  * messages continaining the current list of algorithms to be
6927c478bd9Sstevel@tonic-gate  * sent up to the AH listeners.
6937c478bd9Sstevel@tonic-gate  */
6947c478bd9Sstevel@tonic-gate void
695f4b3ec61Sdh155122 ipsecah_algs_changed(netstack_t *ns)
6967c478bd9Sstevel@tonic-gate {
697f4b3ec61Sdh155122 	ipsecah_stack_t	*ahstack = ns->netstack_ipsecah;
698f4b3ec61Sdh155122 
6997c478bd9Sstevel@tonic-gate 	/*
7007c478bd9Sstevel@tonic-gate 	 * Time to send a PF_KEY SADB_REGISTER message to AH listeners
7017c478bd9Sstevel@tonic-gate 	 * everywhere.  (The function itself checks for NULL ah_pfkey_q.)
7027c478bd9Sstevel@tonic-gate 	 */
7035d3b8cb7SBill Sommerfeld 	(void) ah_register_out(0, 0, 0, ahstack, NULL);
7047c478bd9Sstevel@tonic-gate }
7057c478bd9Sstevel@tonic-gate 
7067c478bd9Sstevel@tonic-gate /*
7077c478bd9Sstevel@tonic-gate  * Stub function that taskq_dispatch() invokes to take the mblk (in arg)
708bd670b35SErik Nordmark  * and send it into AH and IP again.
7097c478bd9Sstevel@tonic-gate  */
7107c478bd9Sstevel@tonic-gate static void
7117c478bd9Sstevel@tonic-gate inbound_task(void *arg)
7127c478bd9Sstevel@tonic-gate {
7137c478bd9Sstevel@tonic-gate 	mblk_t		*mp = (mblk_t *)arg;
714bd670b35SErik Nordmark 	mblk_t		*async_mp;
715bd670b35SErik Nordmark 	ip_recv_attr_t	iras;
716bd670b35SErik Nordmark 
717bd670b35SErik Nordmark 	async_mp = mp;
718bd670b35SErik Nordmark 	mp = async_mp->b_cont;
719bd670b35SErik Nordmark 	async_mp->b_cont = NULL;
720bd670b35SErik Nordmark 	if (!ip_recv_attr_from_mblk(async_mp, &iras)) {
721bd670b35SErik Nordmark 		/* The ill or ip_stack_t disappeared on us */
722bd670b35SErik Nordmark 		ip_drop_input("ip_recv_attr_from_mblk", mp, NULL);
723bd670b35SErik Nordmark 		freemsg(mp);
724bd670b35SErik Nordmark 		goto done;
725bd670b35SErik Nordmark 	}
726bd670b35SErik Nordmark 
727bd670b35SErik Nordmark 	ah_inbound_restart(mp, &iras);
728bd670b35SErik Nordmark done:
729bd670b35SErik Nordmark 	ira_cleanup(&iras, B_TRUE);
730bd670b35SErik Nordmark }
731bd670b35SErik Nordmark 
732bd670b35SErik Nordmark /*
733bd670b35SErik Nordmark  * Restart ESP after the SA has been added.
734bd670b35SErik Nordmark  */
735bd670b35SErik Nordmark static void
736bd670b35SErik Nordmark ah_inbound_restart(mblk_t *mp, ip_recv_attr_t *ira)
737bd670b35SErik Nordmark {
738bd670b35SErik Nordmark 	ah_t		*ah;
73973184bc7SDan McDonald 	netstack_t	*ns;
74073184bc7SDan McDonald 	ipsecah_stack_t	*ahstack;
74173184bc7SDan McDonald 
742bd670b35SErik Nordmark 	ns = ira->ira_ill->ill_ipst->ips_netstack;
74373184bc7SDan McDonald 	ahstack = ns->netstack_ipsecah;
7447c478bd9Sstevel@tonic-gate 
745f4b3ec61Sdh155122 	ASSERT(ahstack != NULL);
746bd670b35SErik Nordmark 	mp = ipsec_inbound_ah_sa(mp, ira, &ah);
747bd670b35SErik Nordmark 	if (mp == NULL)
748bd670b35SErik Nordmark 		return;
749bd670b35SErik Nordmark 
750bd670b35SErik Nordmark 	ASSERT(ah != NULL);
751bd670b35SErik Nordmark 	ASSERT(ira->ira_flags & IRAF_IPSEC_SECURE);
752bd670b35SErik Nordmark 	ASSERT(ira->ira_ipsec_ah_sa != NULL);
753bd670b35SErik Nordmark 
754bd670b35SErik Nordmark 	mp = ira->ira_ipsec_ah_sa->ipsa_input_func(mp, ah, ira);
755bd670b35SErik Nordmark 	if (mp == NULL) {
756bd670b35SErik Nordmark 		/*
757bd670b35SErik Nordmark 		 * Either it failed or is pending. In the former case
758bd670b35SErik Nordmark 		 * ipIfStatsInDiscards was increased.
759bd670b35SErik Nordmark 		 */
760bd670b35SErik Nordmark 		return;
7617c478bd9Sstevel@tonic-gate 	}
762bd670b35SErik Nordmark 	ip_input_post_ipsec(mp, ira);
76373184bc7SDan McDonald }
7647c478bd9Sstevel@tonic-gate 
7657c478bd9Sstevel@tonic-gate /*
7667c478bd9Sstevel@tonic-gate  * Now that weak-key passed, actually ADD the security association, and
7677c478bd9Sstevel@tonic-gate  * send back a reply ADD message.
7687c478bd9Sstevel@tonic-gate  */
7697c478bd9Sstevel@tonic-gate static int
7708810c16bSdanmcd ah_add_sa_finish(mblk_t *mp, sadb_msg_t *samsg, keysock_in_t *ksi,
771f4b3ec61Sdh155122     int *diagnostic, ipsecah_stack_t *ahstack)
7727c478bd9Sstevel@tonic-gate {
7735d3b8cb7SBill Sommerfeld 	isaf_t *primary = NULL, *secondary;
7745d3b8cb7SBill Sommerfeld 	boolean_t clone = B_FALSE, is_inbound = B_FALSE;
7757c478bd9Sstevel@tonic-gate 	sadb_sa_t *assoc = (sadb_sa_t *)ksi->ks_in_extv[SADB_EXT_SA];
7767c478bd9Sstevel@tonic-gate 	ipsa_t *larval;
7777c478bd9Sstevel@tonic-gate 	ipsacq_t *acqrec;
7787c478bd9Sstevel@tonic-gate 	iacqf_t *acq_bucket;
7797c478bd9Sstevel@tonic-gate 	mblk_t *acq_msgs = NULL;
7807c478bd9Sstevel@tonic-gate 	mblk_t *lpkt;
7817c478bd9Sstevel@tonic-gate 	int rc;
7825d3b8cb7SBill Sommerfeld 	ipsa_query_t sq;
7835d3b8cb7SBill Sommerfeld 	int error;
784f4b3ec61Sdh155122 	netstack_t	*ns = ahstack->ipsecah_netstack;
785f4b3ec61Sdh155122 	ipsec_stack_t	*ipss = ns->netstack_ipsec;
7867c478bd9Sstevel@tonic-gate 
7877c478bd9Sstevel@tonic-gate 	/*
7887c478bd9Sstevel@tonic-gate 	 * Locate the appropriate table(s).
7897c478bd9Sstevel@tonic-gate 	 */
7907c478bd9Sstevel@tonic-gate 
7915d3b8cb7SBill Sommerfeld 	sq.spp = &ahstack->ah_sadb;
7925d3b8cb7SBill Sommerfeld 	error = sadb_form_query(ksi, IPSA_Q_SA|IPSA_Q_DST,
7935d3b8cb7SBill Sommerfeld 	    IPSA_Q_SA|IPSA_Q_DST|IPSA_Q_INBOUND|IPSA_Q_OUTBOUND,
7945d3b8cb7SBill Sommerfeld 	    &sq, diagnostic);
7955d3b8cb7SBill Sommerfeld 	if (error)
7965d3b8cb7SBill Sommerfeld 		return (error);
7977c478bd9Sstevel@tonic-gate 
79838d95a78Smarkfen 	/*
79938d95a78Smarkfen 	 * Use the direction flags provided by the KMD to determine
80038d95a78Smarkfen 	 * if the inbound or outbound table should be the primary
80138d95a78Smarkfen 	 * for this SA. If these flags were absent then make this
80238d95a78Smarkfen 	 * decision based on the addresses.
80338d95a78Smarkfen 	 */
80438d95a78Smarkfen 	if (assoc->sadb_sa_flags & IPSA_F_INBOUND) {
8055d3b8cb7SBill Sommerfeld 		primary = sq.inbound;
8065d3b8cb7SBill Sommerfeld 		secondary = sq.outbound;
80738d95a78Smarkfen 		is_inbound = B_TRUE;
80838d95a78Smarkfen 		if (assoc->sadb_sa_flags & IPSA_F_OUTBOUND)
80938d95a78Smarkfen 			clone = B_TRUE;
81038d95a78Smarkfen 	} else {
81138d95a78Smarkfen 		if (assoc->sadb_sa_flags & IPSA_F_OUTBOUND) {
8125d3b8cb7SBill Sommerfeld 			primary = sq.outbound;
8135d3b8cb7SBill Sommerfeld 			secondary = sq.inbound;
81438d95a78Smarkfen 		}
81538d95a78Smarkfen 	}
81638d95a78Smarkfen 	if (primary == NULL) {
81738d95a78Smarkfen 		/*
81838d95a78Smarkfen 		 * The KMD did not set a direction flag, determine which
81938d95a78Smarkfen 		 * table to insert the SA into based on addresses.
82038d95a78Smarkfen 		 */
8217c478bd9Sstevel@tonic-gate 		switch (ksi->ks_in_dsttype) {
8227c478bd9Sstevel@tonic-gate 		case KS_IN_ADDR_MBCAST:
8237c478bd9Sstevel@tonic-gate 			clone = B_TRUE;	/* All mcast SAs can be bidirectional */
82438d95a78Smarkfen 			assoc->sadb_sa_flags |= IPSA_F_OUTBOUND;
8257c478bd9Sstevel@tonic-gate 			/* FALLTHRU */
8267c478bd9Sstevel@tonic-gate 		/*
8277c478bd9Sstevel@tonic-gate 		 * If the source address is either one of mine, or unspecified
8287c478bd9Sstevel@tonic-gate 		 * (which is best summed up by saying "not 'not mine'"),
8297c478bd9Sstevel@tonic-gate 		 * then the association is potentially bi-directional,
8307c478bd9Sstevel@tonic-gate 		 * in that it can be used for inbound traffic and outbound
8317c478bd9Sstevel@tonic-gate 		 * traffic.  The best example of such and SA is a multicast
8327c478bd9Sstevel@tonic-gate 		 * SA (which allows me to receive the outbound traffic).
8337c478bd9Sstevel@tonic-gate 		 */
83438d95a78Smarkfen 		case KS_IN_ADDR_ME:
83538d95a78Smarkfen 			assoc->sadb_sa_flags |= IPSA_F_INBOUND;
8365d3b8cb7SBill Sommerfeld 			primary = sq.inbound;
8375d3b8cb7SBill Sommerfeld 			secondary = sq.outbound;
8387c478bd9Sstevel@tonic-gate 			if (ksi->ks_in_srctype != KS_IN_ADDR_NOTME)
8397c478bd9Sstevel@tonic-gate 				clone = B_TRUE;
8407c478bd9Sstevel@tonic-gate 			is_inbound = B_TRUE;
8417c478bd9Sstevel@tonic-gate 			break;
8425d3b8cb7SBill Sommerfeld 
8437c478bd9Sstevel@tonic-gate 		/*
8447c478bd9Sstevel@tonic-gate 		 * If the source address literally not mine (either
8457c478bd9Sstevel@tonic-gate 		 * unspecified or not mine), then this SA may have an
8467c478bd9Sstevel@tonic-gate 		 * address that WILL be mine after some configuration.
8477c478bd9Sstevel@tonic-gate 		 * We pay the price for this by making it a bi-directional
8487c478bd9Sstevel@tonic-gate 		 * SA.
8497c478bd9Sstevel@tonic-gate 		 */
85038d95a78Smarkfen 		case KS_IN_ADDR_NOTME:
85138d95a78Smarkfen 			assoc->sadb_sa_flags |= IPSA_F_OUTBOUND;
8525d3b8cb7SBill Sommerfeld 			primary = sq.outbound;
8535d3b8cb7SBill Sommerfeld 			secondary = sq.inbound;
85438d95a78Smarkfen 			if (ksi->ks_in_srctype != KS_IN_ADDR_ME) {
85538d95a78Smarkfen 				assoc->sadb_sa_flags |= IPSA_F_INBOUND;
8567c478bd9Sstevel@tonic-gate 				clone = B_TRUE;
85738d95a78Smarkfen 			}
8587c478bd9Sstevel@tonic-gate 			break;
8597c478bd9Sstevel@tonic-gate 		default:
8608810c16bSdanmcd 			*diagnostic = SADB_X_DIAGNOSTIC_BAD_DST;
8617c478bd9Sstevel@tonic-gate 			return (EINVAL);
8627c478bd9Sstevel@tonic-gate 		}
86338d95a78Smarkfen 	}
8647c478bd9Sstevel@tonic-gate 
8657c478bd9Sstevel@tonic-gate 	/*
8667c478bd9Sstevel@tonic-gate 	 * Find a ACQUIRE list entry if possible.  If we've added an SA that
8677c478bd9Sstevel@tonic-gate 	 * suits the needs of an ACQUIRE list entry, we can eliminate the
8687c478bd9Sstevel@tonic-gate 	 * ACQUIRE list entry and transmit the enqueued packets.  Use the
8697c478bd9Sstevel@tonic-gate 	 * high-bit of the sequence number to queue it.  Key off destination
8707c478bd9Sstevel@tonic-gate 	 * addr, and change acqrec's state.
8717c478bd9Sstevel@tonic-gate 	 */
8727c478bd9Sstevel@tonic-gate 
8737c478bd9Sstevel@tonic-gate 	if (samsg->sadb_msg_seq & IACQF_LOWEST_SEQ) {
8745d3b8cb7SBill Sommerfeld 		acq_bucket = &(sq.sp->sdb_acq[sq.outhash]);
8757c478bd9Sstevel@tonic-gate 		mutex_enter(&acq_bucket->iacqf_lock);
8767c478bd9Sstevel@tonic-gate 		for (acqrec = acq_bucket->iacqf_ipsacq; acqrec != NULL;
8777c478bd9Sstevel@tonic-gate 		    acqrec = acqrec->ipsacq_next) {
8787c478bd9Sstevel@tonic-gate 			mutex_enter(&acqrec->ipsacq_lock);
8797c478bd9Sstevel@tonic-gate 			/*
8807c478bd9Sstevel@tonic-gate 			 * Q:  I only check sequence.  Should I check dst?
8817c478bd9Sstevel@tonic-gate 			 * A: Yes, check dest because those are the packets
8827c478bd9Sstevel@tonic-gate 			 *    that are queued up.
8837c478bd9Sstevel@tonic-gate 			 */
8847c478bd9Sstevel@tonic-gate 			if (acqrec->ipsacq_seq == samsg->sadb_msg_seq &&
8855d3b8cb7SBill Sommerfeld 			    IPSA_ARE_ADDR_EQUAL(sq.dstaddr,
8867c478bd9Sstevel@tonic-gate 			    acqrec->ipsacq_dstaddr, acqrec->ipsacq_addrfam))
8877c478bd9Sstevel@tonic-gate 				break;
8887c478bd9Sstevel@tonic-gate 			mutex_exit(&acqrec->ipsacq_lock);
8897c478bd9Sstevel@tonic-gate 		}
8907c478bd9Sstevel@tonic-gate 		if (acqrec != NULL) {
8917c478bd9Sstevel@tonic-gate 			/*
8927c478bd9Sstevel@tonic-gate 			 * AHA!  I found an ACQUIRE record for this SA.
8937c478bd9Sstevel@tonic-gate 			 * Grab the msg list, and free the acquire record.
8947c478bd9Sstevel@tonic-gate 			 * I already am holding the lock for this record,
8957c478bd9Sstevel@tonic-gate 			 * so all I have to do is free it.
8967c478bd9Sstevel@tonic-gate 			 */
8977c478bd9Sstevel@tonic-gate 			acq_msgs = acqrec->ipsacq_mp;
8987c478bd9Sstevel@tonic-gate 			acqrec->ipsacq_mp = NULL;
8997c478bd9Sstevel@tonic-gate 			mutex_exit(&acqrec->ipsacq_lock);
900f4b3ec61Sdh155122 			sadb_destroy_acquire(acqrec, ns);
9017c478bd9Sstevel@tonic-gate 		}
9027c478bd9Sstevel@tonic-gate 		mutex_exit(&acq_bucket->iacqf_lock);
9037c478bd9Sstevel@tonic-gate 	}
9047c478bd9Sstevel@tonic-gate 
9057c478bd9Sstevel@tonic-gate 	/*
9067c478bd9Sstevel@tonic-gate 	 * Find PF_KEY message, and see if I'm an update.  If so, find entry
9077c478bd9Sstevel@tonic-gate 	 * in larval list (if there).
9087c478bd9Sstevel@tonic-gate 	 */
9097c478bd9Sstevel@tonic-gate 
9107c478bd9Sstevel@tonic-gate 	larval = NULL;
9117c478bd9Sstevel@tonic-gate 
9127c478bd9Sstevel@tonic-gate 	if (samsg->sadb_msg_type == SADB_UPDATE) {
9135d3b8cb7SBill Sommerfeld 		mutex_enter(&sq.inbound->isaf_lock);
9145d3b8cb7SBill Sommerfeld 		larval = ipsec_getassocbyspi(sq.inbound, sq.assoc->sadb_sa_spi,
9155d3b8cb7SBill Sommerfeld 		    ALL_ZEROES_PTR, sq.dstaddr, sq.dst->sin_family);
9165d3b8cb7SBill Sommerfeld 		mutex_exit(&sq.inbound->isaf_lock);
9177c478bd9Sstevel@tonic-gate 
9187c478bd9Sstevel@tonic-gate 		if ((larval == NULL) ||
9197c478bd9Sstevel@tonic-gate 		    (larval->ipsa_state != IPSA_STATE_LARVAL)) {
92038d95a78Smarkfen 			*diagnostic = SADB_X_DIAGNOSTIC_SA_NOTFOUND;
92172bd9b6bSdanmcd 			if (larval != NULL) {
92272bd9b6bSdanmcd 				IPSA_REFRELE(larval);
92372bd9b6bSdanmcd 			}
9247c478bd9Sstevel@tonic-gate 			ah0dbg(("Larval update, but larval disappeared.\n"));
9257c478bd9Sstevel@tonic-gate 			return (ESRCH);
9267c478bd9Sstevel@tonic-gate 		} /* Else sadb_common_add unlinks it for me! */
9277c478bd9Sstevel@tonic-gate 	}
9287c478bd9Sstevel@tonic-gate 
929930af642SDan McDonald 	if (larval != NULL) {
930930af642SDan McDonald 		/*
931930af642SDan McDonald 		 * Hold again, because sadb_common_add() consumes a reference,
932930af642SDan McDonald 		 * and we don't want to clear_lpkt() without a reference.
933930af642SDan McDonald 		 */
934930af642SDan McDonald 		IPSA_REFHOLD(larval);
935930af642SDan McDonald 	}
9367c478bd9Sstevel@tonic-gate 
937bd670b35SErik Nordmark 	rc = sadb_common_add(ahstack->ah_pfkey_q, mp,
938f4b3ec61Sdh155122 	    samsg, ksi, primary, secondary, larval, clone, is_inbound,
93938d95a78Smarkfen 	    diagnostic, ns, &ahstack->ah_sadb);
9407c478bd9Sstevel@tonic-gate 
941930af642SDan McDonald 	if (larval != NULL) {
942bd670b35SErik Nordmark 		if (rc == 0) {
943930af642SDan McDonald 			lpkt = sadb_clear_lpkt(larval);
944930af642SDan McDonald 			if (lpkt != NULL) {
945930af642SDan McDonald 				rc = !taskq_dispatch(ah_taskq, inbound_task,
946930af642SDan McDonald 				    lpkt, TQ_NOSLEEP);
947bd670b35SErik Nordmark 			}
9487c478bd9Sstevel@tonic-gate 		}
949930af642SDan McDonald 		IPSA_REFRELE(larval);
950bd670b35SErik Nordmark 	}
9517c478bd9Sstevel@tonic-gate 
952bd670b35SErik Nordmark 	/*
953bd670b35SErik Nordmark 	 * How much more stack will I create with all of these
954bd670b35SErik Nordmark 	 * ah_outbound_*() calls?
955bd670b35SErik Nordmark 	 */
956bd670b35SErik Nordmark 
957bd670b35SErik Nordmark 	/* Handle the packets queued waiting for the SA */
9587c478bd9Sstevel@tonic-gate 	while (acq_msgs != NULL) {
959bd670b35SErik Nordmark 		mblk_t		*asyncmp;
960bd670b35SErik Nordmark 		mblk_t		*data_mp;
961bd670b35SErik Nordmark 		ip_xmit_attr_t	ixas;
962bd670b35SErik Nordmark 		ill_t		*ill;
9637c478bd9Sstevel@tonic-gate 
964bd670b35SErik Nordmark 		asyncmp = acq_msgs;
9657c478bd9Sstevel@tonic-gate 		acq_msgs = acq_msgs->b_next;
966bd670b35SErik Nordmark 		asyncmp->b_next = NULL;
9677c478bd9Sstevel@tonic-gate 
968bd670b35SErik Nordmark 		/*
969bd670b35SErik Nordmark 		 * Extract the ip_xmit_attr_t from the first mblk.
970bd670b35SErik Nordmark 		 * Verifies that the netstack and ill is still around; could
971bd670b35SErik Nordmark 		 * have vanished while iked was doing its work.
972bd670b35SErik Nordmark 		 * On succesful return we have a nce_t and the ill/ipst can't
973bd670b35SErik Nordmark 		 * disappear until we do the nce_refrele in ixa_cleanup.
974bd670b35SErik Nordmark 		 */
975bd670b35SErik Nordmark 		data_mp = asyncmp->b_cont;
976bd670b35SErik Nordmark 		asyncmp->b_cont = NULL;
977bd670b35SErik Nordmark 		if (!ip_xmit_attr_from_mblk(asyncmp, &ixas)) {
978f4b3ec61Sdh155122 			AH_BUMP_STAT(ahstack, out_discards);
979bd670b35SErik Nordmark 			ip_drop_packet(data_mp, B_FALSE, NULL,
980f4b3ec61Sdh155122 			    DROPPER(ipss, ipds_sadb_acquire_timeout),
981f4b3ec61Sdh155122 			    &ahstack->ah_dropper);
982bd670b35SErik Nordmark 		} else if (rc != 0) {
983bd670b35SErik Nordmark 			ill = ixas.ixa_nce->nce_ill;
984bd670b35SErik Nordmark 			AH_BUMP_STAT(ahstack, out_discards);
985bd670b35SErik Nordmark 			ip_drop_packet(data_mp, B_FALSE, ill,
986bd670b35SErik Nordmark 			    DROPPER(ipss, ipds_sadb_acquire_timeout),
987bd670b35SErik Nordmark 			    &ahstack->ah_dropper);
988bd670b35SErik Nordmark 			BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
989bd670b35SErik Nordmark 		} else {
990bd670b35SErik Nordmark 			ah_outbound_finish(data_mp, &ixas);
991bd670b35SErik Nordmark 		}
992bd670b35SErik Nordmark 		ixa_cleanup(&ixas);
9937c478bd9Sstevel@tonic-gate 	}
9947c478bd9Sstevel@tonic-gate 
9957c478bd9Sstevel@tonic-gate 	return (rc);
9967c478bd9Sstevel@tonic-gate }
9977c478bd9Sstevel@tonic-gate 
998bd670b35SErik Nordmark 
999bd670b35SErik Nordmark /*
1000bd670b35SErik Nordmark  * Process one of the queued messages (from ipsacq_mp) once the SA
1001bd670b35SErik Nordmark  * has been added.
1002bd670b35SErik Nordmark  */
1003bd670b35SErik Nordmark static void
1004bd670b35SErik Nordmark ah_outbound_finish(mblk_t *data_mp, ip_xmit_attr_t *ixa)
1005bd670b35SErik Nordmark {
1006bd670b35SErik Nordmark 	netstack_t	*ns = ixa->ixa_ipst->ips_netstack;
1007bd670b35SErik Nordmark 	ipsecah_stack_t *ahstack = ns->netstack_ipsecah;
1008bd670b35SErik Nordmark 	ipsec_stack_t	*ipss = ns->netstack_ipsec;
1009bd670b35SErik Nordmark 	ill_t		*ill = ixa->ixa_nce->nce_ill;
1010bd670b35SErik Nordmark 
1011bd670b35SErik Nordmark 	if (!ipsec_outbound_sa(data_mp, ixa, IPPROTO_AH)) {
1012bd670b35SErik Nordmark 		AH_BUMP_STAT(ahstack, out_discards);
1013bd670b35SErik Nordmark 		ip_drop_packet(data_mp, B_FALSE, ill,
1014bd670b35SErik Nordmark 		    DROPPER(ipss, ipds_sadb_acquire_timeout),
1015bd670b35SErik Nordmark 		    &ahstack->ah_dropper);
1016bd670b35SErik Nordmark 		BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
1017bd670b35SErik Nordmark 		return;
1018bd670b35SErik Nordmark 	}
1019bd670b35SErik Nordmark 
1020bd670b35SErik Nordmark 	data_mp = ah_outbound(data_mp, ixa);
1021bd670b35SErik Nordmark 	if (data_mp == NULL)
1022bd670b35SErik Nordmark 		return;
1023bd670b35SErik Nordmark 
1024bd670b35SErik Nordmark 	(void) ip_output_post_ipsec(data_mp, ixa);
1025bd670b35SErik Nordmark }
1026bd670b35SErik Nordmark 
10277c478bd9Sstevel@tonic-gate /*
10287c478bd9Sstevel@tonic-gate  * Add new AH security association.  This may become a generic AH/ESP
10297c478bd9Sstevel@tonic-gate  * routine eventually.
10307c478bd9Sstevel@tonic-gate  */
10317c478bd9Sstevel@tonic-gate static int
1032f4b3ec61Sdh155122 ah_add_sa(mblk_t *mp, keysock_in_t *ksi, int *diagnostic, netstack_t *ns)
10337c478bd9Sstevel@tonic-gate {
10347c478bd9Sstevel@tonic-gate 	sadb_sa_t *assoc = (sadb_sa_t *)ksi->ks_in_extv[SADB_EXT_SA];
10357c478bd9Sstevel@tonic-gate 	sadb_address_t *srcext =
10367c478bd9Sstevel@tonic-gate 	    (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_SRC];
10377c478bd9Sstevel@tonic-gate 	sadb_address_t *dstext =
10387c478bd9Sstevel@tonic-gate 	    (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_DST];
10398810c16bSdanmcd 	sadb_address_t *isrcext =
10408810c16bSdanmcd 	    (sadb_address_t *)ksi->ks_in_extv[SADB_X_EXT_ADDRESS_INNER_SRC];
10418810c16bSdanmcd 	sadb_address_t *idstext =
10428810c16bSdanmcd 	    (sadb_address_t *)ksi->ks_in_extv[SADB_X_EXT_ADDRESS_INNER_DST];
10437c478bd9Sstevel@tonic-gate 	sadb_key_t *key = (sadb_key_t *)ksi->ks_in_extv[SADB_EXT_KEY_AUTH];
10447c478bd9Sstevel@tonic-gate 	struct sockaddr_in *src, *dst;
10457c478bd9Sstevel@tonic-gate 	/* We don't need sockaddr_in6 for now. */
10467c478bd9Sstevel@tonic-gate 	sadb_lifetime_t *soft =
10477c478bd9Sstevel@tonic-gate 	    (sadb_lifetime_t *)ksi->ks_in_extv[SADB_EXT_LIFETIME_SOFT];
10487c478bd9Sstevel@tonic-gate 	sadb_lifetime_t *hard =
10497c478bd9Sstevel@tonic-gate 	    (sadb_lifetime_t *)ksi->ks_in_extv[SADB_EXT_LIFETIME_HARD];
10509c2c14abSThejaswini Singarajipura 	sadb_lifetime_t *idle =
10519c2c14abSThejaswini Singarajipura 	    (sadb_lifetime_t *)ksi->ks_in_extv[SADB_X_EXT_LIFETIME_IDLE];
10527c478bd9Sstevel@tonic-gate 	ipsec_alginfo_t *aalg;
1053f4b3ec61Sdh155122 	ipsecah_stack_t	*ahstack = ns->netstack_ipsecah;
1054f4b3ec61Sdh155122 	ipsec_stack_t	*ipss = ns->netstack_ipsec;
10557c478bd9Sstevel@tonic-gate 
10567c478bd9Sstevel@tonic-gate 	/* I need certain extensions present for an ADD message. */
10577c478bd9Sstevel@tonic-gate 	if (srcext == NULL) {
10587c478bd9Sstevel@tonic-gate 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_SRC;
10597c478bd9Sstevel@tonic-gate 		return (EINVAL);
10607c478bd9Sstevel@tonic-gate 	}
10617c478bd9Sstevel@tonic-gate 	if (dstext == NULL) {
10627c478bd9Sstevel@tonic-gate 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_DST;
10637c478bd9Sstevel@tonic-gate 		return (EINVAL);
10647c478bd9Sstevel@tonic-gate 	}
10658810c16bSdanmcd 	if (isrcext == NULL && idstext != NULL) {
10668810c16bSdanmcd 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_INNER_SRC;
10678810c16bSdanmcd 		return (EINVAL);
10688810c16bSdanmcd 	}
10698810c16bSdanmcd 	if (isrcext != NULL && idstext == NULL) {
10708810c16bSdanmcd 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_INNER_DST;
10718810c16bSdanmcd 		return (EINVAL);
10728810c16bSdanmcd 	}
10737c478bd9Sstevel@tonic-gate 	if (assoc == NULL) {
10747c478bd9Sstevel@tonic-gate 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_SA;
10757c478bd9Sstevel@tonic-gate 		return (EINVAL);
10767c478bd9Sstevel@tonic-gate 	}
10777c478bd9Sstevel@tonic-gate 	if (key == NULL) {
10787c478bd9Sstevel@tonic-gate 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_AKEY;
10797c478bd9Sstevel@tonic-gate 		return (EINVAL);
10807c478bd9Sstevel@tonic-gate 	}
10817c478bd9Sstevel@tonic-gate 
10827c478bd9Sstevel@tonic-gate 	src = (struct sockaddr_in *)(srcext + 1);
10837c478bd9Sstevel@tonic-gate 	dst = (struct sockaddr_in *)(dstext + 1);
10847c478bd9Sstevel@tonic-gate 
10857c478bd9Sstevel@tonic-gate 	/* Sundry ADD-specific reality checks. */
10867c478bd9Sstevel@tonic-gate 	/* XXX STATS : Logging/stats here? */
10877c478bd9Sstevel@tonic-gate 
10889c2c14abSThejaswini Singarajipura 	if ((assoc->sadb_sa_state != SADB_SASTATE_MATURE) &&
10899c2c14abSThejaswini Singarajipura 	    (assoc->sadb_sa_state != SADB_X_SASTATE_ACTIVE_ELSEWHERE)) {
10907c478bd9Sstevel@tonic-gate 		*diagnostic = SADB_X_DIAGNOSTIC_BAD_SASTATE;
10917c478bd9Sstevel@tonic-gate 		return (EINVAL);
10927c478bd9Sstevel@tonic-gate 	}
10937c478bd9Sstevel@tonic-gate 	if (assoc->sadb_sa_encrypt != SADB_EALG_NONE) {
10947c478bd9Sstevel@tonic-gate 		*diagnostic = SADB_X_DIAGNOSTIC_ENCR_NOTSUPP;
10957c478bd9Sstevel@tonic-gate 		return (EINVAL);
10967c478bd9Sstevel@tonic-gate 	}
109772bd9b6bSdanmcd 	if (assoc->sadb_sa_flags & ~ahstack->ah_sadb.s_addflags) {
10987c478bd9Sstevel@tonic-gate 		*diagnostic = SADB_X_DIAGNOSTIC_BAD_SAFLAGS;
10997c478bd9Sstevel@tonic-gate 		return (EINVAL);
11007c478bd9Sstevel@tonic-gate 	}
11019c2c14abSThejaswini Singarajipura 	if ((*diagnostic = sadb_hardsoftchk(hard, soft, idle)) != 0)
11027c478bd9Sstevel@tonic-gate 		return (EINVAL);
11037c478bd9Sstevel@tonic-gate 
11048810c16bSdanmcd 	ASSERT(src->sin_family == dst->sin_family);
11057c478bd9Sstevel@tonic-gate 
11067c478bd9Sstevel@tonic-gate 	/* Stuff I don't support, for now.  XXX Diagnostic? */
11075d3b8cb7SBill Sommerfeld 	if (ksi->ks_in_extv[SADB_EXT_LIFETIME_CURRENT] != NULL)
11087c478bd9Sstevel@tonic-gate 		return (EOPNOTSUPP);
11097c478bd9Sstevel@tonic-gate 
11105d3b8cb7SBill Sommerfeld 	if (ksi->ks_in_extv[SADB_EXT_SENSITIVITY] != NULL) {
11115d3b8cb7SBill Sommerfeld 		if (!is_system_labeled())
11125d3b8cb7SBill Sommerfeld 			return (EOPNOTSUPP);
11135d3b8cb7SBill Sommerfeld 	}
11145d3b8cb7SBill Sommerfeld 
11155d3b8cb7SBill Sommerfeld 	if (ksi->ks_in_extv[SADB_X_EXT_OUTER_SENS] != NULL) {
11165d3b8cb7SBill Sommerfeld 		if (!is_system_labeled())
11175d3b8cb7SBill Sommerfeld 			return (EOPNOTSUPP);
11185d3b8cb7SBill Sommerfeld 	}
11197c478bd9Sstevel@tonic-gate 	/*
11205d3b8cb7SBill Sommerfeld 	 * XXX Policy : I'm not checking identities at this time, but
11215d3b8cb7SBill Sommerfeld 	 * if I did, I'd do them here, before I sent the weak key
11225d3b8cb7SBill Sommerfeld 	 * check up to the algorithm.
11237c478bd9Sstevel@tonic-gate 	 */
11247c478bd9Sstevel@tonic-gate 
11257c478bd9Sstevel@tonic-gate 	/* verify that there is a mapping for the specified algorithm */
1126f4b3ec61Sdh155122 	mutex_enter(&ipss->ipsec_alg_lock);
1127f4b3ec61Sdh155122 	aalg = ipss->ipsec_alglists[IPSEC_ALG_AUTH][assoc->sadb_sa_auth];
11287c478bd9Sstevel@tonic-gate 	if (aalg == NULL || !ALG_VALID(aalg)) {
1129f4b3ec61Sdh155122 		mutex_exit(&ipss->ipsec_alg_lock);
1130f4b3ec61Sdh155122 		ah1dbg(ahstack, ("Couldn't find auth alg #%d.\n",
1131f4b3ec61Sdh155122 		    assoc->sadb_sa_auth));
11327c478bd9Sstevel@tonic-gate 		*diagnostic = SADB_X_DIAGNOSTIC_BAD_AALG;
11337c478bd9Sstevel@tonic-gate 		return (EINVAL);
11347c478bd9Sstevel@tonic-gate 	}
11357c478bd9Sstevel@tonic-gate 	ASSERT(aalg->alg_mech_type != CRYPTO_MECHANISM_INVALID);
11367c478bd9Sstevel@tonic-gate 
11377c478bd9Sstevel@tonic-gate 	/* sanity check key sizes */
11387c478bd9Sstevel@tonic-gate 	if (!ipsec_valid_key_size(key->sadb_key_bits, aalg)) {
1139f4b3ec61Sdh155122 		mutex_exit(&ipss->ipsec_alg_lock);
11407c478bd9Sstevel@tonic-gate 		*diagnostic = SADB_X_DIAGNOSTIC_BAD_AKEYBITS;
11417c478bd9Sstevel@tonic-gate 		return (EINVAL);
11427c478bd9Sstevel@tonic-gate 	}
11437c478bd9Sstevel@tonic-gate 
11447c478bd9Sstevel@tonic-gate 	/* check key and fix parity if needed */
11457c478bd9Sstevel@tonic-gate 	if (ipsec_check_key(aalg->alg_mech_type, key, B_TRUE,
11467c478bd9Sstevel@tonic-gate 	    diagnostic) != 0) {
1147f4b3ec61Sdh155122 		mutex_exit(&ipss->ipsec_alg_lock);
11487c478bd9Sstevel@tonic-gate 		return (EINVAL);
11497c478bd9Sstevel@tonic-gate 	}
11507c478bd9Sstevel@tonic-gate 
1151f4b3ec61Sdh155122 	mutex_exit(&ipss->ipsec_alg_lock);
11527c478bd9Sstevel@tonic-gate 
11538810c16bSdanmcd 	return (ah_add_sa_finish(mp, (sadb_msg_t *)mp->b_cont->b_rptr, ksi,
1154f4b3ec61Sdh155122 	    diagnostic, ahstack));
11557c478bd9Sstevel@tonic-gate }
11567c478bd9Sstevel@tonic-gate 
11575d3b8cb7SBill Sommerfeld /* Refactor me */
11587c478bd9Sstevel@tonic-gate /*
11597c478bd9Sstevel@tonic-gate  * Update a security association.  Updates come in two varieties.  The first
11607c478bd9Sstevel@tonic-gate  * is an update of lifetimes on a non-larval SA.  The second is an update of
11617c478bd9Sstevel@tonic-gate  * a larval SA, which ends up looking a lot more like an add.
11627c478bd9Sstevel@tonic-gate  */
11637c478bd9Sstevel@tonic-gate static int
1164f4b3ec61Sdh155122 ah_update_sa(mblk_t *mp, keysock_in_t *ksi, int *diagnostic,
116538d95a78Smarkfen     ipsecah_stack_t *ahstack, uint8_t sadb_msg_type)
11667c478bd9Sstevel@tonic-gate {
11679c2c14abSThejaswini Singarajipura 	sadb_sa_t *assoc = (sadb_sa_t *)ksi->ks_in_extv[SADB_EXT_SA];
11687c478bd9Sstevel@tonic-gate 	sadb_address_t *dstext =
11697c478bd9Sstevel@tonic-gate 	    (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_DST];
11709c2c14abSThejaswini Singarajipura 	mblk_t	*buf_pkt;
11719c2c14abSThejaswini Singarajipura 	int rcode;
11727c478bd9Sstevel@tonic-gate 
11737c478bd9Sstevel@tonic-gate 	if (dstext == NULL) {
11747c478bd9Sstevel@tonic-gate 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_DST;
11757c478bd9Sstevel@tonic-gate 		return (EINVAL);
11767c478bd9Sstevel@tonic-gate 	}
11779c2c14abSThejaswini Singarajipura 
11789c2c14abSThejaswini Singarajipura 	rcode = sadb_update_sa(mp, ksi, &buf_pkt, &ahstack->ah_sadb,
11799c2c14abSThejaswini Singarajipura 	    diagnostic, ahstack->ah_pfkey_q, ah_add_sa,
11809c2c14abSThejaswini Singarajipura 	    ahstack->ipsecah_netstack, sadb_msg_type);
11819c2c14abSThejaswini Singarajipura 
11829c2c14abSThejaswini Singarajipura 	if ((assoc->sadb_sa_state != SADB_X_SASTATE_ACTIVE) ||
11839c2c14abSThejaswini Singarajipura 	    (rcode != 0)) {
11849c2c14abSThejaswini Singarajipura 		return (rcode);
11859c2c14abSThejaswini Singarajipura 	}
11869c2c14abSThejaswini Singarajipura 
118773184bc7SDan McDonald 	HANDLE_BUF_PKT(ah_taskq, ahstack->ipsecah_netstack->netstack_ipsec,
118873184bc7SDan McDonald 	    ahstack->ah_dropper, buf_pkt);
11899c2c14abSThejaswini Singarajipura 
11909c2c14abSThejaswini Singarajipura 	return (rcode);
11917c478bd9Sstevel@tonic-gate }
11927c478bd9Sstevel@tonic-gate 
11935d3b8cb7SBill Sommerfeld /* Refactor me */
11947c478bd9Sstevel@tonic-gate /*
11957c478bd9Sstevel@tonic-gate  * Delete a security association.  This is REALLY likely to be code common to
11967c478bd9Sstevel@tonic-gate  * both AH and ESP.  Find the association, then unlink it.
11977c478bd9Sstevel@tonic-gate  */
11987c478bd9Sstevel@tonic-gate static int
1199f4b3ec61Sdh155122 ah_del_sa(mblk_t *mp, keysock_in_t *ksi, int *diagnostic,
120038d95a78Smarkfen     ipsecah_stack_t *ahstack, uint8_t sadb_msg_type)
12017c478bd9Sstevel@tonic-gate {
12027c478bd9Sstevel@tonic-gate 	sadb_sa_t *assoc = (sadb_sa_t *)ksi->ks_in_extv[SADB_EXT_SA];
12037c478bd9Sstevel@tonic-gate 	sadb_address_t *dstext =
12047c478bd9Sstevel@tonic-gate 	    (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_DST];
12057c478bd9Sstevel@tonic-gate 	sadb_address_t *srcext =
12067c478bd9Sstevel@tonic-gate 	    (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_SRC];
12077c478bd9Sstevel@tonic-gate 	struct sockaddr_in *sin;
12087c478bd9Sstevel@tonic-gate 
12097c478bd9Sstevel@tonic-gate 	if (assoc == NULL) {
12107c478bd9Sstevel@tonic-gate 		if (dstext != NULL)
12117c478bd9Sstevel@tonic-gate 			sin = (struct sockaddr_in *)(dstext + 1);
12127c478bd9Sstevel@tonic-gate 		else if (srcext != NULL)
12137c478bd9Sstevel@tonic-gate 			sin = (struct sockaddr_in *)(srcext + 1);
12147c478bd9Sstevel@tonic-gate 		else {
12157c478bd9Sstevel@tonic-gate 			*diagnostic = SADB_X_DIAGNOSTIC_MISSING_SA;
12167c478bd9Sstevel@tonic-gate 			return (EINVAL);
12177c478bd9Sstevel@tonic-gate 		}
12188810c16bSdanmcd 		return (sadb_purge_sa(mp, ksi,
1219f4b3ec61Sdh155122 		    (sin->sin_family == AF_INET6) ? &ahstack->ah_sadb.s_v6 :
1220bd670b35SErik Nordmark 		    &ahstack->ah_sadb.s_v4, diagnostic, ahstack->ah_pfkey_q));
12217c478bd9Sstevel@tonic-gate 	}
12227c478bd9Sstevel@tonic-gate 
122338d95a78Smarkfen 	return (sadb_delget_sa(mp, ksi, &ahstack->ah_sadb, diagnostic,
122438d95a78Smarkfen 	    ahstack->ah_pfkey_q, sadb_msg_type));
12257c478bd9Sstevel@tonic-gate }
12267c478bd9Sstevel@tonic-gate 
12275d3b8cb7SBill Sommerfeld /* Refactor me */
12287c478bd9Sstevel@tonic-gate /*
12297c478bd9Sstevel@tonic-gate  * Convert the entire contents of all of AH's SA tables into PF_KEY SADB_DUMP
12307c478bd9Sstevel@tonic-gate  * messages.
12317c478bd9Sstevel@tonic-gate  */
12327c478bd9Sstevel@tonic-gate static void
1233f4b3ec61Sdh155122 ah_dump(mblk_t *mp, keysock_in_t *ksi, ipsecah_stack_t *ahstack)
12347c478bd9Sstevel@tonic-gate {
12357c478bd9Sstevel@tonic-gate 	int error;
12367c478bd9Sstevel@tonic-gate 	sadb_msg_t *samsg;
12377c478bd9Sstevel@tonic-gate 
12387c478bd9Sstevel@tonic-gate 	/*
12397c478bd9Sstevel@tonic-gate 	 * Dump each fanout, bailing if error is non-zero.
12407c478bd9Sstevel@tonic-gate 	 */
12417c478bd9Sstevel@tonic-gate 
12429c2c14abSThejaswini Singarajipura 	error = sadb_dump(ahstack->ah_pfkey_q, mp, ksi, &ahstack->ah_sadb.s_v4);
12437c478bd9Sstevel@tonic-gate 	if (error != 0)
12447c478bd9Sstevel@tonic-gate 		goto bail;
12457c478bd9Sstevel@tonic-gate 
12469c2c14abSThejaswini Singarajipura 	error = sadb_dump(ahstack->ah_pfkey_q, mp, ksi, &ahstack->ah_sadb.s_v6);
12477c478bd9Sstevel@tonic-gate bail:
12487c478bd9Sstevel@tonic-gate 	ASSERT(mp->b_cont != NULL);
12497c478bd9Sstevel@tonic-gate 	samsg = (sadb_msg_t *)mp->b_cont->b_rptr;
12507c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_errno = (uint8_t)error;
1251f4b3ec61Sdh155122 	sadb_pfkey_echo(ahstack->ah_pfkey_q, mp,
1252f4b3ec61Sdh155122 	    (sadb_msg_t *)mp->b_cont->b_rptr, ksi, NULL);
12537c478bd9Sstevel@tonic-gate }
12547c478bd9Sstevel@tonic-gate 
12557c478bd9Sstevel@tonic-gate /*
12568810c16bSdanmcd  * First-cut reality check for an inbound PF_KEY message.
12578810c16bSdanmcd  */
12588810c16bSdanmcd static boolean_t
1259f4b3ec61Sdh155122 ah_pfkey_reality_failures(mblk_t *mp, keysock_in_t *ksi,
1260f4b3ec61Sdh155122     ipsecah_stack_t *ahstack)
12618810c16bSdanmcd {
12628810c16bSdanmcd 	int diagnostic;
12638810c16bSdanmcd 
12648810c16bSdanmcd 	if (mp->b_cont == NULL) {
12658810c16bSdanmcd 		freemsg(mp);
12668810c16bSdanmcd 		return (B_TRUE);
12678810c16bSdanmcd 	}
12688810c16bSdanmcd 
12698810c16bSdanmcd 	if (ksi->ks_in_extv[SADB_EXT_KEY_ENCRYPT] != NULL) {
12708810c16bSdanmcd 		diagnostic = SADB_X_DIAGNOSTIC_EKEY_PRESENT;
12718810c16bSdanmcd 		goto badmsg;
12728810c16bSdanmcd 	}
12738810c16bSdanmcd 	if (ksi->ks_in_extv[SADB_EXT_PROPOSAL] != NULL) {
12748810c16bSdanmcd 		diagnostic = SADB_X_DIAGNOSTIC_PROP_PRESENT;
12758810c16bSdanmcd 		goto badmsg;
12768810c16bSdanmcd 	}
12778810c16bSdanmcd 	if (ksi->ks_in_extv[SADB_EXT_SUPPORTED_AUTH] != NULL ||
12788810c16bSdanmcd 	    ksi->ks_in_extv[SADB_EXT_SUPPORTED_ENCRYPT] != NULL) {
12798810c16bSdanmcd 		diagnostic = SADB_X_DIAGNOSTIC_SUPP_PRESENT;
12808810c16bSdanmcd 		goto badmsg;
12818810c16bSdanmcd 	}
12828810c16bSdanmcd 	return (B_FALSE);	/* False ==> no failures */
12838810c16bSdanmcd 
12848810c16bSdanmcd badmsg:
1285f4b3ec61Sdh155122 	sadb_pfkey_error(ahstack->ah_pfkey_q, mp, EINVAL,
1286f4b3ec61Sdh155122 	    diagnostic, ksi->ks_in_serial);
12878810c16bSdanmcd 	return (B_TRUE);	/* True ==> failures */
12888810c16bSdanmcd }
12898810c16bSdanmcd 
12908810c16bSdanmcd /*
12917c478bd9Sstevel@tonic-gate  * AH parsing of PF_KEY messages.  Keysock did most of the really silly
12927c478bd9Sstevel@tonic-gate  * error cases.  What I receive is a fully-formed, syntactically legal
12937c478bd9Sstevel@tonic-gate  * PF_KEY message.  I then need to check semantics...
12947c478bd9Sstevel@tonic-gate  *
12957c478bd9Sstevel@tonic-gate  * This code may become common to AH and ESP.  Stay tuned.
12967c478bd9Sstevel@tonic-gate  *
12977c478bd9Sstevel@tonic-gate  * I also make the assumption that db_ref's are cool.  If this assumption
12987c478bd9Sstevel@tonic-gate  * is wrong, this means that someone other than keysock or me has been
12997c478bd9Sstevel@tonic-gate  * mucking with PF_KEY messages.
13007c478bd9Sstevel@tonic-gate  */
13017c478bd9Sstevel@tonic-gate static void
1302f4b3ec61Sdh155122 ah_parse_pfkey(mblk_t *mp, ipsecah_stack_t *ahstack)
13037c478bd9Sstevel@tonic-gate {
13047c478bd9Sstevel@tonic-gate 	mblk_t *msg = mp->b_cont;
13057c478bd9Sstevel@tonic-gate 	sadb_msg_t *samsg;
13067c478bd9Sstevel@tonic-gate 	keysock_in_t *ksi;
13077c478bd9Sstevel@tonic-gate 	int error;
13087c478bd9Sstevel@tonic-gate 	int diagnostic = SADB_X_DIAGNOSTIC_NONE;
13097c478bd9Sstevel@tonic-gate 
13107c478bd9Sstevel@tonic-gate 	ASSERT(msg != NULL);
1311f4b3ec61Sdh155122 
13127c478bd9Sstevel@tonic-gate 	samsg = (sadb_msg_t *)msg->b_rptr;
13137c478bd9Sstevel@tonic-gate 	ksi = (keysock_in_t *)mp->b_rptr;
13147c478bd9Sstevel@tonic-gate 
13157c478bd9Sstevel@tonic-gate 	/*
13167c478bd9Sstevel@tonic-gate 	 * If applicable, convert unspecified AF_INET6 to unspecified
13177c478bd9Sstevel@tonic-gate 	 * AF_INET.
13187c478bd9Sstevel@tonic-gate 	 */
1319f4b3ec61Sdh155122 	if (!sadb_addrfix(ksi, ahstack->ah_pfkey_q, mp,
1320f4b3ec61Sdh155122 	    ahstack->ipsecah_netstack) ||
1321f4b3ec61Sdh155122 	    ah_pfkey_reality_failures(mp, ksi, ahstack)) {
13228810c16bSdanmcd 		return;
13238810c16bSdanmcd 	}
13247c478bd9Sstevel@tonic-gate 
13257c478bd9Sstevel@tonic-gate 	switch (samsg->sadb_msg_type) {
13267c478bd9Sstevel@tonic-gate 	case SADB_ADD:
1327f4b3ec61Sdh155122 		error = ah_add_sa(mp, ksi, &diagnostic,
1328f4b3ec61Sdh155122 		    ahstack->ipsecah_netstack);
13297c478bd9Sstevel@tonic-gate 		if (error != 0) {
1330f4b3ec61Sdh155122 			sadb_pfkey_error(ahstack->ah_pfkey_q, mp, error,
1331f4b3ec61Sdh155122 			    diagnostic, ksi->ks_in_serial);
13327c478bd9Sstevel@tonic-gate 		}
13337c478bd9Sstevel@tonic-gate 		/* else ah_add_sa() took care of things. */
13347c478bd9Sstevel@tonic-gate 		break;
13357c478bd9Sstevel@tonic-gate 	case SADB_DELETE:
133638d95a78Smarkfen 	case SADB_X_DELPAIR:
13379c2c14abSThejaswini Singarajipura 	case SADB_X_DELPAIR_STATE:
133838d95a78Smarkfen 		error = ah_del_sa(mp, ksi, &diagnostic, ahstack,
133938d95a78Smarkfen 		    samsg->sadb_msg_type);
13407c478bd9Sstevel@tonic-gate 		if (error != 0) {
1341f4b3ec61Sdh155122 			sadb_pfkey_error(ahstack->ah_pfkey_q, mp, error,
1342f4b3ec61Sdh155122 			    diagnostic, ksi->ks_in_serial);
13437c478bd9Sstevel@tonic-gate 		}
13447c478bd9Sstevel@tonic-gate 		/* Else ah_del_sa() took care of things. */
13457c478bd9Sstevel@tonic-gate 		break;
13467c478bd9Sstevel@tonic-gate 	case SADB_GET:
134738d95a78Smarkfen 		error = sadb_delget_sa(mp, ksi, &ahstack->ah_sadb, &diagnostic,
134838d95a78Smarkfen 		    ahstack->ah_pfkey_q, samsg->sadb_msg_type);
13497c478bd9Sstevel@tonic-gate 		if (error != 0) {
1350f4b3ec61Sdh155122 			sadb_pfkey_error(ahstack->ah_pfkey_q, mp, error,
1351f4b3ec61Sdh155122 			    diagnostic, ksi->ks_in_serial);
13527c478bd9Sstevel@tonic-gate 		}
13537c478bd9Sstevel@tonic-gate 		/* Else sadb_get_sa() took care of things. */
13547c478bd9Sstevel@tonic-gate 		break;
13557c478bd9Sstevel@tonic-gate 	case SADB_FLUSH:
1356f4b3ec61Sdh155122 		sadbp_flush(&ahstack->ah_sadb, ahstack->ipsecah_netstack);
1357f4b3ec61Sdh155122 		sadb_pfkey_echo(ahstack->ah_pfkey_q, mp, samsg, ksi, NULL);
13587c478bd9Sstevel@tonic-gate 		break;
13597c478bd9Sstevel@tonic-gate 	case SADB_REGISTER:
13607c478bd9Sstevel@tonic-gate 		/*
13617c478bd9Sstevel@tonic-gate 		 * Hmmm, let's do it!  Check for extensions (there should
13627c478bd9Sstevel@tonic-gate 		 * be none), extract the fields, call ah_register_out(),
13637c478bd9Sstevel@tonic-gate 		 * then either free or report an error.
13647c478bd9Sstevel@tonic-gate 		 *
13657c478bd9Sstevel@tonic-gate 		 * Keysock takes care of the PF_KEY bookkeeping for this.
13667c478bd9Sstevel@tonic-gate 		 */
13677c478bd9Sstevel@tonic-gate 		if (ah_register_out(samsg->sadb_msg_seq, samsg->sadb_msg_pid,
1368bd670b35SErik Nordmark 		    ksi->ks_in_serial, ahstack, msg_getcred(mp, NULL))) {
13697c478bd9Sstevel@tonic-gate 			freemsg(mp);
13707c478bd9Sstevel@tonic-gate 		} else {
13717c478bd9Sstevel@tonic-gate 			/*
13727c478bd9Sstevel@tonic-gate 			 * Only way this path hits is if there is a memory
13737c478bd9Sstevel@tonic-gate 			 * failure.  It will not return B_FALSE because of
13747c478bd9Sstevel@tonic-gate 			 * lack of ah_pfkey_q if I am in wput().
13757c478bd9Sstevel@tonic-gate 			 */
1376f4b3ec61Sdh155122 			sadb_pfkey_error(ahstack->ah_pfkey_q, mp, ENOMEM,
1377f4b3ec61Sdh155122 			    diagnostic, ksi->ks_in_serial);
13787c478bd9Sstevel@tonic-gate 		}
13797c478bd9Sstevel@tonic-gate 		break;
13807c478bd9Sstevel@tonic-gate 	case SADB_UPDATE:
138138d95a78Smarkfen 	case SADB_X_UPDATEPAIR:
13827c478bd9Sstevel@tonic-gate 		/*
13837c478bd9Sstevel@tonic-gate 		 * Find a larval, if not there, find a full one and get
13847c478bd9Sstevel@tonic-gate 		 * strict.
13857c478bd9Sstevel@tonic-gate 		 */
138638d95a78Smarkfen 		error = ah_update_sa(mp, ksi, &diagnostic, ahstack,
138738d95a78Smarkfen 		    samsg->sadb_msg_type);
13887c478bd9Sstevel@tonic-gate 		if (error != 0) {
1389f4b3ec61Sdh155122 			sadb_pfkey_error(ahstack->ah_pfkey_q, mp, error,
1390f4b3ec61Sdh155122 			    diagnostic, ksi->ks_in_serial);
13917c478bd9Sstevel@tonic-gate 		}
13927c478bd9Sstevel@tonic-gate 		/* else ah_update_sa() took care of things. */
13937c478bd9Sstevel@tonic-gate 		break;
13947c478bd9Sstevel@tonic-gate 	case SADB_GETSPI:
13957c478bd9Sstevel@tonic-gate 		/*
13967c478bd9Sstevel@tonic-gate 		 * Reserve a new larval entry.
13977c478bd9Sstevel@tonic-gate 		 */
1398f4b3ec61Sdh155122 		ah_getspi(mp, ksi, ahstack);
13997c478bd9Sstevel@tonic-gate 		break;
14007c478bd9Sstevel@tonic-gate 	case SADB_ACQUIRE:
14017c478bd9Sstevel@tonic-gate 		/*
14027c478bd9Sstevel@tonic-gate 		 * Find larval and/or ACQUIRE record and kill it (them), I'm
14037c478bd9Sstevel@tonic-gate 		 * most likely an error.  Inbound ACQUIRE messages should only
14047c478bd9Sstevel@tonic-gate 		 * have the base header.
14057c478bd9Sstevel@tonic-gate 		 */
1406f4b3ec61Sdh155122 		sadb_in_acquire(samsg, &ahstack->ah_sadb, ahstack->ah_pfkey_q,
1407f4b3ec61Sdh155122 		    ahstack->ipsecah_netstack);
14087c478bd9Sstevel@tonic-gate 		freemsg(mp);
14097c478bd9Sstevel@tonic-gate 		break;
14107c478bd9Sstevel@tonic-gate 	case SADB_DUMP:
14117c478bd9Sstevel@tonic-gate 		/*
14127c478bd9Sstevel@tonic-gate 		 * Dump all entries.
14137c478bd9Sstevel@tonic-gate 		 */
1414f4b3ec61Sdh155122 		ah_dump(mp, ksi, ahstack);
14157c478bd9Sstevel@tonic-gate 		/* ah_dump will take care of the return message, etc. */
14167c478bd9Sstevel@tonic-gate 		break;
14177c478bd9Sstevel@tonic-gate 	case SADB_EXPIRE:
14187c478bd9Sstevel@tonic-gate 		/* Should never reach me. */
1419f4b3ec61Sdh155122 		sadb_pfkey_error(ahstack->ah_pfkey_q, mp, EOPNOTSUPP,
1420f4b3ec61Sdh155122 		    diagnostic, ksi->ks_in_serial);
14217c478bd9Sstevel@tonic-gate 		break;
14227c478bd9Sstevel@tonic-gate 	default:
1423f4b3ec61Sdh155122 		sadb_pfkey_error(ahstack->ah_pfkey_q, mp, EINVAL,
14247c478bd9Sstevel@tonic-gate 		    SADB_X_DIAGNOSTIC_UNKNOWN_MSG, ksi->ks_in_serial);
14257c478bd9Sstevel@tonic-gate 		break;
14267c478bd9Sstevel@tonic-gate 	}
14277c478bd9Sstevel@tonic-gate }
14287c478bd9Sstevel@tonic-gate 
14297c478bd9Sstevel@tonic-gate /*
14307c478bd9Sstevel@tonic-gate  * Handle case where PF_KEY says it can't find a keysock for one of my
14317c478bd9Sstevel@tonic-gate  * ACQUIRE messages.
14327c478bd9Sstevel@tonic-gate  */
14337c478bd9Sstevel@tonic-gate static void
1434f4b3ec61Sdh155122 ah_keysock_no_socket(mblk_t *mp, ipsecah_stack_t *ahstack)
14357c478bd9Sstevel@tonic-gate {
14367c478bd9Sstevel@tonic-gate 	sadb_msg_t *samsg;
14377c478bd9Sstevel@tonic-gate 	keysock_out_err_t *kse = (keysock_out_err_t *)mp->b_rptr;
14387c478bd9Sstevel@tonic-gate 
14397c478bd9Sstevel@tonic-gate 	if (mp->b_cont == NULL) {
14407c478bd9Sstevel@tonic-gate 		freemsg(mp);
14417c478bd9Sstevel@tonic-gate 		return;
14427c478bd9Sstevel@tonic-gate 	}
14437c478bd9Sstevel@tonic-gate 	samsg = (sadb_msg_t *)mp->b_cont->b_rptr;
14447c478bd9Sstevel@tonic-gate 
14457c478bd9Sstevel@tonic-gate 	/*
14467c478bd9Sstevel@tonic-gate 	 * If keysock can't find any registered, delete the acquire record
14477c478bd9Sstevel@tonic-gate 	 * immediately, and handle errors.
14487c478bd9Sstevel@tonic-gate 	 */
14497c478bd9Sstevel@tonic-gate 	if (samsg->sadb_msg_type == SADB_ACQUIRE) {
14507c478bd9Sstevel@tonic-gate 		samsg->sadb_msg_errno = kse->ks_err_errno;
14517c478bd9Sstevel@tonic-gate 		samsg->sadb_msg_len = SADB_8TO64(sizeof (*samsg));
14527c478bd9Sstevel@tonic-gate 		/*
1453bd670b35SErik Nordmark 		 * Use the write-side of the ah_pfkey_q
14547c478bd9Sstevel@tonic-gate 		 */
1455f4b3ec61Sdh155122 		sadb_in_acquire(samsg, &ahstack->ah_sadb,
1456f4b3ec61Sdh155122 		    WR(ahstack->ah_pfkey_q), ahstack->ipsecah_netstack);
14577c478bd9Sstevel@tonic-gate 	}
14587c478bd9Sstevel@tonic-gate 
14597c478bd9Sstevel@tonic-gate 	freemsg(mp);
14607c478bd9Sstevel@tonic-gate }
14617c478bd9Sstevel@tonic-gate 
14627c478bd9Sstevel@tonic-gate /*
14637c478bd9Sstevel@tonic-gate  * AH module write put routine.
14647c478bd9Sstevel@tonic-gate  */
14657c478bd9Sstevel@tonic-gate static void
14667c478bd9Sstevel@tonic-gate ipsecah_wput(queue_t *q, mblk_t *mp)
14677c478bd9Sstevel@tonic-gate {
14687c478bd9Sstevel@tonic-gate 	ipsec_info_t *ii;
14697c478bd9Sstevel@tonic-gate 	struct iocblk *iocp;
1470f4b3ec61Sdh155122 	ipsecah_stack_t	*ahstack = (ipsecah_stack_t *)q->q_ptr;
14717c478bd9Sstevel@tonic-gate 
1472f4b3ec61Sdh155122 	ah3dbg(ahstack, ("In ah_wput().\n"));
14737c478bd9Sstevel@tonic-gate 
14747c478bd9Sstevel@tonic-gate 	/* NOTE:  Each case must take care of freeing or passing mp. */
14757c478bd9Sstevel@tonic-gate 	switch (mp->b_datap->db_type) {
14767c478bd9Sstevel@tonic-gate 	case M_CTL:
14777c478bd9Sstevel@tonic-gate 		if ((mp->b_wptr - mp->b_rptr) < sizeof (ipsec_info_t)) {
14787c478bd9Sstevel@tonic-gate 			/* Not big enough message. */
14797c478bd9Sstevel@tonic-gate 			freemsg(mp);
14807c478bd9Sstevel@tonic-gate 			break;
14817c478bd9Sstevel@tonic-gate 		}
14827c478bd9Sstevel@tonic-gate 		ii = (ipsec_info_t *)mp->b_rptr;
14837c478bd9Sstevel@tonic-gate 
14847c478bd9Sstevel@tonic-gate 		switch (ii->ipsec_info_type) {
14857c478bd9Sstevel@tonic-gate 		case KEYSOCK_OUT_ERR:
1486f4b3ec61Sdh155122 			ah1dbg(ahstack, ("Got KEYSOCK_OUT_ERR message.\n"));
1487f4b3ec61Sdh155122 			ah_keysock_no_socket(mp, ahstack);
14887c478bd9Sstevel@tonic-gate 			break;
14897c478bd9Sstevel@tonic-gate 		case KEYSOCK_IN:
1490f4b3ec61Sdh155122 			AH_BUMP_STAT(ahstack, keysock_in);
1491f4b3ec61Sdh155122 			ah3dbg(ahstack, ("Got KEYSOCK_IN message.\n"));
14927c478bd9Sstevel@tonic-gate 
14938810c16bSdanmcd 			/* Parse the message. */
1494f4b3ec61Sdh155122 			ah_parse_pfkey(mp, ahstack);
14957c478bd9Sstevel@tonic-gate 			break;
14967c478bd9Sstevel@tonic-gate 		case KEYSOCK_HELLO:
1497f4b3ec61Sdh155122 			sadb_keysock_hello(&ahstack->ah_pfkey_q, q, mp,
1498f4b3ec61Sdh155122 			    ah_ager, (void *)ahstack, &ahstack->ah_event,
1499f4b3ec61Sdh155122 			    SADB_SATYPE_AH);
15007c478bd9Sstevel@tonic-gate 			break;
15017c478bd9Sstevel@tonic-gate 		default:
1502f4b3ec61Sdh155122 			ah1dbg(ahstack, ("Got M_CTL from above of 0x%x.\n",
15037c478bd9Sstevel@tonic-gate 			    ii->ipsec_info_type));
15047c478bd9Sstevel@tonic-gate 			freemsg(mp);
15057c478bd9Sstevel@tonic-gate 			break;
15067c478bd9Sstevel@tonic-gate 		}
15077c478bd9Sstevel@tonic-gate 		break;
15087c478bd9Sstevel@tonic-gate 	case M_IOCTL:
15097c478bd9Sstevel@tonic-gate 		iocp = (struct iocblk *)mp->b_rptr;
15107c478bd9Sstevel@tonic-gate 		switch (iocp->ioc_cmd) {
15117c478bd9Sstevel@tonic-gate 		case ND_SET:
15127c478bd9Sstevel@tonic-gate 		case ND_GET:
1513f4b3ec61Sdh155122 			if (nd_getset(q, ahstack->ipsecah_g_nd, mp)) {
15147c478bd9Sstevel@tonic-gate 				qreply(q, mp);
15157c478bd9Sstevel@tonic-gate 				return;
15167c478bd9Sstevel@tonic-gate 			} else {
15177c478bd9Sstevel@tonic-gate 				iocp->ioc_error = ENOENT;
15187c478bd9Sstevel@tonic-gate 			}
15197c478bd9Sstevel@tonic-gate 			/* FALLTHRU */
15207c478bd9Sstevel@tonic-gate 		default:
15217c478bd9Sstevel@tonic-gate 			/* We really don't support any other ioctls, do we? */
15227c478bd9Sstevel@tonic-gate 
15237c478bd9Sstevel@tonic-gate 			/* Return EINVAL */
15247c478bd9Sstevel@tonic-gate 			if (iocp->ioc_error != ENOENT)
15257c478bd9Sstevel@tonic-gate 				iocp->ioc_error = EINVAL;
15267c478bd9Sstevel@tonic-gate 			iocp->ioc_count = 0;
15277c478bd9Sstevel@tonic-gate 			mp->b_datap->db_type = M_IOCACK;
15287c478bd9Sstevel@tonic-gate 			qreply(q, mp);
15297c478bd9Sstevel@tonic-gate 			return;
15307c478bd9Sstevel@tonic-gate 		}
15317c478bd9Sstevel@tonic-gate 	default:
1532f4b3ec61Sdh155122 		ah3dbg(ahstack,
1533f4b3ec61Sdh155122 		    ("Got default message, type %d, passing to IP.\n",
15347c478bd9Sstevel@tonic-gate 		    mp->b_datap->db_type));
15357c478bd9Sstevel@tonic-gate 		putnext(q, mp);
15367c478bd9Sstevel@tonic-gate 	}
15377c478bd9Sstevel@tonic-gate }
15387c478bd9Sstevel@tonic-gate 
15395d3b8cb7SBill Sommerfeld /* Refactor me */
15407c478bd9Sstevel@tonic-gate /*
15417c478bd9Sstevel@tonic-gate  * Updating use times can be tricky business if the ipsa_haspeer flag is
15427c478bd9Sstevel@tonic-gate  * set.  This function is called once in an SA's lifetime.
15437c478bd9Sstevel@tonic-gate  *
15447c478bd9Sstevel@tonic-gate  * Caller has to REFRELE "assoc" which is passed in.  This function has
15457c478bd9Sstevel@tonic-gate  * to REFRELE any peer SA that is obtained.
15467c478bd9Sstevel@tonic-gate  */
15477c478bd9Sstevel@tonic-gate static void
15487c478bd9Sstevel@tonic-gate ah_set_usetime(ipsa_t *assoc, boolean_t inbound)
15497c478bd9Sstevel@tonic-gate {
15507c478bd9Sstevel@tonic-gate 	ipsa_t *inassoc, *outassoc;
15517c478bd9Sstevel@tonic-gate 	isaf_t *bucket;
15527c478bd9Sstevel@tonic-gate 	sadb_t *sp;
15537c478bd9Sstevel@tonic-gate 	int outhash;
15547c478bd9Sstevel@tonic-gate 	boolean_t isv6;
1555f4b3ec61Sdh155122 	netstack_t	*ns = assoc->ipsa_netstack;
1556f4b3ec61Sdh155122 	ipsecah_stack_t	*ahstack = ns->netstack_ipsecah;
15577c478bd9Sstevel@tonic-gate 
15587c478bd9Sstevel@tonic-gate 	/* No peer?  No problem! */
15597c478bd9Sstevel@tonic-gate 	if (!assoc->ipsa_haspeer) {
15607c478bd9Sstevel@tonic-gate 		sadb_set_usetime(assoc);
15617c478bd9Sstevel@tonic-gate 		return;
15627c478bd9Sstevel@tonic-gate 	}
15637c478bd9Sstevel@tonic-gate 
15647c478bd9Sstevel@tonic-gate 	/*
15657c478bd9Sstevel@tonic-gate 	 * Otherwise, we want to grab both the original assoc and its peer.
15667c478bd9Sstevel@tonic-gate 	 * There might be a race for this, but if it's a real race, the times
15677c478bd9Sstevel@tonic-gate 	 * will be out-of-synch by at most a second, and since our time
15687c478bd9Sstevel@tonic-gate 	 * granularity is a second, this won't be a problem.
15697c478bd9Sstevel@tonic-gate 	 *
15707c478bd9Sstevel@tonic-gate 	 * If we need tight synchronization on the peer SA, then we need to
15717c478bd9Sstevel@tonic-gate 	 * reconsider.
15727c478bd9Sstevel@tonic-gate 	 */
15737c478bd9Sstevel@tonic-gate 
15747c478bd9Sstevel@tonic-gate 	/* Use address family to select IPv6/IPv4 */
15757c478bd9Sstevel@tonic-gate 	isv6 = (assoc->ipsa_addrfam == AF_INET6);
15767c478bd9Sstevel@tonic-gate 	if (isv6) {
1577f4b3ec61Sdh155122 		sp = &ahstack->ah_sadb.s_v6;
15787c478bd9Sstevel@tonic-gate 	} else {
1579f4b3ec61Sdh155122 		sp = &ahstack->ah_sadb.s_v4;
15807c478bd9Sstevel@tonic-gate 		ASSERT(assoc->ipsa_addrfam == AF_INET);
15817c478bd9Sstevel@tonic-gate 	}
15827c478bd9Sstevel@tonic-gate 	if (inbound) {
15837c478bd9Sstevel@tonic-gate 		inassoc = assoc;
15847c478bd9Sstevel@tonic-gate 		if (isv6)
1585437220cdSdanmcd 			outhash = OUTBOUND_HASH_V6(sp,
1586437220cdSdanmcd 			    *((in6_addr_t *)&inassoc->ipsa_dstaddr));
15877c478bd9Sstevel@tonic-gate 		else
1588437220cdSdanmcd 			outhash = OUTBOUND_HASH_V4(sp,
1589437220cdSdanmcd 			    *((ipaddr_t *)&inassoc->ipsa_dstaddr));
15907c478bd9Sstevel@tonic-gate 		bucket = &sp->sdb_of[outhash];
15917c478bd9Sstevel@tonic-gate 
15927c478bd9Sstevel@tonic-gate 		mutex_enter(&bucket->isaf_lock);
15937c478bd9Sstevel@tonic-gate 		outassoc = ipsec_getassocbyspi(bucket, inassoc->ipsa_spi,
15947c478bd9Sstevel@tonic-gate 		    inassoc->ipsa_srcaddr, inassoc->ipsa_dstaddr,
15957c478bd9Sstevel@tonic-gate 		    inassoc->ipsa_addrfam);
15967c478bd9Sstevel@tonic-gate 		mutex_exit(&bucket->isaf_lock);
15977c478bd9Sstevel@tonic-gate 		if (outassoc == NULL) {
15987c478bd9Sstevel@tonic-gate 			/* Q: Do we wish to set haspeer == B_FALSE? */
15997c478bd9Sstevel@tonic-gate 			ah0dbg(("ah_set_usetime: "
16007c478bd9Sstevel@tonic-gate 			    "can't find peer for inbound.\n"));
16017c478bd9Sstevel@tonic-gate 			sadb_set_usetime(inassoc);
16027c478bd9Sstevel@tonic-gate 			return;
16037c478bd9Sstevel@tonic-gate 		}
16047c478bd9Sstevel@tonic-gate 	} else {
16057c478bd9Sstevel@tonic-gate 		outassoc = assoc;
1606fb87b5d2Ssommerfe 		bucket = INBOUND_BUCKET(sp, outassoc->ipsa_spi);
16077c478bd9Sstevel@tonic-gate 		mutex_enter(&bucket->isaf_lock);
16087c478bd9Sstevel@tonic-gate 		inassoc = ipsec_getassocbyspi(bucket, outassoc->ipsa_spi,
16097c478bd9Sstevel@tonic-gate 		    outassoc->ipsa_srcaddr, outassoc->ipsa_dstaddr,
16107c478bd9Sstevel@tonic-gate 		    outassoc->ipsa_addrfam);
16117c478bd9Sstevel@tonic-gate 		mutex_exit(&bucket->isaf_lock);
16127c478bd9Sstevel@tonic-gate 		if (inassoc == NULL) {
16137c478bd9Sstevel@tonic-gate 			/* Q: Do we wish to set haspeer == B_FALSE? */
16147c478bd9Sstevel@tonic-gate 			ah0dbg(("ah_set_usetime: "
16157c478bd9Sstevel@tonic-gate 			    "can't find peer for outbound.\n"));
16167c478bd9Sstevel@tonic-gate 			sadb_set_usetime(outassoc);
16177c478bd9Sstevel@tonic-gate 			return;
16187c478bd9Sstevel@tonic-gate 		}
16197c478bd9Sstevel@tonic-gate 	}
16207c478bd9Sstevel@tonic-gate 
16217c478bd9Sstevel@tonic-gate 	/* Update usetime on both. */
16227c478bd9Sstevel@tonic-gate 	sadb_set_usetime(inassoc);
16237c478bd9Sstevel@tonic-gate 	sadb_set_usetime(outassoc);
16247c478bd9Sstevel@tonic-gate 
16257c478bd9Sstevel@tonic-gate 	/*
16267c478bd9Sstevel@tonic-gate 	 * REFRELE any peer SA.
16277c478bd9Sstevel@tonic-gate 	 *
16287c478bd9Sstevel@tonic-gate 	 * Because of the multi-line macro nature of IPSA_REFRELE, keep
16297c478bd9Sstevel@tonic-gate 	 * them in { }.
16307c478bd9Sstevel@tonic-gate 	 */
16317c478bd9Sstevel@tonic-gate 	if (inbound) {
16327c478bd9Sstevel@tonic-gate 		IPSA_REFRELE(outassoc);
16337c478bd9Sstevel@tonic-gate 	} else {
16347c478bd9Sstevel@tonic-gate 		IPSA_REFRELE(inassoc);
16357c478bd9Sstevel@tonic-gate 	}
16367c478bd9Sstevel@tonic-gate }
16377c478bd9Sstevel@tonic-gate 
16385d3b8cb7SBill Sommerfeld /* Refactor me */
16397c478bd9Sstevel@tonic-gate /*
16407c478bd9Sstevel@tonic-gate  * Add a number of bytes to what the SA has protected so far.  Return
16417c478bd9Sstevel@tonic-gate  * B_TRUE if the SA can still protect that many bytes.
16427c478bd9Sstevel@tonic-gate  *
16437c478bd9Sstevel@tonic-gate  * Caller must REFRELE the passed-in assoc.  This function must REFRELE
16447c478bd9Sstevel@tonic-gate  * any obtained peer SA.
16457c478bd9Sstevel@tonic-gate  */
16467c478bd9Sstevel@tonic-gate static boolean_t
16477c478bd9Sstevel@tonic-gate ah_age_bytes(ipsa_t *assoc, uint64_t bytes, boolean_t inbound)
16487c478bd9Sstevel@tonic-gate {
16497c478bd9Sstevel@tonic-gate 	ipsa_t *inassoc, *outassoc;
16507c478bd9Sstevel@tonic-gate 	isaf_t *bucket;
16517c478bd9Sstevel@tonic-gate 	boolean_t inrc, outrc, isv6;
16527c478bd9Sstevel@tonic-gate 	sadb_t *sp;
16537c478bd9Sstevel@tonic-gate 	int outhash;
1654f4b3ec61Sdh155122 	netstack_t	*ns = assoc->ipsa_netstack;
1655f4b3ec61Sdh155122 	ipsecah_stack_t	*ahstack = ns->netstack_ipsecah;
16567c478bd9Sstevel@tonic-gate 
16577c478bd9Sstevel@tonic-gate 	/* No peer?  No problem! */
16587c478bd9Sstevel@tonic-gate 	if (!assoc->ipsa_haspeer) {
1659f4b3ec61Sdh155122 		return (sadb_age_bytes(ahstack->ah_pfkey_q, assoc, bytes,
16607c478bd9Sstevel@tonic-gate 		    B_TRUE));
16617c478bd9Sstevel@tonic-gate 	}
16627c478bd9Sstevel@tonic-gate 
16637c478bd9Sstevel@tonic-gate 	/*
16647c478bd9Sstevel@tonic-gate 	 * Otherwise, we want to grab both the original assoc and its peer.
16657c478bd9Sstevel@tonic-gate 	 * There might be a race for this, but if it's a real race, two
16667c478bd9Sstevel@tonic-gate 	 * expire messages may occur.  We limit this by only sending the
16677c478bd9Sstevel@tonic-gate 	 * expire message on one of the peers, we'll pick the inbound
16687c478bd9Sstevel@tonic-gate 	 * arbitrarily.
16697c478bd9Sstevel@tonic-gate 	 *
16707c478bd9Sstevel@tonic-gate 	 * If we need tight synchronization on the peer SA, then we need to
16717c478bd9Sstevel@tonic-gate 	 * reconsider.
16727c478bd9Sstevel@tonic-gate 	 */
16737c478bd9Sstevel@tonic-gate 
16747c478bd9Sstevel@tonic-gate 	/* Pick v4/v6 bucket based on addrfam. */
16757c478bd9Sstevel@tonic-gate 	isv6 = (assoc->ipsa_addrfam == AF_INET6);
16767c478bd9Sstevel@tonic-gate 	if (isv6) {
1677f4b3ec61Sdh155122 		sp = &ahstack->ah_sadb.s_v6;
16787c478bd9Sstevel@tonic-gate 	} else {
1679f4b3ec61Sdh155122 		sp = &ahstack->ah_sadb.s_v4;
16807c478bd9Sstevel@tonic-gate 		ASSERT(assoc->ipsa_addrfam == AF_INET);
16817c478bd9Sstevel@tonic-gate 	}
16827c478bd9Sstevel@tonic-gate 	if (inbound) {
16837c478bd9Sstevel@tonic-gate 		inassoc = assoc;
16847c478bd9Sstevel@tonic-gate 		if (isv6)
1685437220cdSdanmcd 			outhash = OUTBOUND_HASH_V6(sp,
1686437220cdSdanmcd 			    *((in6_addr_t *)&inassoc->ipsa_dstaddr));
16877c478bd9Sstevel@tonic-gate 		else
1688437220cdSdanmcd 			outhash = OUTBOUND_HASH_V4(sp,
1689437220cdSdanmcd 			    *((ipaddr_t *)&inassoc->ipsa_dstaddr));
16907c478bd9Sstevel@tonic-gate 		bucket = &sp->sdb_of[outhash];
16917c478bd9Sstevel@tonic-gate 		mutex_enter(&bucket->isaf_lock);
16927c478bd9Sstevel@tonic-gate 		outassoc = ipsec_getassocbyspi(bucket, inassoc->ipsa_spi,
16937c478bd9Sstevel@tonic-gate 		    inassoc->ipsa_srcaddr, inassoc->ipsa_dstaddr,
16947c478bd9Sstevel@tonic-gate 		    inassoc->ipsa_addrfam);
16957c478bd9Sstevel@tonic-gate 		mutex_exit(&bucket->isaf_lock);
16967c478bd9Sstevel@tonic-gate 		if (outassoc == NULL) {
16977c478bd9Sstevel@tonic-gate 			/* Q: Do we wish to set haspeer == B_FALSE? */
16987c478bd9Sstevel@tonic-gate 			ah0dbg(("ah_age_bytes: "
16997c478bd9Sstevel@tonic-gate 			    "can't find peer for inbound.\n"));
1700f4b3ec61Sdh155122 			return (sadb_age_bytes(ahstack->ah_pfkey_q, inassoc,
17017c478bd9Sstevel@tonic-gate 			    bytes, B_TRUE));
17027c478bd9Sstevel@tonic-gate 		}
17037c478bd9Sstevel@tonic-gate 	} else {
17047c478bd9Sstevel@tonic-gate 		outassoc = assoc;
1705fb87b5d2Ssommerfe 		bucket = INBOUND_BUCKET(sp, outassoc->ipsa_spi);
17067c478bd9Sstevel@tonic-gate 		mutex_enter(&bucket->isaf_lock);
17077c478bd9Sstevel@tonic-gate 		inassoc = ipsec_getassocbyspi(bucket, outassoc->ipsa_spi,
17087c478bd9Sstevel@tonic-gate 		    outassoc->ipsa_srcaddr, outassoc->ipsa_dstaddr,
17097c478bd9Sstevel@tonic-gate 		    outassoc->ipsa_addrfam);
17107c478bd9Sstevel@tonic-gate 		mutex_exit(&bucket->isaf_lock);
17117c478bd9Sstevel@tonic-gate 		if (inassoc == NULL) {
17127c478bd9Sstevel@tonic-gate 			/* Q: Do we wish to set haspeer == B_FALSE? */
17137c478bd9Sstevel@tonic-gate 			ah0dbg(("ah_age_bytes: "
17147c478bd9Sstevel@tonic-gate 			    "can't find peer for outbound.\n"));
1715f4b3ec61Sdh155122 			return (sadb_age_bytes(ahstack->ah_pfkey_q, outassoc,
17167c478bd9Sstevel@tonic-gate 			    bytes, B_TRUE));
17177c478bd9Sstevel@tonic-gate 		}
17187c478bd9Sstevel@tonic-gate 	}
17197c478bd9Sstevel@tonic-gate 
1720f4b3ec61Sdh155122 	inrc = sadb_age_bytes(ahstack->ah_pfkey_q, inassoc, bytes, B_TRUE);
1721f4b3ec61Sdh155122 	outrc = sadb_age_bytes(ahstack->ah_pfkey_q, outassoc, bytes, B_FALSE);
17227c478bd9Sstevel@tonic-gate 
17237c478bd9Sstevel@tonic-gate 	/*
17247c478bd9Sstevel@tonic-gate 	 * REFRELE any peer SA.
17257c478bd9Sstevel@tonic-gate 	 *
17267c478bd9Sstevel@tonic-gate 	 * Because of the multi-line macro nature of IPSA_REFRELE, keep
17277c478bd9Sstevel@tonic-gate 	 * them in { }.
17287c478bd9Sstevel@tonic-gate 	 */
17297c478bd9Sstevel@tonic-gate 	if (inbound) {
17307c478bd9Sstevel@tonic-gate 		IPSA_REFRELE(outassoc);
17317c478bd9Sstevel@tonic-gate 	} else {
17327c478bd9Sstevel@tonic-gate 		IPSA_REFRELE(inassoc);
17337c478bd9Sstevel@tonic-gate 	}
17347c478bd9Sstevel@tonic-gate 
17357c478bd9Sstevel@tonic-gate 	return (inrc && outrc);
17367c478bd9Sstevel@tonic-gate }
17377c478bd9Sstevel@tonic-gate 
17387c478bd9Sstevel@tonic-gate /*
17397c478bd9Sstevel@tonic-gate  * Perform the really difficult work of inserting the proposed situation.
17407c478bd9Sstevel@tonic-gate  * Called while holding the algorithm lock.
17417c478bd9Sstevel@tonic-gate  */
17427c478bd9Sstevel@tonic-gate static void
1743bd670b35SErik Nordmark ah_insert_prop(sadb_prop_t *prop, ipsacq_t *acqrec, uint_t combs,
1744bd670b35SErik Nordmark     netstack_t *ns)
17457c478bd9Sstevel@tonic-gate {
17467c478bd9Sstevel@tonic-gate 	sadb_comb_t *comb = (sadb_comb_t *)(prop + 1);
17477c478bd9Sstevel@tonic-gate 	ipsec_action_t *ap;
17487c478bd9Sstevel@tonic-gate 	ipsec_prot_t *prot;
1749bd670b35SErik Nordmark 	ipsecah_stack_t	*ahstack = ns->netstack_ipsecah;
1750bd670b35SErik Nordmark 	ipsec_stack_t	*ipss = ns->netstack_ipsec;
17517c478bd9Sstevel@tonic-gate 
1752f4b3ec61Sdh155122 	ASSERT(MUTEX_HELD(&ipss->ipsec_alg_lock));
1753f4b3ec61Sdh155122 
17547c478bd9Sstevel@tonic-gate 	prop->sadb_prop_exttype = SADB_EXT_PROPOSAL;
17557c478bd9Sstevel@tonic-gate 	prop->sadb_prop_len = SADB_8TO64(sizeof (sadb_prop_t));
17567c478bd9Sstevel@tonic-gate 	*(uint32_t *)(&prop->sadb_prop_replay) = 0;	/* Quick zero-out! */
17577c478bd9Sstevel@tonic-gate 
1758f4b3ec61Sdh155122 	prop->sadb_prop_replay = ahstack->ipsecah_replay_size;
17597c478bd9Sstevel@tonic-gate 
17607c478bd9Sstevel@tonic-gate 	/*
17617c478bd9Sstevel@tonic-gate 	 * Based upon algorithm properties, and what-not, prioritize a
1762bd670b35SErik Nordmark 	 * proposal, based on the ordering of the AH algorithms in the
1763bd670b35SErik Nordmark 	 * alternatives in the policy rule or socket that was placed
1764bd670b35SErik Nordmark 	 * in the acquire record.
17657c478bd9Sstevel@tonic-gate 	 */
17667c478bd9Sstevel@tonic-gate 
17677c478bd9Sstevel@tonic-gate 	for (ap = acqrec->ipsacq_act; ap != NULL;
17687c478bd9Sstevel@tonic-gate 	    ap = ap->ipa_next) {
17697c478bd9Sstevel@tonic-gate 		ipsec_alginfo_t *aalg;
17707c478bd9Sstevel@tonic-gate 
17717c478bd9Sstevel@tonic-gate 		if ((ap->ipa_act.ipa_type != IPSEC_POLICY_APPLY) ||
17727c478bd9Sstevel@tonic-gate 		    (!ap->ipa_act.ipa_apply.ipp_use_ah))
17737c478bd9Sstevel@tonic-gate 			continue;
17747c478bd9Sstevel@tonic-gate 
17757c478bd9Sstevel@tonic-gate 		prot = &ap->ipa_act.ipa_apply;
17767c478bd9Sstevel@tonic-gate 
17777c478bd9Sstevel@tonic-gate 		ASSERT(prot->ipp_auth_alg > 0);
17787c478bd9Sstevel@tonic-gate 
1779f4b3ec61Sdh155122 		aalg = ipss->ipsec_alglists[IPSEC_ALG_AUTH]
1780f4b3ec61Sdh155122 		    [prot->ipp_auth_alg];
17817c478bd9Sstevel@tonic-gate 		if (aalg == NULL || !ALG_VALID(aalg))
17827c478bd9Sstevel@tonic-gate 			continue;
17837c478bd9Sstevel@tonic-gate 
17847c478bd9Sstevel@tonic-gate 		/* XXX check aalg for duplicates??.. */
17857c478bd9Sstevel@tonic-gate 
17867c478bd9Sstevel@tonic-gate 		comb->sadb_comb_flags = 0;
17877c478bd9Sstevel@tonic-gate 		comb->sadb_comb_reserved = 0;
17887c478bd9Sstevel@tonic-gate 		comb->sadb_comb_encrypt = 0;
17897c478bd9Sstevel@tonic-gate 		comb->sadb_comb_encrypt_minbits = 0;
17907c478bd9Sstevel@tonic-gate 		comb->sadb_comb_encrypt_maxbits = 0;
17917c478bd9Sstevel@tonic-gate 
17927c478bd9Sstevel@tonic-gate 		comb->sadb_comb_auth = aalg->alg_id;
17937845d282Sdanmcd 		comb->sadb_comb_auth_minbits =
17947845d282Sdanmcd 		    MAX(prot->ipp_ah_minbits, aalg->alg_ef_minbits);
17957845d282Sdanmcd 		comb->sadb_comb_auth_maxbits =
17967845d282Sdanmcd 		    MIN(prot->ipp_ah_maxbits, aalg->alg_ef_maxbits);
17977c478bd9Sstevel@tonic-gate 
17987c478bd9Sstevel@tonic-gate 		/*
17997c478bd9Sstevel@tonic-gate 		 * The following may be based on algorithm
18007c478bd9Sstevel@tonic-gate 		 * properties, but in the meantime, we just pick
18017c478bd9Sstevel@tonic-gate 		 * some good, sensible numbers.  Key mgmt. can
18027c478bd9Sstevel@tonic-gate 		 * (and perhaps should) be the place to finalize
18037c478bd9Sstevel@tonic-gate 		 * such decisions.
18047c478bd9Sstevel@tonic-gate 		 */
18057c478bd9Sstevel@tonic-gate 
18067c478bd9Sstevel@tonic-gate 		/*
18077c478bd9Sstevel@tonic-gate 		 * No limits on allocations, since we really don't
18087c478bd9Sstevel@tonic-gate 		 * support that concept currently.
18097c478bd9Sstevel@tonic-gate 		 */
18107c478bd9Sstevel@tonic-gate 		comb->sadb_comb_soft_allocations = 0;
18117c478bd9Sstevel@tonic-gate 		comb->sadb_comb_hard_allocations = 0;
18127c478bd9Sstevel@tonic-gate 
18137c478bd9Sstevel@tonic-gate 		/*
18147c478bd9Sstevel@tonic-gate 		 * These may want to come from policy rule..
18157c478bd9Sstevel@tonic-gate 		 */
1816f4b3ec61Sdh155122 		comb->sadb_comb_soft_bytes =
1817f4b3ec61Sdh155122 		    ahstack->ipsecah_default_soft_bytes;
1818f4b3ec61Sdh155122 		comb->sadb_comb_hard_bytes =
1819f4b3ec61Sdh155122 		    ahstack->ipsecah_default_hard_bytes;
1820f4b3ec61Sdh155122 		comb->sadb_comb_soft_addtime =
1821f4b3ec61Sdh155122 		    ahstack->ipsecah_default_soft_addtime;
1822f4b3ec61Sdh155122 		comb->sadb_comb_hard_addtime =
1823f4b3ec61Sdh155122 		    ahstack->ipsecah_default_hard_addtime;
1824f4b3ec61Sdh155122 		comb->sadb_comb_soft_usetime =
1825f4b3ec61Sdh155122 		    ahstack->ipsecah_default_soft_usetime;
1826f4b3ec61Sdh155122 		comb->sadb_comb_hard_usetime =
1827f4b3ec61Sdh155122 		    ahstack->ipsecah_default_hard_usetime;
18287c478bd9Sstevel@tonic-gate 
18297c478bd9Sstevel@tonic-gate 		prop->sadb_prop_len += SADB_8TO64(sizeof (*comb));
18307c478bd9Sstevel@tonic-gate 		if (--combs == 0)
18317c478bd9Sstevel@tonic-gate 			return;	/* out of space.. */
18327c478bd9Sstevel@tonic-gate 		comb++;
18337c478bd9Sstevel@tonic-gate 	}
18347c478bd9Sstevel@tonic-gate }
18357c478bd9Sstevel@tonic-gate 
18367c478bd9Sstevel@tonic-gate /*
18377c478bd9Sstevel@tonic-gate  * Prepare and actually send the SADB_ACQUIRE message to PF_KEY.
18387c478bd9Sstevel@tonic-gate  */
18397c478bd9Sstevel@tonic-gate static void
1840f4b3ec61Sdh155122 ah_send_acquire(ipsacq_t *acqrec, mblk_t *extended, netstack_t *ns)
18417c478bd9Sstevel@tonic-gate {
18428810c16bSdanmcd 	uint_t combs;
18437c478bd9Sstevel@tonic-gate 	sadb_msg_t *samsg;
18447c478bd9Sstevel@tonic-gate 	sadb_prop_t *prop;
18458810c16bSdanmcd 	mblk_t *pfkeymp, *msgmp;
1846f4b3ec61Sdh155122 	ipsecah_stack_t	*ahstack = ns->netstack_ipsecah;
1847f4b3ec61Sdh155122 	ipsec_stack_t	*ipss = ns->netstack_ipsec;
18487c478bd9Sstevel@tonic-gate 
1849f4b3ec61Sdh155122 	AH_BUMP_STAT(ahstack, acquire_requests);
18507c478bd9Sstevel@tonic-gate 
18517ba9381bSpwernau 	if (ahstack->ah_pfkey_q == NULL) {
18527ba9381bSpwernau 		mutex_exit(&acqrec->ipsacq_lock);
18538810c16bSdanmcd 		return;
18547ba9381bSpwernau 	}
18557c478bd9Sstevel@tonic-gate 
18567c478bd9Sstevel@tonic-gate 	/* Set up ACQUIRE. */
1857f4b3ec61Sdh155122 	pfkeymp = sadb_setup_acquire(acqrec, SADB_SATYPE_AH,
1858f4b3ec61Sdh155122 	    ns->netstack_ipsec);
18598810c16bSdanmcd 	if (pfkeymp == NULL) {
18607c478bd9Sstevel@tonic-gate 		ah0dbg(("sadb_setup_acquire failed.\n"));
18617ba9381bSpwernau 		mutex_exit(&acqrec->ipsacq_lock);
18628810c16bSdanmcd 		return;
18637c478bd9Sstevel@tonic-gate 	}
1864f4b3ec61Sdh155122 	ASSERT(MUTEX_HELD(&ipss->ipsec_alg_lock));
1865f4b3ec61Sdh155122 	combs = ipss->ipsec_nalgs[IPSEC_ALG_AUTH];
18668810c16bSdanmcd 	msgmp = pfkeymp->b_cont;
18678810c16bSdanmcd 	samsg = (sadb_msg_t *)(msgmp->b_rptr);
18687c478bd9Sstevel@tonic-gate 
18697c478bd9Sstevel@tonic-gate 	/* Insert proposal here. */
18707c478bd9Sstevel@tonic-gate 
18717c478bd9Sstevel@tonic-gate 	prop = (sadb_prop_t *)(((uint64_t *)samsg) + samsg->sadb_msg_len);
1872bd670b35SErik Nordmark 	ah_insert_prop(prop, acqrec, combs, ns);
18737c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_len += prop->sadb_prop_len;
18747c478bd9Sstevel@tonic-gate 	msgmp->b_wptr += SADB_64TO8(samsg->sadb_msg_len);
18757c478bd9Sstevel@tonic-gate 
1876f4b3ec61Sdh155122 	mutex_exit(&ipss->ipsec_alg_lock);
18777c478bd9Sstevel@tonic-gate 
18787c478bd9Sstevel@tonic-gate 	/*
18797c478bd9Sstevel@tonic-gate 	 * Must mutex_exit() before sending PF_KEY message up, in
18807c478bd9Sstevel@tonic-gate 	 * order to avoid recursive mutex_enter() if there are no registered
18817c478bd9Sstevel@tonic-gate 	 * listeners.
18827c478bd9Sstevel@tonic-gate 	 *
18837c478bd9Sstevel@tonic-gate 	 * Once I've sent the message, I'm cool anyway.
18847c478bd9Sstevel@tonic-gate 	 */
18857c478bd9Sstevel@tonic-gate 	mutex_exit(&acqrec->ipsacq_lock);
18867c478bd9Sstevel@tonic-gate 	if (extended != NULL) {
1887f4b3ec61Sdh155122 		putnext(ahstack->ah_pfkey_q, extended);
18887c478bd9Sstevel@tonic-gate 	}
1889f4b3ec61Sdh155122 	putnext(ahstack->ah_pfkey_q, pfkeymp);
18907c478bd9Sstevel@tonic-gate }
18917c478bd9Sstevel@tonic-gate 
18925d3b8cb7SBill Sommerfeld /* Refactor me */
18937c478bd9Sstevel@tonic-gate /*
18947c478bd9Sstevel@tonic-gate  * Handle the SADB_GETSPI message.  Create a larval SA.
18957c478bd9Sstevel@tonic-gate  */
18967c478bd9Sstevel@tonic-gate static void
1897f4b3ec61Sdh155122 ah_getspi(mblk_t *mp, keysock_in_t *ksi, ipsecah_stack_t *ahstack)
18987c478bd9Sstevel@tonic-gate {
18997c478bd9Sstevel@tonic-gate 	ipsa_t *newbie, *target;
19007c478bd9Sstevel@tonic-gate 	isaf_t *outbound, *inbound;
19017c478bd9Sstevel@tonic-gate 	int rc, diagnostic;
19027c478bd9Sstevel@tonic-gate 	sadb_sa_t *assoc;
19037c478bd9Sstevel@tonic-gate 	keysock_out_t *kso;
19047c478bd9Sstevel@tonic-gate 	uint32_t newspi;
19057c478bd9Sstevel@tonic-gate 
19067c478bd9Sstevel@tonic-gate 	/*
19077c478bd9Sstevel@tonic-gate 	 * Randomly generate a proposed SPI value.
19087c478bd9Sstevel@tonic-gate 	 */
19099c2c14abSThejaswini Singarajipura 	if (cl_inet_getspi != NULL) {
19108e4b770fSLu Huafeng 		cl_inet_getspi(ahstack->ipsecah_netstack->netstack_stackid,
19118e4b770fSLu Huafeng 		    IPPROTO_AH, (uint8_t *)&newspi, sizeof (uint32_t), NULL);
19129c2c14abSThejaswini Singarajipura 	} else {
19139c2c14abSThejaswini Singarajipura 		(void) random_get_pseudo_bytes((uint8_t *)&newspi,
19149c2c14abSThejaswini Singarajipura 		    sizeof (uint32_t));
19159c2c14abSThejaswini Singarajipura 	}
1916f4b3ec61Sdh155122 	newbie = sadb_getspi(ksi, newspi, &diagnostic,
19179c2c14abSThejaswini Singarajipura 	    ahstack->ipsecah_netstack, IPPROTO_AH);
19187c478bd9Sstevel@tonic-gate 
19197c478bd9Sstevel@tonic-gate 	if (newbie == NULL) {
1920f4b3ec61Sdh155122 		sadb_pfkey_error(ahstack->ah_pfkey_q, mp, ENOMEM, diagnostic,
19217c478bd9Sstevel@tonic-gate 		    ksi->ks_in_serial);
19227c478bd9Sstevel@tonic-gate 		return;
19237c478bd9Sstevel@tonic-gate 	} else if (newbie == (ipsa_t *)-1) {
1924f4b3ec61Sdh155122 		sadb_pfkey_error(ahstack->ah_pfkey_q, mp, EINVAL, diagnostic,
19257c478bd9Sstevel@tonic-gate 		    ksi->ks_in_serial);
19267c478bd9Sstevel@tonic-gate 		return;
19277c478bd9Sstevel@tonic-gate 	}
19287c478bd9Sstevel@tonic-gate 
19297c478bd9Sstevel@tonic-gate 	/*
19307c478bd9Sstevel@tonic-gate 	 * XXX - We may randomly collide.  We really should recover from this.
19317c478bd9Sstevel@tonic-gate 	 *	 Unfortunately, that could require spending way-too-much-time
19327c478bd9Sstevel@tonic-gate 	 *	 in here.  For now, let the user retry.
19337c478bd9Sstevel@tonic-gate 	 */
19347c478bd9Sstevel@tonic-gate 
19357c478bd9Sstevel@tonic-gate 	if (newbie->ipsa_addrfam == AF_INET6) {
1936f4b3ec61Sdh155122 		outbound = OUTBOUND_BUCKET_V6(&ahstack->ah_sadb.s_v6,
1937fb87b5d2Ssommerfe 		    *(uint32_t *)(newbie->ipsa_dstaddr));
1938f4b3ec61Sdh155122 		inbound = INBOUND_BUCKET(&ahstack->ah_sadb.s_v6,
1939f4b3ec61Sdh155122 		    newbie->ipsa_spi);
19407c478bd9Sstevel@tonic-gate 	} else {
1941f4b3ec61Sdh155122 		outbound = OUTBOUND_BUCKET_V4(&ahstack->ah_sadb.s_v4,
1942fb87b5d2Ssommerfe 		    *(uint32_t *)(newbie->ipsa_dstaddr));
1943f4b3ec61Sdh155122 		inbound = INBOUND_BUCKET(&ahstack->ah_sadb.s_v4,
1944f4b3ec61Sdh155122 		    newbie->ipsa_spi);
19457c478bd9Sstevel@tonic-gate 	}
19467c478bd9Sstevel@tonic-gate 
19477c478bd9Sstevel@tonic-gate 	mutex_enter(&outbound->isaf_lock);
19487c478bd9Sstevel@tonic-gate 	mutex_enter(&inbound->isaf_lock);
19497c478bd9Sstevel@tonic-gate 
19507c478bd9Sstevel@tonic-gate 	/*
19517c478bd9Sstevel@tonic-gate 	 * Check for collisions (i.e. did sadb_getspi() return with something
19527c478bd9Sstevel@tonic-gate 	 * that already exists?).
19537c478bd9Sstevel@tonic-gate 	 *
19547c478bd9Sstevel@tonic-gate 	 * Try outbound first.  Even though SADB_GETSPI is traditionally
19557c478bd9Sstevel@tonic-gate 	 * for inbound SAs, you never know what a user might do.
19567c478bd9Sstevel@tonic-gate 	 */
19577c478bd9Sstevel@tonic-gate 	target = ipsec_getassocbyspi(outbound, newbie->ipsa_spi,
19587c478bd9Sstevel@tonic-gate 	    newbie->ipsa_srcaddr, newbie->ipsa_dstaddr, newbie->ipsa_addrfam);
19597c478bd9Sstevel@tonic-gate 	if (target == NULL) {
19607c478bd9Sstevel@tonic-gate 		target = ipsec_getassocbyspi(inbound, newbie->ipsa_spi,
19617c478bd9Sstevel@tonic-gate 		    newbie->ipsa_srcaddr, newbie->ipsa_dstaddr,
19627c478bd9Sstevel@tonic-gate 		    newbie->ipsa_addrfam);
19637c478bd9Sstevel@tonic-gate 	}
19647c478bd9Sstevel@tonic-gate 
19657c478bd9Sstevel@tonic-gate 	/*
19667c478bd9Sstevel@tonic-gate 	 * I don't have collisions elsewhere!
19677c478bd9Sstevel@tonic-gate 	 * (Nor will I because I'm still holding inbound/outbound locks.)
19687c478bd9Sstevel@tonic-gate 	 */
19697c478bd9Sstevel@tonic-gate 
19707c478bd9Sstevel@tonic-gate 	if (target != NULL) {
19717c478bd9Sstevel@tonic-gate 		rc = EEXIST;
19727c478bd9Sstevel@tonic-gate 		IPSA_REFRELE(target);
19737c478bd9Sstevel@tonic-gate 	} else {
19747c478bd9Sstevel@tonic-gate 		/*
19757c478bd9Sstevel@tonic-gate 		 * sadb_insertassoc() also checks for collisions, so
19767c478bd9Sstevel@tonic-gate 		 * if there's a colliding larval entry, rc will be set
19777c478bd9Sstevel@tonic-gate 		 * to EEXIST.
19787c478bd9Sstevel@tonic-gate 		 */
19797c478bd9Sstevel@tonic-gate 		rc = sadb_insertassoc(newbie, inbound);
1980437220cdSdanmcd 		newbie->ipsa_hardexpiretime = gethrestime_sec();
1981f4b3ec61Sdh155122 		newbie->ipsa_hardexpiretime += ahstack->ipsecah_larval_timeout;
19827c478bd9Sstevel@tonic-gate 	}
19837c478bd9Sstevel@tonic-gate 
19847c478bd9Sstevel@tonic-gate 	/*
19857c478bd9Sstevel@tonic-gate 	 * Can exit outbound mutex.  Hold inbound until we're done with
19867c478bd9Sstevel@tonic-gate 	 * newbie.
19877c478bd9Sstevel@tonic-gate 	 */
19887c478bd9Sstevel@tonic-gate 	mutex_exit(&outbound->isaf_lock);
19897c478bd9Sstevel@tonic-gate 
19907c478bd9Sstevel@tonic-gate 	if (rc != 0) {
19917c478bd9Sstevel@tonic-gate 		mutex_exit(&inbound->isaf_lock);
19927c478bd9Sstevel@tonic-gate 		IPSA_REFRELE(newbie);
1993f4b3ec61Sdh155122 		sadb_pfkey_error(ahstack->ah_pfkey_q, mp, rc,
1994f4b3ec61Sdh155122 		    SADB_X_DIAGNOSTIC_NONE, ksi->ks_in_serial);
19957c478bd9Sstevel@tonic-gate 		return;
19967c478bd9Sstevel@tonic-gate 	}
19977c478bd9Sstevel@tonic-gate 
19987c478bd9Sstevel@tonic-gate 	/* Can write here because I'm still holding the bucket lock. */
19997c478bd9Sstevel@tonic-gate 	newbie->ipsa_type = SADB_SATYPE_AH;
20007c478bd9Sstevel@tonic-gate 
20017c478bd9Sstevel@tonic-gate 	/*
20027c478bd9Sstevel@tonic-gate 	 * Construct successful return message.  We have one thing going
20037c478bd9Sstevel@tonic-gate 	 * for us in PF_KEY v2.  That's the fact that
20047c478bd9Sstevel@tonic-gate 	 *	sizeof (sadb_spirange_t) == sizeof (sadb_sa_t)
20057c478bd9Sstevel@tonic-gate 	 */
20067c478bd9Sstevel@tonic-gate 	assoc = (sadb_sa_t *)ksi->ks_in_extv[SADB_EXT_SPIRANGE];
20077c478bd9Sstevel@tonic-gate 	assoc->sadb_sa_exttype = SADB_EXT_SA;
20087c478bd9Sstevel@tonic-gate 	assoc->sadb_sa_spi = newbie->ipsa_spi;
20097c478bd9Sstevel@tonic-gate 	*((uint64_t *)(&assoc->sadb_sa_replay)) = 0;
20107c478bd9Sstevel@tonic-gate 	mutex_exit(&inbound->isaf_lock);
20117c478bd9Sstevel@tonic-gate 
20127c478bd9Sstevel@tonic-gate 	/* Convert KEYSOCK_IN to KEYSOCK_OUT. */
20137c478bd9Sstevel@tonic-gate 	kso = (keysock_out_t *)ksi;
20147c478bd9Sstevel@tonic-gate 	kso->ks_out_len = sizeof (*kso);
20157c478bd9Sstevel@tonic-gate 	kso->ks_out_serial = ksi->ks_in_serial;
20167c478bd9Sstevel@tonic-gate 	kso->ks_out_type = KEYSOCK_OUT;
20177c478bd9Sstevel@tonic-gate 
20187c478bd9Sstevel@tonic-gate 	/*
20197c478bd9Sstevel@tonic-gate 	 * Can safely putnext() to ah_pfkey_q, because this is a turnaround
20207c478bd9Sstevel@tonic-gate 	 * from the ah_pfkey_q.
20217c478bd9Sstevel@tonic-gate 	 */
2022f4b3ec61Sdh155122 	putnext(ahstack->ah_pfkey_q, mp);
20237c478bd9Sstevel@tonic-gate }
20247c478bd9Sstevel@tonic-gate 
20257c478bd9Sstevel@tonic-gate /*
20267c478bd9Sstevel@tonic-gate  * IPv6 sends up the ICMP errors for validation and the removal of the AH
20277c478bd9Sstevel@tonic-gate  * header.
2028bd670b35SErik Nordmark  * If succesful, the mp has been modified to not include the AH header so
2029bd670b35SErik Nordmark  * that the caller can fanout to the ULP's icmp error handler.
20307c478bd9Sstevel@tonic-gate  */
2031bd670b35SErik Nordmark static mblk_t *
2032bd670b35SErik Nordmark ah_icmp_error_v6(mblk_t *mp, ip_recv_attr_t *ira, ipsecah_stack_t *ahstack)
20337c478bd9Sstevel@tonic-gate {
20347c478bd9Sstevel@tonic-gate 	ip6_t *ip6h, *oip6h;
20357c478bd9Sstevel@tonic-gate 	uint16_t hdr_length, ah_length;
20367c478bd9Sstevel@tonic-gate 	uint8_t *nexthdrp;
20377c478bd9Sstevel@tonic-gate 	ah_t *ah;
20387c478bd9Sstevel@tonic-gate 	icmp6_t *icmp6;
20397c478bd9Sstevel@tonic-gate 	isaf_t *isaf;
20407c478bd9Sstevel@tonic-gate 	ipsa_t *assoc;
20417c478bd9Sstevel@tonic-gate 	uint8_t *post_ah_ptr;
2042f4b3ec61Sdh155122 	ipsec_stack_t	*ipss = ahstack->ipsecah_netstack->netstack_ipsec;
20437c478bd9Sstevel@tonic-gate 
20447c478bd9Sstevel@tonic-gate 	/*
20457c478bd9Sstevel@tonic-gate 	 * Eat the cost of a pullupmsg() for now.  It makes the rest of this
20467c478bd9Sstevel@tonic-gate 	 * code far less convoluted.
20477c478bd9Sstevel@tonic-gate 	 */
20487c478bd9Sstevel@tonic-gate 	if (!pullupmsg(mp, -1) ||
20497c478bd9Sstevel@tonic-gate 	    !ip_hdr_length_nexthdr_v6(mp, (ip6_t *)mp->b_rptr, &hdr_length,
20507c478bd9Sstevel@tonic-gate 	    &nexthdrp) ||
20517c478bd9Sstevel@tonic-gate 	    mp->b_rptr + hdr_length + sizeof (icmp6_t) + sizeof (ip6_t) +
20527c478bd9Sstevel@tonic-gate 	    sizeof (ah_t) > mp->b_wptr) {
2053f4b3ec61Sdh155122 		IP_AH_BUMP_STAT(ipss, in_discards);
2054bd670b35SErik Nordmark 		ip_drop_packet(mp, B_TRUE, ira->ira_ill,
2055f4b3ec61Sdh155122 		    DROPPER(ipss, ipds_ah_nomem),
2056f4b3ec61Sdh155122 		    &ahstack->ah_dropper);
2057bd670b35SErik Nordmark 		return (NULL);
20587c478bd9Sstevel@tonic-gate 	}
20597c478bd9Sstevel@tonic-gate 
20607c478bd9Sstevel@tonic-gate 	oip6h = (ip6_t *)mp->b_rptr;
20617c478bd9Sstevel@tonic-gate 	icmp6 = (icmp6_t *)((uint8_t *)oip6h + hdr_length);
20627c478bd9Sstevel@tonic-gate 	ip6h = (ip6_t *)(icmp6 + 1);
20637c478bd9Sstevel@tonic-gate 	if (!ip_hdr_length_nexthdr_v6(mp, ip6h, &hdr_length, &nexthdrp)) {
2064f4b3ec61Sdh155122 		IP_AH_BUMP_STAT(ipss, in_discards);
2065bd670b35SErik Nordmark 		ip_drop_packet(mp, B_TRUE, ira->ira_ill,
2066f4b3ec61Sdh155122 		    DROPPER(ipss, ipds_ah_bad_v6_hdrs),
2067f4b3ec61Sdh155122 		    &ahstack->ah_dropper);
2068bd670b35SErik Nordmark 		return (NULL);
20697c478bd9Sstevel@tonic-gate 	}
20707c478bd9Sstevel@tonic-gate 	ah = (ah_t *)((uint8_t *)ip6h + hdr_length);
20717c478bd9Sstevel@tonic-gate 
2072f4b3ec61Sdh155122 	isaf = OUTBOUND_BUCKET_V6(&ahstack->ah_sadb.s_v6, ip6h->ip6_dst);
20737c478bd9Sstevel@tonic-gate 	mutex_enter(&isaf->isaf_lock);
20747c478bd9Sstevel@tonic-gate 	assoc = ipsec_getassocbyspi(isaf, ah->ah_spi,
20757c478bd9Sstevel@tonic-gate 	    (uint32_t *)&ip6h->ip6_src, (uint32_t *)&ip6h->ip6_dst, AF_INET6);
20767c478bd9Sstevel@tonic-gate 	mutex_exit(&isaf->isaf_lock);
20777c478bd9Sstevel@tonic-gate 
20787c478bd9Sstevel@tonic-gate 	if (assoc == NULL) {
2079f4b3ec61Sdh155122 		IP_AH_BUMP_STAT(ipss, lookup_failure);
2080f4b3ec61Sdh155122 		IP_AH_BUMP_STAT(ipss, in_discards);
2081f4b3ec61Sdh155122 		if (ahstack->ipsecah_log_unknown_spi) {
20827c478bd9Sstevel@tonic-gate 			ipsec_assocfailure(info.mi_idnum, 0, 0,
20837c478bd9Sstevel@tonic-gate 			    SL_CONSOLE | SL_WARN | SL_ERROR,
20847c478bd9Sstevel@tonic-gate 			    "Bad ICMP message - No association for the "
20857c478bd9Sstevel@tonic-gate 			    "attached AH header whose spi is 0x%x, "
20867c478bd9Sstevel@tonic-gate 			    "sender is 0x%x\n",
2087f4b3ec61Sdh155122 			    ah->ah_spi, &oip6h->ip6_src, AF_INET6,
2088f4b3ec61Sdh155122 			    ahstack->ipsecah_netstack);
20897c478bd9Sstevel@tonic-gate 		}
2090bd670b35SErik Nordmark 		ip_drop_packet(mp, B_TRUE, ira->ira_ill,
2091f4b3ec61Sdh155122 		    DROPPER(ipss, ipds_ah_no_sa),
2092f4b3ec61Sdh155122 		    &ahstack->ah_dropper);
2093bd670b35SErik Nordmark 		return (NULL);
20947c478bd9Sstevel@tonic-gate 	}
20957c478bd9Sstevel@tonic-gate 
20967c478bd9Sstevel@tonic-gate 	IPSA_REFRELE(assoc);
20977c478bd9Sstevel@tonic-gate 
20987c478bd9Sstevel@tonic-gate 	/*
20997c478bd9Sstevel@tonic-gate 	 * There seems to be a valid association. If there is enough of AH
21007c478bd9Sstevel@tonic-gate 	 * header remove it, otherwise bail.  One could check whether it has
21017c478bd9Sstevel@tonic-gate 	 * complete AH header plus 8 bytes but it does not make sense if an
21027c478bd9Sstevel@tonic-gate 	 * icmp error is returned for ICMP messages e.g ICMP time exceeded,
21037c478bd9Sstevel@tonic-gate 	 * that are being sent up. Let the caller figure out.
21047c478bd9Sstevel@tonic-gate 	 *
21057c478bd9Sstevel@tonic-gate 	 * NOTE: ah_length is the number of 32 bit words minus 2.
21067c478bd9Sstevel@tonic-gate 	 */
21077c478bd9Sstevel@tonic-gate 	ah_length = (ah->ah_length << 2) + 8;
21087c478bd9Sstevel@tonic-gate 	post_ah_ptr = (uint8_t *)ah + ah_length;
21097c478bd9Sstevel@tonic-gate 
21107c478bd9Sstevel@tonic-gate 	if (post_ah_ptr > mp->b_wptr) {
2111f4b3ec61Sdh155122 		IP_AH_BUMP_STAT(ipss, in_discards);
2112bd670b35SErik Nordmark 		ip_drop_packet(mp, B_TRUE, ira->ira_ill,
2113f4b3ec61Sdh155122 		    DROPPER(ipss, ipds_ah_bad_length),
2114f4b3ec61Sdh155122 		    &ahstack->ah_dropper);
2115bd670b35SErik Nordmark 		return (NULL);
21167c478bd9Sstevel@tonic-gate 	}
21177c478bd9Sstevel@tonic-gate 
21187c478bd9Sstevel@tonic-gate 	ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) - ah_length);
21197c478bd9Sstevel@tonic-gate 	*nexthdrp = ah->ah_nexthdr;
21207c478bd9Sstevel@tonic-gate 	ovbcopy(post_ah_ptr, ah,
21217c478bd9Sstevel@tonic-gate 	    (size_t)((uintptr_t)mp->b_wptr - (uintptr_t)post_ah_ptr));
21227c478bd9Sstevel@tonic-gate 	mp->b_wptr -= ah_length;
21237c478bd9Sstevel@tonic-gate 
2124bd670b35SErik Nordmark 	return (mp);
21257c478bd9Sstevel@tonic-gate }
21267c478bd9Sstevel@tonic-gate 
21277c478bd9Sstevel@tonic-gate /*
21287c478bd9Sstevel@tonic-gate  * IP sends up the ICMP errors for validation and the removal of
21297c478bd9Sstevel@tonic-gate  * the AH header.
2130bd670b35SErik Nordmark  * If succesful, the mp has been modified to not include the AH header so
2131bd670b35SErik Nordmark  * that the caller can fanout to the ULP's icmp error handler.
21327c478bd9Sstevel@tonic-gate  */
2133bd670b35SErik Nordmark static mblk_t *
2134bd670b35SErik Nordmark ah_icmp_error_v4(mblk_t *mp, ip_recv_attr_t *ira, ipsecah_stack_t *ahstack)
21357c478bd9Sstevel@tonic-gate {
21367c478bd9Sstevel@tonic-gate 	mblk_t *mp1;
21377c478bd9Sstevel@tonic-gate 	icmph_t *icmph;
21387c478bd9Sstevel@tonic-gate 	int iph_hdr_length;
21397c478bd9Sstevel@tonic-gate 	int hdr_length;
21407c478bd9Sstevel@tonic-gate 	isaf_t *hptr;
21417c478bd9Sstevel@tonic-gate 	ipsa_t *assoc;
21427c478bd9Sstevel@tonic-gate 	int ah_length;
21437c478bd9Sstevel@tonic-gate 	ipha_t *ipha;
21447c478bd9Sstevel@tonic-gate 	ipha_t *oipha;
21457c478bd9Sstevel@tonic-gate 	ah_t *ah;
21467c478bd9Sstevel@tonic-gate 	uint32_t length;
21477c478bd9Sstevel@tonic-gate 	int alloc_size;
21487c478bd9Sstevel@tonic-gate 	uint8_t nexthdr;
2149f4b3ec61Sdh155122 	ipsec_stack_t	*ipss = ahstack->ipsecah_netstack->netstack_ipsec;
21507c478bd9Sstevel@tonic-gate 
21517c478bd9Sstevel@tonic-gate 	oipha = ipha = (ipha_t *)mp->b_rptr;
21527c478bd9Sstevel@tonic-gate 	iph_hdr_length = IPH_HDR_LENGTH(ipha);
21537c478bd9Sstevel@tonic-gate 	icmph = (icmph_t *)&mp->b_rptr[iph_hdr_length];
21547c478bd9Sstevel@tonic-gate 
21557c478bd9Sstevel@tonic-gate 	ipha = (ipha_t *)&icmph[1];
21567c478bd9Sstevel@tonic-gate 	hdr_length = IPH_HDR_LENGTH(ipha);
21577c478bd9Sstevel@tonic-gate 
21587c478bd9Sstevel@tonic-gate 	/*
21597c478bd9Sstevel@tonic-gate 	 * See if we have enough to locate the SPI
21607c478bd9Sstevel@tonic-gate 	 */
21617c478bd9Sstevel@tonic-gate 	if ((uchar_t *)ipha + hdr_length + 8 > mp->b_wptr) {
21627c478bd9Sstevel@tonic-gate 		if (!pullupmsg(mp, (uchar_t *)ipha + hdr_length + 8 -
21637c478bd9Sstevel@tonic-gate 		    mp->b_rptr)) {
2164f4b3ec61Sdh155122 			ipsec_rl_strlog(ahstack->ipsecah_netstack,
2165f4b3ec61Sdh155122 			    info.mi_idnum, 0, 0,
21667c478bd9Sstevel@tonic-gate 			    SL_WARN | SL_ERROR,
21677c478bd9Sstevel@tonic-gate 			    "ICMP error: Small AH header\n");
2168f4b3ec61Sdh155122 			IP_AH_BUMP_STAT(ipss, in_discards);
2169bd670b35SErik Nordmark 			ip_drop_packet(mp, B_TRUE, ira->ira_ill,
2170f4b3ec61Sdh155122 			    DROPPER(ipss, ipds_ah_bad_length),
2171f4b3ec61Sdh155122 			    &ahstack->ah_dropper);
2172bd670b35SErik Nordmark 			return (NULL);
21737c478bd9Sstevel@tonic-gate 		}
21747c478bd9Sstevel@tonic-gate 		icmph = (icmph_t *)&mp->b_rptr[iph_hdr_length];
21757c478bd9Sstevel@tonic-gate 		ipha = (ipha_t *)&icmph[1];
21767c478bd9Sstevel@tonic-gate 	}
21777c478bd9Sstevel@tonic-gate 
21787c478bd9Sstevel@tonic-gate 	ah = (ah_t *)((uint8_t *)ipha + hdr_length);
21797c478bd9Sstevel@tonic-gate 	nexthdr = ah->ah_nexthdr;
21807c478bd9Sstevel@tonic-gate 
2181f4b3ec61Sdh155122 	hptr = OUTBOUND_BUCKET_V4(&ahstack->ah_sadb.s_v4, ipha->ipha_dst);
21827c478bd9Sstevel@tonic-gate 	mutex_enter(&hptr->isaf_lock);
21837c478bd9Sstevel@tonic-gate 	assoc = ipsec_getassocbyspi(hptr, ah->ah_spi,
21847c478bd9Sstevel@tonic-gate 	    (uint32_t *)&ipha->ipha_src, (uint32_t *)&ipha->ipha_dst, AF_INET);
21857c478bd9Sstevel@tonic-gate 	mutex_exit(&hptr->isaf_lock);
21867c478bd9Sstevel@tonic-gate 
21877c478bd9Sstevel@tonic-gate 	if (assoc == NULL) {
2188f4b3ec61Sdh155122 		IP_AH_BUMP_STAT(ipss, lookup_failure);
2189f4b3ec61Sdh155122 		IP_AH_BUMP_STAT(ipss, in_discards);
2190f4b3ec61Sdh155122 		if (ahstack->ipsecah_log_unknown_spi) {
21917c478bd9Sstevel@tonic-gate 			ipsec_assocfailure(info.mi_idnum, 0, 0,
21927c478bd9Sstevel@tonic-gate 			    SL_CONSOLE | SL_WARN | SL_ERROR,
21937c478bd9Sstevel@tonic-gate 			    "Bad ICMP message - No association for the "
21947c478bd9Sstevel@tonic-gate 			    "attached AH header whose spi is 0x%x, "
21957c478bd9Sstevel@tonic-gate 			    "sender is 0x%x\n",
2196f4b3ec61Sdh155122 			    ah->ah_spi, &oipha->ipha_src, AF_INET,
2197f4b3ec61Sdh155122 			    ahstack->ipsecah_netstack);
21987c478bd9Sstevel@tonic-gate 		}
2199bd670b35SErik Nordmark 		ip_drop_packet(mp, B_TRUE, ira->ira_ill,
2200f4b3ec61Sdh155122 		    DROPPER(ipss, ipds_ah_no_sa),
2201f4b3ec61Sdh155122 		    &ahstack->ah_dropper);
2202bd670b35SErik Nordmark 		return (NULL);
22037c478bd9Sstevel@tonic-gate 	}
22047c478bd9Sstevel@tonic-gate 
22057c478bd9Sstevel@tonic-gate 	IPSA_REFRELE(assoc);
22067c478bd9Sstevel@tonic-gate 	/*
22077c478bd9Sstevel@tonic-gate 	 * There seems to be a valid association. If there
22087c478bd9Sstevel@tonic-gate 	 * is enough of AH header remove it, otherwise remove
22097c478bd9Sstevel@tonic-gate 	 * as much as possible and send it back. One could check
22107c478bd9Sstevel@tonic-gate 	 * whether it has complete AH header plus 8 bytes but it
22117c478bd9Sstevel@tonic-gate 	 * does not make sense if an icmp error is returned for
22127c478bd9Sstevel@tonic-gate 	 * ICMP messages e.g ICMP time exceeded, that are being
22137c478bd9Sstevel@tonic-gate 	 * sent up. Let the caller figure out.
22147c478bd9Sstevel@tonic-gate 	 *
22157c478bd9Sstevel@tonic-gate 	 * NOTE: ah_length is the number of 32 bit words minus 2.
22167c478bd9Sstevel@tonic-gate 	 */
22177c478bd9Sstevel@tonic-gate 	ah_length = (ah->ah_length << 2) + 8;
22187c478bd9Sstevel@tonic-gate 
22197c478bd9Sstevel@tonic-gate 	if ((uchar_t *)ipha + hdr_length + ah_length > mp->b_wptr) {
22207c478bd9Sstevel@tonic-gate 		if (mp->b_cont == NULL) {
22217c478bd9Sstevel@tonic-gate 			/*
22227c478bd9Sstevel@tonic-gate 			 * There is nothing to pullup. Just remove as
22237c478bd9Sstevel@tonic-gate 			 * much as possible. This is a common case for
22247c478bd9Sstevel@tonic-gate 			 * IPV4.
22257c478bd9Sstevel@tonic-gate 			 */
22267c478bd9Sstevel@tonic-gate 			ah_length = (mp->b_wptr - ((uchar_t *)ipha +
22277c478bd9Sstevel@tonic-gate 			    hdr_length));
22287c478bd9Sstevel@tonic-gate 			goto done;
22297c478bd9Sstevel@tonic-gate 		}
22307c478bd9Sstevel@tonic-gate 		/* Pullup the full ah header */
22317c478bd9Sstevel@tonic-gate 		if (!pullupmsg(mp, (uchar_t *)ah + ah_length - mp->b_rptr)) {
22327c478bd9Sstevel@tonic-gate 			/*
22337c478bd9Sstevel@tonic-gate 			 * pullupmsg could have failed if there was not
22347c478bd9Sstevel@tonic-gate 			 * enough to pullup or memory allocation failed.
22357c478bd9Sstevel@tonic-gate 			 * We tried hard, give up now.
22367c478bd9Sstevel@tonic-gate 			 */
2237f4b3ec61Sdh155122 			IP_AH_BUMP_STAT(ipss, in_discards);
2238bd670b35SErik Nordmark 			ip_drop_packet(mp, B_TRUE, ira->ira_ill,
2239f4b3ec61Sdh155122 			    DROPPER(ipss, ipds_ah_nomem),
2240f4b3ec61Sdh155122 			    &ahstack->ah_dropper);
2241bd670b35SErik Nordmark 			return (NULL);
22427c478bd9Sstevel@tonic-gate 		}
22437c478bd9Sstevel@tonic-gate 		icmph = (icmph_t *)&mp->b_rptr[iph_hdr_length];
22447c478bd9Sstevel@tonic-gate 		ipha = (ipha_t *)&icmph[1];
22457c478bd9Sstevel@tonic-gate 	}
22467c478bd9Sstevel@tonic-gate done:
22477c478bd9Sstevel@tonic-gate 	/*
22487c478bd9Sstevel@tonic-gate 	 * Remove the AH header and change the protocol.
2249bd670b35SErik Nordmark 	 * Don't update the spi fields in the ip_recv_attr_t
2250bd670b35SErik Nordmark 	 * as we are called just to validate the
22517c478bd9Sstevel@tonic-gate 	 * message attached to the ICMP message.
22527c478bd9Sstevel@tonic-gate 	 *
22537c478bd9Sstevel@tonic-gate 	 * If we never pulled up since all of the message
22547c478bd9Sstevel@tonic-gate 	 * is in one single mblk, we can't remove the AH header
22557c478bd9Sstevel@tonic-gate 	 * by just setting the b_wptr to the beginning of the
22567c478bd9Sstevel@tonic-gate 	 * AH header. We need to allocate a mblk that can hold
22577c478bd9Sstevel@tonic-gate 	 * up until the inner IP header and copy them.
22587c478bd9Sstevel@tonic-gate 	 */
22597c478bd9Sstevel@tonic-gate 	alloc_size = iph_hdr_length + sizeof (icmph_t) + hdr_length;
22607c478bd9Sstevel@tonic-gate 
22617c478bd9Sstevel@tonic-gate 	if ((mp1 = allocb(alloc_size, BPRI_LO)) == NULL) {
2262f4b3ec61Sdh155122 		IP_AH_BUMP_STAT(ipss, in_discards);
2263bd670b35SErik Nordmark 		ip_drop_packet(mp, B_TRUE, ira->ira_ill,
2264f4b3ec61Sdh155122 		    DROPPER(ipss, ipds_ah_nomem),
2265f4b3ec61Sdh155122 		    &ahstack->ah_dropper);
2266bd670b35SErik Nordmark 		return (NULL);
22677c478bd9Sstevel@tonic-gate 	}
22687c478bd9Sstevel@tonic-gate 	bcopy(mp->b_rptr, mp1->b_rptr, alloc_size);
22697c478bd9Sstevel@tonic-gate 	mp1->b_wptr += alloc_size;
22707c478bd9Sstevel@tonic-gate 
22717c478bd9Sstevel@tonic-gate 	/*
22727c478bd9Sstevel@tonic-gate 	 * Skip whatever we have copied and as much of AH header
22737c478bd9Sstevel@tonic-gate 	 * possible. If we still have something left in the original
22747c478bd9Sstevel@tonic-gate 	 * message, tag on.
22757c478bd9Sstevel@tonic-gate 	 */
22767c478bd9Sstevel@tonic-gate 	mp->b_rptr = (uchar_t *)ipha + hdr_length + ah_length;
22777c478bd9Sstevel@tonic-gate 
22787c478bd9Sstevel@tonic-gate 	if (mp->b_rptr != mp->b_wptr) {
22797c478bd9Sstevel@tonic-gate 		mp1->b_cont = mp;
22807c478bd9Sstevel@tonic-gate 	} else {
22817c478bd9Sstevel@tonic-gate 		if (mp->b_cont != NULL)
22827c478bd9Sstevel@tonic-gate 			mp1->b_cont = mp->b_cont;
22837c478bd9Sstevel@tonic-gate 		freeb(mp);
22847c478bd9Sstevel@tonic-gate 	}
22857c478bd9Sstevel@tonic-gate 
22867c478bd9Sstevel@tonic-gate 	ipha = (ipha_t *)(mp1->b_rptr + iph_hdr_length + sizeof (icmph_t));
22877c478bd9Sstevel@tonic-gate 	ipha->ipha_protocol = nexthdr;
22887c478bd9Sstevel@tonic-gate 	length = ntohs(ipha->ipha_length);
22897c478bd9Sstevel@tonic-gate 	length -= ah_length;
22907c478bd9Sstevel@tonic-gate 	ipha->ipha_length = htons((uint16_t)length);
22917c478bd9Sstevel@tonic-gate 	ipha->ipha_hdr_checksum = 0;
22927c478bd9Sstevel@tonic-gate 	ipha->ipha_hdr_checksum = (uint16_t)ip_csum_hdr(ipha);
22937c478bd9Sstevel@tonic-gate 
2294bd670b35SErik Nordmark 	return (mp1);
22957c478bd9Sstevel@tonic-gate }
22967c478bd9Sstevel@tonic-gate 
22977c478bd9Sstevel@tonic-gate /*
22987c478bd9Sstevel@tonic-gate  * IP calls this to validate the ICMP errors that
22997c478bd9Sstevel@tonic-gate  * we got from the network.
23007c478bd9Sstevel@tonic-gate  */
2301bd670b35SErik Nordmark mblk_t *
2302bd670b35SErik Nordmark ipsecah_icmp_error(mblk_t *data_mp, ip_recv_attr_t *ira)
23037c478bd9Sstevel@tonic-gate {
2304bd670b35SErik Nordmark 	netstack_t	*ns = ira->ira_ill->ill_ipst->ips_netstack;
2305f4b3ec61Sdh155122 	ipsecah_stack_t	*ahstack = ns->netstack_ipsecah;
23067c478bd9Sstevel@tonic-gate 
2307bd670b35SErik Nordmark 	if (ira->ira_flags & IRAF_IS_IPV4)
2308bd670b35SErik Nordmark 		return (ah_icmp_error_v4(data_mp, ira, ahstack));
23097c478bd9Sstevel@tonic-gate 	else
2310bd670b35SErik Nordmark 		return (ah_icmp_error_v6(data_mp, ira, ahstack));
23117c478bd9Sstevel@tonic-gate }
23127c478bd9Sstevel@tonic-gate 
23137c478bd9Sstevel@tonic-gate static int
23147c478bd9Sstevel@tonic-gate ah_fix_tlv_options_v6(uint8_t *oi_opt, uint8_t *pi_opt, uint_t ehdrlen,
23157c478bd9Sstevel@tonic-gate     uint8_t hdr_type, boolean_t copy_always)
23167c478bd9Sstevel@tonic-gate {
23177c478bd9Sstevel@tonic-gate 	uint8_t opt_type;
23187c478bd9Sstevel@tonic-gate 	uint_t optlen;
23197c478bd9Sstevel@tonic-gate 
23207c478bd9Sstevel@tonic-gate 	ASSERT(hdr_type == IPPROTO_DSTOPTS || hdr_type == IPPROTO_HOPOPTS);
23217c478bd9Sstevel@tonic-gate 
23227c478bd9Sstevel@tonic-gate 	/*
23237c478bd9Sstevel@tonic-gate 	 * Copy the next header and hdr ext. len of the HOP-by-HOP
23247c478bd9Sstevel@tonic-gate 	 * and Destination option.
23257c478bd9Sstevel@tonic-gate 	 */
23267c478bd9Sstevel@tonic-gate 	*pi_opt++ = *oi_opt++;
23277c478bd9Sstevel@tonic-gate 	*pi_opt++ = *oi_opt++;
23287c478bd9Sstevel@tonic-gate 	ehdrlen -= 2;
23297c478bd9Sstevel@tonic-gate 
23307c478bd9Sstevel@tonic-gate 	/*
23317c478bd9Sstevel@tonic-gate 	 * Now handle all the TLV encoded options.
23327c478bd9Sstevel@tonic-gate 	 */
23337c478bd9Sstevel@tonic-gate 	while (ehdrlen != 0) {
23347c478bd9Sstevel@tonic-gate 		opt_type = *oi_opt;
23357c478bd9Sstevel@tonic-gate 
23367c478bd9Sstevel@tonic-gate 		if (opt_type == IP6OPT_PAD1) {
23377c478bd9Sstevel@tonic-gate 			optlen = 1;
23387c478bd9Sstevel@tonic-gate 		} else {
23397c478bd9Sstevel@tonic-gate 			if (ehdrlen < 2)
23407c478bd9Sstevel@tonic-gate 				goto bad_opt;
23417c478bd9Sstevel@tonic-gate 			optlen = 2 + oi_opt[1];
23427c478bd9Sstevel@tonic-gate 			if (optlen > ehdrlen)
23437c478bd9Sstevel@tonic-gate 				goto bad_opt;
23447c478bd9Sstevel@tonic-gate 		}
23457c478bd9Sstevel@tonic-gate 		if (copy_always || !(opt_type & IP6OPT_MUTABLE)) {
23467c478bd9Sstevel@tonic-gate 			bcopy(oi_opt, pi_opt, optlen);
23477c478bd9Sstevel@tonic-gate 		} else {
23487c478bd9Sstevel@tonic-gate 			if (optlen == 1) {
23497c478bd9Sstevel@tonic-gate 				*pi_opt = 0;
23507c478bd9Sstevel@tonic-gate 			} else {
23517c478bd9Sstevel@tonic-gate 				/*
23527c478bd9Sstevel@tonic-gate 				 * Copy the type and data length fields.
23537c478bd9Sstevel@tonic-gate 				 * Zero the option data by skipping
23547c478bd9Sstevel@tonic-gate 				 * option type and option data len
23557c478bd9Sstevel@tonic-gate 				 * fields.
23567c478bd9Sstevel@tonic-gate 				 */
23577c478bd9Sstevel@tonic-gate 				*pi_opt = *oi_opt;
23587c478bd9Sstevel@tonic-gate 				*(pi_opt + 1) = *(oi_opt + 1);
23597c478bd9Sstevel@tonic-gate 				bzero(pi_opt + 2, optlen - 2);
23607c478bd9Sstevel@tonic-gate 			}
23617c478bd9Sstevel@tonic-gate 		}
23627c478bd9Sstevel@tonic-gate 		ehdrlen -= optlen;
23637c478bd9Sstevel@tonic-gate 		oi_opt += optlen;
23647c478bd9Sstevel@tonic-gate 		pi_opt += optlen;
23657c478bd9Sstevel@tonic-gate 	}
23667c478bd9Sstevel@tonic-gate 	return (0);
23677c478bd9Sstevel@tonic-gate bad_opt:
23687c478bd9Sstevel@tonic-gate 	return (-1);
23697c478bd9Sstevel@tonic-gate }
23707c478bd9Sstevel@tonic-gate 
23717c478bd9Sstevel@tonic-gate /*
23727c478bd9Sstevel@tonic-gate  * Construct a pseudo header for AH, processing all the options.
23737c478bd9Sstevel@tonic-gate  *
23747c478bd9Sstevel@tonic-gate  * oip6h is the IPv6 header of the incoming or outgoing packet.
23757c478bd9Sstevel@tonic-gate  * ip6h is the pointer to the pseudo headers IPV6 header. All
23767c478bd9Sstevel@tonic-gate  * the space needed for the options have been allocated including
23777c478bd9Sstevel@tonic-gate  * the AH header.
23787c478bd9Sstevel@tonic-gate  *
23797c478bd9Sstevel@tonic-gate  * If copy_always is set, all the options that appear before AH are copied
23807c478bd9Sstevel@tonic-gate  * blindly without checking for IP6OPT_MUTABLE. This is used by
23817c478bd9Sstevel@tonic-gate  * ah_auth_out_done().  Please refer to that function for details.
23827c478bd9Sstevel@tonic-gate  *
23837c478bd9Sstevel@tonic-gate  * NOTE :
23847c478bd9Sstevel@tonic-gate  *
23857c478bd9Sstevel@tonic-gate  * *  AH header is never copied in this function even if copy_always
23867c478bd9Sstevel@tonic-gate  *    is set. It just returns the ah_offset - offset of the AH header
23877c478bd9Sstevel@tonic-gate  *    and the caller needs to do the copying. This is done so that we
23887c478bd9Sstevel@tonic-gate  *    don't have pass extra arguments e.g. SA etc. and also,
23897c478bd9Sstevel@tonic-gate  *    it is not needed when ah_auth_out_done is calling this function.
23907c478bd9Sstevel@tonic-gate  */
23917c478bd9Sstevel@tonic-gate static uint_t
23927c478bd9Sstevel@tonic-gate ah_fix_phdr_v6(ip6_t *ip6h, ip6_t *oip6h, boolean_t outbound,
23937c478bd9Sstevel@tonic-gate     boolean_t copy_always)
23947c478bd9Sstevel@tonic-gate {
23957c478bd9Sstevel@tonic-gate 	uint8_t	*oi_opt;
23967c478bd9Sstevel@tonic-gate 	uint8_t	*pi_opt;
23977c478bd9Sstevel@tonic-gate 	uint8_t nexthdr;
23987c478bd9Sstevel@tonic-gate 	uint8_t *prev_nexthdr;
23997c478bd9Sstevel@tonic-gate 	ip6_hbh_t *hbhhdr;
24007c478bd9Sstevel@tonic-gate 	ip6_dest_t *dsthdr = NULL;
24017c478bd9Sstevel@tonic-gate 	ip6_rthdr0_t *rthdr;
24027c478bd9Sstevel@tonic-gate 	int ehdrlen;
24037c478bd9Sstevel@tonic-gate 	ah_t *ah;
24047c478bd9Sstevel@tonic-gate 	int ret;
24057c478bd9Sstevel@tonic-gate 
24067c478bd9Sstevel@tonic-gate 	/*
24077c478bd9Sstevel@tonic-gate 	 * In the outbound case for source route, ULP has already moved
24087c478bd9Sstevel@tonic-gate 	 * the first hop, which is now in ip6_dst. We need to re-arrange
24097c478bd9Sstevel@tonic-gate 	 * the header to make it look like how it would appear in the
24107c478bd9Sstevel@tonic-gate 	 * receiver i.e
24117c478bd9Sstevel@tonic-gate 	 *
24127c478bd9Sstevel@tonic-gate 	 * Because of ip_massage_options_v6 the header looks like
24137c478bd9Sstevel@tonic-gate 	 * this :
24147c478bd9Sstevel@tonic-gate 	 *
24157c478bd9Sstevel@tonic-gate 	 * ip6_src = S, ip6_dst = I1. followed by I2,I3,D.
24167c478bd9Sstevel@tonic-gate 	 *
24177c478bd9Sstevel@tonic-gate 	 * When it reaches the receiver, it would look like
24187c478bd9Sstevel@tonic-gate 	 *
24197c478bd9Sstevel@tonic-gate 	 * ip6_src = S, ip6_dst = D. followed by I1,I2,I3.
24207c478bd9Sstevel@tonic-gate 	 *
24217c478bd9Sstevel@tonic-gate 	 * NOTE : We assume that there are no problems with the options
24227c478bd9Sstevel@tonic-gate 	 * as IP should have already checked this.
24237c478bd9Sstevel@tonic-gate 	 */
24247c478bd9Sstevel@tonic-gate 
24257c478bd9Sstevel@tonic-gate 	oi_opt = (uchar_t *)&oip6h[1];
24267c478bd9Sstevel@tonic-gate 	pi_opt = (uchar_t *)&ip6h[1];
24277c478bd9Sstevel@tonic-gate 
24287c478bd9Sstevel@tonic-gate 	/*
24297c478bd9Sstevel@tonic-gate 	 * We set the prev_nexthdr properly in the pseudo header.
24307c478bd9Sstevel@tonic-gate 	 * After we finish authentication and come back from the
24317c478bd9Sstevel@tonic-gate 	 * algorithm module, pseudo header will become the real
24327c478bd9Sstevel@tonic-gate 	 * IP header.
24337c478bd9Sstevel@tonic-gate 	 */
24347c478bd9Sstevel@tonic-gate 	prev_nexthdr = (uint8_t *)&ip6h->ip6_nxt;
24357c478bd9Sstevel@tonic-gate 	nexthdr = oip6h->ip6_nxt;
24367c478bd9Sstevel@tonic-gate 	/* Assume IP has already stripped it */
2437bd670b35SErik Nordmark 	ASSERT(nexthdr != IPPROTO_FRAGMENT);
24387c478bd9Sstevel@tonic-gate 	ah = NULL;
24397c478bd9Sstevel@tonic-gate 	dsthdr = NULL;
24407c478bd9Sstevel@tonic-gate 	for (;;) {
24417c478bd9Sstevel@tonic-gate 		switch (nexthdr) {
24427c478bd9Sstevel@tonic-gate 		case IPPROTO_HOPOPTS:
24437c478bd9Sstevel@tonic-gate 			hbhhdr = (ip6_hbh_t *)oi_opt;
24447c478bd9Sstevel@tonic-gate 			nexthdr = hbhhdr->ip6h_nxt;
24457c478bd9Sstevel@tonic-gate 			ehdrlen = 8 * (hbhhdr->ip6h_len + 1);
24467c478bd9Sstevel@tonic-gate 			ret = ah_fix_tlv_options_v6(oi_opt, pi_opt, ehdrlen,
24477c478bd9Sstevel@tonic-gate 			    IPPROTO_HOPOPTS, copy_always);
24487c478bd9Sstevel@tonic-gate 			/*
24497c478bd9Sstevel@tonic-gate 			 * Return a zero offset indicating error if there
24507c478bd9Sstevel@tonic-gate 			 * was error.
24517c478bd9Sstevel@tonic-gate 			 */
24527c478bd9Sstevel@tonic-gate 			if (ret == -1)
24537c478bd9Sstevel@tonic-gate 				return (0);
24547c478bd9Sstevel@tonic-gate 			hbhhdr = (ip6_hbh_t *)pi_opt;
24557c478bd9Sstevel@tonic-gate 			prev_nexthdr = (uint8_t *)&hbhhdr->ip6h_nxt;
24567c478bd9Sstevel@tonic-gate 			break;
24577c478bd9Sstevel@tonic-gate 		case IPPROTO_ROUTING:
24587c478bd9Sstevel@tonic-gate 			rthdr = (ip6_rthdr0_t *)oi_opt;
24597c478bd9Sstevel@tonic-gate 			nexthdr = rthdr->ip6r0_nxt;
24607c478bd9Sstevel@tonic-gate 			ehdrlen = 8 * (rthdr->ip6r0_len + 1);
24617c478bd9Sstevel@tonic-gate 			if (!copy_always && outbound) {
24627c478bd9Sstevel@tonic-gate 				int i, left;
24637c478bd9Sstevel@tonic-gate 				ip6_rthdr0_t *prthdr;
24647c478bd9Sstevel@tonic-gate 				in6_addr_t *ap, *pap;
24657c478bd9Sstevel@tonic-gate 
24667c478bd9Sstevel@tonic-gate 				left = rthdr->ip6r0_segleft;
24677c478bd9Sstevel@tonic-gate 				prthdr = (ip6_rthdr0_t *)pi_opt;
24687c478bd9Sstevel@tonic-gate 				pap = (in6_addr_t *)(prthdr + 1);
24697c478bd9Sstevel@tonic-gate 				ap = (in6_addr_t *)(rthdr + 1);
24707c478bd9Sstevel@tonic-gate 				/*
24717c478bd9Sstevel@tonic-gate 				 * First eight bytes except seg_left
24727c478bd9Sstevel@tonic-gate 				 * does not change en route.
24737c478bd9Sstevel@tonic-gate 				 */
24747c478bd9Sstevel@tonic-gate 				bcopy(oi_opt, pi_opt, 8);
24757c478bd9Sstevel@tonic-gate 				prthdr->ip6r0_segleft = 0;
24767c478bd9Sstevel@tonic-gate 				/*
24777c478bd9Sstevel@tonic-gate 				 * First address has been moved to
24787c478bd9Sstevel@tonic-gate 				 * the destination address of the
24797c478bd9Sstevel@tonic-gate 				 * ip header by ip_massage_options_v6.
24807c478bd9Sstevel@tonic-gate 				 * And the real destination address is
24817c478bd9Sstevel@tonic-gate 				 * in the last address part of the
24827c478bd9Sstevel@tonic-gate 				 * option.
24837c478bd9Sstevel@tonic-gate 				 */
24847c478bd9Sstevel@tonic-gate 				*pap = oip6h->ip6_dst;
24857c478bd9Sstevel@tonic-gate 				for (i = 1; i < left - 1; i++)
24867c478bd9Sstevel@tonic-gate 					pap[i] = ap[i - 1];
24877c478bd9Sstevel@tonic-gate 				ip6h->ip6_dst = *(ap + left - 1);
24887c478bd9Sstevel@tonic-gate 			} else {
24897c478bd9Sstevel@tonic-gate 				bcopy(oi_opt, pi_opt, ehdrlen);
24907c478bd9Sstevel@tonic-gate 			}
24917c478bd9Sstevel@tonic-gate 			rthdr = (ip6_rthdr0_t *)pi_opt;
24927c478bd9Sstevel@tonic-gate 			prev_nexthdr = (uint8_t *)&rthdr->ip6r0_nxt;
24937c478bd9Sstevel@tonic-gate 			break;
24947c478bd9Sstevel@tonic-gate 		case IPPROTO_DSTOPTS:
24957c478bd9Sstevel@tonic-gate 			/*
24967c478bd9Sstevel@tonic-gate 			 * Destination options are tricky.  If there is
24977c478bd9Sstevel@tonic-gate 			 * a terminal (e.g. non-IPv6-extension) header
24987c478bd9Sstevel@tonic-gate 			 * following the destination options, don't
24997c478bd9Sstevel@tonic-gate 			 * reset prev_nexthdr or advance the AH insertion
25007c478bd9Sstevel@tonic-gate 			 * point and just treat this as a terminal header.
25017c478bd9Sstevel@tonic-gate 			 *
25027c478bd9Sstevel@tonic-gate 			 * If this is an inbound packet, just deal with
25037c478bd9Sstevel@tonic-gate 			 * it as is.
25047c478bd9Sstevel@tonic-gate 			 */
25057c478bd9Sstevel@tonic-gate 			dsthdr = (ip6_dest_t *)oi_opt;
25067c478bd9Sstevel@tonic-gate 			/*
25077c478bd9Sstevel@tonic-gate 			 * XXX I hope common-subexpression elimination
25087c478bd9Sstevel@tonic-gate 			 * saves us the double-evaluate.
25097c478bd9Sstevel@tonic-gate 			 */
25107c478bd9Sstevel@tonic-gate 			if (outbound && dsthdr->ip6d_nxt != IPPROTO_ROUTING &&
25117c478bd9Sstevel@tonic-gate 			    dsthdr->ip6d_nxt != IPPROTO_HOPOPTS)
25127c478bd9Sstevel@tonic-gate 				goto terminal_hdr;
25137c478bd9Sstevel@tonic-gate 			nexthdr = dsthdr->ip6d_nxt;
25147c478bd9Sstevel@tonic-gate 			ehdrlen = 8 * (dsthdr->ip6d_len + 1);
25157c478bd9Sstevel@tonic-gate 			ret = ah_fix_tlv_options_v6(oi_opt, pi_opt, ehdrlen,
25167c478bd9Sstevel@tonic-gate 			    IPPROTO_DSTOPTS, copy_always);
25177c478bd9Sstevel@tonic-gate 			/*
25187c478bd9Sstevel@tonic-gate 			 * Return a zero offset indicating error if there
25197c478bd9Sstevel@tonic-gate 			 * was error.
25207c478bd9Sstevel@tonic-gate 			 */
25217c478bd9Sstevel@tonic-gate 			if (ret == -1)
25227c478bd9Sstevel@tonic-gate 				return (0);
25237c478bd9Sstevel@tonic-gate 			break;
25247c478bd9Sstevel@tonic-gate 		case IPPROTO_AH:
25257c478bd9Sstevel@tonic-gate 			/*
25267c478bd9Sstevel@tonic-gate 			 * Be conservative in what you send.  We shouldn't
25277c478bd9Sstevel@tonic-gate 			 * see two same-scoped AH's in one packet.
25287c478bd9Sstevel@tonic-gate 			 * (Inner-IP-scoped AH will be hit by terminal
25297c478bd9Sstevel@tonic-gate 			 * header of IP or IPv6.)
25307c478bd9Sstevel@tonic-gate 			 */
25317c478bd9Sstevel@tonic-gate 			ASSERT(!outbound);
25327c478bd9Sstevel@tonic-gate 			return ((uint_t)(pi_opt - (uint8_t *)ip6h));
25337c478bd9Sstevel@tonic-gate 		default:
25347c478bd9Sstevel@tonic-gate 			ASSERT(outbound);
25357c478bd9Sstevel@tonic-gate terminal_hdr:
25367c478bd9Sstevel@tonic-gate 			*prev_nexthdr = IPPROTO_AH;
25377c478bd9Sstevel@tonic-gate 			ah = (ah_t *)pi_opt;
25387c478bd9Sstevel@tonic-gate 			ah->ah_nexthdr = nexthdr;
25397c478bd9Sstevel@tonic-gate 			return ((uint_t)(pi_opt - (uint8_t *)ip6h));
25407c478bd9Sstevel@tonic-gate 		}
25417c478bd9Sstevel@tonic-gate 		pi_opt += ehdrlen;
25427c478bd9Sstevel@tonic-gate 		oi_opt += ehdrlen;
25437c478bd9Sstevel@tonic-gate 	}
25447c478bd9Sstevel@tonic-gate 	/* NOTREACHED */
25457c478bd9Sstevel@tonic-gate }
25467c478bd9Sstevel@tonic-gate 
25477c478bd9Sstevel@tonic-gate static boolean_t
25487c478bd9Sstevel@tonic-gate ah_finish_up(ah_t *phdr_ah, ah_t *inbound_ah, ipsa_t *assoc,
2549f4b3ec61Sdh155122     int ah_data_sz, int ah_align_sz, ipsecah_stack_t *ahstack)
25507c478bd9Sstevel@tonic-gate {
25517c478bd9Sstevel@tonic-gate 	int i;
25527c478bd9Sstevel@tonic-gate 
25537c478bd9Sstevel@tonic-gate 	/*
25547c478bd9Sstevel@tonic-gate 	 * Padding :
25557c478bd9Sstevel@tonic-gate 	 *
25567c478bd9Sstevel@tonic-gate 	 * 1) Authentication data may have to be padded
25577c478bd9Sstevel@tonic-gate 	 * before ICV calculation if ICV is not a multiple
25587c478bd9Sstevel@tonic-gate 	 * of 64 bits. This padding is arbitrary and transmitted
25597c478bd9Sstevel@tonic-gate 	 * with the packet at the end of the authentication data.
25607c478bd9Sstevel@tonic-gate 	 * Payload length should include the padding bytes.
25617c478bd9Sstevel@tonic-gate 	 *
25627c478bd9Sstevel@tonic-gate 	 * 2) Explicit padding of the whole datagram may be
25637c478bd9Sstevel@tonic-gate 	 * required by the algorithm which need not be
25647c478bd9Sstevel@tonic-gate 	 * transmitted. It is assumed that this will be taken
25657c478bd9Sstevel@tonic-gate 	 * care by the algorithm module.
25667c478bd9Sstevel@tonic-gate 	 */
25677c478bd9Sstevel@tonic-gate 	bzero(phdr_ah + 1, ah_data_sz);	/* Zero out ICV for pseudo-hdr. */
25687c478bd9Sstevel@tonic-gate 
25697c478bd9Sstevel@tonic-gate 	if (inbound_ah == NULL) {
25707c478bd9Sstevel@tonic-gate 		/* Outbound AH datagram. */
25717c478bd9Sstevel@tonic-gate 
25727c478bd9Sstevel@tonic-gate 		phdr_ah->ah_length = (ah_align_sz >> 2) + 1;
25737c478bd9Sstevel@tonic-gate 		phdr_ah->ah_reserved = 0;
25747c478bd9Sstevel@tonic-gate 		phdr_ah->ah_spi = assoc->ipsa_spi;
25757c478bd9Sstevel@tonic-gate 
25767c478bd9Sstevel@tonic-gate 		phdr_ah->ah_replay =
2577*1a5e258fSJosef 'Jeff' Sipek 		    htonl(atomic_inc_32_nv(&assoc->ipsa_replay));
25787c478bd9Sstevel@tonic-gate 		if (phdr_ah->ah_replay == 0 && assoc->ipsa_replay_wsize != 0) {
25797c478bd9Sstevel@tonic-gate 			/*
25807c478bd9Sstevel@tonic-gate 			 * XXX We have replay counter wrapping.  We probably
25817c478bd9Sstevel@tonic-gate 			 * want to nuke this SA (and its peer).
25827c478bd9Sstevel@tonic-gate 			 */
25837c478bd9Sstevel@tonic-gate 			ipsec_assocfailure(info.mi_idnum, 0, 0,
25847c478bd9Sstevel@tonic-gate 			    SL_ERROR | SL_CONSOLE | SL_WARN,
25857c478bd9Sstevel@tonic-gate 			    "Outbound AH SA (0x%x), dst %s has wrapped "
25867c478bd9Sstevel@tonic-gate 			    "sequence.\n", phdr_ah->ah_spi,
2587f4b3ec61Sdh155122 			    assoc->ipsa_dstaddr, assoc->ipsa_addrfam,
2588f4b3ec61Sdh155122 			    ahstack->ipsecah_netstack);
25897c478bd9Sstevel@tonic-gate 
25907c478bd9Sstevel@tonic-gate 			sadb_replay_delete(assoc);
25917c478bd9Sstevel@tonic-gate 			/* Caller will free phdr_mp and return NULL. */
25927c478bd9Sstevel@tonic-gate 			return (B_FALSE);
25937c478bd9Sstevel@tonic-gate 		}
25947c478bd9Sstevel@tonic-gate 
25957c478bd9Sstevel@tonic-gate 		if (ah_data_sz != ah_align_sz) {
25967c478bd9Sstevel@tonic-gate 			uchar_t *pad = ((uchar_t *)phdr_ah + sizeof (ah_t) +
25977c478bd9Sstevel@tonic-gate 			    ah_data_sz);
25987c478bd9Sstevel@tonic-gate 
25997c478bd9Sstevel@tonic-gate 			for (i = 0; i < (ah_align_sz - ah_data_sz); i++) {
26007c478bd9Sstevel@tonic-gate 				pad[i] = (uchar_t)i;	/* Fill the padding */
26017c478bd9Sstevel@tonic-gate 			}
26027c478bd9Sstevel@tonic-gate 		}
26037c478bd9Sstevel@tonic-gate 	} else {
26047c478bd9Sstevel@tonic-gate 		/* Inbound AH datagram. */
26057c478bd9Sstevel@tonic-gate 		phdr_ah->ah_nexthdr = inbound_ah->ah_nexthdr;
26067c478bd9Sstevel@tonic-gate 		phdr_ah->ah_length = inbound_ah->ah_length;
26077c478bd9Sstevel@tonic-gate 		phdr_ah->ah_reserved = 0;
26087c478bd9Sstevel@tonic-gate 		ASSERT(inbound_ah->ah_spi == assoc->ipsa_spi);
26097c478bd9Sstevel@tonic-gate 		phdr_ah->ah_spi = inbound_ah->ah_spi;
26107c478bd9Sstevel@tonic-gate 		phdr_ah->ah_replay = inbound_ah->ah_replay;
26117c478bd9Sstevel@tonic-gate 
26127c478bd9Sstevel@tonic-gate 		if (ah_data_sz != ah_align_sz) {
2613f4b3ec61Sdh155122 			uchar_t *opad = ((uchar_t *)inbound_ah +
2614f4b3ec61Sdh155122 			    sizeof (ah_t) + ah_data_sz);
26157c478bd9Sstevel@tonic-gate 			uchar_t *pad = ((uchar_t *)phdr_ah + sizeof (ah_t) +
26167c478bd9Sstevel@tonic-gate 			    ah_data_sz);
26177c478bd9Sstevel@tonic-gate 
26187c478bd9Sstevel@tonic-gate 			for (i = 0; i < (ah_align_sz - ah_data_sz); i++) {
26197c478bd9Sstevel@tonic-gate 				pad[i] = opad[i];	/* Copy the padding */
26207c478bd9Sstevel@tonic-gate 			}
26217c478bd9Sstevel@tonic-gate 		}
26227c478bd9Sstevel@tonic-gate 	}
26237c478bd9Sstevel@tonic-gate 
26247c478bd9Sstevel@tonic-gate 	return (B_TRUE);
26257c478bd9Sstevel@tonic-gate }
26267c478bd9Sstevel@tonic-gate 
26277c478bd9Sstevel@tonic-gate /*
26287c478bd9Sstevel@tonic-gate  * Called upon failing the inbound ICV check. The message passed as
26297c478bd9Sstevel@tonic-gate  * argument is freed.
26307c478bd9Sstevel@tonic-gate  */
26317c478bd9Sstevel@tonic-gate static void
2632bd670b35SErik Nordmark ah_log_bad_auth(mblk_t *mp, ip_recv_attr_t *ira, ipsec_crypto_t *ic)
26337c478bd9Sstevel@tonic-gate {
2634bd670b35SErik Nordmark 	boolean_t	isv4 = (ira->ira_flags & IRAF_IS_IPV4);
2635bd670b35SErik Nordmark 	ipsa_t		*assoc = ira->ira_ipsec_ah_sa;
26367c478bd9Sstevel@tonic-gate 	int		af;
26377c478bd9Sstevel@tonic-gate 	void		*addr;
2638bd670b35SErik Nordmark 	netstack_t	*ns = ira->ira_ill->ill_ipst->ips_netstack;
2639f4b3ec61Sdh155122 	ipsecah_stack_t	*ahstack = ns->netstack_ipsecah;
2640f4b3ec61Sdh155122 	ipsec_stack_t	*ipss = ns->netstack_ipsec;
26417c478bd9Sstevel@tonic-gate 
2642bd670b35SErik Nordmark 	ASSERT(mp->b_datap->db_type == M_DATA);
2643bd670b35SErik Nordmark 
2644bd670b35SErik Nordmark 	mp->b_rptr -= ic->ic_skip_len;
26457c478bd9Sstevel@tonic-gate 
26467c478bd9Sstevel@tonic-gate 	if (isv4) {
26477c478bd9Sstevel@tonic-gate 		ipha_t *ipha = (ipha_t *)mp->b_rptr;
26487c478bd9Sstevel@tonic-gate 		addr = &ipha->ipha_dst;
26497c478bd9Sstevel@tonic-gate 		af = AF_INET;
26507c478bd9Sstevel@tonic-gate 	} else {
26517c478bd9Sstevel@tonic-gate 		ip6_t *ip6h = (ip6_t *)mp->b_rptr;
26527c478bd9Sstevel@tonic-gate 		addr = &ip6h->ip6_dst;
26537c478bd9Sstevel@tonic-gate 		af = AF_INET6;
26547c478bd9Sstevel@tonic-gate 	}
26557c478bd9Sstevel@tonic-gate 
26567c478bd9Sstevel@tonic-gate 	/*
26577c478bd9Sstevel@tonic-gate 	 * Log the event. Don't print to the console, block
26587c478bd9Sstevel@tonic-gate 	 * potential denial-of-service attack.
26597c478bd9Sstevel@tonic-gate 	 */
2660f4b3ec61Sdh155122 	AH_BUMP_STAT(ahstack, bad_auth);
26617c478bd9Sstevel@tonic-gate 
26627c478bd9Sstevel@tonic-gate 	ipsec_assocfailure(info.mi_idnum, 0, 0, SL_ERROR | SL_WARN,
26637c478bd9Sstevel@tonic-gate 	    "AH Authentication failed spi %x, dst_addr %s",
2664f4b3ec61Sdh155122 	    assoc->ipsa_spi, addr, af, ahstack->ipsecah_netstack);
26657c478bd9Sstevel@tonic-gate 
2666f4b3ec61Sdh155122 	IP_AH_BUMP_STAT(ipss, in_discards);
2667bd670b35SErik Nordmark 	ip_drop_packet(mp, B_TRUE, ira->ira_ill,
2668f4b3ec61Sdh155122 	    DROPPER(ipss, ipds_ah_bad_auth),
2669f4b3ec61Sdh155122 	    &ahstack->ah_dropper);
26707c478bd9Sstevel@tonic-gate }
26717c478bd9Sstevel@tonic-gate 
26727c478bd9Sstevel@tonic-gate /*
26737c478bd9Sstevel@tonic-gate  * Kernel crypto framework callback invoked after completion of async
2674bd670b35SErik Nordmark  * crypto requests for outbound packets.
26757c478bd9Sstevel@tonic-gate  */
26767c478bd9Sstevel@tonic-gate static void
2677bd670b35SErik Nordmark ah_kcf_callback_outbound(void *arg, int status)
26787c478bd9Sstevel@tonic-gate {
2679bd670b35SErik Nordmark 	mblk_t		*mp = (mblk_t *)arg;
2680bd670b35SErik Nordmark 	mblk_t		*async_mp;
2681bd670b35SErik Nordmark 	netstack_t	*ns;
2682f4b3ec61Sdh155122 	ipsec_stack_t	*ipss;
2683f4b3ec61Sdh155122 	ipsecah_stack_t	*ahstack;
2684bd670b35SErik Nordmark 	mblk_t		*data_mp;
2685bd670b35SErik Nordmark 	ip_xmit_attr_t	ixas;
2686bd670b35SErik Nordmark 	ipsec_crypto_t	*ic;
2687bd670b35SErik Nordmark 	ill_t		*ill;
26887c478bd9Sstevel@tonic-gate 
2689f4b3ec61Sdh155122 	/*
2690bd670b35SErik Nordmark 	 * First remove the ipsec_crypto_t mblk
2691bd670b35SErik Nordmark 	 * Note that we need to ipsec_free_crypto_data(mp) once done with ic.
2692f4b3ec61Sdh155122 	 */
2693bd670b35SErik Nordmark 	async_mp = ipsec_remove_crypto_data(mp, &ic);
2694bd670b35SErik Nordmark 	ASSERT(async_mp != NULL);
2695bd670b35SErik Nordmark 
2696bd670b35SErik Nordmark 	/*
2697bd670b35SErik Nordmark 	 * Extract the ip_xmit_attr_t from the first mblk.
2698bd670b35SErik Nordmark 	 * Verifies that the netstack and ill is still around; could
2699bd670b35SErik Nordmark 	 * have vanished while kEf was doing its work.
2700bd670b35SErik Nordmark 	 * On succesful return we have a nce_t and the ill/ipst can't
2701bd670b35SErik Nordmark 	 * disappear until we do the nce_refrele in ixa_cleanup.
2702bd670b35SErik Nordmark 	 */
2703bd670b35SErik Nordmark 	data_mp = async_mp->b_cont;
2704bd670b35SErik Nordmark 	async_mp->b_cont = NULL;
2705bd670b35SErik Nordmark 	if (!ip_xmit_attr_from_mblk(async_mp, &ixas)) {
2706bd670b35SErik Nordmark 		/* Disappeared on us - no ill/ipst for MIB */
2707bd670b35SErik Nordmark 		if (ixas.ixa_nce != NULL) {
2708bd670b35SErik Nordmark 			ill = ixas.ixa_nce->nce_ill;
2709bd670b35SErik Nordmark 			BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
2710bd670b35SErik Nordmark 			ip_drop_output("ipIfStatsOutDiscards", data_mp, ill);
2711bd670b35SErik Nordmark 		}
2712bd670b35SErik Nordmark 		freemsg(data_mp);
2713bd670b35SErik Nordmark 		goto done;
2714bd670b35SErik Nordmark 	}
2715bd670b35SErik Nordmark 	ns = ixas.ixa_ipst->ips_netstack;
2716bd670b35SErik Nordmark 	ahstack = ns->netstack_ipsecah;
2717bd670b35SErik Nordmark 	ipss = ns->netstack_ipsec;
2718bd670b35SErik Nordmark 	ill = ixas.ixa_nce->nce_ill;
2719bd670b35SErik Nordmark 
2720bd670b35SErik Nordmark 	if (status == CRYPTO_SUCCESS) {
2721bd670b35SErik Nordmark 		data_mp = ah_auth_out_done(data_mp, &ixas, ic);
2722bd670b35SErik Nordmark 		if (data_mp == NULL)
2723bd670b35SErik Nordmark 			goto done;
2724bd670b35SErik Nordmark 
2725bd670b35SErik Nordmark 		(void) ip_output_post_ipsec(data_mp, &ixas);
2726bd670b35SErik Nordmark 	} else {
2727bd670b35SErik Nordmark 		/* Outbound shouldn't see invalid MAC */
2728bd670b35SErik Nordmark 		ASSERT(status != CRYPTO_INVALID_MAC);
2729bd670b35SErik Nordmark 
2730bd670b35SErik Nordmark 		ah1dbg(ahstack,
2731bd670b35SErik Nordmark 		    ("ah_kcf_callback_outbound: crypto failed with 0x%x\n",
2732bd670b35SErik Nordmark 		    status));
2733bd670b35SErik Nordmark 		AH_BUMP_STAT(ahstack, crypto_failures);
2734bd670b35SErik Nordmark 		AH_BUMP_STAT(ahstack, out_discards);
2735bd670b35SErik Nordmark 
2736bd670b35SErik Nordmark 		ip_drop_packet(data_mp, B_FALSE, ill,
2737bd670b35SErik Nordmark 		    DROPPER(ipss, ipds_ah_crypto_failed),
2738bd670b35SErik Nordmark 		    &ahstack->ah_dropper);
2739bd670b35SErik Nordmark 		BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
2740bd670b35SErik Nordmark 	}
2741bd670b35SErik Nordmark done:
2742bd670b35SErik Nordmark 	ixa_cleanup(&ixas);
2743bd670b35SErik Nordmark 	(void) ipsec_free_crypto_data(mp);
2744f4b3ec61Sdh155122 }
2745f4b3ec61Sdh155122 
2746bd670b35SErik Nordmark /*
2747bd670b35SErik Nordmark  * Kernel crypto framework callback invoked after completion of async
2748bd670b35SErik Nordmark  * crypto requests for inbound packets.
2749bd670b35SErik Nordmark  */
2750bd670b35SErik Nordmark static void
2751bd670b35SErik Nordmark ah_kcf_callback_inbound(void *arg, int status)
2752bd670b35SErik Nordmark {
2753bd670b35SErik Nordmark 	mblk_t		*mp = (mblk_t *)arg;
2754bd670b35SErik Nordmark 	mblk_t		*async_mp;
2755bd670b35SErik Nordmark 	netstack_t	*ns;
2756bd670b35SErik Nordmark 	ipsec_stack_t	*ipss;
2757bd670b35SErik Nordmark 	ipsecah_stack_t	*ahstack;
2758bd670b35SErik Nordmark 	mblk_t		*data_mp;
2759bd670b35SErik Nordmark 	ip_recv_attr_t	iras;
2760bd670b35SErik Nordmark 	ipsec_crypto_t	*ic;
2761bd670b35SErik Nordmark 
2762bd670b35SErik Nordmark 	/*
2763bd670b35SErik Nordmark 	 * First remove the ipsec_crypto_t mblk
2764bd670b35SErik Nordmark 	 * Note that we need to ipsec_free_crypto_data(mp) once done with ic.
2765bd670b35SErik Nordmark 	 */
2766bd670b35SErik Nordmark 	async_mp = ipsec_remove_crypto_data(mp, &ic);
2767bd670b35SErik Nordmark 	ASSERT(async_mp != NULL);
2768bd670b35SErik Nordmark 
2769bd670b35SErik Nordmark 	/*
2770bd670b35SErik Nordmark 	 * Extract the ip_xmit_attr_t from the first mblk.
2771bd670b35SErik Nordmark 	 * Verifies that the netstack and ill is still around; could
2772bd670b35SErik Nordmark 	 * have vanished while kEf was doing its work.
2773bd670b35SErik Nordmark 	 */
2774bd670b35SErik Nordmark 	data_mp = async_mp->b_cont;
2775bd670b35SErik Nordmark 	async_mp->b_cont = NULL;
2776bd670b35SErik Nordmark 	if (!ip_recv_attr_from_mblk(async_mp, &iras)) {
2777bd670b35SErik Nordmark 		/* The ill or ip_stack_t disappeared on us */
2778bd670b35SErik Nordmark 		ip_drop_input("ip_recv_attr_from_mblk", data_mp, NULL);
2779bd670b35SErik Nordmark 		freemsg(data_mp);
2780bd670b35SErik Nordmark 		goto done;
2781bd670b35SErik Nordmark 	}
2782bd670b35SErik Nordmark 	ns = iras.ira_ill->ill_ipst->ips_netstack;
2783f4b3ec61Sdh155122 	ahstack = ns->netstack_ipsecah;
2784f4b3ec61Sdh155122 	ipss = ns->netstack_ipsec;
2785f4b3ec61Sdh155122 
27867c478bd9Sstevel@tonic-gate 	if (status == CRYPTO_SUCCESS) {
2787bd670b35SErik Nordmark 		data_mp = ah_auth_in_done(data_mp, &iras, ic);
2788bd670b35SErik Nordmark 		if (data_mp == NULL)
2789bd670b35SErik Nordmark 			goto done;
27907c478bd9Sstevel@tonic-gate 
27917c478bd9Sstevel@tonic-gate 		/* finish IPsec processing */
2792bd670b35SErik Nordmark 		ip_input_post_ipsec(data_mp, &iras);
27937c478bd9Sstevel@tonic-gate 
27947c478bd9Sstevel@tonic-gate 	} else if (status == CRYPTO_INVALID_MAC) {
2795bd670b35SErik Nordmark 		ah_log_bad_auth(data_mp, &iras, ic);
27967c478bd9Sstevel@tonic-gate 	} else {
2797bd670b35SErik Nordmark 		ah1dbg(ahstack,
2798bd670b35SErik Nordmark 		    ("ah_kcf_callback_inbound: crypto failed with 0x%x\n",
2799f4b3ec61Sdh155122 		    status));
2800f4b3ec61Sdh155122 		AH_BUMP_STAT(ahstack, crypto_failures);
2801f4b3ec61Sdh155122 		IP_AH_BUMP_STAT(ipss, in_discards);
2802bd670b35SErik Nordmark 		ip_drop_packet(data_mp, B_TRUE, iras.ira_ill,
2803f4b3ec61Sdh155122 		    DROPPER(ipss, ipds_ah_crypto_failed),
2804f4b3ec61Sdh155122 		    &ahstack->ah_dropper);
2805bd670b35SErik Nordmark 		BUMP_MIB(iras.ira_ill->ill_ip_mib, ipIfStatsInDiscards);
28067c478bd9Sstevel@tonic-gate 	}
2807bd670b35SErik Nordmark done:
2808bd670b35SErik Nordmark 	ira_cleanup(&iras, B_TRUE);
2809bd670b35SErik Nordmark 	(void) ipsec_free_crypto_data(mp);
28107c478bd9Sstevel@tonic-gate }
28117c478bd9Sstevel@tonic-gate 
28127c478bd9Sstevel@tonic-gate /*
28137c478bd9Sstevel@tonic-gate  * Invoked on kernel crypto failure during inbound and outbound processing.
28147c478bd9Sstevel@tonic-gate  */
28157c478bd9Sstevel@tonic-gate static void
2816bd670b35SErik Nordmark ah_crypto_failed(mblk_t *data_mp, boolean_t is_inbound, int kef_rc,
2817bd670b35SErik Nordmark     ill_t *ill, ipsecah_stack_t *ahstack)
28187c478bd9Sstevel@tonic-gate {
2819f4b3ec61Sdh155122 	ipsec_stack_t	*ipss = ahstack->ipsecah_netstack->netstack_ipsec;
2820f4b3ec61Sdh155122 
2821f4b3ec61Sdh155122 	ah1dbg(ahstack, ("crypto failed for %s AH with 0x%x\n",
28227c478bd9Sstevel@tonic-gate 	    is_inbound ? "inbound" : "outbound", kef_rc));
2823bd670b35SErik Nordmark 	ip_drop_packet(data_mp, is_inbound, ill,
2824f4b3ec61Sdh155122 	    DROPPER(ipss, ipds_ah_crypto_failed),
2825f4b3ec61Sdh155122 	    &ahstack->ah_dropper);
2826f4b3ec61Sdh155122 	AH_BUMP_STAT(ahstack, crypto_failures);
28277c478bd9Sstevel@tonic-gate 	if (is_inbound)
2828f4b3ec61Sdh155122 		IP_AH_BUMP_STAT(ipss, in_discards);
28297c478bd9Sstevel@tonic-gate 	else
2830f4b3ec61Sdh155122 		AH_BUMP_STAT(ahstack, out_discards);
28317c478bd9Sstevel@tonic-gate }
28327c478bd9Sstevel@tonic-gate 
28337c478bd9Sstevel@tonic-gate /*
28347c478bd9Sstevel@tonic-gate  * Helper macros for the ah_submit_req_{inbound,outbound}() functions.
28357c478bd9Sstevel@tonic-gate  */
28367c478bd9Sstevel@tonic-gate 
2837bd670b35SErik Nordmark /*
2838bd670b35SErik Nordmark  * A statement-equivalent macro, _cr MUST point to a modifiable
2839bd670b35SErik Nordmark  * crypto_call_req_t.
2840bd670b35SErik Nordmark  */
2841bd670b35SErik Nordmark #define	AH_INIT_CALLREQ(_cr, _mp, _callback)		\
2842bd670b35SErik Nordmark 	(_cr)->cr_flag = CRYPTO_SKIP_REQID|CRYPTO_ALWAYS_QUEUE;	\
2843bd670b35SErik Nordmark 	(_cr)->cr_callback_arg = (_mp);				\
2844bd670b35SErik Nordmark 	(_cr)->cr_callback_func = (_callback)
28457c478bd9Sstevel@tonic-gate 
28467c478bd9Sstevel@tonic-gate #define	AH_INIT_CRYPTO_DATA(data, msglen, mblk) {			\
28477c478bd9Sstevel@tonic-gate 	(data)->cd_format = CRYPTO_DATA_MBLK;				\
28487c478bd9Sstevel@tonic-gate 	(data)->cd_mp = mblk;						\
28497c478bd9Sstevel@tonic-gate 	(data)->cd_offset = 0;						\
28507c478bd9Sstevel@tonic-gate 	(data)->cd_length = msglen;					\
28517c478bd9Sstevel@tonic-gate }
28527c478bd9Sstevel@tonic-gate 
28537c478bd9Sstevel@tonic-gate #define	AH_INIT_CRYPTO_MAC(mac, icvlen, icvbuf) {			\
28547c478bd9Sstevel@tonic-gate 	(mac)->cd_format = CRYPTO_DATA_RAW;				\
28557c478bd9Sstevel@tonic-gate 	(mac)->cd_offset = 0;						\
28567c478bd9Sstevel@tonic-gate 	(mac)->cd_length = icvlen;					\
28577c478bd9Sstevel@tonic-gate 	(mac)->cd_raw.iov_base = icvbuf;				\
28587c478bd9Sstevel@tonic-gate 	(mac)->cd_raw.iov_len = icvlen;					\
28597c478bd9Sstevel@tonic-gate }
28607c478bd9Sstevel@tonic-gate 
28617c478bd9Sstevel@tonic-gate /*
28627c478bd9Sstevel@tonic-gate  * Submit an inbound packet for processing by the crypto framework.
28637c478bd9Sstevel@tonic-gate  */
2864bd670b35SErik Nordmark static mblk_t *
2865bd670b35SErik Nordmark ah_submit_req_inbound(mblk_t *phdr_mp, ip_recv_attr_t *ira,
2866bd670b35SErik Nordmark     size_t skip_len, uint32_t ah_offset, ipsa_t *assoc)
28677c478bd9Sstevel@tonic-gate {
28687c478bd9Sstevel@tonic-gate 	int kef_rc;
2869bd670b35SErik Nordmark 	mblk_t *mp;
2870bd670b35SErik Nordmark 	crypto_call_req_t call_req, *callrp;
28717c478bd9Sstevel@tonic-gate 	uint_t icv_len = assoc->ipsa_mac_len;
28727c478bd9Sstevel@tonic-gate 	crypto_ctx_template_t ctx_tmpl;
2873bd670b35SErik Nordmark 	ipsecah_stack_t	*ahstack;
2874bd670b35SErik Nordmark 	ipsec_crypto_t	*ic, icstack;
2875bd670b35SErik Nordmark 	boolean_t force = (assoc->ipsa_flags & IPSA_F_ASYNC);
28767c478bd9Sstevel@tonic-gate 
2877bd670b35SErik Nordmark 	ahstack = ira->ira_ill->ill_ipst->ips_netstack->netstack_ipsecah;
2878bd670b35SErik Nordmark 
28797c478bd9Sstevel@tonic-gate 	ASSERT(phdr_mp != NULL);
2880bd670b35SErik Nordmark 	ASSERT(phdr_mp->b_datap->db_type == M_DATA);
28817c478bd9Sstevel@tonic-gate 
2882bd670b35SErik Nordmark 	if (force) {
2883bd670b35SErik Nordmark 		/* We are doing asynch; allocate mblks to hold state */
2884bd670b35SErik Nordmark 		if ((mp = ip_recv_attr_to_mblk(ira)) == NULL ||
2885bd670b35SErik Nordmark 		    (mp = ipsec_add_crypto_data(mp, &ic)) == NULL) {
2886bd670b35SErik Nordmark 			BUMP_MIB(ira->ira_ill->ill_ip_mib, ipIfStatsInDiscards);
2887bd670b35SErik Nordmark 			ip_drop_input("ipIfStatsInDiscards", phdr_mp,
2888bd670b35SErik Nordmark 			    ira->ira_ill);
2889bd670b35SErik Nordmark 			freemsg(phdr_mp);
2890bd670b35SErik Nordmark 			return (NULL);
2891bd670b35SErik Nordmark 		}
2892bd670b35SErik Nordmark 
2893bd670b35SErik Nordmark 		linkb(mp, phdr_mp);
2894bd670b35SErik Nordmark 		callrp = &call_req;
2895bd670b35SErik Nordmark 		AH_INIT_CALLREQ(callrp, mp, ah_kcf_callback_inbound);
2896bd670b35SErik Nordmark 	} else {
2897f4b3ec61Sdh155122 		/*
2898bd670b35SErik Nordmark 		 * If we know we are going to do sync then ipsec_crypto_t
2899bd670b35SErik Nordmark 		 * should be on the stack.
2900f4b3ec61Sdh155122 		 */
2901bd670b35SErik Nordmark 		ic = &icstack;
2902bd670b35SErik Nordmark 		bzero(ic, sizeof (*ic));
2903bd670b35SErik Nordmark 		callrp = NULL;
2904bd670b35SErik Nordmark 	}
2905f4b3ec61Sdh155122 
29067c478bd9Sstevel@tonic-gate 	/* init arguments for the crypto framework */
2907bd670b35SErik Nordmark 	AH_INIT_CRYPTO_DATA(&ic->ic_crypto_data, AH_MSGSIZE(phdr_mp),
29087c478bd9Sstevel@tonic-gate 	    phdr_mp);
29097c478bd9Sstevel@tonic-gate 
2910bd670b35SErik Nordmark 	AH_INIT_CRYPTO_MAC(&ic->ic_crypto_mac, icv_len,
29117c478bd9Sstevel@tonic-gate 	    (char *)phdr_mp->b_cont->b_rptr - skip_len + ah_offset +
29127c478bd9Sstevel@tonic-gate 	    sizeof (ah_t));
29137c478bd9Sstevel@tonic-gate 
2914bd670b35SErik Nordmark 	ic->ic_skip_len = skip_len;
29157c478bd9Sstevel@tonic-gate 
29167c478bd9Sstevel@tonic-gate 	IPSEC_CTX_TMPL(assoc, ipsa_authtmpl, IPSEC_ALG_AUTH, ctx_tmpl);
29177c478bd9Sstevel@tonic-gate 
29187c478bd9Sstevel@tonic-gate 	/* call KEF to do the MAC operation */
29197c478bd9Sstevel@tonic-gate 	kef_rc = crypto_mac_verify(&assoc->ipsa_amech,
2920bd670b35SErik Nordmark 	    &ic->ic_crypto_data, &assoc->ipsa_kcfauthkey, ctx_tmpl,
2921bd670b35SErik Nordmark 	    &ic->ic_crypto_mac, callrp);
29227c478bd9Sstevel@tonic-gate 
29237c478bd9Sstevel@tonic-gate 	switch (kef_rc) {
29247c478bd9Sstevel@tonic-gate 	case CRYPTO_SUCCESS:
2925f4b3ec61Sdh155122 		AH_BUMP_STAT(ahstack, crypto_sync);
2926bd670b35SErik Nordmark 		phdr_mp = ah_auth_in_done(phdr_mp, ira, ic);
2927bd670b35SErik Nordmark 		if (force) {
2928bd670b35SErik Nordmark 			/* Free mp after we are done with ic */
2929bd670b35SErik Nordmark 			mp = ipsec_free_crypto_data(mp);
2930bd670b35SErik Nordmark 			(void) ip_recv_attr_free_mblk(mp);
2931bd670b35SErik Nordmark 		}
2932bd670b35SErik Nordmark 		return (phdr_mp);
29337c478bd9Sstevel@tonic-gate 	case CRYPTO_QUEUED:
2934bd670b35SErik Nordmark 		/* ah_kcf_callback_inbound() will be invoked on completion */
2935f4b3ec61Sdh155122 		AH_BUMP_STAT(ahstack, crypto_async);
2936bd670b35SErik Nordmark 		return (NULL);
29377c478bd9Sstevel@tonic-gate 	case CRYPTO_INVALID_MAC:
2938bd670b35SErik Nordmark 		/* Free mp after we are done with ic */
2939f4b3ec61Sdh155122 		AH_BUMP_STAT(ahstack, crypto_sync);
2940bd670b35SErik Nordmark 		BUMP_MIB(ira->ira_ill->ill_ip_mib, ipIfStatsInDiscards);
2941bd670b35SErik Nordmark 		ah_log_bad_auth(phdr_mp, ira, ic);
2942bd670b35SErik Nordmark 		/* phdr_mp was passed to ip_drop_packet */
2943bd670b35SErik Nordmark 		if (force) {
2944bd670b35SErik Nordmark 			mp = ipsec_free_crypto_data(mp);
2945bd670b35SErik Nordmark 			(void) ip_recv_attr_free_mblk(mp);
2946bd670b35SErik Nordmark 		}
2947bd670b35SErik Nordmark 		return (NULL);
29487c478bd9Sstevel@tonic-gate 	}
29497c478bd9Sstevel@tonic-gate 
2950bd670b35SErik Nordmark 	if (force) {
2951bd670b35SErik Nordmark 		mp = ipsec_free_crypto_data(mp);
2952bd670b35SErik Nordmark 		phdr_mp = ip_recv_attr_free_mblk(mp);
2953bd670b35SErik Nordmark 	}
2954bd670b35SErik Nordmark 	BUMP_MIB(ira->ira_ill->ill_ip_mib, ipIfStatsInDiscards);
2955bd670b35SErik Nordmark 	ah_crypto_failed(phdr_mp, B_TRUE, kef_rc, ira->ira_ill, ahstack);
2956bd670b35SErik Nordmark 	/* phdr_mp was passed to ip_drop_packet */
2957bd670b35SErik Nordmark 	return (NULL);
29587c478bd9Sstevel@tonic-gate }
29597c478bd9Sstevel@tonic-gate 
29607c478bd9Sstevel@tonic-gate /*
29617c478bd9Sstevel@tonic-gate  * Submit an outbound packet for processing by the crypto framework.
29627c478bd9Sstevel@tonic-gate  */
2963bd670b35SErik Nordmark static mblk_t *
2964bd670b35SErik Nordmark ah_submit_req_outbound(mblk_t *phdr_mp, ip_xmit_attr_t *ixa,
2965bd670b35SErik Nordmark     size_t skip_len, ipsa_t *assoc)
29667c478bd9Sstevel@tonic-gate {
29677c478bd9Sstevel@tonic-gate 	int kef_rc;
2968bd670b35SErik Nordmark 	mblk_t *mp;
2969bd670b35SErik Nordmark 	crypto_call_req_t call_req, *callrp;
29707c478bd9Sstevel@tonic-gate 	uint_t icv_len = assoc->ipsa_mac_len;
2971bd670b35SErik Nordmark 	ipsecah_stack_t	*ahstack;
2972bd670b35SErik Nordmark 	ipsec_crypto_t	*ic, icstack;
2973bd670b35SErik Nordmark 	ill_t		*ill = ixa->ixa_nce->nce_ill;
2974bd670b35SErik Nordmark 	boolean_t force = (assoc->ipsa_flags & IPSA_F_ASYNC);
29757c478bd9Sstevel@tonic-gate 
2976bd670b35SErik Nordmark 	ahstack = ill->ill_ipst->ips_netstack->netstack_ipsecah;
2977bd670b35SErik Nordmark 
29787c478bd9Sstevel@tonic-gate 	ASSERT(phdr_mp != NULL);
2979bd670b35SErik Nordmark 	ASSERT(phdr_mp->b_datap->db_type == M_DATA);
29807c478bd9Sstevel@tonic-gate 
2981bd670b35SErik Nordmark 	if (force) {
2982bd670b35SErik Nordmark 		/* We are doing asynch; allocate mblks to hold state */
2983bd670b35SErik Nordmark 		if ((mp = ip_xmit_attr_to_mblk(ixa)) == NULL ||
2984bd670b35SErik Nordmark 		    (mp = ipsec_add_crypto_data(mp, &ic)) == NULL) {
2985bd670b35SErik Nordmark 			BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
2986bd670b35SErik Nordmark 			ip_drop_output("ipIfStatsOutDiscards", phdr_mp, ill);
2987bd670b35SErik Nordmark 			freemsg(phdr_mp);
2988bd670b35SErik Nordmark 			return (NULL);
2989bd670b35SErik Nordmark 		}
2990bd670b35SErik Nordmark 		linkb(mp, phdr_mp);
2991bd670b35SErik Nordmark 		callrp = &call_req;
2992bd670b35SErik Nordmark 		AH_INIT_CALLREQ(callrp, mp, ah_kcf_callback_outbound);
2993bd670b35SErik Nordmark 	} else {
2994f4b3ec61Sdh155122 		/*
2995bd670b35SErik Nordmark 		 * If we know we are going to do sync then ipsec_crypto_t
2996bd670b35SErik Nordmark 		 * should be on the stack.
2997f4b3ec61Sdh155122 		 */
2998bd670b35SErik Nordmark 		ic = &icstack;
2999bd670b35SErik Nordmark 		bzero(ic, sizeof (*ic));
3000bd670b35SErik Nordmark 		callrp = NULL;
3001bd670b35SErik Nordmark 	}
3002f4b3ec61Sdh155122 
30037c478bd9Sstevel@tonic-gate 	/* init arguments for the crypto framework */
3004bd670b35SErik Nordmark 	AH_INIT_CRYPTO_DATA(&ic->ic_crypto_data, AH_MSGSIZE(phdr_mp),
30057c478bd9Sstevel@tonic-gate 	    phdr_mp);
30067c478bd9Sstevel@tonic-gate 
3007bd670b35SErik Nordmark 	AH_INIT_CRYPTO_MAC(&ic->ic_crypto_mac, icv_len,
30087c478bd9Sstevel@tonic-gate 	    (char *)phdr_mp->b_wptr);
30097c478bd9Sstevel@tonic-gate 
3010bd670b35SErik Nordmark 	ic->ic_skip_len = skip_len;
30117c478bd9Sstevel@tonic-gate 
3012bd670b35SErik Nordmark 	ASSERT(ixa->ixa_ipsec_ah_sa != NULL);
30137c478bd9Sstevel@tonic-gate 
30147c478bd9Sstevel@tonic-gate 	/* call KEF to do the MAC operation */
3015bd670b35SErik Nordmark 	kef_rc = crypto_mac(&assoc->ipsa_amech, &ic->ic_crypto_data,
30167c478bd9Sstevel@tonic-gate 	    &assoc->ipsa_kcfauthkey, assoc->ipsa_authtmpl,
3017bd670b35SErik Nordmark 	    &ic->ic_crypto_mac, callrp);
30187c478bd9Sstevel@tonic-gate 
30197c478bd9Sstevel@tonic-gate 	switch (kef_rc) {
30207c478bd9Sstevel@tonic-gate 	case CRYPTO_SUCCESS:
3021f4b3ec61Sdh155122 		AH_BUMP_STAT(ahstack, crypto_sync);
3022bd670b35SErik Nordmark 		phdr_mp = ah_auth_out_done(phdr_mp, ixa, ic);
3023bd670b35SErik Nordmark 		if (force) {
3024bd670b35SErik Nordmark 			/* Free mp after we are done with ic */
3025bd670b35SErik Nordmark 			mp = ipsec_free_crypto_data(mp);
3026bd670b35SErik Nordmark 			(void) ip_xmit_attr_free_mblk(mp);
3027bd670b35SErik Nordmark 		}
3028bd670b35SErik Nordmark 		return (phdr_mp);
30297c478bd9Sstevel@tonic-gate 	case CRYPTO_QUEUED:
3030bd670b35SErik Nordmark 		/* ah_kcf_callback_outbound() will be invoked on completion */
3031f4b3ec61Sdh155122 		AH_BUMP_STAT(ahstack, crypto_async);
3032bd670b35SErik Nordmark 		return (NULL);
30337c478bd9Sstevel@tonic-gate 	}
30347c478bd9Sstevel@tonic-gate 
3035bd670b35SErik Nordmark 	if (force) {
3036bd670b35SErik Nordmark 		mp = ipsec_free_crypto_data(mp);
3037bd670b35SErik Nordmark 		phdr_mp = ip_xmit_attr_free_mblk(mp);
3038bd670b35SErik Nordmark 	}
3039bd670b35SErik Nordmark 	BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
3040bd670b35SErik Nordmark 	ah_crypto_failed(phdr_mp, B_FALSE, kef_rc, NULL, ahstack);
3041bd670b35SErik Nordmark 	/* phdr_mp was passed to ip_drop_packet */
3042bd670b35SErik Nordmark 	return (NULL);
30437c478bd9Sstevel@tonic-gate }
30447c478bd9Sstevel@tonic-gate 
30457c478bd9Sstevel@tonic-gate /*
30467c478bd9Sstevel@tonic-gate  * This function constructs a pseudo header by looking at the IP header
30477c478bd9Sstevel@tonic-gate  * and options if any. This is called for both outbound and inbound,
30487c478bd9Sstevel@tonic-gate  * before computing the ICV.
30497c478bd9Sstevel@tonic-gate  */
30507c478bd9Sstevel@tonic-gate static mblk_t *
30517c478bd9Sstevel@tonic-gate ah_process_ip_options_v6(mblk_t *mp, ipsa_t *assoc, int *length_to_skip,
3052f4b3ec61Sdh155122     uint_t ah_data_sz, boolean_t outbound, ipsecah_stack_t *ahstack)
30537c478bd9Sstevel@tonic-gate {
30547c478bd9Sstevel@tonic-gate 	ip6_t	*ip6h;
30557c478bd9Sstevel@tonic-gate 	ip6_t	*oip6h;
30567c478bd9Sstevel@tonic-gate 	mblk_t 	*phdr_mp;
30577c478bd9Sstevel@tonic-gate 	int option_length;
30587c478bd9Sstevel@tonic-gate 	uint_t	ah_align_sz;
30597c478bd9Sstevel@tonic-gate 	uint_t ah_offset;
30607c478bd9Sstevel@tonic-gate 	int hdr_size;
30617c478bd9Sstevel@tonic-gate 
30627c478bd9Sstevel@tonic-gate 	/*
30637c478bd9Sstevel@tonic-gate 	 * Allocate space for the authentication data also. It is
30647c478bd9Sstevel@tonic-gate 	 * useful both during the ICV calculation where we need to
30657c478bd9Sstevel@tonic-gate 	 * feed in zeroes and while sending the datagram back to IP
30667c478bd9Sstevel@tonic-gate 	 * where we will be using the same space.
30677c478bd9Sstevel@tonic-gate 	 *
30687c478bd9Sstevel@tonic-gate 	 * We need to allocate space for padding bytes if it is not
30697c478bd9Sstevel@tonic-gate 	 * a multiple of IPV6_PADDING_ALIGN.
30707c478bd9Sstevel@tonic-gate 	 *
30717c478bd9Sstevel@tonic-gate 	 * In addition, we allocate space for the ICV computed by
30727c478bd9Sstevel@tonic-gate 	 * the kernel crypto framework, saving us a separate kmem
30737c478bd9Sstevel@tonic-gate 	 * allocation down the road.
30747c478bd9Sstevel@tonic-gate 	 */
30757c478bd9Sstevel@tonic-gate 
30767c478bd9Sstevel@tonic-gate 	ah_align_sz = P2ALIGN(ah_data_sz + IPV6_PADDING_ALIGN - 1,
30777c478bd9Sstevel@tonic-gate 	    IPV6_PADDING_ALIGN);
30787c478bd9Sstevel@tonic-gate 
30797c478bd9Sstevel@tonic-gate 	ASSERT(ah_align_sz >= ah_data_sz);
30807c478bd9Sstevel@tonic-gate 
30817c478bd9Sstevel@tonic-gate 	hdr_size = ipsec_ah_get_hdr_size_v6(mp, B_FALSE);
30827c478bd9Sstevel@tonic-gate 	option_length = hdr_size - IPV6_HDR_LEN;
30837c478bd9Sstevel@tonic-gate 
30847c478bd9Sstevel@tonic-gate 	/* This was not included in ipsec_ah_get_hdr_size_v6() */
30857c478bd9Sstevel@tonic-gate 	hdr_size += (sizeof (ah_t) + ah_align_sz);
30867c478bd9Sstevel@tonic-gate 
30877c478bd9Sstevel@tonic-gate 	if (!outbound && (MBLKL(mp) < hdr_size)) {
30887c478bd9Sstevel@tonic-gate 		/*
30897c478bd9Sstevel@tonic-gate 		 * We have post-AH header options in a separate mblk,
30907c478bd9Sstevel@tonic-gate 		 * a pullup is required.
30917c478bd9Sstevel@tonic-gate 		 */
30927c478bd9Sstevel@tonic-gate 		if (!pullupmsg(mp, hdr_size))
30937c478bd9Sstevel@tonic-gate 			return (NULL);
30947c478bd9Sstevel@tonic-gate 	}
30957c478bd9Sstevel@tonic-gate 
3096de8c4a14SErik Nordmark 	if ((phdr_mp = allocb_tmpl(hdr_size + ah_data_sz, mp)) == NULL) {
30977c478bd9Sstevel@tonic-gate 		return (NULL);
30987c478bd9Sstevel@tonic-gate 	}
30997c478bd9Sstevel@tonic-gate 
31007c478bd9Sstevel@tonic-gate 	oip6h = (ip6_t *)mp->b_rptr;
31017c478bd9Sstevel@tonic-gate 
31027c478bd9Sstevel@tonic-gate 	/*
31037c478bd9Sstevel@tonic-gate 	 * Form the basic IP header first. Zero out the header
31047c478bd9Sstevel@tonic-gate 	 * so that the mutable fields are zeroed out.
31057c478bd9Sstevel@tonic-gate 	 */
31067c478bd9Sstevel@tonic-gate 	ip6h = (ip6_t *)phdr_mp->b_rptr;
31077c478bd9Sstevel@tonic-gate 	bzero(ip6h, sizeof (ip6_t));
31087c478bd9Sstevel@tonic-gate 	ip6h->ip6_vcf = IPV6_DEFAULT_VERS_AND_FLOW;
31097c478bd9Sstevel@tonic-gate 
31107c478bd9Sstevel@tonic-gate 	if (outbound) {
31117c478bd9Sstevel@tonic-gate 		/*
31127c478bd9Sstevel@tonic-gate 		 * Include the size of AH and authentication data.
31137c478bd9Sstevel@tonic-gate 		 * This is how our recipient would compute the
31147c478bd9Sstevel@tonic-gate 		 * authentication data. Look at what we do in the
31157c478bd9Sstevel@tonic-gate 		 * inbound case below.
31167c478bd9Sstevel@tonic-gate 		 */
31177c478bd9Sstevel@tonic-gate 		ip6h->ip6_plen = htons(ntohs(oip6h->ip6_plen) +
31187c478bd9Sstevel@tonic-gate 		    sizeof (ah_t) + ah_align_sz);
31197c478bd9Sstevel@tonic-gate 	} else {
31207c478bd9Sstevel@tonic-gate 		ip6h->ip6_plen = oip6h->ip6_plen;
31217c478bd9Sstevel@tonic-gate 	}
31227c478bd9Sstevel@tonic-gate 
31237c478bd9Sstevel@tonic-gate 	ip6h->ip6_src = oip6h->ip6_src;
31247c478bd9Sstevel@tonic-gate 	ip6h->ip6_dst = oip6h->ip6_dst;
31257c478bd9Sstevel@tonic-gate 
31267c478bd9Sstevel@tonic-gate 	*length_to_skip = IPV6_HDR_LEN;
31277c478bd9Sstevel@tonic-gate 	if (option_length == 0) {
31287c478bd9Sstevel@tonic-gate 		/* Form the AH header */
31297c478bd9Sstevel@tonic-gate 		ip6h->ip6_nxt = IPPROTO_AH;
31307c478bd9Sstevel@tonic-gate 		((ah_t *)(ip6h + 1))->ah_nexthdr = oip6h->ip6_nxt;
31317c478bd9Sstevel@tonic-gate 		ah_offset = *length_to_skip;
31327c478bd9Sstevel@tonic-gate 	} else {
31337c478bd9Sstevel@tonic-gate 		ip6h->ip6_nxt = oip6h->ip6_nxt;
31347c478bd9Sstevel@tonic-gate 		/* option_length does not include the AH header's size */
31357c478bd9Sstevel@tonic-gate 		*length_to_skip += option_length;
31367c478bd9Sstevel@tonic-gate 
31377c478bd9Sstevel@tonic-gate 		ah_offset = ah_fix_phdr_v6(ip6h, oip6h, outbound, B_FALSE);
31387c478bd9Sstevel@tonic-gate 		if (ah_offset == 0) {
31397c478bd9Sstevel@tonic-gate 			return (NULL);
31407c478bd9Sstevel@tonic-gate 		}
31417c478bd9Sstevel@tonic-gate 	}
31427c478bd9Sstevel@tonic-gate 
31437c478bd9Sstevel@tonic-gate 	if (!ah_finish_up(((ah_t *)((uint8_t *)ip6h + ah_offset)),
31447c478bd9Sstevel@tonic-gate 	    (outbound ? NULL : ((ah_t *)((uint8_t *)oip6h + ah_offset))),
3145f4b3ec61Sdh155122 	    assoc, ah_data_sz, ah_align_sz, ahstack)) {
31467c478bd9Sstevel@tonic-gate 		freeb(phdr_mp);
31477c478bd9Sstevel@tonic-gate 		/*
31487c478bd9Sstevel@tonic-gate 		 * Returning NULL will tell the caller to
31497c478bd9Sstevel@tonic-gate 		 * IPSA_REFELE(), free the memory, etc.
31507c478bd9Sstevel@tonic-gate 		 */
31517c478bd9Sstevel@tonic-gate 		return (NULL);
31527c478bd9Sstevel@tonic-gate 	}
31537c478bd9Sstevel@tonic-gate 
31547c478bd9Sstevel@tonic-gate 	phdr_mp->b_wptr = ((uint8_t *)ip6h + ah_offset + sizeof (ah_t) +
31557c478bd9Sstevel@tonic-gate 	    ah_align_sz);
31567c478bd9Sstevel@tonic-gate 	if (!outbound)
31577c478bd9Sstevel@tonic-gate 		*length_to_skip += sizeof (ah_t) + ah_align_sz;
31587c478bd9Sstevel@tonic-gate 	return (phdr_mp);
31597c478bd9Sstevel@tonic-gate }
31607c478bd9Sstevel@tonic-gate 
31617c478bd9Sstevel@tonic-gate /*
31627c478bd9Sstevel@tonic-gate  * This function constructs a pseudo header by looking at the IP header
31637c478bd9Sstevel@tonic-gate  * and options if any. This is called for both outbound and inbound,
31647c478bd9Sstevel@tonic-gate  * before computing the ICV.
31657c478bd9Sstevel@tonic-gate  */
31667c478bd9Sstevel@tonic-gate static mblk_t *
31677c478bd9Sstevel@tonic-gate ah_process_ip_options_v4(mblk_t *mp, ipsa_t *assoc, int *length_to_skip,
3168f4b3ec61Sdh155122     uint_t ah_data_sz, boolean_t outbound, ipsecah_stack_t *ahstack)
31697c478bd9Sstevel@tonic-gate {
31707c478bd9Sstevel@tonic-gate 	ipoptp_t opts;
31717c478bd9Sstevel@tonic-gate 	uint32_t option_length;
31727c478bd9Sstevel@tonic-gate 	ipha_t	*ipha;
31737c478bd9Sstevel@tonic-gate 	ipha_t	*oipha;
31747c478bd9Sstevel@tonic-gate 	mblk_t 	*phdr_mp;
31757c478bd9Sstevel@tonic-gate 	int	 size;
31767c478bd9Sstevel@tonic-gate 	uchar_t	*optptr;
31777c478bd9Sstevel@tonic-gate 	uint8_t optval;
31787c478bd9Sstevel@tonic-gate 	uint8_t optlen;
31797c478bd9Sstevel@tonic-gate 	ipaddr_t dst;
31807c478bd9Sstevel@tonic-gate 	uint32_t v_hlen_tos_len;
31817c478bd9Sstevel@tonic-gate 	int ip_hdr_length;
31827c478bd9Sstevel@tonic-gate 	uint_t	ah_align_sz;
31837c478bd9Sstevel@tonic-gate 	uint32_t off;
31847c478bd9Sstevel@tonic-gate 
31857c478bd9Sstevel@tonic-gate #ifdef	_BIG_ENDIAN
31867c478bd9Sstevel@tonic-gate #define	V_HLEN	(v_hlen_tos_len >> 24)
31877c478bd9Sstevel@tonic-gate #else
31887c478bd9Sstevel@tonic-gate #define	V_HLEN	(v_hlen_tos_len & 0xFF)
31897c478bd9Sstevel@tonic-gate #endif
31907c478bd9Sstevel@tonic-gate 
31917c478bd9Sstevel@tonic-gate 	oipha = (ipha_t *)mp->b_rptr;
31927c478bd9Sstevel@tonic-gate 	v_hlen_tos_len = ((uint32_t *)oipha)[0];
31937c478bd9Sstevel@tonic-gate 
31947c478bd9Sstevel@tonic-gate 	/*
31957c478bd9Sstevel@tonic-gate 	 * Allocate space for the authentication data also. It is
31967c478bd9Sstevel@tonic-gate 	 * useful both during the ICV calculation where we need to
31977c478bd9Sstevel@tonic-gate 	 * feed in zeroes and while sending the datagram back to IP
31987c478bd9Sstevel@tonic-gate 	 * where we will be using the same space.
31997c478bd9Sstevel@tonic-gate 	 *
32007c478bd9Sstevel@tonic-gate 	 * We need to allocate space for padding bytes if it is not
32017c478bd9Sstevel@tonic-gate 	 * a multiple of IPV4_PADDING_ALIGN.
32027c478bd9Sstevel@tonic-gate 	 *
32037c478bd9Sstevel@tonic-gate 	 * In addition, we allocate space for the ICV computed by
32047c478bd9Sstevel@tonic-gate 	 * the kernel crypto framework, saving us a separate kmem
32057c478bd9Sstevel@tonic-gate 	 * allocation down the road.
32067c478bd9Sstevel@tonic-gate 	 */
32077c478bd9Sstevel@tonic-gate 
32087c478bd9Sstevel@tonic-gate 	ah_align_sz = P2ALIGN(ah_data_sz + IPV4_PADDING_ALIGN - 1,
32097c478bd9Sstevel@tonic-gate 	    IPV4_PADDING_ALIGN);
32107c478bd9Sstevel@tonic-gate 
32117c478bd9Sstevel@tonic-gate 	ASSERT(ah_align_sz >= ah_data_sz);
32127c478bd9Sstevel@tonic-gate 
32137c478bd9Sstevel@tonic-gate 	size = IP_SIMPLE_HDR_LENGTH + sizeof (ah_t) + ah_align_sz +
32147c478bd9Sstevel@tonic-gate 	    ah_data_sz;
32157c478bd9Sstevel@tonic-gate 
32167c478bd9Sstevel@tonic-gate 	if (V_HLEN != IP_SIMPLE_HDR_VERSION) {
32177c478bd9Sstevel@tonic-gate 		option_length = oipha->ipha_version_and_hdr_length -
32187c478bd9Sstevel@tonic-gate 		    (uint8_t)((IP_VERSION << 4) +
32197c478bd9Sstevel@tonic-gate 		    IP_SIMPLE_HDR_LENGTH_IN_WORDS);
32207c478bd9Sstevel@tonic-gate 		option_length <<= 2;
32217c478bd9Sstevel@tonic-gate 		size += option_length;
32227c478bd9Sstevel@tonic-gate 	}
32237c478bd9Sstevel@tonic-gate 
3224de8c4a14SErik Nordmark 	if ((phdr_mp = allocb_tmpl(size, mp)) == NULL) {
32257c478bd9Sstevel@tonic-gate 		return (NULL);
32267c478bd9Sstevel@tonic-gate 	}
32277c478bd9Sstevel@tonic-gate 
32287c478bd9Sstevel@tonic-gate 	/*
32297c478bd9Sstevel@tonic-gate 	 * Form the basic IP header first.
32307c478bd9Sstevel@tonic-gate 	 */
32317c478bd9Sstevel@tonic-gate 	ipha = (ipha_t *)phdr_mp->b_rptr;
32327c478bd9Sstevel@tonic-gate 	ipha->ipha_version_and_hdr_length = oipha->ipha_version_and_hdr_length;
32337c478bd9Sstevel@tonic-gate 	ipha->ipha_type_of_service = 0;
32347c478bd9Sstevel@tonic-gate 
32357c478bd9Sstevel@tonic-gate 	if (outbound) {
32367c478bd9Sstevel@tonic-gate 		/*
32377c478bd9Sstevel@tonic-gate 		 * Include the size of AH and authentication data.
32387c478bd9Sstevel@tonic-gate 		 * This is how our recipient would compute the
32397c478bd9Sstevel@tonic-gate 		 * authentication data. Look at what we do in the
32407c478bd9Sstevel@tonic-gate 		 * inbound case below.
32417c478bd9Sstevel@tonic-gate 		 */
32427c478bd9Sstevel@tonic-gate 		ipha->ipha_length = ntohs(htons(oipha->ipha_length) +
32437c478bd9Sstevel@tonic-gate 		    sizeof (ah_t) + ah_align_sz);
32447c478bd9Sstevel@tonic-gate 	} else {
32457c478bd9Sstevel@tonic-gate 		ipha->ipha_length = oipha->ipha_length;
32467c478bd9Sstevel@tonic-gate 	}
32477c478bd9Sstevel@tonic-gate 
32487c478bd9Sstevel@tonic-gate 	ipha->ipha_ident = oipha->ipha_ident;
32497c478bd9Sstevel@tonic-gate 	ipha->ipha_fragment_offset_and_flags = 0;
32507c478bd9Sstevel@tonic-gate 	ipha->ipha_ttl = 0;
32517c478bd9Sstevel@tonic-gate 	ipha->ipha_protocol = IPPROTO_AH;
32527c478bd9Sstevel@tonic-gate 	ipha->ipha_hdr_checksum = 0;
32537c478bd9Sstevel@tonic-gate 	ipha->ipha_src = oipha->ipha_src;
32547c478bd9Sstevel@tonic-gate 	ipha->ipha_dst = dst = oipha->ipha_dst;
32557c478bd9Sstevel@tonic-gate 
32567c478bd9Sstevel@tonic-gate 	/*
32577c478bd9Sstevel@tonic-gate 	 * If there is no option to process return now.
32587c478bd9Sstevel@tonic-gate 	 */
32597c478bd9Sstevel@tonic-gate 	ip_hdr_length = IP_SIMPLE_HDR_LENGTH;
32607c478bd9Sstevel@tonic-gate 
32617c478bd9Sstevel@tonic-gate 	if (V_HLEN == IP_SIMPLE_HDR_VERSION) {
32627c478bd9Sstevel@tonic-gate 		/* Form the AH header */
32637c478bd9Sstevel@tonic-gate 		goto ah_hdr;
32647c478bd9Sstevel@tonic-gate 	}
32657c478bd9Sstevel@tonic-gate 
32667c478bd9Sstevel@tonic-gate 	ip_hdr_length += option_length;
32677c478bd9Sstevel@tonic-gate 
32687c478bd9Sstevel@tonic-gate 	/*
32697c478bd9Sstevel@tonic-gate 	 * We have options. In the outbound case for source route,
32707c478bd9Sstevel@tonic-gate 	 * ULP has already moved the first hop, which is now in
32717c478bd9Sstevel@tonic-gate 	 * ipha_dst. We need the final destination for the calculation
32727c478bd9Sstevel@tonic-gate 	 * of authentication data. And also make sure that mutable
32737c478bd9Sstevel@tonic-gate 	 * and experimental fields are zeroed out in the IP options.
32747c478bd9Sstevel@tonic-gate 	 */
32757c478bd9Sstevel@tonic-gate 
32767c478bd9Sstevel@tonic-gate 	bcopy(&oipha[1], &ipha[1], option_length);
32777c478bd9Sstevel@tonic-gate 
32787c478bd9Sstevel@tonic-gate 	for (optval = ipoptp_first(&opts, ipha);
32797c478bd9Sstevel@tonic-gate 	    optval != IPOPT_EOL;
32807c478bd9Sstevel@tonic-gate 	    optval = ipoptp_next(&opts)) {
32817c478bd9Sstevel@tonic-gate 		optptr = opts.ipoptp_cur;
32827c478bd9Sstevel@tonic-gate 		optlen = opts.ipoptp_len;
32837c478bd9Sstevel@tonic-gate 		switch (optval) {
32847c478bd9Sstevel@tonic-gate 		case IPOPT_EXTSEC:
32857c478bd9Sstevel@tonic-gate 		case IPOPT_COMSEC:
32867c478bd9Sstevel@tonic-gate 		case IPOPT_RA:
32877c478bd9Sstevel@tonic-gate 		case IPOPT_SDMDD:
32887c478bd9Sstevel@tonic-gate 		case IPOPT_SECURITY:
32897c478bd9Sstevel@tonic-gate 			/*
32907c478bd9Sstevel@tonic-gate 			 * These options are Immutable, leave them as-is.
32917c478bd9Sstevel@tonic-gate 			 * Note that IPOPT_NOP is also Immutable, but it
32927c478bd9Sstevel@tonic-gate 			 * was skipped by ipoptp_next() and thus remains
32937c478bd9Sstevel@tonic-gate 			 * intact in the header.
32947c478bd9Sstevel@tonic-gate 			 */
32957c478bd9Sstevel@tonic-gate 			break;
32967c478bd9Sstevel@tonic-gate 		case IPOPT_SSRR:
32977c478bd9Sstevel@tonic-gate 		case IPOPT_LSRR:
32987c478bd9Sstevel@tonic-gate 			if ((opts.ipoptp_flags & IPOPTP_ERROR) != 0)
32997c478bd9Sstevel@tonic-gate 				goto bad_ipv4opt;
33007c478bd9Sstevel@tonic-gate 			/*
33017c478bd9Sstevel@tonic-gate 			 * These two are mutable and will be zeroed, but
33027c478bd9Sstevel@tonic-gate 			 * first get the final destination.
33037c478bd9Sstevel@tonic-gate 			 */
33047c478bd9Sstevel@tonic-gate 			off = optptr[IPOPT_OFFSET];
33057c478bd9Sstevel@tonic-gate 			/*
33067c478bd9Sstevel@tonic-gate 			 * If one of the conditions is true, it means
33077c478bd9Sstevel@tonic-gate 			 * end of options and dst already has the right
33087c478bd9Sstevel@tonic-gate 			 * value. So, just fall through.
33097c478bd9Sstevel@tonic-gate 			 */
33107c478bd9Sstevel@tonic-gate 			if (!(optlen < IP_ADDR_LEN || off > optlen - 3)) {
33117c478bd9Sstevel@tonic-gate 				off = optlen - IP_ADDR_LEN;
33127c478bd9Sstevel@tonic-gate 				bcopy(&optptr[off], &dst, IP_ADDR_LEN);
33137c478bd9Sstevel@tonic-gate 			}
33147c478bd9Sstevel@tonic-gate 			/* FALLTHRU */
33157c478bd9Sstevel@tonic-gate 		case IPOPT_RR:
33167c478bd9Sstevel@tonic-gate 		case IPOPT_TS:
33177c478bd9Sstevel@tonic-gate 		case IPOPT_SATID:
33187c478bd9Sstevel@tonic-gate 		default:
33197c478bd9Sstevel@tonic-gate 			/*
33207c478bd9Sstevel@tonic-gate 			 * optlen should include from the beginning of an
33217c478bd9Sstevel@tonic-gate 			 * option.
33227c478bd9Sstevel@tonic-gate 			 * NOTE : Stream Identifier Option (SID): RFC 791
33237c478bd9Sstevel@tonic-gate 			 * shows the bit pattern of optlen as 2 and documents
33247c478bd9Sstevel@tonic-gate 			 * the length as 4. We assume it to be 2 here.
33257c478bd9Sstevel@tonic-gate 			 */
33267c478bd9Sstevel@tonic-gate 			bzero(optptr, optlen);
33277c478bd9Sstevel@tonic-gate 			break;
33287c478bd9Sstevel@tonic-gate 		}
33297c478bd9Sstevel@tonic-gate 	}
33307c478bd9Sstevel@tonic-gate 
33317c478bd9Sstevel@tonic-gate 	if ((opts.ipoptp_flags & IPOPTP_ERROR) != 0) {
33327c478bd9Sstevel@tonic-gate bad_ipv4opt:
3333f4b3ec61Sdh155122 		ah1dbg(ahstack, ("AH : bad IPv4 option"));
33347c478bd9Sstevel@tonic-gate 		freeb(phdr_mp);
33357c478bd9Sstevel@tonic-gate 		return (NULL);
33367c478bd9Sstevel@tonic-gate 	}
33377c478bd9Sstevel@tonic-gate 
33387c478bd9Sstevel@tonic-gate 	/*
33397c478bd9Sstevel@tonic-gate 	 * Don't change ipha_dst for an inbound datagram as it points
33407c478bd9Sstevel@tonic-gate 	 * to the right value. Only for the outbound with LSRR/SSRR,
33417c478bd9Sstevel@tonic-gate 	 * because of ip_massage_options called by the ULP, ipha_dst
33427c478bd9Sstevel@tonic-gate 	 * points to the first hop and we need to use the final
33437c478bd9Sstevel@tonic-gate 	 * destination for computing the ICV.
33447c478bd9Sstevel@tonic-gate 	 */
33457c478bd9Sstevel@tonic-gate 
33467c478bd9Sstevel@tonic-gate 	if (outbound)
33477c478bd9Sstevel@tonic-gate 		ipha->ipha_dst = dst;
33487c478bd9Sstevel@tonic-gate ah_hdr:
33497c478bd9Sstevel@tonic-gate 	((ah_t *)((uint8_t *)ipha + ip_hdr_length))->ah_nexthdr =
33507c478bd9Sstevel@tonic-gate 	    oipha->ipha_protocol;
33517c478bd9Sstevel@tonic-gate 	if (!ah_finish_up(((ah_t *)((uint8_t *)ipha + ip_hdr_length)),
33527c478bd9Sstevel@tonic-gate 	    (outbound ? NULL : ((ah_t *)((uint8_t *)oipha + ip_hdr_length))),
3353f4b3ec61Sdh155122 	    assoc, ah_data_sz, ah_align_sz, ahstack)) {
33547c478bd9Sstevel@tonic-gate 		freeb(phdr_mp);
33557c478bd9Sstevel@tonic-gate 		/*
33567c478bd9Sstevel@tonic-gate 		 * Returning NULL will tell the caller to IPSA_REFELE(), free
33577c478bd9Sstevel@tonic-gate 		 * the memory, etc.
33587c478bd9Sstevel@tonic-gate 		 */
33597c478bd9Sstevel@tonic-gate 		return (NULL);
33607c478bd9Sstevel@tonic-gate 	}
33617c478bd9Sstevel@tonic-gate 
33627c478bd9Sstevel@tonic-gate 	phdr_mp->b_wptr = ((uchar_t *)ipha + ip_hdr_length +
33637c478bd9Sstevel@tonic-gate 	    sizeof (ah_t) + ah_align_sz);
33647c478bd9Sstevel@tonic-gate 
33657c478bd9Sstevel@tonic-gate 	ASSERT(phdr_mp->b_wptr <= phdr_mp->b_datap->db_lim);
33667c478bd9Sstevel@tonic-gate 	if (outbound)
33677c478bd9Sstevel@tonic-gate 		*length_to_skip = ip_hdr_length;
33687c478bd9Sstevel@tonic-gate 	else
33697c478bd9Sstevel@tonic-gate 		*length_to_skip = ip_hdr_length + sizeof (ah_t) + ah_align_sz;
33707c478bd9Sstevel@tonic-gate 	return (phdr_mp);
33717c478bd9Sstevel@tonic-gate }
33727c478bd9Sstevel@tonic-gate 
33737c478bd9Sstevel@tonic-gate /*
33747c478bd9Sstevel@tonic-gate  * Authenticate an outbound datagram. This function is called
33757c478bd9Sstevel@tonic-gate  * whenever IP sends an outbound datagram that needs authentication.
3376bd670b35SErik Nordmark  * Returns a modified packet if done. Returns NULL if error or queued.
3377bd670b35SErik Nordmark  * If error return then ipIfStatsOutDiscards has been increased.
33787c478bd9Sstevel@tonic-gate  */
3379bd670b35SErik Nordmark static mblk_t *
3380bd670b35SErik Nordmark ah_outbound(mblk_t *data_mp, ip_xmit_attr_t *ixa)
33817c478bd9Sstevel@tonic-gate {
33827c478bd9Sstevel@tonic-gate 	mblk_t *phdr_mp;
33837c478bd9Sstevel@tonic-gate 	ipsa_t *assoc;
33847c478bd9Sstevel@tonic-gate 	int length_to_skip;
33857c478bd9Sstevel@tonic-gate 	uint_t ah_align_sz;
33867c478bd9Sstevel@tonic-gate 	uint_t age_bytes;
3387bd670b35SErik Nordmark 	netstack_t	*ns = ixa->ixa_ipst->ips_netstack;
3388bd670b35SErik Nordmark 	ipsecah_stack_t	*ahstack = ns->netstack_ipsecah;
3389bd670b35SErik Nordmark 	ipsec_stack_t	*ipss = ns->netstack_ipsec;
3390bd670b35SErik Nordmark 	ill_t		*ill = ixa->ixa_nce->nce_ill;
3391bd670b35SErik Nordmark 	boolean_t	need_refrele = B_FALSE;
33927c478bd9Sstevel@tonic-gate 
33937c478bd9Sstevel@tonic-gate 	/*
33947c478bd9Sstevel@tonic-gate 	 * Construct the chain of mblks
33957c478bd9Sstevel@tonic-gate 	 *
3396bd670b35SErik Nordmark 	 * PSEUDO_HDR->DATA
33977c478bd9Sstevel@tonic-gate 	 *
33987c478bd9Sstevel@tonic-gate 	 * one by one.
33997c478bd9Sstevel@tonic-gate 	 */
34007c478bd9Sstevel@tonic-gate 
3401f4b3ec61Sdh155122 	AH_BUMP_STAT(ahstack, out_requests);
34027c478bd9Sstevel@tonic-gate 
3403bd670b35SErik Nordmark 	ASSERT(data_mp->b_datap->db_type == M_DATA);
34047c478bd9Sstevel@tonic-gate 
3405bd670b35SErik Nordmark 	assoc = ixa->ixa_ipsec_ah_sa;
34067c478bd9Sstevel@tonic-gate 	ASSERT(assoc != NULL);
34077c478bd9Sstevel@tonic-gate 
34085d3b8cb7SBill Sommerfeld 
34095d3b8cb7SBill Sommerfeld 	/*
34105d3b8cb7SBill Sommerfeld 	 * Get the outer IP header in shape to escape this system..
34115d3b8cb7SBill Sommerfeld 	 */
3412bd670b35SErik Nordmark 	if (is_system_labeled() && (assoc->ipsa_otsl != NULL)) {
3413bd670b35SErik Nordmark 		/*
3414bd670b35SErik Nordmark 		 * Need to update packet with any CIPSO option and update
3415bd670b35SErik Nordmark 		 * ixa_tsl to capture the new label.
3416bd670b35SErik Nordmark 		 * We allocate a separate ixa for that purpose.
3417bd670b35SErik Nordmark 		 */
3418bd670b35SErik Nordmark 		ixa = ip_xmit_attr_duplicate(ixa);
3419bd670b35SErik Nordmark 		if (ixa == NULL) {
3420bd670b35SErik Nordmark 			ip_drop_packet(data_mp, B_FALSE, ill,
3421bd670b35SErik Nordmark 			    DROPPER(ipss, ipds_ah_nomem),
34225d3b8cb7SBill Sommerfeld 			    &ahstack->ah_dropper);
3423bd670b35SErik Nordmark 			return (NULL);
34245d3b8cb7SBill Sommerfeld 		}
3425bd670b35SErik Nordmark 		need_refrele = B_TRUE;
3426bd670b35SErik Nordmark 
3427bd670b35SErik Nordmark 		label_hold(assoc->ipsa_otsl);
3428bd670b35SErik Nordmark 		ip_xmit_attr_replace_tsl(ixa, assoc->ipsa_otsl);
3429bd670b35SErik Nordmark 
3430bd670b35SErik Nordmark 		data_mp = sadb_whack_label(data_mp, assoc, ixa,
3431bd670b35SErik Nordmark 		    DROPPER(ipss, ipds_ah_nomem), &ahstack->ah_dropper);
3432bd670b35SErik Nordmark 		if (data_mp == NULL) {
3433bd670b35SErik Nordmark 			/* Packet dropped by sadb_whack_label */
3434bd670b35SErik Nordmark 			ixa_refrele(ixa);
3435bd670b35SErik Nordmark 			return (NULL);
3436bd670b35SErik Nordmark 		}
34375d3b8cb7SBill Sommerfeld 	}
34385d3b8cb7SBill Sommerfeld 
34397c478bd9Sstevel@tonic-gate 	/*
34407c478bd9Sstevel@tonic-gate 	 * Age SA according to number of bytes that will be sent after
34417c478bd9Sstevel@tonic-gate 	 * adding the AH header, ICV, and padding to the packet.
34427c478bd9Sstevel@tonic-gate 	 */
34437c478bd9Sstevel@tonic-gate 
3444bd670b35SErik Nordmark 	if (ixa->ixa_flags & IXAF_IS_IPV4) {
3445bd670b35SErik Nordmark 		ipha_t *ipha = (ipha_t *)data_mp->b_rptr;
34467c478bd9Sstevel@tonic-gate 		ah_align_sz = P2ALIGN(assoc->ipsa_mac_len +
34477c478bd9Sstevel@tonic-gate 		    IPV4_PADDING_ALIGN - 1, IPV4_PADDING_ALIGN);
34487c478bd9Sstevel@tonic-gate 		age_bytes = ntohs(ipha->ipha_length) + sizeof (ah_t) +
34497c478bd9Sstevel@tonic-gate 		    ah_align_sz;
34507c478bd9Sstevel@tonic-gate 	} else {
3451bd670b35SErik Nordmark 		ip6_t *ip6h = (ip6_t *)data_mp->b_rptr;
34527c478bd9Sstevel@tonic-gate 		ah_align_sz = P2ALIGN(assoc->ipsa_mac_len +
34537c478bd9Sstevel@tonic-gate 		    IPV6_PADDING_ALIGN - 1, IPV6_PADDING_ALIGN);
34547c478bd9Sstevel@tonic-gate 		age_bytes = sizeof (ip6_t) + ntohs(ip6h->ip6_plen) +
34557c478bd9Sstevel@tonic-gate 		    sizeof (ah_t) + ah_align_sz;
34567c478bd9Sstevel@tonic-gate 	}
34577c478bd9Sstevel@tonic-gate 
34587c478bd9Sstevel@tonic-gate 	if (!ah_age_bytes(assoc, age_bytes, B_FALSE)) {
34597c478bd9Sstevel@tonic-gate 		/* rig things as if ipsec_getassocbyconn() failed */
34607c478bd9Sstevel@tonic-gate 		ipsec_assocfailure(info.mi_idnum, 0, 0, SL_ERROR | SL_WARN,
34617c478bd9Sstevel@tonic-gate 		    "AH association 0x%x, dst %s had bytes expire.\n",
3462f4b3ec61Sdh155122 		    ntohl(assoc->ipsa_spi), assoc->ipsa_dstaddr, AF_INET,
3463f4b3ec61Sdh155122 		    ahstack->ipsecah_netstack);
3464bd670b35SErik Nordmark 		BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
3465bd670b35SErik Nordmark 		ip_drop_output("ipIfStatsOutDiscards", data_mp, ill);
3466bd670b35SErik Nordmark 		freemsg(data_mp);
3467bd670b35SErik Nordmark 		if (need_refrele)
3468bd670b35SErik Nordmark 			ixa_refrele(ixa);
3469bd670b35SErik Nordmark 		return (NULL);
34707c478bd9Sstevel@tonic-gate 	}
34717c478bd9Sstevel@tonic-gate 
34725d3b8cb7SBill Sommerfeld 	/*
34735d3b8cb7SBill Sommerfeld 	 * XXX We need to have fixed up the outer label before we get here.
34745d3b8cb7SBill Sommerfeld 	 * (AH is computing the checksum over the outer label).
34755d3b8cb7SBill Sommerfeld 	 */
34765d3b8cb7SBill Sommerfeld 
34777c478bd9Sstevel@tonic-gate 	/*
34787c478bd9Sstevel@tonic-gate 	 * Insert pseudo header:
3479bd670b35SErik Nordmark 	 * [IP, ULP] => [IP, AH, ICV] -> ULP
34807c478bd9Sstevel@tonic-gate 	 */
34817c478bd9Sstevel@tonic-gate 
3482bd670b35SErik Nordmark 	if (ixa->ixa_flags & IXAF_IS_IPV4) {
3483bd670b35SErik Nordmark 		phdr_mp = ah_process_ip_options_v4(data_mp, assoc,
3484bd670b35SErik Nordmark 		    &length_to_skip, assoc->ipsa_mac_len, B_TRUE, ahstack);
34857c478bd9Sstevel@tonic-gate 	} else {
3486bd670b35SErik Nordmark 		phdr_mp = ah_process_ip_options_v6(data_mp, assoc,
3487bd670b35SErik Nordmark 		    &length_to_skip, assoc->ipsa_mac_len, B_TRUE, ahstack);
34887c478bd9Sstevel@tonic-gate 	}
34897c478bd9Sstevel@tonic-gate 
34907c478bd9Sstevel@tonic-gate 	if (phdr_mp == NULL) {
3491f4b3ec61Sdh155122 		AH_BUMP_STAT(ahstack, out_discards);
3492bd670b35SErik Nordmark 		ip_drop_packet(data_mp, B_FALSE, ixa->ixa_nce->nce_ill,
3493f4b3ec61Sdh155122 		    DROPPER(ipss, ipds_ah_bad_v4_opts),
3494f4b3ec61Sdh155122 		    &ahstack->ah_dropper);
3495bd670b35SErik Nordmark 		BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
3496bd670b35SErik Nordmark 		if (need_refrele)
3497bd670b35SErik Nordmark 			ixa_refrele(ixa);
3498bd670b35SErik Nordmark 		return (NULL);
34997c478bd9Sstevel@tonic-gate 	}
35007c478bd9Sstevel@tonic-gate 
3501bd670b35SErik Nordmark 	phdr_mp->b_cont = data_mp;
3502bd670b35SErik Nordmark 	data_mp->b_rptr += length_to_skip;
3503bd670b35SErik Nordmark 	data_mp = phdr_mp;
35047c478bd9Sstevel@tonic-gate 
35057c478bd9Sstevel@tonic-gate 	/*
3506bd670b35SErik Nordmark 	 * At this point data_mp points to
3507bd670b35SErik Nordmark 	 * an mblk containing the pseudo header (IP header,
35087c478bd9Sstevel@tonic-gate 	 * AH header, and ICV with mutable fields zero'ed out).
35097c478bd9Sstevel@tonic-gate 	 * mp points to the mblk containing the ULP data. The original
3510bd670b35SErik Nordmark 	 * IP header is kept before the ULP data in data_mp.
35117c478bd9Sstevel@tonic-gate 	 */
35127c478bd9Sstevel@tonic-gate 
35137c478bd9Sstevel@tonic-gate 	/* submit MAC request to KCF */
3514bd670b35SErik Nordmark 	data_mp = ah_submit_req_outbound(data_mp, ixa, length_to_skip, assoc);
3515bd670b35SErik Nordmark 	if (need_refrele)
3516bd670b35SErik Nordmark 		ixa_refrele(ixa);
3517bd670b35SErik Nordmark 	return (data_mp);
35187c478bd9Sstevel@tonic-gate }
35197c478bd9Sstevel@tonic-gate 
3520bd670b35SErik Nordmark static mblk_t *
3521bd670b35SErik Nordmark ah_inbound(mblk_t *data_mp, void *arg, ip_recv_attr_t *ira)
35227c478bd9Sstevel@tonic-gate {
35237c478bd9Sstevel@tonic-gate 	ah_t		*ah = (ah_t *)arg;
3524bd670b35SErik Nordmark 	ipsa_t		*assoc = ira->ira_ipsec_ah_sa;
35257c478bd9Sstevel@tonic-gate 	int		length_to_skip;
35267c478bd9Sstevel@tonic-gate 	int		ah_length;
35277c478bd9Sstevel@tonic-gate 	mblk_t		*phdr_mp;
35287c478bd9Sstevel@tonic-gate 	uint32_t	ah_offset;
3529bd670b35SErik Nordmark 	netstack_t	*ns = ira->ira_ill->ill_ipst->ips_netstack;
3530f4b3ec61Sdh155122 	ipsecah_stack_t	*ahstack = ns->netstack_ipsecah;
3531f4b3ec61Sdh155122 	ipsec_stack_t	*ipss = ns->netstack_ipsec;
35327c478bd9Sstevel@tonic-gate 
35337c478bd9Sstevel@tonic-gate 	ASSERT(assoc != NULL);
35347c478bd9Sstevel@tonic-gate 
35357c478bd9Sstevel@tonic-gate 	/*
35367c478bd9Sstevel@tonic-gate 	 * We may wish to check replay in-range-only here as an optimization.
35377c478bd9Sstevel@tonic-gate 	 * Include the reality check of ipsa->ipsa_replay >
35387c478bd9Sstevel@tonic-gate 	 * ipsa->ipsa_replay_wsize for times when it's the first N packets,
35397c478bd9Sstevel@tonic-gate 	 * where N == ipsa->ipsa_replay_wsize.
35407c478bd9Sstevel@tonic-gate 	 *
35417c478bd9Sstevel@tonic-gate 	 * Another check that may come here later is the "collision" check.
35427c478bd9Sstevel@tonic-gate 	 * If legitimate packets flow quickly enough, this won't be a problem,
35437c478bd9Sstevel@tonic-gate 	 * but collisions may cause authentication algorithm crunching to
35447c478bd9Sstevel@tonic-gate 	 * take place when it doesn't need to.
35457c478bd9Sstevel@tonic-gate 	 */
35467c478bd9Sstevel@tonic-gate 	if (!sadb_replay_peek(assoc, ah->ah_replay)) {
3547f4b3ec61Sdh155122 		AH_BUMP_STAT(ahstack, replay_early_failures);
3548f4b3ec61Sdh155122 		IP_AH_BUMP_STAT(ipss, in_discards);
3549bd670b35SErik Nordmark 		ip_drop_packet(data_mp, B_TRUE, ira->ira_ill,
3550f4b3ec61Sdh155122 		    DROPPER(ipss, ipds_ah_early_replay),
3551f4b3ec61Sdh155122 		    &ahstack->ah_dropper);
3552bd670b35SErik Nordmark 		BUMP_MIB(ira->ira_ill->ill_ip_mib, ipIfStatsInDiscards);
3553bd670b35SErik Nordmark 		return (NULL);
35547c478bd9Sstevel@tonic-gate 	}
35557c478bd9Sstevel@tonic-gate 
35567c478bd9Sstevel@tonic-gate 	/*
35577c478bd9Sstevel@tonic-gate 	 * The offset of the AH header can be computed from its pointer
35587c478bd9Sstevel@tonic-gate 	 * within the data mblk, which was pulled up until the AH header
35597c478bd9Sstevel@tonic-gate 	 * by ipsec_inbound_ah_sa() during SA selection.
35607c478bd9Sstevel@tonic-gate 	 */
35617c478bd9Sstevel@tonic-gate 	ah_offset = (uchar_t *)ah - data_mp->b_rptr;
35627c478bd9Sstevel@tonic-gate 
35637c478bd9Sstevel@tonic-gate 	/*
35647c478bd9Sstevel@tonic-gate 	 * We need to pullup until the ICV before we call
35657c478bd9Sstevel@tonic-gate 	 * ah_process_ip_options_v6.
35667c478bd9Sstevel@tonic-gate 	 */
35677c478bd9Sstevel@tonic-gate 	ah_length = (ah->ah_length << 2) + 8;
35687c478bd9Sstevel@tonic-gate 
35697c478bd9Sstevel@tonic-gate 	/*
35707c478bd9Sstevel@tonic-gate 	 * NOTE : If we want to use any field of IP/AH header, you need
35717c478bd9Sstevel@tonic-gate 	 * to re-assign following the pullup.
35727c478bd9Sstevel@tonic-gate 	 */
35737c478bd9Sstevel@tonic-gate 	if (((uchar_t *)ah + ah_length) > data_mp->b_wptr) {
35747c478bd9Sstevel@tonic-gate 		if (!pullupmsg(data_mp, (uchar_t *)ah + ah_length -
35757c478bd9Sstevel@tonic-gate 		    data_mp->b_rptr)) {
3576f4b3ec61Sdh155122 			(void) ipsec_rl_strlog(ns, info.mi_idnum, 0, 0,
35777c478bd9Sstevel@tonic-gate 			    SL_WARN | SL_ERROR,
35787c478bd9Sstevel@tonic-gate 			    "ah_inbound: Small AH header\n");
3579f4b3ec61Sdh155122 			IP_AH_BUMP_STAT(ipss, in_discards);
3580bd670b35SErik Nordmark 			ip_drop_packet(data_mp, B_TRUE, ira->ira_ill,
3581f4b3ec61Sdh155122 			    DROPPER(ipss, ipds_ah_nomem),
3582f4b3ec61Sdh155122 			    &ahstack->ah_dropper);
3583bd670b35SErik Nordmark 			BUMP_MIB(ira->ira_ill->ill_ip_mib, ipIfStatsInDiscards);
3584bd670b35SErik Nordmark 			return (NULL);
35857c478bd9Sstevel@tonic-gate 		}
35867c478bd9Sstevel@tonic-gate 	}
35877c478bd9Sstevel@tonic-gate 
35887c478bd9Sstevel@tonic-gate 	/*
35897c478bd9Sstevel@tonic-gate 	 * Insert pseudo header:
3590bd670b35SErik Nordmark 	 * [IP, ULP] => [IP, AH, ICV] -> ULP
35917c478bd9Sstevel@tonic-gate 	 */
3592bd670b35SErik Nordmark 	if (ira->ira_flags & IRAF_IS_IPV4) {
35937c478bd9Sstevel@tonic-gate 		phdr_mp = ah_process_ip_options_v4(data_mp, assoc,
3594f4b3ec61Sdh155122 		    &length_to_skip, assoc->ipsa_mac_len, B_FALSE, ahstack);
35957c478bd9Sstevel@tonic-gate 	} else {
35967c478bd9Sstevel@tonic-gate 		phdr_mp = ah_process_ip_options_v6(data_mp, assoc,
3597f4b3ec61Sdh155122 		    &length_to_skip, assoc->ipsa_mac_len, B_FALSE, ahstack);
35987c478bd9Sstevel@tonic-gate 	}
35997c478bd9Sstevel@tonic-gate 
36007c478bd9Sstevel@tonic-gate 	if (phdr_mp == NULL) {
3601f4b3ec61Sdh155122 		IP_AH_BUMP_STAT(ipss, in_discards);
3602bd670b35SErik Nordmark 		ip_drop_packet(data_mp, B_TRUE, ira->ira_ill,
3603bd670b35SErik Nordmark 		    ((ira->ira_flags & IRAF_IS_IPV4) ?
3604f4b3ec61Sdh155122 		    DROPPER(ipss, ipds_ah_bad_v4_opts) :
3605f4b3ec61Sdh155122 		    DROPPER(ipss, ipds_ah_bad_v6_hdrs)),
3606f4b3ec61Sdh155122 		    &ahstack->ah_dropper);
3607bd670b35SErik Nordmark 		BUMP_MIB(ira->ira_ill->ill_ip_mib, ipIfStatsInDiscards);
3608bd670b35SErik Nordmark 		return (NULL);
36097c478bd9Sstevel@tonic-gate 	}
36107c478bd9Sstevel@tonic-gate 
36117c478bd9Sstevel@tonic-gate 	phdr_mp->b_cont = data_mp;
36127c478bd9Sstevel@tonic-gate 	data_mp->b_rptr += length_to_skip;
3613bd670b35SErik Nordmark 	data_mp = phdr_mp;
36147c478bd9Sstevel@tonic-gate 
36157c478bd9Sstevel@tonic-gate 	/* submit request to KCF */
3616bd670b35SErik Nordmark 	return (ah_submit_req_inbound(data_mp, ira, length_to_skip, ah_offset,
36177c478bd9Sstevel@tonic-gate 	    assoc));
36187c478bd9Sstevel@tonic-gate }
36197c478bd9Sstevel@tonic-gate 
36207c478bd9Sstevel@tonic-gate /*
36217c478bd9Sstevel@tonic-gate  * Invoked after processing of an inbound packet by the
36227c478bd9Sstevel@tonic-gate  * kernel crypto framework. Called by ah_submit_req() for a sync request,
36237c478bd9Sstevel@tonic-gate  * or by the kcf callback for an async request.
3624bd670b35SErik Nordmark  * Returns NULL if the mblk chain is consumed.
36257c478bd9Sstevel@tonic-gate  */
3626bd670b35SErik Nordmark static mblk_t *
3627bd670b35SErik Nordmark ah_auth_in_done(mblk_t *phdr_mp, ip_recv_attr_t *ira, ipsec_crypto_t *ic)
36287c478bd9Sstevel@tonic-gate {
36297c478bd9Sstevel@tonic-gate 	ipha_t *ipha;
36307c478bd9Sstevel@tonic-gate 	uint_t ah_offset = 0;
36317c478bd9Sstevel@tonic-gate 	mblk_t *mp;
363232350c00Sdanmcd 	int align_len, newpos;
36337c478bd9Sstevel@tonic-gate 	ah_t *ah;
36347c478bd9Sstevel@tonic-gate 	uint32_t length;
363532350c00Sdanmcd 	uint32_t *dest32;
363632350c00Sdanmcd 	uint8_t *dest;
36377c478bd9Sstevel@tonic-gate 	boolean_t isv4;
36387c478bd9Sstevel@tonic-gate 	ip6_t *ip6h;
36397c478bd9Sstevel@tonic-gate 	uint_t icv_len;
36407c478bd9Sstevel@tonic-gate 	ipsa_t *assoc;
36417c478bd9Sstevel@tonic-gate 	kstat_named_t *counter;
3642bd670b35SErik Nordmark 	netstack_t	*ns = ira->ira_ill->ill_ipst->ips_netstack;
3643bd670b35SErik Nordmark 	ipsecah_stack_t	*ahstack = ns->netstack_ipsecah;
3644bd670b35SErik Nordmark 	ipsec_stack_t	*ipss = ns->netstack_ipsec;
36457c478bd9Sstevel@tonic-gate 
3646bd670b35SErik Nordmark 	isv4 = (ira->ira_flags & IRAF_IS_IPV4);
3647bd670b35SErik Nordmark 	assoc = ira->ira_ipsec_ah_sa;
3648bd670b35SErik Nordmark 	icv_len = (uint_t)ic->ic_crypto_mac.cd_raw.iov_len;
3649f4b3ec61Sdh155122 
36507c478bd9Sstevel@tonic-gate 	if (phdr_mp == NULL) {
3651bd670b35SErik Nordmark 		ip_drop_packet(phdr_mp, B_TRUE, ira->ira_ill,
3652f4b3ec61Sdh155122 		    DROPPER(ipss, ipds_ah_nomem),
3653f4b3ec61Sdh155122 		    &ahstack->ah_dropper);
3654bd670b35SErik Nordmark 		BUMP_MIB(ira->ira_ill->ill_ip_mib, ipIfStatsInDiscards);
3655bd670b35SErik Nordmark 		return (NULL);
36567c478bd9Sstevel@tonic-gate 	}
36577c478bd9Sstevel@tonic-gate 
36587c478bd9Sstevel@tonic-gate 	mp = phdr_mp->b_cont;
36597c478bd9Sstevel@tonic-gate 	if (mp == NULL) {
3660bd670b35SErik Nordmark 		ip_drop_packet(phdr_mp, B_TRUE, ira->ira_ill,
3661f4b3ec61Sdh155122 		    DROPPER(ipss, ipds_ah_nomem),
3662f4b3ec61Sdh155122 		    &ahstack->ah_dropper);
3663bd670b35SErik Nordmark 		BUMP_MIB(ira->ira_ill->ill_ip_mib, ipIfStatsInDiscards);
3664bd670b35SErik Nordmark 		return (NULL);
36657c478bd9Sstevel@tonic-gate 	}
3666bd670b35SErik Nordmark 	mp->b_rptr -= ic->ic_skip_len;
36677c478bd9Sstevel@tonic-gate 
3668437220cdSdanmcd 	ah_set_usetime(assoc, B_TRUE);
3669437220cdSdanmcd 
36707c478bd9Sstevel@tonic-gate 	if (isv4) {
36717c478bd9Sstevel@tonic-gate 		ipha = (ipha_t *)mp->b_rptr;
36727c478bd9Sstevel@tonic-gate 		ah_offset = ipha->ipha_version_and_hdr_length -
36737c478bd9Sstevel@tonic-gate 		    (uint8_t)((IP_VERSION << 4));
36747c478bd9Sstevel@tonic-gate 		ah_offset <<= 2;
36757c478bd9Sstevel@tonic-gate 		align_len = P2ALIGN(icv_len + IPV4_PADDING_ALIGN - 1,
36767c478bd9Sstevel@tonic-gate 		    IPV4_PADDING_ALIGN);
36777c478bd9Sstevel@tonic-gate 	} else {
36787c478bd9Sstevel@tonic-gate 		ip6h = (ip6_t *)mp->b_rptr;
36797c478bd9Sstevel@tonic-gate 		ah_offset = ipsec_ah_get_hdr_size_v6(mp, B_TRUE);
36807c478bd9Sstevel@tonic-gate 		ASSERT((mp->b_wptr - mp->b_rptr) >= ah_offset);
36817c478bd9Sstevel@tonic-gate 		align_len = P2ALIGN(icv_len + IPV6_PADDING_ALIGN - 1,
36827c478bd9Sstevel@tonic-gate 		    IPV6_PADDING_ALIGN);
36837c478bd9Sstevel@tonic-gate 	}
36847c478bd9Sstevel@tonic-gate 
36857c478bd9Sstevel@tonic-gate 	ah = (ah_t *)(mp->b_rptr + ah_offset);
368632350c00Sdanmcd 	newpos = sizeof (ah_t) + align_len;
36877c478bd9Sstevel@tonic-gate 
36887c478bd9Sstevel@tonic-gate 	/*
36897c478bd9Sstevel@tonic-gate 	 * We get here only when authentication passed.
36907c478bd9Sstevel@tonic-gate 	 */
36917c478bd9Sstevel@tonic-gate 
3692f4b3ec61Sdh155122 	ah3dbg(ahstack, ("AH succeeded, checking replay\n"));
3693f4b3ec61Sdh155122 	AH_BUMP_STAT(ahstack, good_auth);
36947c478bd9Sstevel@tonic-gate 
36957c478bd9Sstevel@tonic-gate 	if (!sadb_replay_check(assoc, ah->ah_replay)) {
36967c478bd9Sstevel@tonic-gate 		int af;
36977c478bd9Sstevel@tonic-gate 		void *addr;
36987c478bd9Sstevel@tonic-gate 
36997c478bd9Sstevel@tonic-gate 		if (isv4) {
37007c478bd9Sstevel@tonic-gate 			addr = &ipha->ipha_dst;
37017c478bd9Sstevel@tonic-gate 			af = AF_INET;
37027c478bd9Sstevel@tonic-gate 		} else {
37037c478bd9Sstevel@tonic-gate 			addr = &ip6h->ip6_dst;
37047c478bd9Sstevel@tonic-gate 			af = AF_INET6;
37057c478bd9Sstevel@tonic-gate 		}
37067c478bd9Sstevel@tonic-gate 
37077c478bd9Sstevel@tonic-gate 		/*
37087c478bd9Sstevel@tonic-gate 		 * Log the event. As of now we print out an event.
37097c478bd9Sstevel@tonic-gate 		 * Do not print the replay failure number, or else
37107c478bd9Sstevel@tonic-gate 		 * syslog cannot collate the error messages.  Printing
37117c478bd9Sstevel@tonic-gate 		 * the replay number that failed (or printing to the
37127c478bd9Sstevel@tonic-gate 		 * console) opens a denial-of-service attack.
37137c478bd9Sstevel@tonic-gate 		 */
3714f4b3ec61Sdh155122 		AH_BUMP_STAT(ahstack, replay_failures);
37157c478bd9Sstevel@tonic-gate 		ipsec_assocfailure(info.mi_idnum, 0, 0,
37167c478bd9Sstevel@tonic-gate 		    SL_ERROR | SL_WARN,
37177c478bd9Sstevel@tonic-gate 		    "Replay failed for AH spi %x, dst_addr %s",
3718f4b3ec61Sdh155122 		    assoc->ipsa_spi, addr, af, ahstack->ipsecah_netstack);
3719f4b3ec61Sdh155122 		counter = DROPPER(ipss, ipds_ah_replay);
37207c478bd9Sstevel@tonic-gate 		goto ah_in_discard;
37217c478bd9Sstevel@tonic-gate 	}
37227c478bd9Sstevel@tonic-gate 
37237c478bd9Sstevel@tonic-gate 	/*
37247c478bd9Sstevel@tonic-gate 	 * We need to remove the AH header from the original
372532350c00Sdanmcd 	 * datagram. Best way to do this is to move the pre-AH headers
372632350c00Sdanmcd 	 * forward in the (relatively simple) IPv4 case.  In IPv6, it's
372732350c00Sdanmcd 	 * a bit more complicated because of IPv6's next-header chaining,
372832350c00Sdanmcd 	 * but it's doable.
37297c478bd9Sstevel@tonic-gate 	 */
37307c478bd9Sstevel@tonic-gate 	if (isv4) {
37317c478bd9Sstevel@tonic-gate 		/*
37327c478bd9Sstevel@tonic-gate 		 * Assign the right protocol, adjust the length as we
37337c478bd9Sstevel@tonic-gate 		 * are removing the AH header and adjust the checksum to
37347c478bd9Sstevel@tonic-gate 		 * account for the protocol and length.
37357c478bd9Sstevel@tonic-gate 		 */
373632350c00Sdanmcd 		length = ntohs(ipha->ipha_length);
37377c478bd9Sstevel@tonic-gate 		if (!ah_age_bytes(assoc, length, B_TRUE)) {
37387c478bd9Sstevel@tonic-gate 			/* The ipsa has hit hard expiration, LOG and AUDIT. */
37397c478bd9Sstevel@tonic-gate 			ipsec_assocfailure(info.mi_idnum, 0, 0,
37407c478bd9Sstevel@tonic-gate 			    SL_ERROR | SL_WARN,
37417c478bd9Sstevel@tonic-gate 			    "AH Association 0x%x, dst %s had bytes expire.\n",
37427c478bd9Sstevel@tonic-gate 			    assoc->ipsa_spi, assoc->ipsa_dstaddr,
3743f4b3ec61Sdh155122 			    AF_INET, ahstack->ipsecah_netstack);
3744f4b3ec61Sdh155122 			AH_BUMP_STAT(ahstack, bytes_expired);
3745f4b3ec61Sdh155122 			counter = DROPPER(ipss, ipds_ah_bytes_expire);
37467c478bd9Sstevel@tonic-gate 			goto ah_in_discard;
37477c478bd9Sstevel@tonic-gate 		}
374832350c00Sdanmcd 		ipha->ipha_protocol = ah->ah_nexthdr;
374932350c00Sdanmcd 		length -= newpos;
37507c478bd9Sstevel@tonic-gate 
375132350c00Sdanmcd 		ipha->ipha_length = htons((uint16_t)length);
375232350c00Sdanmcd 		ipha->ipha_hdr_checksum = 0;
375332350c00Sdanmcd 		ipha->ipha_hdr_checksum = (uint16_t)ip_csum_hdr(ipha);
37547c478bd9Sstevel@tonic-gate 	} else {
37557c478bd9Sstevel@tonic-gate 		uchar_t *whereptr;
37567c478bd9Sstevel@tonic-gate 		int hdrlen;
37577c478bd9Sstevel@tonic-gate 		uint8_t *nexthdr;
37587c478bd9Sstevel@tonic-gate 		ip6_hbh_t *hbhhdr;
37597c478bd9Sstevel@tonic-gate 		ip6_dest_t *dsthdr;
37607c478bd9Sstevel@tonic-gate 		ip6_rthdr0_t *rthdr;
37617c478bd9Sstevel@tonic-gate 
37627c478bd9Sstevel@tonic-gate 		/*
37637c478bd9Sstevel@tonic-gate 		 * Make phdr_mp hold until the AH header and make
37647c478bd9Sstevel@tonic-gate 		 * mp hold everything past AH header.
37657c478bd9Sstevel@tonic-gate 		 */
376632350c00Sdanmcd 		length = ntohs(ip6h->ip6_plen);
37677c478bd9Sstevel@tonic-gate 		if (!ah_age_bytes(assoc, length + sizeof (ip6_t), B_TRUE)) {
37687c478bd9Sstevel@tonic-gate 			/* The ipsa has hit hard expiration, LOG and AUDIT. */
37697c478bd9Sstevel@tonic-gate 			ipsec_assocfailure(info.mi_idnum, 0, 0,
37707c478bd9Sstevel@tonic-gate 			    SL_ERROR | SL_WARN,
37717c478bd9Sstevel@tonic-gate 			    "AH Association 0x%x, dst %s had bytes "
37727c478bd9Sstevel@tonic-gate 			    "expire.\n", assoc->ipsa_spi, &ip6h->ip6_dst,
3773f4b3ec61Sdh155122 			    AF_INET6, ahstack->ipsecah_netstack);
3774f4b3ec61Sdh155122 			AH_BUMP_STAT(ahstack, bytes_expired);
3775f4b3ec61Sdh155122 			counter = DROPPER(ipss, ipds_ah_bytes_expire);
37767c478bd9Sstevel@tonic-gate 			goto ah_in_discard;
37777c478bd9Sstevel@tonic-gate 		}
37787c478bd9Sstevel@tonic-gate 
37797c478bd9Sstevel@tonic-gate 		/*
37807c478bd9Sstevel@tonic-gate 		 * Update the next header field of the header preceding
37817c478bd9Sstevel@tonic-gate 		 * AH with the next header field of AH. Start with the
37827c478bd9Sstevel@tonic-gate 		 * IPv6 header and proceed with the extension headers
37837c478bd9Sstevel@tonic-gate 		 * until we find what we're looking for.
37847c478bd9Sstevel@tonic-gate 		 */
378532350c00Sdanmcd 		nexthdr = &ip6h->ip6_nxt;
378632350c00Sdanmcd 		whereptr =  (uchar_t *)ip6h;
37877c478bd9Sstevel@tonic-gate 		hdrlen = sizeof (ip6_t);
37887c478bd9Sstevel@tonic-gate 
37897c478bd9Sstevel@tonic-gate 		while (*nexthdr != IPPROTO_AH) {
37907c478bd9Sstevel@tonic-gate 			whereptr += hdrlen;
37917c478bd9Sstevel@tonic-gate 			/* Assume IP has already stripped it */
3792bd670b35SErik Nordmark 			ASSERT(*nexthdr != IPPROTO_FRAGMENT);
37937c478bd9Sstevel@tonic-gate 			switch (*nexthdr) {
37947c478bd9Sstevel@tonic-gate 			case IPPROTO_HOPOPTS:
37957c478bd9Sstevel@tonic-gate 				hbhhdr = (ip6_hbh_t *)whereptr;
37967c478bd9Sstevel@tonic-gate 				nexthdr = &hbhhdr->ip6h_nxt;
37977c478bd9Sstevel@tonic-gate 				hdrlen = 8 * (hbhhdr->ip6h_len + 1);
37987c478bd9Sstevel@tonic-gate 				break;
37997c478bd9Sstevel@tonic-gate 			case IPPROTO_DSTOPTS:
38007c478bd9Sstevel@tonic-gate 				dsthdr = (ip6_dest_t *)whereptr;
38017c478bd9Sstevel@tonic-gate 				nexthdr = &dsthdr->ip6d_nxt;
38027c478bd9Sstevel@tonic-gate 				hdrlen = 8 * (dsthdr->ip6d_len + 1);
38037c478bd9Sstevel@tonic-gate 				break;
38047c478bd9Sstevel@tonic-gate 			case IPPROTO_ROUTING:
38057c478bd9Sstevel@tonic-gate 				rthdr = (ip6_rthdr0_t *)whereptr;
38067c478bd9Sstevel@tonic-gate 				nexthdr = &rthdr->ip6r0_nxt;
38077c478bd9Sstevel@tonic-gate 				hdrlen = 8 * (rthdr->ip6r0_len + 1);
38087c478bd9Sstevel@tonic-gate 				break;
38097c478bd9Sstevel@tonic-gate 			}
38107c478bd9Sstevel@tonic-gate 		}
38117c478bd9Sstevel@tonic-gate 		*nexthdr = ah->ah_nexthdr;
381232350c00Sdanmcd 		length -= newpos;
381332350c00Sdanmcd 		ip6h->ip6_plen = htons((uint16_t)length);
38147c478bd9Sstevel@tonic-gate 	}
38157c478bd9Sstevel@tonic-gate 
381632350c00Sdanmcd 	/* Now that we've fixed the IP header, move it forward. */
381732350c00Sdanmcd 	mp->b_rptr += newpos;
381832350c00Sdanmcd 	if (IS_P2ALIGNED(mp->b_rptr, sizeof (uint32_t))) {
381932350c00Sdanmcd 		dest32 = (uint32_t *)(mp->b_rptr + ah_offset);
382032350c00Sdanmcd 		while (--dest32 >= (uint32_t *)mp->b_rptr)
382132350c00Sdanmcd 			*dest32 = *(dest32 - (newpos >> 2));
382232350c00Sdanmcd 	} else {
382332350c00Sdanmcd 		dest = mp->b_rptr + ah_offset;
382432350c00Sdanmcd 		while (--dest >= mp->b_rptr)
382532350c00Sdanmcd 			*dest = *(dest - newpos);
382632350c00Sdanmcd 	}
382732350c00Sdanmcd 	freeb(phdr_mp);
38285d3b8cb7SBill Sommerfeld 
38295d3b8cb7SBill Sommerfeld 	/*
38305d3b8cb7SBill Sommerfeld 	 * If SA is labelled, use its label, else inherit the label
38315d3b8cb7SBill Sommerfeld 	 */
3832bd670b35SErik Nordmark 	if (is_system_labeled() && (assoc->ipsa_tsl != NULL)) {
3833bd670b35SErik Nordmark 		if (!ip_recv_attr_replace_label(ira, assoc->ipsa_tsl)) {
3834bd670b35SErik Nordmark 			ip_drop_packet(mp, B_TRUE, ira->ira_ill,
3835bd670b35SErik Nordmark 			    DROPPER(ipss, ipds_ah_nomem), &ahstack->ah_dropper);
3836bd670b35SErik Nordmark 			BUMP_MIB(ira->ira_ill->ill_ip_mib, ipIfStatsInDiscards);
3837bd670b35SErik Nordmark 			return (NULL);
3838bd670b35SErik Nordmark 		}
38395d3b8cb7SBill Sommerfeld 	}
38405d3b8cb7SBill Sommerfeld 
3841d74f5ecaSDan McDonald 	if (assoc->ipsa_state == IPSA_STATE_IDLE) {
3842d74f5ecaSDan McDonald 		/*
3843d74f5ecaSDan McDonald 		 * Cluster buffering case.  Tell caller that we're
3844d74f5ecaSDan McDonald 		 * handling the packet.
3845d74f5ecaSDan McDonald 		 */
3846bd670b35SErik Nordmark 		sadb_buf_pkt(assoc, mp, ira);
3847bd670b35SErik Nordmark 		return (NULL);
3848d74f5ecaSDan McDonald 	}
38495d3b8cb7SBill Sommerfeld 
3850bd670b35SErik Nordmark 	return (mp);
38517c478bd9Sstevel@tonic-gate 
38527c478bd9Sstevel@tonic-gate ah_in_discard:
3853f4b3ec61Sdh155122 	IP_AH_BUMP_STAT(ipss, in_discards);
3854bd670b35SErik Nordmark 	ip_drop_packet(phdr_mp, B_TRUE, ira->ira_ill, counter,
3855f4b3ec61Sdh155122 	    &ahstack->ah_dropper);
3856bd670b35SErik Nordmark 	BUMP_MIB(ira->ira_ill->ill_ip_mib, ipIfStatsInDiscards);
3857bd670b35SErik Nordmark 	return (NULL);
38587c478bd9Sstevel@tonic-gate }
38597c478bd9Sstevel@tonic-gate 
38607c478bd9Sstevel@tonic-gate /*
38617c478bd9Sstevel@tonic-gate  * Invoked after processing of an outbound packet by the
38627c478bd9Sstevel@tonic-gate  * kernel crypto framework, either by ah_submit_req() for a request
38637c478bd9Sstevel@tonic-gate  * executed syncrhonously, or by the KEF callback for a request
38647c478bd9Sstevel@tonic-gate  * executed asynchronously.
38657c478bd9Sstevel@tonic-gate  */
3866bd670b35SErik Nordmark static mblk_t *
3867bd670b35SErik Nordmark ah_auth_out_done(mblk_t *phdr_mp, ip_xmit_attr_t *ixa, ipsec_crypto_t *ic)
38687c478bd9Sstevel@tonic-gate {
38697c478bd9Sstevel@tonic-gate 	mblk_t *mp;
38707c478bd9Sstevel@tonic-gate 	int align_len;
38717c478bd9Sstevel@tonic-gate 	uint32_t hdrs_length;
38727c478bd9Sstevel@tonic-gate 	uchar_t *ptr;
38737c478bd9Sstevel@tonic-gate 	uint32_t length;
38747c478bd9Sstevel@tonic-gate 	boolean_t isv4;
38757c478bd9Sstevel@tonic-gate 	size_t icv_len;
3876bd670b35SErik Nordmark 	netstack_t	*ns = ixa->ixa_ipst->ips_netstack;
3877bd670b35SErik Nordmark 	ipsecah_stack_t	*ahstack = ns->netstack_ipsecah;
3878bd670b35SErik Nordmark 	ipsec_stack_t	*ipss = ns->netstack_ipsec;
3879bd670b35SErik Nordmark 	ill_t		*ill = ixa->ixa_nce->nce_ill;
38807c478bd9Sstevel@tonic-gate 
3881bd670b35SErik Nordmark 	isv4 = (ixa->ixa_flags & IXAF_IS_IPV4);
3882bd670b35SErik Nordmark 	icv_len = ic->ic_crypto_mac.cd_raw.iov_len;
38837c478bd9Sstevel@tonic-gate 
38847c478bd9Sstevel@tonic-gate 	mp = phdr_mp->b_cont;
38857c478bd9Sstevel@tonic-gate 	if (mp == NULL) {
3886bd670b35SErik Nordmark 		ip_drop_packet(phdr_mp, B_FALSE, ill,
3887f4b3ec61Sdh155122 		    DROPPER(ipss, ipds_ah_nomem),
3888f4b3ec61Sdh155122 		    &ahstack->ah_dropper);
3889bd670b35SErik Nordmark 		BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
3890bd670b35SErik Nordmark 		return (NULL);
38917c478bd9Sstevel@tonic-gate 	}
3892bd670b35SErik Nordmark 	mp->b_rptr -= ic->ic_skip_len;
38937c478bd9Sstevel@tonic-gate 
3894bd670b35SErik Nordmark 	ASSERT(ixa->ixa_flags & IXAF_IPSEC_SECURE);
3895bd670b35SErik Nordmark 	ASSERT(ixa->ixa_ipsec_ah_sa != NULL);
3896bd670b35SErik Nordmark 	ah_set_usetime(ixa->ixa_ipsec_ah_sa, B_FALSE);
3897437220cdSdanmcd 
38987c478bd9Sstevel@tonic-gate 	if (isv4) {
38997c478bd9Sstevel@tonic-gate 		ipha_t *ipha;
39007c478bd9Sstevel@tonic-gate 		ipha_t *nipha;
39017c478bd9Sstevel@tonic-gate 
39027c478bd9Sstevel@tonic-gate 		ipha = (ipha_t *)mp->b_rptr;
39037c478bd9Sstevel@tonic-gate 		hdrs_length = ipha->ipha_version_and_hdr_length -
39047c478bd9Sstevel@tonic-gate 		    (uint8_t)((IP_VERSION << 4));
39057c478bd9Sstevel@tonic-gate 		hdrs_length <<= 2;
39067c478bd9Sstevel@tonic-gate 		align_len = P2ALIGN(icv_len + IPV4_PADDING_ALIGN - 1,
39077c478bd9Sstevel@tonic-gate 		    IPV4_PADDING_ALIGN);
39087c478bd9Sstevel@tonic-gate 		/*
39097c478bd9Sstevel@tonic-gate 		 * phdr_mp must have the right amount of space for the
39107c478bd9Sstevel@tonic-gate 		 * combined IP and AH header. Copy the IP header and
39117c478bd9Sstevel@tonic-gate 		 * the ack_data onto AH. Note that the AH header was
39127c478bd9Sstevel@tonic-gate 		 * already formed before the ICV calculation and hence
39137c478bd9Sstevel@tonic-gate 		 * you don't have to copy it here.
39147c478bd9Sstevel@tonic-gate 		 */
39157c478bd9Sstevel@tonic-gate 		bcopy(mp->b_rptr, phdr_mp->b_rptr, hdrs_length);
39167c478bd9Sstevel@tonic-gate 
39177c478bd9Sstevel@tonic-gate 		ptr = phdr_mp->b_rptr + hdrs_length + sizeof (ah_t);
39187c478bd9Sstevel@tonic-gate 		bcopy(phdr_mp->b_wptr, ptr, icv_len);
39197c478bd9Sstevel@tonic-gate 
39207c478bd9Sstevel@tonic-gate 		/*
39217c478bd9Sstevel@tonic-gate 		 * Compute the new header checksum as we are assigning
39227c478bd9Sstevel@tonic-gate 		 * IPPROTO_AH and adjusting the length here.
39237c478bd9Sstevel@tonic-gate 		 */
39247c478bd9Sstevel@tonic-gate 		nipha = (ipha_t *)phdr_mp->b_rptr;
39257c478bd9Sstevel@tonic-gate 
39267c478bd9Sstevel@tonic-gate 		nipha->ipha_protocol = IPPROTO_AH;
39277c478bd9Sstevel@tonic-gate 		length = ntohs(nipha->ipha_length);
39287c478bd9Sstevel@tonic-gate 		length += (sizeof (ah_t) + align_len);
39297c478bd9Sstevel@tonic-gate 		nipha->ipha_length = htons((uint16_t)length);
39307c478bd9Sstevel@tonic-gate 		nipha->ipha_hdr_checksum = 0;
39317c478bd9Sstevel@tonic-gate 		nipha->ipha_hdr_checksum = (uint16_t)ip_csum_hdr(nipha);
39327c478bd9Sstevel@tonic-gate 	} else {
39337c478bd9Sstevel@tonic-gate 		ip6_t *ip6h;
39347c478bd9Sstevel@tonic-gate 		ip6_t *nip6h;
39357c478bd9Sstevel@tonic-gate 		uint_t ah_offset;
39367c478bd9Sstevel@tonic-gate 
39377c478bd9Sstevel@tonic-gate 		ip6h = (ip6_t *)mp->b_rptr;
39387c478bd9Sstevel@tonic-gate 		nip6h = (ip6_t *)phdr_mp->b_rptr;
39397c478bd9Sstevel@tonic-gate 		align_len = P2ALIGN(icv_len + IPV6_PADDING_ALIGN - 1,
39407c478bd9Sstevel@tonic-gate 		    IPV6_PADDING_ALIGN);
39417c478bd9Sstevel@tonic-gate 		/*
39427c478bd9Sstevel@tonic-gate 		 * phdr_mp must have the right amount of space for the
39437c478bd9Sstevel@tonic-gate 		 * combined IP and AH header. Copy the IP header with
39447c478bd9Sstevel@tonic-gate 		 * options into the pseudo header. When we constructed
39457c478bd9Sstevel@tonic-gate 		 * a pseudo header, we did not copy some of the mutable
39467c478bd9Sstevel@tonic-gate 		 * fields. We do it now by calling ah_fix_phdr_v6()
39477c478bd9Sstevel@tonic-gate 		 * with the last argument B_TRUE. It returns the
39487c478bd9Sstevel@tonic-gate 		 * ah_offset into the pseudo header.
39497c478bd9Sstevel@tonic-gate 		 */
39507c478bd9Sstevel@tonic-gate 
39517c478bd9Sstevel@tonic-gate 		bcopy(ip6h, nip6h, IPV6_HDR_LEN);
39527c478bd9Sstevel@tonic-gate 		ah_offset = ah_fix_phdr_v6(nip6h, ip6h, B_TRUE, B_TRUE);
39537c478bd9Sstevel@tonic-gate 		ASSERT(ah_offset != 0);
39547c478bd9Sstevel@tonic-gate 		/*
39557c478bd9Sstevel@tonic-gate 		 * phdr_mp can hold exactly the whole IP header with options
39567c478bd9Sstevel@tonic-gate 		 * plus the AH header also. Thus subtracting the AH header's
39577c478bd9Sstevel@tonic-gate 		 * size should give exactly how much of the original header
39587c478bd9Sstevel@tonic-gate 		 * should be skipped.
39597c478bd9Sstevel@tonic-gate 		 */
39607c478bd9Sstevel@tonic-gate 		hdrs_length = (phdr_mp->b_wptr - phdr_mp->b_rptr) -
39617c478bd9Sstevel@tonic-gate 		    sizeof (ah_t) - icv_len;
39627c478bd9Sstevel@tonic-gate 		bcopy(phdr_mp->b_wptr, ((uint8_t *)nip6h + ah_offset +
39637c478bd9Sstevel@tonic-gate 		    sizeof (ah_t)), icv_len);
39647c478bd9Sstevel@tonic-gate 		length = ntohs(nip6h->ip6_plen);
39657c478bd9Sstevel@tonic-gate 		length += (sizeof (ah_t) + align_len);
39667c478bd9Sstevel@tonic-gate 		nip6h->ip6_plen = htons((uint16_t)length);
39677c478bd9Sstevel@tonic-gate 	}
39687c478bd9Sstevel@tonic-gate 
39697c478bd9Sstevel@tonic-gate 	/* Skip the original IP header */
39707c478bd9Sstevel@tonic-gate 	mp->b_rptr += hdrs_length;
39717c478bd9Sstevel@tonic-gate 	if (mp->b_rptr == mp->b_wptr) {
39727c478bd9Sstevel@tonic-gate 		phdr_mp->b_cont = mp->b_cont;
39737c478bd9Sstevel@tonic-gate 		freeb(mp);
39747c478bd9Sstevel@tonic-gate 	}
39757c478bd9Sstevel@tonic-gate 
3976bd670b35SErik Nordmark 	return (phdr_mp);
39777c478bd9Sstevel@tonic-gate }
39787c478bd9Sstevel@tonic-gate 
39795d3b8cb7SBill Sommerfeld /* Refactor me */
39807c478bd9Sstevel@tonic-gate /*
39817c478bd9Sstevel@tonic-gate  * Wrapper to allow IP to trigger an AH association failure message
39827c478bd9Sstevel@tonic-gate  * during SA inbound selection.
39837c478bd9Sstevel@tonic-gate  */
39847c478bd9Sstevel@tonic-gate void
39857c478bd9Sstevel@tonic-gate ipsecah_in_assocfailure(mblk_t *mp, char level, ushort_t sl, char *fmt,
3986bd670b35SErik Nordmark     uint32_t spi, void *addr, int af, ip_recv_attr_t *ira)
39877c478bd9Sstevel@tonic-gate {
3988bd670b35SErik Nordmark 	netstack_t	*ns = ira->ira_ill->ill_ipst->ips_netstack;
3989bd670b35SErik Nordmark 	ipsecah_stack_t	*ahstack = ns->netstack_ipsecah;
3990bd670b35SErik Nordmark 	ipsec_stack_t	*ipss = ns->netstack_ipsec;
3991f4b3ec61Sdh155122 
3992f4b3ec61Sdh155122 	if (ahstack->ipsecah_log_unknown_spi) {
39937c478bd9Sstevel@tonic-gate 		ipsec_assocfailure(info.mi_idnum, 0, level, sl, fmt, spi,
3994f4b3ec61Sdh155122 		    addr, af, ahstack->ipsecah_netstack);
39957c478bd9Sstevel@tonic-gate 	}
39967c478bd9Sstevel@tonic-gate 
3997bd670b35SErik Nordmark 	ip_drop_packet(mp, B_TRUE, ira->ira_ill,
3998f4b3ec61Sdh155122 	    DROPPER(ipss, ipds_ah_no_sa),
3999f4b3ec61Sdh155122 	    &ahstack->ah_dropper);
40007c478bd9Sstevel@tonic-gate }
40017c478bd9Sstevel@tonic-gate 
40027c478bd9Sstevel@tonic-gate /*
40037c478bd9Sstevel@tonic-gate  * Initialize the AH input and output processing functions.
40047c478bd9Sstevel@tonic-gate  */
40057c478bd9Sstevel@tonic-gate void
40067c478bd9Sstevel@tonic-gate ipsecah_init_funcs(ipsa_t *sa)
40077c478bd9Sstevel@tonic-gate {
40087c478bd9Sstevel@tonic-gate 	if (sa->ipsa_output_func == NULL)
40097c478bd9Sstevel@tonic-gate 		sa->ipsa_output_func = ah_outbound;
40107c478bd9Sstevel@tonic-gate 	if (sa->ipsa_input_func == NULL)
40117c478bd9Sstevel@tonic-gate 		sa->ipsa_input_func = ah_inbound;
40127c478bd9Sstevel@tonic-gate }
4013