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