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