xref: /onnv-gate/usr/src/uts/common/inet/ip/ipsecah.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate #include <sys/types.h>
30*0Sstevel@tonic-gate #include <sys/stream.h>
31*0Sstevel@tonic-gate #include <sys/stropts.h>
32*0Sstevel@tonic-gate #include <sys/errno.h>
33*0Sstevel@tonic-gate #include <sys/strlog.h>
34*0Sstevel@tonic-gate #include <sys/tihdr.h>
35*0Sstevel@tonic-gate #include <sys/socket.h>
36*0Sstevel@tonic-gate #include <sys/ddi.h>
37*0Sstevel@tonic-gate #include <sys/sunddi.h>
38*0Sstevel@tonic-gate #include <sys/kmem.h>
39*0Sstevel@tonic-gate #include <sys/cmn_err.h>
40*0Sstevel@tonic-gate #include <sys/vtrace.h>
41*0Sstevel@tonic-gate #include <sys/debug.h>
42*0Sstevel@tonic-gate #include <sys/atomic.h>
43*0Sstevel@tonic-gate #include <sys/strsun.h>
44*0Sstevel@tonic-gate #include <sys/random.h>
45*0Sstevel@tonic-gate #include <netinet/in.h>
46*0Sstevel@tonic-gate #include <net/if.h>
47*0Sstevel@tonic-gate #include <netinet/ip6.h>
48*0Sstevel@tonic-gate #include <netinet/icmp6.h>
49*0Sstevel@tonic-gate #include <net/pfkeyv2.h>
50*0Sstevel@tonic-gate 
51*0Sstevel@tonic-gate #include <inet/common.h>
52*0Sstevel@tonic-gate #include <inet/mi.h>
53*0Sstevel@tonic-gate #include <inet/ip.h>
54*0Sstevel@tonic-gate #include <inet/ip6.h>
55*0Sstevel@tonic-gate #include <inet/nd.h>
56*0Sstevel@tonic-gate #include <inet/ipsec_info.h>
57*0Sstevel@tonic-gate #include <inet/ipsec_impl.h>
58*0Sstevel@tonic-gate #include <inet/sadb.h>
59*0Sstevel@tonic-gate #include <inet/ipsecah.h>
60*0Sstevel@tonic-gate #include <inet/ipsec_impl.h>
61*0Sstevel@tonic-gate #include <inet/ipdrop.h>
62*0Sstevel@tonic-gate #include <sys/taskq.h>
63*0Sstevel@tonic-gate #include <sys/policy.h>
64*0Sstevel@tonic-gate /* EXPORT DELETE START */
65*0Sstevel@tonic-gate #include <sys/iphada.h>
66*0Sstevel@tonic-gate /* EXPORT DELETE END */
67*0Sstevel@tonic-gate #include <sys/strsun.h>
68*0Sstevel@tonic-gate 
69*0Sstevel@tonic-gate #include <sys/crypto/common.h>
70*0Sstevel@tonic-gate #include <sys/crypto/api.h>
71*0Sstevel@tonic-gate #include <sys/kstat.h>
72*0Sstevel@tonic-gate 
73*0Sstevel@tonic-gate /* Packet dropper for AH drops. */
74*0Sstevel@tonic-gate static ipdropper_t ah_dropper;
75*0Sstevel@tonic-gate 
76*0Sstevel@tonic-gate static kmutex_t ipsecah_param_lock;	/* Protect ipsecah_param_arr[] below. */
77*0Sstevel@tonic-gate /*
78*0Sstevel@tonic-gate  * Table of ND variables supported by ipsecah. These are loaded into
79*0Sstevel@tonic-gate  * ipsecah_g_nd in ipsecah_init_nd.
80*0Sstevel@tonic-gate  * All of these are alterable, within the min/max values given, at run time.
81*0Sstevel@tonic-gate  */
82*0Sstevel@tonic-gate static	ipsecahparam_t	ipsecah_param_arr[] = {
83*0Sstevel@tonic-gate 	/* min	max			value	name */
84*0Sstevel@tonic-gate 	{ 0,	3,			0,	"ipsecah_debug"},
85*0Sstevel@tonic-gate 	{ 125,	32000, SADB_AGE_INTERVAL_DEFAULT,	"ipsecah_age_interval"},
86*0Sstevel@tonic-gate 	{ 1,	10,			1,	"ipsecah_reap_delay"},
87*0Sstevel@tonic-gate 	{ 1,	SADB_MAX_REPLAY,	64,	"ipsecah_replay_size"},
88*0Sstevel@tonic-gate 	{ 1,	300,			15,	"ipsecah_acquire_timeout"},
89*0Sstevel@tonic-gate 	{ 1,	1800,			90,	"ipsecah_larval_timeout"},
90*0Sstevel@tonic-gate 	/* Default lifetime values for ACQUIRE messages. */
91*0Sstevel@tonic-gate 	{ 0,	0xffffffffU,		0,	"ipsecah_default_soft_bytes"},
92*0Sstevel@tonic-gate 	{ 0,	0xffffffffU,		0,	"ipsecah_default_hard_bytes"},
93*0Sstevel@tonic-gate 	{ 0,	0xffffffffU,		24000,	"ipsecah_default_soft_addtime"},
94*0Sstevel@tonic-gate 	{ 0,	0xffffffffU,		28800,	"ipsecah_default_hard_addtime"},
95*0Sstevel@tonic-gate 	{ 0,	0xffffffffU,		0,	"ipsecah_default_soft_usetime"},
96*0Sstevel@tonic-gate 	{ 0,	0xffffffffU,		0,	"ipsecah_default_hard_usetime"},
97*0Sstevel@tonic-gate 	{ 0,	1,			0,	"ipsecah_log_unknown_spi"},
98*0Sstevel@tonic-gate };
99*0Sstevel@tonic-gate #define	ipsecah_debug		ipsecah_param_arr[0].ipsecah_param_value
100*0Sstevel@tonic-gate #define	ipsecah_age_interval	ipsecah_param_arr[1].ipsecah_param_value
101*0Sstevel@tonic-gate #define	ipsecah_age_int_max	ipsecah_param_arr[1].ipsecah_param_max
102*0Sstevel@tonic-gate #define	ipsecah_reap_delay	ipsecah_param_arr[2].ipsecah_param_value
103*0Sstevel@tonic-gate #define	ipsecah_replay_size	ipsecah_param_arr[3].ipsecah_param_value
104*0Sstevel@tonic-gate #define	ipsecah_acquire_timeout	ipsecah_param_arr[4].ipsecah_param_value
105*0Sstevel@tonic-gate #define	ipsecah_larval_timeout	ipsecah_param_arr[5].ipsecah_param_value
106*0Sstevel@tonic-gate #define	ipsecah_default_soft_bytes   ipsecah_param_arr[6].ipsecah_param_value
107*0Sstevel@tonic-gate #define	ipsecah_default_hard_bytes   ipsecah_param_arr[7].ipsecah_param_value
108*0Sstevel@tonic-gate #define	ipsecah_default_soft_addtime ipsecah_param_arr[8].ipsecah_param_value
109*0Sstevel@tonic-gate #define	ipsecah_default_hard_addtime ipsecah_param_arr[9].ipsecah_param_value
110*0Sstevel@tonic-gate #define	ipsecah_default_soft_usetime ipsecah_param_arr[10].ipsecah_param_value
111*0Sstevel@tonic-gate #define	ipsecah_default_hard_usetime ipsecah_param_arr[11].ipsecah_param_value
112*0Sstevel@tonic-gate #define	ipsecah_log_unknown_spi ipsecah_param_arr[12].ipsecah_param_value
113*0Sstevel@tonic-gate 
114*0Sstevel@tonic-gate #define	ah0dbg(a)	printf a
115*0Sstevel@tonic-gate /* NOTE:  != 0 instead of > 0 so lint doesn't complain. */
116*0Sstevel@tonic-gate #define	ah1dbg(a)	if (ipsecah_debug != 0) printf a
117*0Sstevel@tonic-gate #define	ah2dbg(a)	if (ipsecah_debug > 1) printf a
118*0Sstevel@tonic-gate #define	ah3dbg(a)	if (ipsecah_debug > 2) printf a
119*0Sstevel@tonic-gate 
120*0Sstevel@tonic-gate static IDP ipsecah_g_nd;
121*0Sstevel@tonic-gate 
122*0Sstevel@tonic-gate /*
123*0Sstevel@tonic-gate  * XXX This is broken. Padding should be determined dynamically
124*0Sstevel@tonic-gate  * depending on the ICV size and IP version number so that the
125*0Sstevel@tonic-gate  * total AH header size is a multiple of 32 bits or 64 bits
126*0Sstevel@tonic-gate  * for V4 and V6 respectively. For 96bit ICVs we have no problems.
127*0Sstevel@tonic-gate  * Anything different from that, we need to fix our code.
128*0Sstevel@tonic-gate  */
129*0Sstevel@tonic-gate #define	IPV4_PADDING_ALIGN	0x04	/* Multiple of 32 bits */
130*0Sstevel@tonic-gate #define	IPV6_PADDING_ALIGN	0x04	/* Multiple of 32 bits */
131*0Sstevel@tonic-gate 
132*0Sstevel@tonic-gate /*
133*0Sstevel@tonic-gate  * Helper macro. Avoids a call to msgdsize if there is only one
134*0Sstevel@tonic-gate  * mblk in the chain.
135*0Sstevel@tonic-gate  */
136*0Sstevel@tonic-gate #define	AH_MSGSIZE(mp) ((mp)->b_cont != NULL ? msgdsize(mp) : MBLKL(mp))
137*0Sstevel@tonic-gate 
138*0Sstevel@tonic-gate static ipsec_status_t ah_auth_out_done(mblk_t *);
139*0Sstevel@tonic-gate static ipsec_status_t ah_auth_in_done(mblk_t *);
140*0Sstevel@tonic-gate static mblk_t *ah_process_ip_options_v4(mblk_t *, ipsa_t *, int *, uint_t,
141*0Sstevel@tonic-gate     boolean_t);
142*0Sstevel@tonic-gate static mblk_t *ah_process_ip_options_v6(mblk_t *, ipsa_t *, int *, uint_t,
143*0Sstevel@tonic-gate     boolean_t);
144*0Sstevel@tonic-gate static void ah_getspi(mblk_t *, keysock_in_t *);
145*0Sstevel@tonic-gate static ipsec_status_t ah_inbound_accelerated(mblk_t *, boolean_t, ipsa_t *,
146*0Sstevel@tonic-gate     uint32_t);
147*0Sstevel@tonic-gate static ipsec_status_t ah_outbound_accelerated_v4(mblk_t *, ipsa_t *);
148*0Sstevel@tonic-gate static ipsec_status_t ah_outbound_accelerated_v6(mblk_t *, ipsa_t *);
149*0Sstevel@tonic-gate static ipsec_status_t ah_outbound(mblk_t *);
150*0Sstevel@tonic-gate 
151*0Sstevel@tonic-gate static int ipsecah_open(queue_t *, dev_t *, int, int, cred_t *);
152*0Sstevel@tonic-gate static int ipsecah_close(queue_t *);
153*0Sstevel@tonic-gate static void ipsecah_rput(queue_t *, mblk_t *);
154*0Sstevel@tonic-gate static void ipsecah_wput(queue_t *, mblk_t *);
155*0Sstevel@tonic-gate static void ah_send_acquire(ipsacq_t *, mblk_t *);
156*0Sstevel@tonic-gate static boolean_t ah_register_out(uint32_t, uint32_t, uint_t);
157*0Sstevel@tonic-gate 
158*0Sstevel@tonic-gate static struct module_info info = {
159*0Sstevel@tonic-gate 	5136, "ipsecah", 0, INFPSZ, 65536, 1024
160*0Sstevel@tonic-gate };
161*0Sstevel@tonic-gate 
162*0Sstevel@tonic-gate static struct qinit rinit = {
163*0Sstevel@tonic-gate 	(pfi_t)ipsecah_rput, NULL, ipsecah_open, ipsecah_close, NULL, &info,
164*0Sstevel@tonic-gate 	NULL
165*0Sstevel@tonic-gate };
166*0Sstevel@tonic-gate 
167*0Sstevel@tonic-gate static struct qinit winit = {
168*0Sstevel@tonic-gate 	(pfi_t)ipsecah_wput, NULL, ipsecah_open, ipsecah_close, NULL, &info,
169*0Sstevel@tonic-gate 	NULL
170*0Sstevel@tonic-gate };
171*0Sstevel@tonic-gate 
172*0Sstevel@tonic-gate struct streamtab ipsecahinfo = {
173*0Sstevel@tonic-gate 	&rinit, &winit, NULL, NULL
174*0Sstevel@tonic-gate };
175*0Sstevel@tonic-gate 
176*0Sstevel@tonic-gate /*
177*0Sstevel@tonic-gate  * Keysock instance of AH.  "There can be only one." :)
178*0Sstevel@tonic-gate  * Use casptr() on this because I don't set it until KEYSOCK_HELLO comes down.
179*0Sstevel@tonic-gate  * Paired up with the ah_pfkey_q is the ah_event, which will age SAs.
180*0Sstevel@tonic-gate  */
181*0Sstevel@tonic-gate static queue_t *ah_pfkey_q;
182*0Sstevel@tonic-gate static timeout_id_t ah_event;
183*0Sstevel@tonic-gate static taskq_t *ah_taskq;
184*0Sstevel@tonic-gate 
185*0Sstevel@tonic-gate static mblk_t *ah_ip_unbind;
186*0Sstevel@tonic-gate 
187*0Sstevel@tonic-gate /*
188*0Sstevel@tonic-gate  * Stats.  This may eventually become a full-blown SNMP MIB once that spec
189*0Sstevel@tonic-gate  * stabilizes.
190*0Sstevel@tonic-gate  */
191*0Sstevel@tonic-gate typedef struct
192*0Sstevel@tonic-gate {
193*0Sstevel@tonic-gate 	kstat_named_t ah_stat_num_aalgs;
194*0Sstevel@tonic-gate 	kstat_named_t ah_stat_good_auth;
195*0Sstevel@tonic-gate 	kstat_named_t ah_stat_bad_auth;
196*0Sstevel@tonic-gate 	kstat_named_t ah_stat_replay_failures;
197*0Sstevel@tonic-gate 	kstat_named_t ah_stat_replay_early_failures;
198*0Sstevel@tonic-gate 	kstat_named_t ah_stat_keysock_in;
199*0Sstevel@tonic-gate 	kstat_named_t ah_stat_out_requests;
200*0Sstevel@tonic-gate 	kstat_named_t ah_stat_acquire_requests;
201*0Sstevel@tonic-gate 	kstat_named_t ah_stat_bytes_expired;
202*0Sstevel@tonic-gate 	kstat_named_t ah_stat_out_discards;
203*0Sstevel@tonic-gate 	kstat_named_t ah_stat_in_accelerated;
204*0Sstevel@tonic-gate 	kstat_named_t ah_stat_out_accelerated;
205*0Sstevel@tonic-gate 	kstat_named_t ah_stat_noaccel;
206*0Sstevel@tonic-gate 	kstat_named_t ah_stat_crypto_sync;
207*0Sstevel@tonic-gate 	kstat_named_t ah_stat_crypto_async;
208*0Sstevel@tonic-gate 	kstat_named_t ah_stat_crypto_failures;
209*0Sstevel@tonic-gate } ah_kstats_t;
210*0Sstevel@tonic-gate 
211*0Sstevel@tonic-gate #define	AH_BUMP_STAT(x) (ah_kstats->ah_stat_ ## x).value.ui64++
212*0Sstevel@tonic-gate #define	AH_DEBUMP_STAT(x) (ah_kstats->ah_stat_ ## x).value.ui64--
213*0Sstevel@tonic-gate 
214*0Sstevel@tonic-gate static kstat_t *ah_ksp;
215*0Sstevel@tonic-gate static ah_kstats_t *ah_kstats;
216*0Sstevel@tonic-gate 
217*0Sstevel@tonic-gate static int ah_kstat_update(kstat_t *, int);
218*0Sstevel@tonic-gate 
219*0Sstevel@tonic-gate static boolean_t
220*0Sstevel@tonic-gate ah_kstat_init(void)
221*0Sstevel@tonic-gate {
222*0Sstevel@tonic-gate 
223*0Sstevel@tonic-gate 	ah_ksp = kstat_create("ipsecah", 0, "ah_stat", "net",
224*0Sstevel@tonic-gate 	    KSTAT_TYPE_NAMED, sizeof (*ah_kstats) / sizeof (kstat_named_t),
225*0Sstevel@tonic-gate 	    KSTAT_FLAG_PERSISTENT);
226*0Sstevel@tonic-gate 
227*0Sstevel@tonic-gate 	if (ah_ksp == NULL)
228*0Sstevel@tonic-gate 		return (B_FALSE);
229*0Sstevel@tonic-gate 
230*0Sstevel@tonic-gate 	ah_kstats = ah_ksp->ks_data;
231*0Sstevel@tonic-gate 
232*0Sstevel@tonic-gate 	ah_ksp->ks_update = ah_kstat_update;
233*0Sstevel@tonic-gate 
234*0Sstevel@tonic-gate #define	K64 KSTAT_DATA_UINT64
235*0Sstevel@tonic-gate #define	KI(x) kstat_named_init(&(ah_kstats->ah_stat_##x), #x, K64)
236*0Sstevel@tonic-gate 
237*0Sstevel@tonic-gate 	KI(num_aalgs);
238*0Sstevel@tonic-gate 	KI(good_auth);
239*0Sstevel@tonic-gate 	KI(bad_auth);
240*0Sstevel@tonic-gate 	KI(replay_failures);
241*0Sstevel@tonic-gate 	KI(replay_early_failures);
242*0Sstevel@tonic-gate 	KI(keysock_in);
243*0Sstevel@tonic-gate 	KI(out_requests);
244*0Sstevel@tonic-gate 	KI(acquire_requests);
245*0Sstevel@tonic-gate 	KI(bytes_expired);
246*0Sstevel@tonic-gate 	KI(out_discards);
247*0Sstevel@tonic-gate 	KI(in_accelerated);
248*0Sstevel@tonic-gate 	KI(out_accelerated);
249*0Sstevel@tonic-gate 	KI(noaccel);
250*0Sstevel@tonic-gate 	KI(crypto_sync);
251*0Sstevel@tonic-gate 	KI(crypto_async);
252*0Sstevel@tonic-gate 	KI(crypto_failures);
253*0Sstevel@tonic-gate 
254*0Sstevel@tonic-gate #undef KI
255*0Sstevel@tonic-gate #undef K64
256*0Sstevel@tonic-gate 
257*0Sstevel@tonic-gate 	kstat_install(ah_ksp);
258*0Sstevel@tonic-gate 
259*0Sstevel@tonic-gate 	return (B_TRUE);
260*0Sstevel@tonic-gate }
261*0Sstevel@tonic-gate 
262*0Sstevel@tonic-gate static int
263*0Sstevel@tonic-gate ah_kstat_update(kstat_t *kp, int rw)
264*0Sstevel@tonic-gate {
265*0Sstevel@tonic-gate 	ah_kstats_t *ekp;
266*0Sstevel@tonic-gate 
267*0Sstevel@tonic-gate 	if ((kp == NULL) || (kp->ks_data == NULL))
268*0Sstevel@tonic-gate 		return (EIO);
269*0Sstevel@tonic-gate 
270*0Sstevel@tonic-gate 	if (rw == KSTAT_WRITE)
271*0Sstevel@tonic-gate 		return (EACCES);
272*0Sstevel@tonic-gate 
273*0Sstevel@tonic-gate 	ASSERT(kp == ah_ksp);
274*0Sstevel@tonic-gate 	ekp = (ah_kstats_t *)kp->ks_data;
275*0Sstevel@tonic-gate 	ASSERT(ekp == ah_kstats);
276*0Sstevel@tonic-gate 
277*0Sstevel@tonic-gate 	mutex_enter(&alg_lock);
278*0Sstevel@tonic-gate 	ekp->ah_stat_num_aalgs.value.ui64 = ipsec_nalgs[IPSEC_ALG_AUTH];
279*0Sstevel@tonic-gate 	mutex_exit(&alg_lock);
280*0Sstevel@tonic-gate 
281*0Sstevel@tonic-gate 	return (0);
282*0Sstevel@tonic-gate }
283*0Sstevel@tonic-gate 
284*0Sstevel@tonic-gate /*
285*0Sstevel@tonic-gate  * Don't have to lock ipsec_age_interval, as only one thread will access it at
286*0Sstevel@tonic-gate  * a time, because I control the one function that does a qtimeout() on
287*0Sstevel@tonic-gate  * ah_pfkey_q.
288*0Sstevel@tonic-gate  */
289*0Sstevel@tonic-gate /* ARGSUSED */
290*0Sstevel@tonic-gate static void
291*0Sstevel@tonic-gate ah_ager(void *ignoreme)
292*0Sstevel@tonic-gate {
293*0Sstevel@tonic-gate 	hrtime_t begin = gethrtime();
294*0Sstevel@tonic-gate 
295*0Sstevel@tonic-gate 	sadb_ager(&ah_sadb.s_v4, ah_pfkey_q, ah_sadb.s_ip_q,
296*0Sstevel@tonic-gate 	    ipsecah_reap_delay);
297*0Sstevel@tonic-gate 	sadb_ager(&ah_sadb.s_v6, ah_pfkey_q, ah_sadb.s_ip_q,
298*0Sstevel@tonic-gate 	    ipsecah_reap_delay);
299*0Sstevel@tonic-gate 
300*0Sstevel@tonic-gate 	ah_event = sadb_retimeout(begin, ah_pfkey_q, ah_ager,
301*0Sstevel@tonic-gate 	    &ipsecah_age_interval, ipsecah_age_int_max, info.mi_idnum);
302*0Sstevel@tonic-gate }
303*0Sstevel@tonic-gate 
304*0Sstevel@tonic-gate /*
305*0Sstevel@tonic-gate  * Get an AH NDD parameter.
306*0Sstevel@tonic-gate  */
307*0Sstevel@tonic-gate /* ARGSUSED */
308*0Sstevel@tonic-gate static int
309*0Sstevel@tonic-gate ipsecah_param_get(q, mp, cp, cr)
310*0Sstevel@tonic-gate 	queue_t	*q;
311*0Sstevel@tonic-gate 	mblk_t	*mp;
312*0Sstevel@tonic-gate 	caddr_t	cp;
313*0Sstevel@tonic-gate 	cred_t *cr;
314*0Sstevel@tonic-gate {
315*0Sstevel@tonic-gate 	ipsecahparam_t	*ipsecahpa = (ipsecahparam_t *)cp;
316*0Sstevel@tonic-gate 	uint_t value;
317*0Sstevel@tonic-gate 
318*0Sstevel@tonic-gate 	mutex_enter(&ipsecah_param_lock);
319*0Sstevel@tonic-gate 	value = ipsecahpa->ipsecah_param_value;
320*0Sstevel@tonic-gate 	mutex_exit(&ipsecah_param_lock);
321*0Sstevel@tonic-gate 
322*0Sstevel@tonic-gate 	(void) mi_mpprintf(mp, "%u", value);
323*0Sstevel@tonic-gate 	return (0);
324*0Sstevel@tonic-gate }
325*0Sstevel@tonic-gate 
326*0Sstevel@tonic-gate /*
327*0Sstevel@tonic-gate  * This routine sets an NDD variable in a ipsecahparam_t structure.
328*0Sstevel@tonic-gate  */
329*0Sstevel@tonic-gate /* ARGSUSED */
330*0Sstevel@tonic-gate static int
331*0Sstevel@tonic-gate ipsecah_param_set(q, mp, value, cp, cr)
332*0Sstevel@tonic-gate 	queue_t	*q;
333*0Sstevel@tonic-gate 	mblk_t	*mp;
334*0Sstevel@tonic-gate 	char	*value;
335*0Sstevel@tonic-gate 	caddr_t	cp;
336*0Sstevel@tonic-gate 	cred_t *cr;
337*0Sstevel@tonic-gate {
338*0Sstevel@tonic-gate 	ulong_t	new_value;
339*0Sstevel@tonic-gate 	ipsecahparam_t	*ipsecahpa = (ipsecahparam_t *)cp;
340*0Sstevel@tonic-gate 
341*0Sstevel@tonic-gate 	/*
342*0Sstevel@tonic-gate 	 * Fail the request if the new value does not lie within the
343*0Sstevel@tonic-gate 	 * required bounds.
344*0Sstevel@tonic-gate 	 */
345*0Sstevel@tonic-gate 	if (ddi_strtoul(value, NULL, 10, &new_value) != 0 ||
346*0Sstevel@tonic-gate 	    new_value < ipsecahpa->ipsecah_param_min ||
347*0Sstevel@tonic-gate 	    new_value > ipsecahpa->ipsecah_param_max) {
348*0Sstevel@tonic-gate 		    return (EINVAL);
349*0Sstevel@tonic-gate 	}
350*0Sstevel@tonic-gate 
351*0Sstevel@tonic-gate 	/* Set the new value */
352*0Sstevel@tonic-gate 	mutex_enter(&ipsecah_param_lock);
353*0Sstevel@tonic-gate 	ipsecahpa->ipsecah_param_value = new_value;
354*0Sstevel@tonic-gate 	mutex_exit(&ipsecah_param_lock);
355*0Sstevel@tonic-gate 	return (0);
356*0Sstevel@tonic-gate }
357*0Sstevel@tonic-gate 
358*0Sstevel@tonic-gate /*
359*0Sstevel@tonic-gate  * Using lifetime NDD variables, fill in an extended combination's
360*0Sstevel@tonic-gate  * lifetime information.
361*0Sstevel@tonic-gate  */
362*0Sstevel@tonic-gate void
363*0Sstevel@tonic-gate ipsecah_fill_defs(sadb_x_ecomb_t *ecomb)
364*0Sstevel@tonic-gate {
365*0Sstevel@tonic-gate 	ecomb->sadb_x_ecomb_soft_bytes = ipsecah_default_soft_bytes;
366*0Sstevel@tonic-gate 	ecomb->sadb_x_ecomb_hard_bytes = ipsecah_default_hard_bytes;
367*0Sstevel@tonic-gate 	ecomb->sadb_x_ecomb_soft_addtime = ipsecah_default_soft_addtime;
368*0Sstevel@tonic-gate 	ecomb->sadb_x_ecomb_hard_addtime = ipsecah_default_hard_addtime;
369*0Sstevel@tonic-gate 	ecomb->sadb_x_ecomb_soft_usetime = ipsecah_default_soft_usetime;
370*0Sstevel@tonic-gate 	ecomb->sadb_x_ecomb_hard_usetime = ipsecah_default_hard_usetime;
371*0Sstevel@tonic-gate }
372*0Sstevel@tonic-gate 
373*0Sstevel@tonic-gate /*
374*0Sstevel@tonic-gate  * Initialize things for AH at module load time.
375*0Sstevel@tonic-gate  */
376*0Sstevel@tonic-gate boolean_t
377*0Sstevel@tonic-gate ipsecah_ddi_init(void)
378*0Sstevel@tonic-gate {
379*0Sstevel@tonic-gate 	int count;
380*0Sstevel@tonic-gate 	ipsecahparam_t *ahp = ipsecah_param_arr;
381*0Sstevel@tonic-gate 
382*0Sstevel@tonic-gate 	for (count = A_CNT(ipsecah_param_arr); count-- > 0; ahp++) {
383*0Sstevel@tonic-gate 		if (ahp->ipsecah_param_name != NULL &&
384*0Sstevel@tonic-gate 		    ahp->ipsecah_param_name[0]) {
385*0Sstevel@tonic-gate 			if (!nd_load(&ipsecah_g_nd, ahp->ipsecah_param_name,
386*0Sstevel@tonic-gate 			    ipsecah_param_get, ipsecah_param_set,
387*0Sstevel@tonic-gate 			    (caddr_t)ahp)) {
388*0Sstevel@tonic-gate 				nd_free(&ipsecah_g_nd);
389*0Sstevel@tonic-gate 				return (B_FALSE);
390*0Sstevel@tonic-gate 			}
391*0Sstevel@tonic-gate 		}
392*0Sstevel@tonic-gate 	}
393*0Sstevel@tonic-gate 
394*0Sstevel@tonic-gate 	if (!ah_kstat_init()) {
395*0Sstevel@tonic-gate 		nd_free(&ipsecah_g_nd);
396*0Sstevel@tonic-gate 		return (B_FALSE);
397*0Sstevel@tonic-gate 	}
398*0Sstevel@tonic-gate 
399*0Sstevel@tonic-gate 	ah_taskq = taskq_create("ah_taskq", 1, minclsyspri,
400*0Sstevel@tonic-gate 	    IPSEC_TASKQ_MIN, IPSEC_TASKQ_MAX, 0);
401*0Sstevel@tonic-gate 
402*0Sstevel@tonic-gate 	ah_sadb.s_acquire_timeout = &ipsecah_acquire_timeout;
403*0Sstevel@tonic-gate 	ah_sadb.s_acqfn = ah_send_acquire;
404*0Sstevel@tonic-gate 	sadbp_init(&ah_sadb, SADB_SATYPE_AH);
405*0Sstevel@tonic-gate 
406*0Sstevel@tonic-gate 	mutex_init(&ipsecah_param_lock, NULL, MUTEX_DEFAULT, 0);
407*0Sstevel@tonic-gate 
408*0Sstevel@tonic-gate 	ip_drop_register(&ah_dropper, "IPsec AH");
409*0Sstevel@tonic-gate 
410*0Sstevel@tonic-gate 	return (B_TRUE);
411*0Sstevel@tonic-gate }
412*0Sstevel@tonic-gate 
413*0Sstevel@tonic-gate /*
414*0Sstevel@tonic-gate  * Destroy things for AH at module unload time.
415*0Sstevel@tonic-gate  */
416*0Sstevel@tonic-gate void
417*0Sstevel@tonic-gate ipsecah_ddi_destroy(void)
418*0Sstevel@tonic-gate {
419*0Sstevel@tonic-gate 	ah1dbg(("In ddi_destroy.\n"));
420*0Sstevel@tonic-gate 
421*0Sstevel@tonic-gate 	sadbp_destroy(&ah_sadb);
422*0Sstevel@tonic-gate 	ip_drop_unregister(&ah_dropper);
423*0Sstevel@tonic-gate 	taskq_destroy(ah_taskq);
424*0Sstevel@tonic-gate 	mutex_destroy(&ipsecah_param_lock);
425*0Sstevel@tonic-gate 	nd_free(&ipsecah_g_nd);
426*0Sstevel@tonic-gate 
427*0Sstevel@tonic-gate 	kstat_delete(ah_ksp);
428*0Sstevel@tonic-gate }
429*0Sstevel@tonic-gate 
430*0Sstevel@tonic-gate /*
431*0Sstevel@tonic-gate  * AH module open routine. The module should be opened by keysock.
432*0Sstevel@tonic-gate  */
433*0Sstevel@tonic-gate /* ARGSUSED */
434*0Sstevel@tonic-gate static int
435*0Sstevel@tonic-gate ipsecah_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp)
436*0Sstevel@tonic-gate {
437*0Sstevel@tonic-gate 	if (secpolicy_net_config(credp, B_FALSE) != 0) {
438*0Sstevel@tonic-gate 		ah1dbg(("Non-privileged user trying to open ipsecah.\n"));
439*0Sstevel@tonic-gate 		return (EPERM);
440*0Sstevel@tonic-gate 	}
441*0Sstevel@tonic-gate 
442*0Sstevel@tonic-gate 	if (q->q_ptr != NULL)
443*0Sstevel@tonic-gate 		return (0);  /* Re-open of an already open instance. */
444*0Sstevel@tonic-gate 
445*0Sstevel@tonic-gate 	if (sflag != MODOPEN)
446*0Sstevel@tonic-gate 		return (EINVAL);
447*0Sstevel@tonic-gate 
448*0Sstevel@tonic-gate 	/*
449*0Sstevel@tonic-gate 	 * ASSUMPTIONS (because I'm MT_OCEXCL):
450*0Sstevel@tonic-gate 	 *
451*0Sstevel@tonic-gate 	 *	* I'm being pushed on top of IP for all my opens (incl. #1).
452*0Sstevel@tonic-gate 	 *	* Only ipsecah_open() can write into ah_sadb.s_ip_q.
453*0Sstevel@tonic-gate 	 *	* Because of this, I can check lazily for ah_sadb.s_ip_q.
454*0Sstevel@tonic-gate 	 *
455*0Sstevel@tonic-gate 	 *  If these assumptions are wrong, I'm in BIG trouble...
456*0Sstevel@tonic-gate 	 */
457*0Sstevel@tonic-gate 
458*0Sstevel@tonic-gate 	q->q_ptr = q; /* just so I know I'm open */
459*0Sstevel@tonic-gate 
460*0Sstevel@tonic-gate 	if (ah_sadb.s_ip_q == NULL) {
461*0Sstevel@tonic-gate 		struct T_unbind_req *tur;
462*0Sstevel@tonic-gate 
463*0Sstevel@tonic-gate 		ah_sadb.s_ip_q = WR(q);
464*0Sstevel@tonic-gate 		/* Allocate an unbind... */
465*0Sstevel@tonic-gate 		ah_ip_unbind = allocb(sizeof (struct T_unbind_req), BPRI_HI);
466*0Sstevel@tonic-gate 
467*0Sstevel@tonic-gate 		/*
468*0Sstevel@tonic-gate 		 * Send down T_BIND_REQ to bind IPPROTO_AH.
469*0Sstevel@tonic-gate 		 * Handle the ACK here in AH.
470*0Sstevel@tonic-gate 		 */
471*0Sstevel@tonic-gate 		qprocson(q);
472*0Sstevel@tonic-gate 		if (ah_ip_unbind == NULL ||
473*0Sstevel@tonic-gate 		    !sadb_t_bind_req(ah_sadb.s_ip_q, IPPROTO_AH)) {
474*0Sstevel@tonic-gate 			if (ah_ip_unbind != NULL) {
475*0Sstevel@tonic-gate 				freeb(ah_ip_unbind);
476*0Sstevel@tonic-gate 				ah_ip_unbind = NULL;
477*0Sstevel@tonic-gate 			}
478*0Sstevel@tonic-gate 			q->q_ptr = NULL;
479*0Sstevel@tonic-gate 			qprocsoff(q);
480*0Sstevel@tonic-gate 			return (ENOMEM);
481*0Sstevel@tonic-gate 		}
482*0Sstevel@tonic-gate 
483*0Sstevel@tonic-gate 		ah_ip_unbind->b_datap->db_type = M_PROTO;
484*0Sstevel@tonic-gate 		tur = (struct T_unbind_req *)ah_ip_unbind->b_rptr;
485*0Sstevel@tonic-gate 		tur->PRIM_type = T_UNBIND_REQ;
486*0Sstevel@tonic-gate 	} else {
487*0Sstevel@tonic-gate 		qprocson(q);
488*0Sstevel@tonic-gate 	}
489*0Sstevel@tonic-gate 
490*0Sstevel@tonic-gate 	/*
491*0Sstevel@tonic-gate 	 * For now, there's not much I can do.  I'll be getting a message
492*0Sstevel@tonic-gate 	 * passed down to me from keysock (in my wput), and a T_BIND_ACK
493*0Sstevel@tonic-gate 	 * up from IP (in my rput).
494*0Sstevel@tonic-gate 	 */
495*0Sstevel@tonic-gate 
496*0Sstevel@tonic-gate 	return (0);
497*0Sstevel@tonic-gate }
498*0Sstevel@tonic-gate 
499*0Sstevel@tonic-gate /*
500*0Sstevel@tonic-gate  * AH module close routine.
501*0Sstevel@tonic-gate  */
502*0Sstevel@tonic-gate static int
503*0Sstevel@tonic-gate ipsecah_close(queue_t *q)
504*0Sstevel@tonic-gate {
505*0Sstevel@tonic-gate 	/*
506*0Sstevel@tonic-gate 	 * If ah_sadb.s_ip_q is attached to this instance, send a
507*0Sstevel@tonic-gate 	 * T_UNBIND_REQ to IP for the instance before doing
508*0Sstevel@tonic-gate 	 * a qprocsoff().
509*0Sstevel@tonic-gate 	 */
510*0Sstevel@tonic-gate 	if (WR(q) == ah_sadb.s_ip_q && ah_ip_unbind != NULL) {
511*0Sstevel@tonic-gate 		putnext(WR(q), ah_ip_unbind);
512*0Sstevel@tonic-gate 		ah_ip_unbind = NULL;
513*0Sstevel@tonic-gate 	}
514*0Sstevel@tonic-gate 
515*0Sstevel@tonic-gate 	/*
516*0Sstevel@tonic-gate 	 * Clean up q_ptr, if needed.
517*0Sstevel@tonic-gate 	 */
518*0Sstevel@tonic-gate 	qprocsoff(q);
519*0Sstevel@tonic-gate 
520*0Sstevel@tonic-gate 	/* Keysock queue check is safe, because of OCEXCL perimeter. */
521*0Sstevel@tonic-gate 
522*0Sstevel@tonic-gate 	if (q == ah_pfkey_q) {
523*0Sstevel@tonic-gate 		ah0dbg(("ipsecah_close:  Ummm... keysock is closing AH.\n"));
524*0Sstevel@tonic-gate 		ah_pfkey_q = NULL;
525*0Sstevel@tonic-gate 		/* Detach qtimeouts. */
526*0Sstevel@tonic-gate 		(void) quntimeout(q, ah_event);
527*0Sstevel@tonic-gate 	}
528*0Sstevel@tonic-gate 
529*0Sstevel@tonic-gate 	if (WR(q) == ah_sadb.s_ip_q) {
530*0Sstevel@tonic-gate 		/*
531*0Sstevel@tonic-gate 		 * If the ah_sadb.s_ip_q is attached to this instance, find
532*0Sstevel@tonic-gate 		 * another.  The OCEXCL outer perimeter helps us here.
533*0Sstevel@tonic-gate 		 */
534*0Sstevel@tonic-gate 
535*0Sstevel@tonic-gate 		ah_sadb.s_ip_q = NULL;
536*0Sstevel@tonic-gate 
537*0Sstevel@tonic-gate 		/*
538*0Sstevel@tonic-gate 		 * Find a replacement queue for ah_sadb.s_ip_q.
539*0Sstevel@tonic-gate 		 */
540*0Sstevel@tonic-gate 		if (ah_pfkey_q != NULL && ah_pfkey_q != RD(q)) {
541*0Sstevel@tonic-gate 			/*
542*0Sstevel@tonic-gate 			 * See if we can use the pfkey_q.
543*0Sstevel@tonic-gate 			 */
544*0Sstevel@tonic-gate 			ah_sadb.s_ip_q = WR(ah_pfkey_q);
545*0Sstevel@tonic-gate 		}
546*0Sstevel@tonic-gate 
547*0Sstevel@tonic-gate 		if (ah_sadb.s_ip_q == NULL ||
548*0Sstevel@tonic-gate 		    !sadb_t_bind_req(ah_sadb.s_ip_q, IPPROTO_AH)) {
549*0Sstevel@tonic-gate 			ah1dbg(("ipsecah: Can't reassign ah_sadb.s_ip_q.\n"));
550*0Sstevel@tonic-gate 			ah_sadb.s_ip_q = NULL;
551*0Sstevel@tonic-gate 		} else {
552*0Sstevel@tonic-gate 			ah_ip_unbind = allocb(sizeof (struct T_unbind_req),
553*0Sstevel@tonic-gate 			    BPRI_HI);
554*0Sstevel@tonic-gate 
555*0Sstevel@tonic-gate 			if (ah_ip_unbind != NULL) {
556*0Sstevel@tonic-gate 				struct T_unbind_req *tur;
557*0Sstevel@tonic-gate 
558*0Sstevel@tonic-gate 				ah_ip_unbind->b_datap->db_type = M_PROTO;
559*0Sstevel@tonic-gate 				tur = (struct T_unbind_req *)
560*0Sstevel@tonic-gate 				    ah_ip_unbind->b_rptr;
561*0Sstevel@tonic-gate 				tur->PRIM_type = T_UNBIND_REQ;
562*0Sstevel@tonic-gate 			}
563*0Sstevel@tonic-gate 			/* If it's NULL, I can't do much here. */
564*0Sstevel@tonic-gate 		}
565*0Sstevel@tonic-gate 	}
566*0Sstevel@tonic-gate 
567*0Sstevel@tonic-gate 	return (0);
568*0Sstevel@tonic-gate }
569*0Sstevel@tonic-gate 
570*0Sstevel@tonic-gate /*
571*0Sstevel@tonic-gate  * AH module read put routine.
572*0Sstevel@tonic-gate  */
573*0Sstevel@tonic-gate /* ARGSUSED */
574*0Sstevel@tonic-gate static void
575*0Sstevel@tonic-gate ipsecah_rput(queue_t *q, mblk_t *mp)
576*0Sstevel@tonic-gate {
577*0Sstevel@tonic-gate 	keysock_in_t *ksi;
578*0Sstevel@tonic-gate 	int *addrtype;
579*0Sstevel@tonic-gate 	ire_t *ire;
580*0Sstevel@tonic-gate 	mblk_t *ire_mp, *last_mp;
581*0Sstevel@tonic-gate 
582*0Sstevel@tonic-gate 	switch (mp->b_datap->db_type) {
583*0Sstevel@tonic-gate 	case M_CTL:
584*0Sstevel@tonic-gate 		/*
585*0Sstevel@tonic-gate 		 * IPsec request of some variety from IP.  IPSEC_{IN,OUT}
586*0Sstevel@tonic-gate 		 * are the common cases, but even ICMP error messages from IP
587*0Sstevel@tonic-gate 		 * may rise up here.
588*0Sstevel@tonic-gate 		 *
589*0Sstevel@tonic-gate 		 * Ummmm, actually, this can also be the reflected KEYSOCK_IN
590*0Sstevel@tonic-gate 		 * message, with an IRE_DB_TYPE hung off at the end.
591*0Sstevel@tonic-gate 		 */
592*0Sstevel@tonic-gate 		switch (((ipsec_info_t *)(mp->b_rptr))->ipsec_info_type) {
593*0Sstevel@tonic-gate 		case KEYSOCK_IN:
594*0Sstevel@tonic-gate 			last_mp = mp;
595*0Sstevel@tonic-gate 			while (last_mp->b_cont != NULL &&
596*0Sstevel@tonic-gate 			    last_mp->b_cont->b_datap->db_type != IRE_DB_TYPE)
597*0Sstevel@tonic-gate 				last_mp = last_mp->b_cont;
598*0Sstevel@tonic-gate 
599*0Sstevel@tonic-gate 			if (last_mp->b_cont == NULL) {
600*0Sstevel@tonic-gate 				freemsg(mp);
601*0Sstevel@tonic-gate 				break;	/* Out of switch. */
602*0Sstevel@tonic-gate 			}
603*0Sstevel@tonic-gate 
604*0Sstevel@tonic-gate 			ire_mp = last_mp->b_cont;
605*0Sstevel@tonic-gate 			last_mp->b_cont = NULL;
606*0Sstevel@tonic-gate 
607*0Sstevel@tonic-gate 			ksi = (keysock_in_t *)mp->b_rptr;
608*0Sstevel@tonic-gate 
609*0Sstevel@tonic-gate 			if (ksi->ks_in_srctype == KS_IN_ADDR_UNKNOWN)
610*0Sstevel@tonic-gate 				addrtype = &ksi->ks_in_srctype;
611*0Sstevel@tonic-gate 			else if (ksi->ks_in_dsttype == KS_IN_ADDR_UNKNOWN)
612*0Sstevel@tonic-gate 				addrtype = &ksi->ks_in_dsttype;
613*0Sstevel@tonic-gate 			else if (ksi->ks_in_proxytype == KS_IN_ADDR_UNKNOWN)
614*0Sstevel@tonic-gate 				addrtype = &ksi->ks_in_proxytype;
615*0Sstevel@tonic-gate 
616*0Sstevel@tonic-gate 			ire = (ire_t *)ire_mp->b_rptr;
617*0Sstevel@tonic-gate 
618*0Sstevel@tonic-gate 			*addrtype = sadb_addrset(ire);
619*0Sstevel@tonic-gate 
620*0Sstevel@tonic-gate 			freemsg(ire_mp);
621*0Sstevel@tonic-gate 			if (ah_pfkey_q != NULL) {
622*0Sstevel@tonic-gate 				/*
623*0Sstevel@tonic-gate 				 * Decrement counter to make up for
624*0Sstevel@tonic-gate 				 * auto-increment in ipsecah_wput().
625*0Sstevel@tonic-gate 				 * I'm running all MT-hot through here, so
626*0Sstevel@tonic-gate 				 * don't worry about perimeters and lateral
627*0Sstevel@tonic-gate 				 * puts.
628*0Sstevel@tonic-gate 				 */
629*0Sstevel@tonic-gate 				AH_DEBUMP_STAT(keysock_in);
630*0Sstevel@tonic-gate 				ipsecah_wput(WR(ah_pfkey_q), mp);
631*0Sstevel@tonic-gate 			} else {
632*0Sstevel@tonic-gate 				freemsg(mp);
633*0Sstevel@tonic-gate 			}
634*0Sstevel@tonic-gate 			break;
635*0Sstevel@tonic-gate 		default:
636*0Sstevel@tonic-gate 			freemsg(mp);
637*0Sstevel@tonic-gate 			break;
638*0Sstevel@tonic-gate 		}
639*0Sstevel@tonic-gate 		break;
640*0Sstevel@tonic-gate 	case M_PROTO:
641*0Sstevel@tonic-gate 	case M_PCPROTO:
642*0Sstevel@tonic-gate 		/* TPI message of some sort. */
643*0Sstevel@tonic-gate 		switch (*((t_scalar_t *)mp->b_rptr)) {
644*0Sstevel@tonic-gate 		case T_BIND_ACK:
645*0Sstevel@tonic-gate 			/* We expect this. */
646*0Sstevel@tonic-gate 			ah3dbg(("Thank you IP from AH for T_BIND_ACK\n"));
647*0Sstevel@tonic-gate 			break;
648*0Sstevel@tonic-gate 		case T_ERROR_ACK:
649*0Sstevel@tonic-gate 			cmn_err(CE_WARN,
650*0Sstevel@tonic-gate 			    "ipsecah:  AH received T_ERROR_ACK from IP.");
651*0Sstevel@tonic-gate 			break;
652*0Sstevel@tonic-gate 		case T_OK_ACK:
653*0Sstevel@tonic-gate 			/* Probably from a (rarely sent) T_UNBIND_REQ. */
654*0Sstevel@tonic-gate 			break;
655*0Sstevel@tonic-gate 		default:
656*0Sstevel@tonic-gate 			ah1dbg(("Unknown M_{,PC}PROTO message.\n"));
657*0Sstevel@tonic-gate 		}
658*0Sstevel@tonic-gate 		freemsg(mp);
659*0Sstevel@tonic-gate 		break;
660*0Sstevel@tonic-gate 	default:
661*0Sstevel@tonic-gate 		/* For now, passthru message. */
662*0Sstevel@tonic-gate 		ah2dbg(("AH got unknown mblk type %d.\n",
663*0Sstevel@tonic-gate 		    mp->b_datap->db_type));
664*0Sstevel@tonic-gate 		putnext(q, mp);
665*0Sstevel@tonic-gate 	}
666*0Sstevel@tonic-gate }
667*0Sstevel@tonic-gate 
668*0Sstevel@tonic-gate /*
669*0Sstevel@tonic-gate  * Construct an SADB_REGISTER message with the current algorithms.
670*0Sstevel@tonic-gate  */
671*0Sstevel@tonic-gate static boolean_t
672*0Sstevel@tonic-gate ah_register_out(uint32_t sequence, uint32_t pid, uint_t serial)
673*0Sstevel@tonic-gate {
674*0Sstevel@tonic-gate 	mblk_t *mp;
675*0Sstevel@tonic-gate 	boolean_t rc = B_TRUE;
676*0Sstevel@tonic-gate 	sadb_msg_t *samsg;
677*0Sstevel@tonic-gate 	sadb_supported_t *sasupp;
678*0Sstevel@tonic-gate 	sadb_alg_t *saalg;
679*0Sstevel@tonic-gate 	uint_t allocsize = sizeof (*samsg);
680*0Sstevel@tonic-gate 	uint_t i, numalgs_snap;
681*0Sstevel@tonic-gate 	ipsec_alginfo_t **authalgs;
682*0Sstevel@tonic-gate 	uint_t num_aalgs;
683*0Sstevel@tonic-gate 
684*0Sstevel@tonic-gate 	/* Allocate the KEYSOCK_OUT. */
685*0Sstevel@tonic-gate 	mp = sadb_keysock_out(serial);
686*0Sstevel@tonic-gate 	if (mp == NULL) {
687*0Sstevel@tonic-gate 		ah0dbg(("ah_register_out: couldn't allocate mblk.\n"));
688*0Sstevel@tonic-gate 		return (B_FALSE);
689*0Sstevel@tonic-gate 	}
690*0Sstevel@tonic-gate 
691*0Sstevel@tonic-gate 	/*
692*0Sstevel@tonic-gate 	 * Allocate the PF_KEY message that follows KEYSOCK_OUT.
693*0Sstevel@tonic-gate 	 * The alg reader lock needs to be held while allocating
694*0Sstevel@tonic-gate 	 * the variable part (i.e. the algorithms) of the message.
695*0Sstevel@tonic-gate 	 */
696*0Sstevel@tonic-gate 
697*0Sstevel@tonic-gate 	mutex_enter(&alg_lock);
698*0Sstevel@tonic-gate 
699*0Sstevel@tonic-gate 	/*
700*0Sstevel@tonic-gate 	 * Return only valid algorithms, so the number of algorithms
701*0Sstevel@tonic-gate 	 * to send up may be less than the number of algorithm entries
702*0Sstevel@tonic-gate 	 * in the table.
703*0Sstevel@tonic-gate 	 */
704*0Sstevel@tonic-gate 	authalgs = ipsec_alglists[IPSEC_ALG_AUTH];
705*0Sstevel@tonic-gate 	for (num_aalgs = 0, i = 0; i < IPSEC_MAX_ALGS; i++)
706*0Sstevel@tonic-gate 		if (authalgs[i] != NULL && ALG_VALID(authalgs[i]))
707*0Sstevel@tonic-gate 			num_aalgs++;
708*0Sstevel@tonic-gate 
709*0Sstevel@tonic-gate 	/*
710*0Sstevel@tonic-gate 	 * Fill SADB_REGISTER message's algorithm descriptors.  Hold
711*0Sstevel@tonic-gate 	 * down the lock while filling it.
712*0Sstevel@tonic-gate 	 */
713*0Sstevel@tonic-gate 	if (num_aalgs != 0) {
714*0Sstevel@tonic-gate 		allocsize += (num_aalgs * sizeof (*saalg));
715*0Sstevel@tonic-gate 		allocsize += sizeof (*sasupp);
716*0Sstevel@tonic-gate 	}
717*0Sstevel@tonic-gate 	mp->b_cont = allocb(allocsize, BPRI_HI);
718*0Sstevel@tonic-gate 	if (mp->b_cont == NULL) {
719*0Sstevel@tonic-gate 		mutex_exit(&alg_lock);
720*0Sstevel@tonic-gate 		freemsg(mp);
721*0Sstevel@tonic-gate 		return (B_FALSE);
722*0Sstevel@tonic-gate 	}
723*0Sstevel@tonic-gate 
724*0Sstevel@tonic-gate 	mp->b_cont->b_wptr += allocsize;
725*0Sstevel@tonic-gate 	if (num_aalgs != 0) {
726*0Sstevel@tonic-gate 
727*0Sstevel@tonic-gate 		saalg = (sadb_alg_t *)(mp->b_cont->b_rptr + sizeof (*samsg) +
728*0Sstevel@tonic-gate 		    sizeof (*sasupp));
729*0Sstevel@tonic-gate 		ASSERT(((ulong_t)saalg & 0x7) == 0);
730*0Sstevel@tonic-gate 
731*0Sstevel@tonic-gate 		numalgs_snap = 0;
732*0Sstevel@tonic-gate 		for (i = 0;
733*0Sstevel@tonic-gate 		    ((i < IPSEC_MAX_ALGS) && (numalgs_snap < num_aalgs)); i++) {
734*0Sstevel@tonic-gate 			if (authalgs[i] == NULL || !ALG_VALID(authalgs[i]))
735*0Sstevel@tonic-gate 				continue;
736*0Sstevel@tonic-gate 
737*0Sstevel@tonic-gate 			saalg->sadb_alg_id = authalgs[i]->alg_id;
738*0Sstevel@tonic-gate 			saalg->sadb_alg_ivlen = 0;
739*0Sstevel@tonic-gate 			saalg->sadb_alg_minbits = authalgs[i]->alg_ef_minbits;
740*0Sstevel@tonic-gate 			saalg->sadb_alg_maxbits = authalgs[i]->alg_ef_maxbits;
741*0Sstevel@tonic-gate 			saalg->sadb_x_alg_increment =
742*0Sstevel@tonic-gate 			    authalgs[i]->alg_increment;
743*0Sstevel@tonic-gate 			saalg->sadb_x_alg_defincr = authalgs[i]->alg_ef_default;
744*0Sstevel@tonic-gate 			numalgs_snap++;
745*0Sstevel@tonic-gate 			saalg++;
746*0Sstevel@tonic-gate 		}
747*0Sstevel@tonic-gate 		ASSERT(numalgs_snap == num_aalgs);
748*0Sstevel@tonic-gate #ifdef DEBUG
749*0Sstevel@tonic-gate 		/*
750*0Sstevel@tonic-gate 		 * Reality check to make sure I snagged all of the
751*0Sstevel@tonic-gate 		 * algorithms.
752*0Sstevel@tonic-gate 		 */
753*0Sstevel@tonic-gate 		for (; i < IPSEC_MAX_ALGS; i++)
754*0Sstevel@tonic-gate 			if (authalgs[i] != NULL && ALG_VALID(authalgs[i]))
755*0Sstevel@tonic-gate 				cmn_err(CE_PANIC,
756*0Sstevel@tonic-gate 				    "ah_register_out()!  Missed #%d.\n", i);
757*0Sstevel@tonic-gate #endif /* DEBUG */
758*0Sstevel@tonic-gate 	}
759*0Sstevel@tonic-gate 
760*0Sstevel@tonic-gate 	mutex_exit(&alg_lock);
761*0Sstevel@tonic-gate 
762*0Sstevel@tonic-gate 	/* Now fill the restof the SADB_REGISTER message. */
763*0Sstevel@tonic-gate 
764*0Sstevel@tonic-gate 	samsg = (sadb_msg_t *)mp->b_cont->b_rptr;
765*0Sstevel@tonic-gate 	samsg->sadb_msg_version = PF_KEY_V2;
766*0Sstevel@tonic-gate 	samsg->sadb_msg_type = SADB_REGISTER;
767*0Sstevel@tonic-gate 	samsg->sadb_msg_errno = 0;
768*0Sstevel@tonic-gate 	samsg->sadb_msg_satype = SADB_SATYPE_AH;
769*0Sstevel@tonic-gate 	samsg->sadb_msg_len = SADB_8TO64(allocsize);
770*0Sstevel@tonic-gate 	samsg->sadb_msg_reserved = 0;
771*0Sstevel@tonic-gate 	/*
772*0Sstevel@tonic-gate 	 * Assume caller has sufficient sequence/pid number info.  If it's one
773*0Sstevel@tonic-gate 	 * from me over a new alg., I could give two hoots about sequence.
774*0Sstevel@tonic-gate 	 */
775*0Sstevel@tonic-gate 	samsg->sadb_msg_seq = sequence;
776*0Sstevel@tonic-gate 	samsg->sadb_msg_pid = pid;
777*0Sstevel@tonic-gate 
778*0Sstevel@tonic-gate 	if (allocsize > sizeof (*samsg)) {
779*0Sstevel@tonic-gate 		sasupp = (sadb_supported_t *)(samsg + 1);
780*0Sstevel@tonic-gate 		sasupp->sadb_supported_len =
781*0Sstevel@tonic-gate 		    SADB_8TO64(allocsize - sizeof (sadb_msg_t));
782*0Sstevel@tonic-gate 		sasupp->sadb_supported_exttype = SADB_EXT_SUPPORTED_AUTH;
783*0Sstevel@tonic-gate 		sasupp->sadb_supported_reserved = 0;
784*0Sstevel@tonic-gate 	}
785*0Sstevel@tonic-gate 
786*0Sstevel@tonic-gate 	if (ah_pfkey_q != NULL)
787*0Sstevel@tonic-gate 		putnext(ah_pfkey_q, mp);
788*0Sstevel@tonic-gate 	else {
789*0Sstevel@tonic-gate 		rc = B_FALSE;
790*0Sstevel@tonic-gate 		freemsg(mp);
791*0Sstevel@tonic-gate 	}
792*0Sstevel@tonic-gate 
793*0Sstevel@tonic-gate 	return (rc);
794*0Sstevel@tonic-gate }
795*0Sstevel@tonic-gate 
796*0Sstevel@tonic-gate /*
797*0Sstevel@tonic-gate  * Invoked when the algorithm table changes. Causes SADB_REGISTER
798*0Sstevel@tonic-gate  * messages continaining the current list of algorithms to be
799*0Sstevel@tonic-gate  * sent up to the AH listeners.
800*0Sstevel@tonic-gate  */
801*0Sstevel@tonic-gate void
802*0Sstevel@tonic-gate ipsecah_algs_changed(void)
803*0Sstevel@tonic-gate {
804*0Sstevel@tonic-gate 	/*
805*0Sstevel@tonic-gate 	 * Time to send a PF_KEY SADB_REGISTER message to AH listeners
806*0Sstevel@tonic-gate 	 * everywhere.  (The function itself checks for NULL ah_pfkey_q.)
807*0Sstevel@tonic-gate 	 */
808*0Sstevel@tonic-gate 	(void) ah_register_out(0, 0, 0);
809*0Sstevel@tonic-gate }
810*0Sstevel@tonic-gate 
811*0Sstevel@tonic-gate /*
812*0Sstevel@tonic-gate  * Stub function that taskq_dispatch() invokes to take the mblk (in arg)
813*0Sstevel@tonic-gate  * and put() it into AH and STREAMS again.
814*0Sstevel@tonic-gate  */
815*0Sstevel@tonic-gate static void
816*0Sstevel@tonic-gate inbound_task(void *arg)
817*0Sstevel@tonic-gate {
818*0Sstevel@tonic-gate 	ah_t *ah;
819*0Sstevel@tonic-gate 	mblk_t *mp = (mblk_t *)arg;
820*0Sstevel@tonic-gate 	ipsec_in_t *ii = (ipsec_in_t *)mp->b_rptr;
821*0Sstevel@tonic-gate 	int ipsec_rc;
822*0Sstevel@tonic-gate 
823*0Sstevel@tonic-gate 	ah2dbg(("in AH inbound_task"));
824*0Sstevel@tonic-gate 
825*0Sstevel@tonic-gate 	ah = ipsec_inbound_ah_sa(mp);
826*0Sstevel@tonic-gate 	if (ah == NULL)
827*0Sstevel@tonic-gate 		return;
828*0Sstevel@tonic-gate 	ASSERT(ii->ipsec_in_ah_sa != NULL);
829*0Sstevel@tonic-gate 	ipsec_rc = ii->ipsec_in_ah_sa->ipsa_input_func(mp, ah);
830*0Sstevel@tonic-gate 	if (ipsec_rc != IPSEC_STATUS_SUCCESS)
831*0Sstevel@tonic-gate 		return;
832*0Sstevel@tonic-gate 	ip_fanout_proto_again(mp, NULL, NULL, NULL);
833*0Sstevel@tonic-gate }
834*0Sstevel@tonic-gate 
835*0Sstevel@tonic-gate 
836*0Sstevel@tonic-gate /*
837*0Sstevel@tonic-gate  * Now that weak-key passed, actually ADD the security association, and
838*0Sstevel@tonic-gate  * send back a reply ADD message.
839*0Sstevel@tonic-gate  */
840*0Sstevel@tonic-gate static int
841*0Sstevel@tonic-gate ah_add_sa_finish(mblk_t *mp, sadb_msg_t *samsg, keysock_in_t *ksi)
842*0Sstevel@tonic-gate {
843*0Sstevel@tonic-gate 	isaf_t *primary, *secondary, *inbound;
844*0Sstevel@tonic-gate 	sadb_sa_t *assoc = (sadb_sa_t *)ksi->ks_in_extv[SADB_EXT_SA];
845*0Sstevel@tonic-gate 	sadb_address_t *dstext =
846*0Sstevel@tonic-gate 	    (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_DST];
847*0Sstevel@tonic-gate 	struct sockaddr_in *dst;
848*0Sstevel@tonic-gate 	struct sockaddr_in6 *dst6;
849*0Sstevel@tonic-gate 	boolean_t is_ipv4, clone = B_FALSE, is_inbound = B_FALSE;
850*0Sstevel@tonic-gate 	uint32_t *dstaddr;
851*0Sstevel@tonic-gate 	ipsa_t *larval;
852*0Sstevel@tonic-gate 	ipsacq_t *acqrec;
853*0Sstevel@tonic-gate 	iacqf_t *acq_bucket;
854*0Sstevel@tonic-gate 	mblk_t *acq_msgs = NULL;
855*0Sstevel@tonic-gate 	mblk_t *lpkt;
856*0Sstevel@tonic-gate 	int rc;
857*0Sstevel@tonic-gate 	sadb_t *sp;
858*0Sstevel@tonic-gate 	int outhash;
859*0Sstevel@tonic-gate 
860*0Sstevel@tonic-gate 	/*
861*0Sstevel@tonic-gate 	 * Locate the appropriate table(s).
862*0Sstevel@tonic-gate 	 */
863*0Sstevel@tonic-gate 
864*0Sstevel@tonic-gate 	dst = (struct sockaddr_in *)(dstext + 1);
865*0Sstevel@tonic-gate 	dst6 = (struct sockaddr_in6 *)dst;
866*0Sstevel@tonic-gate 	is_ipv4 = (dst->sin_family == AF_INET);
867*0Sstevel@tonic-gate 	if (is_ipv4) {
868*0Sstevel@tonic-gate 		sp = &ah_sadb.s_v4;
869*0Sstevel@tonic-gate 		dstaddr = (uint32_t *)(&dst->sin_addr);
870*0Sstevel@tonic-gate 		outhash = OUTBOUND_HASH_V4(*(ipaddr_t *)dstaddr);
871*0Sstevel@tonic-gate 	} else {
872*0Sstevel@tonic-gate 		ASSERT(dst->sin_family == AF_INET6);
873*0Sstevel@tonic-gate 		sp = &ah_sadb.s_v6;
874*0Sstevel@tonic-gate 		dstaddr = (uint32_t *)(&dst6->sin6_addr);
875*0Sstevel@tonic-gate 		outhash = OUTBOUND_HASH_V6(*(in6_addr_t *)dstaddr);
876*0Sstevel@tonic-gate 	}
877*0Sstevel@tonic-gate 
878*0Sstevel@tonic-gate 	inbound = &sp->sdb_if[INBOUND_HASH(assoc->sadb_sa_spi)];
879*0Sstevel@tonic-gate 
880*0Sstevel@tonic-gate 	switch (ksi->ks_in_dsttype) {
881*0Sstevel@tonic-gate 	case KS_IN_ADDR_MBCAST:
882*0Sstevel@tonic-gate 		clone = B_TRUE;	/* All mcast SAs can be bidirectional */
883*0Sstevel@tonic-gate 		/* FALLTHRU */
884*0Sstevel@tonic-gate 	case KS_IN_ADDR_ME:
885*0Sstevel@tonic-gate 		primary = inbound;
886*0Sstevel@tonic-gate 		secondary = &sp->sdb_of[outhash];
887*0Sstevel@tonic-gate 		/*
888*0Sstevel@tonic-gate 		 * If the source address is either one of mine, or unspecified
889*0Sstevel@tonic-gate 		 * (which is best summed up by saying "not 'not mine'"),
890*0Sstevel@tonic-gate 		 * then the association is potentially bi-directional,
891*0Sstevel@tonic-gate 		 * in that it can be used for inbound traffic and outbound
892*0Sstevel@tonic-gate 		 * traffic.  The best example of such and SA is a multicast
893*0Sstevel@tonic-gate 		 * SA (which allows me to receive the outbound traffic).
894*0Sstevel@tonic-gate 		 */
895*0Sstevel@tonic-gate 		if (ksi->ks_in_srctype != KS_IN_ADDR_NOTME)
896*0Sstevel@tonic-gate 			clone = B_TRUE;
897*0Sstevel@tonic-gate 		is_inbound = B_TRUE;
898*0Sstevel@tonic-gate 		break;
899*0Sstevel@tonic-gate 	case KS_IN_ADDR_NOTME:
900*0Sstevel@tonic-gate 		primary = &sp->sdb_of[outhash];
901*0Sstevel@tonic-gate 		secondary = &sp->sdb_if[INBOUND_HASH(assoc->sadb_sa_spi)];
902*0Sstevel@tonic-gate 		/*
903*0Sstevel@tonic-gate 		 * If the source address literally not mine (either
904*0Sstevel@tonic-gate 		 * unspecified or not mine), then this SA may have an
905*0Sstevel@tonic-gate 		 * address that WILL be mine after some configuration.
906*0Sstevel@tonic-gate 		 * We pay the price for this by making it a bi-directional
907*0Sstevel@tonic-gate 		 * SA.
908*0Sstevel@tonic-gate 		 */
909*0Sstevel@tonic-gate 		if (ksi->ks_in_srctype != KS_IN_ADDR_ME)
910*0Sstevel@tonic-gate 			clone = B_TRUE;
911*0Sstevel@tonic-gate 		break;
912*0Sstevel@tonic-gate 	default:
913*0Sstevel@tonic-gate 		samsg->sadb_x_msg_diagnostic = SADB_X_DIAGNOSTIC_BAD_DST;
914*0Sstevel@tonic-gate 		return (EINVAL);
915*0Sstevel@tonic-gate 	}
916*0Sstevel@tonic-gate 
917*0Sstevel@tonic-gate 	/*
918*0Sstevel@tonic-gate 	 * Find a ACQUIRE list entry if possible.  If we've added an SA that
919*0Sstevel@tonic-gate 	 * suits the needs of an ACQUIRE list entry, we can eliminate the
920*0Sstevel@tonic-gate 	 * ACQUIRE list entry and transmit the enqueued packets.  Use the
921*0Sstevel@tonic-gate 	 * high-bit of the sequence number to queue it.  Key off destination
922*0Sstevel@tonic-gate 	 * addr, and change acqrec's state.
923*0Sstevel@tonic-gate 	 */
924*0Sstevel@tonic-gate 
925*0Sstevel@tonic-gate 	if (samsg->sadb_msg_seq & IACQF_LOWEST_SEQ) {
926*0Sstevel@tonic-gate 		acq_bucket = &sp->sdb_acq[outhash];
927*0Sstevel@tonic-gate 		mutex_enter(&acq_bucket->iacqf_lock);
928*0Sstevel@tonic-gate 		for (acqrec = acq_bucket->iacqf_ipsacq; acqrec != NULL;
929*0Sstevel@tonic-gate 		    acqrec = acqrec->ipsacq_next) {
930*0Sstevel@tonic-gate 			mutex_enter(&acqrec->ipsacq_lock);
931*0Sstevel@tonic-gate 			/*
932*0Sstevel@tonic-gate 			 * Q:  I only check sequence.  Should I check dst?
933*0Sstevel@tonic-gate 			 * A: Yes, check dest because those are the packets
934*0Sstevel@tonic-gate 			 *    that are queued up.
935*0Sstevel@tonic-gate 			 */
936*0Sstevel@tonic-gate 			if (acqrec->ipsacq_seq == samsg->sadb_msg_seq &&
937*0Sstevel@tonic-gate 			    IPSA_ARE_ADDR_EQUAL(dstaddr,
938*0Sstevel@tonic-gate 				acqrec->ipsacq_dstaddr, acqrec->ipsacq_addrfam))
939*0Sstevel@tonic-gate 				break;
940*0Sstevel@tonic-gate 			mutex_exit(&acqrec->ipsacq_lock);
941*0Sstevel@tonic-gate 		}
942*0Sstevel@tonic-gate 		if (acqrec != NULL) {
943*0Sstevel@tonic-gate 			/*
944*0Sstevel@tonic-gate 			 * AHA!  I found an ACQUIRE record for this SA.
945*0Sstevel@tonic-gate 			 * Grab the msg list, and free the acquire record.
946*0Sstevel@tonic-gate 			 * I already am holding the lock for this record,
947*0Sstevel@tonic-gate 			 * so all I have to do is free it.
948*0Sstevel@tonic-gate 			 */
949*0Sstevel@tonic-gate 			acq_msgs = acqrec->ipsacq_mp;
950*0Sstevel@tonic-gate 			acqrec->ipsacq_mp = NULL;
951*0Sstevel@tonic-gate 			mutex_exit(&acqrec->ipsacq_lock);
952*0Sstevel@tonic-gate 			sadb_destroy_acquire(acqrec);
953*0Sstevel@tonic-gate 		}
954*0Sstevel@tonic-gate 		mutex_exit(&acq_bucket->iacqf_lock);
955*0Sstevel@tonic-gate 	}
956*0Sstevel@tonic-gate 
957*0Sstevel@tonic-gate 	/*
958*0Sstevel@tonic-gate 	 * Find PF_KEY message, and see if I'm an update.  If so, find entry
959*0Sstevel@tonic-gate 	 * in larval list (if there).
960*0Sstevel@tonic-gate 	 */
961*0Sstevel@tonic-gate 
962*0Sstevel@tonic-gate 	larval = NULL;
963*0Sstevel@tonic-gate 
964*0Sstevel@tonic-gate 	if (samsg->sadb_msg_type == SADB_UPDATE) {
965*0Sstevel@tonic-gate 		mutex_enter(&inbound->isaf_lock);
966*0Sstevel@tonic-gate 		larval = ipsec_getassocbyspi(inbound, assoc->sadb_sa_spi,
967*0Sstevel@tonic-gate 		    ALL_ZEROES_PTR, dstaddr, dst->sin_family);
968*0Sstevel@tonic-gate 		mutex_exit(&inbound->isaf_lock);
969*0Sstevel@tonic-gate 
970*0Sstevel@tonic-gate 		if ((larval == NULL) ||
971*0Sstevel@tonic-gate 		    (larval->ipsa_state != IPSA_STATE_LARVAL)) {
972*0Sstevel@tonic-gate 			ah0dbg(("Larval update, but larval disappeared.\n"));
973*0Sstevel@tonic-gate 			return (ESRCH);
974*0Sstevel@tonic-gate 		} /* Else sadb_common_add unlinks it for me! */
975*0Sstevel@tonic-gate 	}
976*0Sstevel@tonic-gate 
977*0Sstevel@tonic-gate 	lpkt = NULL;
978*0Sstevel@tonic-gate 	if (larval != NULL)
979*0Sstevel@tonic-gate 		lpkt = sadb_clear_lpkt(larval);
980*0Sstevel@tonic-gate 
981*0Sstevel@tonic-gate 	rc = sadb_common_add(ah_sadb.s_ip_q, ah_pfkey_q, mp, samsg, ksi,
982*0Sstevel@tonic-gate 	    primary, secondary, larval, clone, is_inbound);
983*0Sstevel@tonic-gate 
984*0Sstevel@tonic-gate 	/*
985*0Sstevel@tonic-gate 	 * How much more stack will I create with all of these
986*0Sstevel@tonic-gate 	 * ah_inbound_* and ah_outbound_*() calls?
987*0Sstevel@tonic-gate 	 */
988*0Sstevel@tonic-gate 
989*0Sstevel@tonic-gate 
990*0Sstevel@tonic-gate 	if (rc == 0 && lpkt != NULL)
991*0Sstevel@tonic-gate 		rc = !taskq_dispatch(ah_taskq, inbound_task,
992*0Sstevel@tonic-gate 			    (void *) lpkt, TQ_NOSLEEP);
993*0Sstevel@tonic-gate 
994*0Sstevel@tonic-gate 	if (rc != 0) {
995*0Sstevel@tonic-gate 		ip_drop_packet(lpkt, B_TRUE, NULL, NULL,
996*0Sstevel@tonic-gate 		    &ipdrops_sadb_inlarval_timeout, &ah_dropper);
997*0Sstevel@tonic-gate 	}
998*0Sstevel@tonic-gate 
999*0Sstevel@tonic-gate 	while (acq_msgs != NULL) {
1000*0Sstevel@tonic-gate 		mblk_t *mp = acq_msgs;
1001*0Sstevel@tonic-gate 
1002*0Sstevel@tonic-gate 		acq_msgs = acq_msgs->b_next;
1003*0Sstevel@tonic-gate 		mp->b_next = NULL;
1004*0Sstevel@tonic-gate 		if (rc == 0) {
1005*0Sstevel@tonic-gate 			ipsec_out_t *io = (ipsec_out_t *)mp->b_rptr;
1006*0Sstevel@tonic-gate 
1007*0Sstevel@tonic-gate 			ASSERT(ah_sadb.s_ip_q != NULL);
1008*0Sstevel@tonic-gate 			if (ipsec_outbound_sa(mp, IPPROTO_AH)) {
1009*0Sstevel@tonic-gate 				io->ipsec_out_ah_done = B_TRUE;
1010*0Sstevel@tonic-gate 				if (ah_outbound(mp) == IPSEC_STATUS_SUCCESS) {
1011*0Sstevel@tonic-gate 					ipha_t *ipha = (ipha_t *)
1012*0Sstevel@tonic-gate 					    mp->b_cont->b_rptr;
1013*0Sstevel@tonic-gate 					if (is_ipv4) {
1014*0Sstevel@tonic-gate 						ip_wput_ipsec_out(NULL, mp,
1015*0Sstevel@tonic-gate 						    ipha, NULL, NULL);
1016*0Sstevel@tonic-gate 					} else {
1017*0Sstevel@tonic-gate 						ip6_t *ip6h = (ip6_t *)ipha;
1018*0Sstevel@tonic-gate 						ip_wput_ipsec_out_v6(NULL,
1019*0Sstevel@tonic-gate 						    mp, ip6h, NULL, NULL);
1020*0Sstevel@tonic-gate 					}
1021*0Sstevel@tonic-gate 				}
1022*0Sstevel@tonic-gate 				continue;
1023*0Sstevel@tonic-gate 			}
1024*0Sstevel@tonic-gate 		}
1025*0Sstevel@tonic-gate 		AH_BUMP_STAT(out_discards);
1026*0Sstevel@tonic-gate 		ip_drop_packet(mp, B_FALSE, NULL, NULL,
1027*0Sstevel@tonic-gate 		    &ipdrops_sadb_acquire_timeout, &ah_dropper);
1028*0Sstevel@tonic-gate 	}
1029*0Sstevel@tonic-gate 
1030*0Sstevel@tonic-gate 	return (rc);
1031*0Sstevel@tonic-gate }
1032*0Sstevel@tonic-gate 
1033*0Sstevel@tonic-gate /*
1034*0Sstevel@tonic-gate  * Add new AH security association.  This may become a generic AH/ESP
1035*0Sstevel@tonic-gate  * routine eventually.
1036*0Sstevel@tonic-gate  */
1037*0Sstevel@tonic-gate static int
1038*0Sstevel@tonic-gate ah_add_sa(mblk_t *mp, keysock_in_t *ksi, int *diagnostic)
1039*0Sstevel@tonic-gate {
1040*0Sstevel@tonic-gate 	sadb_sa_t *assoc = (sadb_sa_t *)ksi->ks_in_extv[SADB_EXT_SA];
1041*0Sstevel@tonic-gate 	sadb_address_t *srcext =
1042*0Sstevel@tonic-gate 	    (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_SRC];
1043*0Sstevel@tonic-gate 	sadb_address_t *dstext =
1044*0Sstevel@tonic-gate 	    (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_DST];
1045*0Sstevel@tonic-gate 	sadb_key_t *key = (sadb_key_t *)ksi->ks_in_extv[SADB_EXT_KEY_AUTH];
1046*0Sstevel@tonic-gate 	struct sockaddr_in *src, *dst;
1047*0Sstevel@tonic-gate 	/* We don't need sockaddr_in6 for now. */
1048*0Sstevel@tonic-gate 	sadb_lifetime_t *soft =
1049*0Sstevel@tonic-gate 	    (sadb_lifetime_t *)ksi->ks_in_extv[SADB_EXT_LIFETIME_SOFT];
1050*0Sstevel@tonic-gate 	sadb_lifetime_t *hard =
1051*0Sstevel@tonic-gate 	    (sadb_lifetime_t *)ksi->ks_in_extv[SADB_EXT_LIFETIME_HARD];
1052*0Sstevel@tonic-gate 	ipsec_alginfo_t *aalg;
1053*0Sstevel@tonic-gate 
1054*0Sstevel@tonic-gate 	/* I need certain extensions present for an ADD message. */
1055*0Sstevel@tonic-gate 	if (srcext == NULL) {
1056*0Sstevel@tonic-gate 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_SRC;
1057*0Sstevel@tonic-gate 		return (EINVAL);
1058*0Sstevel@tonic-gate 	}
1059*0Sstevel@tonic-gate 	if (dstext == NULL) {
1060*0Sstevel@tonic-gate 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_DST;
1061*0Sstevel@tonic-gate 		return (EINVAL);
1062*0Sstevel@tonic-gate 	}
1063*0Sstevel@tonic-gate 	if (assoc == NULL) {
1064*0Sstevel@tonic-gate 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_SA;
1065*0Sstevel@tonic-gate 		return (EINVAL);
1066*0Sstevel@tonic-gate 	}
1067*0Sstevel@tonic-gate 	if (key == NULL) {
1068*0Sstevel@tonic-gate 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_AKEY;
1069*0Sstevel@tonic-gate 		return (EINVAL);
1070*0Sstevel@tonic-gate 	}
1071*0Sstevel@tonic-gate 
1072*0Sstevel@tonic-gate 	src = (struct sockaddr_in *)(srcext + 1);
1073*0Sstevel@tonic-gate 	dst = (struct sockaddr_in *)(dstext + 1);
1074*0Sstevel@tonic-gate 
1075*0Sstevel@tonic-gate 	/* Sundry ADD-specific reality checks. */
1076*0Sstevel@tonic-gate 	/* XXX STATS : Logging/stats here? */
1077*0Sstevel@tonic-gate 
1078*0Sstevel@tonic-gate 	if (assoc->sadb_sa_state != SADB_SASTATE_MATURE) {
1079*0Sstevel@tonic-gate 		*diagnostic = SADB_X_DIAGNOSTIC_BAD_SASTATE;
1080*0Sstevel@tonic-gate 		return (EINVAL);
1081*0Sstevel@tonic-gate 	}
1082*0Sstevel@tonic-gate 	if (assoc->sadb_sa_encrypt != SADB_EALG_NONE) {
1083*0Sstevel@tonic-gate 		*diagnostic = SADB_X_DIAGNOSTIC_ENCR_NOTSUPP;
1084*0Sstevel@tonic-gate 		return (EINVAL);
1085*0Sstevel@tonic-gate 	}
1086*0Sstevel@tonic-gate 	if (assoc->sadb_sa_flags & ~(SADB_SAFLAGS_NOREPLAY)) {
1087*0Sstevel@tonic-gate 		*diagnostic = SADB_X_DIAGNOSTIC_BAD_SAFLAGS;
1088*0Sstevel@tonic-gate 		return (EINVAL);
1089*0Sstevel@tonic-gate 	}
1090*0Sstevel@tonic-gate 
1091*0Sstevel@tonic-gate 	if ((*diagnostic = sadb_hardsoftchk(hard, soft)) != 0)
1092*0Sstevel@tonic-gate 		return (EINVAL);
1093*0Sstevel@tonic-gate 
1094*0Sstevel@tonic-gate 	if (src->sin_family != dst->sin_family) {
1095*0Sstevel@tonic-gate 		*diagnostic = SADB_X_DIAGNOSTIC_AF_MISMATCH;
1096*0Sstevel@tonic-gate 		return (EINVAL);
1097*0Sstevel@tonic-gate 	}
1098*0Sstevel@tonic-gate 
1099*0Sstevel@tonic-gate 	/* Stuff I don't support, for now.  XXX Diagnostic? */
1100*0Sstevel@tonic-gate 	if (ksi->ks_in_extv[SADB_EXT_LIFETIME_CURRENT] != NULL ||
1101*0Sstevel@tonic-gate 	    ksi->ks_in_extv[SADB_EXT_SENSITIVITY] != NULL)
1102*0Sstevel@tonic-gate 		return (EOPNOTSUPP);
1103*0Sstevel@tonic-gate 
1104*0Sstevel@tonic-gate 	/*
1105*0Sstevel@tonic-gate 	 * XXX Policy : I'm not checking identities or sensitivity
1106*0Sstevel@tonic-gate 	 * labels at this time, but if I did, I'd do them here, before I sent
1107*0Sstevel@tonic-gate 	 * the weak key check up to the algorithm.
1108*0Sstevel@tonic-gate 	 */
1109*0Sstevel@tonic-gate 
1110*0Sstevel@tonic-gate 	/* verify that there is a mapping for the specified algorithm */
1111*0Sstevel@tonic-gate 	mutex_enter(&alg_lock);
1112*0Sstevel@tonic-gate 	aalg = ipsec_alglists[IPSEC_ALG_AUTH][assoc->sadb_sa_auth];
1113*0Sstevel@tonic-gate 	if (aalg == NULL || !ALG_VALID(aalg)) {
1114*0Sstevel@tonic-gate 		mutex_exit(&alg_lock);
1115*0Sstevel@tonic-gate 		ah1dbg(("Couldn't find auth alg #%d.\n", assoc->sadb_sa_auth));
1116*0Sstevel@tonic-gate 		*diagnostic = SADB_X_DIAGNOSTIC_BAD_AALG;
1117*0Sstevel@tonic-gate 		return (EINVAL);
1118*0Sstevel@tonic-gate 	}
1119*0Sstevel@tonic-gate 	ASSERT(aalg->alg_mech_type != CRYPTO_MECHANISM_INVALID);
1120*0Sstevel@tonic-gate 
1121*0Sstevel@tonic-gate 	/* sanity check key sizes */
1122*0Sstevel@tonic-gate 	if (!ipsec_valid_key_size(key->sadb_key_bits, aalg)) {
1123*0Sstevel@tonic-gate 		mutex_exit(&alg_lock);
1124*0Sstevel@tonic-gate 		*diagnostic = SADB_X_DIAGNOSTIC_BAD_AKEYBITS;
1125*0Sstevel@tonic-gate 		return (EINVAL);
1126*0Sstevel@tonic-gate 	}
1127*0Sstevel@tonic-gate 
1128*0Sstevel@tonic-gate 	/* check key and fix parity if needed */
1129*0Sstevel@tonic-gate 	if (ipsec_check_key(aalg->alg_mech_type, key, B_TRUE,
1130*0Sstevel@tonic-gate 	    diagnostic) != 0) {
1131*0Sstevel@tonic-gate 		mutex_exit(&alg_lock);
1132*0Sstevel@tonic-gate 		return (EINVAL);
1133*0Sstevel@tonic-gate 	}
1134*0Sstevel@tonic-gate 
1135*0Sstevel@tonic-gate 	mutex_exit(&alg_lock);
1136*0Sstevel@tonic-gate 
1137*0Sstevel@tonic-gate 	return (ah_add_sa_finish(mp, (sadb_msg_t *)mp->b_cont->b_rptr, ksi));
1138*0Sstevel@tonic-gate }
1139*0Sstevel@tonic-gate 
1140*0Sstevel@tonic-gate /*
1141*0Sstevel@tonic-gate  * Update a security association.  Updates come in two varieties.  The first
1142*0Sstevel@tonic-gate  * is an update of lifetimes on a non-larval SA.  The second is an update of
1143*0Sstevel@tonic-gate  * a larval SA, which ends up looking a lot more like an add.
1144*0Sstevel@tonic-gate  */
1145*0Sstevel@tonic-gate static int
1146*0Sstevel@tonic-gate ah_update_sa(mblk_t *mp, keysock_in_t *ksi, int *diagnostic)
1147*0Sstevel@tonic-gate {
1148*0Sstevel@tonic-gate 	sadb_address_t *dstext =
1149*0Sstevel@tonic-gate 	    (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_DST];
1150*0Sstevel@tonic-gate 	struct sockaddr_in *sin;
1151*0Sstevel@tonic-gate 
1152*0Sstevel@tonic-gate 	if (dstext == NULL) {
1153*0Sstevel@tonic-gate 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_DST;
1154*0Sstevel@tonic-gate 		return (EINVAL);
1155*0Sstevel@tonic-gate 	}
1156*0Sstevel@tonic-gate 	sin = (struct sockaddr_in *)(dstext + 1);
1157*0Sstevel@tonic-gate 	return (sadb_update_sa(mp, ksi,
1158*0Sstevel@tonic-gate 	    (sin->sin_family == AF_INET6) ? &ah_sadb.s_v6 : &ah_sadb.s_v4,
1159*0Sstevel@tonic-gate 	    diagnostic, ah_pfkey_q, ah_add_sa));
1160*0Sstevel@tonic-gate }
1161*0Sstevel@tonic-gate 
1162*0Sstevel@tonic-gate /*
1163*0Sstevel@tonic-gate  * Delete a security association.  This is REALLY likely to be code common to
1164*0Sstevel@tonic-gate  * both AH and ESP.  Find the association, then unlink it.
1165*0Sstevel@tonic-gate  */
1166*0Sstevel@tonic-gate static int
1167*0Sstevel@tonic-gate ah_del_sa(mblk_t *mp, keysock_in_t *ksi, int *diagnostic)
1168*0Sstevel@tonic-gate {
1169*0Sstevel@tonic-gate 	sadb_sa_t *assoc = (sadb_sa_t *)ksi->ks_in_extv[SADB_EXT_SA];
1170*0Sstevel@tonic-gate 	sadb_address_t *dstext =
1171*0Sstevel@tonic-gate 	    (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_DST];
1172*0Sstevel@tonic-gate 	sadb_address_t *srcext =
1173*0Sstevel@tonic-gate 	    (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_SRC];
1174*0Sstevel@tonic-gate 	struct sockaddr_in *sin;
1175*0Sstevel@tonic-gate 
1176*0Sstevel@tonic-gate 	if (assoc == NULL) {
1177*0Sstevel@tonic-gate 		if (dstext != NULL)
1178*0Sstevel@tonic-gate 			sin = (struct sockaddr_in *)(dstext + 1);
1179*0Sstevel@tonic-gate 		else if (srcext != NULL)
1180*0Sstevel@tonic-gate 			sin = (struct sockaddr_in *)(srcext + 1);
1181*0Sstevel@tonic-gate 		else {
1182*0Sstevel@tonic-gate 			*diagnostic = SADB_X_DIAGNOSTIC_MISSING_SA;
1183*0Sstevel@tonic-gate 			return (EINVAL);
1184*0Sstevel@tonic-gate 		}
1185*0Sstevel@tonic-gate 		return sadb_purge_sa(mp, ksi,
1186*0Sstevel@tonic-gate 		    (sin->sin_family == AF_INET6) ? &ah_sadb.s_v6 :
1187*0Sstevel@tonic-gate 		    &ah_sadb.s_v4,
1188*0Sstevel@tonic-gate 		    diagnostic, ah_pfkey_q, ah_sadb.s_ip_q);
1189*0Sstevel@tonic-gate 	}
1190*0Sstevel@tonic-gate 
1191*0Sstevel@tonic-gate 	return (sadb_del_sa(mp, ksi, &ah_sadb, diagnostic, ah_pfkey_q));
1192*0Sstevel@tonic-gate }
1193*0Sstevel@tonic-gate 
1194*0Sstevel@tonic-gate /*
1195*0Sstevel@tonic-gate  * Convert the entire contents of all of AH's SA tables into PF_KEY SADB_DUMP
1196*0Sstevel@tonic-gate  * messages.
1197*0Sstevel@tonic-gate  */
1198*0Sstevel@tonic-gate static void
1199*0Sstevel@tonic-gate ah_dump(mblk_t *mp, keysock_in_t *ksi)
1200*0Sstevel@tonic-gate {
1201*0Sstevel@tonic-gate 	int error;
1202*0Sstevel@tonic-gate 	sadb_msg_t *samsg;
1203*0Sstevel@tonic-gate 
1204*0Sstevel@tonic-gate 	/*
1205*0Sstevel@tonic-gate 	 * Dump each fanout, bailing if error is non-zero.
1206*0Sstevel@tonic-gate 	 */
1207*0Sstevel@tonic-gate 
1208*0Sstevel@tonic-gate 	error = sadb_dump(ah_pfkey_q, mp, ksi->ks_in_serial, &ah_sadb.s_v4);
1209*0Sstevel@tonic-gate 	if (error != 0)
1210*0Sstevel@tonic-gate 		goto bail;
1211*0Sstevel@tonic-gate 
1212*0Sstevel@tonic-gate 	error = sadb_dump(ah_pfkey_q, mp, ksi->ks_in_serial, &ah_sadb.s_v6);
1213*0Sstevel@tonic-gate bail:
1214*0Sstevel@tonic-gate 	ASSERT(mp->b_cont != NULL);
1215*0Sstevel@tonic-gate 	samsg = (sadb_msg_t *)mp->b_cont->b_rptr;
1216*0Sstevel@tonic-gate 	samsg->sadb_msg_errno = (uint8_t)error;
1217*0Sstevel@tonic-gate 	sadb_pfkey_echo(ah_pfkey_q, mp, (sadb_msg_t *)mp->b_cont->b_rptr, ksi,
1218*0Sstevel@tonic-gate 	    NULL);
1219*0Sstevel@tonic-gate }
1220*0Sstevel@tonic-gate 
1221*0Sstevel@tonic-gate /*
1222*0Sstevel@tonic-gate  * AH parsing of PF_KEY messages.  Keysock did most of the really silly
1223*0Sstevel@tonic-gate  * error cases.  What I receive is a fully-formed, syntactically legal
1224*0Sstevel@tonic-gate  * PF_KEY message.  I then need to check semantics...
1225*0Sstevel@tonic-gate  *
1226*0Sstevel@tonic-gate  * This code may become common to AH and ESP.  Stay tuned.
1227*0Sstevel@tonic-gate  *
1228*0Sstevel@tonic-gate  * I also make the assumption that db_ref's are cool.  If this assumption
1229*0Sstevel@tonic-gate  * is wrong, this means that someone other than keysock or me has been
1230*0Sstevel@tonic-gate  * mucking with PF_KEY messages.
1231*0Sstevel@tonic-gate  */
1232*0Sstevel@tonic-gate static void
1233*0Sstevel@tonic-gate ah_parse_pfkey(mblk_t *mp)
1234*0Sstevel@tonic-gate {
1235*0Sstevel@tonic-gate 	mblk_t *msg = mp->b_cont;
1236*0Sstevel@tonic-gate 	sadb_msg_t *samsg;
1237*0Sstevel@tonic-gate 	keysock_in_t *ksi;
1238*0Sstevel@tonic-gate 	int error;
1239*0Sstevel@tonic-gate 	int diagnostic = SADB_X_DIAGNOSTIC_NONE;
1240*0Sstevel@tonic-gate 
1241*0Sstevel@tonic-gate 	ASSERT(msg != NULL);
1242*0Sstevel@tonic-gate 	samsg = (sadb_msg_t *)msg->b_rptr;
1243*0Sstevel@tonic-gate 	ksi = (keysock_in_t *)mp->b_rptr;
1244*0Sstevel@tonic-gate 
1245*0Sstevel@tonic-gate 	/*
1246*0Sstevel@tonic-gate 	 * If applicable, convert unspecified AF_INET6 to unspecified
1247*0Sstevel@tonic-gate 	 * AF_INET.
1248*0Sstevel@tonic-gate 	 */
1249*0Sstevel@tonic-gate 	sadb_srcaddrfix(ksi);
1250*0Sstevel@tonic-gate 
1251*0Sstevel@tonic-gate 	switch (samsg->sadb_msg_type) {
1252*0Sstevel@tonic-gate 	case SADB_ADD:
1253*0Sstevel@tonic-gate 		error = ah_add_sa(mp, ksi, &diagnostic);
1254*0Sstevel@tonic-gate 		if (error != 0) {
1255*0Sstevel@tonic-gate 			sadb_pfkey_error(ah_pfkey_q, mp, error, diagnostic,
1256*0Sstevel@tonic-gate 			    ksi->ks_in_serial);
1257*0Sstevel@tonic-gate 		}
1258*0Sstevel@tonic-gate 		/* else ah_add_sa() took care of things. */
1259*0Sstevel@tonic-gate 		break;
1260*0Sstevel@tonic-gate 	case SADB_DELETE:
1261*0Sstevel@tonic-gate 		error = ah_del_sa(mp, ksi, &diagnostic);
1262*0Sstevel@tonic-gate 		if (error != 0) {
1263*0Sstevel@tonic-gate 			sadb_pfkey_error(ah_pfkey_q, mp, error, diagnostic,
1264*0Sstevel@tonic-gate 			    ksi->ks_in_serial);
1265*0Sstevel@tonic-gate 		}
1266*0Sstevel@tonic-gate 		/* Else ah_del_sa() took care of things. */
1267*0Sstevel@tonic-gate 		break;
1268*0Sstevel@tonic-gate 	case SADB_GET:
1269*0Sstevel@tonic-gate 		error = sadb_get_sa(mp, ksi, &ah_sadb, &diagnostic, ah_pfkey_q);
1270*0Sstevel@tonic-gate 		if (error != 0) {
1271*0Sstevel@tonic-gate 			sadb_pfkey_error(ah_pfkey_q, mp, error, diagnostic,
1272*0Sstevel@tonic-gate 			    ksi->ks_in_serial);
1273*0Sstevel@tonic-gate 		}
1274*0Sstevel@tonic-gate 		/* Else sadb_get_sa() took care of things. */
1275*0Sstevel@tonic-gate 		break;
1276*0Sstevel@tonic-gate 	case SADB_FLUSH:
1277*0Sstevel@tonic-gate 		sadbp_flush(&ah_sadb);
1278*0Sstevel@tonic-gate 		sadb_pfkey_echo(ah_pfkey_q, mp, samsg, ksi, NULL);
1279*0Sstevel@tonic-gate 		break;
1280*0Sstevel@tonic-gate 	case SADB_REGISTER:
1281*0Sstevel@tonic-gate 		/*
1282*0Sstevel@tonic-gate 		 * Hmmm, let's do it!  Check for extensions (there should
1283*0Sstevel@tonic-gate 		 * be none), extract the fields, call ah_register_out(),
1284*0Sstevel@tonic-gate 		 * then either free or report an error.
1285*0Sstevel@tonic-gate 		 *
1286*0Sstevel@tonic-gate 		 * Keysock takes care of the PF_KEY bookkeeping for this.
1287*0Sstevel@tonic-gate 		 */
1288*0Sstevel@tonic-gate 		if (ah_register_out(samsg->sadb_msg_seq, samsg->sadb_msg_pid,
1289*0Sstevel@tonic-gate 		    ksi->ks_in_serial)) {
1290*0Sstevel@tonic-gate 			freemsg(mp);
1291*0Sstevel@tonic-gate 		} else {
1292*0Sstevel@tonic-gate 			/*
1293*0Sstevel@tonic-gate 			 * Only way this path hits is if there is a memory
1294*0Sstevel@tonic-gate 			 * failure.  It will not return B_FALSE because of
1295*0Sstevel@tonic-gate 			 * lack of ah_pfkey_q if I am in wput().
1296*0Sstevel@tonic-gate 			 */
1297*0Sstevel@tonic-gate 			sadb_pfkey_error(ah_pfkey_q, mp, ENOMEM, diagnostic,
1298*0Sstevel@tonic-gate 			    ksi->ks_in_serial);
1299*0Sstevel@tonic-gate 		}
1300*0Sstevel@tonic-gate 		break;
1301*0Sstevel@tonic-gate 	case SADB_UPDATE:
1302*0Sstevel@tonic-gate 		/*
1303*0Sstevel@tonic-gate 		 * Find a larval, if not there, find a full one and get
1304*0Sstevel@tonic-gate 		 * strict.
1305*0Sstevel@tonic-gate 		 */
1306*0Sstevel@tonic-gate 		error = ah_update_sa(mp, ksi, &diagnostic);
1307*0Sstevel@tonic-gate 		if (error != 0) {
1308*0Sstevel@tonic-gate 			sadb_pfkey_error(ah_pfkey_q, mp, error, diagnostic,
1309*0Sstevel@tonic-gate 			    ksi->ks_in_serial);
1310*0Sstevel@tonic-gate 		}
1311*0Sstevel@tonic-gate 		/* else ah_update_sa() took care of things. */
1312*0Sstevel@tonic-gate 		break;
1313*0Sstevel@tonic-gate 	case SADB_GETSPI:
1314*0Sstevel@tonic-gate 		/*
1315*0Sstevel@tonic-gate 		 * Reserve a new larval entry.
1316*0Sstevel@tonic-gate 		 */
1317*0Sstevel@tonic-gate 		ah_getspi(mp, ksi);
1318*0Sstevel@tonic-gate 		break;
1319*0Sstevel@tonic-gate 	case SADB_ACQUIRE:
1320*0Sstevel@tonic-gate 		/*
1321*0Sstevel@tonic-gate 		 * Find larval and/or ACQUIRE record and kill it (them), I'm
1322*0Sstevel@tonic-gate 		 * most likely an error.  Inbound ACQUIRE messages should only
1323*0Sstevel@tonic-gate 		 * have the base header.
1324*0Sstevel@tonic-gate 		 */
1325*0Sstevel@tonic-gate 		sadb_in_acquire(samsg, &ah_sadb, ah_pfkey_q);
1326*0Sstevel@tonic-gate 		freemsg(mp);
1327*0Sstevel@tonic-gate 		break;
1328*0Sstevel@tonic-gate 	case SADB_DUMP:
1329*0Sstevel@tonic-gate 		/*
1330*0Sstevel@tonic-gate 		 * Dump all entries.
1331*0Sstevel@tonic-gate 		 */
1332*0Sstevel@tonic-gate 		ah_dump(mp, ksi);
1333*0Sstevel@tonic-gate 		/* ah_dump will take care of the return message, etc. */
1334*0Sstevel@tonic-gate 		break;
1335*0Sstevel@tonic-gate 	case SADB_EXPIRE:
1336*0Sstevel@tonic-gate 		/* Should never reach me. */
1337*0Sstevel@tonic-gate 		sadb_pfkey_error(ah_pfkey_q, mp, EOPNOTSUPP, diagnostic,
1338*0Sstevel@tonic-gate 		    ksi->ks_in_serial);
1339*0Sstevel@tonic-gate 		break;
1340*0Sstevel@tonic-gate 	default:
1341*0Sstevel@tonic-gate 		sadb_pfkey_error(ah_pfkey_q, mp, EINVAL,
1342*0Sstevel@tonic-gate 		    SADB_X_DIAGNOSTIC_UNKNOWN_MSG, ksi->ks_in_serial);
1343*0Sstevel@tonic-gate 		break;
1344*0Sstevel@tonic-gate 	}
1345*0Sstevel@tonic-gate }
1346*0Sstevel@tonic-gate 
1347*0Sstevel@tonic-gate /*
1348*0Sstevel@tonic-gate  * Handle case where PF_KEY says it can't find a keysock for one of my
1349*0Sstevel@tonic-gate  * ACQUIRE messages.
1350*0Sstevel@tonic-gate  */
1351*0Sstevel@tonic-gate static void
1352*0Sstevel@tonic-gate ah_keysock_no_socket(mblk_t *mp)
1353*0Sstevel@tonic-gate {
1354*0Sstevel@tonic-gate 	sadb_msg_t *samsg;
1355*0Sstevel@tonic-gate 	keysock_out_err_t *kse = (keysock_out_err_t *)mp->b_rptr;
1356*0Sstevel@tonic-gate 
1357*0Sstevel@tonic-gate 	if (mp->b_cont == NULL) {
1358*0Sstevel@tonic-gate 		freemsg(mp);
1359*0Sstevel@tonic-gate 		return;
1360*0Sstevel@tonic-gate 	}
1361*0Sstevel@tonic-gate 	samsg = (sadb_msg_t *)mp->b_cont->b_rptr;
1362*0Sstevel@tonic-gate 
1363*0Sstevel@tonic-gate 	/*
1364*0Sstevel@tonic-gate 	 * If keysock can't find any registered, delete the acquire record
1365*0Sstevel@tonic-gate 	 * immediately, and handle errors.
1366*0Sstevel@tonic-gate 	 */
1367*0Sstevel@tonic-gate 	if (samsg->sadb_msg_type == SADB_ACQUIRE) {
1368*0Sstevel@tonic-gate 		samsg->sadb_msg_errno = kse->ks_err_errno;
1369*0Sstevel@tonic-gate 		samsg->sadb_msg_len = SADB_8TO64(sizeof (*samsg));
1370*0Sstevel@tonic-gate 		/*
1371*0Sstevel@tonic-gate 		 * Use the write-side of the ah_pfkey_q, in case there is
1372*0Sstevel@tonic-gate 		 * no ah_sadb.s_ip_q.
1373*0Sstevel@tonic-gate 		 */
1374*0Sstevel@tonic-gate 		sadb_in_acquire(samsg, &ah_sadb, WR(ah_pfkey_q));
1375*0Sstevel@tonic-gate 	}
1376*0Sstevel@tonic-gate 
1377*0Sstevel@tonic-gate 	freemsg(mp);
1378*0Sstevel@tonic-gate }
1379*0Sstevel@tonic-gate 
1380*0Sstevel@tonic-gate /*
1381*0Sstevel@tonic-gate  * First-cut reality check for an inbound PF_KEY message.
1382*0Sstevel@tonic-gate  */
1383*0Sstevel@tonic-gate static boolean_t
1384*0Sstevel@tonic-gate ah_pfkey_reality_failures(mblk_t *mp, keysock_in_t *ksi)
1385*0Sstevel@tonic-gate {
1386*0Sstevel@tonic-gate 	int diagnostic;
1387*0Sstevel@tonic-gate 
1388*0Sstevel@tonic-gate 	if (mp->b_cont == NULL) {
1389*0Sstevel@tonic-gate 		freemsg(mp);
1390*0Sstevel@tonic-gate 		return (B_TRUE);
1391*0Sstevel@tonic-gate 	}
1392*0Sstevel@tonic-gate 
1393*0Sstevel@tonic-gate 	if (ksi->ks_in_extv[SADB_EXT_KEY_ENCRYPT] != NULL) {
1394*0Sstevel@tonic-gate 		diagnostic = SADB_X_DIAGNOSTIC_EKEY_PRESENT;
1395*0Sstevel@tonic-gate 		goto badmsg;
1396*0Sstevel@tonic-gate 	}
1397*0Sstevel@tonic-gate 	if (ksi->ks_in_extv[SADB_EXT_PROPOSAL] != NULL) {
1398*0Sstevel@tonic-gate 		diagnostic = SADB_X_DIAGNOSTIC_PROP_PRESENT;
1399*0Sstevel@tonic-gate 		goto badmsg;
1400*0Sstevel@tonic-gate 	}
1401*0Sstevel@tonic-gate 	if (ksi->ks_in_extv[SADB_EXT_SUPPORTED_AUTH] != NULL ||
1402*0Sstevel@tonic-gate 	    ksi->ks_in_extv[SADB_EXT_SUPPORTED_ENCRYPT] != NULL) {
1403*0Sstevel@tonic-gate 		diagnostic = SADB_X_DIAGNOSTIC_SUPP_PRESENT;
1404*0Sstevel@tonic-gate 		goto badmsg;
1405*0Sstevel@tonic-gate 	}
1406*0Sstevel@tonic-gate 	if (ksi->ks_in_srctype == KS_IN_ADDR_MBCAST) {
1407*0Sstevel@tonic-gate 		diagnostic = SADB_X_DIAGNOSTIC_BAD_SRC;
1408*0Sstevel@tonic-gate 		goto badmsg;
1409*0Sstevel@tonic-gate 	}
1410*0Sstevel@tonic-gate 	if (ksi->ks_in_dsttype == KS_IN_ADDR_UNSPEC) {
1411*0Sstevel@tonic-gate 		diagnostic = SADB_X_DIAGNOSTIC_BAD_DST;
1412*0Sstevel@tonic-gate 		goto badmsg;
1413*0Sstevel@tonic-gate 	}
1414*0Sstevel@tonic-gate 
1415*0Sstevel@tonic-gate 	return (B_FALSE);	/* False ==> no failures */
1416*0Sstevel@tonic-gate 
1417*0Sstevel@tonic-gate badmsg:
1418*0Sstevel@tonic-gate 	sadb_pfkey_error(ah_pfkey_q, mp, EINVAL, diagnostic, ksi->ks_in_serial);
1419*0Sstevel@tonic-gate 	return (B_TRUE);	/* True ==> failures */
1420*0Sstevel@tonic-gate }
1421*0Sstevel@tonic-gate 
1422*0Sstevel@tonic-gate /*
1423*0Sstevel@tonic-gate  * AH module write put routine.
1424*0Sstevel@tonic-gate  */
1425*0Sstevel@tonic-gate static void
1426*0Sstevel@tonic-gate ipsecah_wput(queue_t *q, mblk_t *mp)
1427*0Sstevel@tonic-gate {
1428*0Sstevel@tonic-gate 	ipsec_info_t *ii;
1429*0Sstevel@tonic-gate 	keysock_in_t *ksi;
1430*0Sstevel@tonic-gate 	int rc;
1431*0Sstevel@tonic-gate 	struct iocblk *iocp;
1432*0Sstevel@tonic-gate 
1433*0Sstevel@tonic-gate 	ah3dbg(("In ah_wput().\n"));
1434*0Sstevel@tonic-gate 
1435*0Sstevel@tonic-gate 	/* NOTE:  Each case must take care of freeing or passing mp. */
1436*0Sstevel@tonic-gate 	switch (mp->b_datap->db_type) {
1437*0Sstevel@tonic-gate 	case M_CTL:
1438*0Sstevel@tonic-gate 		if ((mp->b_wptr - mp->b_rptr) < sizeof (ipsec_info_t)) {
1439*0Sstevel@tonic-gate 			/* Not big enough message. */
1440*0Sstevel@tonic-gate 			freemsg(mp);
1441*0Sstevel@tonic-gate 			break;
1442*0Sstevel@tonic-gate 		}
1443*0Sstevel@tonic-gate 		ii = (ipsec_info_t *)mp->b_rptr;
1444*0Sstevel@tonic-gate 
1445*0Sstevel@tonic-gate 		switch (ii->ipsec_info_type) {
1446*0Sstevel@tonic-gate 		case KEYSOCK_OUT_ERR:
1447*0Sstevel@tonic-gate 			ah1dbg(("Got KEYSOCK_OUT_ERR message.\n"));
1448*0Sstevel@tonic-gate 			ah_keysock_no_socket(mp);
1449*0Sstevel@tonic-gate 			break;
1450*0Sstevel@tonic-gate 		case KEYSOCK_IN:
1451*0Sstevel@tonic-gate 			AH_BUMP_STAT(keysock_in);
1452*0Sstevel@tonic-gate 			ah3dbg(("Got KEYSOCK_IN message.\n"));
1453*0Sstevel@tonic-gate 			ksi = (keysock_in_t *)ii;
1454*0Sstevel@tonic-gate 			/*
1455*0Sstevel@tonic-gate 			 * Some common reality checks.
1456*0Sstevel@tonic-gate 			 */
1457*0Sstevel@tonic-gate 
1458*0Sstevel@tonic-gate 			if (ah_pfkey_reality_failures(mp, ksi))
1459*0Sstevel@tonic-gate 				return;
1460*0Sstevel@tonic-gate 
1461*0Sstevel@tonic-gate 			/*
1462*0Sstevel@tonic-gate 			 * Use 'q' instead of ah_sadb.s_ip_q, since
1463*0Sstevel@tonic-gate 			 * it's the write side already, and it'll go
1464*0Sstevel@tonic-gate 			 * down to IP.  Use ah_pfkey_q because we
1465*0Sstevel@tonic-gate 			 * wouldn't get here if that weren't set, and
1466*0Sstevel@tonic-gate 			 * the RD(q) has been done already.
1467*0Sstevel@tonic-gate 			 */
1468*0Sstevel@tonic-gate 			if (ksi->ks_in_srctype == KS_IN_ADDR_UNKNOWN) {
1469*0Sstevel@tonic-gate 				rc = sadb_addrcheck(q, ah_pfkey_q, mp,
1470*0Sstevel@tonic-gate 				    ksi->ks_in_extv[SADB_EXT_ADDRESS_SRC],
1471*0Sstevel@tonic-gate 				    ksi->ks_in_serial);
1472*0Sstevel@tonic-gate 				if (rc == KS_IN_ADDR_UNKNOWN)
1473*0Sstevel@tonic-gate 					return;
1474*0Sstevel@tonic-gate 				else
1475*0Sstevel@tonic-gate 					ksi->ks_in_srctype = rc;
1476*0Sstevel@tonic-gate 			}
1477*0Sstevel@tonic-gate 			if (ksi->ks_in_dsttype == KS_IN_ADDR_UNKNOWN) {
1478*0Sstevel@tonic-gate 				rc = sadb_addrcheck(q, ah_pfkey_q, mp,
1479*0Sstevel@tonic-gate 				    ksi->ks_in_extv[SADB_EXT_ADDRESS_DST],
1480*0Sstevel@tonic-gate 				    ksi->ks_in_serial);
1481*0Sstevel@tonic-gate 				if (rc == KS_IN_ADDR_UNKNOWN)
1482*0Sstevel@tonic-gate 					return;
1483*0Sstevel@tonic-gate 				else
1484*0Sstevel@tonic-gate 					ksi->ks_in_dsttype = rc;
1485*0Sstevel@tonic-gate 			}
1486*0Sstevel@tonic-gate 			/*
1487*0Sstevel@tonic-gate 			 * XXX Proxy may be a different address family.
1488*0Sstevel@tonic-gate 			 */
1489*0Sstevel@tonic-gate 			if (ksi->ks_in_proxytype == KS_IN_ADDR_UNKNOWN) {
1490*0Sstevel@tonic-gate 				rc = sadb_addrcheck(q, ah_pfkey_q, mp,
1491*0Sstevel@tonic-gate 				    ksi->ks_in_extv[SADB_EXT_ADDRESS_PROXY],
1492*0Sstevel@tonic-gate 				    ksi->ks_in_serial);
1493*0Sstevel@tonic-gate 				if (rc == KS_IN_ADDR_UNKNOWN)
1494*0Sstevel@tonic-gate 					return;
1495*0Sstevel@tonic-gate 				else
1496*0Sstevel@tonic-gate 					ksi->ks_in_proxytype = rc;
1497*0Sstevel@tonic-gate 			}
1498*0Sstevel@tonic-gate 			ah_parse_pfkey(mp);
1499*0Sstevel@tonic-gate 			break;
1500*0Sstevel@tonic-gate 		case KEYSOCK_HELLO:
1501*0Sstevel@tonic-gate 			sadb_keysock_hello(&ah_pfkey_q, q, mp,
1502*0Sstevel@tonic-gate 			    ah_ager, &ah_event, SADB_SATYPE_AH);
1503*0Sstevel@tonic-gate 			break;
1504*0Sstevel@tonic-gate 		default:
1505*0Sstevel@tonic-gate 			ah1dbg(("Got M_CTL from above of 0x%x.\n",
1506*0Sstevel@tonic-gate 			    ii->ipsec_info_type));
1507*0Sstevel@tonic-gate 			freemsg(mp);
1508*0Sstevel@tonic-gate 			break;
1509*0Sstevel@tonic-gate 		}
1510*0Sstevel@tonic-gate 		break;
1511*0Sstevel@tonic-gate 	case M_IOCTL:
1512*0Sstevel@tonic-gate 		iocp = (struct iocblk *)mp->b_rptr;
1513*0Sstevel@tonic-gate 		switch (iocp->ioc_cmd) {
1514*0Sstevel@tonic-gate 		case ND_SET:
1515*0Sstevel@tonic-gate 		case ND_GET:
1516*0Sstevel@tonic-gate 			if (nd_getset(q, ipsecah_g_nd, mp)) {
1517*0Sstevel@tonic-gate 				qreply(q, mp);
1518*0Sstevel@tonic-gate 				return;
1519*0Sstevel@tonic-gate 			} else {
1520*0Sstevel@tonic-gate 				iocp->ioc_error = ENOENT;
1521*0Sstevel@tonic-gate 			}
1522*0Sstevel@tonic-gate 			/* FALLTHRU */
1523*0Sstevel@tonic-gate 		default:
1524*0Sstevel@tonic-gate 			/* We really don't support any other ioctls, do we? */
1525*0Sstevel@tonic-gate 
1526*0Sstevel@tonic-gate 			/* Return EINVAL */
1527*0Sstevel@tonic-gate 			if (iocp->ioc_error != ENOENT)
1528*0Sstevel@tonic-gate 				iocp->ioc_error = EINVAL;
1529*0Sstevel@tonic-gate 			iocp->ioc_count = 0;
1530*0Sstevel@tonic-gate 			mp->b_datap->db_type = M_IOCACK;
1531*0Sstevel@tonic-gate 			qreply(q, mp);
1532*0Sstevel@tonic-gate 			return;
1533*0Sstevel@tonic-gate 		}
1534*0Sstevel@tonic-gate 	default:
1535*0Sstevel@tonic-gate 		ah3dbg(("Got default message, type %d, passing to IP.\n",
1536*0Sstevel@tonic-gate 		    mp->b_datap->db_type));
1537*0Sstevel@tonic-gate 		putnext(q, mp);
1538*0Sstevel@tonic-gate 	}
1539*0Sstevel@tonic-gate }
1540*0Sstevel@tonic-gate 
1541*0Sstevel@tonic-gate /*
1542*0Sstevel@tonic-gate  * Updating use times can be tricky business if the ipsa_haspeer flag is
1543*0Sstevel@tonic-gate  * set.  This function is called once in an SA's lifetime.
1544*0Sstevel@tonic-gate  *
1545*0Sstevel@tonic-gate  * Caller has to REFRELE "assoc" which is passed in.  This function has
1546*0Sstevel@tonic-gate  * to REFRELE any peer SA that is obtained.
1547*0Sstevel@tonic-gate  */
1548*0Sstevel@tonic-gate static void
1549*0Sstevel@tonic-gate ah_set_usetime(ipsa_t *assoc, boolean_t inbound)
1550*0Sstevel@tonic-gate {
1551*0Sstevel@tonic-gate 	ipsa_t *inassoc, *outassoc;
1552*0Sstevel@tonic-gate 	isaf_t *bucket;
1553*0Sstevel@tonic-gate 	sadb_t *sp;
1554*0Sstevel@tonic-gate 	int outhash;
1555*0Sstevel@tonic-gate 	boolean_t isv6;
1556*0Sstevel@tonic-gate 
1557*0Sstevel@tonic-gate 	/* No peer?  No problem! */
1558*0Sstevel@tonic-gate 	if (!assoc->ipsa_haspeer) {
1559*0Sstevel@tonic-gate 		sadb_set_usetime(assoc);
1560*0Sstevel@tonic-gate 		return;
1561*0Sstevel@tonic-gate 	}
1562*0Sstevel@tonic-gate 
1563*0Sstevel@tonic-gate 	/*
1564*0Sstevel@tonic-gate 	 * Otherwise, we want to grab both the original assoc and its peer.
1565*0Sstevel@tonic-gate 	 * There might be a race for this, but if it's a real race, the times
1566*0Sstevel@tonic-gate 	 * will be out-of-synch by at most a second, and since our time
1567*0Sstevel@tonic-gate 	 * granularity is a second, this won't be a problem.
1568*0Sstevel@tonic-gate 	 *
1569*0Sstevel@tonic-gate 	 * If we need tight synchronization on the peer SA, then we need to
1570*0Sstevel@tonic-gate 	 * reconsider.
1571*0Sstevel@tonic-gate 	 */
1572*0Sstevel@tonic-gate 
1573*0Sstevel@tonic-gate 	/* Use address family to select IPv6/IPv4 */
1574*0Sstevel@tonic-gate 	isv6 = (assoc->ipsa_addrfam == AF_INET6);
1575*0Sstevel@tonic-gate 	if (isv6) {
1576*0Sstevel@tonic-gate 		sp = &ah_sadb.s_v6;
1577*0Sstevel@tonic-gate 	} else {
1578*0Sstevel@tonic-gate 		sp = &ah_sadb.s_v4;
1579*0Sstevel@tonic-gate 		ASSERT(assoc->ipsa_addrfam == AF_INET);
1580*0Sstevel@tonic-gate 	}
1581*0Sstevel@tonic-gate 	if (inbound) {
1582*0Sstevel@tonic-gate 		inassoc = assoc;
1583*0Sstevel@tonic-gate 		if (isv6)
1584*0Sstevel@tonic-gate 			outhash = OUTBOUND_HASH_V6(*((in6_addr_t *)
1585*0Sstevel@tonic-gate 			    &inassoc->ipsa_dstaddr));
1586*0Sstevel@tonic-gate 		else
1587*0Sstevel@tonic-gate 			outhash = OUTBOUND_HASH_V4(*((ipaddr_t *)
1588*0Sstevel@tonic-gate 				&inassoc->ipsa_dstaddr));
1589*0Sstevel@tonic-gate 		bucket = &sp->sdb_of[outhash];
1590*0Sstevel@tonic-gate 
1591*0Sstevel@tonic-gate 		mutex_enter(&bucket->isaf_lock);
1592*0Sstevel@tonic-gate 		outassoc = ipsec_getassocbyspi(bucket, inassoc->ipsa_spi,
1593*0Sstevel@tonic-gate 		    inassoc->ipsa_srcaddr, inassoc->ipsa_dstaddr,
1594*0Sstevel@tonic-gate 		    inassoc->ipsa_addrfam);
1595*0Sstevel@tonic-gate 		mutex_exit(&bucket->isaf_lock);
1596*0Sstevel@tonic-gate 		if (outassoc == NULL) {
1597*0Sstevel@tonic-gate 			/* Q: Do we wish to set haspeer == B_FALSE? */
1598*0Sstevel@tonic-gate 			ah0dbg(("ah_set_usetime: "
1599*0Sstevel@tonic-gate 			    "can't find peer for inbound.\n"));
1600*0Sstevel@tonic-gate 			sadb_set_usetime(inassoc);
1601*0Sstevel@tonic-gate 			return;
1602*0Sstevel@tonic-gate 		}
1603*0Sstevel@tonic-gate 	} else {
1604*0Sstevel@tonic-gate 		outassoc = assoc;
1605*0Sstevel@tonic-gate 		bucket = &sp->sdb_if[INBOUND_HASH(outassoc->ipsa_spi)];
1606*0Sstevel@tonic-gate 		mutex_enter(&bucket->isaf_lock);
1607*0Sstevel@tonic-gate 		inassoc = ipsec_getassocbyspi(bucket, outassoc->ipsa_spi,
1608*0Sstevel@tonic-gate 		    outassoc->ipsa_srcaddr, outassoc->ipsa_dstaddr,
1609*0Sstevel@tonic-gate 		    outassoc->ipsa_addrfam);
1610*0Sstevel@tonic-gate 		mutex_exit(&bucket->isaf_lock);
1611*0Sstevel@tonic-gate 		if (inassoc == NULL) {
1612*0Sstevel@tonic-gate 			/* Q: Do we wish to set haspeer == B_FALSE? */
1613*0Sstevel@tonic-gate 			ah0dbg(("ah_set_usetime: "
1614*0Sstevel@tonic-gate 			    "can't find peer for outbound.\n"));
1615*0Sstevel@tonic-gate 			sadb_set_usetime(outassoc);
1616*0Sstevel@tonic-gate 			return;
1617*0Sstevel@tonic-gate 		}
1618*0Sstevel@tonic-gate 	}
1619*0Sstevel@tonic-gate 
1620*0Sstevel@tonic-gate 	/* Update usetime on both. */
1621*0Sstevel@tonic-gate 	sadb_set_usetime(inassoc);
1622*0Sstevel@tonic-gate 	sadb_set_usetime(outassoc);
1623*0Sstevel@tonic-gate 
1624*0Sstevel@tonic-gate 	/*
1625*0Sstevel@tonic-gate 	 * REFRELE any peer SA.
1626*0Sstevel@tonic-gate 	 *
1627*0Sstevel@tonic-gate 	 * Because of the multi-line macro nature of IPSA_REFRELE, keep
1628*0Sstevel@tonic-gate 	 * them in { }.
1629*0Sstevel@tonic-gate 	 */
1630*0Sstevel@tonic-gate 	if (inbound) {
1631*0Sstevel@tonic-gate 		IPSA_REFRELE(outassoc);
1632*0Sstevel@tonic-gate 	} else {
1633*0Sstevel@tonic-gate 		IPSA_REFRELE(inassoc);
1634*0Sstevel@tonic-gate 	}
1635*0Sstevel@tonic-gate }
1636*0Sstevel@tonic-gate 
1637*0Sstevel@tonic-gate /*
1638*0Sstevel@tonic-gate  * Add a number of bytes to what the SA has protected so far.  Return
1639*0Sstevel@tonic-gate  * B_TRUE if the SA can still protect that many bytes.
1640*0Sstevel@tonic-gate  *
1641*0Sstevel@tonic-gate  * Caller must REFRELE the passed-in assoc.  This function must REFRELE
1642*0Sstevel@tonic-gate  * any obtained peer SA.
1643*0Sstevel@tonic-gate  */
1644*0Sstevel@tonic-gate static boolean_t
1645*0Sstevel@tonic-gate ah_age_bytes(ipsa_t *assoc, uint64_t bytes, boolean_t inbound)
1646*0Sstevel@tonic-gate {
1647*0Sstevel@tonic-gate 	ipsa_t *inassoc, *outassoc;
1648*0Sstevel@tonic-gate 	isaf_t *bucket;
1649*0Sstevel@tonic-gate 	boolean_t inrc, outrc, isv6;
1650*0Sstevel@tonic-gate 	sadb_t *sp;
1651*0Sstevel@tonic-gate 	int outhash;
1652*0Sstevel@tonic-gate 
1653*0Sstevel@tonic-gate 	/* No peer?  No problem! */
1654*0Sstevel@tonic-gate 	if (!assoc->ipsa_haspeer) {
1655*0Sstevel@tonic-gate 		return (sadb_age_bytes(ah_pfkey_q, assoc, bytes,
1656*0Sstevel@tonic-gate 		    B_TRUE));
1657*0Sstevel@tonic-gate 	}
1658*0Sstevel@tonic-gate 
1659*0Sstevel@tonic-gate 	/*
1660*0Sstevel@tonic-gate 	 * Otherwise, we want to grab both the original assoc and its peer.
1661*0Sstevel@tonic-gate 	 * There might be a race for this, but if it's a real race, two
1662*0Sstevel@tonic-gate 	 * expire messages may occur.  We limit this by only sending the
1663*0Sstevel@tonic-gate 	 * expire message on one of the peers, we'll pick the inbound
1664*0Sstevel@tonic-gate 	 * arbitrarily.
1665*0Sstevel@tonic-gate 	 *
1666*0Sstevel@tonic-gate 	 * If we need tight synchronization on the peer SA, then we need to
1667*0Sstevel@tonic-gate 	 * reconsider.
1668*0Sstevel@tonic-gate 	 */
1669*0Sstevel@tonic-gate 
1670*0Sstevel@tonic-gate 	/* Pick v4/v6 bucket based on addrfam. */
1671*0Sstevel@tonic-gate 	isv6 = (assoc->ipsa_addrfam == AF_INET6);
1672*0Sstevel@tonic-gate 	if (isv6) {
1673*0Sstevel@tonic-gate 		sp = &ah_sadb.s_v6;
1674*0Sstevel@tonic-gate 	} else {
1675*0Sstevel@tonic-gate 		sp = &ah_sadb.s_v4;
1676*0Sstevel@tonic-gate 		ASSERT(assoc->ipsa_addrfam == AF_INET);
1677*0Sstevel@tonic-gate 	}
1678*0Sstevel@tonic-gate 	if (inbound) {
1679*0Sstevel@tonic-gate 		inassoc = assoc;
1680*0Sstevel@tonic-gate 		if (isv6)
1681*0Sstevel@tonic-gate 			outhash = OUTBOUND_HASH_V6(*((in6_addr_t *)
1682*0Sstevel@tonic-gate 			    &inassoc->ipsa_dstaddr));
1683*0Sstevel@tonic-gate 		else
1684*0Sstevel@tonic-gate 			outhash = OUTBOUND_HASH_V4(*((ipaddr_t *)
1685*0Sstevel@tonic-gate 				&inassoc->ipsa_dstaddr));
1686*0Sstevel@tonic-gate 		bucket = &sp->sdb_of[outhash];
1687*0Sstevel@tonic-gate 		mutex_enter(&bucket->isaf_lock);
1688*0Sstevel@tonic-gate 		outassoc = ipsec_getassocbyspi(bucket, inassoc->ipsa_spi,
1689*0Sstevel@tonic-gate 		    inassoc->ipsa_srcaddr, inassoc->ipsa_dstaddr,
1690*0Sstevel@tonic-gate 		    inassoc->ipsa_addrfam);
1691*0Sstevel@tonic-gate 		mutex_exit(&bucket->isaf_lock);
1692*0Sstevel@tonic-gate 		if (outassoc == NULL) {
1693*0Sstevel@tonic-gate 			/* Q: Do we wish to set haspeer == B_FALSE? */
1694*0Sstevel@tonic-gate 			ah0dbg(("ah_age_bytes: "
1695*0Sstevel@tonic-gate 			    "can't find peer for inbound.\n"));
1696*0Sstevel@tonic-gate 			return (sadb_age_bytes(ah_pfkey_q, inassoc,
1697*0Sstevel@tonic-gate 			    bytes, B_TRUE));
1698*0Sstevel@tonic-gate 		}
1699*0Sstevel@tonic-gate 	} else {
1700*0Sstevel@tonic-gate 		outassoc = assoc;
1701*0Sstevel@tonic-gate 		bucket = &sp->sdb_if[INBOUND_HASH(outassoc->ipsa_spi)];
1702*0Sstevel@tonic-gate 		mutex_enter(&bucket->isaf_lock);
1703*0Sstevel@tonic-gate 		inassoc = ipsec_getassocbyspi(bucket, outassoc->ipsa_spi,
1704*0Sstevel@tonic-gate 		    outassoc->ipsa_srcaddr, outassoc->ipsa_dstaddr,
1705*0Sstevel@tonic-gate 		    outassoc->ipsa_addrfam);
1706*0Sstevel@tonic-gate 		mutex_exit(&bucket->isaf_lock);
1707*0Sstevel@tonic-gate 		if (inassoc == NULL) {
1708*0Sstevel@tonic-gate 			/* Q: Do we wish to set haspeer == B_FALSE? */
1709*0Sstevel@tonic-gate 			ah0dbg(("ah_age_bytes: "
1710*0Sstevel@tonic-gate 			    "can't find peer for outbound.\n"));
1711*0Sstevel@tonic-gate 			return (sadb_age_bytes(ah_pfkey_q, outassoc,
1712*0Sstevel@tonic-gate 			    bytes, B_TRUE));
1713*0Sstevel@tonic-gate 		}
1714*0Sstevel@tonic-gate 	}
1715*0Sstevel@tonic-gate 
1716*0Sstevel@tonic-gate 	inrc = sadb_age_bytes(ah_pfkey_q, inassoc, bytes, B_TRUE);
1717*0Sstevel@tonic-gate 	outrc = sadb_age_bytes(ah_pfkey_q, outassoc, bytes, B_FALSE);
1718*0Sstevel@tonic-gate 
1719*0Sstevel@tonic-gate 	/*
1720*0Sstevel@tonic-gate 	 * REFRELE any peer SA.
1721*0Sstevel@tonic-gate 	 *
1722*0Sstevel@tonic-gate 	 * Because of the multi-line macro nature of IPSA_REFRELE, keep
1723*0Sstevel@tonic-gate 	 * them in { }.
1724*0Sstevel@tonic-gate 	 */
1725*0Sstevel@tonic-gate 	if (inbound) {
1726*0Sstevel@tonic-gate 		IPSA_REFRELE(outassoc);
1727*0Sstevel@tonic-gate 	} else {
1728*0Sstevel@tonic-gate 		IPSA_REFRELE(inassoc);
1729*0Sstevel@tonic-gate 	}
1730*0Sstevel@tonic-gate 
1731*0Sstevel@tonic-gate 	return (inrc && outrc);
1732*0Sstevel@tonic-gate }
1733*0Sstevel@tonic-gate 
1734*0Sstevel@tonic-gate /*
1735*0Sstevel@tonic-gate  * Perform the really difficult work of inserting the proposed situation.
1736*0Sstevel@tonic-gate  * Called while holding the algorithm lock.
1737*0Sstevel@tonic-gate  */
1738*0Sstevel@tonic-gate static void
1739*0Sstevel@tonic-gate ah_insert_prop(sadb_prop_t *prop, ipsacq_t *acqrec, uint_t combs)
1740*0Sstevel@tonic-gate {
1741*0Sstevel@tonic-gate 	sadb_comb_t *comb = (sadb_comb_t *)(prop + 1);
1742*0Sstevel@tonic-gate 	ipsec_out_t *io;
1743*0Sstevel@tonic-gate 	ipsec_action_t *ap;
1744*0Sstevel@tonic-gate 	ipsec_prot_t *prot;
1745*0Sstevel@tonic-gate 	io = (ipsec_out_t *)acqrec->ipsacq_mp->b_rptr;
1746*0Sstevel@tonic-gate 
1747*0Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&alg_lock));
1748*0Sstevel@tonic-gate 	ASSERT(io->ipsec_out_type == IPSEC_OUT);
1749*0Sstevel@tonic-gate 
1750*0Sstevel@tonic-gate 	prop->sadb_prop_exttype = SADB_EXT_PROPOSAL;
1751*0Sstevel@tonic-gate 	prop->sadb_prop_len = SADB_8TO64(sizeof (sadb_prop_t));
1752*0Sstevel@tonic-gate 	*(uint32_t *)(&prop->sadb_prop_replay) = 0;	/* Quick zero-out! */
1753*0Sstevel@tonic-gate 
1754*0Sstevel@tonic-gate 	prop->sadb_prop_replay = ipsecah_replay_size;
1755*0Sstevel@tonic-gate 
1756*0Sstevel@tonic-gate 	/*
1757*0Sstevel@tonic-gate 	 * Based upon algorithm properties, and what-not, prioritize a
1758*0Sstevel@tonic-gate 	 * proposal, based on the ordering of the ah algorithms in the
1759*0Sstevel@tonic-gate 	 * alternatives presented in the policy rule passed down
1760*0Sstevel@tonic-gate 	 * through the ipsec_out_t and attached to the acquire record.
1761*0Sstevel@tonic-gate 	 */
1762*0Sstevel@tonic-gate 
1763*0Sstevel@tonic-gate 	for (ap = acqrec->ipsacq_act; ap != NULL;
1764*0Sstevel@tonic-gate 	    ap = ap->ipa_next) {
1765*0Sstevel@tonic-gate 		ipsec_alginfo_t *aalg;
1766*0Sstevel@tonic-gate 
1767*0Sstevel@tonic-gate 		if ((ap->ipa_act.ipa_type != IPSEC_POLICY_APPLY) ||
1768*0Sstevel@tonic-gate 		    (!ap->ipa_act.ipa_apply.ipp_use_ah))
1769*0Sstevel@tonic-gate 			continue;
1770*0Sstevel@tonic-gate 
1771*0Sstevel@tonic-gate 		prot = &ap->ipa_act.ipa_apply;
1772*0Sstevel@tonic-gate 
1773*0Sstevel@tonic-gate 		ASSERT(prot->ipp_auth_alg > 0);
1774*0Sstevel@tonic-gate 
1775*0Sstevel@tonic-gate 		aalg = ipsec_alglists[IPSEC_ALG_AUTH][prot->ipp_auth_alg];
1776*0Sstevel@tonic-gate 		if (aalg == NULL || !ALG_VALID(aalg))
1777*0Sstevel@tonic-gate 			continue;
1778*0Sstevel@tonic-gate 
1779*0Sstevel@tonic-gate 		/* XXX check aalg for duplicates??.. */
1780*0Sstevel@tonic-gate 
1781*0Sstevel@tonic-gate 		comb->sadb_comb_flags = 0;
1782*0Sstevel@tonic-gate 		comb->sadb_comb_reserved = 0;
1783*0Sstevel@tonic-gate 		comb->sadb_comb_encrypt = 0;
1784*0Sstevel@tonic-gate 		comb->sadb_comb_encrypt_minbits = 0;
1785*0Sstevel@tonic-gate 		comb->sadb_comb_encrypt_maxbits = 0;
1786*0Sstevel@tonic-gate 
1787*0Sstevel@tonic-gate 		comb->sadb_comb_auth = aalg->alg_id;
1788*0Sstevel@tonic-gate 		comb->sadb_comb_auth_minbits = prot->ipp_ah_minbits;
1789*0Sstevel@tonic-gate 		comb->sadb_comb_auth_maxbits = prot->ipp_ah_maxbits;
1790*0Sstevel@tonic-gate 
1791*0Sstevel@tonic-gate 		/*
1792*0Sstevel@tonic-gate 		 * The following may be based on algorithm
1793*0Sstevel@tonic-gate 		 * properties, but in the meantime, we just pick
1794*0Sstevel@tonic-gate 		 * some good, sensible numbers.  Key mgmt. can
1795*0Sstevel@tonic-gate 		 * (and perhaps should) be the place to finalize
1796*0Sstevel@tonic-gate 		 * such decisions.
1797*0Sstevel@tonic-gate 		 */
1798*0Sstevel@tonic-gate 
1799*0Sstevel@tonic-gate 		/*
1800*0Sstevel@tonic-gate 		 * No limits on allocations, since we really don't
1801*0Sstevel@tonic-gate 		 * support that concept currently.
1802*0Sstevel@tonic-gate 		 */
1803*0Sstevel@tonic-gate 		comb->sadb_comb_soft_allocations = 0;
1804*0Sstevel@tonic-gate 		comb->sadb_comb_hard_allocations = 0;
1805*0Sstevel@tonic-gate 
1806*0Sstevel@tonic-gate 		/*
1807*0Sstevel@tonic-gate 		 * These may want to come from policy rule..
1808*0Sstevel@tonic-gate 		 */
1809*0Sstevel@tonic-gate 		comb->sadb_comb_soft_bytes = ipsecah_default_soft_bytes;
1810*0Sstevel@tonic-gate 		comb->sadb_comb_hard_bytes = ipsecah_default_hard_bytes;
1811*0Sstevel@tonic-gate 		comb->sadb_comb_soft_addtime = ipsecah_default_soft_addtime;
1812*0Sstevel@tonic-gate 		comb->sadb_comb_hard_addtime = ipsecah_default_hard_addtime;
1813*0Sstevel@tonic-gate 		comb->sadb_comb_soft_usetime = ipsecah_default_soft_usetime;
1814*0Sstevel@tonic-gate 		comb->sadb_comb_hard_usetime = ipsecah_default_hard_usetime;
1815*0Sstevel@tonic-gate 
1816*0Sstevel@tonic-gate 		prop->sadb_prop_len += SADB_8TO64(sizeof (*comb));
1817*0Sstevel@tonic-gate 		if (--combs == 0)
1818*0Sstevel@tonic-gate 			return;	/* out of space.. */
1819*0Sstevel@tonic-gate 		comb++;
1820*0Sstevel@tonic-gate 	}
1821*0Sstevel@tonic-gate }
1822*0Sstevel@tonic-gate 
1823*0Sstevel@tonic-gate /*
1824*0Sstevel@tonic-gate  * Prepare and actually send the SADB_ACQUIRE message to PF_KEY.
1825*0Sstevel@tonic-gate  */
1826*0Sstevel@tonic-gate static void
1827*0Sstevel@tonic-gate ah_send_acquire(ipsacq_t *acqrec, mblk_t *extended)
1828*0Sstevel@tonic-gate {
1829*0Sstevel@tonic-gate 	mblk_t *pfkeymp, *msgmp;
1830*0Sstevel@tonic-gate 	uint_t allocsize, combs;
1831*0Sstevel@tonic-gate 	sadb_msg_t *samsg;
1832*0Sstevel@tonic-gate 	sadb_prop_t *prop;
1833*0Sstevel@tonic-gate 	uint8_t *cur, *end;
1834*0Sstevel@tonic-gate 
1835*0Sstevel@tonic-gate 	AH_BUMP_STAT(acquire_requests);
1836*0Sstevel@tonic-gate 
1837*0Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&acqrec->ipsacq_lock));
1838*0Sstevel@tonic-gate 
1839*0Sstevel@tonic-gate 	pfkeymp = sadb_keysock_out(0);
1840*0Sstevel@tonic-gate 	if (pfkeymp == NULL) {
1841*0Sstevel@tonic-gate 		ah1dbg(("ah_send_acquire: 1st allocb() failed.\n"));
1842*0Sstevel@tonic-gate 		/* Just bail. */
1843*0Sstevel@tonic-gate 		goto done;
1844*0Sstevel@tonic-gate 	}
1845*0Sstevel@tonic-gate 
1846*0Sstevel@tonic-gate 	/*
1847*0Sstevel@tonic-gate 	 * First, allocate a basic ACQUIRE message.  Beyond that,
1848*0Sstevel@tonic-gate 	 * you need to extract certificate info from
1849*0Sstevel@tonic-gate 	 */
1850*0Sstevel@tonic-gate 	allocsize = sizeof (sadb_msg_t) + sizeof (sadb_address_t) +
1851*0Sstevel@tonic-gate 	    sizeof (sadb_address_t) + sizeof (sadb_prop_t);
1852*0Sstevel@tonic-gate 
1853*0Sstevel@tonic-gate 	switch (acqrec->ipsacq_addrfam) {
1854*0Sstevel@tonic-gate 	case AF_INET:
1855*0Sstevel@tonic-gate 		allocsize += 2 * sizeof (struct sockaddr_in);
1856*0Sstevel@tonic-gate 		break;
1857*0Sstevel@tonic-gate 	case AF_INET6:
1858*0Sstevel@tonic-gate 		allocsize += 2 * sizeof (struct sockaddr_in6);
1859*0Sstevel@tonic-gate 		break;
1860*0Sstevel@tonic-gate 	}
1861*0Sstevel@tonic-gate 
1862*0Sstevel@tonic-gate 	mutex_enter(&alg_lock);
1863*0Sstevel@tonic-gate 
1864*0Sstevel@tonic-gate 	combs = ipsec_nalgs[IPSEC_ALG_AUTH];
1865*0Sstevel@tonic-gate 
1866*0Sstevel@tonic-gate 	allocsize += combs * sizeof (sadb_comb_t);
1867*0Sstevel@tonic-gate 
1868*0Sstevel@tonic-gate 	/*
1869*0Sstevel@tonic-gate 	 * XXX If there are:
1870*0Sstevel@tonic-gate 	 *	certificate IDs
1871*0Sstevel@tonic-gate 	 *	proxy address
1872*0Sstevel@tonic-gate 	 *	<Others>
1873*0Sstevel@tonic-gate 	 * add additional allocation size.
1874*0Sstevel@tonic-gate 	 */
1875*0Sstevel@tonic-gate 
1876*0Sstevel@tonic-gate 	msgmp = allocb(allocsize, BPRI_HI);
1877*0Sstevel@tonic-gate 	if (msgmp == NULL) {
1878*0Sstevel@tonic-gate 		ah0dbg(("ah_send_acquire: 2nd allocb() failed.\n"));
1879*0Sstevel@tonic-gate 		/* Just bail. */
1880*0Sstevel@tonic-gate 		freemsg(pfkeymp);
1881*0Sstevel@tonic-gate 		pfkeymp = NULL;
1882*0Sstevel@tonic-gate 		goto done;
1883*0Sstevel@tonic-gate 	}
1884*0Sstevel@tonic-gate 
1885*0Sstevel@tonic-gate 	cur = msgmp->b_rptr;
1886*0Sstevel@tonic-gate 	end = cur + allocsize;
1887*0Sstevel@tonic-gate 	samsg = (sadb_msg_t *)cur;
1888*0Sstevel@tonic-gate 	pfkeymp->b_cont = msgmp;
1889*0Sstevel@tonic-gate 
1890*0Sstevel@tonic-gate 	/* Set up ACQUIRE. */
1891*0Sstevel@tonic-gate 	cur = sadb_setup_acquire(cur, end, acqrec);
1892*0Sstevel@tonic-gate 	if (cur == NULL) {
1893*0Sstevel@tonic-gate 		ah0dbg(("sadb_setup_acquire failed.\n"));
1894*0Sstevel@tonic-gate 		/* Just bail. */
1895*0Sstevel@tonic-gate 		freemsg(pfkeymp);
1896*0Sstevel@tonic-gate 		pfkeymp = NULL;
1897*0Sstevel@tonic-gate 		goto done;
1898*0Sstevel@tonic-gate 	}
1899*0Sstevel@tonic-gate 	samsg->sadb_msg_satype = SADB_SATYPE_AH;
1900*0Sstevel@tonic-gate 
1901*0Sstevel@tonic-gate 	/* XXX Insert proxy address information here. */
1902*0Sstevel@tonic-gate 
1903*0Sstevel@tonic-gate 	/* XXX Insert identity information here. */
1904*0Sstevel@tonic-gate 
1905*0Sstevel@tonic-gate 	/* XXXMLS Insert sensitivity information here. */
1906*0Sstevel@tonic-gate 
1907*0Sstevel@tonic-gate 	/* Insert proposal here. */
1908*0Sstevel@tonic-gate 
1909*0Sstevel@tonic-gate 	prop = (sadb_prop_t *)(((uint64_t *)samsg) + samsg->sadb_msg_len);
1910*0Sstevel@tonic-gate 	ah_insert_prop(prop, acqrec, combs);
1911*0Sstevel@tonic-gate 	samsg->sadb_msg_len += prop->sadb_prop_len;
1912*0Sstevel@tonic-gate 	msgmp->b_wptr += SADB_64TO8(samsg->sadb_msg_len);
1913*0Sstevel@tonic-gate 
1914*0Sstevel@tonic-gate done:
1915*0Sstevel@tonic-gate 	mutex_exit(&alg_lock);
1916*0Sstevel@tonic-gate 
1917*0Sstevel@tonic-gate 	/*
1918*0Sstevel@tonic-gate 	 * Must mutex_exit() before sending PF_KEY message up, in
1919*0Sstevel@tonic-gate 	 * order to avoid recursive mutex_enter() if there are no registered
1920*0Sstevel@tonic-gate 	 * listeners.
1921*0Sstevel@tonic-gate 	 *
1922*0Sstevel@tonic-gate 	 * Once I've sent the message, I'm cool anyway.
1923*0Sstevel@tonic-gate 	 */
1924*0Sstevel@tonic-gate 	mutex_exit(&acqrec->ipsacq_lock);
1925*0Sstevel@tonic-gate 	if (ah_pfkey_q != NULL && pfkeymp != NULL) {
1926*0Sstevel@tonic-gate 		if (extended != NULL) {
1927*0Sstevel@tonic-gate 			putnext(ah_pfkey_q, extended);
1928*0Sstevel@tonic-gate 		}
1929*0Sstevel@tonic-gate 		putnext(ah_pfkey_q, pfkeymp);
1930*0Sstevel@tonic-gate 		return;
1931*0Sstevel@tonic-gate 	}
1932*0Sstevel@tonic-gate 	/* NOTE: freemsg() works for extended == NULL. */
1933*0Sstevel@tonic-gate 	freemsg(extended);
1934*0Sstevel@tonic-gate 	freemsg(pfkeymp);
1935*0Sstevel@tonic-gate }
1936*0Sstevel@tonic-gate 
1937*0Sstevel@tonic-gate /*
1938*0Sstevel@tonic-gate  * Handle the SADB_GETSPI message.  Create a larval SA.
1939*0Sstevel@tonic-gate  */
1940*0Sstevel@tonic-gate static void
1941*0Sstevel@tonic-gate ah_getspi(mblk_t *mp, keysock_in_t *ksi)
1942*0Sstevel@tonic-gate {
1943*0Sstevel@tonic-gate 	ipsa_t *newbie, *target;
1944*0Sstevel@tonic-gate 	isaf_t *outbound, *inbound;
1945*0Sstevel@tonic-gate 	int rc, diagnostic;
1946*0Sstevel@tonic-gate 	sadb_sa_t *assoc;
1947*0Sstevel@tonic-gate 	keysock_out_t *kso;
1948*0Sstevel@tonic-gate 	uint32_t newspi;
1949*0Sstevel@tonic-gate 
1950*0Sstevel@tonic-gate 	/*
1951*0Sstevel@tonic-gate 	 * Randomly generate a proposed SPI value.
1952*0Sstevel@tonic-gate 	 */
1953*0Sstevel@tonic-gate 	(void) random_get_pseudo_bytes((uint8_t *)&newspi, sizeof (uint32_t));
1954*0Sstevel@tonic-gate 	newbie = sadb_getspi(ksi, newspi, &diagnostic);
1955*0Sstevel@tonic-gate 
1956*0Sstevel@tonic-gate 	if (newbie == NULL) {
1957*0Sstevel@tonic-gate 		sadb_pfkey_error(ah_pfkey_q, mp, ENOMEM, diagnostic,
1958*0Sstevel@tonic-gate 		    ksi->ks_in_serial);
1959*0Sstevel@tonic-gate 		return;
1960*0Sstevel@tonic-gate 	} else if (newbie == (ipsa_t *)-1) {
1961*0Sstevel@tonic-gate 		sadb_pfkey_error(ah_pfkey_q, mp, EINVAL, diagnostic,
1962*0Sstevel@tonic-gate 		    ksi->ks_in_serial);
1963*0Sstevel@tonic-gate 		return;
1964*0Sstevel@tonic-gate 	}
1965*0Sstevel@tonic-gate 
1966*0Sstevel@tonic-gate 	/*
1967*0Sstevel@tonic-gate 	 * XXX - We may randomly collide.  We really should recover from this.
1968*0Sstevel@tonic-gate 	 *	 Unfortunately, that could require spending way-too-much-time
1969*0Sstevel@tonic-gate 	 *	 in here.  For now, let the user retry.
1970*0Sstevel@tonic-gate 	 */
1971*0Sstevel@tonic-gate 
1972*0Sstevel@tonic-gate 	if (newbie->ipsa_addrfam == AF_INET6) {
1973*0Sstevel@tonic-gate 		outbound = &ah_sadb.s_v6.sdb_of[
1974*0Sstevel@tonic-gate 		    OUTBOUND_HASH_V6(*(uint32_t *)(newbie->ipsa_dstaddr))];
1975*0Sstevel@tonic-gate 		inbound = &ah_sadb.s_v6.sdb_if[INBOUND_HASH(newbie->ipsa_spi)];
1976*0Sstevel@tonic-gate 	} else {
1977*0Sstevel@tonic-gate 		outbound = &ah_sadb.s_v4.sdb_of[
1978*0Sstevel@tonic-gate 		    OUTBOUND_HASH_V4(*(uint32_t *)(newbie->ipsa_dstaddr))];
1979*0Sstevel@tonic-gate 		inbound = &ah_sadb.s_v4.sdb_if[INBOUND_HASH(newbie->ipsa_spi)];
1980*0Sstevel@tonic-gate 	}
1981*0Sstevel@tonic-gate 
1982*0Sstevel@tonic-gate 	mutex_enter(&outbound->isaf_lock);
1983*0Sstevel@tonic-gate 	mutex_enter(&inbound->isaf_lock);
1984*0Sstevel@tonic-gate 
1985*0Sstevel@tonic-gate 	/*
1986*0Sstevel@tonic-gate 	 * Check for collisions (i.e. did sadb_getspi() return with something
1987*0Sstevel@tonic-gate 	 * that already exists?).
1988*0Sstevel@tonic-gate 	 *
1989*0Sstevel@tonic-gate 	 * Try outbound first.  Even though SADB_GETSPI is traditionally
1990*0Sstevel@tonic-gate 	 * for inbound SAs, you never know what a user might do.
1991*0Sstevel@tonic-gate 	 */
1992*0Sstevel@tonic-gate 	target = ipsec_getassocbyspi(outbound, newbie->ipsa_spi,
1993*0Sstevel@tonic-gate 	    newbie->ipsa_srcaddr, newbie->ipsa_dstaddr, newbie->ipsa_addrfam);
1994*0Sstevel@tonic-gate 	if (target == NULL) {
1995*0Sstevel@tonic-gate 		target = ipsec_getassocbyspi(inbound, newbie->ipsa_spi,
1996*0Sstevel@tonic-gate 		    newbie->ipsa_srcaddr, newbie->ipsa_dstaddr,
1997*0Sstevel@tonic-gate 		    newbie->ipsa_addrfam);
1998*0Sstevel@tonic-gate 	}
1999*0Sstevel@tonic-gate 
2000*0Sstevel@tonic-gate 	/*
2001*0Sstevel@tonic-gate 	 * I don't have collisions elsewhere!
2002*0Sstevel@tonic-gate 	 * (Nor will I because I'm still holding inbound/outbound locks.)
2003*0Sstevel@tonic-gate 	 */
2004*0Sstevel@tonic-gate 
2005*0Sstevel@tonic-gate 	if (target != NULL) {
2006*0Sstevel@tonic-gate 		rc = EEXIST;
2007*0Sstevel@tonic-gate 		IPSA_REFRELE(target);
2008*0Sstevel@tonic-gate 	} else {
2009*0Sstevel@tonic-gate 		/*
2010*0Sstevel@tonic-gate 		 * sadb_insertassoc() also checks for collisions, so
2011*0Sstevel@tonic-gate 		 * if there's a colliding larval entry, rc will be set
2012*0Sstevel@tonic-gate 		 * to EEXIST.
2013*0Sstevel@tonic-gate 		 */
2014*0Sstevel@tonic-gate 		rc = sadb_insertassoc(newbie, inbound);
2015*0Sstevel@tonic-gate 		(void) drv_getparm(TIME, &newbie->ipsa_hardexpiretime);
2016*0Sstevel@tonic-gate 		newbie->ipsa_hardexpiretime += ipsecah_larval_timeout;
2017*0Sstevel@tonic-gate 	}
2018*0Sstevel@tonic-gate 
2019*0Sstevel@tonic-gate 	/*
2020*0Sstevel@tonic-gate 	 * Can exit outbound mutex.  Hold inbound until we're done with
2021*0Sstevel@tonic-gate 	 * newbie.
2022*0Sstevel@tonic-gate 	 */
2023*0Sstevel@tonic-gate 	mutex_exit(&outbound->isaf_lock);
2024*0Sstevel@tonic-gate 
2025*0Sstevel@tonic-gate 	if (rc != 0) {
2026*0Sstevel@tonic-gate 		mutex_exit(&inbound->isaf_lock);
2027*0Sstevel@tonic-gate 		IPSA_REFRELE(newbie);
2028*0Sstevel@tonic-gate 		sadb_pfkey_error(ah_pfkey_q, mp, rc, SADB_X_DIAGNOSTIC_NONE,
2029*0Sstevel@tonic-gate 		    ksi->ks_in_serial);
2030*0Sstevel@tonic-gate 		return;
2031*0Sstevel@tonic-gate 	}
2032*0Sstevel@tonic-gate 
2033*0Sstevel@tonic-gate 	/* Can write here because I'm still holding the bucket lock. */
2034*0Sstevel@tonic-gate 	newbie->ipsa_type = SADB_SATYPE_AH;
2035*0Sstevel@tonic-gate 
2036*0Sstevel@tonic-gate 	/*
2037*0Sstevel@tonic-gate 	 * Construct successful return message.  We have one thing going
2038*0Sstevel@tonic-gate 	 * for us in PF_KEY v2.  That's the fact that
2039*0Sstevel@tonic-gate 	 *	sizeof (sadb_spirange_t) == sizeof (sadb_sa_t)
2040*0Sstevel@tonic-gate 	 */
2041*0Sstevel@tonic-gate 	assoc = (sadb_sa_t *)ksi->ks_in_extv[SADB_EXT_SPIRANGE];
2042*0Sstevel@tonic-gate 	assoc->sadb_sa_exttype = SADB_EXT_SA;
2043*0Sstevel@tonic-gate 	assoc->sadb_sa_spi = newbie->ipsa_spi;
2044*0Sstevel@tonic-gate 	*((uint64_t *)(&assoc->sadb_sa_replay)) = 0;
2045*0Sstevel@tonic-gate 	mutex_exit(&inbound->isaf_lock);
2046*0Sstevel@tonic-gate 
2047*0Sstevel@tonic-gate 	/* Convert KEYSOCK_IN to KEYSOCK_OUT. */
2048*0Sstevel@tonic-gate 	kso = (keysock_out_t *)ksi;
2049*0Sstevel@tonic-gate 	kso->ks_out_len = sizeof (*kso);
2050*0Sstevel@tonic-gate 	kso->ks_out_serial = ksi->ks_in_serial;
2051*0Sstevel@tonic-gate 	kso->ks_out_type = KEYSOCK_OUT;
2052*0Sstevel@tonic-gate 
2053*0Sstevel@tonic-gate 	/*
2054*0Sstevel@tonic-gate 	 * Can safely putnext() to ah_pfkey_q, because this is a turnaround
2055*0Sstevel@tonic-gate 	 * from the ah_pfkey_q.
2056*0Sstevel@tonic-gate 	 */
2057*0Sstevel@tonic-gate 	putnext(ah_pfkey_q, mp);
2058*0Sstevel@tonic-gate }
2059*0Sstevel@tonic-gate 
2060*0Sstevel@tonic-gate /*
2061*0Sstevel@tonic-gate  * IPv6 sends up the ICMP errors for validation and the removal of the AH
2062*0Sstevel@tonic-gate  * header.
2063*0Sstevel@tonic-gate  */
2064*0Sstevel@tonic-gate static ipsec_status_t
2065*0Sstevel@tonic-gate ah_icmp_error_v6(mblk_t *ipsec_mp)
2066*0Sstevel@tonic-gate {
2067*0Sstevel@tonic-gate 	mblk_t *mp;
2068*0Sstevel@tonic-gate 	ip6_t *ip6h, *oip6h;
2069*0Sstevel@tonic-gate 	uint16_t hdr_length, ah_length;
2070*0Sstevel@tonic-gate 	uint8_t *nexthdrp;
2071*0Sstevel@tonic-gate 	ah_t *ah;
2072*0Sstevel@tonic-gate 	icmp6_t *icmp6;
2073*0Sstevel@tonic-gate 	isaf_t *isaf;
2074*0Sstevel@tonic-gate 	ipsa_t *assoc;
2075*0Sstevel@tonic-gate 	uint8_t *post_ah_ptr;
2076*0Sstevel@tonic-gate 
2077*0Sstevel@tonic-gate 	mp = ipsec_mp->b_cont;
2078*0Sstevel@tonic-gate 	ASSERT(mp->b_datap->db_type == M_CTL);
2079*0Sstevel@tonic-gate 
2080*0Sstevel@tonic-gate 	/*
2081*0Sstevel@tonic-gate 	 * Change the type to M_DATA till we finish pullups.
2082*0Sstevel@tonic-gate 	 */
2083*0Sstevel@tonic-gate 	mp->b_datap->db_type = M_DATA;
2084*0Sstevel@tonic-gate 
2085*0Sstevel@tonic-gate 	/*
2086*0Sstevel@tonic-gate 	 * Eat the cost of a pullupmsg() for now.  It makes the rest of this
2087*0Sstevel@tonic-gate 	 * code far less convoluted.
2088*0Sstevel@tonic-gate 	 */
2089*0Sstevel@tonic-gate 	if (!pullupmsg(mp, -1) ||
2090*0Sstevel@tonic-gate 	    !ip_hdr_length_nexthdr_v6(mp, (ip6_t *)mp->b_rptr, &hdr_length,
2091*0Sstevel@tonic-gate 		&nexthdrp) ||
2092*0Sstevel@tonic-gate 	    mp->b_rptr + hdr_length + sizeof (icmp6_t) + sizeof (ip6_t) +
2093*0Sstevel@tonic-gate 	    sizeof (ah_t) > mp->b_wptr) {
2094*0Sstevel@tonic-gate 		IP_AH_BUMP_STAT(in_discards);
2095*0Sstevel@tonic-gate 		ip_drop_packet(ipsec_mp, B_TRUE, NULL, NULL, &ipdrops_ah_nomem,
2096*0Sstevel@tonic-gate 		    &ah_dropper);
2097*0Sstevel@tonic-gate 		return (IPSEC_STATUS_FAILED);
2098*0Sstevel@tonic-gate 	}
2099*0Sstevel@tonic-gate 
2100*0Sstevel@tonic-gate 	oip6h = (ip6_t *)mp->b_rptr;
2101*0Sstevel@tonic-gate 	icmp6 = (icmp6_t *)((uint8_t *)oip6h + hdr_length);
2102*0Sstevel@tonic-gate 	ip6h = (ip6_t *)(icmp6 + 1);
2103*0Sstevel@tonic-gate 	if (!ip_hdr_length_nexthdr_v6(mp, ip6h, &hdr_length, &nexthdrp)) {
2104*0Sstevel@tonic-gate 		IP_AH_BUMP_STAT(in_discards);
2105*0Sstevel@tonic-gate 		ip_drop_packet(ipsec_mp, B_TRUE, NULL, NULL,
2106*0Sstevel@tonic-gate 		    &ipdrops_ah_bad_v6_hdrs, &ah_dropper);
2107*0Sstevel@tonic-gate 		return (IPSEC_STATUS_FAILED);
2108*0Sstevel@tonic-gate 	}
2109*0Sstevel@tonic-gate 	ah = (ah_t *)((uint8_t *)ip6h + hdr_length);
2110*0Sstevel@tonic-gate 
2111*0Sstevel@tonic-gate 	isaf = &ah_sadb.s_v6.sdb_of[OUTBOUND_HASH_V6(ip6h->ip6_dst)];
2112*0Sstevel@tonic-gate 	mutex_enter(&isaf->isaf_lock);
2113*0Sstevel@tonic-gate 	assoc = ipsec_getassocbyspi(isaf, ah->ah_spi,
2114*0Sstevel@tonic-gate 	    (uint32_t *)&ip6h->ip6_src, (uint32_t *)&ip6h->ip6_dst, AF_INET6);
2115*0Sstevel@tonic-gate 	mutex_exit(&isaf->isaf_lock);
2116*0Sstevel@tonic-gate 
2117*0Sstevel@tonic-gate 	if (assoc == NULL) {
2118*0Sstevel@tonic-gate 		IP_AH_BUMP_STAT(lookup_failure);
2119*0Sstevel@tonic-gate 		IP_AH_BUMP_STAT(in_discards);
2120*0Sstevel@tonic-gate 		if (ipsecah_log_unknown_spi) {
2121*0Sstevel@tonic-gate 			ipsec_assocfailure(info.mi_idnum, 0, 0,
2122*0Sstevel@tonic-gate 			    SL_CONSOLE | SL_WARN | SL_ERROR,
2123*0Sstevel@tonic-gate 			    "Bad ICMP message - No association for the "
2124*0Sstevel@tonic-gate 			    "attached AH header whose spi is 0x%x, "
2125*0Sstevel@tonic-gate 			    "sender is 0x%x\n",
2126*0Sstevel@tonic-gate 			    ah->ah_spi, &oip6h->ip6_src, AF_INET6);
2127*0Sstevel@tonic-gate 		}
2128*0Sstevel@tonic-gate 		ip_drop_packet(ipsec_mp, B_TRUE, NULL, NULL, &ipdrops_ah_no_sa,
2129*0Sstevel@tonic-gate 		    &ah_dropper);
2130*0Sstevel@tonic-gate 		return (IPSEC_STATUS_FAILED);
2131*0Sstevel@tonic-gate 	}
2132*0Sstevel@tonic-gate 
2133*0Sstevel@tonic-gate 	IPSA_REFRELE(assoc);
2134*0Sstevel@tonic-gate 
2135*0Sstevel@tonic-gate 	/*
2136*0Sstevel@tonic-gate 	 * There seems to be a valid association. If there is enough of AH
2137*0Sstevel@tonic-gate 	 * header remove it, otherwise bail.  One could check whether it has
2138*0Sstevel@tonic-gate 	 * complete AH header plus 8 bytes but it does not make sense if an
2139*0Sstevel@tonic-gate 	 * icmp error is returned for ICMP messages e.g ICMP time exceeded,
2140*0Sstevel@tonic-gate 	 * that are being sent up. Let the caller figure out.
2141*0Sstevel@tonic-gate 	 *
2142*0Sstevel@tonic-gate 	 * NOTE: ah_length is the number of 32 bit words minus 2.
2143*0Sstevel@tonic-gate 	 */
2144*0Sstevel@tonic-gate 	ah_length = (ah->ah_length << 2) + 8;
2145*0Sstevel@tonic-gate 	post_ah_ptr = (uint8_t *)ah + ah_length;
2146*0Sstevel@tonic-gate 
2147*0Sstevel@tonic-gate 	if (post_ah_ptr > mp->b_wptr) {
2148*0Sstevel@tonic-gate 		IP_AH_BUMP_STAT(in_discards);
2149*0Sstevel@tonic-gate 		ip_drop_packet(ipsec_mp, B_TRUE, NULL, NULL,
2150*0Sstevel@tonic-gate 		    &ipdrops_ah_bad_length, &ah_dropper);
2151*0Sstevel@tonic-gate 		return (IPSEC_STATUS_FAILED);
2152*0Sstevel@tonic-gate 	}
2153*0Sstevel@tonic-gate 
2154*0Sstevel@tonic-gate 	ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) - ah_length);
2155*0Sstevel@tonic-gate 	*nexthdrp = ah->ah_nexthdr;
2156*0Sstevel@tonic-gate 	ovbcopy(post_ah_ptr, ah,
2157*0Sstevel@tonic-gate 	    (size_t)((uintptr_t)mp->b_wptr - (uintptr_t)post_ah_ptr));
2158*0Sstevel@tonic-gate 	mp->b_wptr -= ah_length;
2159*0Sstevel@tonic-gate 	/* Rewhack to be an ICMP error. */
2160*0Sstevel@tonic-gate 	mp->b_datap->db_type = M_CTL;
2161*0Sstevel@tonic-gate 
2162*0Sstevel@tonic-gate 	return (IPSEC_STATUS_SUCCESS);
2163*0Sstevel@tonic-gate }
2164*0Sstevel@tonic-gate 
2165*0Sstevel@tonic-gate /*
2166*0Sstevel@tonic-gate  * IP sends up the ICMP errors for validation and the removal of
2167*0Sstevel@tonic-gate  * the AH header.
2168*0Sstevel@tonic-gate  */
2169*0Sstevel@tonic-gate static ipsec_status_t
2170*0Sstevel@tonic-gate ah_icmp_error_v4(mblk_t *ipsec_mp)
2171*0Sstevel@tonic-gate {
2172*0Sstevel@tonic-gate 	mblk_t *mp;
2173*0Sstevel@tonic-gate 	mblk_t *mp1;
2174*0Sstevel@tonic-gate 	icmph_t *icmph;
2175*0Sstevel@tonic-gate 	int iph_hdr_length;
2176*0Sstevel@tonic-gate 	int hdr_length;
2177*0Sstevel@tonic-gate 	isaf_t *hptr;
2178*0Sstevel@tonic-gate 	ipsa_t *assoc;
2179*0Sstevel@tonic-gate 	int ah_length;
2180*0Sstevel@tonic-gate 	ipha_t *ipha;
2181*0Sstevel@tonic-gate 	ipha_t *oipha;
2182*0Sstevel@tonic-gate 	ah_t *ah;
2183*0Sstevel@tonic-gate 	uint32_t length;
2184*0Sstevel@tonic-gate 	int alloc_size;
2185*0Sstevel@tonic-gate 	uint8_t nexthdr;
2186*0Sstevel@tonic-gate 
2187*0Sstevel@tonic-gate 	mp = ipsec_mp->b_cont;
2188*0Sstevel@tonic-gate 	ASSERT(mp->b_datap->db_type == M_CTL);
2189*0Sstevel@tonic-gate 
2190*0Sstevel@tonic-gate 	/*
2191*0Sstevel@tonic-gate 	 * Change the type to M_DATA till we finish pullups.
2192*0Sstevel@tonic-gate 	 */
2193*0Sstevel@tonic-gate 	mp->b_datap->db_type = M_DATA;
2194*0Sstevel@tonic-gate 
2195*0Sstevel@tonic-gate 	oipha = ipha = (ipha_t *)mp->b_rptr;
2196*0Sstevel@tonic-gate 	iph_hdr_length = IPH_HDR_LENGTH(ipha);
2197*0Sstevel@tonic-gate 	icmph = (icmph_t *)&mp->b_rptr[iph_hdr_length];
2198*0Sstevel@tonic-gate 
2199*0Sstevel@tonic-gate 	ipha = (ipha_t *)&icmph[1];
2200*0Sstevel@tonic-gate 	hdr_length = IPH_HDR_LENGTH(ipha);
2201*0Sstevel@tonic-gate 
2202*0Sstevel@tonic-gate 	/*
2203*0Sstevel@tonic-gate 	 * See if we have enough to locate the SPI
2204*0Sstevel@tonic-gate 	 */
2205*0Sstevel@tonic-gate 	if ((uchar_t *)ipha + hdr_length + 8 > mp->b_wptr) {
2206*0Sstevel@tonic-gate 		if (!pullupmsg(mp, (uchar_t *)ipha + hdr_length + 8 -
2207*0Sstevel@tonic-gate 			    mp->b_rptr)) {
2208*0Sstevel@tonic-gate 			ipsec_rl_strlog(info.mi_idnum, 0, 0,
2209*0Sstevel@tonic-gate 			    SL_WARN | SL_ERROR,
2210*0Sstevel@tonic-gate 			    "ICMP error: Small AH header\n");
2211*0Sstevel@tonic-gate 			IP_AH_BUMP_STAT(in_discards);
2212*0Sstevel@tonic-gate 			ip_drop_packet(ipsec_mp, B_TRUE, NULL, NULL,
2213*0Sstevel@tonic-gate 			    &ipdrops_ah_bad_length, &ah_dropper);
2214*0Sstevel@tonic-gate 			return (IPSEC_STATUS_FAILED);
2215*0Sstevel@tonic-gate 		}
2216*0Sstevel@tonic-gate 		icmph = (icmph_t *)&mp->b_rptr[iph_hdr_length];
2217*0Sstevel@tonic-gate 		ipha = (ipha_t *)&icmph[1];
2218*0Sstevel@tonic-gate 	}
2219*0Sstevel@tonic-gate 
2220*0Sstevel@tonic-gate 	ah = (ah_t *)((uint8_t *)ipha + hdr_length);
2221*0Sstevel@tonic-gate 	nexthdr = ah->ah_nexthdr;
2222*0Sstevel@tonic-gate 
2223*0Sstevel@tonic-gate 	hptr = &ah_sadb.s_v4.sdb_of[OUTBOUND_HASH_V4(ipha->ipha_dst)];
2224*0Sstevel@tonic-gate 	mutex_enter(&hptr->isaf_lock);
2225*0Sstevel@tonic-gate 	assoc = ipsec_getassocbyspi(hptr, ah->ah_spi,
2226*0Sstevel@tonic-gate 	    (uint32_t *)&ipha->ipha_src, (uint32_t *)&ipha->ipha_dst, AF_INET);
2227*0Sstevel@tonic-gate 	mutex_exit(&hptr->isaf_lock);
2228*0Sstevel@tonic-gate 
2229*0Sstevel@tonic-gate 	if (assoc == NULL) {
2230*0Sstevel@tonic-gate 		IP_AH_BUMP_STAT(lookup_failure);
2231*0Sstevel@tonic-gate 		IP_AH_BUMP_STAT(in_discards);
2232*0Sstevel@tonic-gate 		if (ipsecah_log_unknown_spi) {
2233*0Sstevel@tonic-gate 			ipsec_assocfailure(info.mi_idnum, 0, 0,
2234*0Sstevel@tonic-gate 			    SL_CONSOLE | SL_WARN | SL_ERROR,
2235*0Sstevel@tonic-gate 			    "Bad ICMP message - No association for the "
2236*0Sstevel@tonic-gate 			    "attached AH header whose spi is 0x%x, "
2237*0Sstevel@tonic-gate 			    "sender is 0x%x\n",
2238*0Sstevel@tonic-gate 			    ah->ah_spi, &oipha->ipha_src, AF_INET);
2239*0Sstevel@tonic-gate 		}
2240*0Sstevel@tonic-gate 		ip_drop_packet(ipsec_mp, B_TRUE, NULL, NULL, &ipdrops_ah_no_sa,
2241*0Sstevel@tonic-gate 		    &ah_dropper);
2242*0Sstevel@tonic-gate 		return (IPSEC_STATUS_FAILED);
2243*0Sstevel@tonic-gate 	}
2244*0Sstevel@tonic-gate 
2245*0Sstevel@tonic-gate 	IPSA_REFRELE(assoc);
2246*0Sstevel@tonic-gate 	/*
2247*0Sstevel@tonic-gate 	 * There seems to be a valid association. If there
2248*0Sstevel@tonic-gate 	 * is enough of AH header remove it, otherwise remove
2249*0Sstevel@tonic-gate 	 * as much as possible and send it back. One could check
2250*0Sstevel@tonic-gate 	 * whether it has complete AH header plus 8 bytes but it
2251*0Sstevel@tonic-gate 	 * does not make sense if an icmp error is returned for
2252*0Sstevel@tonic-gate 	 * ICMP messages e.g ICMP time exceeded, that are being
2253*0Sstevel@tonic-gate 	 * sent up. Let the caller figure out.
2254*0Sstevel@tonic-gate 	 *
2255*0Sstevel@tonic-gate 	 * NOTE: ah_length is the number of 32 bit words minus 2.
2256*0Sstevel@tonic-gate 	 */
2257*0Sstevel@tonic-gate 	ah_length = (ah->ah_length << 2) + 8;
2258*0Sstevel@tonic-gate 
2259*0Sstevel@tonic-gate 	if ((uchar_t *)ipha + hdr_length + ah_length > mp->b_wptr) {
2260*0Sstevel@tonic-gate 		if (mp->b_cont == NULL) {
2261*0Sstevel@tonic-gate 			/*
2262*0Sstevel@tonic-gate 			 * There is nothing to pullup. Just remove as
2263*0Sstevel@tonic-gate 			 * much as possible. This is a common case for
2264*0Sstevel@tonic-gate 			 * IPV4.
2265*0Sstevel@tonic-gate 			 */
2266*0Sstevel@tonic-gate 			ah_length = (mp->b_wptr - ((uchar_t *)ipha +
2267*0Sstevel@tonic-gate 			    hdr_length));
2268*0Sstevel@tonic-gate 			goto done;
2269*0Sstevel@tonic-gate 		}
2270*0Sstevel@tonic-gate 		/* Pullup the full ah header */
2271*0Sstevel@tonic-gate 		if (!pullupmsg(mp, (uchar_t *)ah + ah_length - mp->b_rptr)) {
2272*0Sstevel@tonic-gate 			/*
2273*0Sstevel@tonic-gate 			 * pullupmsg could have failed if there was not
2274*0Sstevel@tonic-gate 			 * enough to pullup or memory allocation failed.
2275*0Sstevel@tonic-gate 			 * We tried hard, give up now.
2276*0Sstevel@tonic-gate 			 */
2277*0Sstevel@tonic-gate 			IP_AH_BUMP_STAT(in_discards);
2278*0Sstevel@tonic-gate 			ip_drop_packet(ipsec_mp, B_TRUE, NULL, NULL,
2279*0Sstevel@tonic-gate 			    &ipdrops_ah_nomem, &ah_dropper);
2280*0Sstevel@tonic-gate 			return (IPSEC_STATUS_FAILED);
2281*0Sstevel@tonic-gate 		}
2282*0Sstevel@tonic-gate 		icmph = (icmph_t *)&mp->b_rptr[iph_hdr_length];
2283*0Sstevel@tonic-gate 		ipha = (ipha_t *)&icmph[1];
2284*0Sstevel@tonic-gate 	}
2285*0Sstevel@tonic-gate done:
2286*0Sstevel@tonic-gate 	/*
2287*0Sstevel@tonic-gate 	 * Remove the AH header and change the protocol.
2288*0Sstevel@tonic-gate 	 * Don't update the spi fields in the ipsec_in
2289*0Sstevel@tonic-gate 	 * message as we are called just to validate the
2290*0Sstevel@tonic-gate 	 * message attached to the ICMP message.
2291*0Sstevel@tonic-gate 	 *
2292*0Sstevel@tonic-gate 	 * If we never pulled up since all of the message
2293*0Sstevel@tonic-gate 	 * is in one single mblk, we can't remove the AH header
2294*0Sstevel@tonic-gate 	 * by just setting the b_wptr to the beginning of the
2295*0Sstevel@tonic-gate 	 * AH header. We need to allocate a mblk that can hold
2296*0Sstevel@tonic-gate 	 * up until the inner IP header and copy them.
2297*0Sstevel@tonic-gate 	 */
2298*0Sstevel@tonic-gate 	alloc_size = iph_hdr_length + sizeof (icmph_t) + hdr_length;
2299*0Sstevel@tonic-gate 
2300*0Sstevel@tonic-gate 	if ((mp1 = allocb(alloc_size, BPRI_LO)) == NULL) {
2301*0Sstevel@tonic-gate 		IP_AH_BUMP_STAT(in_discards);
2302*0Sstevel@tonic-gate 		ip_drop_packet(ipsec_mp, B_TRUE, NULL, NULL, &ipdrops_ah_nomem,
2303*0Sstevel@tonic-gate 		    &ah_dropper);
2304*0Sstevel@tonic-gate 		return (IPSEC_STATUS_FAILED);
2305*0Sstevel@tonic-gate 	}
2306*0Sstevel@tonic-gate 	/* ICMP errors are M_CTL messages */
2307*0Sstevel@tonic-gate 	mp1->b_datap->db_type = M_CTL;
2308*0Sstevel@tonic-gate 	ipsec_mp->b_cont = mp1;
2309*0Sstevel@tonic-gate 	bcopy(mp->b_rptr, mp1->b_rptr, alloc_size);
2310*0Sstevel@tonic-gate 	mp1->b_wptr += alloc_size;
2311*0Sstevel@tonic-gate 
2312*0Sstevel@tonic-gate 	/*
2313*0Sstevel@tonic-gate 	 * Skip whatever we have copied and as much of AH header
2314*0Sstevel@tonic-gate 	 * possible. If we still have something left in the original
2315*0Sstevel@tonic-gate 	 * message, tag on.
2316*0Sstevel@tonic-gate 	 */
2317*0Sstevel@tonic-gate 	mp->b_rptr = (uchar_t *)ipha + hdr_length + ah_length;
2318*0Sstevel@tonic-gate 
2319*0Sstevel@tonic-gate 	if (mp->b_rptr != mp->b_wptr) {
2320*0Sstevel@tonic-gate 		mp1->b_cont = mp;
2321*0Sstevel@tonic-gate 	} else {
2322*0Sstevel@tonic-gate 		if (mp->b_cont != NULL)
2323*0Sstevel@tonic-gate 			mp1->b_cont = mp->b_cont;
2324*0Sstevel@tonic-gate 		freeb(mp);
2325*0Sstevel@tonic-gate 	}
2326*0Sstevel@tonic-gate 
2327*0Sstevel@tonic-gate 	ipha = (ipha_t *)(mp1->b_rptr + iph_hdr_length + sizeof (icmph_t));
2328*0Sstevel@tonic-gate 	ipha->ipha_protocol = nexthdr;
2329*0Sstevel@tonic-gate 	length = ntohs(ipha->ipha_length);
2330*0Sstevel@tonic-gate 	length -= ah_length;
2331*0Sstevel@tonic-gate 	ipha->ipha_length = htons((uint16_t)length);
2332*0Sstevel@tonic-gate 	ipha->ipha_hdr_checksum = 0;
2333*0Sstevel@tonic-gate 	ipha->ipha_hdr_checksum = (uint16_t)ip_csum_hdr(ipha);
2334*0Sstevel@tonic-gate 
2335*0Sstevel@tonic-gate 	return (IPSEC_STATUS_SUCCESS);
2336*0Sstevel@tonic-gate }
2337*0Sstevel@tonic-gate 
2338*0Sstevel@tonic-gate /*
2339*0Sstevel@tonic-gate  * IP calls this to validate the ICMP errors that
2340*0Sstevel@tonic-gate  * we got from the network.
2341*0Sstevel@tonic-gate  */
2342*0Sstevel@tonic-gate ipsec_status_t
2343*0Sstevel@tonic-gate ipsecah_icmp_error(mblk_t *mp)
2344*0Sstevel@tonic-gate {
2345*0Sstevel@tonic-gate 	ipsec_in_t *ii = (ipsec_in_t *)mp->b_rptr;
2346*0Sstevel@tonic-gate 
2347*0Sstevel@tonic-gate 	if (ii->ipsec_in_v4)
2348*0Sstevel@tonic-gate 		return (ah_icmp_error_v4(mp));
2349*0Sstevel@tonic-gate 	else
2350*0Sstevel@tonic-gate 		return (ah_icmp_error_v6(mp));
2351*0Sstevel@tonic-gate }
2352*0Sstevel@tonic-gate 
2353*0Sstevel@tonic-gate static int
2354*0Sstevel@tonic-gate ah_fix_tlv_options_v6(uint8_t *oi_opt, uint8_t *pi_opt, uint_t ehdrlen,
2355*0Sstevel@tonic-gate     uint8_t hdr_type, boolean_t copy_always)
2356*0Sstevel@tonic-gate {
2357*0Sstevel@tonic-gate 	uint8_t opt_type;
2358*0Sstevel@tonic-gate 	uint_t optlen;
2359*0Sstevel@tonic-gate 
2360*0Sstevel@tonic-gate 	ASSERT(hdr_type == IPPROTO_DSTOPTS || hdr_type == IPPROTO_HOPOPTS);
2361*0Sstevel@tonic-gate 
2362*0Sstevel@tonic-gate 	/*
2363*0Sstevel@tonic-gate 	 * Copy the next header and hdr ext. len of the HOP-by-HOP
2364*0Sstevel@tonic-gate 	 * and Destination option.
2365*0Sstevel@tonic-gate 	 */
2366*0Sstevel@tonic-gate 	*pi_opt++ = *oi_opt++;
2367*0Sstevel@tonic-gate 	*pi_opt++ = *oi_opt++;
2368*0Sstevel@tonic-gate 	ehdrlen -= 2;
2369*0Sstevel@tonic-gate 
2370*0Sstevel@tonic-gate 	/*
2371*0Sstevel@tonic-gate 	 * Now handle all the TLV encoded options.
2372*0Sstevel@tonic-gate 	 */
2373*0Sstevel@tonic-gate 	while (ehdrlen != 0) {
2374*0Sstevel@tonic-gate 		opt_type = *oi_opt;
2375*0Sstevel@tonic-gate 
2376*0Sstevel@tonic-gate 		if (opt_type == IP6OPT_PAD1) {
2377*0Sstevel@tonic-gate 			optlen = 1;
2378*0Sstevel@tonic-gate 		} else {
2379*0Sstevel@tonic-gate 			if (ehdrlen < 2)
2380*0Sstevel@tonic-gate 				goto bad_opt;
2381*0Sstevel@tonic-gate 			optlen = 2 + oi_opt[1];
2382*0Sstevel@tonic-gate 			if (optlen > ehdrlen)
2383*0Sstevel@tonic-gate 				goto bad_opt;
2384*0Sstevel@tonic-gate 		}
2385*0Sstevel@tonic-gate 		if (copy_always || !(opt_type & IP6OPT_MUTABLE)) {
2386*0Sstevel@tonic-gate 			bcopy(oi_opt, pi_opt, optlen);
2387*0Sstevel@tonic-gate 		} else {
2388*0Sstevel@tonic-gate 			if (optlen == 1) {
2389*0Sstevel@tonic-gate 				*pi_opt = 0;
2390*0Sstevel@tonic-gate 			} else {
2391*0Sstevel@tonic-gate 				/*
2392*0Sstevel@tonic-gate 				 * Copy the type and data length fields.
2393*0Sstevel@tonic-gate 				 * Zero the option data by skipping
2394*0Sstevel@tonic-gate 				 * option type and option data len
2395*0Sstevel@tonic-gate 				 * fields.
2396*0Sstevel@tonic-gate 				 */
2397*0Sstevel@tonic-gate 				*pi_opt = *oi_opt;
2398*0Sstevel@tonic-gate 				*(pi_opt + 1) = *(oi_opt + 1);
2399*0Sstevel@tonic-gate 				bzero(pi_opt + 2, optlen - 2);
2400*0Sstevel@tonic-gate 			}
2401*0Sstevel@tonic-gate 		}
2402*0Sstevel@tonic-gate 		ehdrlen -= optlen;
2403*0Sstevel@tonic-gate 		oi_opt += optlen;
2404*0Sstevel@tonic-gate 		pi_opt += optlen;
2405*0Sstevel@tonic-gate 	}
2406*0Sstevel@tonic-gate 	return (0);
2407*0Sstevel@tonic-gate bad_opt:
2408*0Sstevel@tonic-gate 	return (-1);
2409*0Sstevel@tonic-gate }
2410*0Sstevel@tonic-gate 
2411*0Sstevel@tonic-gate /*
2412*0Sstevel@tonic-gate  * Construct a pseudo header for AH, processing all the options.
2413*0Sstevel@tonic-gate  *
2414*0Sstevel@tonic-gate  * oip6h is the IPv6 header of the incoming or outgoing packet.
2415*0Sstevel@tonic-gate  * ip6h is the pointer to the pseudo headers IPV6 header. All
2416*0Sstevel@tonic-gate  * the space needed for the options have been allocated including
2417*0Sstevel@tonic-gate  * the AH header.
2418*0Sstevel@tonic-gate  *
2419*0Sstevel@tonic-gate  * If copy_always is set, all the options that appear before AH are copied
2420*0Sstevel@tonic-gate  * blindly without checking for IP6OPT_MUTABLE. This is used by
2421*0Sstevel@tonic-gate  * ah_auth_out_done().  Please refer to that function for details.
2422*0Sstevel@tonic-gate  *
2423*0Sstevel@tonic-gate  * NOTE :
2424*0Sstevel@tonic-gate  *
2425*0Sstevel@tonic-gate  * *  AH header is never copied in this function even if copy_always
2426*0Sstevel@tonic-gate  *    is set. It just returns the ah_offset - offset of the AH header
2427*0Sstevel@tonic-gate  *    and the caller needs to do the copying. This is done so that we
2428*0Sstevel@tonic-gate  *    don't have pass extra arguments e.g. SA etc. and also,
2429*0Sstevel@tonic-gate  *    it is not needed when ah_auth_out_done is calling this function.
2430*0Sstevel@tonic-gate  */
2431*0Sstevel@tonic-gate static uint_t
2432*0Sstevel@tonic-gate ah_fix_phdr_v6(ip6_t *ip6h, ip6_t *oip6h, boolean_t outbound,
2433*0Sstevel@tonic-gate     boolean_t copy_always)
2434*0Sstevel@tonic-gate {
2435*0Sstevel@tonic-gate 	uint8_t	*oi_opt;
2436*0Sstevel@tonic-gate 	uint8_t	*pi_opt;
2437*0Sstevel@tonic-gate 	uint8_t nexthdr;
2438*0Sstevel@tonic-gate 	uint8_t *prev_nexthdr;
2439*0Sstevel@tonic-gate 	ip6_hbh_t *hbhhdr;
2440*0Sstevel@tonic-gate 	ip6_dest_t *dsthdr = NULL;
2441*0Sstevel@tonic-gate 	ip6_rthdr0_t *rthdr;
2442*0Sstevel@tonic-gate 	int ehdrlen;
2443*0Sstevel@tonic-gate 	ah_t *ah;
2444*0Sstevel@tonic-gate 	int ret;
2445*0Sstevel@tonic-gate 
2446*0Sstevel@tonic-gate 	/*
2447*0Sstevel@tonic-gate 	 * In the outbound case for source route, ULP has already moved
2448*0Sstevel@tonic-gate 	 * the first hop, which is now in ip6_dst. We need to re-arrange
2449*0Sstevel@tonic-gate 	 * the header to make it look like how it would appear in the
2450*0Sstevel@tonic-gate 	 * receiver i.e
2451*0Sstevel@tonic-gate 	 *
2452*0Sstevel@tonic-gate 	 * Because of ip_massage_options_v6 the header looks like
2453*0Sstevel@tonic-gate 	 * this :
2454*0Sstevel@tonic-gate 	 *
2455*0Sstevel@tonic-gate 	 * ip6_src = S, ip6_dst = I1. followed by I2,I3,D.
2456*0Sstevel@tonic-gate 	 *
2457*0Sstevel@tonic-gate 	 * When it reaches the receiver, it would look like
2458*0Sstevel@tonic-gate 	 *
2459*0Sstevel@tonic-gate 	 * ip6_src = S, ip6_dst = D. followed by I1,I2,I3.
2460*0Sstevel@tonic-gate 	 *
2461*0Sstevel@tonic-gate 	 * NOTE : We assume that there are no problems with the options
2462*0Sstevel@tonic-gate 	 * as IP should have already checked this.
2463*0Sstevel@tonic-gate 	 */
2464*0Sstevel@tonic-gate 
2465*0Sstevel@tonic-gate 	oi_opt = (uchar_t *)&oip6h[1];
2466*0Sstevel@tonic-gate 	pi_opt = (uchar_t *)&ip6h[1];
2467*0Sstevel@tonic-gate 
2468*0Sstevel@tonic-gate 	/*
2469*0Sstevel@tonic-gate 	 * We set the prev_nexthdr properly in the pseudo header.
2470*0Sstevel@tonic-gate 	 * After we finish authentication and come back from the
2471*0Sstevel@tonic-gate 	 * algorithm module, pseudo header will become the real
2472*0Sstevel@tonic-gate 	 * IP header.
2473*0Sstevel@tonic-gate 	 */
2474*0Sstevel@tonic-gate 	prev_nexthdr = (uint8_t *)&ip6h->ip6_nxt;
2475*0Sstevel@tonic-gate 	nexthdr = oip6h->ip6_nxt;
2476*0Sstevel@tonic-gate 	/* Assume IP has already stripped it */
2477*0Sstevel@tonic-gate 	ASSERT(nexthdr != IPPROTO_FRAGMENT && nexthdr != IPPROTO_RAW);
2478*0Sstevel@tonic-gate 	ah = NULL;
2479*0Sstevel@tonic-gate 	dsthdr = NULL;
2480*0Sstevel@tonic-gate 	for (;;) {
2481*0Sstevel@tonic-gate 		switch (nexthdr) {
2482*0Sstevel@tonic-gate 		case IPPROTO_HOPOPTS:
2483*0Sstevel@tonic-gate 			hbhhdr = (ip6_hbh_t *)oi_opt;
2484*0Sstevel@tonic-gate 			nexthdr = hbhhdr->ip6h_nxt;
2485*0Sstevel@tonic-gate 			ehdrlen = 8 * (hbhhdr->ip6h_len + 1);
2486*0Sstevel@tonic-gate 			ret = ah_fix_tlv_options_v6(oi_opt, pi_opt, ehdrlen,
2487*0Sstevel@tonic-gate 			    IPPROTO_HOPOPTS, copy_always);
2488*0Sstevel@tonic-gate 			/*
2489*0Sstevel@tonic-gate 			 * Return a zero offset indicating error if there
2490*0Sstevel@tonic-gate 			 * was error.
2491*0Sstevel@tonic-gate 			 */
2492*0Sstevel@tonic-gate 			if (ret == -1)
2493*0Sstevel@tonic-gate 				return (0);
2494*0Sstevel@tonic-gate 			hbhhdr = (ip6_hbh_t *)pi_opt;
2495*0Sstevel@tonic-gate 			prev_nexthdr = (uint8_t *)&hbhhdr->ip6h_nxt;
2496*0Sstevel@tonic-gate 			break;
2497*0Sstevel@tonic-gate 		case IPPROTO_ROUTING:
2498*0Sstevel@tonic-gate 			rthdr = (ip6_rthdr0_t *)oi_opt;
2499*0Sstevel@tonic-gate 			nexthdr = rthdr->ip6r0_nxt;
2500*0Sstevel@tonic-gate 			ehdrlen = 8 * (rthdr->ip6r0_len + 1);
2501*0Sstevel@tonic-gate 			if (!copy_always && outbound) {
2502*0Sstevel@tonic-gate 				int i, left;
2503*0Sstevel@tonic-gate 				ip6_rthdr0_t *prthdr;
2504*0Sstevel@tonic-gate 				in6_addr_t *ap, *pap;
2505*0Sstevel@tonic-gate 
2506*0Sstevel@tonic-gate 				left = rthdr->ip6r0_segleft;
2507*0Sstevel@tonic-gate 				prthdr = (ip6_rthdr0_t *)pi_opt;
2508*0Sstevel@tonic-gate 				pap = (in6_addr_t *)(prthdr + 1);
2509*0Sstevel@tonic-gate 				ap = (in6_addr_t *)(rthdr + 1);
2510*0Sstevel@tonic-gate 				/*
2511*0Sstevel@tonic-gate 				 * First eight bytes except seg_left
2512*0Sstevel@tonic-gate 				 * does not change en route.
2513*0Sstevel@tonic-gate 				 */
2514*0Sstevel@tonic-gate 				bcopy(oi_opt, pi_opt, 8);
2515*0Sstevel@tonic-gate 				prthdr->ip6r0_segleft = 0;
2516*0Sstevel@tonic-gate 				/*
2517*0Sstevel@tonic-gate 				 * First address has been moved to
2518*0Sstevel@tonic-gate 				 * the destination address of the
2519*0Sstevel@tonic-gate 				 * ip header by ip_massage_options_v6.
2520*0Sstevel@tonic-gate 				 * And the real destination address is
2521*0Sstevel@tonic-gate 				 * in the last address part of the
2522*0Sstevel@tonic-gate 				 * option.
2523*0Sstevel@tonic-gate 				 */
2524*0Sstevel@tonic-gate 				*pap = oip6h->ip6_dst;
2525*0Sstevel@tonic-gate 				for (i = 1; i < left - 1; i++)
2526*0Sstevel@tonic-gate 					pap[i] = ap[i - 1];
2527*0Sstevel@tonic-gate 				ip6h->ip6_dst = *(ap + left - 1);
2528*0Sstevel@tonic-gate 			} else {
2529*0Sstevel@tonic-gate 				bcopy(oi_opt, pi_opt, ehdrlen);
2530*0Sstevel@tonic-gate 			}
2531*0Sstevel@tonic-gate 			rthdr = (ip6_rthdr0_t *)pi_opt;
2532*0Sstevel@tonic-gate 			prev_nexthdr = (uint8_t *)&rthdr->ip6r0_nxt;
2533*0Sstevel@tonic-gate 			break;
2534*0Sstevel@tonic-gate 		case IPPROTO_DSTOPTS:
2535*0Sstevel@tonic-gate 			/*
2536*0Sstevel@tonic-gate 			 * Destination options are tricky.  If there is
2537*0Sstevel@tonic-gate 			 * a terminal (e.g. non-IPv6-extension) header
2538*0Sstevel@tonic-gate 			 * following the destination options, don't
2539*0Sstevel@tonic-gate 			 * reset prev_nexthdr or advance the AH insertion
2540*0Sstevel@tonic-gate 			 * point and just treat this as a terminal header.
2541*0Sstevel@tonic-gate 			 *
2542*0Sstevel@tonic-gate 			 * If this is an inbound packet, just deal with
2543*0Sstevel@tonic-gate 			 * it as is.
2544*0Sstevel@tonic-gate 			 */
2545*0Sstevel@tonic-gate 			dsthdr = (ip6_dest_t *)oi_opt;
2546*0Sstevel@tonic-gate 			/*
2547*0Sstevel@tonic-gate 			 * XXX I hope common-subexpression elimination
2548*0Sstevel@tonic-gate 			 * saves us the double-evaluate.
2549*0Sstevel@tonic-gate 			 */
2550*0Sstevel@tonic-gate 			if (outbound && dsthdr->ip6d_nxt != IPPROTO_ROUTING &&
2551*0Sstevel@tonic-gate 			    dsthdr->ip6d_nxt != IPPROTO_HOPOPTS)
2552*0Sstevel@tonic-gate 				goto terminal_hdr;
2553*0Sstevel@tonic-gate 			nexthdr = dsthdr->ip6d_nxt;
2554*0Sstevel@tonic-gate 			ehdrlen = 8 * (dsthdr->ip6d_len + 1);
2555*0Sstevel@tonic-gate 			ret = ah_fix_tlv_options_v6(oi_opt, pi_opt, ehdrlen,
2556*0Sstevel@tonic-gate 			    IPPROTO_DSTOPTS, copy_always);
2557*0Sstevel@tonic-gate 			/*
2558*0Sstevel@tonic-gate 			 * Return a zero offset indicating error if there
2559*0Sstevel@tonic-gate 			 * was error.
2560*0Sstevel@tonic-gate 			 */
2561*0Sstevel@tonic-gate 			if (ret == -1)
2562*0Sstevel@tonic-gate 				return (0);
2563*0Sstevel@tonic-gate 			break;
2564*0Sstevel@tonic-gate 		case IPPROTO_AH:
2565*0Sstevel@tonic-gate 			/*
2566*0Sstevel@tonic-gate 			 * Be conservative in what you send.  We shouldn't
2567*0Sstevel@tonic-gate 			 * see two same-scoped AH's in one packet.
2568*0Sstevel@tonic-gate 			 * (Inner-IP-scoped AH will be hit by terminal
2569*0Sstevel@tonic-gate 			 * header of IP or IPv6.)
2570*0Sstevel@tonic-gate 			 */
2571*0Sstevel@tonic-gate 			ASSERT(!outbound);
2572*0Sstevel@tonic-gate 			return ((uint_t)(pi_opt - (uint8_t *)ip6h));
2573*0Sstevel@tonic-gate 		default:
2574*0Sstevel@tonic-gate 			ASSERT(outbound);
2575*0Sstevel@tonic-gate terminal_hdr:
2576*0Sstevel@tonic-gate 			*prev_nexthdr = IPPROTO_AH;
2577*0Sstevel@tonic-gate 			ah = (ah_t *)pi_opt;
2578*0Sstevel@tonic-gate 			ah->ah_nexthdr = nexthdr;
2579*0Sstevel@tonic-gate 			return ((uint_t)(pi_opt - (uint8_t *)ip6h));
2580*0Sstevel@tonic-gate 		}
2581*0Sstevel@tonic-gate 		pi_opt += ehdrlen;
2582*0Sstevel@tonic-gate 		oi_opt += ehdrlen;
2583*0Sstevel@tonic-gate 	}
2584*0Sstevel@tonic-gate 	/* NOTREACHED */
2585*0Sstevel@tonic-gate }
2586*0Sstevel@tonic-gate 
2587*0Sstevel@tonic-gate static boolean_t
2588*0Sstevel@tonic-gate ah_finish_up(ah_t *phdr_ah, ah_t *inbound_ah, ipsa_t *assoc,
2589*0Sstevel@tonic-gate     int ah_data_sz, int ah_align_sz)
2590*0Sstevel@tonic-gate {
2591*0Sstevel@tonic-gate 	int i;
2592*0Sstevel@tonic-gate 
2593*0Sstevel@tonic-gate 	/*
2594*0Sstevel@tonic-gate 	 * Padding :
2595*0Sstevel@tonic-gate 	 *
2596*0Sstevel@tonic-gate 	 * 1) Authentication data may have to be padded
2597*0Sstevel@tonic-gate 	 * before ICV calculation if ICV is not a multiple
2598*0Sstevel@tonic-gate 	 * of 64 bits. This padding is arbitrary and transmitted
2599*0Sstevel@tonic-gate 	 * with the packet at the end of the authentication data.
2600*0Sstevel@tonic-gate 	 * Payload length should include the padding bytes.
2601*0Sstevel@tonic-gate 	 *
2602*0Sstevel@tonic-gate 	 * 2) Explicit padding of the whole datagram may be
2603*0Sstevel@tonic-gate 	 * required by the algorithm which need not be
2604*0Sstevel@tonic-gate 	 * transmitted. It is assumed that this will be taken
2605*0Sstevel@tonic-gate 	 * care by the algorithm module.
2606*0Sstevel@tonic-gate 	 */
2607*0Sstevel@tonic-gate 	bzero(phdr_ah + 1, ah_data_sz);	/* Zero out ICV for pseudo-hdr. */
2608*0Sstevel@tonic-gate 
2609*0Sstevel@tonic-gate 	if (inbound_ah == NULL) {
2610*0Sstevel@tonic-gate 		/* Outbound AH datagram. */
2611*0Sstevel@tonic-gate 
2612*0Sstevel@tonic-gate 		phdr_ah->ah_length = (ah_align_sz >> 2) + 1;
2613*0Sstevel@tonic-gate 		phdr_ah->ah_reserved = 0;
2614*0Sstevel@tonic-gate 		phdr_ah->ah_spi = assoc->ipsa_spi;
2615*0Sstevel@tonic-gate 
2616*0Sstevel@tonic-gate 		phdr_ah->ah_replay =
2617*0Sstevel@tonic-gate 		    htonl(atomic_add_32_nv(&assoc->ipsa_replay, 1));
2618*0Sstevel@tonic-gate 		if (phdr_ah->ah_replay == 0 && assoc->ipsa_replay_wsize != 0) {
2619*0Sstevel@tonic-gate 			/*
2620*0Sstevel@tonic-gate 			 * XXX We have replay counter wrapping.  We probably
2621*0Sstevel@tonic-gate 			 * want to nuke this SA (and its peer).
2622*0Sstevel@tonic-gate 			 */
2623*0Sstevel@tonic-gate 			ipsec_assocfailure(info.mi_idnum, 0, 0,
2624*0Sstevel@tonic-gate 			    SL_ERROR | SL_CONSOLE | SL_WARN,
2625*0Sstevel@tonic-gate 			    "Outbound AH SA (0x%x), dst %s has wrapped "
2626*0Sstevel@tonic-gate 			    "sequence.\n", phdr_ah->ah_spi,
2627*0Sstevel@tonic-gate 			    assoc->ipsa_dstaddr, assoc->ipsa_addrfam);
2628*0Sstevel@tonic-gate 
2629*0Sstevel@tonic-gate 			sadb_replay_delete(assoc);
2630*0Sstevel@tonic-gate 			/* Caller will free phdr_mp and return NULL. */
2631*0Sstevel@tonic-gate 			return (B_FALSE);
2632*0Sstevel@tonic-gate 		}
2633*0Sstevel@tonic-gate 
2634*0Sstevel@tonic-gate 		if (ah_data_sz != ah_align_sz) {
2635*0Sstevel@tonic-gate 			uchar_t *pad = ((uchar_t *)phdr_ah + sizeof (ah_t) +
2636*0Sstevel@tonic-gate 			    ah_data_sz);
2637*0Sstevel@tonic-gate 
2638*0Sstevel@tonic-gate 			for (i = 0; i < (ah_align_sz - ah_data_sz); i++) {
2639*0Sstevel@tonic-gate 				pad[i] = (uchar_t)i;	/* Fill the padding */
2640*0Sstevel@tonic-gate 			}
2641*0Sstevel@tonic-gate 		}
2642*0Sstevel@tonic-gate 	} else {
2643*0Sstevel@tonic-gate 		/* Inbound AH datagram. */
2644*0Sstevel@tonic-gate 		phdr_ah->ah_nexthdr = inbound_ah->ah_nexthdr;
2645*0Sstevel@tonic-gate 		phdr_ah->ah_length = inbound_ah->ah_length;
2646*0Sstevel@tonic-gate 		phdr_ah->ah_reserved = 0;
2647*0Sstevel@tonic-gate 		ASSERT(inbound_ah->ah_spi == assoc->ipsa_spi);
2648*0Sstevel@tonic-gate 		phdr_ah->ah_spi = inbound_ah->ah_spi;
2649*0Sstevel@tonic-gate 		phdr_ah->ah_replay = inbound_ah->ah_replay;
2650*0Sstevel@tonic-gate 
2651*0Sstevel@tonic-gate 		if (ah_data_sz != ah_align_sz) {
2652*0Sstevel@tonic-gate 			uchar_t *opad = ((uchar_t *)inbound_ah + sizeof (ah_t) +
2653*0Sstevel@tonic-gate 			    ah_data_sz);
2654*0Sstevel@tonic-gate 			uchar_t *pad = ((uchar_t *)phdr_ah + sizeof (ah_t) +
2655*0Sstevel@tonic-gate 			    ah_data_sz);
2656*0Sstevel@tonic-gate 
2657*0Sstevel@tonic-gate 			for (i = 0; i < (ah_align_sz - ah_data_sz); i++) {
2658*0Sstevel@tonic-gate 				pad[i] = opad[i];	/* Copy the padding */
2659*0Sstevel@tonic-gate 			}
2660*0Sstevel@tonic-gate 		}
2661*0Sstevel@tonic-gate 	}
2662*0Sstevel@tonic-gate 
2663*0Sstevel@tonic-gate 	return (B_TRUE);
2664*0Sstevel@tonic-gate }
2665*0Sstevel@tonic-gate 
2666*0Sstevel@tonic-gate /*
2667*0Sstevel@tonic-gate  * Called upon failing the inbound ICV check. The message passed as
2668*0Sstevel@tonic-gate  * argument is freed.
2669*0Sstevel@tonic-gate  */
2670*0Sstevel@tonic-gate static void
2671*0Sstevel@tonic-gate ah_log_bad_auth(mblk_t *ipsec_in)
2672*0Sstevel@tonic-gate {
2673*0Sstevel@tonic-gate 	mblk_t *mp = ipsec_in->b_cont->b_cont;
2674*0Sstevel@tonic-gate 	ipsec_in_t *ii = (ipsec_in_t *)ipsec_in->b_rptr;
2675*0Sstevel@tonic-gate 	boolean_t isv4 = ii->ipsec_in_v4;
2676*0Sstevel@tonic-gate 	ipsa_t *assoc = ii->ipsec_in_ah_sa;
2677*0Sstevel@tonic-gate 	int af;
2678*0Sstevel@tonic-gate 	void *addr;
2679*0Sstevel@tonic-gate 
2680*0Sstevel@tonic-gate 	mp->b_rptr -= ii->ipsec_in_skip_len;
2681*0Sstevel@tonic-gate 
2682*0Sstevel@tonic-gate 	if (isv4) {
2683*0Sstevel@tonic-gate 		ipha_t *ipha = (ipha_t *)mp->b_rptr;
2684*0Sstevel@tonic-gate 		addr = &ipha->ipha_dst;
2685*0Sstevel@tonic-gate 		af = AF_INET;
2686*0Sstevel@tonic-gate 	} else {
2687*0Sstevel@tonic-gate 		ip6_t *ip6h = (ip6_t *)mp->b_rptr;
2688*0Sstevel@tonic-gate 		addr = &ip6h->ip6_dst;
2689*0Sstevel@tonic-gate 		af = AF_INET6;
2690*0Sstevel@tonic-gate 	}
2691*0Sstevel@tonic-gate 
2692*0Sstevel@tonic-gate 	/*
2693*0Sstevel@tonic-gate 	 * Log the event. Don't print to the console, block
2694*0Sstevel@tonic-gate 	 * potential denial-of-service attack.
2695*0Sstevel@tonic-gate 	 */
2696*0Sstevel@tonic-gate 	AH_BUMP_STAT(bad_auth);
2697*0Sstevel@tonic-gate 
2698*0Sstevel@tonic-gate 	ipsec_assocfailure(info.mi_idnum, 0, 0, SL_ERROR | SL_WARN,
2699*0Sstevel@tonic-gate 	    "AH Authentication failed spi %x, dst_addr %s",
2700*0Sstevel@tonic-gate 	    assoc->ipsa_spi, addr, af);
2701*0Sstevel@tonic-gate 
2702*0Sstevel@tonic-gate 	IP_AH_BUMP_STAT(in_discards);
2703*0Sstevel@tonic-gate 	ip_drop_packet(ipsec_in, B_TRUE, NULL, NULL, &ipdrops_ah_bad_auth,
2704*0Sstevel@tonic-gate 	    &ah_dropper);
2705*0Sstevel@tonic-gate }
2706*0Sstevel@tonic-gate 
2707*0Sstevel@tonic-gate /*
2708*0Sstevel@tonic-gate  * Kernel crypto framework callback invoked after completion of async
2709*0Sstevel@tonic-gate  * crypto requests.
2710*0Sstevel@tonic-gate  */
2711*0Sstevel@tonic-gate static void
2712*0Sstevel@tonic-gate ah_kcf_callback(void *arg, int status)
2713*0Sstevel@tonic-gate {
2714*0Sstevel@tonic-gate 	mblk_t *ipsec_mp = (mblk_t *)arg;
2715*0Sstevel@tonic-gate 	ipsec_in_t *ii = (ipsec_in_t *)ipsec_mp->b_rptr;
2716*0Sstevel@tonic-gate 	boolean_t is_inbound = (ii->ipsec_in_type == IPSEC_IN);
2717*0Sstevel@tonic-gate 
2718*0Sstevel@tonic-gate 	ASSERT(ipsec_mp->b_cont != NULL);
2719*0Sstevel@tonic-gate 
2720*0Sstevel@tonic-gate 	if (status == CRYPTO_SUCCESS) {
2721*0Sstevel@tonic-gate 		if (is_inbound) {
2722*0Sstevel@tonic-gate 			if (ah_auth_in_done(ipsec_mp) != IPSEC_STATUS_SUCCESS)
2723*0Sstevel@tonic-gate 				return;
2724*0Sstevel@tonic-gate 			/* finish IPsec processing */
2725*0Sstevel@tonic-gate 			ip_fanout_proto_again(ipsec_mp, NULL, NULL, NULL);
2726*0Sstevel@tonic-gate 		} else {
2727*0Sstevel@tonic-gate 			ipha_t *ipha;
2728*0Sstevel@tonic-gate 
2729*0Sstevel@tonic-gate 			if (ah_auth_out_done(ipsec_mp) != IPSEC_STATUS_SUCCESS)
2730*0Sstevel@tonic-gate 				return;
2731*0Sstevel@tonic-gate 
2732*0Sstevel@tonic-gate 			/* finish IPsec processing */
2733*0Sstevel@tonic-gate 			ipha = (ipha_t *)ipsec_mp->b_cont->b_rptr;
2734*0Sstevel@tonic-gate 			if (IPH_HDR_VERSION(ipha) == IP_VERSION) {
2735*0Sstevel@tonic-gate 				ip_wput_ipsec_out(NULL, ipsec_mp, ipha, NULL,
2736*0Sstevel@tonic-gate 				    NULL);
2737*0Sstevel@tonic-gate 			} else {
2738*0Sstevel@tonic-gate 				ip6_t *ip6h = (ip6_t *)ipha;
2739*0Sstevel@tonic-gate 				ip_wput_ipsec_out_v6(NULL, ipsec_mp, ip6h,
2740*0Sstevel@tonic-gate 				    NULL, NULL);
2741*0Sstevel@tonic-gate 			}
2742*0Sstevel@tonic-gate 		}
2743*0Sstevel@tonic-gate 
2744*0Sstevel@tonic-gate 	} else if (status == CRYPTO_INVALID_MAC) {
2745*0Sstevel@tonic-gate 		ah_log_bad_auth(ipsec_mp);
2746*0Sstevel@tonic-gate 
2747*0Sstevel@tonic-gate 	} else {
2748*0Sstevel@tonic-gate 		ah1dbg(("ah_kcf_callback: crypto failed with 0x%x\n", status));
2749*0Sstevel@tonic-gate 		AH_BUMP_STAT(crypto_failures);
2750*0Sstevel@tonic-gate 		if (is_inbound)
2751*0Sstevel@tonic-gate 			IP_AH_BUMP_STAT(in_discards);
2752*0Sstevel@tonic-gate 		else
2753*0Sstevel@tonic-gate 			AH_BUMP_STAT(out_discards);
2754*0Sstevel@tonic-gate 		ip_drop_packet(ipsec_mp, is_inbound, NULL, NULL,
2755*0Sstevel@tonic-gate 		    &ipdrops_ah_crypto_failed, &ah_dropper);
2756*0Sstevel@tonic-gate 	}
2757*0Sstevel@tonic-gate }
2758*0Sstevel@tonic-gate 
2759*0Sstevel@tonic-gate /*
2760*0Sstevel@tonic-gate  * Invoked on kernel crypto failure during inbound and outbound processing.
2761*0Sstevel@tonic-gate  */
2762*0Sstevel@tonic-gate static void
2763*0Sstevel@tonic-gate ah_crypto_failed(mblk_t *mp, boolean_t is_inbound, int kef_rc)
2764*0Sstevel@tonic-gate {
2765*0Sstevel@tonic-gate 	ah1dbg(("crypto failed for %s AH with 0x%x\n",
2766*0Sstevel@tonic-gate 	    is_inbound ? "inbound" : "outbound", kef_rc));
2767*0Sstevel@tonic-gate 	ip_drop_packet(mp, is_inbound, NULL, NULL, &ipdrops_ah_crypto_failed,
2768*0Sstevel@tonic-gate 	    &ah_dropper);
2769*0Sstevel@tonic-gate 	AH_BUMP_STAT(crypto_failures);
2770*0Sstevel@tonic-gate 	if (is_inbound)
2771*0Sstevel@tonic-gate 		IP_AH_BUMP_STAT(in_discards);
2772*0Sstevel@tonic-gate 	else
2773*0Sstevel@tonic-gate 		AH_BUMP_STAT(out_discards);
2774*0Sstevel@tonic-gate }
2775*0Sstevel@tonic-gate 
2776*0Sstevel@tonic-gate /*
2777*0Sstevel@tonic-gate  * Helper macros for the ah_submit_req_{inbound,outbound}() functions.
2778*0Sstevel@tonic-gate  */
2779*0Sstevel@tonic-gate 
2780*0Sstevel@tonic-gate #define	AH_INIT_CALLREQ(_cr) {						\
2781*0Sstevel@tonic-gate 	(_cr)->cr_flag = CRYPTO_SKIP_REQID|CRYPTO_RESTRICTED;		\
2782*0Sstevel@tonic-gate 	if (ipsec_algs_exec_mode[IPSEC_ALG_AUTH] == IPSEC_ALGS_EXEC_ASYNC) \
2783*0Sstevel@tonic-gate 		(_cr)->cr_flag |= CRYPTO_ALWAYS_QUEUE;			\
2784*0Sstevel@tonic-gate 	(_cr)->cr_callback_arg = ipsec_mp;				\
2785*0Sstevel@tonic-gate 	(_cr)->cr_callback_func = ah_kcf_callback;			\
2786*0Sstevel@tonic-gate }
2787*0Sstevel@tonic-gate 
2788*0Sstevel@tonic-gate #define	AH_INIT_CRYPTO_DATA(data, msglen, mblk) {			\
2789*0Sstevel@tonic-gate 	(data)->cd_format = CRYPTO_DATA_MBLK;				\
2790*0Sstevel@tonic-gate 	(data)->cd_mp = mblk;						\
2791*0Sstevel@tonic-gate 	(data)->cd_offset = 0;						\
2792*0Sstevel@tonic-gate 	(data)->cd_length = msglen;					\
2793*0Sstevel@tonic-gate }
2794*0Sstevel@tonic-gate 
2795*0Sstevel@tonic-gate #define	AH_INIT_CRYPTO_MAC(mac, icvlen, icvbuf) {			\
2796*0Sstevel@tonic-gate 	(mac)->cd_format = CRYPTO_DATA_RAW;				\
2797*0Sstevel@tonic-gate 	(mac)->cd_offset = 0;						\
2798*0Sstevel@tonic-gate 	(mac)->cd_length = icvlen;					\
2799*0Sstevel@tonic-gate 	(mac)->cd_raw.iov_base = icvbuf;				\
2800*0Sstevel@tonic-gate 	(mac)->cd_raw.iov_len = icvlen;					\
2801*0Sstevel@tonic-gate }
2802*0Sstevel@tonic-gate 
2803*0Sstevel@tonic-gate /*
2804*0Sstevel@tonic-gate  * Submit an inbound packet for processing by the crypto framework.
2805*0Sstevel@tonic-gate  */
2806*0Sstevel@tonic-gate static ipsec_status_t
2807*0Sstevel@tonic-gate ah_submit_req_inbound(mblk_t *ipsec_mp, size_t skip_len, uint32_t ah_offset,
2808*0Sstevel@tonic-gate     ipsa_t *assoc)
2809*0Sstevel@tonic-gate {
2810*0Sstevel@tonic-gate 	int kef_rc;
2811*0Sstevel@tonic-gate 	mblk_t *phdr_mp;
2812*0Sstevel@tonic-gate 	crypto_call_req_t call_req;
2813*0Sstevel@tonic-gate 	ipsec_in_t *ii = (ipsec_in_t *)ipsec_mp->b_rptr;
2814*0Sstevel@tonic-gate 	uint_t icv_len = assoc->ipsa_mac_len;
2815*0Sstevel@tonic-gate 	crypto_ctx_template_t ctx_tmpl;
2816*0Sstevel@tonic-gate 
2817*0Sstevel@tonic-gate 	phdr_mp = ipsec_mp->b_cont;
2818*0Sstevel@tonic-gate 	ASSERT(phdr_mp != NULL);
2819*0Sstevel@tonic-gate 	ASSERT(ii->ipsec_in_type == IPSEC_IN);
2820*0Sstevel@tonic-gate 
2821*0Sstevel@tonic-gate 	/* init arguments for the crypto framework */
2822*0Sstevel@tonic-gate 	AH_INIT_CRYPTO_DATA(&ii->ipsec_in_crypto_data, AH_MSGSIZE(phdr_mp),
2823*0Sstevel@tonic-gate 	    phdr_mp);
2824*0Sstevel@tonic-gate 
2825*0Sstevel@tonic-gate 	AH_INIT_CRYPTO_MAC(&ii->ipsec_in_crypto_mac, icv_len,
2826*0Sstevel@tonic-gate 	    (char *)phdr_mp->b_cont->b_rptr - skip_len + ah_offset +
2827*0Sstevel@tonic-gate 	    sizeof (ah_t));
2828*0Sstevel@tonic-gate 
2829*0Sstevel@tonic-gate 	AH_INIT_CALLREQ(&call_req);
2830*0Sstevel@tonic-gate 
2831*0Sstevel@tonic-gate 	ii->ipsec_in_skip_len = skip_len;
2832*0Sstevel@tonic-gate 
2833*0Sstevel@tonic-gate 	IPSEC_CTX_TMPL(assoc, ipsa_authtmpl, IPSEC_ALG_AUTH, ctx_tmpl);
2834*0Sstevel@tonic-gate 
2835*0Sstevel@tonic-gate 	/* call KEF to do the MAC operation */
2836*0Sstevel@tonic-gate 	kef_rc = crypto_mac_verify(&assoc->ipsa_amech,
2837*0Sstevel@tonic-gate 	    &ii->ipsec_in_crypto_data, &assoc->ipsa_kcfauthkey, ctx_tmpl,
2838*0Sstevel@tonic-gate 	    &ii->ipsec_in_crypto_mac, &call_req);
2839*0Sstevel@tonic-gate 
2840*0Sstevel@tonic-gate 	switch (kef_rc) {
2841*0Sstevel@tonic-gate 	case CRYPTO_SUCCESS:
2842*0Sstevel@tonic-gate 		AH_BUMP_STAT(crypto_sync);
2843*0Sstevel@tonic-gate 		return (ah_auth_in_done(ipsec_mp));
2844*0Sstevel@tonic-gate 	case CRYPTO_QUEUED:
2845*0Sstevel@tonic-gate 		/* ah_callback() will be invoked on completion */
2846*0Sstevel@tonic-gate 		AH_BUMP_STAT(crypto_async);
2847*0Sstevel@tonic-gate 		return (IPSEC_STATUS_PENDING);
2848*0Sstevel@tonic-gate 	case CRYPTO_INVALID_MAC:
2849*0Sstevel@tonic-gate 		AH_BUMP_STAT(crypto_sync);
2850*0Sstevel@tonic-gate 		ah_log_bad_auth(ipsec_mp);
2851*0Sstevel@tonic-gate 		return (IPSEC_STATUS_FAILED);
2852*0Sstevel@tonic-gate 	}
2853*0Sstevel@tonic-gate 
2854*0Sstevel@tonic-gate 	ah_crypto_failed(ipsec_mp, B_TRUE, kef_rc);
2855*0Sstevel@tonic-gate 	return (IPSEC_STATUS_FAILED);
2856*0Sstevel@tonic-gate }
2857*0Sstevel@tonic-gate 
2858*0Sstevel@tonic-gate /*
2859*0Sstevel@tonic-gate  * Submit an outbound packet for processing by the crypto framework.
2860*0Sstevel@tonic-gate  */
2861*0Sstevel@tonic-gate static ipsec_status_t
2862*0Sstevel@tonic-gate ah_submit_req_outbound(mblk_t *ipsec_mp, size_t skip_len, ipsa_t *assoc)
2863*0Sstevel@tonic-gate {
2864*0Sstevel@tonic-gate 	int kef_rc;
2865*0Sstevel@tonic-gate 	mblk_t *phdr_mp;
2866*0Sstevel@tonic-gate 	crypto_call_req_t call_req;
2867*0Sstevel@tonic-gate 	ipsec_out_t *io = (ipsec_out_t *)ipsec_mp->b_rptr;
2868*0Sstevel@tonic-gate 	uint_t icv_len = assoc->ipsa_mac_len;
2869*0Sstevel@tonic-gate 
2870*0Sstevel@tonic-gate 	phdr_mp = ipsec_mp->b_cont;
2871*0Sstevel@tonic-gate 	ASSERT(phdr_mp != NULL);
2872*0Sstevel@tonic-gate 	ASSERT(io->ipsec_out_type == IPSEC_OUT);
2873*0Sstevel@tonic-gate 
2874*0Sstevel@tonic-gate 	/* init arguments for the crypto framework */
2875*0Sstevel@tonic-gate 	AH_INIT_CRYPTO_DATA(&io->ipsec_out_crypto_data, AH_MSGSIZE(phdr_mp),
2876*0Sstevel@tonic-gate 	    phdr_mp);
2877*0Sstevel@tonic-gate 
2878*0Sstevel@tonic-gate 	AH_INIT_CRYPTO_MAC(&io->ipsec_out_crypto_mac, icv_len,
2879*0Sstevel@tonic-gate 	    (char *)phdr_mp->b_wptr);
2880*0Sstevel@tonic-gate 
2881*0Sstevel@tonic-gate 	AH_INIT_CALLREQ(&call_req);
2882*0Sstevel@tonic-gate 
2883*0Sstevel@tonic-gate 	io->ipsec_out_skip_len = skip_len;
2884*0Sstevel@tonic-gate 
2885*0Sstevel@tonic-gate 	ASSERT(io->ipsec_out_ah_sa != NULL);
2886*0Sstevel@tonic-gate 
2887*0Sstevel@tonic-gate 	/* call KEF to do the MAC operation */
2888*0Sstevel@tonic-gate 	kef_rc = crypto_mac(&assoc->ipsa_amech, &io->ipsec_out_crypto_data,
2889*0Sstevel@tonic-gate 	    &assoc->ipsa_kcfauthkey, assoc->ipsa_authtmpl,
2890*0Sstevel@tonic-gate 	    &io->ipsec_out_crypto_mac, &call_req);
2891*0Sstevel@tonic-gate 
2892*0Sstevel@tonic-gate 	switch (kef_rc) {
2893*0Sstevel@tonic-gate 	case CRYPTO_SUCCESS:
2894*0Sstevel@tonic-gate 		AH_BUMP_STAT(crypto_sync);
2895*0Sstevel@tonic-gate 		return (ah_auth_out_done(ipsec_mp));
2896*0Sstevel@tonic-gate 	case CRYPTO_QUEUED:
2897*0Sstevel@tonic-gate 		/* ah_callback() will be invoked on completion */
2898*0Sstevel@tonic-gate 		AH_BUMP_STAT(crypto_async);
2899*0Sstevel@tonic-gate 		return (IPSEC_STATUS_PENDING);
2900*0Sstevel@tonic-gate 	}
2901*0Sstevel@tonic-gate 
2902*0Sstevel@tonic-gate 	ah_crypto_failed(ipsec_mp, B_FALSE, kef_rc);
2903*0Sstevel@tonic-gate 	return (IPSEC_STATUS_FAILED);
2904*0Sstevel@tonic-gate }
2905*0Sstevel@tonic-gate 
2906*0Sstevel@tonic-gate /*
2907*0Sstevel@tonic-gate  * This function constructs a pseudo header by looking at the IP header
2908*0Sstevel@tonic-gate  * and options if any. This is called for both outbound and inbound,
2909*0Sstevel@tonic-gate  * before computing the ICV.
2910*0Sstevel@tonic-gate  */
2911*0Sstevel@tonic-gate static mblk_t *
2912*0Sstevel@tonic-gate ah_process_ip_options_v6(mblk_t *mp, ipsa_t *assoc, int *length_to_skip,
2913*0Sstevel@tonic-gate     uint_t ah_data_sz, boolean_t outbound)
2914*0Sstevel@tonic-gate {
2915*0Sstevel@tonic-gate 	ip6_t	*ip6h;
2916*0Sstevel@tonic-gate 	ip6_t	*oip6h;
2917*0Sstevel@tonic-gate 	mblk_t 	*phdr_mp;
2918*0Sstevel@tonic-gate 	int option_length;
2919*0Sstevel@tonic-gate 	uint_t	ah_align_sz;
2920*0Sstevel@tonic-gate 	uint_t ah_offset;
2921*0Sstevel@tonic-gate 	int hdr_size;
2922*0Sstevel@tonic-gate 
2923*0Sstevel@tonic-gate 	/*
2924*0Sstevel@tonic-gate 	 * Allocate space for the authentication data also. It is
2925*0Sstevel@tonic-gate 	 * useful both during the ICV calculation where we need to
2926*0Sstevel@tonic-gate 	 * feed in zeroes and while sending the datagram back to IP
2927*0Sstevel@tonic-gate 	 * where we will be using the same space.
2928*0Sstevel@tonic-gate 	 *
2929*0Sstevel@tonic-gate 	 * We need to allocate space for padding bytes if it is not
2930*0Sstevel@tonic-gate 	 * a multiple of IPV6_PADDING_ALIGN.
2931*0Sstevel@tonic-gate 	 *
2932*0Sstevel@tonic-gate 	 * In addition, we allocate space for the ICV computed by
2933*0Sstevel@tonic-gate 	 * the kernel crypto framework, saving us a separate kmem
2934*0Sstevel@tonic-gate 	 * allocation down the road.
2935*0Sstevel@tonic-gate 	 */
2936*0Sstevel@tonic-gate 
2937*0Sstevel@tonic-gate 	ah_align_sz = P2ALIGN(ah_data_sz + IPV6_PADDING_ALIGN - 1,
2938*0Sstevel@tonic-gate 	    IPV6_PADDING_ALIGN);
2939*0Sstevel@tonic-gate 
2940*0Sstevel@tonic-gate 	ASSERT(ah_align_sz >= ah_data_sz);
2941*0Sstevel@tonic-gate 
2942*0Sstevel@tonic-gate 	hdr_size = ipsec_ah_get_hdr_size_v6(mp, B_FALSE);
2943*0Sstevel@tonic-gate 	option_length = hdr_size - IPV6_HDR_LEN;
2944*0Sstevel@tonic-gate 
2945*0Sstevel@tonic-gate 	/* This was not included in ipsec_ah_get_hdr_size_v6() */
2946*0Sstevel@tonic-gate 	hdr_size += (sizeof (ah_t) + ah_align_sz);
2947*0Sstevel@tonic-gate 
2948*0Sstevel@tonic-gate 	if (!outbound && (MBLKL(mp) < hdr_size)) {
2949*0Sstevel@tonic-gate 		/*
2950*0Sstevel@tonic-gate 		 * We have post-AH header options in a separate mblk,
2951*0Sstevel@tonic-gate 		 * a pullup is required.
2952*0Sstevel@tonic-gate 		 */
2953*0Sstevel@tonic-gate 		if (!pullupmsg(mp, hdr_size))
2954*0Sstevel@tonic-gate 			return (NULL);
2955*0Sstevel@tonic-gate 	}
2956*0Sstevel@tonic-gate 
2957*0Sstevel@tonic-gate 	if ((phdr_mp = allocb(hdr_size + ah_data_sz, BPRI_HI)) == NULL) {
2958*0Sstevel@tonic-gate 		return (NULL);
2959*0Sstevel@tonic-gate 	}
2960*0Sstevel@tonic-gate 
2961*0Sstevel@tonic-gate 	oip6h = (ip6_t *)mp->b_rptr;
2962*0Sstevel@tonic-gate 
2963*0Sstevel@tonic-gate 	/*
2964*0Sstevel@tonic-gate 	 * Form the basic IP header first. Zero out the header
2965*0Sstevel@tonic-gate 	 * so that the mutable fields are zeroed out.
2966*0Sstevel@tonic-gate 	 */
2967*0Sstevel@tonic-gate 	ip6h = (ip6_t *)phdr_mp->b_rptr;
2968*0Sstevel@tonic-gate 	bzero(ip6h, sizeof (ip6_t));
2969*0Sstevel@tonic-gate 	ip6h->ip6_vcf = IPV6_DEFAULT_VERS_AND_FLOW;
2970*0Sstevel@tonic-gate 
2971*0Sstevel@tonic-gate 	if (outbound) {
2972*0Sstevel@tonic-gate 		/*
2973*0Sstevel@tonic-gate 		 * Include the size of AH and authentication data.
2974*0Sstevel@tonic-gate 		 * This is how our recipient would compute the
2975*0Sstevel@tonic-gate 		 * authentication data. Look at what we do in the
2976*0Sstevel@tonic-gate 		 * inbound case below.
2977*0Sstevel@tonic-gate 		 */
2978*0Sstevel@tonic-gate 		ip6h->ip6_plen = htons(ntohs(oip6h->ip6_plen) +
2979*0Sstevel@tonic-gate 		    sizeof (ah_t) + ah_align_sz);
2980*0Sstevel@tonic-gate 	} else {
2981*0Sstevel@tonic-gate 		ip6h->ip6_plen = oip6h->ip6_plen;
2982*0Sstevel@tonic-gate 	}
2983*0Sstevel@tonic-gate 
2984*0Sstevel@tonic-gate 	ip6h->ip6_src = oip6h->ip6_src;
2985*0Sstevel@tonic-gate 	ip6h->ip6_dst = oip6h->ip6_dst;
2986*0Sstevel@tonic-gate 
2987*0Sstevel@tonic-gate 	*length_to_skip = IPV6_HDR_LEN;
2988*0Sstevel@tonic-gate 	if (option_length == 0) {
2989*0Sstevel@tonic-gate 		/* Form the AH header */
2990*0Sstevel@tonic-gate 		ip6h->ip6_nxt = IPPROTO_AH;
2991*0Sstevel@tonic-gate 		((ah_t *)(ip6h + 1))->ah_nexthdr = oip6h->ip6_nxt;
2992*0Sstevel@tonic-gate 		ah_offset = *length_to_skip;
2993*0Sstevel@tonic-gate 	} else {
2994*0Sstevel@tonic-gate 		ip6h->ip6_nxt = oip6h->ip6_nxt;
2995*0Sstevel@tonic-gate 		/* option_length does not include the AH header's size */
2996*0Sstevel@tonic-gate 		*length_to_skip += option_length;
2997*0Sstevel@tonic-gate 
2998*0Sstevel@tonic-gate 		ah_offset = ah_fix_phdr_v6(ip6h, oip6h, outbound, B_FALSE);
2999*0Sstevel@tonic-gate 		if (ah_offset == 0) {
3000*0Sstevel@tonic-gate 			ip_drop_packet(phdr_mp, !outbound, NULL, NULL,
3001*0Sstevel@tonic-gate 			    &ipdrops_ah_bad_v6_hdrs, &ah_dropper);
3002*0Sstevel@tonic-gate 			return (NULL);
3003*0Sstevel@tonic-gate 		}
3004*0Sstevel@tonic-gate 	}
3005*0Sstevel@tonic-gate 
3006*0Sstevel@tonic-gate 	if (!ah_finish_up(((ah_t *)((uint8_t *)ip6h + ah_offset)),
3007*0Sstevel@tonic-gate 	    (outbound ? NULL : ((ah_t *)((uint8_t *)oip6h + ah_offset))),
3008*0Sstevel@tonic-gate 	    assoc, ah_data_sz, ah_align_sz)) {
3009*0Sstevel@tonic-gate 		freeb(phdr_mp);
3010*0Sstevel@tonic-gate 		/*
3011*0Sstevel@tonic-gate 		 * Returning NULL will tell the caller to
3012*0Sstevel@tonic-gate 		 * IPSA_REFELE(), free the memory, etc.
3013*0Sstevel@tonic-gate 		 */
3014*0Sstevel@tonic-gate 		return (NULL);
3015*0Sstevel@tonic-gate 	}
3016*0Sstevel@tonic-gate 
3017*0Sstevel@tonic-gate 	phdr_mp->b_wptr = ((uint8_t *)ip6h + ah_offset + sizeof (ah_t) +
3018*0Sstevel@tonic-gate 	    ah_align_sz);
3019*0Sstevel@tonic-gate 	if (!outbound)
3020*0Sstevel@tonic-gate 		*length_to_skip += sizeof (ah_t) + ah_align_sz;
3021*0Sstevel@tonic-gate 	return (phdr_mp);
3022*0Sstevel@tonic-gate }
3023*0Sstevel@tonic-gate 
3024*0Sstevel@tonic-gate /*
3025*0Sstevel@tonic-gate  * This function constructs a pseudo header by looking at the IP header
3026*0Sstevel@tonic-gate  * and options if any. This is called for both outbound and inbound,
3027*0Sstevel@tonic-gate  * before computing the ICV.
3028*0Sstevel@tonic-gate  */
3029*0Sstevel@tonic-gate static mblk_t *
3030*0Sstevel@tonic-gate ah_process_ip_options_v4(mblk_t *mp, ipsa_t *assoc, int *length_to_skip,
3031*0Sstevel@tonic-gate     uint_t ah_data_sz, boolean_t outbound)
3032*0Sstevel@tonic-gate {
3033*0Sstevel@tonic-gate 	ipoptp_t opts;
3034*0Sstevel@tonic-gate 	uint32_t option_length;
3035*0Sstevel@tonic-gate 	ipha_t	*ipha;
3036*0Sstevel@tonic-gate 	ipha_t	*oipha;
3037*0Sstevel@tonic-gate 	mblk_t 	*phdr_mp;
3038*0Sstevel@tonic-gate 	int	 size;
3039*0Sstevel@tonic-gate 	uchar_t	*optptr;
3040*0Sstevel@tonic-gate 	uint8_t optval;
3041*0Sstevel@tonic-gate 	uint8_t optlen;
3042*0Sstevel@tonic-gate 	ipaddr_t dst;
3043*0Sstevel@tonic-gate 	uint32_t v_hlen_tos_len;
3044*0Sstevel@tonic-gate 	int ip_hdr_length;
3045*0Sstevel@tonic-gate 	uint_t	ah_align_sz;
3046*0Sstevel@tonic-gate 	uint32_t off;
3047*0Sstevel@tonic-gate 
3048*0Sstevel@tonic-gate #ifdef	_BIG_ENDIAN
3049*0Sstevel@tonic-gate #define	V_HLEN	(v_hlen_tos_len >> 24)
3050*0Sstevel@tonic-gate #else
3051*0Sstevel@tonic-gate #define	V_HLEN	(v_hlen_tos_len & 0xFF)
3052*0Sstevel@tonic-gate #endif
3053*0Sstevel@tonic-gate 
3054*0Sstevel@tonic-gate 	oipha = (ipha_t *)mp->b_rptr;
3055*0Sstevel@tonic-gate 	v_hlen_tos_len = ((uint32_t *)oipha)[0];
3056*0Sstevel@tonic-gate 
3057*0Sstevel@tonic-gate 	/*
3058*0Sstevel@tonic-gate 	 * Allocate space for the authentication data also. It is
3059*0Sstevel@tonic-gate 	 * useful both during the ICV calculation where we need to
3060*0Sstevel@tonic-gate 	 * feed in zeroes and while sending the datagram back to IP
3061*0Sstevel@tonic-gate 	 * where we will be using the same space.
3062*0Sstevel@tonic-gate 	 *
3063*0Sstevel@tonic-gate 	 * We need to allocate space for padding bytes if it is not
3064*0Sstevel@tonic-gate 	 * a multiple of IPV4_PADDING_ALIGN.
3065*0Sstevel@tonic-gate 	 *
3066*0Sstevel@tonic-gate 	 * In addition, we allocate space for the ICV computed by
3067*0Sstevel@tonic-gate 	 * the kernel crypto framework, saving us a separate kmem
3068*0Sstevel@tonic-gate 	 * allocation down the road.
3069*0Sstevel@tonic-gate 	 */
3070*0Sstevel@tonic-gate 
3071*0Sstevel@tonic-gate 	ah_align_sz = P2ALIGN(ah_data_sz + IPV4_PADDING_ALIGN - 1,
3072*0Sstevel@tonic-gate 	    IPV4_PADDING_ALIGN);
3073*0Sstevel@tonic-gate 
3074*0Sstevel@tonic-gate 	ASSERT(ah_align_sz >= ah_data_sz);
3075*0Sstevel@tonic-gate 
3076*0Sstevel@tonic-gate 	size = IP_SIMPLE_HDR_LENGTH + sizeof (ah_t) + ah_align_sz +
3077*0Sstevel@tonic-gate 	    ah_data_sz;
3078*0Sstevel@tonic-gate 
3079*0Sstevel@tonic-gate 	if (V_HLEN != IP_SIMPLE_HDR_VERSION) {
3080*0Sstevel@tonic-gate 		option_length = oipha->ipha_version_and_hdr_length -
3081*0Sstevel@tonic-gate 		    (uint8_t)((IP_VERSION << 4) +
3082*0Sstevel@tonic-gate 		    IP_SIMPLE_HDR_LENGTH_IN_WORDS);
3083*0Sstevel@tonic-gate 		option_length <<= 2;
3084*0Sstevel@tonic-gate 		size += option_length;
3085*0Sstevel@tonic-gate 	}
3086*0Sstevel@tonic-gate 
3087*0Sstevel@tonic-gate 	if ((phdr_mp = allocb(size, BPRI_HI)) == NULL) {
3088*0Sstevel@tonic-gate 		return (NULL);
3089*0Sstevel@tonic-gate 	}
3090*0Sstevel@tonic-gate 
3091*0Sstevel@tonic-gate 	/*
3092*0Sstevel@tonic-gate 	 * Form the basic IP header first.
3093*0Sstevel@tonic-gate 	 */
3094*0Sstevel@tonic-gate 	ipha = (ipha_t *)phdr_mp->b_rptr;
3095*0Sstevel@tonic-gate 	ipha->ipha_version_and_hdr_length = oipha->ipha_version_and_hdr_length;
3096*0Sstevel@tonic-gate 	ipha->ipha_type_of_service = 0;
3097*0Sstevel@tonic-gate 
3098*0Sstevel@tonic-gate 	if (outbound) {
3099*0Sstevel@tonic-gate 		/*
3100*0Sstevel@tonic-gate 		 * Include the size of AH and authentication data.
3101*0Sstevel@tonic-gate 		 * This is how our recipient would compute the
3102*0Sstevel@tonic-gate 		 * authentication data. Look at what we do in the
3103*0Sstevel@tonic-gate 		 * inbound case below.
3104*0Sstevel@tonic-gate 		 */
3105*0Sstevel@tonic-gate 		ipha->ipha_length = ntohs(htons(oipha->ipha_length) +
3106*0Sstevel@tonic-gate 		    sizeof (ah_t) + ah_align_sz);
3107*0Sstevel@tonic-gate 	} else {
3108*0Sstevel@tonic-gate 		ipha->ipha_length = oipha->ipha_length;
3109*0Sstevel@tonic-gate 	}
3110*0Sstevel@tonic-gate 
3111*0Sstevel@tonic-gate 	ipha->ipha_ident = oipha->ipha_ident;
3112*0Sstevel@tonic-gate 	ipha->ipha_fragment_offset_and_flags = 0;
3113*0Sstevel@tonic-gate 	ipha->ipha_ttl = 0;
3114*0Sstevel@tonic-gate 	ipha->ipha_protocol = IPPROTO_AH;
3115*0Sstevel@tonic-gate 	ipha->ipha_hdr_checksum = 0;
3116*0Sstevel@tonic-gate 	ipha->ipha_src = oipha->ipha_src;
3117*0Sstevel@tonic-gate 	ipha->ipha_dst = dst = oipha->ipha_dst;
3118*0Sstevel@tonic-gate 
3119*0Sstevel@tonic-gate 	/*
3120*0Sstevel@tonic-gate 	 * If there is no option to process return now.
3121*0Sstevel@tonic-gate 	 */
3122*0Sstevel@tonic-gate 	ip_hdr_length = IP_SIMPLE_HDR_LENGTH;
3123*0Sstevel@tonic-gate 
3124*0Sstevel@tonic-gate 	if (V_HLEN == IP_SIMPLE_HDR_VERSION) {
3125*0Sstevel@tonic-gate 		/* Form the AH header */
3126*0Sstevel@tonic-gate 		goto ah_hdr;
3127*0Sstevel@tonic-gate 	}
3128*0Sstevel@tonic-gate 
3129*0Sstevel@tonic-gate 	ip_hdr_length += option_length;
3130*0Sstevel@tonic-gate 
3131*0Sstevel@tonic-gate 	/*
3132*0Sstevel@tonic-gate 	 * We have options. In the outbound case for source route,
3133*0Sstevel@tonic-gate 	 * ULP has already moved the first hop, which is now in
3134*0Sstevel@tonic-gate 	 * ipha_dst. We need the final destination for the calculation
3135*0Sstevel@tonic-gate 	 * of authentication data. And also make sure that mutable
3136*0Sstevel@tonic-gate 	 * and experimental fields are zeroed out in the IP options.
3137*0Sstevel@tonic-gate 	 */
3138*0Sstevel@tonic-gate 
3139*0Sstevel@tonic-gate 	bcopy(&oipha[1], &ipha[1], option_length);
3140*0Sstevel@tonic-gate 
3141*0Sstevel@tonic-gate 	for (optval = ipoptp_first(&opts, ipha);
3142*0Sstevel@tonic-gate 	    optval != IPOPT_EOL;
3143*0Sstevel@tonic-gate 	    optval = ipoptp_next(&opts)) {
3144*0Sstevel@tonic-gate 		optptr = opts.ipoptp_cur;
3145*0Sstevel@tonic-gate 		optlen = opts.ipoptp_len;
3146*0Sstevel@tonic-gate 		switch (optval) {
3147*0Sstevel@tonic-gate 		case IPOPT_EXTSEC:
3148*0Sstevel@tonic-gate 		case IPOPT_COMSEC:
3149*0Sstevel@tonic-gate 		case IPOPT_RA:
3150*0Sstevel@tonic-gate 		case IPOPT_SDMDD:
3151*0Sstevel@tonic-gate 		case IPOPT_SECURITY:
3152*0Sstevel@tonic-gate 			/*
3153*0Sstevel@tonic-gate 			 * These options are Immutable, leave them as-is.
3154*0Sstevel@tonic-gate 			 * Note that IPOPT_NOP is also Immutable, but it
3155*0Sstevel@tonic-gate 			 * was skipped by ipoptp_next() and thus remains
3156*0Sstevel@tonic-gate 			 * intact in the header.
3157*0Sstevel@tonic-gate 			 */
3158*0Sstevel@tonic-gate 			break;
3159*0Sstevel@tonic-gate 		case IPOPT_SSRR:
3160*0Sstevel@tonic-gate 		case IPOPT_LSRR:
3161*0Sstevel@tonic-gate 			if ((opts.ipoptp_flags & IPOPTP_ERROR) != 0)
3162*0Sstevel@tonic-gate 				goto bad_ipv4opt;
3163*0Sstevel@tonic-gate 			/*
3164*0Sstevel@tonic-gate 			 * These two are mutable and will be zeroed, but
3165*0Sstevel@tonic-gate 			 * first get the final destination.
3166*0Sstevel@tonic-gate 			 */
3167*0Sstevel@tonic-gate 			off = optptr[IPOPT_OFFSET];
3168*0Sstevel@tonic-gate 			/*
3169*0Sstevel@tonic-gate 			 * If one of the conditions is true, it means
3170*0Sstevel@tonic-gate 			 * end of options and dst already has the right
3171*0Sstevel@tonic-gate 			 * value. So, just fall through.
3172*0Sstevel@tonic-gate 			 */
3173*0Sstevel@tonic-gate 			if (!(optlen < IP_ADDR_LEN || off > optlen - 3)) {
3174*0Sstevel@tonic-gate 				off = optlen - IP_ADDR_LEN;
3175*0Sstevel@tonic-gate 				bcopy(&optptr[off], &dst, IP_ADDR_LEN);
3176*0Sstevel@tonic-gate 			}
3177*0Sstevel@tonic-gate 			/* FALLTHRU */
3178*0Sstevel@tonic-gate 		case IPOPT_RR:
3179*0Sstevel@tonic-gate 		case IPOPT_TS:
3180*0Sstevel@tonic-gate 		case IPOPT_SATID:
3181*0Sstevel@tonic-gate 		default:
3182*0Sstevel@tonic-gate 			/*
3183*0Sstevel@tonic-gate 			 * optlen should include from the beginning of an
3184*0Sstevel@tonic-gate 			 * option.
3185*0Sstevel@tonic-gate 			 * NOTE : Stream Identifier Option (SID): RFC 791
3186*0Sstevel@tonic-gate 			 * shows the bit pattern of optlen as 2 and documents
3187*0Sstevel@tonic-gate 			 * the length as 4. We assume it to be 2 here.
3188*0Sstevel@tonic-gate 			 */
3189*0Sstevel@tonic-gate 			bzero(optptr, optlen);
3190*0Sstevel@tonic-gate 			break;
3191*0Sstevel@tonic-gate 		}
3192*0Sstevel@tonic-gate 	}
3193*0Sstevel@tonic-gate 
3194*0Sstevel@tonic-gate 	if ((opts.ipoptp_flags & IPOPTP_ERROR) != 0) {
3195*0Sstevel@tonic-gate bad_ipv4opt:
3196*0Sstevel@tonic-gate 		ah1dbg(("AH : bad IPv4 option"));
3197*0Sstevel@tonic-gate 		freeb(phdr_mp);
3198*0Sstevel@tonic-gate 		return (NULL);
3199*0Sstevel@tonic-gate 	}
3200*0Sstevel@tonic-gate 
3201*0Sstevel@tonic-gate 	/*
3202*0Sstevel@tonic-gate 	 * Don't change ipha_dst for an inbound datagram as it points
3203*0Sstevel@tonic-gate 	 * to the right value. Only for the outbound with LSRR/SSRR,
3204*0Sstevel@tonic-gate 	 * because of ip_massage_options called by the ULP, ipha_dst
3205*0Sstevel@tonic-gate 	 * points to the first hop and we need to use the final
3206*0Sstevel@tonic-gate 	 * destination for computing the ICV.
3207*0Sstevel@tonic-gate 	 */
3208*0Sstevel@tonic-gate 
3209*0Sstevel@tonic-gate 	if (outbound)
3210*0Sstevel@tonic-gate 		ipha->ipha_dst = dst;
3211*0Sstevel@tonic-gate ah_hdr:
3212*0Sstevel@tonic-gate 	((ah_t *)((uint8_t *)ipha + ip_hdr_length))->ah_nexthdr =
3213*0Sstevel@tonic-gate 	    oipha->ipha_protocol;
3214*0Sstevel@tonic-gate 	if (!ah_finish_up(((ah_t *)((uint8_t *)ipha + ip_hdr_length)),
3215*0Sstevel@tonic-gate 	    (outbound ? NULL : ((ah_t *)((uint8_t *)oipha + ip_hdr_length))),
3216*0Sstevel@tonic-gate 	    assoc, ah_data_sz, ah_align_sz)) {
3217*0Sstevel@tonic-gate 		freeb(phdr_mp);
3218*0Sstevel@tonic-gate 		/*
3219*0Sstevel@tonic-gate 		 * Returning NULL will tell the caller to IPSA_REFELE(), free
3220*0Sstevel@tonic-gate 		 * the memory, etc.
3221*0Sstevel@tonic-gate 		 */
3222*0Sstevel@tonic-gate 		return (NULL);
3223*0Sstevel@tonic-gate 	}
3224*0Sstevel@tonic-gate 
3225*0Sstevel@tonic-gate 	phdr_mp->b_wptr = ((uchar_t *)ipha + ip_hdr_length +
3226*0Sstevel@tonic-gate 	    sizeof (ah_t) + ah_align_sz);
3227*0Sstevel@tonic-gate 
3228*0Sstevel@tonic-gate 	ASSERT(phdr_mp->b_wptr <= phdr_mp->b_datap->db_lim);
3229*0Sstevel@tonic-gate 	if (outbound)
3230*0Sstevel@tonic-gate 		*length_to_skip = ip_hdr_length;
3231*0Sstevel@tonic-gate 	else
3232*0Sstevel@tonic-gate 		*length_to_skip = ip_hdr_length + sizeof (ah_t) + ah_align_sz;
3233*0Sstevel@tonic-gate 	return (phdr_mp);
3234*0Sstevel@tonic-gate }
3235*0Sstevel@tonic-gate 
3236*0Sstevel@tonic-gate /*
3237*0Sstevel@tonic-gate  * Authenticate an outbound datagram. This function is called
3238*0Sstevel@tonic-gate  * whenever IP sends an outbound datagram that needs authentication.
3239*0Sstevel@tonic-gate  */
3240*0Sstevel@tonic-gate static ipsec_status_t
3241*0Sstevel@tonic-gate ah_outbound(mblk_t *ipsec_out)
3242*0Sstevel@tonic-gate {
3243*0Sstevel@tonic-gate 	mblk_t *mp;
3244*0Sstevel@tonic-gate 	mblk_t *phdr_mp;
3245*0Sstevel@tonic-gate 	ipsec_out_t *oi;
3246*0Sstevel@tonic-gate 	ipsa_t *assoc;
3247*0Sstevel@tonic-gate 	int length_to_skip;
3248*0Sstevel@tonic-gate 	uint_t ah_align_sz;
3249*0Sstevel@tonic-gate 	uint_t age_bytes;
3250*0Sstevel@tonic-gate 
3251*0Sstevel@tonic-gate 	/*
3252*0Sstevel@tonic-gate 	 * Construct the chain of mblks
3253*0Sstevel@tonic-gate 	 *
3254*0Sstevel@tonic-gate 	 * IPSEC_OUT->PSEUDO_HDR->DATA
3255*0Sstevel@tonic-gate 	 *
3256*0Sstevel@tonic-gate 	 * one by one.
3257*0Sstevel@tonic-gate 	 */
3258*0Sstevel@tonic-gate 
3259*0Sstevel@tonic-gate 	AH_BUMP_STAT(out_requests);
3260*0Sstevel@tonic-gate 
3261*0Sstevel@tonic-gate 	ASSERT(ipsec_out->b_datap->db_type == M_CTL);
3262*0Sstevel@tonic-gate 
3263*0Sstevel@tonic-gate 	ASSERT(MBLKL(ipsec_out) >= sizeof (ipsec_info_t));
3264*0Sstevel@tonic-gate 
3265*0Sstevel@tonic-gate 	mp = ipsec_out->b_cont;
3266*0Sstevel@tonic-gate 	oi = (ipsec_out_t *)ipsec_out->b_rptr;
3267*0Sstevel@tonic-gate 
3268*0Sstevel@tonic-gate 	ASSERT(mp->b_datap->db_type == M_DATA);
3269*0Sstevel@tonic-gate 
3270*0Sstevel@tonic-gate 	assoc = oi->ipsec_out_ah_sa;
3271*0Sstevel@tonic-gate 	ASSERT(assoc != NULL);
3272*0Sstevel@tonic-gate 	if (assoc->ipsa_usetime == 0)
3273*0Sstevel@tonic-gate 		ah_set_usetime(assoc, B_FALSE);
3274*0Sstevel@tonic-gate 
3275*0Sstevel@tonic-gate 	/*
3276*0Sstevel@tonic-gate 	 * Age SA according to number of bytes that will be sent after
3277*0Sstevel@tonic-gate 	 * adding the AH header, ICV, and padding to the packet.
3278*0Sstevel@tonic-gate 	 */
3279*0Sstevel@tonic-gate 
3280*0Sstevel@tonic-gate 	if (oi->ipsec_out_v4) {
3281*0Sstevel@tonic-gate 		ipha_t *ipha = (ipha_t *)mp->b_rptr;
3282*0Sstevel@tonic-gate 		ah_align_sz = P2ALIGN(assoc->ipsa_mac_len +
3283*0Sstevel@tonic-gate 		    IPV4_PADDING_ALIGN - 1, IPV4_PADDING_ALIGN);
3284*0Sstevel@tonic-gate 		age_bytes = ntohs(ipha->ipha_length) + sizeof (ah_t) +
3285*0Sstevel@tonic-gate 		    ah_align_sz;
3286*0Sstevel@tonic-gate 	} else {
3287*0Sstevel@tonic-gate 		ip6_t *ip6h = (ip6_t *)mp->b_rptr;
3288*0Sstevel@tonic-gate 		ah_align_sz = P2ALIGN(assoc->ipsa_mac_len +
3289*0Sstevel@tonic-gate 		    IPV6_PADDING_ALIGN - 1, IPV6_PADDING_ALIGN);
3290*0Sstevel@tonic-gate 		age_bytes = sizeof (ip6_t) + ntohs(ip6h->ip6_plen) +
3291*0Sstevel@tonic-gate 			sizeof (ah_t) + ah_align_sz;
3292*0Sstevel@tonic-gate 	}
3293*0Sstevel@tonic-gate 
3294*0Sstevel@tonic-gate 	if (!ah_age_bytes(assoc, age_bytes, B_FALSE)) {
3295*0Sstevel@tonic-gate 		/* rig things as if ipsec_getassocbyconn() failed */
3296*0Sstevel@tonic-gate 		ipsec_assocfailure(info.mi_idnum, 0, 0, SL_ERROR | SL_WARN,
3297*0Sstevel@tonic-gate 		    "AH association 0x%x, dst %s had bytes expire.\n",
3298*0Sstevel@tonic-gate 		    ntohl(assoc->ipsa_spi), assoc->ipsa_dstaddr, AF_INET);
3299*0Sstevel@tonic-gate 		freemsg(ipsec_out);
3300*0Sstevel@tonic-gate 		return (IPSEC_STATUS_FAILED);
3301*0Sstevel@tonic-gate 	}
3302*0Sstevel@tonic-gate 
3303*0Sstevel@tonic-gate /* EXPORT DELETE START */
3304*0Sstevel@tonic-gate 	if (oi->ipsec_out_is_capab_ill) {
3305*0Sstevel@tonic-gate 		ah3dbg(("ah_outbound: pkt can be accelerated\n"));
3306*0Sstevel@tonic-gate 		if (oi->ipsec_out_v4)
3307*0Sstevel@tonic-gate 			return (ah_outbound_accelerated_v4(ipsec_out, assoc));
3308*0Sstevel@tonic-gate 		else
3309*0Sstevel@tonic-gate 			return (ah_outbound_accelerated_v6(ipsec_out, assoc));
3310*0Sstevel@tonic-gate 	}
3311*0Sstevel@tonic-gate 	AH_BUMP_STAT(noaccel);
3312*0Sstevel@tonic-gate /* EXPORT DELETE END */
3313*0Sstevel@tonic-gate 
3314*0Sstevel@tonic-gate 	/*
3315*0Sstevel@tonic-gate 	 * Insert pseudo header:
3316*0Sstevel@tonic-gate 	 * IPSEC_INFO -> [IP, ULP] => IPSEC_INFO -> [IP, AH, ICV] -> ULP
3317*0Sstevel@tonic-gate 	 */
3318*0Sstevel@tonic-gate 
3319*0Sstevel@tonic-gate 	if (oi->ipsec_out_v4) {
3320*0Sstevel@tonic-gate 		phdr_mp = ah_process_ip_options_v4(mp, assoc, &length_to_skip,
3321*0Sstevel@tonic-gate 		    assoc->ipsa_mac_len, B_TRUE);
3322*0Sstevel@tonic-gate 	} else {
3323*0Sstevel@tonic-gate 		phdr_mp = ah_process_ip_options_v6(mp, assoc, &length_to_skip,
3324*0Sstevel@tonic-gate 		    assoc->ipsa_mac_len, B_TRUE);
3325*0Sstevel@tonic-gate 	}
3326*0Sstevel@tonic-gate 
3327*0Sstevel@tonic-gate 	if (phdr_mp == NULL) {
3328*0Sstevel@tonic-gate 		AH_BUMP_STAT(out_discards);
3329*0Sstevel@tonic-gate 		ip_drop_packet(ipsec_out, B_FALSE, NULL, NULL,
3330*0Sstevel@tonic-gate 		    &ipdrops_ah_bad_v4_opts, &ah_dropper);
3331*0Sstevel@tonic-gate 		return (IPSEC_STATUS_FAILED);
3332*0Sstevel@tonic-gate 	}
3333*0Sstevel@tonic-gate 
3334*0Sstevel@tonic-gate 	ipsec_out->b_cont = phdr_mp;
3335*0Sstevel@tonic-gate 	phdr_mp->b_cont = mp;
3336*0Sstevel@tonic-gate 	mp->b_rptr += length_to_skip;
3337*0Sstevel@tonic-gate 
3338*0Sstevel@tonic-gate 	/*
3339*0Sstevel@tonic-gate 	 * At this point ipsec_out points to the IPSEC_OUT, new_mp
3340*0Sstevel@tonic-gate 	 * points to an mblk containing the pseudo header (IP header,
3341*0Sstevel@tonic-gate 	 * AH header, and ICV with mutable fields zero'ed out).
3342*0Sstevel@tonic-gate 	 * mp points to the mblk containing the ULP data. The original
3343*0Sstevel@tonic-gate 	 * IP header is kept before the ULP data in mp.
3344*0Sstevel@tonic-gate 	 */
3345*0Sstevel@tonic-gate 
3346*0Sstevel@tonic-gate 	/* submit MAC request to KCF */
3347*0Sstevel@tonic-gate 	return (ah_submit_req_outbound(ipsec_out, length_to_skip, assoc));
3348*0Sstevel@tonic-gate }
3349*0Sstevel@tonic-gate 
3350*0Sstevel@tonic-gate static ipsec_status_t
3351*0Sstevel@tonic-gate ah_inbound(mblk_t *ipsec_in_mp, void *arg)
3352*0Sstevel@tonic-gate {
3353*0Sstevel@tonic-gate 	mblk_t *data_mp = ipsec_in_mp->b_cont;
3354*0Sstevel@tonic-gate 	ipsec_in_t *ii = (ipsec_in_t *)ipsec_in_mp->b_rptr;
3355*0Sstevel@tonic-gate 	ah_t *ah = (ah_t *)arg;
3356*0Sstevel@tonic-gate 	ipsa_t *assoc = ii->ipsec_in_ah_sa;
3357*0Sstevel@tonic-gate 	int length_to_skip;
3358*0Sstevel@tonic-gate 	int ah_length;
3359*0Sstevel@tonic-gate 	mblk_t *phdr_mp;
3360*0Sstevel@tonic-gate 	uint32_t ah_offset;
3361*0Sstevel@tonic-gate 
3362*0Sstevel@tonic-gate 	ASSERT(assoc != NULL);
3363*0Sstevel@tonic-gate 	if (assoc->ipsa_usetime == 0)
3364*0Sstevel@tonic-gate 		ah_set_usetime(assoc, B_TRUE);
3365*0Sstevel@tonic-gate 
3366*0Sstevel@tonic-gate 	/*
3367*0Sstevel@tonic-gate 	 * We may wish to check replay in-range-only here as an optimization.
3368*0Sstevel@tonic-gate 	 * Include the reality check of ipsa->ipsa_replay >
3369*0Sstevel@tonic-gate 	 * ipsa->ipsa_replay_wsize for times when it's the first N packets,
3370*0Sstevel@tonic-gate 	 * where N == ipsa->ipsa_replay_wsize.
3371*0Sstevel@tonic-gate 	 *
3372*0Sstevel@tonic-gate 	 * Another check that may come here later is the "collision" check.
3373*0Sstevel@tonic-gate 	 * If legitimate packets flow quickly enough, this won't be a problem,
3374*0Sstevel@tonic-gate 	 * but collisions may cause authentication algorithm crunching to
3375*0Sstevel@tonic-gate 	 * take place when it doesn't need to.
3376*0Sstevel@tonic-gate 	 */
3377*0Sstevel@tonic-gate 	if (!sadb_replay_peek(assoc, ah->ah_replay)) {
3378*0Sstevel@tonic-gate 		AH_BUMP_STAT(replay_early_failures);
3379*0Sstevel@tonic-gate 		IP_AH_BUMP_STAT(in_discards);
3380*0Sstevel@tonic-gate 		ip_drop_packet(ipsec_in_mp, B_TRUE, NULL, NULL,
3381*0Sstevel@tonic-gate 		    &ipdrops_ah_early_replay, &ah_dropper);
3382*0Sstevel@tonic-gate 		return (IPSEC_STATUS_FAILED);
3383*0Sstevel@tonic-gate 	}
3384*0Sstevel@tonic-gate 
3385*0Sstevel@tonic-gate 	/*
3386*0Sstevel@tonic-gate 	 * The offset of the AH header can be computed from its pointer
3387*0Sstevel@tonic-gate 	 * within the data mblk, which was pulled up until the AH header
3388*0Sstevel@tonic-gate 	 * by ipsec_inbound_ah_sa() during SA selection.
3389*0Sstevel@tonic-gate 	 */
3390*0Sstevel@tonic-gate 	ah_offset = (uchar_t *)ah - data_mp->b_rptr;
3391*0Sstevel@tonic-gate 
3392*0Sstevel@tonic-gate /* EXPORT DELETE START */
3393*0Sstevel@tonic-gate 	/*
3394*0Sstevel@tonic-gate 	 * Has this packet already been processed by a hardware
3395*0Sstevel@tonic-gate 	 * IPsec accelerator?
3396*0Sstevel@tonic-gate 	 */
3397*0Sstevel@tonic-gate 	if (ii->ipsec_in_accelerated) {
3398*0Sstevel@tonic-gate 		ah3dbg(("ah_inbound_v6: pkt processed by ill=%d isv6=%d\n",
3399*0Sstevel@tonic-gate 		    ii->ipsec_in_ill_index, !ii->ipsec_in_v4));
3400*0Sstevel@tonic-gate 		return (ah_inbound_accelerated(ipsec_in_mp, ii->ipsec_in_v4,
3401*0Sstevel@tonic-gate 		    assoc, ah_offset));
3402*0Sstevel@tonic-gate 	}
3403*0Sstevel@tonic-gate 	AH_BUMP_STAT(noaccel);
3404*0Sstevel@tonic-gate /* EXPORT DELETE END */
3405*0Sstevel@tonic-gate 
3406*0Sstevel@tonic-gate 	/*
3407*0Sstevel@tonic-gate 	 * We need to pullup until the ICV before we call
3408*0Sstevel@tonic-gate 	 * ah_process_ip_options_v6.
3409*0Sstevel@tonic-gate 	 */
3410*0Sstevel@tonic-gate 	ah_length = (ah->ah_length << 2) + 8;
3411*0Sstevel@tonic-gate 
3412*0Sstevel@tonic-gate 	/*
3413*0Sstevel@tonic-gate 	 * NOTE : If we want to use any field of IP/AH header, you need
3414*0Sstevel@tonic-gate 	 * to re-assign following the pullup.
3415*0Sstevel@tonic-gate 	 */
3416*0Sstevel@tonic-gate 	if (((uchar_t *)ah + ah_length) > data_mp->b_wptr) {
3417*0Sstevel@tonic-gate 		if (!pullupmsg(data_mp, (uchar_t *)ah + ah_length -
3418*0Sstevel@tonic-gate 		    data_mp->b_rptr)) {
3419*0Sstevel@tonic-gate 			(void) ipsec_rl_strlog(info.mi_idnum, 0, 0,
3420*0Sstevel@tonic-gate 			    SL_WARN | SL_ERROR,
3421*0Sstevel@tonic-gate 			    "ah_inbound: Small AH header\n");
3422*0Sstevel@tonic-gate 			IP_AH_BUMP_STAT(in_discards);
3423*0Sstevel@tonic-gate 			ip_drop_packet(ipsec_in_mp, B_TRUE, NULL, NULL,
3424*0Sstevel@tonic-gate 			    &ipdrops_ah_nomem, &ah_dropper);
3425*0Sstevel@tonic-gate 			return (IPSEC_STATUS_FAILED);
3426*0Sstevel@tonic-gate 		}
3427*0Sstevel@tonic-gate 	}
3428*0Sstevel@tonic-gate 
3429*0Sstevel@tonic-gate 	/*
3430*0Sstevel@tonic-gate 	 * Insert pseudo header:
3431*0Sstevel@tonic-gate 	 * IPSEC_INFO -> [IP, ULP] => IPSEC_INFO -> [IP, AH, ICV] -> ULP
3432*0Sstevel@tonic-gate 	 */
3433*0Sstevel@tonic-gate 	if (ii->ipsec_in_v4) {
3434*0Sstevel@tonic-gate 		phdr_mp = ah_process_ip_options_v4(data_mp, assoc,
3435*0Sstevel@tonic-gate 		    &length_to_skip, assoc->ipsa_mac_len, B_FALSE);
3436*0Sstevel@tonic-gate 	} else {
3437*0Sstevel@tonic-gate 		phdr_mp = ah_process_ip_options_v6(data_mp, assoc,
3438*0Sstevel@tonic-gate 		    &length_to_skip, assoc->ipsa_mac_len, B_FALSE);
3439*0Sstevel@tonic-gate 	}
3440*0Sstevel@tonic-gate 
3441*0Sstevel@tonic-gate 	if (phdr_mp == NULL) {
3442*0Sstevel@tonic-gate 		IP_AH_BUMP_STAT(in_discards);
3443*0Sstevel@tonic-gate 		ip_drop_packet(ipsec_in_mp, B_TRUE, NULL, NULL,
3444*0Sstevel@tonic-gate 		    ii->ipsec_in_v4 ? &ipdrops_ah_bad_v4_opts :
3445*0Sstevel@tonic-gate 		    &ipdrops_ah_bad_v6_hdrs, &ah_dropper);
3446*0Sstevel@tonic-gate 		return (IPSEC_STATUS_FAILED);
3447*0Sstevel@tonic-gate 	}
3448*0Sstevel@tonic-gate 
3449*0Sstevel@tonic-gate 	ipsec_in_mp->b_cont = phdr_mp;
3450*0Sstevel@tonic-gate 	phdr_mp->b_cont = data_mp;
3451*0Sstevel@tonic-gate 	data_mp->b_rptr += length_to_skip;
3452*0Sstevel@tonic-gate 
3453*0Sstevel@tonic-gate 	/* submit request to KCF */
3454*0Sstevel@tonic-gate 	return (ah_submit_req_inbound(ipsec_in_mp, length_to_skip, ah_offset,
3455*0Sstevel@tonic-gate 	    assoc));
3456*0Sstevel@tonic-gate }
3457*0Sstevel@tonic-gate 
3458*0Sstevel@tonic-gate /* EXPORT DELETE START */
3459*0Sstevel@tonic-gate /*
3460*0Sstevel@tonic-gate  * ah_inbound_accelerated:
3461*0Sstevel@tonic-gate  * Called from ah_inbound() to process IPsec packets that have been
3462*0Sstevel@tonic-gate  * accelerated by hardware.
3463*0Sstevel@tonic-gate  *
3464*0Sstevel@tonic-gate  * Basically does what ah_auth_in_done() with some changes since
3465*0Sstevel@tonic-gate  * no pseudo-headers are involved, i.e. the passed message is a
3466*0Sstevel@tonic-gate  * IPSEC_INFO->DATA.
3467*0Sstevel@tonic-gate  *
3468*0Sstevel@tonic-gate  * It is assumed that only packets that have been successfully
3469*0Sstevel@tonic-gate  * processed by the adapter come here.
3470*0Sstevel@tonic-gate  *
3471*0Sstevel@tonic-gate  * 1. get algorithm structure corresponding to association
3472*0Sstevel@tonic-gate  * 2. calculate pointers to authentication header and ICV
3473*0Sstevel@tonic-gate  * 3. compare ICV in AH header with ICV in data attributes
3474*0Sstevel@tonic-gate  *    3.1 if different:
3475*0Sstevel@tonic-gate  *	  3.1.1 generate error
3476*0Sstevel@tonic-gate  *        3.1.2 discard message
3477*0Sstevel@tonic-gate  *    3.2 if ICV matches:
3478*0Sstevel@tonic-gate  *	  3.2.1 check replay
3479*0Sstevel@tonic-gate  *        3.2.2 remove AH header
3480*0Sstevel@tonic-gate  *        3.2.3 age SA byte
3481*0Sstevel@tonic-gate  *        3.2.4 send to IP
3482*0Sstevel@tonic-gate  */
3483*0Sstevel@tonic-gate ipsec_status_t
3484*0Sstevel@tonic-gate ah_inbound_accelerated(mblk_t *ipsec_in, boolean_t isv4, ipsa_t *assoc,
3485*0Sstevel@tonic-gate     uint32_t ah_offset)
3486*0Sstevel@tonic-gate {
3487*0Sstevel@tonic-gate 	mblk_t *mp;
3488*0Sstevel@tonic-gate 	ipha_t *ipha;
3489*0Sstevel@tonic-gate 	ah_t *ah;
3490*0Sstevel@tonic-gate 	ipsec_in_t *ii;
3491*0Sstevel@tonic-gate 	uint32_t icv_len;
3492*0Sstevel@tonic-gate 	uint32_t align_len;
3493*0Sstevel@tonic-gate 	uint32_t age_bytes;
3494*0Sstevel@tonic-gate 	ip6_t *ip6h;
3495*0Sstevel@tonic-gate 	uint8_t *in_icv;
3496*0Sstevel@tonic-gate 	mblk_t *hada_mp;
3497*0Sstevel@tonic-gate 	uint32_t next_hdr;
3498*0Sstevel@tonic-gate 	da_ipsec_t *hada;
3499*0Sstevel@tonic-gate 	kstat_named_t *counter;
3500*0Sstevel@tonic-gate 
3501*0Sstevel@tonic-gate 	AH_BUMP_STAT(in_accelerated);
3502*0Sstevel@tonic-gate 
3503*0Sstevel@tonic-gate 	ii = (ipsec_in_t *)ipsec_in->b_rptr;
3504*0Sstevel@tonic-gate 	mp = ipsec_in->b_cont;
3505*0Sstevel@tonic-gate 	hada_mp = ii->ipsec_in_da;
3506*0Sstevel@tonic-gate 	ASSERT(hada_mp != NULL);
3507*0Sstevel@tonic-gate 	hada = (da_ipsec_t *)hada_mp->b_rptr;
3508*0Sstevel@tonic-gate 
3509*0Sstevel@tonic-gate 	/*
3510*0Sstevel@tonic-gate 	 * We only support one level of decapsulation in hardware, so
3511*0Sstevel@tonic-gate 	 * nuke the pointer.
3512*0Sstevel@tonic-gate 	 */
3513*0Sstevel@tonic-gate 	ii->ipsec_in_da = NULL;
3514*0Sstevel@tonic-gate 	ii->ipsec_in_accelerated = B_FALSE;
3515*0Sstevel@tonic-gate 
3516*0Sstevel@tonic-gate 	/*
3517*0Sstevel@tonic-gate 	 * Extract ICV length from attributes M_CTL and sanity check
3518*0Sstevel@tonic-gate 	 * its value. We allow the mblk to be smaller than da_ipsec_t
3519*0Sstevel@tonic-gate 	 * for a small ICV, as long as the entire ICV fits within the mblk.
3520*0Sstevel@tonic-gate 	 * Also ensures that the ICV length computed by Provider
3521*0Sstevel@tonic-gate 	 * corresponds to the ICV length of the algorithm specified by the SA.
3522*0Sstevel@tonic-gate 	 */
3523*0Sstevel@tonic-gate 	icv_len = hada->da_icv_len;
3524*0Sstevel@tonic-gate 	if ((icv_len != assoc->ipsa_mac_len) ||
3525*0Sstevel@tonic-gate 	    (icv_len > DA_ICV_MAX_LEN) || (MBLKL(hada_mp) <
3526*0Sstevel@tonic-gate 		(sizeof (da_ipsec_t) - DA_ICV_MAX_LEN + icv_len))) {
3527*0Sstevel@tonic-gate 		ah0dbg(("ah_inbound_accelerated: "
3528*0Sstevel@tonic-gate 		    "ICV len (%u) incorrect or mblk too small (%u)\n",
3529*0Sstevel@tonic-gate 		    icv_len, (uint32_t)(MBLKL(hada_mp))));
3530*0Sstevel@tonic-gate 		counter = &ipdrops_ah_bad_length;
3531*0Sstevel@tonic-gate 		goto ah_in_discard;
3532*0Sstevel@tonic-gate 	}
3533*0Sstevel@tonic-gate 	ASSERT(icv_len != 0);
3534*0Sstevel@tonic-gate 
3535*0Sstevel@tonic-gate 	/* compute the padded AH ICV len */
3536*0Sstevel@tonic-gate 	if (isv4) {
3537*0Sstevel@tonic-gate 		ipha = (ipha_t *)mp->b_rptr;
3538*0Sstevel@tonic-gate 		align_len = (icv_len + IPV4_PADDING_ALIGN - 1) &
3539*0Sstevel@tonic-gate 		    -IPV4_PADDING_ALIGN;
3540*0Sstevel@tonic-gate 	} else {
3541*0Sstevel@tonic-gate 		ip6h = (ip6_t *)mp->b_rptr;
3542*0Sstevel@tonic-gate 		align_len = (icv_len + IPV6_PADDING_ALIGN - 1) &
3543*0Sstevel@tonic-gate 		    -IPV6_PADDING_ALIGN;
3544*0Sstevel@tonic-gate 	}
3545*0Sstevel@tonic-gate 
3546*0Sstevel@tonic-gate 	ah = (ah_t *)(mp->b_rptr + ah_offset);
3547*0Sstevel@tonic-gate 	in_icv = (uint8_t *)ah + sizeof (ah_t);
3548*0Sstevel@tonic-gate 
3549*0Sstevel@tonic-gate 	/* compare ICV in AH header vs ICV computed by adapter */
3550*0Sstevel@tonic-gate 	if (bcmp(hada->da_icv, in_icv, icv_len)) {
3551*0Sstevel@tonic-gate 		int af;
3552*0Sstevel@tonic-gate 		void *addr;
3553*0Sstevel@tonic-gate 
3554*0Sstevel@tonic-gate 		if (isv4) {
3555*0Sstevel@tonic-gate 			addr = &ipha->ipha_dst;
3556*0Sstevel@tonic-gate 			af = AF_INET;
3557*0Sstevel@tonic-gate 		} else {
3558*0Sstevel@tonic-gate 			addr = &ip6h->ip6_dst;
3559*0Sstevel@tonic-gate 			af = AF_INET6;
3560*0Sstevel@tonic-gate 		}
3561*0Sstevel@tonic-gate 
3562*0Sstevel@tonic-gate 		/*
3563*0Sstevel@tonic-gate 		 * Log the event. Don't print to the console, block
3564*0Sstevel@tonic-gate 		 * potential denial-of-service attack.
3565*0Sstevel@tonic-gate 		 */
3566*0Sstevel@tonic-gate 		AH_BUMP_STAT(bad_auth);
3567*0Sstevel@tonic-gate 		ipsec_assocfailure(info.mi_idnum, 0, 0, SL_ERROR | SL_WARN,
3568*0Sstevel@tonic-gate 		    "AH Authentication failed spi %x, dst_addr %s",
3569*0Sstevel@tonic-gate 		    assoc->ipsa_spi, addr, af);
3570*0Sstevel@tonic-gate 		counter = &ipdrops_ah_bad_auth;
3571*0Sstevel@tonic-gate 		goto ah_in_discard;
3572*0Sstevel@tonic-gate 	}
3573*0Sstevel@tonic-gate 
3574*0Sstevel@tonic-gate 	ah3dbg(("AH succeeded, checking replay\n"));
3575*0Sstevel@tonic-gate 	AH_BUMP_STAT(good_auth);
3576*0Sstevel@tonic-gate 
3577*0Sstevel@tonic-gate 	if (!sadb_replay_check(assoc, ah->ah_replay)) {
3578*0Sstevel@tonic-gate 		int af;
3579*0Sstevel@tonic-gate 		void *addr;
3580*0Sstevel@tonic-gate 
3581*0Sstevel@tonic-gate 		if (isv4) {
3582*0Sstevel@tonic-gate 			addr = &ipha->ipha_dst;
3583*0Sstevel@tonic-gate 			af = AF_INET;
3584*0Sstevel@tonic-gate 		} else {
3585*0Sstevel@tonic-gate 			addr = &ip6h->ip6_dst;
3586*0Sstevel@tonic-gate 			af = AF_INET6;
3587*0Sstevel@tonic-gate 		}
3588*0Sstevel@tonic-gate 
3589*0Sstevel@tonic-gate 		/*
3590*0Sstevel@tonic-gate 		 * Log the event. As of now we print out an event.
3591*0Sstevel@tonic-gate 		 * Do not print the replay failure number, or else
3592*0Sstevel@tonic-gate 		 * syslog cannot collate the error messages.  Printing
3593*0Sstevel@tonic-gate 		 * the replay number that failed (or printing to the
3594*0Sstevel@tonic-gate 		 * console) opens a denial-of-service attack.
3595*0Sstevel@tonic-gate 		 */
3596*0Sstevel@tonic-gate 		AH_BUMP_STAT(replay_failures);
3597*0Sstevel@tonic-gate 		ipsec_assocfailure(info.mi_idnum, 0, 0,
3598*0Sstevel@tonic-gate 		    SL_ERROR | SL_WARN,
3599*0Sstevel@tonic-gate 		    "Replay failed for AH spi %x, dst_addr %s",
3600*0Sstevel@tonic-gate 		    assoc->ipsa_spi, addr, af);
3601*0Sstevel@tonic-gate 		counter = &ipdrops_ah_replay;
3602*0Sstevel@tonic-gate 		goto ah_in_discard;
3603*0Sstevel@tonic-gate 	}
3604*0Sstevel@tonic-gate 
3605*0Sstevel@tonic-gate 	/*
3606*0Sstevel@tonic-gate 	 * Remove AH header. We do this by copying everything before
3607*0Sstevel@tonic-gate 	 * the AH header onto the AH header+ICV.
3608*0Sstevel@tonic-gate 	 */
3609*0Sstevel@tonic-gate 	/* overwrite AH with what was preceeding it (IP header) */
3610*0Sstevel@tonic-gate 	next_hdr = ah->ah_nexthdr;
3611*0Sstevel@tonic-gate 	ovbcopy(mp->b_rptr, mp->b_rptr + sizeof (ah_t) + align_len,
3612*0Sstevel@tonic-gate 	    ah_offset);
3613*0Sstevel@tonic-gate 	mp->b_rptr += sizeof (ah_t) + align_len;
3614*0Sstevel@tonic-gate 	if (isv4) {
3615*0Sstevel@tonic-gate 		/* adjust IP header next protocol */
3616*0Sstevel@tonic-gate 		ipha = (ipha_t *)mp->b_rptr;
3617*0Sstevel@tonic-gate 		ipha->ipha_protocol = next_hdr;
3618*0Sstevel@tonic-gate 
3619*0Sstevel@tonic-gate 		age_bytes = ipha->ipha_length;
3620*0Sstevel@tonic-gate 
3621*0Sstevel@tonic-gate 		/* adjust length in IP header */
3622*0Sstevel@tonic-gate 		ipha->ipha_length -= (sizeof (ah_t) + align_len);
3623*0Sstevel@tonic-gate 
3624*0Sstevel@tonic-gate 		/* recalculate checksum */
3625*0Sstevel@tonic-gate 		ipha->ipha_hdr_checksum = 0;
3626*0Sstevel@tonic-gate 		ipha->ipha_hdr_checksum = (uint16_t)ip_csum_hdr(ipha);
3627*0Sstevel@tonic-gate 	} else {
3628*0Sstevel@tonic-gate 		/* adjust IP header next protocol */
3629*0Sstevel@tonic-gate 		ip6h = (ip6_t *)mp->b_rptr;
3630*0Sstevel@tonic-gate 		ip6h->ip6_nxt = next_hdr;
3631*0Sstevel@tonic-gate 
3632*0Sstevel@tonic-gate 		age_bytes = sizeof (ip6_t) + ntohs(ip6h->ip6_plen) +
3633*0Sstevel@tonic-gate 		    sizeof (ah_t);
3634*0Sstevel@tonic-gate 
3635*0Sstevel@tonic-gate 		/* adjust length in IP header */
3636*0Sstevel@tonic-gate 		ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) -
3637*0Sstevel@tonic-gate 		    (sizeof (ah_t) + align_len));
3638*0Sstevel@tonic-gate 	}
3639*0Sstevel@tonic-gate 
3640*0Sstevel@tonic-gate 	/* age SA */
3641*0Sstevel@tonic-gate 	if (!ah_age_bytes(assoc, age_bytes, B_TRUE)) {
3642*0Sstevel@tonic-gate 		/* The ipsa has hit hard expiration, LOG and AUDIT. */
3643*0Sstevel@tonic-gate 		ipsec_assocfailure(info.mi_idnum, 0, 0,
3644*0Sstevel@tonic-gate 		    SL_ERROR | SL_WARN,
3645*0Sstevel@tonic-gate 		    "AH Association 0x%x, dst %s had bytes expire.\n",
3646*0Sstevel@tonic-gate 		    assoc->ipsa_spi, assoc->ipsa_dstaddr,
3647*0Sstevel@tonic-gate 		    AF_INET);
3648*0Sstevel@tonic-gate 		AH_BUMP_STAT(bytes_expired);
3649*0Sstevel@tonic-gate 		counter = &ipdrops_ah_bytes_expire;
3650*0Sstevel@tonic-gate 		goto ah_in_discard;
3651*0Sstevel@tonic-gate 	}
3652*0Sstevel@tonic-gate 
3653*0Sstevel@tonic-gate 	freeb(hada_mp);
3654*0Sstevel@tonic-gate 	return (IPSEC_STATUS_SUCCESS);
3655*0Sstevel@tonic-gate 
3656*0Sstevel@tonic-gate ah_in_discard:
3657*0Sstevel@tonic-gate 	IP_AH_BUMP_STAT(in_discards);
3658*0Sstevel@tonic-gate 	freeb(hada_mp);
3659*0Sstevel@tonic-gate 	ip_drop_packet(ipsec_in, B_TRUE, NULL, NULL, counter, &ah_dropper);
3660*0Sstevel@tonic-gate 	return (IPSEC_STATUS_FAILED);
3661*0Sstevel@tonic-gate }
3662*0Sstevel@tonic-gate 
3663*0Sstevel@tonic-gate /*
3664*0Sstevel@tonic-gate  * ah_outbound_accelerated_v4:
3665*0Sstevel@tonic-gate  * Called from ah_outbound_v4() and once it is determined that the
3666*0Sstevel@tonic-gate  * packet is elligible for hardware acceleration.
3667*0Sstevel@tonic-gate  *
3668*0Sstevel@tonic-gate  * We proceed as follows:
3669*0Sstevel@tonic-gate  * 1. allocate and initialize attributes mblk
3670*0Sstevel@tonic-gate  * 2. mark IPSEC_OUT to indicate that pkt is accelerated
3671*0Sstevel@tonic-gate  * 3. insert AH header
3672*0Sstevel@tonic-gate  */
3673*0Sstevel@tonic-gate static ipsec_status_t
3674*0Sstevel@tonic-gate ah_outbound_accelerated_v4(mblk_t *ipsec_mp, ipsa_t *assoc)
3675*0Sstevel@tonic-gate {
3676*0Sstevel@tonic-gate 	mblk_t *mp, *new_mp;
3677*0Sstevel@tonic-gate 	ipsec_out_t *oi;
3678*0Sstevel@tonic-gate 	uint_t ah_data_sz;	/* ICV length, algorithm dependent */
3679*0Sstevel@tonic-gate 	uint_t ah_align_sz;	/* ICV length + padding */
3680*0Sstevel@tonic-gate 	uint32_t v_hlen_tos_len; /* from original IP header */
3681*0Sstevel@tonic-gate 	ipha_t	*oipha;		/* original IP header */
3682*0Sstevel@tonic-gate 	ipha_t	*nipha;		/* new IP header */
3683*0Sstevel@tonic-gate 	uint_t option_length = 0;
3684*0Sstevel@tonic-gate 	uint_t new_hdr_len;	/* new header length */
3685*0Sstevel@tonic-gate 	uint_t iphdr_length;
3686*0Sstevel@tonic-gate 	ah_t *ah_hdr;		/* ptr to AH header */
3687*0Sstevel@tonic-gate 
3688*0Sstevel@tonic-gate 	AH_BUMP_STAT(out_accelerated);
3689*0Sstevel@tonic-gate 
3690*0Sstevel@tonic-gate 	oi = (ipsec_out_t *)ipsec_mp->b_rptr;
3691*0Sstevel@tonic-gate 	mp = ipsec_mp->b_cont;
3692*0Sstevel@tonic-gate 
3693*0Sstevel@tonic-gate 	oipha = (ipha_t *)mp->b_rptr;
3694*0Sstevel@tonic-gate 	v_hlen_tos_len = ((uint32_t *)oipha)[0];
3695*0Sstevel@tonic-gate 
3696*0Sstevel@tonic-gate 	/* mark packet as being accelerated in IPSEC_OUT */
3697*0Sstevel@tonic-gate 	ASSERT(oi->ipsec_out_accelerated == B_FALSE);
3698*0Sstevel@tonic-gate 	oi->ipsec_out_accelerated = B_TRUE;
3699*0Sstevel@tonic-gate 
3700*0Sstevel@tonic-gate 	/* calculate authentication data length, i.e. ICV + padding */
3701*0Sstevel@tonic-gate 	ah_data_sz = assoc->ipsa_mac_len;
3702*0Sstevel@tonic-gate 	ah_align_sz = (ah_data_sz + IPV4_PADDING_ALIGN - 1) &
3703*0Sstevel@tonic-gate 	    -IPV4_PADDING_ALIGN;
3704*0Sstevel@tonic-gate 
3705*0Sstevel@tonic-gate 	/*
3706*0Sstevel@tonic-gate 	 * Insert pseudo header:
3707*0Sstevel@tonic-gate 	 * IPSEC_INFO -> [IP, ULP] => IPSEC_INFO -> [IP, AH, ICV] -> ULP
3708*0Sstevel@tonic-gate 	 */
3709*0Sstevel@tonic-gate 
3710*0Sstevel@tonic-gate 	/* IP + AH + authentication + padding data length */
3711*0Sstevel@tonic-gate 	new_hdr_len = IP_SIMPLE_HDR_LENGTH + sizeof (ah_t) + ah_align_sz;
3712*0Sstevel@tonic-gate 	if (V_HLEN != IP_SIMPLE_HDR_VERSION) {
3713*0Sstevel@tonic-gate 		option_length = oipha->ipha_version_and_hdr_length -
3714*0Sstevel@tonic-gate 		    (uint8_t)((IP_VERSION << 4) +
3715*0Sstevel@tonic-gate 		    IP_SIMPLE_HDR_LENGTH_IN_WORDS);
3716*0Sstevel@tonic-gate 		option_length <<= 2;
3717*0Sstevel@tonic-gate 		new_hdr_len += option_length;
3718*0Sstevel@tonic-gate 	}
3719*0Sstevel@tonic-gate 
3720*0Sstevel@tonic-gate 	/* allocate pseudo-header mblk */
3721*0Sstevel@tonic-gate 	if ((new_mp = allocb(new_hdr_len, BPRI_HI)) == NULL) {
3722*0Sstevel@tonic-gate 		/* IPsec kstats: bump bean counter here */
3723*0Sstevel@tonic-gate 		ip_drop_packet(ipsec_mp, B_FALSE, NULL, NULL,
3724*0Sstevel@tonic-gate 		    &ipdrops_ah_nomem, &ah_dropper);
3725*0Sstevel@tonic-gate 		return (IPSEC_STATUS_FAILED);
3726*0Sstevel@tonic-gate 	}
3727*0Sstevel@tonic-gate 
3728*0Sstevel@tonic-gate 	new_mp->b_cont = mp;
3729*0Sstevel@tonic-gate 	ipsec_mp->b_cont = new_mp;
3730*0Sstevel@tonic-gate 	new_mp->b_wptr += new_hdr_len;
3731*0Sstevel@tonic-gate 
3732*0Sstevel@tonic-gate 	/* copy original IP header to new header */
3733*0Sstevel@tonic-gate 	bcopy(mp->b_rptr, new_mp->b_rptr, IP_SIMPLE_HDR_LENGTH +
3734*0Sstevel@tonic-gate 	    option_length);
3735*0Sstevel@tonic-gate 
3736*0Sstevel@tonic-gate 	/* update IP header */
3737*0Sstevel@tonic-gate 	nipha = (ipha_t *)new_mp->b_rptr;
3738*0Sstevel@tonic-gate 	nipha->ipha_protocol = IPPROTO_AH;
3739*0Sstevel@tonic-gate 	iphdr_length = ntohs(nipha->ipha_length);
3740*0Sstevel@tonic-gate 	iphdr_length += sizeof (ah_t) + ah_align_sz;
3741*0Sstevel@tonic-gate 	nipha->ipha_length = htons(iphdr_length);
3742*0Sstevel@tonic-gate 	nipha->ipha_hdr_checksum = 0;
3743*0Sstevel@tonic-gate 	nipha->ipha_hdr_checksum = (uint16_t)ip_csum_hdr(nipha);
3744*0Sstevel@tonic-gate 
3745*0Sstevel@tonic-gate 	/* skip original IP header in mp */
3746*0Sstevel@tonic-gate 	mp->b_rptr += IP_SIMPLE_HDR_LENGTH + option_length;
3747*0Sstevel@tonic-gate 
3748*0Sstevel@tonic-gate 	/* initialize AH header */
3749*0Sstevel@tonic-gate 	ah_hdr = (ah_t *)(new_mp->b_rptr + IP_SIMPLE_HDR_LENGTH +
3750*0Sstevel@tonic-gate 	    option_length);
3751*0Sstevel@tonic-gate 	ah_hdr->ah_nexthdr = oipha->ipha_protocol;
3752*0Sstevel@tonic-gate 	if (!ah_finish_up(ah_hdr, NULL, assoc, ah_data_sz, ah_align_sz)) {
3753*0Sstevel@tonic-gate 		/* Only way this fails is if outbound replay counter wraps. */
3754*0Sstevel@tonic-gate 		ip_drop_packet(ipsec_mp, B_FALSE, NULL, NULL,
3755*0Sstevel@tonic-gate 		    &ipdrops_ah_replay, &ah_dropper);
3756*0Sstevel@tonic-gate 		return (IPSEC_STATUS_FAILED);
3757*0Sstevel@tonic-gate 	}
3758*0Sstevel@tonic-gate 
3759*0Sstevel@tonic-gate 	return (IPSEC_STATUS_SUCCESS);
3760*0Sstevel@tonic-gate }
3761*0Sstevel@tonic-gate 
3762*0Sstevel@tonic-gate /*
3763*0Sstevel@tonic-gate  * ah_outbound_accelerated_v6:
3764*0Sstevel@tonic-gate  *
3765*0Sstevel@tonic-gate  * Called from ah_outbound_v6() once it is determined that the packet
3766*0Sstevel@tonic-gate  * is eligible for hardware acceleration.
3767*0Sstevel@tonic-gate  *
3768*0Sstevel@tonic-gate  * We proceed as follows:
3769*0Sstevel@tonic-gate  * 1. allocate and initialize attributes mblk
3770*0Sstevel@tonic-gate  * 2. mark IPSEC_OUT to indicate that pkt is accelerated
3771*0Sstevel@tonic-gate  * 3. insert AH header
3772*0Sstevel@tonic-gate  */
3773*0Sstevel@tonic-gate static ipsec_status_t
3774*0Sstevel@tonic-gate ah_outbound_accelerated_v6(mblk_t *ipsec_mp, ipsa_t *assoc)
3775*0Sstevel@tonic-gate {
3776*0Sstevel@tonic-gate 	mblk_t *mp, *phdr_mp;
3777*0Sstevel@tonic-gate 	ipsec_out_t *oi;
3778*0Sstevel@tonic-gate 	uint_t ah_data_sz;	/* ICV length, algorithm dependent */
3779*0Sstevel@tonic-gate 	uint_t ah_align_sz;	/* ICV length + padding */
3780*0Sstevel@tonic-gate 	ip6_t	*oip6h;		/* original IP header */
3781*0Sstevel@tonic-gate 	ip6_t	*ip6h;		/* new IP header */
3782*0Sstevel@tonic-gate 	uint_t option_length = 0;
3783*0Sstevel@tonic-gate 	uint_t hdr_size;
3784*0Sstevel@tonic-gate 	uint_t ah_offset;
3785*0Sstevel@tonic-gate 	ah_t *ah_hdr;		/* ptr to AH header */
3786*0Sstevel@tonic-gate 
3787*0Sstevel@tonic-gate 	AH_BUMP_STAT(out_accelerated);
3788*0Sstevel@tonic-gate 
3789*0Sstevel@tonic-gate 	oi = (ipsec_out_t *)ipsec_mp->b_rptr;
3790*0Sstevel@tonic-gate 	mp = ipsec_mp->b_cont;
3791*0Sstevel@tonic-gate 
3792*0Sstevel@tonic-gate 	oip6h = (ip6_t *)mp->b_rptr;
3793*0Sstevel@tonic-gate 
3794*0Sstevel@tonic-gate 	/* mark packet as being accelerated in IPSEC_OUT */
3795*0Sstevel@tonic-gate 	ASSERT(oi->ipsec_out_accelerated == B_FALSE);
3796*0Sstevel@tonic-gate 	oi->ipsec_out_accelerated = B_TRUE;
3797*0Sstevel@tonic-gate 
3798*0Sstevel@tonic-gate 	/* calculate authentication data length, i.e. ICV + padding */
3799*0Sstevel@tonic-gate 	ah_data_sz = assoc->ipsa_mac_len;
3800*0Sstevel@tonic-gate 	ah_align_sz = (ah_data_sz + IPV4_PADDING_ALIGN - 1) &
3801*0Sstevel@tonic-gate 	    -IPV4_PADDING_ALIGN;
3802*0Sstevel@tonic-gate 
3803*0Sstevel@tonic-gate 	ASSERT(ah_align_sz >= ah_data_sz);
3804*0Sstevel@tonic-gate 
3805*0Sstevel@tonic-gate 	hdr_size = ipsec_ah_get_hdr_size_v6(mp, B_FALSE);
3806*0Sstevel@tonic-gate 	option_length = hdr_size - IPV6_HDR_LEN;
3807*0Sstevel@tonic-gate 
3808*0Sstevel@tonic-gate 	/* This was not included in ipsec_ah_get_hdr_size_v6() */
3809*0Sstevel@tonic-gate 	hdr_size += (sizeof (ah_t) + ah_align_sz);
3810*0Sstevel@tonic-gate 
3811*0Sstevel@tonic-gate 	if ((phdr_mp = allocb(hdr_size, BPRI_HI)) == NULL) {
3812*0Sstevel@tonic-gate 		ip_drop_packet(ipsec_mp, B_FALSE, NULL, NULL, &ipdrops_ah_nomem,
3813*0Sstevel@tonic-gate 		    &ah_dropper);
3814*0Sstevel@tonic-gate 		return (IPSEC_STATUS_FAILED);
3815*0Sstevel@tonic-gate 	}
3816*0Sstevel@tonic-gate 	phdr_mp->b_wptr += hdr_size;
3817*0Sstevel@tonic-gate 
3818*0Sstevel@tonic-gate 	/*
3819*0Sstevel@tonic-gate 	 * Form the basic IP header first.  We always assign every bit
3820*0Sstevel@tonic-gate 	 * of the v6 basic header, so a separate bzero is unneeded.
3821*0Sstevel@tonic-gate 	 */
3822*0Sstevel@tonic-gate 	ip6h = (ip6_t *)phdr_mp->b_rptr;
3823*0Sstevel@tonic-gate 	ip6h->ip6_vcf = oip6h->ip6_vcf;
3824*0Sstevel@tonic-gate 	ip6h->ip6_hlim = oip6h->ip6_hlim;
3825*0Sstevel@tonic-gate 	ip6h->ip6_src = oip6h->ip6_src;
3826*0Sstevel@tonic-gate 	ip6h->ip6_dst = oip6h->ip6_dst;
3827*0Sstevel@tonic-gate 	/*
3828*0Sstevel@tonic-gate 	 * Include the size of AH and authentication data.
3829*0Sstevel@tonic-gate 	 * This is how our recipient would compute the
3830*0Sstevel@tonic-gate 	 * authentication data. Look at what we do in the
3831*0Sstevel@tonic-gate 	 * inbound case below.
3832*0Sstevel@tonic-gate 	 */
3833*0Sstevel@tonic-gate 	ip6h->ip6_plen = htons(ntohs(oip6h->ip6_plen) + sizeof (ah_t) +
3834*0Sstevel@tonic-gate 	    ah_align_sz);
3835*0Sstevel@tonic-gate 
3836*0Sstevel@tonic-gate 	/*
3837*0Sstevel@tonic-gate 	 * Insert pseudo header:
3838*0Sstevel@tonic-gate 	 * IPSEC_INFO -> [IP6, LLH, ULP] =>
3839*0Sstevel@tonic-gate 	 *	IPSEC_INFO -> [IP, LLH, AH, ICV] -> ULP
3840*0Sstevel@tonic-gate 	 */
3841*0Sstevel@tonic-gate 
3842*0Sstevel@tonic-gate 	if (option_length == 0) {
3843*0Sstevel@tonic-gate 		/* Form the AH header */
3844*0Sstevel@tonic-gate 		ip6h->ip6_nxt = IPPROTO_AH;
3845*0Sstevel@tonic-gate 		((ah_t *)(ip6h + 1))->ah_nexthdr = oip6h->ip6_nxt;
3846*0Sstevel@tonic-gate 		ah_offset = IPV6_HDR_LEN;
3847*0Sstevel@tonic-gate 	} else {
3848*0Sstevel@tonic-gate 		ip6h->ip6_nxt = oip6h->ip6_nxt;
3849*0Sstevel@tonic-gate 		/* option_length does not include the AH header's size */
3850*0Sstevel@tonic-gate 		ah_offset = ah_fix_phdr_v6(ip6h, oip6h, B_TRUE, B_FALSE);
3851*0Sstevel@tonic-gate 		if (ah_offset == 0) {
3852*0Sstevel@tonic-gate 			freemsg(phdr_mp);
3853*0Sstevel@tonic-gate 			ip_drop_packet(ipsec_mp, B_FALSE, NULL, NULL,
3854*0Sstevel@tonic-gate 			    &ipdrops_ah_bad_v6_hdrs, &ah_dropper);
3855*0Sstevel@tonic-gate 			return (IPSEC_STATUS_FAILED);
3856*0Sstevel@tonic-gate 		}
3857*0Sstevel@tonic-gate 	}
3858*0Sstevel@tonic-gate 
3859*0Sstevel@tonic-gate 	phdr_mp->b_cont = mp;
3860*0Sstevel@tonic-gate 	ipsec_mp->b_cont = phdr_mp;
3861*0Sstevel@tonic-gate 
3862*0Sstevel@tonic-gate 	/* skip original IP header in mp */
3863*0Sstevel@tonic-gate 	mp->b_rptr += IPV6_HDR_LEN + option_length;
3864*0Sstevel@tonic-gate 
3865*0Sstevel@tonic-gate 	/* initialize AH header */
3866*0Sstevel@tonic-gate 	ah_hdr = (ah_t *)(phdr_mp->b_rptr + IPV6_HDR_LEN + option_length);
3867*0Sstevel@tonic-gate 	ah_hdr->ah_nexthdr = oip6h->ip6_nxt;
3868*0Sstevel@tonic-gate 
3869*0Sstevel@tonic-gate 	if (!ah_finish_up(((ah_t *)((uint8_t *)ip6h + ah_offset)), NULL,
3870*0Sstevel@tonic-gate 	    assoc, ah_data_sz, ah_align_sz)) {
3871*0Sstevel@tonic-gate 		/* Only way this fails is if outbound replay counter wraps. */
3872*0Sstevel@tonic-gate 		ip_drop_packet(ipsec_mp, B_FALSE, NULL, NULL,
3873*0Sstevel@tonic-gate 		    &ipdrops_ah_replay, &ah_dropper);
3874*0Sstevel@tonic-gate 		return (IPSEC_STATUS_FAILED);
3875*0Sstevel@tonic-gate 	}
3876*0Sstevel@tonic-gate 
3877*0Sstevel@tonic-gate 	return (IPSEC_STATUS_SUCCESS);
3878*0Sstevel@tonic-gate }
3879*0Sstevel@tonic-gate /* EXPORT DELETE END */
3880*0Sstevel@tonic-gate 
3881*0Sstevel@tonic-gate /*
3882*0Sstevel@tonic-gate  * Invoked after processing of an inbound packet by the
3883*0Sstevel@tonic-gate  * kernel crypto framework. Called by ah_submit_req() for a sync request,
3884*0Sstevel@tonic-gate  * or by the kcf callback for an async request.
3885*0Sstevel@tonic-gate  * Returns IPSEC_STATUS_SUCCESS on success, IPSEC_STATUS_FAILED on failure.
3886*0Sstevel@tonic-gate  * On failure, the mblk chain ipsec_in is freed by this function.
3887*0Sstevel@tonic-gate  */
3888*0Sstevel@tonic-gate static ipsec_status_t
3889*0Sstevel@tonic-gate ah_auth_in_done(mblk_t *ipsec_in)
3890*0Sstevel@tonic-gate {
3891*0Sstevel@tonic-gate 	mblk_t *phdr_mp;
3892*0Sstevel@tonic-gate 	ipha_t *ipha;
3893*0Sstevel@tonic-gate 	uint_t ah_offset = 0;
3894*0Sstevel@tonic-gate 	mblk_t *mp;
3895*0Sstevel@tonic-gate 	int align_len;
3896*0Sstevel@tonic-gate 	ah_t *ah;
3897*0Sstevel@tonic-gate 	ipha_t *nipha;
3898*0Sstevel@tonic-gate 	uint32_t length;
3899*0Sstevel@tonic-gate 	ipsec_in_t *ii;
3900*0Sstevel@tonic-gate 	boolean_t isv4;
3901*0Sstevel@tonic-gate 	ip6_t *ip6h;
3902*0Sstevel@tonic-gate 	ip6_t *nip6h;
3903*0Sstevel@tonic-gate 	uint_t icv_len;
3904*0Sstevel@tonic-gate 	ipsa_t *assoc;
3905*0Sstevel@tonic-gate 	kstat_named_t *counter;
3906*0Sstevel@tonic-gate 
3907*0Sstevel@tonic-gate 	ii = (ipsec_in_t *)ipsec_in->b_rptr;
3908*0Sstevel@tonic-gate 	isv4 = ii->ipsec_in_v4;
3909*0Sstevel@tonic-gate 	assoc = ii->ipsec_in_ah_sa;
3910*0Sstevel@tonic-gate 	icv_len = (uint_t)ii->ipsec_in_crypto_mac.cd_raw.iov_len;
3911*0Sstevel@tonic-gate 
3912*0Sstevel@tonic-gate 	phdr_mp = ipsec_in->b_cont;
3913*0Sstevel@tonic-gate 	if (phdr_mp == NULL) {
3914*0Sstevel@tonic-gate 		ip_drop_packet(ipsec_in, B_TRUE, NULL, NULL, &ipdrops_ah_nomem,
3915*0Sstevel@tonic-gate 		    &ah_dropper);
3916*0Sstevel@tonic-gate 		return (IPSEC_STATUS_FAILED);
3917*0Sstevel@tonic-gate 	}
3918*0Sstevel@tonic-gate 
3919*0Sstevel@tonic-gate 	mp = phdr_mp->b_cont;
3920*0Sstevel@tonic-gate 	if (mp == NULL) {
3921*0Sstevel@tonic-gate 		ip_drop_packet(ipsec_in, B_TRUE, NULL, NULL, &ipdrops_ah_nomem,
3922*0Sstevel@tonic-gate 		    &ah_dropper);
3923*0Sstevel@tonic-gate 		return (IPSEC_STATUS_FAILED);
3924*0Sstevel@tonic-gate 	}
3925*0Sstevel@tonic-gate 	mp->b_rptr -= ii->ipsec_in_skip_len;
3926*0Sstevel@tonic-gate 
3927*0Sstevel@tonic-gate 	if (isv4) {
3928*0Sstevel@tonic-gate 		ipha = (ipha_t *)mp->b_rptr;
3929*0Sstevel@tonic-gate 		ah_offset = ipha->ipha_version_and_hdr_length -
3930*0Sstevel@tonic-gate 		    (uint8_t)((IP_VERSION << 4));
3931*0Sstevel@tonic-gate 		ah_offset <<= 2;
3932*0Sstevel@tonic-gate 		align_len = P2ALIGN(icv_len + IPV4_PADDING_ALIGN - 1,
3933*0Sstevel@tonic-gate 		    IPV4_PADDING_ALIGN);
3934*0Sstevel@tonic-gate 	} else {
3935*0Sstevel@tonic-gate 		ip6h = (ip6_t *)mp->b_rptr;
3936*0Sstevel@tonic-gate 		ah_offset = ipsec_ah_get_hdr_size_v6(mp, B_TRUE);
3937*0Sstevel@tonic-gate 		ASSERT((mp->b_wptr - mp->b_rptr) >= ah_offset);
3938*0Sstevel@tonic-gate 		align_len = P2ALIGN(icv_len + IPV6_PADDING_ALIGN - 1,
3939*0Sstevel@tonic-gate 		    IPV6_PADDING_ALIGN);
3940*0Sstevel@tonic-gate 	}
3941*0Sstevel@tonic-gate 
3942*0Sstevel@tonic-gate 	ah = (ah_t *)(mp->b_rptr + ah_offset);
3943*0Sstevel@tonic-gate 
3944*0Sstevel@tonic-gate 	/*
3945*0Sstevel@tonic-gate 	 * We get here only when authentication passed.
3946*0Sstevel@tonic-gate 	 */
3947*0Sstevel@tonic-gate 
3948*0Sstevel@tonic-gate 	ah3dbg(("AH succeeded, checking replay\n"));
3949*0Sstevel@tonic-gate 	AH_BUMP_STAT(good_auth);
3950*0Sstevel@tonic-gate 
3951*0Sstevel@tonic-gate 	if (!sadb_replay_check(assoc, ah->ah_replay)) {
3952*0Sstevel@tonic-gate 		int af;
3953*0Sstevel@tonic-gate 		void *addr;
3954*0Sstevel@tonic-gate 
3955*0Sstevel@tonic-gate 		if (isv4) {
3956*0Sstevel@tonic-gate 			addr = &ipha->ipha_dst;
3957*0Sstevel@tonic-gate 			af = AF_INET;
3958*0Sstevel@tonic-gate 		} else {
3959*0Sstevel@tonic-gate 			addr = &ip6h->ip6_dst;
3960*0Sstevel@tonic-gate 			af = AF_INET6;
3961*0Sstevel@tonic-gate 		}
3962*0Sstevel@tonic-gate 
3963*0Sstevel@tonic-gate 		/*
3964*0Sstevel@tonic-gate 		 * Log the event. As of now we print out an event.
3965*0Sstevel@tonic-gate 		 * Do not print the replay failure number, or else
3966*0Sstevel@tonic-gate 		 * syslog cannot collate the error messages.  Printing
3967*0Sstevel@tonic-gate 		 * the replay number that failed (or printing to the
3968*0Sstevel@tonic-gate 		 * console) opens a denial-of-service attack.
3969*0Sstevel@tonic-gate 		 */
3970*0Sstevel@tonic-gate 		AH_BUMP_STAT(replay_failures);
3971*0Sstevel@tonic-gate 		ipsec_assocfailure(info.mi_idnum, 0, 0,
3972*0Sstevel@tonic-gate 		    SL_ERROR | SL_WARN,
3973*0Sstevel@tonic-gate 		    "Replay failed for AH spi %x, dst_addr %s",
3974*0Sstevel@tonic-gate 		    assoc->ipsa_spi, addr, af);
3975*0Sstevel@tonic-gate 		counter = &ipdrops_ah_replay;
3976*0Sstevel@tonic-gate 		goto ah_in_discard;
3977*0Sstevel@tonic-gate 	}
3978*0Sstevel@tonic-gate 
3979*0Sstevel@tonic-gate 	/*
3980*0Sstevel@tonic-gate 	 * We need to remove the AH header from the original
3981*0Sstevel@tonic-gate 	 * datagram. Easy way to do this is to use phdr_mp
3982*0Sstevel@tonic-gate 	 * to hold the IP header and the orginal mp to hold
3983*0Sstevel@tonic-gate 	 * the rest of it. So, we copy the IP header on to
3984*0Sstevel@tonic-gate 	 * phdr_mp, and set the b_rptr in mp past AH header.
3985*0Sstevel@tonic-gate 	 */
3986*0Sstevel@tonic-gate 	if (isv4) {
3987*0Sstevel@tonic-gate 		bcopy(mp->b_rptr, phdr_mp->b_rptr, ah_offset);
3988*0Sstevel@tonic-gate 		phdr_mp->b_wptr = phdr_mp->b_rptr + ah_offset;
3989*0Sstevel@tonic-gate 		nipha = (ipha_t *)phdr_mp->b_rptr;
3990*0Sstevel@tonic-gate 		/*
3991*0Sstevel@tonic-gate 		 * Assign the right protocol, adjust the length as we
3992*0Sstevel@tonic-gate 		 * are removing the AH header and adjust the checksum to
3993*0Sstevel@tonic-gate 		 * account for the protocol and length.
3994*0Sstevel@tonic-gate 		 */
3995*0Sstevel@tonic-gate 		nipha->ipha_protocol = ah->ah_nexthdr;
3996*0Sstevel@tonic-gate 		length = ntohs(nipha->ipha_length);
3997*0Sstevel@tonic-gate 		if (!ah_age_bytes(assoc, length, B_TRUE)) {
3998*0Sstevel@tonic-gate 			/* The ipsa has hit hard expiration, LOG and AUDIT. */
3999*0Sstevel@tonic-gate 			ipsec_assocfailure(info.mi_idnum, 0, 0,
4000*0Sstevel@tonic-gate 			    SL_ERROR | SL_WARN,
4001*0Sstevel@tonic-gate 			    "AH Association 0x%x, dst %s had bytes expire.\n",
4002*0Sstevel@tonic-gate 			    assoc->ipsa_spi, assoc->ipsa_dstaddr,
4003*0Sstevel@tonic-gate 			    AF_INET);
4004*0Sstevel@tonic-gate 			AH_BUMP_STAT(bytes_expired);
4005*0Sstevel@tonic-gate 			counter = &ipdrops_ah_bytes_expire;
4006*0Sstevel@tonic-gate 			goto ah_in_discard;
4007*0Sstevel@tonic-gate 		}
4008*0Sstevel@tonic-gate 		length -= (sizeof (ah_t) + align_len);
4009*0Sstevel@tonic-gate 
4010*0Sstevel@tonic-gate 		nipha->ipha_length = htons((uint16_t)length);
4011*0Sstevel@tonic-gate 		nipha->ipha_hdr_checksum = 0;
4012*0Sstevel@tonic-gate 		nipha->ipha_hdr_checksum = (uint16_t)ip_csum_hdr(nipha);
4013*0Sstevel@tonic-gate 		/*
4014*0Sstevel@tonic-gate 		 * Skip IP,AH and the authentication data in the
4015*0Sstevel@tonic-gate 		 * original datagram.
4016*0Sstevel@tonic-gate 		 */
4017*0Sstevel@tonic-gate 		mp->b_rptr += (ah_offset + sizeof (ah_t) + align_len);
4018*0Sstevel@tonic-gate 	} else {
4019*0Sstevel@tonic-gate 		uchar_t *whereptr;
4020*0Sstevel@tonic-gate 		int hdrlen;
4021*0Sstevel@tonic-gate 		uint8_t *nexthdr;
4022*0Sstevel@tonic-gate 		ip6_hbh_t *hbhhdr;
4023*0Sstevel@tonic-gate 		ip6_dest_t *dsthdr;
4024*0Sstevel@tonic-gate 		ip6_rthdr0_t *rthdr;
4025*0Sstevel@tonic-gate 
4026*0Sstevel@tonic-gate 		nip6h = (ip6_t *)phdr_mp->b_rptr;
4027*0Sstevel@tonic-gate 
4028*0Sstevel@tonic-gate 		/*
4029*0Sstevel@tonic-gate 		 * Make phdr_mp hold until the AH header and make
4030*0Sstevel@tonic-gate 		 * mp hold everything past AH header.
4031*0Sstevel@tonic-gate 		 */
4032*0Sstevel@tonic-gate 		length = ntohs(nip6h->ip6_plen);
4033*0Sstevel@tonic-gate 		if (!ah_age_bytes(assoc, length + sizeof (ip6_t), B_TRUE)) {
4034*0Sstevel@tonic-gate 			/* The ipsa has hit hard expiration, LOG and AUDIT. */
4035*0Sstevel@tonic-gate 			ipsec_assocfailure(info.mi_idnum, 0, 0,
4036*0Sstevel@tonic-gate 			    SL_ERROR | SL_WARN,
4037*0Sstevel@tonic-gate 			    "AH Association 0x%x, dst %s had bytes "
4038*0Sstevel@tonic-gate 			    "expire.\n", assoc->ipsa_spi, &ip6h->ip6_dst,
4039*0Sstevel@tonic-gate 			    AF_INET6);
4040*0Sstevel@tonic-gate 			AH_BUMP_STAT(bytes_expired);
4041*0Sstevel@tonic-gate 			counter = &ipdrops_ah_bytes_expire;
4042*0Sstevel@tonic-gate 			goto ah_in_discard;
4043*0Sstevel@tonic-gate 		}
4044*0Sstevel@tonic-gate 		bcopy(ip6h, nip6h, ah_offset);
4045*0Sstevel@tonic-gate 		phdr_mp->b_wptr = phdr_mp->b_rptr + ah_offset;
4046*0Sstevel@tonic-gate 		mp->b_rptr += (ah_offset + sizeof (ah_t) + align_len);
4047*0Sstevel@tonic-gate 
4048*0Sstevel@tonic-gate 		/*
4049*0Sstevel@tonic-gate 		 * Update the next header field of the header preceding
4050*0Sstevel@tonic-gate 		 * AH with the next header field of AH. Start with the
4051*0Sstevel@tonic-gate 		 * IPv6 header and proceed with the extension headers
4052*0Sstevel@tonic-gate 		 * until we find what we're looking for.
4053*0Sstevel@tonic-gate 		 */
4054*0Sstevel@tonic-gate 		nexthdr = &nip6h->ip6_nxt;
4055*0Sstevel@tonic-gate 		whereptr =  (uchar_t *)nip6h;
4056*0Sstevel@tonic-gate 		hdrlen = sizeof (ip6_t);
4057*0Sstevel@tonic-gate 
4058*0Sstevel@tonic-gate 		while (*nexthdr != IPPROTO_AH) {
4059*0Sstevel@tonic-gate 			whereptr += hdrlen;
4060*0Sstevel@tonic-gate 			/* Assume IP has already stripped it */
4061*0Sstevel@tonic-gate 			ASSERT(*nexthdr != IPPROTO_FRAGMENT &&
4062*0Sstevel@tonic-gate 			    *nexthdr != IPPROTO_RAW);
4063*0Sstevel@tonic-gate 			switch (*nexthdr) {
4064*0Sstevel@tonic-gate 			case IPPROTO_HOPOPTS:
4065*0Sstevel@tonic-gate 				hbhhdr = (ip6_hbh_t *)whereptr;
4066*0Sstevel@tonic-gate 				nexthdr = &hbhhdr->ip6h_nxt;
4067*0Sstevel@tonic-gate 				hdrlen = 8 * (hbhhdr->ip6h_len + 1);
4068*0Sstevel@tonic-gate 				break;
4069*0Sstevel@tonic-gate 			case IPPROTO_DSTOPTS:
4070*0Sstevel@tonic-gate 				dsthdr = (ip6_dest_t *)whereptr;
4071*0Sstevel@tonic-gate 				nexthdr = &dsthdr->ip6d_nxt;
4072*0Sstevel@tonic-gate 				hdrlen = 8 * (dsthdr->ip6d_len + 1);
4073*0Sstevel@tonic-gate 				break;
4074*0Sstevel@tonic-gate 			case IPPROTO_ROUTING:
4075*0Sstevel@tonic-gate 				rthdr = (ip6_rthdr0_t *)whereptr;
4076*0Sstevel@tonic-gate 				nexthdr = &rthdr->ip6r0_nxt;
4077*0Sstevel@tonic-gate 				hdrlen = 8 * (rthdr->ip6r0_len + 1);
4078*0Sstevel@tonic-gate 				break;
4079*0Sstevel@tonic-gate 			}
4080*0Sstevel@tonic-gate 		}
4081*0Sstevel@tonic-gate 		*nexthdr = ah->ah_nexthdr;
4082*0Sstevel@tonic-gate 
4083*0Sstevel@tonic-gate 		length -= (sizeof (ah_t) + align_len);
4084*0Sstevel@tonic-gate 		nip6h->ip6_plen = htons((uint16_t)length);
4085*0Sstevel@tonic-gate 	}
4086*0Sstevel@tonic-gate 
4087*0Sstevel@tonic-gate 	return (IPSEC_STATUS_SUCCESS);
4088*0Sstevel@tonic-gate 
4089*0Sstevel@tonic-gate ah_in_discard:
4090*0Sstevel@tonic-gate 	IP_AH_BUMP_STAT(in_discards);
4091*0Sstevel@tonic-gate 	ip_drop_packet(ipsec_in, B_TRUE, NULL, NULL, counter, &ah_dropper);
4092*0Sstevel@tonic-gate 	return (IPSEC_STATUS_FAILED);
4093*0Sstevel@tonic-gate }
4094*0Sstevel@tonic-gate 
4095*0Sstevel@tonic-gate /*
4096*0Sstevel@tonic-gate  * Invoked after processing of an outbound packet by the
4097*0Sstevel@tonic-gate  * kernel crypto framework, either by ah_submit_req() for a request
4098*0Sstevel@tonic-gate  * executed syncrhonously, or by the KEF callback for a request
4099*0Sstevel@tonic-gate  * executed asynchronously.
4100*0Sstevel@tonic-gate  */
4101*0Sstevel@tonic-gate static ipsec_status_t
4102*0Sstevel@tonic-gate ah_auth_out_done(mblk_t *ipsec_out)
4103*0Sstevel@tonic-gate {
4104*0Sstevel@tonic-gate 	mblk_t *phdr_mp;
4105*0Sstevel@tonic-gate 	mblk_t *mp;
4106*0Sstevel@tonic-gate 	int align_len;
4107*0Sstevel@tonic-gate 	uint32_t hdrs_length;
4108*0Sstevel@tonic-gate 	uchar_t *ptr;
4109*0Sstevel@tonic-gate 	uint32_t length;
4110*0Sstevel@tonic-gate 	boolean_t isv4;
4111*0Sstevel@tonic-gate 	ipsec_out_t *io;
4112*0Sstevel@tonic-gate 	size_t icv_len;
4113*0Sstevel@tonic-gate 
4114*0Sstevel@tonic-gate 	io = (ipsec_out_t *)ipsec_out->b_rptr;
4115*0Sstevel@tonic-gate 	isv4 = io->ipsec_out_v4;
4116*0Sstevel@tonic-gate 	icv_len = io->ipsec_out_crypto_mac.cd_raw.iov_len;
4117*0Sstevel@tonic-gate 
4118*0Sstevel@tonic-gate 	phdr_mp = ipsec_out->b_cont;
4119*0Sstevel@tonic-gate 	if (phdr_mp == NULL) {
4120*0Sstevel@tonic-gate 		ip_drop_packet(ipsec_out, B_FALSE, NULL, NULL,
4121*0Sstevel@tonic-gate 		    &ipdrops_ah_nomem, &ah_dropper);
4122*0Sstevel@tonic-gate 		return (IPSEC_STATUS_FAILED);
4123*0Sstevel@tonic-gate 	}
4124*0Sstevel@tonic-gate 
4125*0Sstevel@tonic-gate 	mp = phdr_mp->b_cont;
4126*0Sstevel@tonic-gate 	if (mp == NULL) {
4127*0Sstevel@tonic-gate 		ip_drop_packet(ipsec_out, B_FALSE, NULL, NULL,
4128*0Sstevel@tonic-gate 		    &ipdrops_ah_nomem, &ah_dropper);
4129*0Sstevel@tonic-gate 		return (IPSEC_STATUS_FAILED);
4130*0Sstevel@tonic-gate 	}
4131*0Sstevel@tonic-gate 	mp->b_rptr -= io->ipsec_out_skip_len;
4132*0Sstevel@tonic-gate 
4133*0Sstevel@tonic-gate 	if (isv4) {
4134*0Sstevel@tonic-gate 		ipha_t *ipha;
4135*0Sstevel@tonic-gate 		ipha_t *nipha;
4136*0Sstevel@tonic-gate 
4137*0Sstevel@tonic-gate 		ipha = (ipha_t *)mp->b_rptr;
4138*0Sstevel@tonic-gate 		hdrs_length = ipha->ipha_version_and_hdr_length -
4139*0Sstevel@tonic-gate 		    (uint8_t)((IP_VERSION << 4));
4140*0Sstevel@tonic-gate 		hdrs_length <<= 2;
4141*0Sstevel@tonic-gate 		align_len = P2ALIGN(icv_len + IPV4_PADDING_ALIGN - 1,
4142*0Sstevel@tonic-gate 		    IPV4_PADDING_ALIGN);
4143*0Sstevel@tonic-gate 		/*
4144*0Sstevel@tonic-gate 		 * phdr_mp must have the right amount of space for the
4145*0Sstevel@tonic-gate 		 * combined IP and AH header. Copy the IP header and
4146*0Sstevel@tonic-gate 		 * the ack_data onto AH. Note that the AH header was
4147*0Sstevel@tonic-gate 		 * already formed before the ICV calculation and hence
4148*0Sstevel@tonic-gate 		 * you don't have to copy it here.
4149*0Sstevel@tonic-gate 		 */
4150*0Sstevel@tonic-gate 		bcopy(mp->b_rptr, phdr_mp->b_rptr, hdrs_length);
4151*0Sstevel@tonic-gate 
4152*0Sstevel@tonic-gate 		ptr = phdr_mp->b_rptr + hdrs_length + sizeof (ah_t);
4153*0Sstevel@tonic-gate 		bcopy(phdr_mp->b_wptr, ptr, icv_len);
4154*0Sstevel@tonic-gate 
4155*0Sstevel@tonic-gate 		/*
4156*0Sstevel@tonic-gate 		 * Compute the new header checksum as we are assigning
4157*0Sstevel@tonic-gate 		 * IPPROTO_AH and adjusting the length here.
4158*0Sstevel@tonic-gate 		 */
4159*0Sstevel@tonic-gate 		nipha = (ipha_t *)phdr_mp->b_rptr;
4160*0Sstevel@tonic-gate 
4161*0Sstevel@tonic-gate 		nipha->ipha_protocol = IPPROTO_AH;
4162*0Sstevel@tonic-gate 		length = ntohs(nipha->ipha_length);
4163*0Sstevel@tonic-gate 		length += (sizeof (ah_t) + align_len);
4164*0Sstevel@tonic-gate 		nipha->ipha_length = htons((uint16_t)length);
4165*0Sstevel@tonic-gate 		nipha->ipha_hdr_checksum = 0;
4166*0Sstevel@tonic-gate 		nipha->ipha_hdr_checksum = (uint16_t)ip_csum_hdr(nipha);
4167*0Sstevel@tonic-gate 	} else {
4168*0Sstevel@tonic-gate 		ip6_t *ip6h;
4169*0Sstevel@tonic-gate 		ip6_t *nip6h;
4170*0Sstevel@tonic-gate 		uint_t ah_offset;
4171*0Sstevel@tonic-gate 
4172*0Sstevel@tonic-gate 		ip6h = (ip6_t *)mp->b_rptr;
4173*0Sstevel@tonic-gate 		nip6h = (ip6_t *)phdr_mp->b_rptr;
4174*0Sstevel@tonic-gate 		align_len = P2ALIGN(icv_len + IPV6_PADDING_ALIGN - 1,
4175*0Sstevel@tonic-gate 		    IPV6_PADDING_ALIGN);
4176*0Sstevel@tonic-gate 		/*
4177*0Sstevel@tonic-gate 		 * phdr_mp must have the right amount of space for the
4178*0Sstevel@tonic-gate 		 * combined IP and AH header. Copy the IP header with
4179*0Sstevel@tonic-gate 		 * options into the pseudo header. When we constructed
4180*0Sstevel@tonic-gate 		 * a pseudo header, we did not copy some of the mutable
4181*0Sstevel@tonic-gate 		 * fields. We do it now by calling ah_fix_phdr_v6()
4182*0Sstevel@tonic-gate 		 * with the last argument B_TRUE. It returns the
4183*0Sstevel@tonic-gate 		 * ah_offset into the pseudo header.
4184*0Sstevel@tonic-gate 		 */
4185*0Sstevel@tonic-gate 
4186*0Sstevel@tonic-gate 		bcopy(ip6h, nip6h, IPV6_HDR_LEN);
4187*0Sstevel@tonic-gate 		ah_offset = ah_fix_phdr_v6(nip6h, ip6h, B_TRUE, B_TRUE);
4188*0Sstevel@tonic-gate 		ASSERT(ah_offset != 0);
4189*0Sstevel@tonic-gate 		/*
4190*0Sstevel@tonic-gate 		 * phdr_mp can hold exactly the whole IP header with options
4191*0Sstevel@tonic-gate 		 * plus the AH header also. Thus subtracting the AH header's
4192*0Sstevel@tonic-gate 		 * size should give exactly how much of the original header
4193*0Sstevel@tonic-gate 		 * should be skipped.
4194*0Sstevel@tonic-gate 		 */
4195*0Sstevel@tonic-gate 		hdrs_length = (phdr_mp->b_wptr - phdr_mp->b_rptr) -
4196*0Sstevel@tonic-gate 		    sizeof (ah_t) - icv_len;
4197*0Sstevel@tonic-gate 		bcopy(phdr_mp->b_wptr, ((uint8_t *)nip6h + ah_offset +
4198*0Sstevel@tonic-gate 		    sizeof (ah_t)), icv_len);
4199*0Sstevel@tonic-gate 		length = ntohs(nip6h->ip6_plen);
4200*0Sstevel@tonic-gate 		length += (sizeof (ah_t) + align_len);
4201*0Sstevel@tonic-gate 		nip6h->ip6_plen = htons((uint16_t)length);
4202*0Sstevel@tonic-gate 	}
4203*0Sstevel@tonic-gate 
4204*0Sstevel@tonic-gate 	/* Skip the original IP header */
4205*0Sstevel@tonic-gate 	mp->b_rptr += hdrs_length;
4206*0Sstevel@tonic-gate 	if (mp->b_rptr == mp->b_wptr) {
4207*0Sstevel@tonic-gate 		phdr_mp->b_cont = mp->b_cont;
4208*0Sstevel@tonic-gate 		freeb(mp);
4209*0Sstevel@tonic-gate 	}
4210*0Sstevel@tonic-gate 
4211*0Sstevel@tonic-gate 	return (IPSEC_STATUS_SUCCESS);
4212*0Sstevel@tonic-gate }
4213*0Sstevel@tonic-gate 
4214*0Sstevel@tonic-gate /*
4215*0Sstevel@tonic-gate  * Wrapper to allow IP to trigger an AH strlog message if an error
4216*0Sstevel@tonic-gate  * condition is found during SA selection.
4217*0Sstevel@tonic-gate  */
4218*0Sstevel@tonic-gate void
4219*0Sstevel@tonic-gate ipsecah_rl_strlog(char level, ushort_t sl, char *fmt, ...)
4220*0Sstevel@tonic-gate {
4221*0Sstevel@tonic-gate 	va_list adx;
4222*0Sstevel@tonic-gate 
4223*0Sstevel@tonic-gate 	va_start(adx, fmt);
4224*0Sstevel@tonic-gate 	ipsec_rl_strlog(info.mi_idnum, 0, level, sl, fmt, adx);
4225*0Sstevel@tonic-gate 	va_end(adx);
4226*0Sstevel@tonic-gate }
4227*0Sstevel@tonic-gate 
4228*0Sstevel@tonic-gate /*
4229*0Sstevel@tonic-gate  * Wrapper to allow IP to trigger an AH association failure message
4230*0Sstevel@tonic-gate  * during SA inbound selection.
4231*0Sstevel@tonic-gate  */
4232*0Sstevel@tonic-gate void
4233*0Sstevel@tonic-gate ipsecah_in_assocfailure(mblk_t *mp, char level, ushort_t sl, char *fmt,
4234*0Sstevel@tonic-gate     uint32_t spi, void *addr, int af)
4235*0Sstevel@tonic-gate {
4236*0Sstevel@tonic-gate 	if (ipsecah_log_unknown_spi) {
4237*0Sstevel@tonic-gate 		ipsec_assocfailure(info.mi_idnum, 0, level, sl, fmt, spi,
4238*0Sstevel@tonic-gate 		    addr, af);
4239*0Sstevel@tonic-gate 	}
4240*0Sstevel@tonic-gate 
4241*0Sstevel@tonic-gate 	ip_drop_packet(mp, B_TRUE, NULL, NULL, &ipdrops_ah_no_sa,
4242*0Sstevel@tonic-gate 	    &ah_dropper);
4243*0Sstevel@tonic-gate }
4244*0Sstevel@tonic-gate 
4245*0Sstevel@tonic-gate /*
4246*0Sstevel@tonic-gate  * Initialize the AH input and output processing functions.
4247*0Sstevel@tonic-gate  */
4248*0Sstevel@tonic-gate void
4249*0Sstevel@tonic-gate ipsecah_init_funcs(ipsa_t *sa)
4250*0Sstevel@tonic-gate {
4251*0Sstevel@tonic-gate 	if (sa->ipsa_output_func == NULL)
4252*0Sstevel@tonic-gate 		sa->ipsa_output_func = ah_outbound;
4253*0Sstevel@tonic-gate 	if (sa->ipsa_input_func == NULL)
4254*0Sstevel@tonic-gate 		sa->ipsa_input_func = ah_inbound;
4255*0Sstevel@tonic-gate }
4256