10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 51659Smarkfen * Common Development and Distribution License (the "License"). 61659Smarkfen * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 223448Sdh155122 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 270Sstevel@tonic-gate 280Sstevel@tonic-gate #include <sys/types.h> 290Sstevel@tonic-gate #include <sys/stream.h> 300Sstevel@tonic-gate #include <sys/stropts.h> 310Sstevel@tonic-gate #include <sys/errno.h> 320Sstevel@tonic-gate #include <sys/strlog.h> 330Sstevel@tonic-gate #include <sys/tihdr.h> 340Sstevel@tonic-gate #include <sys/socket.h> 350Sstevel@tonic-gate #include <sys/ddi.h> 360Sstevel@tonic-gate #include <sys/sunddi.h> 370Sstevel@tonic-gate #include <sys/kmem.h> 383448Sdh155122 #include <sys/zone.h> 393192Sdanmcd #include <sys/sysmacros.h> 400Sstevel@tonic-gate #include <sys/cmn_err.h> 410Sstevel@tonic-gate #include <sys/vtrace.h> 420Sstevel@tonic-gate #include <sys/debug.h> 430Sstevel@tonic-gate #include <sys/atomic.h> 440Sstevel@tonic-gate #include <sys/strsun.h> 450Sstevel@tonic-gate #include <sys/random.h> 460Sstevel@tonic-gate #include <netinet/in.h> 470Sstevel@tonic-gate #include <net/if.h> 480Sstevel@tonic-gate #include <netinet/ip6.h> 490Sstevel@tonic-gate #include <netinet/icmp6.h> 500Sstevel@tonic-gate #include <net/pfkeyv2.h> 510Sstevel@tonic-gate 520Sstevel@tonic-gate #include <inet/common.h> 530Sstevel@tonic-gate #include <inet/mi.h> 540Sstevel@tonic-gate #include <inet/ip.h> 550Sstevel@tonic-gate #include <inet/ip6.h> 560Sstevel@tonic-gate #include <inet/nd.h> 570Sstevel@tonic-gate #include <inet/ipsec_info.h> 580Sstevel@tonic-gate #include <inet/ipsec_impl.h> 590Sstevel@tonic-gate #include <inet/sadb.h> 600Sstevel@tonic-gate #include <inet/ipsecah.h> 610Sstevel@tonic-gate #include <inet/ipsec_impl.h> 620Sstevel@tonic-gate #include <inet/ipdrop.h> 630Sstevel@tonic-gate #include <sys/taskq.h> 640Sstevel@tonic-gate #include <sys/policy.h> 650Sstevel@tonic-gate #include <sys/iphada.h> 660Sstevel@tonic-gate #include <sys/strsun.h> 670Sstevel@tonic-gate 680Sstevel@tonic-gate #include <sys/crypto/common.h> 690Sstevel@tonic-gate #include <sys/crypto/api.h> 700Sstevel@tonic-gate #include <sys/kstat.h> 711676Sjpk #include <sys/strsubr.h> 720Sstevel@tonic-gate 730Sstevel@tonic-gate /* 740Sstevel@tonic-gate * Table of ND variables supported by ipsecah. These are loaded into 750Sstevel@tonic-gate * ipsecah_g_nd in ipsecah_init_nd. 760Sstevel@tonic-gate * All of these are alterable, within the min/max values given, at run time. 770Sstevel@tonic-gate */ 783448Sdh155122 static ipsecahparam_t lcl_param_arr[] = { 790Sstevel@tonic-gate /* min max value name */ 800Sstevel@tonic-gate { 0, 3, 0, "ipsecah_debug"}, 810Sstevel@tonic-gate { 125, 32000, SADB_AGE_INTERVAL_DEFAULT, "ipsecah_age_interval"}, 820Sstevel@tonic-gate { 1, 10, 1, "ipsecah_reap_delay"}, 830Sstevel@tonic-gate { 1, SADB_MAX_REPLAY, 64, "ipsecah_replay_size"}, 840Sstevel@tonic-gate { 1, 300, 15, "ipsecah_acquire_timeout"}, 850Sstevel@tonic-gate { 1, 1800, 90, "ipsecah_larval_timeout"}, 860Sstevel@tonic-gate /* Default lifetime values for ACQUIRE messages. */ 870Sstevel@tonic-gate { 0, 0xffffffffU, 0, "ipsecah_default_soft_bytes"}, 880Sstevel@tonic-gate { 0, 0xffffffffU, 0, "ipsecah_default_hard_bytes"}, 890Sstevel@tonic-gate { 0, 0xffffffffU, 24000, "ipsecah_default_soft_addtime"}, 900Sstevel@tonic-gate { 0, 0xffffffffU, 28800, "ipsecah_default_hard_addtime"}, 910Sstevel@tonic-gate { 0, 0xffffffffU, 0, "ipsecah_default_soft_usetime"}, 920Sstevel@tonic-gate { 0, 0xffffffffU, 0, "ipsecah_default_hard_usetime"}, 930Sstevel@tonic-gate { 0, 1, 0, "ipsecah_log_unknown_spi"}, 940Sstevel@tonic-gate }; 953448Sdh155122 #define ipsecah_debug ipsecah_params[0].ipsecah_param_value 963448Sdh155122 #define ipsecah_age_interval ipsecah_params[1].ipsecah_param_value 973448Sdh155122 #define ipsecah_age_int_max ipsecah_params[1].ipsecah_param_max 983448Sdh155122 #define ipsecah_reap_delay ipsecah_params[2].ipsecah_param_value 993448Sdh155122 #define ipsecah_replay_size ipsecah_params[3].ipsecah_param_value 1003448Sdh155122 #define ipsecah_acquire_timeout ipsecah_params[4].ipsecah_param_value 1013448Sdh155122 #define ipsecah_larval_timeout ipsecah_params[5].ipsecah_param_value 1023448Sdh155122 #define ipsecah_default_soft_bytes ipsecah_params[6].ipsecah_param_value 1033448Sdh155122 #define ipsecah_default_hard_bytes ipsecah_params[7].ipsecah_param_value 1043448Sdh155122 #define ipsecah_default_soft_addtime ipsecah_params[8].ipsecah_param_value 1053448Sdh155122 #define ipsecah_default_hard_addtime ipsecah_params[9].ipsecah_param_value 1063448Sdh155122 #define ipsecah_default_soft_usetime ipsecah_params[10].ipsecah_param_value 1073448Sdh155122 #define ipsecah_default_hard_usetime ipsecah_params[11].ipsecah_param_value 1083448Sdh155122 #define ipsecah_log_unknown_spi ipsecah_params[12].ipsecah_param_value 1090Sstevel@tonic-gate 1100Sstevel@tonic-gate #define ah0dbg(a) printf a 1110Sstevel@tonic-gate /* NOTE: != 0 instead of > 0 so lint doesn't complain. */ 1123448Sdh155122 #define ah1dbg(ahstack, a) if (ahstack->ipsecah_debug != 0) printf a 1133448Sdh155122 #define ah2dbg(ahstack, a) if (ahstack->ipsecah_debug > 1) printf a 1143448Sdh155122 #define ah3dbg(ahstack, a) if (ahstack->ipsecah_debug > 2) printf a 1150Sstevel@tonic-gate 1160Sstevel@tonic-gate /* 1170Sstevel@tonic-gate * XXX This is broken. Padding should be determined dynamically 1180Sstevel@tonic-gate * depending on the ICV size and IP version number so that the 1190Sstevel@tonic-gate * total AH header size is a multiple of 32 bits or 64 bits 1200Sstevel@tonic-gate * for V4 and V6 respectively. For 96bit ICVs we have no problems. 1210Sstevel@tonic-gate * Anything different from that, we need to fix our code. 1220Sstevel@tonic-gate */ 1230Sstevel@tonic-gate #define IPV4_PADDING_ALIGN 0x04 /* Multiple of 32 bits */ 1240Sstevel@tonic-gate #define IPV6_PADDING_ALIGN 0x04 /* Multiple of 32 bits */ 1250Sstevel@tonic-gate 1260Sstevel@tonic-gate /* 1270Sstevel@tonic-gate * Helper macro. Avoids a call to msgdsize if there is only one 1280Sstevel@tonic-gate * mblk in the chain. 1290Sstevel@tonic-gate */ 1300Sstevel@tonic-gate #define AH_MSGSIZE(mp) ((mp)->b_cont != NULL ? msgdsize(mp) : MBLKL(mp)) 1310Sstevel@tonic-gate 1323448Sdh155122 1330Sstevel@tonic-gate static ipsec_status_t ah_auth_out_done(mblk_t *); 1340Sstevel@tonic-gate static ipsec_status_t ah_auth_in_done(mblk_t *); 1350Sstevel@tonic-gate static mblk_t *ah_process_ip_options_v4(mblk_t *, ipsa_t *, int *, uint_t, 1363448Sdh155122 boolean_t, ipsecah_stack_t *); 1370Sstevel@tonic-gate static mblk_t *ah_process_ip_options_v6(mblk_t *, ipsa_t *, int *, uint_t, 1383448Sdh155122 boolean_t, ipsecah_stack_t *); 1393448Sdh155122 static void ah_getspi(mblk_t *, keysock_in_t *, ipsecah_stack_t *); 1400Sstevel@tonic-gate static ipsec_status_t ah_inbound_accelerated(mblk_t *, boolean_t, ipsa_t *, 1410Sstevel@tonic-gate uint32_t); 1420Sstevel@tonic-gate static ipsec_status_t ah_outbound_accelerated_v4(mblk_t *, ipsa_t *); 1430Sstevel@tonic-gate static ipsec_status_t ah_outbound_accelerated_v6(mblk_t *, ipsa_t *); 1440Sstevel@tonic-gate static ipsec_status_t ah_outbound(mblk_t *); 1450Sstevel@tonic-gate 1460Sstevel@tonic-gate static int ipsecah_open(queue_t *, dev_t *, int, int, cred_t *); 1470Sstevel@tonic-gate static int ipsecah_close(queue_t *); 1480Sstevel@tonic-gate static void ipsecah_rput(queue_t *, mblk_t *); 1490Sstevel@tonic-gate static void ipsecah_wput(queue_t *, mblk_t *); 1503448Sdh155122 static void ah_send_acquire(ipsacq_t *, mblk_t *, netstack_t *); 1513448Sdh155122 static boolean_t ah_register_out(uint32_t, uint32_t, uint_t, ipsecah_stack_t *); 1523448Sdh155122 static void *ipsecah_stack_init(netstackid_t stackid, netstack_t *ns); 1533448Sdh155122 static void ipsecah_stack_fini(netstackid_t stackid, void *arg); 1543448Sdh155122 1553448Sdh155122 /* Setable in /etc/system */ 1563448Sdh155122 uint32_t ah_hash_size = IPSEC_DEFAULT_HASH_SIZE; 1573448Sdh155122 1583448Sdh155122 static taskq_t *ah_taskq; 1590Sstevel@tonic-gate 1600Sstevel@tonic-gate static struct module_info info = { 1610Sstevel@tonic-gate 5136, "ipsecah", 0, INFPSZ, 65536, 1024 1620Sstevel@tonic-gate }; 1630Sstevel@tonic-gate 1640Sstevel@tonic-gate static struct qinit rinit = { 1650Sstevel@tonic-gate (pfi_t)ipsecah_rput, NULL, ipsecah_open, ipsecah_close, NULL, &info, 1660Sstevel@tonic-gate NULL 1670Sstevel@tonic-gate }; 1680Sstevel@tonic-gate 1690Sstevel@tonic-gate static struct qinit winit = { 1700Sstevel@tonic-gate (pfi_t)ipsecah_wput, NULL, ipsecah_open, ipsecah_close, NULL, &info, 1710Sstevel@tonic-gate NULL 1720Sstevel@tonic-gate }; 1730Sstevel@tonic-gate 1740Sstevel@tonic-gate struct streamtab ipsecahinfo = { 1750Sstevel@tonic-gate &rinit, &winit, NULL, NULL 1760Sstevel@tonic-gate }; 1770Sstevel@tonic-gate 1780Sstevel@tonic-gate static int ah_kstat_update(kstat_t *, int); 1790Sstevel@tonic-gate 1801659Smarkfen uint64_t ipsacq_maxpackets = IPSACQ_MAXPACKETS; 1811659Smarkfen 1820Sstevel@tonic-gate static boolean_t 1833448Sdh155122 ah_kstat_init(ipsecah_stack_t *ahstack, netstackid_t stackid) 1840Sstevel@tonic-gate { 1853448Sdh155122 ipsec_stack_t *ipss = ahstack->ipsecah_netstack->netstack_ipsec; 1863448Sdh155122 1873448Sdh155122 ahstack->ah_ksp = kstat_create_netstack("ipsecah", 0, "ah_stat", "net", 1883448Sdh155122 KSTAT_TYPE_NAMED, sizeof (ah_kstats_t) / sizeof (kstat_named_t), 1893448Sdh155122 KSTAT_FLAG_PERSISTENT, stackid); 1903448Sdh155122 1913448Sdh155122 if (ahstack->ah_ksp == NULL || ahstack->ah_ksp->ks_data == NULL) 1920Sstevel@tonic-gate return (B_FALSE); 1930Sstevel@tonic-gate 1943448Sdh155122 ahstack->ah_kstats = ahstack->ah_ksp->ks_data; 1953448Sdh155122 1963448Sdh155122 ahstack->ah_ksp->ks_update = ah_kstat_update; 1973448Sdh155122 ahstack->ah_ksp->ks_private = (void *)(uintptr_t)stackid; 1980Sstevel@tonic-gate 1990Sstevel@tonic-gate #define K64 KSTAT_DATA_UINT64 2003448Sdh155122 #define KI(x) kstat_named_init(&(ahstack->ah_kstats->ah_stat_##x), #x, K64) 2010Sstevel@tonic-gate 2020Sstevel@tonic-gate KI(num_aalgs); 2030Sstevel@tonic-gate KI(good_auth); 2040Sstevel@tonic-gate KI(bad_auth); 2050Sstevel@tonic-gate KI(replay_failures); 2060Sstevel@tonic-gate KI(replay_early_failures); 2070Sstevel@tonic-gate KI(keysock_in); 2080Sstevel@tonic-gate KI(out_requests); 2090Sstevel@tonic-gate KI(acquire_requests); 2100Sstevel@tonic-gate KI(bytes_expired); 2110Sstevel@tonic-gate KI(out_discards); 2120Sstevel@tonic-gate KI(in_accelerated); 2130Sstevel@tonic-gate KI(out_accelerated); 2140Sstevel@tonic-gate KI(noaccel); 2150Sstevel@tonic-gate KI(crypto_sync); 2160Sstevel@tonic-gate KI(crypto_async); 2170Sstevel@tonic-gate KI(crypto_failures); 2180Sstevel@tonic-gate 2190Sstevel@tonic-gate #undef KI 2200Sstevel@tonic-gate #undef K64 2210Sstevel@tonic-gate 2223448Sdh155122 kstat_install(ahstack->ah_ksp); 2233448Sdh155122 IP_ACQUIRE_STAT(ipss, maxpackets, ipsacq_maxpackets); 2240Sstevel@tonic-gate return (B_TRUE); 2250Sstevel@tonic-gate } 2260Sstevel@tonic-gate 2270Sstevel@tonic-gate static int 2280Sstevel@tonic-gate ah_kstat_update(kstat_t *kp, int rw) 2290Sstevel@tonic-gate { 2303448Sdh155122 ah_kstats_t *ekp; 2313448Sdh155122 netstackid_t stackid = (netstackid_t)(uintptr_t)kp->ks_private; 2323448Sdh155122 netstack_t *ns; 2333448Sdh155122 ipsec_stack_t *ipss; 2340Sstevel@tonic-gate 2350Sstevel@tonic-gate if ((kp == NULL) || (kp->ks_data == NULL)) 2360Sstevel@tonic-gate return (EIO); 2370Sstevel@tonic-gate 2380Sstevel@tonic-gate if (rw == KSTAT_WRITE) 2390Sstevel@tonic-gate return (EACCES); 2400Sstevel@tonic-gate 2413448Sdh155122 ns = netstack_find_by_stackid(stackid); 2423448Sdh155122 if (ns == NULL) 2433448Sdh155122 return (-1); 2443448Sdh155122 ipss = ns->netstack_ipsec; 2453448Sdh155122 if (ipss == NULL) { 2463448Sdh155122 netstack_rele(ns); 2473448Sdh155122 return (-1); 2483448Sdh155122 } 2490Sstevel@tonic-gate ekp = (ah_kstats_t *)kp->ks_data; 2503448Sdh155122 2513448Sdh155122 mutex_enter(&ipss->ipsec_alg_lock); 2523448Sdh155122 ekp->ah_stat_num_aalgs.value.ui64 = ipss->ipsec_nalgs[IPSEC_ALG_AUTH]; 2533448Sdh155122 mutex_exit(&ipss->ipsec_alg_lock); 2543448Sdh155122 2553448Sdh155122 netstack_rele(ns); 2560Sstevel@tonic-gate return (0); 2570Sstevel@tonic-gate } 2580Sstevel@tonic-gate 2590Sstevel@tonic-gate /* 2600Sstevel@tonic-gate * Don't have to lock ipsec_age_interval, as only one thread will access it at 2610Sstevel@tonic-gate * a time, because I control the one function that does a qtimeout() on 2620Sstevel@tonic-gate * ah_pfkey_q. 2630Sstevel@tonic-gate */ 2640Sstevel@tonic-gate static void 2653448Sdh155122 ah_ager(void *arg) 2660Sstevel@tonic-gate { 2673448Sdh155122 ipsecah_stack_t *ahstack = (ipsecah_stack_t *)arg; 2683448Sdh155122 netstack_t *ns = ahstack->ipsecah_netstack; 2690Sstevel@tonic-gate hrtime_t begin = gethrtime(); 2700Sstevel@tonic-gate 2713448Sdh155122 sadb_ager(&ahstack->ah_sadb.s_v4, ahstack->ah_pfkey_q, 2723448Sdh155122 ahstack->ah_sadb.s_ip_q, ahstack->ipsecah_reap_delay, ns); 2733448Sdh155122 sadb_ager(&ahstack->ah_sadb.s_v6, ahstack->ah_pfkey_q, 2743448Sdh155122 ahstack->ah_sadb.s_ip_q, ahstack->ipsecah_reap_delay, ns); 2753448Sdh155122 2763448Sdh155122 ahstack->ah_event = sadb_retimeout(begin, ahstack->ah_pfkey_q, 2773448Sdh155122 ah_ager, ahstack, 2783448Sdh155122 &ahstack->ipsecah_age_interval, ahstack->ipsecah_age_int_max, 2793448Sdh155122 info.mi_idnum); 2800Sstevel@tonic-gate } 2810Sstevel@tonic-gate 2820Sstevel@tonic-gate /* 2830Sstevel@tonic-gate * Get an AH NDD parameter. 2840Sstevel@tonic-gate */ 2850Sstevel@tonic-gate /* ARGSUSED */ 2860Sstevel@tonic-gate static int 2870Sstevel@tonic-gate ipsecah_param_get(q, mp, cp, cr) 2880Sstevel@tonic-gate queue_t *q; 2890Sstevel@tonic-gate mblk_t *mp; 2900Sstevel@tonic-gate caddr_t cp; 2910Sstevel@tonic-gate cred_t *cr; 2920Sstevel@tonic-gate { 2930Sstevel@tonic-gate ipsecahparam_t *ipsecahpa = (ipsecahparam_t *)cp; 2940Sstevel@tonic-gate uint_t value; 2953448Sdh155122 ipsecah_stack_t *ahstack = (ipsecah_stack_t *)q->q_ptr; 2963448Sdh155122 2973448Sdh155122 mutex_enter(&ahstack->ipsecah_param_lock); 2980Sstevel@tonic-gate value = ipsecahpa->ipsecah_param_value; 2993448Sdh155122 mutex_exit(&ahstack->ipsecah_param_lock); 3000Sstevel@tonic-gate 3010Sstevel@tonic-gate (void) mi_mpprintf(mp, "%u", value); 3020Sstevel@tonic-gate return (0); 3030Sstevel@tonic-gate } 3040Sstevel@tonic-gate 3050Sstevel@tonic-gate /* 3060Sstevel@tonic-gate * This routine sets an NDD variable in a ipsecahparam_t structure. 3070Sstevel@tonic-gate */ 3080Sstevel@tonic-gate /* ARGSUSED */ 3090Sstevel@tonic-gate static int 3100Sstevel@tonic-gate ipsecah_param_set(q, mp, value, cp, cr) 3110Sstevel@tonic-gate queue_t *q; 3120Sstevel@tonic-gate mblk_t *mp; 3130Sstevel@tonic-gate char *value; 3140Sstevel@tonic-gate caddr_t cp; 3150Sstevel@tonic-gate cred_t *cr; 3160Sstevel@tonic-gate { 3170Sstevel@tonic-gate ulong_t new_value; 3180Sstevel@tonic-gate ipsecahparam_t *ipsecahpa = (ipsecahparam_t *)cp; 3193448Sdh155122 ipsecah_stack_t *ahstack = (ipsecah_stack_t *)q->q_ptr; 3200Sstevel@tonic-gate 3210Sstevel@tonic-gate /* 3220Sstevel@tonic-gate * Fail the request if the new value does not lie within the 3230Sstevel@tonic-gate * required bounds. 3240Sstevel@tonic-gate */ 3250Sstevel@tonic-gate if (ddi_strtoul(value, NULL, 10, &new_value) != 0 || 3260Sstevel@tonic-gate new_value < ipsecahpa->ipsecah_param_min || 3270Sstevel@tonic-gate new_value > ipsecahpa->ipsecah_param_max) { 3280Sstevel@tonic-gate return (EINVAL); 3290Sstevel@tonic-gate } 3300Sstevel@tonic-gate 3310Sstevel@tonic-gate /* Set the new value */ 3323448Sdh155122 mutex_enter(&ahstack->ipsecah_param_lock); 3330Sstevel@tonic-gate ipsecahpa->ipsecah_param_value = new_value; 3343448Sdh155122 mutex_exit(&ahstack->ipsecah_param_lock); 3350Sstevel@tonic-gate return (0); 3360Sstevel@tonic-gate } 3370Sstevel@tonic-gate 3380Sstevel@tonic-gate /* 3390Sstevel@tonic-gate * Using lifetime NDD variables, fill in an extended combination's 3400Sstevel@tonic-gate * lifetime information. 3410Sstevel@tonic-gate */ 3420Sstevel@tonic-gate void 3433448Sdh155122 ipsecah_fill_defs(sadb_x_ecomb_t *ecomb, netstack_t *ns) 3440Sstevel@tonic-gate { 3453448Sdh155122 ipsecah_stack_t *ahstack = ns->netstack_ipsecah; 3463448Sdh155122 3473448Sdh155122 ecomb->sadb_x_ecomb_soft_bytes = ahstack->ipsecah_default_soft_bytes; 3483448Sdh155122 ecomb->sadb_x_ecomb_hard_bytes = ahstack->ipsecah_default_hard_bytes; 3493448Sdh155122 ecomb->sadb_x_ecomb_soft_addtime = 3503448Sdh155122 ahstack->ipsecah_default_soft_addtime; 3513448Sdh155122 ecomb->sadb_x_ecomb_hard_addtime = 3523448Sdh155122 ahstack->ipsecah_default_hard_addtime; 3533448Sdh155122 ecomb->sadb_x_ecomb_soft_usetime = 3543448Sdh155122 ahstack->ipsecah_default_soft_usetime; 3553448Sdh155122 ecomb->sadb_x_ecomb_hard_usetime = 3563448Sdh155122 ahstack->ipsecah_default_hard_usetime; 3570Sstevel@tonic-gate } 3580Sstevel@tonic-gate 3590Sstevel@tonic-gate /* 3600Sstevel@tonic-gate * Initialize things for AH at module load time. 3610Sstevel@tonic-gate */ 3620Sstevel@tonic-gate boolean_t 3630Sstevel@tonic-gate ipsecah_ddi_init(void) 3640Sstevel@tonic-gate { 3653448Sdh155122 ah_taskq = taskq_create("ah_taskq", 1, minclsyspri, 3663448Sdh155122 IPSEC_TASKQ_MIN, IPSEC_TASKQ_MAX, 0); 3673448Sdh155122 3683448Sdh155122 /* 3693448Sdh155122 * We want to be informed each time a stack is created or 3703448Sdh155122 * destroyed in the kernel, so we can maintain the 3713448Sdh155122 * set of ipsecah_stack_t's. 3723448Sdh155122 */ 3733448Sdh155122 netstack_register(NS_IPSECAH, ipsecah_stack_init, NULL, 3743448Sdh155122 ipsecah_stack_fini); 3753448Sdh155122 3763448Sdh155122 return (B_TRUE); 3773448Sdh155122 } 3783448Sdh155122 3793448Sdh155122 /* 3803448Sdh155122 * Walk through the param array specified registering each element with the 3813448Sdh155122 * named dispatch handler. 3823448Sdh155122 */ 3833448Sdh155122 static boolean_t 3843448Sdh155122 ipsecah_param_register(IDP *ndp, ipsecahparam_t *ahp, int cnt) 3853448Sdh155122 { 3863448Sdh155122 for (; cnt-- > 0; ahp++) { 3870Sstevel@tonic-gate if (ahp->ipsecah_param_name != NULL && 3880Sstevel@tonic-gate ahp->ipsecah_param_name[0]) { 3893448Sdh155122 if (!nd_load(ndp, 3903448Sdh155122 ahp->ipsecah_param_name, 3910Sstevel@tonic-gate ipsecah_param_get, ipsecah_param_set, 3920Sstevel@tonic-gate (caddr_t)ahp)) { 3933448Sdh155122 nd_free(ndp); 3940Sstevel@tonic-gate return (B_FALSE); 3950Sstevel@tonic-gate } 3960Sstevel@tonic-gate } 3970Sstevel@tonic-gate } 3980Sstevel@tonic-gate return (B_TRUE); 3990Sstevel@tonic-gate } 4000Sstevel@tonic-gate 4010Sstevel@tonic-gate /* 4023448Sdh155122 * Initialize things for AH for each stack instance 4033448Sdh155122 */ 4043448Sdh155122 static void * 4053448Sdh155122 ipsecah_stack_init(netstackid_t stackid, netstack_t *ns) 4063448Sdh155122 { 4073448Sdh155122 ipsecah_stack_t *ahstack; 4083448Sdh155122 ipsecahparam_t *ahp; 4093448Sdh155122 4103448Sdh155122 ahstack = (ipsecah_stack_t *)kmem_zalloc(sizeof (*ahstack), KM_SLEEP); 4113448Sdh155122 ahstack->ipsecah_netstack = ns; 4123448Sdh155122 4133448Sdh155122 ahp = (ipsecahparam_t *)kmem_alloc(sizeof (lcl_param_arr), KM_SLEEP); 4143448Sdh155122 ahstack->ipsecah_params = ahp; 4153448Sdh155122 bcopy(lcl_param_arr, ahp, sizeof (lcl_param_arr)); 4163448Sdh155122 4173448Sdh155122 (void) ipsecah_param_register(&ahstack->ipsecah_g_nd, ahp, 4183448Sdh155122 A_CNT(lcl_param_arr)); 4193448Sdh155122 4203448Sdh155122 (void) ah_kstat_init(ahstack, stackid); 4213448Sdh155122 4223448Sdh155122 ahstack->ah_sadb.s_acquire_timeout = &ahstack->ipsecah_acquire_timeout; 4233448Sdh155122 ahstack->ah_sadb.s_acqfn = ah_send_acquire; 4243448Sdh155122 sadbp_init("AH", &ahstack->ah_sadb, SADB_SATYPE_AH, ah_hash_size, 4253448Sdh155122 ahstack->ipsecah_netstack); 4263448Sdh155122 4273448Sdh155122 mutex_init(&ahstack->ipsecah_param_lock, NULL, MUTEX_DEFAULT, 0); 4283448Sdh155122 4293448Sdh155122 ip_drop_register(&ahstack->ah_dropper, "IPsec AH"); 4303448Sdh155122 return (ahstack); 4313448Sdh155122 } 4323448Sdh155122 4333448Sdh155122 /* 4340Sstevel@tonic-gate * Destroy things for AH at module unload time. 4350Sstevel@tonic-gate */ 4360Sstevel@tonic-gate void 4370Sstevel@tonic-gate ipsecah_ddi_destroy(void) 4380Sstevel@tonic-gate { 4393448Sdh155122 netstack_unregister(NS_IPSECAH); 4400Sstevel@tonic-gate taskq_destroy(ah_taskq); 4413448Sdh155122 } 4423448Sdh155122 4433448Sdh155122 /* 4443448Sdh155122 * Destroy things for AH for one stack... Never called? 4453448Sdh155122 */ 4463448Sdh155122 static void 4473448Sdh155122 ipsecah_stack_fini(netstackid_t stackid, void *arg) 4483448Sdh155122 { 4493448Sdh155122 ipsecah_stack_t *ahstack = (ipsecah_stack_t *)arg; 4503448Sdh155122 4513448Sdh155122 if (ahstack->ah_pfkey_q != NULL) { 4523448Sdh155122 (void) quntimeout(ahstack->ah_pfkey_q, ahstack->ah_event); 4533448Sdh155122 } 4543448Sdh155122 ahstack->ah_sadb.s_acqfn = NULL; 4553448Sdh155122 ahstack->ah_sadb.s_acquire_timeout = NULL; 4563448Sdh155122 sadbp_destroy(&ahstack->ah_sadb, ahstack->ipsecah_netstack); 4573448Sdh155122 ip_drop_unregister(&ahstack->ah_dropper); 4583448Sdh155122 mutex_destroy(&ahstack->ipsecah_param_lock); 4593448Sdh155122 nd_free(&ahstack->ipsecah_g_nd); 4603448Sdh155122 4613448Sdh155122 kmem_free(ahstack->ipsecah_params, sizeof (lcl_param_arr)); 4623448Sdh155122 ahstack->ipsecah_params = NULL; 4633448Sdh155122 kstat_delete_netstack(ahstack->ah_ksp, stackid); 4643448Sdh155122 ahstack->ah_ksp = NULL; 4653448Sdh155122 ahstack->ah_kstats = NULL; 4663448Sdh155122 4673448Sdh155122 kmem_free(ahstack, sizeof (*ahstack)); 4680Sstevel@tonic-gate } 4690Sstevel@tonic-gate 4700Sstevel@tonic-gate /* 4710Sstevel@tonic-gate * AH module open routine. The module should be opened by keysock. 4720Sstevel@tonic-gate */ 4730Sstevel@tonic-gate /* ARGSUSED */ 4740Sstevel@tonic-gate static int 4750Sstevel@tonic-gate ipsecah_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp) 4760Sstevel@tonic-gate { 4773448Sdh155122 netstack_t *ns; 4783448Sdh155122 ipsecah_stack_t *ahstack; 4793448Sdh155122 4803448Sdh155122 if (secpolicy_ip_config(credp, B_FALSE) != 0) { 4813448Sdh155122 ah0dbg(("Non-privileged user trying to open ipsecah.\n")); 4820Sstevel@tonic-gate return (EPERM); 4830Sstevel@tonic-gate } 4840Sstevel@tonic-gate 4850Sstevel@tonic-gate if (q->q_ptr != NULL) 4860Sstevel@tonic-gate return (0); /* Re-open of an already open instance. */ 4870Sstevel@tonic-gate 4880Sstevel@tonic-gate if (sflag != MODOPEN) 4890Sstevel@tonic-gate return (EINVAL); 4900Sstevel@tonic-gate 4913448Sdh155122 ns = netstack_find_by_cred(credp); 4923448Sdh155122 ASSERT(ns != NULL); 4933448Sdh155122 ahstack = ns->netstack_ipsecah; 4943448Sdh155122 ASSERT(ahstack != NULL); 4953448Sdh155122 4960Sstevel@tonic-gate /* 4970Sstevel@tonic-gate * ASSUMPTIONS (because I'm MT_OCEXCL): 4980Sstevel@tonic-gate * 4990Sstevel@tonic-gate * * I'm being pushed on top of IP for all my opens (incl. #1). 5000Sstevel@tonic-gate * * Only ipsecah_open() can write into ah_sadb.s_ip_q. 5010Sstevel@tonic-gate * * Because of this, I can check lazily for ah_sadb.s_ip_q. 5020Sstevel@tonic-gate * 5030Sstevel@tonic-gate * If these assumptions are wrong, I'm in BIG trouble... 5040Sstevel@tonic-gate */ 5050Sstevel@tonic-gate 5063448Sdh155122 q->q_ptr = ahstack; 5073448Sdh155122 WR(q)->q_ptr = q->q_ptr; 5083448Sdh155122 5093448Sdh155122 if (ahstack->ah_sadb.s_ip_q == NULL) { 5100Sstevel@tonic-gate struct T_unbind_req *tur; 5110Sstevel@tonic-gate 5123448Sdh155122 ahstack->ah_sadb.s_ip_q = WR(q); 5130Sstevel@tonic-gate /* Allocate an unbind... */ 5143448Sdh155122 ahstack->ah_ip_unbind = allocb(sizeof (struct T_unbind_req), 5153448Sdh155122 BPRI_HI); 5160Sstevel@tonic-gate 5170Sstevel@tonic-gate /* 5180Sstevel@tonic-gate * Send down T_BIND_REQ to bind IPPROTO_AH. 5190Sstevel@tonic-gate * Handle the ACK here in AH. 5200Sstevel@tonic-gate */ 5210Sstevel@tonic-gate qprocson(q); 5223448Sdh155122 if (ahstack->ah_ip_unbind == NULL || 5233448Sdh155122 !sadb_t_bind_req(ahstack->ah_sadb.s_ip_q, IPPROTO_AH)) { 5243448Sdh155122 if (ahstack->ah_ip_unbind != NULL) { 5253448Sdh155122 freeb(ahstack->ah_ip_unbind); 5263448Sdh155122 ahstack->ah_ip_unbind = NULL; 5270Sstevel@tonic-gate } 5280Sstevel@tonic-gate q->q_ptr = NULL; 5290Sstevel@tonic-gate qprocsoff(q); 5303448Sdh155122 netstack_rele(ahstack->ipsecah_netstack); 5310Sstevel@tonic-gate return (ENOMEM); 5320Sstevel@tonic-gate } 5330Sstevel@tonic-gate 5343448Sdh155122 ahstack->ah_ip_unbind->b_datap->db_type = M_PROTO; 5353448Sdh155122 tur = (struct T_unbind_req *)ahstack->ah_ip_unbind->b_rptr; 5360Sstevel@tonic-gate tur->PRIM_type = T_UNBIND_REQ; 5370Sstevel@tonic-gate } else { 5380Sstevel@tonic-gate qprocson(q); 5390Sstevel@tonic-gate } 5400Sstevel@tonic-gate 5410Sstevel@tonic-gate /* 5420Sstevel@tonic-gate * For now, there's not much I can do. I'll be getting a message 5430Sstevel@tonic-gate * passed down to me from keysock (in my wput), and a T_BIND_ACK 5440Sstevel@tonic-gate * up from IP (in my rput). 5450Sstevel@tonic-gate */ 5460Sstevel@tonic-gate 5470Sstevel@tonic-gate return (0); 5480Sstevel@tonic-gate } 5490Sstevel@tonic-gate 5500Sstevel@tonic-gate /* 5510Sstevel@tonic-gate * AH module close routine. 5520Sstevel@tonic-gate */ 5530Sstevel@tonic-gate static int 5540Sstevel@tonic-gate ipsecah_close(queue_t *q) 5550Sstevel@tonic-gate { 5563448Sdh155122 ipsecah_stack_t *ahstack = (ipsecah_stack_t *)q->q_ptr; 5573448Sdh155122 5580Sstevel@tonic-gate /* 5590Sstevel@tonic-gate * If ah_sadb.s_ip_q is attached to this instance, send a 5600Sstevel@tonic-gate * T_UNBIND_REQ to IP for the instance before doing 5610Sstevel@tonic-gate * a qprocsoff(). 5620Sstevel@tonic-gate */ 5633448Sdh155122 if (WR(q) == ahstack->ah_sadb.s_ip_q && 5643448Sdh155122 ahstack->ah_ip_unbind != NULL) { 5653448Sdh155122 putnext(WR(q), ahstack->ah_ip_unbind); 5663448Sdh155122 ahstack->ah_ip_unbind = NULL; 5670Sstevel@tonic-gate } 5680Sstevel@tonic-gate 5690Sstevel@tonic-gate /* 5700Sstevel@tonic-gate * Clean up q_ptr, if needed. 5710Sstevel@tonic-gate */ 5720Sstevel@tonic-gate qprocsoff(q); 5730Sstevel@tonic-gate 5740Sstevel@tonic-gate /* Keysock queue check is safe, because of OCEXCL perimeter. */ 5750Sstevel@tonic-gate 5763448Sdh155122 if (q == ahstack->ah_pfkey_q) { 5773448Sdh155122 ah1dbg(ahstack, 5783448Sdh155122 ("ipsecah_close: Ummm... keysock is closing AH.\n")); 5793448Sdh155122 ahstack->ah_pfkey_q = NULL; 5800Sstevel@tonic-gate /* Detach qtimeouts. */ 5813448Sdh155122 (void) quntimeout(q, ahstack->ah_event); 5820Sstevel@tonic-gate } 5830Sstevel@tonic-gate 5843448Sdh155122 if (WR(q) == ahstack->ah_sadb.s_ip_q) { 5850Sstevel@tonic-gate /* 5860Sstevel@tonic-gate * If the ah_sadb.s_ip_q is attached to this instance, find 5870Sstevel@tonic-gate * another. The OCEXCL outer perimeter helps us here. 5880Sstevel@tonic-gate */ 5890Sstevel@tonic-gate 5903448Sdh155122 ahstack->ah_sadb.s_ip_q = NULL; 5910Sstevel@tonic-gate 5920Sstevel@tonic-gate /* 5930Sstevel@tonic-gate * Find a replacement queue for ah_sadb.s_ip_q. 5940Sstevel@tonic-gate */ 5953448Sdh155122 if (ahstack->ah_pfkey_q != NULL && 5963448Sdh155122 ahstack->ah_pfkey_q != RD(q)) { 5970Sstevel@tonic-gate /* 5980Sstevel@tonic-gate * See if we can use the pfkey_q. 5990Sstevel@tonic-gate */ 6003448Sdh155122 ahstack->ah_sadb.s_ip_q = WR(ahstack->ah_pfkey_q); 6010Sstevel@tonic-gate } 6020Sstevel@tonic-gate 6033448Sdh155122 if (ahstack->ah_sadb.s_ip_q == NULL || 6043448Sdh155122 !sadb_t_bind_req(ahstack->ah_sadb.s_ip_q, IPPROTO_AH)) { 6053448Sdh155122 ah1dbg(ahstack, 6063448Sdh155122 ("ipsecah: Can't reassign ah_sadb.s_ip_q.\n")); 6073448Sdh155122 ahstack->ah_sadb.s_ip_q = NULL; 6080Sstevel@tonic-gate } else { 6093448Sdh155122 ahstack->ah_ip_unbind = 6103448Sdh155122 allocb(sizeof (struct T_unbind_req), BPRI_HI); 6113448Sdh155122 6123448Sdh155122 if (ahstack->ah_ip_unbind != NULL) { 6130Sstevel@tonic-gate struct T_unbind_req *tur; 6140Sstevel@tonic-gate 6153448Sdh155122 ahstack->ah_ip_unbind->b_datap->db_type = 6163448Sdh155122 M_PROTO; 6170Sstevel@tonic-gate tur = (struct T_unbind_req *) 6183448Sdh155122 ahstack->ah_ip_unbind->b_rptr; 6190Sstevel@tonic-gate tur->PRIM_type = T_UNBIND_REQ; 6200Sstevel@tonic-gate } 6210Sstevel@tonic-gate /* If it's NULL, I can't do much here. */ 6220Sstevel@tonic-gate } 6230Sstevel@tonic-gate } 6240Sstevel@tonic-gate 6253448Sdh155122 netstack_rele(ahstack->ipsecah_netstack); 6260Sstevel@tonic-gate return (0); 6270Sstevel@tonic-gate } 6280Sstevel@tonic-gate 6290Sstevel@tonic-gate /* 6300Sstevel@tonic-gate * AH module read put routine. 6310Sstevel@tonic-gate */ 6320Sstevel@tonic-gate /* ARGSUSED */ 6330Sstevel@tonic-gate static void 6340Sstevel@tonic-gate ipsecah_rput(queue_t *q, mblk_t *mp) 6350Sstevel@tonic-gate { 6363448Sdh155122 ipsecah_stack_t *ahstack = (ipsecah_stack_t *)q->q_ptr; 6373448Sdh155122 6383055Sdanmcd ASSERT(mp->b_datap->db_type != M_CTL); /* No more IRE_DB_REQ. */ 6393448Sdh155122 6400Sstevel@tonic-gate switch (mp->b_datap->db_type) { 6410Sstevel@tonic-gate case M_PROTO: 6420Sstevel@tonic-gate case M_PCPROTO: 6430Sstevel@tonic-gate /* TPI message of some sort. */ 6440Sstevel@tonic-gate switch (*((t_scalar_t *)mp->b_rptr)) { 6450Sstevel@tonic-gate case T_BIND_ACK: 6460Sstevel@tonic-gate /* We expect this. */ 6473448Sdh155122 ah3dbg(ahstack, 6483448Sdh155122 ("Thank you IP from AH for T_BIND_ACK\n")); 6490Sstevel@tonic-gate break; 6500Sstevel@tonic-gate case T_ERROR_ACK: 6510Sstevel@tonic-gate cmn_err(CE_WARN, 6520Sstevel@tonic-gate "ipsecah: AH received T_ERROR_ACK from IP."); 6530Sstevel@tonic-gate break; 6540Sstevel@tonic-gate case T_OK_ACK: 6550Sstevel@tonic-gate /* Probably from a (rarely sent) T_UNBIND_REQ. */ 6560Sstevel@tonic-gate break; 6570Sstevel@tonic-gate default: 6583448Sdh155122 ah1dbg(ahstack, ("Unknown M_{,PC}PROTO message.\n")); 6590Sstevel@tonic-gate } 6600Sstevel@tonic-gate freemsg(mp); 6610Sstevel@tonic-gate break; 6620Sstevel@tonic-gate default: 6630Sstevel@tonic-gate /* For now, passthru message. */ 6643448Sdh155122 ah2dbg(ahstack, ("AH got unknown mblk type %d.\n", 6650Sstevel@tonic-gate mp->b_datap->db_type)); 6660Sstevel@tonic-gate putnext(q, mp); 6670Sstevel@tonic-gate } 6680Sstevel@tonic-gate } 6690Sstevel@tonic-gate 6700Sstevel@tonic-gate /* 6710Sstevel@tonic-gate * Construct an SADB_REGISTER message with the current algorithms. 6720Sstevel@tonic-gate */ 6730Sstevel@tonic-gate static boolean_t 6743448Sdh155122 ah_register_out(uint32_t sequence, uint32_t pid, uint_t serial, 6753448Sdh155122 ipsecah_stack_t *ahstack) 6760Sstevel@tonic-gate { 6770Sstevel@tonic-gate mblk_t *mp; 6780Sstevel@tonic-gate boolean_t rc = B_TRUE; 6790Sstevel@tonic-gate sadb_msg_t *samsg; 6800Sstevel@tonic-gate sadb_supported_t *sasupp; 6810Sstevel@tonic-gate sadb_alg_t *saalg; 6820Sstevel@tonic-gate uint_t allocsize = sizeof (*samsg); 6830Sstevel@tonic-gate uint_t i, numalgs_snap; 6840Sstevel@tonic-gate ipsec_alginfo_t **authalgs; 6850Sstevel@tonic-gate uint_t num_aalgs; 6863448Sdh155122 ipsec_stack_t *ipss = ahstack->ipsecah_netstack->netstack_ipsec; 6870Sstevel@tonic-gate 6880Sstevel@tonic-gate /* Allocate the KEYSOCK_OUT. */ 6890Sstevel@tonic-gate mp = sadb_keysock_out(serial); 6900Sstevel@tonic-gate if (mp == NULL) { 6910Sstevel@tonic-gate ah0dbg(("ah_register_out: couldn't allocate mblk.\n")); 6920Sstevel@tonic-gate return (B_FALSE); 6930Sstevel@tonic-gate } 6940Sstevel@tonic-gate 6950Sstevel@tonic-gate /* 6960Sstevel@tonic-gate * Allocate the PF_KEY message that follows KEYSOCK_OUT. 6970Sstevel@tonic-gate * The alg reader lock needs to be held while allocating 6980Sstevel@tonic-gate * the variable part (i.e. the algorithms) of the message. 6990Sstevel@tonic-gate */ 7000Sstevel@tonic-gate 7013448Sdh155122 mutex_enter(&ipss->ipsec_alg_lock); 7020Sstevel@tonic-gate 7030Sstevel@tonic-gate /* 7040Sstevel@tonic-gate * Return only valid algorithms, so the number of algorithms 7050Sstevel@tonic-gate * to send up may be less than the number of algorithm entries 7060Sstevel@tonic-gate * in the table. 7070Sstevel@tonic-gate */ 7083448Sdh155122 authalgs = ipss->ipsec_alglists[IPSEC_ALG_AUTH]; 7090Sstevel@tonic-gate for (num_aalgs = 0, i = 0; i < IPSEC_MAX_ALGS; i++) 7100Sstevel@tonic-gate if (authalgs[i] != NULL && ALG_VALID(authalgs[i])) 7110Sstevel@tonic-gate num_aalgs++; 7120Sstevel@tonic-gate 7130Sstevel@tonic-gate /* 7140Sstevel@tonic-gate * Fill SADB_REGISTER message's algorithm descriptors. Hold 7150Sstevel@tonic-gate * down the lock while filling it. 7160Sstevel@tonic-gate */ 7170Sstevel@tonic-gate if (num_aalgs != 0) { 7180Sstevel@tonic-gate allocsize += (num_aalgs * sizeof (*saalg)); 7190Sstevel@tonic-gate allocsize += sizeof (*sasupp); 7200Sstevel@tonic-gate } 7210Sstevel@tonic-gate mp->b_cont = allocb(allocsize, BPRI_HI); 7220Sstevel@tonic-gate if (mp->b_cont == NULL) { 7233448Sdh155122 mutex_exit(&ipss->ipsec_alg_lock); 7240Sstevel@tonic-gate freemsg(mp); 7250Sstevel@tonic-gate return (B_FALSE); 7260Sstevel@tonic-gate } 7270Sstevel@tonic-gate 7280Sstevel@tonic-gate mp->b_cont->b_wptr += allocsize; 7290Sstevel@tonic-gate if (num_aalgs != 0) { 7300Sstevel@tonic-gate 7310Sstevel@tonic-gate saalg = (sadb_alg_t *)(mp->b_cont->b_rptr + sizeof (*samsg) + 7320Sstevel@tonic-gate sizeof (*sasupp)); 7330Sstevel@tonic-gate ASSERT(((ulong_t)saalg & 0x7) == 0); 7340Sstevel@tonic-gate 7350Sstevel@tonic-gate numalgs_snap = 0; 7360Sstevel@tonic-gate for (i = 0; 7373448Sdh155122 ((i < IPSEC_MAX_ALGS) && (numalgs_snap < num_aalgs)); 7383448Sdh155122 i++) { 7390Sstevel@tonic-gate if (authalgs[i] == NULL || !ALG_VALID(authalgs[i])) 7400Sstevel@tonic-gate continue; 7410Sstevel@tonic-gate 7420Sstevel@tonic-gate saalg->sadb_alg_id = authalgs[i]->alg_id; 7430Sstevel@tonic-gate saalg->sadb_alg_ivlen = 0; 7440Sstevel@tonic-gate saalg->sadb_alg_minbits = authalgs[i]->alg_ef_minbits; 7450Sstevel@tonic-gate saalg->sadb_alg_maxbits = authalgs[i]->alg_ef_maxbits; 7460Sstevel@tonic-gate saalg->sadb_x_alg_increment = 7470Sstevel@tonic-gate authalgs[i]->alg_increment; 7483448Sdh155122 saalg->sadb_x_alg_defincr = 7493448Sdh155122 authalgs[i]->alg_ef_default; 7500Sstevel@tonic-gate numalgs_snap++; 7510Sstevel@tonic-gate saalg++; 7520Sstevel@tonic-gate } 7530Sstevel@tonic-gate ASSERT(numalgs_snap == num_aalgs); 7540Sstevel@tonic-gate #ifdef DEBUG 7550Sstevel@tonic-gate /* 7560Sstevel@tonic-gate * Reality check to make sure I snagged all of the 7570Sstevel@tonic-gate * algorithms. 7580Sstevel@tonic-gate */ 7590Sstevel@tonic-gate for (; i < IPSEC_MAX_ALGS; i++) 7600Sstevel@tonic-gate if (authalgs[i] != NULL && ALG_VALID(authalgs[i])) 7610Sstevel@tonic-gate cmn_err(CE_PANIC, 7620Sstevel@tonic-gate "ah_register_out()! Missed #%d.\n", i); 7630Sstevel@tonic-gate #endif /* DEBUG */ 7640Sstevel@tonic-gate } 7650Sstevel@tonic-gate 7663448Sdh155122 mutex_exit(&ipss->ipsec_alg_lock); 7670Sstevel@tonic-gate 7680Sstevel@tonic-gate /* Now fill the restof the SADB_REGISTER message. */ 7690Sstevel@tonic-gate 7700Sstevel@tonic-gate samsg = (sadb_msg_t *)mp->b_cont->b_rptr; 7710Sstevel@tonic-gate samsg->sadb_msg_version = PF_KEY_V2; 7720Sstevel@tonic-gate samsg->sadb_msg_type = SADB_REGISTER; 7730Sstevel@tonic-gate samsg->sadb_msg_errno = 0; 7740Sstevel@tonic-gate samsg->sadb_msg_satype = SADB_SATYPE_AH; 7750Sstevel@tonic-gate samsg->sadb_msg_len = SADB_8TO64(allocsize); 7760Sstevel@tonic-gate samsg->sadb_msg_reserved = 0; 7770Sstevel@tonic-gate /* 7780Sstevel@tonic-gate * Assume caller has sufficient sequence/pid number info. If it's one 7790Sstevel@tonic-gate * from me over a new alg., I could give two hoots about sequence. 7800Sstevel@tonic-gate */ 7810Sstevel@tonic-gate samsg->sadb_msg_seq = sequence; 7820Sstevel@tonic-gate samsg->sadb_msg_pid = pid; 7830Sstevel@tonic-gate 7840Sstevel@tonic-gate if (allocsize > sizeof (*samsg)) { 7850Sstevel@tonic-gate sasupp = (sadb_supported_t *)(samsg + 1); 7860Sstevel@tonic-gate sasupp->sadb_supported_len = 7870Sstevel@tonic-gate SADB_8TO64(allocsize - sizeof (sadb_msg_t)); 7880Sstevel@tonic-gate sasupp->sadb_supported_exttype = SADB_EXT_SUPPORTED_AUTH; 7890Sstevel@tonic-gate sasupp->sadb_supported_reserved = 0; 7900Sstevel@tonic-gate } 7910Sstevel@tonic-gate 7923448Sdh155122 if (ahstack->ah_pfkey_q != NULL) 7933448Sdh155122 putnext(ahstack->ah_pfkey_q, mp); 7940Sstevel@tonic-gate else { 7950Sstevel@tonic-gate rc = B_FALSE; 7960Sstevel@tonic-gate freemsg(mp); 7970Sstevel@tonic-gate } 7980Sstevel@tonic-gate 7990Sstevel@tonic-gate return (rc); 8000Sstevel@tonic-gate } 8010Sstevel@tonic-gate 8020Sstevel@tonic-gate /* 8030Sstevel@tonic-gate * Invoked when the algorithm table changes. Causes SADB_REGISTER 8040Sstevel@tonic-gate * messages continaining the current list of algorithms to be 8050Sstevel@tonic-gate * sent up to the AH listeners. 8060Sstevel@tonic-gate */ 8070Sstevel@tonic-gate void 8083448Sdh155122 ipsecah_algs_changed(netstack_t *ns) 8090Sstevel@tonic-gate { 8103448Sdh155122 ipsecah_stack_t *ahstack = ns->netstack_ipsecah; 8113448Sdh155122 8120Sstevel@tonic-gate /* 8130Sstevel@tonic-gate * Time to send a PF_KEY SADB_REGISTER message to AH listeners 8140Sstevel@tonic-gate * everywhere. (The function itself checks for NULL ah_pfkey_q.) 8150Sstevel@tonic-gate */ 8163448Sdh155122 (void) ah_register_out(0, 0, 0, ahstack); 8170Sstevel@tonic-gate } 8180Sstevel@tonic-gate 8190Sstevel@tonic-gate /* 8200Sstevel@tonic-gate * Stub function that taskq_dispatch() invokes to take the mblk (in arg) 8210Sstevel@tonic-gate * and put() it into AH and STREAMS again. 8220Sstevel@tonic-gate */ 8230Sstevel@tonic-gate static void 8240Sstevel@tonic-gate inbound_task(void *arg) 8250Sstevel@tonic-gate { 8260Sstevel@tonic-gate ah_t *ah; 8270Sstevel@tonic-gate mblk_t *mp = (mblk_t *)arg; 8280Sstevel@tonic-gate ipsec_in_t *ii = (ipsec_in_t *)mp->b_rptr; 8290Sstevel@tonic-gate int ipsec_rc; 8303448Sdh155122 netstack_t *ns = ii->ipsec_in_ns; 8313448Sdh155122 ipsecah_stack_t *ahstack = ns->netstack_ipsecah; 8323448Sdh155122 8333448Sdh155122 ah2dbg(ahstack, ("in AH inbound_task")); 8343448Sdh155122 8353448Sdh155122 ASSERT(ahstack != NULL); 8363448Sdh155122 ah = ipsec_inbound_ah_sa(mp, ns); 8370Sstevel@tonic-gate if (ah == NULL) 8380Sstevel@tonic-gate return; 8390Sstevel@tonic-gate ASSERT(ii->ipsec_in_ah_sa != NULL); 8400Sstevel@tonic-gate ipsec_rc = ii->ipsec_in_ah_sa->ipsa_input_func(mp, ah); 8410Sstevel@tonic-gate if (ipsec_rc != IPSEC_STATUS_SUCCESS) 8420Sstevel@tonic-gate return; 8430Sstevel@tonic-gate ip_fanout_proto_again(mp, NULL, NULL, NULL); 8440Sstevel@tonic-gate } 8450Sstevel@tonic-gate 8460Sstevel@tonic-gate 8470Sstevel@tonic-gate /* 8480Sstevel@tonic-gate * Now that weak-key passed, actually ADD the security association, and 8490Sstevel@tonic-gate * send back a reply ADD message. 8500Sstevel@tonic-gate */ 8510Sstevel@tonic-gate static int 8523055Sdanmcd ah_add_sa_finish(mblk_t *mp, sadb_msg_t *samsg, keysock_in_t *ksi, 8533448Sdh155122 int *diagnostic, ipsecah_stack_t *ahstack) 8540Sstevel@tonic-gate { 855691Ssommerfe isaf_t *primary, *secondary, *inbound, *outbound; 8560Sstevel@tonic-gate sadb_sa_t *assoc = (sadb_sa_t *)ksi->ks_in_extv[SADB_EXT_SA]; 8570Sstevel@tonic-gate sadb_address_t *dstext = 8580Sstevel@tonic-gate (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_DST]; 8590Sstevel@tonic-gate struct sockaddr_in *dst; 8600Sstevel@tonic-gate struct sockaddr_in6 *dst6; 8610Sstevel@tonic-gate boolean_t is_ipv4, clone = B_FALSE, is_inbound = B_FALSE; 8620Sstevel@tonic-gate uint32_t *dstaddr; 8630Sstevel@tonic-gate ipsa_t *larval; 8640Sstevel@tonic-gate ipsacq_t *acqrec; 8650Sstevel@tonic-gate iacqf_t *acq_bucket; 8660Sstevel@tonic-gate mblk_t *acq_msgs = NULL; 8670Sstevel@tonic-gate mblk_t *lpkt; 8680Sstevel@tonic-gate int rc; 8690Sstevel@tonic-gate sadb_t *sp; 8700Sstevel@tonic-gate int outhash; 8713448Sdh155122 netstack_t *ns = ahstack->ipsecah_netstack; 8723448Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 8730Sstevel@tonic-gate 8740Sstevel@tonic-gate /* 8750Sstevel@tonic-gate * Locate the appropriate table(s). 8760Sstevel@tonic-gate */ 8770Sstevel@tonic-gate 8780Sstevel@tonic-gate dst = (struct sockaddr_in *)(dstext + 1); 8790Sstevel@tonic-gate dst6 = (struct sockaddr_in6 *)dst; 8800Sstevel@tonic-gate is_ipv4 = (dst->sin_family == AF_INET); 8810Sstevel@tonic-gate if (is_ipv4) { 8823448Sdh155122 sp = &ahstack->ah_sadb.s_v4; 8830Sstevel@tonic-gate dstaddr = (uint32_t *)(&dst->sin_addr); 884564Ssommerfe outhash = OUTBOUND_HASH_V4(sp, *(ipaddr_t *)dstaddr); 8850Sstevel@tonic-gate } else { 8860Sstevel@tonic-gate ASSERT(dst->sin_family == AF_INET6); 8873448Sdh155122 sp = &ahstack->ah_sadb.s_v6; 8880Sstevel@tonic-gate dstaddr = (uint32_t *)(&dst6->sin6_addr); 889564Ssommerfe outhash = OUTBOUND_HASH_V6(sp, *(in6_addr_t *)dstaddr); 8900Sstevel@tonic-gate } 8910Sstevel@tonic-gate 892564Ssommerfe inbound = INBOUND_BUCKET(sp, assoc->sadb_sa_spi); 893691Ssommerfe outbound = &sp->sdb_of[outhash]; 8940Sstevel@tonic-gate 8950Sstevel@tonic-gate switch (ksi->ks_in_dsttype) { 8960Sstevel@tonic-gate case KS_IN_ADDR_MBCAST: 8970Sstevel@tonic-gate clone = B_TRUE; /* All mcast SAs can be bidirectional */ 8980Sstevel@tonic-gate /* FALLTHRU */ 8990Sstevel@tonic-gate case KS_IN_ADDR_ME: 9000Sstevel@tonic-gate primary = inbound; 901691Ssommerfe secondary = outbound; 9020Sstevel@tonic-gate /* 9030Sstevel@tonic-gate * If the source address is either one of mine, or unspecified 9040Sstevel@tonic-gate * (which is best summed up by saying "not 'not mine'"), 9050Sstevel@tonic-gate * then the association is potentially bi-directional, 9060Sstevel@tonic-gate * in that it can be used for inbound traffic and outbound 9070Sstevel@tonic-gate * traffic. The best example of such and SA is a multicast 9080Sstevel@tonic-gate * SA (which allows me to receive the outbound traffic). 9090Sstevel@tonic-gate */ 9100Sstevel@tonic-gate if (ksi->ks_in_srctype != KS_IN_ADDR_NOTME) 9110Sstevel@tonic-gate clone = B_TRUE; 9120Sstevel@tonic-gate is_inbound = B_TRUE; 9130Sstevel@tonic-gate break; 9140Sstevel@tonic-gate case KS_IN_ADDR_NOTME: 915691Ssommerfe primary = outbound; 916564Ssommerfe secondary = inbound; 9170Sstevel@tonic-gate /* 9180Sstevel@tonic-gate * If the source address literally not mine (either 9190Sstevel@tonic-gate * unspecified or not mine), then this SA may have an 9200Sstevel@tonic-gate * address that WILL be mine after some configuration. 9210Sstevel@tonic-gate * We pay the price for this by making it a bi-directional 9220Sstevel@tonic-gate * SA. 9230Sstevel@tonic-gate */ 9240Sstevel@tonic-gate if (ksi->ks_in_srctype != KS_IN_ADDR_ME) 9250Sstevel@tonic-gate clone = B_TRUE; 9260Sstevel@tonic-gate break; 9270Sstevel@tonic-gate default: 9283055Sdanmcd *diagnostic = SADB_X_DIAGNOSTIC_BAD_DST; 9290Sstevel@tonic-gate return (EINVAL); 9300Sstevel@tonic-gate } 9310Sstevel@tonic-gate 9320Sstevel@tonic-gate /* 9330Sstevel@tonic-gate * Find a ACQUIRE list entry if possible. If we've added an SA that 9340Sstevel@tonic-gate * suits the needs of an ACQUIRE list entry, we can eliminate the 9350Sstevel@tonic-gate * ACQUIRE list entry and transmit the enqueued packets. Use the 9360Sstevel@tonic-gate * high-bit of the sequence number to queue it. Key off destination 9370Sstevel@tonic-gate * addr, and change acqrec's state. 9380Sstevel@tonic-gate */ 9390Sstevel@tonic-gate 9400Sstevel@tonic-gate if (samsg->sadb_msg_seq & IACQF_LOWEST_SEQ) { 9410Sstevel@tonic-gate acq_bucket = &sp->sdb_acq[outhash]; 9420Sstevel@tonic-gate mutex_enter(&acq_bucket->iacqf_lock); 9430Sstevel@tonic-gate for (acqrec = acq_bucket->iacqf_ipsacq; acqrec != NULL; 9440Sstevel@tonic-gate acqrec = acqrec->ipsacq_next) { 9450Sstevel@tonic-gate mutex_enter(&acqrec->ipsacq_lock); 9460Sstevel@tonic-gate /* 9470Sstevel@tonic-gate * Q: I only check sequence. Should I check dst? 9480Sstevel@tonic-gate * A: Yes, check dest because those are the packets 9490Sstevel@tonic-gate * that are queued up. 9500Sstevel@tonic-gate */ 9510Sstevel@tonic-gate if (acqrec->ipsacq_seq == samsg->sadb_msg_seq && 9520Sstevel@tonic-gate IPSA_ARE_ADDR_EQUAL(dstaddr, 953*4987Sdanmcd acqrec->ipsacq_dstaddr, acqrec->ipsacq_addrfam)) 9540Sstevel@tonic-gate break; 9550Sstevel@tonic-gate mutex_exit(&acqrec->ipsacq_lock); 9560Sstevel@tonic-gate } 9570Sstevel@tonic-gate if (acqrec != NULL) { 9580Sstevel@tonic-gate /* 9590Sstevel@tonic-gate * AHA! I found an ACQUIRE record for this SA. 9600Sstevel@tonic-gate * Grab the msg list, and free the acquire record. 9610Sstevel@tonic-gate * I already am holding the lock for this record, 9620Sstevel@tonic-gate * so all I have to do is free it. 9630Sstevel@tonic-gate */ 9640Sstevel@tonic-gate acq_msgs = acqrec->ipsacq_mp; 9650Sstevel@tonic-gate acqrec->ipsacq_mp = NULL; 9660Sstevel@tonic-gate mutex_exit(&acqrec->ipsacq_lock); 9673448Sdh155122 sadb_destroy_acquire(acqrec, ns); 9680Sstevel@tonic-gate } 9690Sstevel@tonic-gate mutex_exit(&acq_bucket->iacqf_lock); 9700Sstevel@tonic-gate } 9710Sstevel@tonic-gate 9720Sstevel@tonic-gate /* 9730Sstevel@tonic-gate * Find PF_KEY message, and see if I'm an update. If so, find entry 9740Sstevel@tonic-gate * in larval list (if there). 9750Sstevel@tonic-gate */ 9760Sstevel@tonic-gate 9770Sstevel@tonic-gate larval = NULL; 9780Sstevel@tonic-gate 9790Sstevel@tonic-gate if (samsg->sadb_msg_type == SADB_UPDATE) { 9800Sstevel@tonic-gate mutex_enter(&inbound->isaf_lock); 9810Sstevel@tonic-gate larval = ipsec_getassocbyspi(inbound, assoc->sadb_sa_spi, 9820Sstevel@tonic-gate ALL_ZEROES_PTR, dstaddr, dst->sin_family); 9830Sstevel@tonic-gate mutex_exit(&inbound->isaf_lock); 9840Sstevel@tonic-gate 9850Sstevel@tonic-gate if ((larval == NULL) || 9860Sstevel@tonic-gate (larval->ipsa_state != IPSA_STATE_LARVAL)) { 9870Sstevel@tonic-gate ah0dbg(("Larval update, but larval disappeared.\n")); 9880Sstevel@tonic-gate return (ESRCH); 9890Sstevel@tonic-gate } /* Else sadb_common_add unlinks it for me! */ 9900Sstevel@tonic-gate } 9910Sstevel@tonic-gate 9920Sstevel@tonic-gate lpkt = NULL; 9930Sstevel@tonic-gate if (larval != NULL) 9940Sstevel@tonic-gate lpkt = sadb_clear_lpkt(larval); 9950Sstevel@tonic-gate 9963448Sdh155122 rc = sadb_common_add(ahstack->ah_sadb.s_ip_q, ahstack->ah_pfkey_q, mp, 9973448Sdh155122 samsg, ksi, primary, secondary, larval, clone, is_inbound, 9983448Sdh155122 diagnostic, ns); 9990Sstevel@tonic-gate 10000Sstevel@tonic-gate /* 10010Sstevel@tonic-gate * How much more stack will I create with all of these 10020Sstevel@tonic-gate * ah_inbound_* and ah_outbound_*() calls? 10030Sstevel@tonic-gate */ 10040Sstevel@tonic-gate 10050Sstevel@tonic-gate 10060Sstevel@tonic-gate if (rc == 0 && lpkt != NULL) 10070Sstevel@tonic-gate rc = !taskq_dispatch(ah_taskq, inbound_task, 1008*4987Sdanmcd (void *) lpkt, TQ_NOSLEEP); 10090Sstevel@tonic-gate 10100Sstevel@tonic-gate if (rc != 0) { 10110Sstevel@tonic-gate ip_drop_packet(lpkt, B_TRUE, NULL, NULL, 10123448Sdh155122 DROPPER(ipss, ipds_sadb_inlarval_timeout), 10133448Sdh155122 &ahstack->ah_dropper); 10140Sstevel@tonic-gate } 10150Sstevel@tonic-gate 10160Sstevel@tonic-gate while (acq_msgs != NULL) { 10170Sstevel@tonic-gate mblk_t *mp = acq_msgs; 10180Sstevel@tonic-gate 10190Sstevel@tonic-gate acq_msgs = acq_msgs->b_next; 10200Sstevel@tonic-gate mp->b_next = NULL; 10210Sstevel@tonic-gate if (rc == 0) { 10220Sstevel@tonic-gate ipsec_out_t *io = (ipsec_out_t *)mp->b_rptr; 10230Sstevel@tonic-gate 10243448Sdh155122 ASSERT(ahstack->ah_sadb.s_ip_q != NULL); 10250Sstevel@tonic-gate if (ipsec_outbound_sa(mp, IPPROTO_AH)) { 10260Sstevel@tonic-gate io->ipsec_out_ah_done = B_TRUE; 10270Sstevel@tonic-gate if (ah_outbound(mp) == IPSEC_STATUS_SUCCESS) { 10280Sstevel@tonic-gate ipha_t *ipha = (ipha_t *) 10290Sstevel@tonic-gate mp->b_cont->b_rptr; 10300Sstevel@tonic-gate if (is_ipv4) { 10310Sstevel@tonic-gate ip_wput_ipsec_out(NULL, mp, 10320Sstevel@tonic-gate ipha, NULL, NULL); 10330Sstevel@tonic-gate } else { 10340Sstevel@tonic-gate ip6_t *ip6h = (ip6_t *)ipha; 10350Sstevel@tonic-gate ip_wput_ipsec_out_v6(NULL, 10360Sstevel@tonic-gate mp, ip6h, NULL, NULL); 10370Sstevel@tonic-gate } 10380Sstevel@tonic-gate } 10390Sstevel@tonic-gate continue; 10400Sstevel@tonic-gate } 10410Sstevel@tonic-gate } 10423448Sdh155122 AH_BUMP_STAT(ahstack, out_discards); 10430Sstevel@tonic-gate ip_drop_packet(mp, B_FALSE, NULL, NULL, 10443448Sdh155122 DROPPER(ipss, ipds_sadb_acquire_timeout), 10453448Sdh155122 &ahstack->ah_dropper); 10460Sstevel@tonic-gate } 10470Sstevel@tonic-gate 10480Sstevel@tonic-gate return (rc); 10490Sstevel@tonic-gate } 10500Sstevel@tonic-gate 10510Sstevel@tonic-gate /* 10520Sstevel@tonic-gate * Add new AH security association. This may become a generic AH/ESP 10530Sstevel@tonic-gate * routine eventually. 10540Sstevel@tonic-gate */ 10550Sstevel@tonic-gate static int 10563448Sdh155122 ah_add_sa(mblk_t *mp, keysock_in_t *ksi, int *diagnostic, netstack_t *ns) 10570Sstevel@tonic-gate { 10580Sstevel@tonic-gate sadb_sa_t *assoc = (sadb_sa_t *)ksi->ks_in_extv[SADB_EXT_SA]; 10590Sstevel@tonic-gate sadb_address_t *srcext = 10600Sstevel@tonic-gate (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_SRC]; 10610Sstevel@tonic-gate sadb_address_t *dstext = 10620Sstevel@tonic-gate (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_DST]; 10633055Sdanmcd sadb_address_t *isrcext = 10643055Sdanmcd (sadb_address_t *)ksi->ks_in_extv[SADB_X_EXT_ADDRESS_INNER_SRC]; 10653055Sdanmcd sadb_address_t *idstext = 10663055Sdanmcd (sadb_address_t *)ksi->ks_in_extv[SADB_X_EXT_ADDRESS_INNER_DST]; 10670Sstevel@tonic-gate sadb_key_t *key = (sadb_key_t *)ksi->ks_in_extv[SADB_EXT_KEY_AUTH]; 10680Sstevel@tonic-gate struct sockaddr_in *src, *dst; 10690Sstevel@tonic-gate /* We don't need sockaddr_in6 for now. */ 10700Sstevel@tonic-gate sadb_lifetime_t *soft = 10710Sstevel@tonic-gate (sadb_lifetime_t *)ksi->ks_in_extv[SADB_EXT_LIFETIME_SOFT]; 10720Sstevel@tonic-gate sadb_lifetime_t *hard = 10730Sstevel@tonic-gate (sadb_lifetime_t *)ksi->ks_in_extv[SADB_EXT_LIFETIME_HARD]; 10740Sstevel@tonic-gate ipsec_alginfo_t *aalg; 10753448Sdh155122 ipsecah_stack_t *ahstack = ns->netstack_ipsecah; 10763448Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 10770Sstevel@tonic-gate 10780Sstevel@tonic-gate /* I need certain extensions present for an ADD message. */ 10790Sstevel@tonic-gate if (srcext == NULL) { 10800Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_MISSING_SRC; 10810Sstevel@tonic-gate return (EINVAL); 10820Sstevel@tonic-gate } 10830Sstevel@tonic-gate if (dstext == NULL) { 10840Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_MISSING_DST; 10850Sstevel@tonic-gate return (EINVAL); 10860Sstevel@tonic-gate } 10873055Sdanmcd if (isrcext == NULL && idstext != NULL) { 10883055Sdanmcd *diagnostic = SADB_X_DIAGNOSTIC_MISSING_INNER_SRC; 10893055Sdanmcd return (EINVAL); 10903055Sdanmcd } 10913055Sdanmcd if (isrcext != NULL && idstext == NULL) { 10923055Sdanmcd *diagnostic = SADB_X_DIAGNOSTIC_MISSING_INNER_DST; 10933055Sdanmcd return (EINVAL); 10943055Sdanmcd } 10950Sstevel@tonic-gate if (assoc == NULL) { 10960Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_MISSING_SA; 10970Sstevel@tonic-gate return (EINVAL); 10980Sstevel@tonic-gate } 10990Sstevel@tonic-gate if (key == NULL) { 11000Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_MISSING_AKEY; 11010Sstevel@tonic-gate return (EINVAL); 11020Sstevel@tonic-gate } 11030Sstevel@tonic-gate 11040Sstevel@tonic-gate src = (struct sockaddr_in *)(srcext + 1); 11050Sstevel@tonic-gate dst = (struct sockaddr_in *)(dstext + 1); 11060Sstevel@tonic-gate 11070Sstevel@tonic-gate /* Sundry ADD-specific reality checks. */ 11080Sstevel@tonic-gate /* XXX STATS : Logging/stats here? */ 11090Sstevel@tonic-gate 11100Sstevel@tonic-gate if (assoc->sadb_sa_state != SADB_SASTATE_MATURE) { 11110Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_BAD_SASTATE; 11120Sstevel@tonic-gate return (EINVAL); 11130Sstevel@tonic-gate } 11140Sstevel@tonic-gate if (assoc->sadb_sa_encrypt != SADB_EALG_NONE) { 11150Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_ENCR_NOTSUPP; 11160Sstevel@tonic-gate return (EINVAL); 11170Sstevel@tonic-gate } 1118*4987Sdanmcd if (assoc->sadb_sa_flags & 1119*4987Sdanmcd ~(SADB_SAFLAGS_NOREPLAY | SADB_X_SAFLAGS_TUNNEL)) { 11200Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_BAD_SAFLAGS; 11210Sstevel@tonic-gate return (EINVAL); 11220Sstevel@tonic-gate } 11230Sstevel@tonic-gate 11240Sstevel@tonic-gate if ((*diagnostic = sadb_hardsoftchk(hard, soft)) != 0) 11250Sstevel@tonic-gate return (EINVAL); 11260Sstevel@tonic-gate 11273055Sdanmcd ASSERT(src->sin_family == dst->sin_family); 11280Sstevel@tonic-gate 11290Sstevel@tonic-gate /* Stuff I don't support, for now. XXX Diagnostic? */ 11300Sstevel@tonic-gate if (ksi->ks_in_extv[SADB_EXT_LIFETIME_CURRENT] != NULL || 11310Sstevel@tonic-gate ksi->ks_in_extv[SADB_EXT_SENSITIVITY] != NULL) 11320Sstevel@tonic-gate return (EOPNOTSUPP); 11330Sstevel@tonic-gate 11340Sstevel@tonic-gate /* 11350Sstevel@tonic-gate * XXX Policy : I'm not checking identities or sensitivity 11360Sstevel@tonic-gate * labels at this time, but if I did, I'd do them here, before I sent 11370Sstevel@tonic-gate * the weak key check up to the algorithm. 11380Sstevel@tonic-gate */ 11390Sstevel@tonic-gate 11400Sstevel@tonic-gate /* verify that there is a mapping for the specified algorithm */ 11413448Sdh155122 mutex_enter(&ipss->ipsec_alg_lock); 11423448Sdh155122 aalg = ipss->ipsec_alglists[IPSEC_ALG_AUTH][assoc->sadb_sa_auth]; 11430Sstevel@tonic-gate if (aalg == NULL || !ALG_VALID(aalg)) { 11443448Sdh155122 mutex_exit(&ipss->ipsec_alg_lock); 11453448Sdh155122 ah1dbg(ahstack, ("Couldn't find auth alg #%d.\n", 1146*4987Sdanmcd assoc->sadb_sa_auth)); 11470Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_BAD_AALG; 11480Sstevel@tonic-gate return (EINVAL); 11490Sstevel@tonic-gate } 11500Sstevel@tonic-gate ASSERT(aalg->alg_mech_type != CRYPTO_MECHANISM_INVALID); 11510Sstevel@tonic-gate 11520Sstevel@tonic-gate /* sanity check key sizes */ 11530Sstevel@tonic-gate if (!ipsec_valid_key_size(key->sadb_key_bits, aalg)) { 11543448Sdh155122 mutex_exit(&ipss->ipsec_alg_lock); 11550Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_BAD_AKEYBITS; 11560Sstevel@tonic-gate return (EINVAL); 11570Sstevel@tonic-gate } 11580Sstevel@tonic-gate 11590Sstevel@tonic-gate /* check key and fix parity if needed */ 11600Sstevel@tonic-gate if (ipsec_check_key(aalg->alg_mech_type, key, B_TRUE, 11610Sstevel@tonic-gate diagnostic) != 0) { 11623448Sdh155122 mutex_exit(&ipss->ipsec_alg_lock); 11630Sstevel@tonic-gate return (EINVAL); 11640Sstevel@tonic-gate } 11650Sstevel@tonic-gate 11663448Sdh155122 mutex_exit(&ipss->ipsec_alg_lock); 11670Sstevel@tonic-gate 11683055Sdanmcd return (ah_add_sa_finish(mp, (sadb_msg_t *)mp->b_cont->b_rptr, ksi, 1169*4987Sdanmcd diagnostic, ahstack)); 11700Sstevel@tonic-gate } 11710Sstevel@tonic-gate 11720Sstevel@tonic-gate /* 11730Sstevel@tonic-gate * Update a security association. Updates come in two varieties. The first 11740Sstevel@tonic-gate * is an update of lifetimes on a non-larval SA. The second is an update of 11750Sstevel@tonic-gate * a larval SA, which ends up looking a lot more like an add. 11760Sstevel@tonic-gate */ 11770Sstevel@tonic-gate static int 11783448Sdh155122 ah_update_sa(mblk_t *mp, keysock_in_t *ksi, int *diagnostic, 11793448Sdh155122 ipsecah_stack_t *ahstack) 11800Sstevel@tonic-gate { 11810Sstevel@tonic-gate sadb_address_t *dstext = 11820Sstevel@tonic-gate (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_DST]; 11830Sstevel@tonic-gate struct sockaddr_in *sin; 11840Sstevel@tonic-gate 11850Sstevel@tonic-gate if (dstext == NULL) { 11860Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_MISSING_DST; 11870Sstevel@tonic-gate return (EINVAL); 11880Sstevel@tonic-gate } 11890Sstevel@tonic-gate sin = (struct sockaddr_in *)(dstext + 1); 11900Sstevel@tonic-gate return (sadb_update_sa(mp, ksi, 1191*4987Sdanmcd (sin->sin_family == AF_INET6) ? &ahstack->ah_sadb.s_v6 : 1192*4987Sdanmcd &ahstack->ah_sadb.s_v4, diagnostic, ahstack->ah_pfkey_q, ah_add_sa, 1193*4987Sdanmcd ahstack->ipsecah_netstack)); 11940Sstevel@tonic-gate } 11950Sstevel@tonic-gate 11960Sstevel@tonic-gate /* 11970Sstevel@tonic-gate * Delete a security association. This is REALLY likely to be code common to 11980Sstevel@tonic-gate * both AH and ESP. Find the association, then unlink it. 11990Sstevel@tonic-gate */ 12000Sstevel@tonic-gate static int 12013448Sdh155122 ah_del_sa(mblk_t *mp, keysock_in_t *ksi, int *diagnostic, 12023448Sdh155122 ipsecah_stack_t *ahstack) 12030Sstevel@tonic-gate { 12040Sstevel@tonic-gate sadb_sa_t *assoc = (sadb_sa_t *)ksi->ks_in_extv[SADB_EXT_SA]; 12050Sstevel@tonic-gate sadb_address_t *dstext = 12060Sstevel@tonic-gate (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_DST]; 12070Sstevel@tonic-gate sadb_address_t *srcext = 12080Sstevel@tonic-gate (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_SRC]; 12090Sstevel@tonic-gate struct sockaddr_in *sin; 12100Sstevel@tonic-gate 12110Sstevel@tonic-gate if (assoc == NULL) { 12120Sstevel@tonic-gate if (dstext != NULL) 12130Sstevel@tonic-gate sin = (struct sockaddr_in *)(dstext + 1); 12140Sstevel@tonic-gate else if (srcext != NULL) 12150Sstevel@tonic-gate sin = (struct sockaddr_in *)(srcext + 1); 12160Sstevel@tonic-gate else { 12170Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_MISSING_SA; 12180Sstevel@tonic-gate return (EINVAL); 12190Sstevel@tonic-gate } 12203055Sdanmcd return (sadb_purge_sa(mp, ksi, 12213448Sdh155122 (sin->sin_family == AF_INET6) ? &ahstack->ah_sadb.s_v6 : 12223448Sdh155122 &ahstack->ah_sadb.s_v4, 12233448Sdh155122 ahstack->ah_pfkey_q, ahstack->ah_sadb.s_ip_q)); 12240Sstevel@tonic-gate } 12250Sstevel@tonic-gate 12263448Sdh155122 return (sadb_del_sa(mp, ksi, &ahstack->ah_sadb, diagnostic, 1227*4987Sdanmcd ahstack->ah_pfkey_q)); 12280Sstevel@tonic-gate } 12290Sstevel@tonic-gate 12300Sstevel@tonic-gate /* 12310Sstevel@tonic-gate * Convert the entire contents of all of AH's SA tables into PF_KEY SADB_DUMP 12320Sstevel@tonic-gate * messages. 12330Sstevel@tonic-gate */ 12340Sstevel@tonic-gate static void 12353448Sdh155122 ah_dump(mblk_t *mp, keysock_in_t *ksi, ipsecah_stack_t *ahstack) 12360Sstevel@tonic-gate { 12370Sstevel@tonic-gate int error; 12380Sstevel@tonic-gate sadb_msg_t *samsg; 12390Sstevel@tonic-gate 12400Sstevel@tonic-gate /* 12410Sstevel@tonic-gate * Dump each fanout, bailing if error is non-zero. 12420Sstevel@tonic-gate */ 12430Sstevel@tonic-gate 12443448Sdh155122 error = sadb_dump(ahstack->ah_pfkey_q, mp, ksi->ks_in_serial, 12453448Sdh155122 &ahstack->ah_sadb.s_v4); 12460Sstevel@tonic-gate if (error != 0) 12470Sstevel@tonic-gate goto bail; 12480Sstevel@tonic-gate 12493448Sdh155122 error = sadb_dump(ahstack->ah_pfkey_q, mp, ksi->ks_in_serial, 12503448Sdh155122 &ahstack->ah_sadb.s_v6); 12510Sstevel@tonic-gate bail: 12520Sstevel@tonic-gate ASSERT(mp->b_cont != NULL); 12530Sstevel@tonic-gate samsg = (sadb_msg_t *)mp->b_cont->b_rptr; 12540Sstevel@tonic-gate samsg->sadb_msg_errno = (uint8_t)error; 12553448Sdh155122 sadb_pfkey_echo(ahstack->ah_pfkey_q, mp, 12563448Sdh155122 (sadb_msg_t *)mp->b_cont->b_rptr, ksi, NULL); 12570Sstevel@tonic-gate } 12580Sstevel@tonic-gate 12590Sstevel@tonic-gate /* 12603055Sdanmcd * First-cut reality check for an inbound PF_KEY message. 12613055Sdanmcd */ 12623055Sdanmcd static boolean_t 12633448Sdh155122 ah_pfkey_reality_failures(mblk_t *mp, keysock_in_t *ksi, 12643448Sdh155122 ipsecah_stack_t *ahstack) 12653055Sdanmcd { 12663055Sdanmcd int diagnostic; 12673055Sdanmcd 12683055Sdanmcd if (mp->b_cont == NULL) { 12693055Sdanmcd freemsg(mp); 12703055Sdanmcd return (B_TRUE); 12713055Sdanmcd } 12723055Sdanmcd 12733055Sdanmcd if (ksi->ks_in_extv[SADB_EXT_KEY_ENCRYPT] != NULL) { 12743055Sdanmcd diagnostic = SADB_X_DIAGNOSTIC_EKEY_PRESENT; 12753055Sdanmcd goto badmsg; 12763055Sdanmcd } 12773055Sdanmcd if (ksi->ks_in_extv[SADB_EXT_PROPOSAL] != NULL) { 12783055Sdanmcd diagnostic = SADB_X_DIAGNOSTIC_PROP_PRESENT; 12793055Sdanmcd goto badmsg; 12803055Sdanmcd } 12813055Sdanmcd if (ksi->ks_in_extv[SADB_EXT_SUPPORTED_AUTH] != NULL || 12823055Sdanmcd ksi->ks_in_extv[SADB_EXT_SUPPORTED_ENCRYPT] != NULL) { 12833055Sdanmcd diagnostic = SADB_X_DIAGNOSTIC_SUPP_PRESENT; 12843055Sdanmcd goto badmsg; 12853055Sdanmcd } 12863055Sdanmcd return (B_FALSE); /* False ==> no failures */ 12873055Sdanmcd 12883055Sdanmcd badmsg: 12893448Sdh155122 sadb_pfkey_error(ahstack->ah_pfkey_q, mp, EINVAL, 12903448Sdh155122 diagnostic, ksi->ks_in_serial); 12913055Sdanmcd return (B_TRUE); /* True ==> failures */ 12923055Sdanmcd } 12933055Sdanmcd 12943055Sdanmcd /* 12950Sstevel@tonic-gate * AH parsing of PF_KEY messages. Keysock did most of the really silly 12960Sstevel@tonic-gate * error cases. What I receive is a fully-formed, syntactically legal 12970Sstevel@tonic-gate * PF_KEY message. I then need to check semantics... 12980Sstevel@tonic-gate * 12990Sstevel@tonic-gate * This code may become common to AH and ESP. Stay tuned. 13000Sstevel@tonic-gate * 13010Sstevel@tonic-gate * I also make the assumption that db_ref's are cool. If this assumption 13020Sstevel@tonic-gate * is wrong, this means that someone other than keysock or me has been 13030Sstevel@tonic-gate * mucking with PF_KEY messages. 13040Sstevel@tonic-gate */ 13050Sstevel@tonic-gate static void 13063448Sdh155122 ah_parse_pfkey(mblk_t *mp, ipsecah_stack_t *ahstack) 13070Sstevel@tonic-gate { 13080Sstevel@tonic-gate mblk_t *msg = mp->b_cont; 13090Sstevel@tonic-gate sadb_msg_t *samsg; 13100Sstevel@tonic-gate keysock_in_t *ksi; 13110Sstevel@tonic-gate int error; 13120Sstevel@tonic-gate int diagnostic = SADB_X_DIAGNOSTIC_NONE; 13130Sstevel@tonic-gate 13140Sstevel@tonic-gate ASSERT(msg != NULL); 13153448Sdh155122 13160Sstevel@tonic-gate samsg = (sadb_msg_t *)msg->b_rptr; 13170Sstevel@tonic-gate ksi = (keysock_in_t *)mp->b_rptr; 13180Sstevel@tonic-gate 13190Sstevel@tonic-gate /* 13200Sstevel@tonic-gate * If applicable, convert unspecified AF_INET6 to unspecified 13210Sstevel@tonic-gate * AF_INET. 13220Sstevel@tonic-gate */ 13233448Sdh155122 if (!sadb_addrfix(ksi, ahstack->ah_pfkey_q, mp, 13243448Sdh155122 ahstack->ipsecah_netstack) || 13253448Sdh155122 ah_pfkey_reality_failures(mp, ksi, ahstack)) { 13263055Sdanmcd return; 13273055Sdanmcd } 13280Sstevel@tonic-gate 13290Sstevel@tonic-gate switch (samsg->sadb_msg_type) { 13300Sstevel@tonic-gate case SADB_ADD: 13313448Sdh155122 error = ah_add_sa(mp, ksi, &diagnostic, 13323448Sdh155122 ahstack->ipsecah_netstack); 13330Sstevel@tonic-gate if (error != 0) { 13343448Sdh155122 sadb_pfkey_error(ahstack->ah_pfkey_q, mp, error, 13353448Sdh155122 diagnostic, ksi->ks_in_serial); 13360Sstevel@tonic-gate } 13370Sstevel@tonic-gate /* else ah_add_sa() took care of things. */ 13380Sstevel@tonic-gate break; 13390Sstevel@tonic-gate case SADB_DELETE: 13403448Sdh155122 error = ah_del_sa(mp, ksi, &diagnostic, ahstack); 13410Sstevel@tonic-gate if (error != 0) { 13423448Sdh155122 sadb_pfkey_error(ahstack->ah_pfkey_q, mp, error, 13433448Sdh155122 diagnostic, ksi->ks_in_serial); 13440Sstevel@tonic-gate } 13450Sstevel@tonic-gate /* Else ah_del_sa() took care of things. */ 13460Sstevel@tonic-gate break; 13470Sstevel@tonic-gate case SADB_GET: 13483448Sdh155122 error = sadb_get_sa(mp, ksi, &ahstack->ah_sadb, &diagnostic, 13493448Sdh155122 ahstack->ah_pfkey_q); 13500Sstevel@tonic-gate if (error != 0) { 13513448Sdh155122 sadb_pfkey_error(ahstack->ah_pfkey_q, mp, error, 13523448Sdh155122 diagnostic, ksi->ks_in_serial); 13530Sstevel@tonic-gate } 13540Sstevel@tonic-gate /* Else sadb_get_sa() took care of things. */ 13550Sstevel@tonic-gate break; 13560Sstevel@tonic-gate case SADB_FLUSH: 13573448Sdh155122 sadbp_flush(&ahstack->ah_sadb, ahstack->ipsecah_netstack); 13583448Sdh155122 sadb_pfkey_echo(ahstack->ah_pfkey_q, mp, samsg, ksi, NULL); 13590Sstevel@tonic-gate break; 13600Sstevel@tonic-gate case SADB_REGISTER: 13610Sstevel@tonic-gate /* 13620Sstevel@tonic-gate * Hmmm, let's do it! Check for extensions (there should 13630Sstevel@tonic-gate * be none), extract the fields, call ah_register_out(), 13640Sstevel@tonic-gate * then either free or report an error. 13650Sstevel@tonic-gate * 13660Sstevel@tonic-gate * Keysock takes care of the PF_KEY bookkeeping for this. 13670Sstevel@tonic-gate */ 13680Sstevel@tonic-gate if (ah_register_out(samsg->sadb_msg_seq, samsg->sadb_msg_pid, 13693448Sdh155122 ksi->ks_in_serial, ahstack)) { 13700Sstevel@tonic-gate freemsg(mp); 13710Sstevel@tonic-gate } else { 13720Sstevel@tonic-gate /* 13730Sstevel@tonic-gate * Only way this path hits is if there is a memory 13740Sstevel@tonic-gate * failure. It will not return B_FALSE because of 13750Sstevel@tonic-gate * lack of ah_pfkey_q if I am in wput(). 13760Sstevel@tonic-gate */ 13773448Sdh155122 sadb_pfkey_error(ahstack->ah_pfkey_q, mp, ENOMEM, 13783448Sdh155122 diagnostic, ksi->ks_in_serial); 13790Sstevel@tonic-gate } 13800Sstevel@tonic-gate break; 13810Sstevel@tonic-gate case SADB_UPDATE: 13820Sstevel@tonic-gate /* 13830Sstevel@tonic-gate * Find a larval, if not there, find a full one and get 13840Sstevel@tonic-gate * strict. 13850Sstevel@tonic-gate */ 13863448Sdh155122 error = ah_update_sa(mp, ksi, &diagnostic, ahstack); 13870Sstevel@tonic-gate if (error != 0) { 13883448Sdh155122 sadb_pfkey_error(ahstack->ah_pfkey_q, mp, error, 13893448Sdh155122 diagnostic, ksi->ks_in_serial); 13900Sstevel@tonic-gate } 13910Sstevel@tonic-gate /* else ah_update_sa() took care of things. */ 13920Sstevel@tonic-gate break; 13930Sstevel@tonic-gate case SADB_GETSPI: 13940Sstevel@tonic-gate /* 13950Sstevel@tonic-gate * Reserve a new larval entry. 13960Sstevel@tonic-gate */ 13973448Sdh155122 ah_getspi(mp, ksi, ahstack); 13980Sstevel@tonic-gate break; 13990Sstevel@tonic-gate case SADB_ACQUIRE: 14000Sstevel@tonic-gate /* 14010Sstevel@tonic-gate * Find larval and/or ACQUIRE record and kill it (them), I'm 14020Sstevel@tonic-gate * most likely an error. Inbound ACQUIRE messages should only 14030Sstevel@tonic-gate * have the base header. 14040Sstevel@tonic-gate */ 14053448Sdh155122 sadb_in_acquire(samsg, &ahstack->ah_sadb, ahstack->ah_pfkey_q, 14063448Sdh155122 ahstack->ipsecah_netstack); 14070Sstevel@tonic-gate freemsg(mp); 14080Sstevel@tonic-gate break; 14090Sstevel@tonic-gate case SADB_DUMP: 14100Sstevel@tonic-gate /* 14110Sstevel@tonic-gate * Dump all entries. 14120Sstevel@tonic-gate */ 14133448Sdh155122 ah_dump(mp, ksi, ahstack); 14140Sstevel@tonic-gate /* ah_dump will take care of the return message, etc. */ 14150Sstevel@tonic-gate break; 14160Sstevel@tonic-gate case SADB_EXPIRE: 14170Sstevel@tonic-gate /* Should never reach me. */ 14183448Sdh155122 sadb_pfkey_error(ahstack->ah_pfkey_q, mp, EOPNOTSUPP, 14193448Sdh155122 diagnostic, ksi->ks_in_serial); 14200Sstevel@tonic-gate break; 14210Sstevel@tonic-gate default: 14223448Sdh155122 sadb_pfkey_error(ahstack->ah_pfkey_q, mp, EINVAL, 14230Sstevel@tonic-gate SADB_X_DIAGNOSTIC_UNKNOWN_MSG, ksi->ks_in_serial); 14240Sstevel@tonic-gate break; 14250Sstevel@tonic-gate } 14260Sstevel@tonic-gate } 14270Sstevel@tonic-gate 14280Sstevel@tonic-gate /* 14290Sstevel@tonic-gate * Handle case where PF_KEY says it can't find a keysock for one of my 14300Sstevel@tonic-gate * ACQUIRE messages. 14310Sstevel@tonic-gate */ 14320Sstevel@tonic-gate static void 14333448Sdh155122 ah_keysock_no_socket(mblk_t *mp, ipsecah_stack_t *ahstack) 14340Sstevel@tonic-gate { 14350Sstevel@tonic-gate sadb_msg_t *samsg; 14360Sstevel@tonic-gate keysock_out_err_t *kse = (keysock_out_err_t *)mp->b_rptr; 14370Sstevel@tonic-gate 14380Sstevel@tonic-gate if (mp->b_cont == NULL) { 14390Sstevel@tonic-gate freemsg(mp); 14400Sstevel@tonic-gate return; 14410Sstevel@tonic-gate } 14420Sstevel@tonic-gate samsg = (sadb_msg_t *)mp->b_cont->b_rptr; 14430Sstevel@tonic-gate 14440Sstevel@tonic-gate /* 14450Sstevel@tonic-gate * If keysock can't find any registered, delete the acquire record 14460Sstevel@tonic-gate * immediately, and handle errors. 14470Sstevel@tonic-gate */ 14480Sstevel@tonic-gate if (samsg->sadb_msg_type == SADB_ACQUIRE) { 14490Sstevel@tonic-gate samsg->sadb_msg_errno = kse->ks_err_errno; 14500Sstevel@tonic-gate samsg->sadb_msg_len = SADB_8TO64(sizeof (*samsg)); 14510Sstevel@tonic-gate /* 14520Sstevel@tonic-gate * Use the write-side of the ah_pfkey_q, in case there is 14533448Sdh155122 * no ahstack->ah_sadb.s_ip_q. 14540Sstevel@tonic-gate */ 14553448Sdh155122 sadb_in_acquire(samsg, &ahstack->ah_sadb, 14563448Sdh155122 WR(ahstack->ah_pfkey_q), ahstack->ipsecah_netstack); 14570Sstevel@tonic-gate } 14580Sstevel@tonic-gate 14590Sstevel@tonic-gate freemsg(mp); 14600Sstevel@tonic-gate } 14610Sstevel@tonic-gate 14620Sstevel@tonic-gate /* 14630Sstevel@tonic-gate * AH module write put routine. 14640Sstevel@tonic-gate */ 14650Sstevel@tonic-gate static void 14660Sstevel@tonic-gate ipsecah_wput(queue_t *q, mblk_t *mp) 14670Sstevel@tonic-gate { 14680Sstevel@tonic-gate ipsec_info_t *ii; 14690Sstevel@tonic-gate struct iocblk *iocp; 14703448Sdh155122 ipsecah_stack_t *ahstack = (ipsecah_stack_t *)q->q_ptr; 14713448Sdh155122 14723448Sdh155122 ah3dbg(ahstack, ("In ah_wput().\n")); 14730Sstevel@tonic-gate 14740Sstevel@tonic-gate /* NOTE: Each case must take care of freeing or passing mp. */ 14750Sstevel@tonic-gate switch (mp->b_datap->db_type) { 14760Sstevel@tonic-gate case M_CTL: 14770Sstevel@tonic-gate if ((mp->b_wptr - mp->b_rptr) < sizeof (ipsec_info_t)) { 14780Sstevel@tonic-gate /* Not big enough message. */ 14790Sstevel@tonic-gate freemsg(mp); 14800Sstevel@tonic-gate break; 14810Sstevel@tonic-gate } 14820Sstevel@tonic-gate ii = (ipsec_info_t *)mp->b_rptr; 14830Sstevel@tonic-gate 14840Sstevel@tonic-gate switch (ii->ipsec_info_type) { 14850Sstevel@tonic-gate case KEYSOCK_OUT_ERR: 14863448Sdh155122 ah1dbg(ahstack, ("Got KEYSOCK_OUT_ERR message.\n")); 14873448Sdh155122 ah_keysock_no_socket(mp, ahstack); 14880Sstevel@tonic-gate break; 14890Sstevel@tonic-gate case KEYSOCK_IN: 14903448Sdh155122 AH_BUMP_STAT(ahstack, keysock_in); 14913448Sdh155122 ah3dbg(ahstack, ("Got KEYSOCK_IN message.\n")); 14923055Sdanmcd 14933055Sdanmcd /* Parse the message. */ 14943448Sdh155122 ah_parse_pfkey(mp, ahstack); 14950Sstevel@tonic-gate break; 14960Sstevel@tonic-gate case KEYSOCK_HELLO: 14973448Sdh155122 sadb_keysock_hello(&ahstack->ah_pfkey_q, q, mp, 14983448Sdh155122 ah_ager, (void *)ahstack, &ahstack->ah_event, 14993448Sdh155122 SADB_SATYPE_AH); 15000Sstevel@tonic-gate break; 15010Sstevel@tonic-gate default: 15023448Sdh155122 ah1dbg(ahstack, ("Got M_CTL from above of 0x%x.\n", 15030Sstevel@tonic-gate ii->ipsec_info_type)); 15040Sstevel@tonic-gate freemsg(mp); 15050Sstevel@tonic-gate break; 15060Sstevel@tonic-gate } 15070Sstevel@tonic-gate break; 15080Sstevel@tonic-gate case M_IOCTL: 15090Sstevel@tonic-gate iocp = (struct iocblk *)mp->b_rptr; 15100Sstevel@tonic-gate switch (iocp->ioc_cmd) { 15110Sstevel@tonic-gate case ND_SET: 15120Sstevel@tonic-gate case ND_GET: 15133448Sdh155122 if (nd_getset(q, ahstack->ipsecah_g_nd, mp)) { 15140Sstevel@tonic-gate qreply(q, mp); 15150Sstevel@tonic-gate return; 15160Sstevel@tonic-gate } else { 15170Sstevel@tonic-gate iocp->ioc_error = ENOENT; 15180Sstevel@tonic-gate } 15190Sstevel@tonic-gate /* FALLTHRU */ 15200Sstevel@tonic-gate default: 15210Sstevel@tonic-gate /* We really don't support any other ioctls, do we? */ 15220Sstevel@tonic-gate 15230Sstevel@tonic-gate /* Return EINVAL */ 15240Sstevel@tonic-gate if (iocp->ioc_error != ENOENT) 15250Sstevel@tonic-gate iocp->ioc_error = EINVAL; 15260Sstevel@tonic-gate iocp->ioc_count = 0; 15270Sstevel@tonic-gate mp->b_datap->db_type = M_IOCACK; 15280Sstevel@tonic-gate qreply(q, mp); 15290Sstevel@tonic-gate return; 15300Sstevel@tonic-gate } 15310Sstevel@tonic-gate default: 15323448Sdh155122 ah3dbg(ahstack, 15333448Sdh155122 ("Got default message, type %d, passing to IP.\n", 15340Sstevel@tonic-gate mp->b_datap->db_type)); 15350Sstevel@tonic-gate putnext(q, mp); 15360Sstevel@tonic-gate } 15370Sstevel@tonic-gate } 15380Sstevel@tonic-gate 15390Sstevel@tonic-gate /* 15400Sstevel@tonic-gate * Updating use times can be tricky business if the ipsa_haspeer flag is 15410Sstevel@tonic-gate * set. This function is called once in an SA's lifetime. 15420Sstevel@tonic-gate * 15430Sstevel@tonic-gate * Caller has to REFRELE "assoc" which is passed in. This function has 15440Sstevel@tonic-gate * to REFRELE any peer SA that is obtained. 15450Sstevel@tonic-gate */ 15460Sstevel@tonic-gate static void 15470Sstevel@tonic-gate ah_set_usetime(ipsa_t *assoc, boolean_t inbound) 15480Sstevel@tonic-gate { 15490Sstevel@tonic-gate ipsa_t *inassoc, *outassoc; 15500Sstevel@tonic-gate isaf_t *bucket; 15510Sstevel@tonic-gate sadb_t *sp; 15520Sstevel@tonic-gate int outhash; 15530Sstevel@tonic-gate boolean_t isv6; 15543448Sdh155122 netstack_t *ns = assoc->ipsa_netstack; 15553448Sdh155122 ipsecah_stack_t *ahstack = ns->netstack_ipsecah; 15560Sstevel@tonic-gate 15570Sstevel@tonic-gate /* No peer? No problem! */ 15580Sstevel@tonic-gate if (!assoc->ipsa_haspeer) { 15590Sstevel@tonic-gate sadb_set_usetime(assoc); 15600Sstevel@tonic-gate return; 15610Sstevel@tonic-gate } 15620Sstevel@tonic-gate 15630Sstevel@tonic-gate /* 15640Sstevel@tonic-gate * Otherwise, we want to grab both the original assoc and its peer. 15650Sstevel@tonic-gate * There might be a race for this, but if it's a real race, the times 15660Sstevel@tonic-gate * will be out-of-synch by at most a second, and since our time 15670Sstevel@tonic-gate * granularity is a second, this won't be a problem. 15680Sstevel@tonic-gate * 15690Sstevel@tonic-gate * If we need tight synchronization on the peer SA, then we need to 15700Sstevel@tonic-gate * reconsider. 15710Sstevel@tonic-gate */ 15720Sstevel@tonic-gate 15730Sstevel@tonic-gate /* Use address family to select IPv6/IPv4 */ 15740Sstevel@tonic-gate isv6 = (assoc->ipsa_addrfam == AF_INET6); 15750Sstevel@tonic-gate if (isv6) { 15763448Sdh155122 sp = &ahstack->ah_sadb.s_v6; 15770Sstevel@tonic-gate } else { 15783448Sdh155122 sp = &ahstack->ah_sadb.s_v4; 15790Sstevel@tonic-gate ASSERT(assoc->ipsa_addrfam == AF_INET); 15800Sstevel@tonic-gate } 15810Sstevel@tonic-gate if (inbound) { 15820Sstevel@tonic-gate inassoc = assoc; 15830Sstevel@tonic-gate if (isv6) 1584*4987Sdanmcd outhash = OUTBOUND_HASH_V6(sp, 1585*4987Sdanmcd *((in6_addr_t *)&inassoc->ipsa_dstaddr)); 15860Sstevel@tonic-gate else 1587*4987Sdanmcd outhash = OUTBOUND_HASH_V4(sp, 1588*4987Sdanmcd *((ipaddr_t *)&inassoc->ipsa_dstaddr)); 15890Sstevel@tonic-gate bucket = &sp->sdb_of[outhash]; 15900Sstevel@tonic-gate 15910Sstevel@tonic-gate mutex_enter(&bucket->isaf_lock); 15920Sstevel@tonic-gate outassoc = ipsec_getassocbyspi(bucket, inassoc->ipsa_spi, 15930Sstevel@tonic-gate inassoc->ipsa_srcaddr, inassoc->ipsa_dstaddr, 15940Sstevel@tonic-gate inassoc->ipsa_addrfam); 15950Sstevel@tonic-gate mutex_exit(&bucket->isaf_lock); 15960Sstevel@tonic-gate if (outassoc == NULL) { 15970Sstevel@tonic-gate /* Q: Do we wish to set haspeer == B_FALSE? */ 15980Sstevel@tonic-gate ah0dbg(("ah_set_usetime: " 15990Sstevel@tonic-gate "can't find peer for inbound.\n")); 16000Sstevel@tonic-gate sadb_set_usetime(inassoc); 16010Sstevel@tonic-gate return; 16020Sstevel@tonic-gate } 16030Sstevel@tonic-gate } else { 16040Sstevel@tonic-gate outassoc = assoc; 1605564Ssommerfe bucket = INBOUND_BUCKET(sp, outassoc->ipsa_spi); 16060Sstevel@tonic-gate mutex_enter(&bucket->isaf_lock); 16070Sstevel@tonic-gate inassoc = ipsec_getassocbyspi(bucket, outassoc->ipsa_spi, 16080Sstevel@tonic-gate outassoc->ipsa_srcaddr, outassoc->ipsa_dstaddr, 16090Sstevel@tonic-gate outassoc->ipsa_addrfam); 16100Sstevel@tonic-gate mutex_exit(&bucket->isaf_lock); 16110Sstevel@tonic-gate if (inassoc == NULL) { 16120Sstevel@tonic-gate /* Q: Do we wish to set haspeer == B_FALSE? */ 16130Sstevel@tonic-gate ah0dbg(("ah_set_usetime: " 16140Sstevel@tonic-gate "can't find peer for outbound.\n")); 16150Sstevel@tonic-gate sadb_set_usetime(outassoc); 16160Sstevel@tonic-gate return; 16170Sstevel@tonic-gate } 16180Sstevel@tonic-gate } 16190Sstevel@tonic-gate 16200Sstevel@tonic-gate /* Update usetime on both. */ 16210Sstevel@tonic-gate sadb_set_usetime(inassoc); 16220Sstevel@tonic-gate sadb_set_usetime(outassoc); 16230Sstevel@tonic-gate 16240Sstevel@tonic-gate /* 16250Sstevel@tonic-gate * REFRELE any peer SA. 16260Sstevel@tonic-gate * 16270Sstevel@tonic-gate * Because of the multi-line macro nature of IPSA_REFRELE, keep 16280Sstevel@tonic-gate * them in { }. 16290Sstevel@tonic-gate */ 16300Sstevel@tonic-gate if (inbound) { 16310Sstevel@tonic-gate IPSA_REFRELE(outassoc); 16320Sstevel@tonic-gate } else { 16330Sstevel@tonic-gate IPSA_REFRELE(inassoc); 16340Sstevel@tonic-gate } 16350Sstevel@tonic-gate } 16360Sstevel@tonic-gate 16370Sstevel@tonic-gate /* 16380Sstevel@tonic-gate * Add a number of bytes to what the SA has protected so far. Return 16390Sstevel@tonic-gate * B_TRUE if the SA can still protect that many bytes. 16400Sstevel@tonic-gate * 16410Sstevel@tonic-gate * Caller must REFRELE the passed-in assoc. This function must REFRELE 16420Sstevel@tonic-gate * any obtained peer SA. 16430Sstevel@tonic-gate */ 16440Sstevel@tonic-gate static boolean_t 16450Sstevel@tonic-gate ah_age_bytes(ipsa_t *assoc, uint64_t bytes, boolean_t inbound) 16460Sstevel@tonic-gate { 16470Sstevel@tonic-gate ipsa_t *inassoc, *outassoc; 16480Sstevel@tonic-gate isaf_t *bucket; 16490Sstevel@tonic-gate boolean_t inrc, outrc, isv6; 16500Sstevel@tonic-gate sadb_t *sp; 16510Sstevel@tonic-gate int outhash; 16523448Sdh155122 netstack_t *ns = assoc->ipsa_netstack; 16533448Sdh155122 ipsecah_stack_t *ahstack = ns->netstack_ipsecah; 16540Sstevel@tonic-gate 16550Sstevel@tonic-gate /* No peer? No problem! */ 16560Sstevel@tonic-gate if (!assoc->ipsa_haspeer) { 16573448Sdh155122 return (sadb_age_bytes(ahstack->ah_pfkey_q, assoc, bytes, 16580Sstevel@tonic-gate B_TRUE)); 16590Sstevel@tonic-gate } 16600Sstevel@tonic-gate 16610Sstevel@tonic-gate /* 16620Sstevel@tonic-gate * Otherwise, we want to grab both the original assoc and its peer. 16630Sstevel@tonic-gate * There might be a race for this, but if it's a real race, two 16640Sstevel@tonic-gate * expire messages may occur. We limit this by only sending the 16650Sstevel@tonic-gate * expire message on one of the peers, we'll pick the inbound 16660Sstevel@tonic-gate * arbitrarily. 16670Sstevel@tonic-gate * 16680Sstevel@tonic-gate * If we need tight synchronization on the peer SA, then we need to 16690Sstevel@tonic-gate * reconsider. 16700Sstevel@tonic-gate */ 16710Sstevel@tonic-gate 16720Sstevel@tonic-gate /* Pick v4/v6 bucket based on addrfam. */ 16730Sstevel@tonic-gate isv6 = (assoc->ipsa_addrfam == AF_INET6); 16740Sstevel@tonic-gate if (isv6) { 16753448Sdh155122 sp = &ahstack->ah_sadb.s_v6; 16760Sstevel@tonic-gate } else { 16773448Sdh155122 sp = &ahstack->ah_sadb.s_v4; 16780Sstevel@tonic-gate ASSERT(assoc->ipsa_addrfam == AF_INET); 16790Sstevel@tonic-gate } 16800Sstevel@tonic-gate if (inbound) { 16810Sstevel@tonic-gate inassoc = assoc; 16820Sstevel@tonic-gate if (isv6) 1683*4987Sdanmcd outhash = OUTBOUND_HASH_V6(sp, 1684*4987Sdanmcd *((in6_addr_t *)&inassoc->ipsa_dstaddr)); 16850Sstevel@tonic-gate else 1686*4987Sdanmcd outhash = OUTBOUND_HASH_V4(sp, 1687*4987Sdanmcd *((ipaddr_t *)&inassoc->ipsa_dstaddr)); 16880Sstevel@tonic-gate bucket = &sp->sdb_of[outhash]; 16890Sstevel@tonic-gate mutex_enter(&bucket->isaf_lock); 16900Sstevel@tonic-gate outassoc = ipsec_getassocbyspi(bucket, inassoc->ipsa_spi, 16910Sstevel@tonic-gate inassoc->ipsa_srcaddr, inassoc->ipsa_dstaddr, 16920Sstevel@tonic-gate inassoc->ipsa_addrfam); 16930Sstevel@tonic-gate mutex_exit(&bucket->isaf_lock); 16940Sstevel@tonic-gate if (outassoc == NULL) { 16950Sstevel@tonic-gate /* Q: Do we wish to set haspeer == B_FALSE? */ 16960Sstevel@tonic-gate ah0dbg(("ah_age_bytes: " 16970Sstevel@tonic-gate "can't find peer for inbound.\n")); 16983448Sdh155122 return (sadb_age_bytes(ahstack->ah_pfkey_q, inassoc, 16990Sstevel@tonic-gate bytes, B_TRUE)); 17000Sstevel@tonic-gate } 17010Sstevel@tonic-gate } else { 17020Sstevel@tonic-gate outassoc = assoc; 1703564Ssommerfe bucket = INBOUND_BUCKET(sp, outassoc->ipsa_spi); 17040Sstevel@tonic-gate mutex_enter(&bucket->isaf_lock); 17050Sstevel@tonic-gate inassoc = ipsec_getassocbyspi(bucket, outassoc->ipsa_spi, 17060Sstevel@tonic-gate outassoc->ipsa_srcaddr, outassoc->ipsa_dstaddr, 17070Sstevel@tonic-gate outassoc->ipsa_addrfam); 17080Sstevel@tonic-gate mutex_exit(&bucket->isaf_lock); 17090Sstevel@tonic-gate if (inassoc == NULL) { 17100Sstevel@tonic-gate /* Q: Do we wish to set haspeer == B_FALSE? */ 17110Sstevel@tonic-gate ah0dbg(("ah_age_bytes: " 17120Sstevel@tonic-gate "can't find peer for outbound.\n")); 17133448Sdh155122 return (sadb_age_bytes(ahstack->ah_pfkey_q, outassoc, 17140Sstevel@tonic-gate bytes, B_TRUE)); 17150Sstevel@tonic-gate } 17160Sstevel@tonic-gate } 17170Sstevel@tonic-gate 17183448Sdh155122 inrc = sadb_age_bytes(ahstack->ah_pfkey_q, inassoc, bytes, B_TRUE); 17193448Sdh155122 outrc = sadb_age_bytes(ahstack->ah_pfkey_q, outassoc, bytes, B_FALSE); 17200Sstevel@tonic-gate 17210Sstevel@tonic-gate /* 17220Sstevel@tonic-gate * REFRELE any peer SA. 17230Sstevel@tonic-gate * 17240Sstevel@tonic-gate * Because of the multi-line macro nature of IPSA_REFRELE, keep 17250Sstevel@tonic-gate * them in { }. 17260Sstevel@tonic-gate */ 17270Sstevel@tonic-gate if (inbound) { 17280Sstevel@tonic-gate IPSA_REFRELE(outassoc); 17290Sstevel@tonic-gate } else { 17300Sstevel@tonic-gate IPSA_REFRELE(inassoc); 17310Sstevel@tonic-gate } 17320Sstevel@tonic-gate 17330Sstevel@tonic-gate return (inrc && outrc); 17340Sstevel@tonic-gate } 17350Sstevel@tonic-gate 17360Sstevel@tonic-gate /* 17370Sstevel@tonic-gate * Perform the really difficult work of inserting the proposed situation. 17380Sstevel@tonic-gate * Called while holding the algorithm lock. 17390Sstevel@tonic-gate */ 17400Sstevel@tonic-gate static void 17410Sstevel@tonic-gate ah_insert_prop(sadb_prop_t *prop, ipsacq_t *acqrec, uint_t combs) 17420Sstevel@tonic-gate { 17430Sstevel@tonic-gate sadb_comb_t *comb = (sadb_comb_t *)(prop + 1); 17440Sstevel@tonic-gate ipsec_out_t *io; 17450Sstevel@tonic-gate ipsec_action_t *ap; 17460Sstevel@tonic-gate ipsec_prot_t *prot; 17473448Sdh155122 ipsecah_stack_t *ahstack; 17483448Sdh155122 netstack_t *ns; 17493448Sdh155122 ipsec_stack_t *ipss; 17503448Sdh155122 17510Sstevel@tonic-gate io = (ipsec_out_t *)acqrec->ipsacq_mp->b_rptr; 17520Sstevel@tonic-gate ASSERT(io->ipsec_out_type == IPSEC_OUT); 17530Sstevel@tonic-gate 17543448Sdh155122 ns = io->ipsec_out_ns; 17553448Sdh155122 ipss = ns->netstack_ipsec; 17563448Sdh155122 ahstack = ns->netstack_ipsecah; 17573448Sdh155122 ASSERT(MUTEX_HELD(&ipss->ipsec_alg_lock)); 17583448Sdh155122 17590Sstevel@tonic-gate prop->sadb_prop_exttype = SADB_EXT_PROPOSAL; 17600Sstevel@tonic-gate prop->sadb_prop_len = SADB_8TO64(sizeof (sadb_prop_t)); 17610Sstevel@tonic-gate *(uint32_t *)(&prop->sadb_prop_replay) = 0; /* Quick zero-out! */ 17620Sstevel@tonic-gate 17633448Sdh155122 prop->sadb_prop_replay = ahstack->ipsecah_replay_size; 17640Sstevel@tonic-gate 17650Sstevel@tonic-gate /* 17660Sstevel@tonic-gate * Based upon algorithm properties, and what-not, prioritize a 17670Sstevel@tonic-gate * proposal, based on the ordering of the ah algorithms in the 17680Sstevel@tonic-gate * alternatives presented in the policy rule passed down 17690Sstevel@tonic-gate * through the ipsec_out_t and attached to the acquire record. 17700Sstevel@tonic-gate */ 17710Sstevel@tonic-gate 17720Sstevel@tonic-gate for (ap = acqrec->ipsacq_act; ap != NULL; 17730Sstevel@tonic-gate ap = ap->ipa_next) { 17740Sstevel@tonic-gate ipsec_alginfo_t *aalg; 17750Sstevel@tonic-gate 17760Sstevel@tonic-gate if ((ap->ipa_act.ipa_type != IPSEC_POLICY_APPLY) || 17770Sstevel@tonic-gate (!ap->ipa_act.ipa_apply.ipp_use_ah)) 17780Sstevel@tonic-gate continue; 17790Sstevel@tonic-gate 17800Sstevel@tonic-gate prot = &ap->ipa_act.ipa_apply; 17810Sstevel@tonic-gate 17820Sstevel@tonic-gate ASSERT(prot->ipp_auth_alg > 0); 17830Sstevel@tonic-gate 17843448Sdh155122 aalg = ipss->ipsec_alglists[IPSEC_ALG_AUTH] 17853448Sdh155122 [prot->ipp_auth_alg]; 17860Sstevel@tonic-gate if (aalg == NULL || !ALG_VALID(aalg)) 17870Sstevel@tonic-gate continue; 17880Sstevel@tonic-gate 17890Sstevel@tonic-gate /* XXX check aalg for duplicates??.. */ 17900Sstevel@tonic-gate 17910Sstevel@tonic-gate comb->sadb_comb_flags = 0; 17920Sstevel@tonic-gate comb->sadb_comb_reserved = 0; 17930Sstevel@tonic-gate comb->sadb_comb_encrypt = 0; 17940Sstevel@tonic-gate comb->sadb_comb_encrypt_minbits = 0; 17950Sstevel@tonic-gate comb->sadb_comb_encrypt_maxbits = 0; 17960Sstevel@tonic-gate 17970Sstevel@tonic-gate comb->sadb_comb_auth = aalg->alg_id; 17982751Sdanmcd comb->sadb_comb_auth_minbits = 17992751Sdanmcd MAX(prot->ipp_ah_minbits, aalg->alg_ef_minbits); 18002751Sdanmcd comb->sadb_comb_auth_maxbits = 18012751Sdanmcd MIN(prot->ipp_ah_maxbits, aalg->alg_ef_maxbits); 18020Sstevel@tonic-gate 18030Sstevel@tonic-gate /* 18040Sstevel@tonic-gate * The following may be based on algorithm 18050Sstevel@tonic-gate * properties, but in the meantime, we just pick 18060Sstevel@tonic-gate * some good, sensible numbers. Key mgmt. can 18070Sstevel@tonic-gate * (and perhaps should) be the place to finalize 18080Sstevel@tonic-gate * such decisions. 18090Sstevel@tonic-gate */ 18100Sstevel@tonic-gate 18110Sstevel@tonic-gate /* 18120Sstevel@tonic-gate * No limits on allocations, since we really don't 18130Sstevel@tonic-gate * support that concept currently. 18140Sstevel@tonic-gate */ 18150Sstevel@tonic-gate comb->sadb_comb_soft_allocations = 0; 18160Sstevel@tonic-gate comb->sadb_comb_hard_allocations = 0; 18170Sstevel@tonic-gate 18180Sstevel@tonic-gate /* 18190Sstevel@tonic-gate * These may want to come from policy rule.. 18200Sstevel@tonic-gate */ 18213448Sdh155122 comb->sadb_comb_soft_bytes = 18223448Sdh155122 ahstack->ipsecah_default_soft_bytes; 18233448Sdh155122 comb->sadb_comb_hard_bytes = 18243448Sdh155122 ahstack->ipsecah_default_hard_bytes; 18253448Sdh155122 comb->sadb_comb_soft_addtime = 18263448Sdh155122 ahstack->ipsecah_default_soft_addtime; 18273448Sdh155122 comb->sadb_comb_hard_addtime = 18283448Sdh155122 ahstack->ipsecah_default_hard_addtime; 18293448Sdh155122 comb->sadb_comb_soft_usetime = 18303448Sdh155122 ahstack->ipsecah_default_soft_usetime; 18313448Sdh155122 comb->sadb_comb_hard_usetime = 18323448Sdh155122 ahstack->ipsecah_default_hard_usetime; 18330Sstevel@tonic-gate 18340Sstevel@tonic-gate prop->sadb_prop_len += SADB_8TO64(sizeof (*comb)); 18350Sstevel@tonic-gate if (--combs == 0) 18360Sstevel@tonic-gate return; /* out of space.. */ 18370Sstevel@tonic-gate comb++; 18380Sstevel@tonic-gate } 18390Sstevel@tonic-gate } 18400Sstevel@tonic-gate 18410Sstevel@tonic-gate /* 18420Sstevel@tonic-gate * Prepare and actually send the SADB_ACQUIRE message to PF_KEY. 18430Sstevel@tonic-gate */ 18440Sstevel@tonic-gate static void 18453448Sdh155122 ah_send_acquire(ipsacq_t *acqrec, mblk_t *extended, netstack_t *ns) 18460Sstevel@tonic-gate { 18473055Sdanmcd uint_t combs; 18480Sstevel@tonic-gate sadb_msg_t *samsg; 18490Sstevel@tonic-gate sadb_prop_t *prop; 18503055Sdanmcd mblk_t *pfkeymp, *msgmp; 18513448Sdh155122 ipsecah_stack_t *ahstack = ns->netstack_ipsecah; 18523448Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 18533448Sdh155122 18543448Sdh155122 AH_BUMP_STAT(ahstack, acquire_requests); 18553448Sdh155122 18563448Sdh155122 if (ahstack->ah_pfkey_q == NULL) 18573055Sdanmcd return; 18580Sstevel@tonic-gate 18590Sstevel@tonic-gate /* Set up ACQUIRE. */ 18603448Sdh155122 pfkeymp = sadb_setup_acquire(acqrec, SADB_SATYPE_AH, 18613448Sdh155122 ns->netstack_ipsec); 18623055Sdanmcd if (pfkeymp == NULL) { 18630Sstevel@tonic-gate ah0dbg(("sadb_setup_acquire failed.\n")); 18643055Sdanmcd return; 18650Sstevel@tonic-gate } 18663448Sdh155122 ASSERT(MUTEX_HELD(&ipss->ipsec_alg_lock)); 18673448Sdh155122 combs = ipss->ipsec_nalgs[IPSEC_ALG_AUTH]; 18683055Sdanmcd msgmp = pfkeymp->b_cont; 18693055Sdanmcd samsg = (sadb_msg_t *)(msgmp->b_rptr); 18700Sstevel@tonic-gate 18710Sstevel@tonic-gate /* Insert proposal here. */ 18720Sstevel@tonic-gate 18730Sstevel@tonic-gate prop = (sadb_prop_t *)(((uint64_t *)samsg) + samsg->sadb_msg_len); 18740Sstevel@tonic-gate ah_insert_prop(prop, acqrec, combs); 18750Sstevel@tonic-gate samsg->sadb_msg_len += prop->sadb_prop_len; 18760Sstevel@tonic-gate msgmp->b_wptr += SADB_64TO8(samsg->sadb_msg_len); 18770Sstevel@tonic-gate 18783448Sdh155122 mutex_exit(&ipss->ipsec_alg_lock); 18790Sstevel@tonic-gate 18800Sstevel@tonic-gate /* 18810Sstevel@tonic-gate * Must mutex_exit() before sending PF_KEY message up, in 18820Sstevel@tonic-gate * order to avoid recursive mutex_enter() if there are no registered 18830Sstevel@tonic-gate * listeners. 18840Sstevel@tonic-gate * 18850Sstevel@tonic-gate * Once I've sent the message, I'm cool anyway. 18860Sstevel@tonic-gate */ 18870Sstevel@tonic-gate mutex_exit(&acqrec->ipsacq_lock); 18883055Sdanmcd if (extended != NULL) { 18893448Sdh155122 putnext(ahstack->ah_pfkey_q, extended); 18900Sstevel@tonic-gate } 18913448Sdh155122 putnext(ahstack->ah_pfkey_q, pfkeymp); 18920Sstevel@tonic-gate } 18930Sstevel@tonic-gate 18940Sstevel@tonic-gate /* 18950Sstevel@tonic-gate * Handle the SADB_GETSPI message. Create a larval SA. 18960Sstevel@tonic-gate */ 18970Sstevel@tonic-gate static void 18983448Sdh155122 ah_getspi(mblk_t *mp, keysock_in_t *ksi, ipsecah_stack_t *ahstack) 18990Sstevel@tonic-gate { 19000Sstevel@tonic-gate ipsa_t *newbie, *target; 19010Sstevel@tonic-gate isaf_t *outbound, *inbound; 19020Sstevel@tonic-gate int rc, diagnostic; 19030Sstevel@tonic-gate sadb_sa_t *assoc; 19040Sstevel@tonic-gate keysock_out_t *kso; 19050Sstevel@tonic-gate uint32_t newspi; 19060Sstevel@tonic-gate 19070Sstevel@tonic-gate /* 19080Sstevel@tonic-gate * Randomly generate a proposed SPI value. 19090Sstevel@tonic-gate */ 19100Sstevel@tonic-gate (void) random_get_pseudo_bytes((uint8_t *)&newspi, sizeof (uint32_t)); 19113448Sdh155122 newbie = sadb_getspi(ksi, newspi, &diagnostic, 19123448Sdh155122 ahstack->ipsecah_netstack); 19130Sstevel@tonic-gate 19140Sstevel@tonic-gate if (newbie == NULL) { 19153448Sdh155122 sadb_pfkey_error(ahstack->ah_pfkey_q, mp, ENOMEM, diagnostic, 19160Sstevel@tonic-gate ksi->ks_in_serial); 19170Sstevel@tonic-gate return; 19180Sstevel@tonic-gate } else if (newbie == (ipsa_t *)-1) { 19193448Sdh155122 sadb_pfkey_error(ahstack->ah_pfkey_q, mp, EINVAL, diagnostic, 19200Sstevel@tonic-gate ksi->ks_in_serial); 19210Sstevel@tonic-gate return; 19220Sstevel@tonic-gate } 19230Sstevel@tonic-gate 19240Sstevel@tonic-gate /* 19250Sstevel@tonic-gate * XXX - We may randomly collide. We really should recover from this. 19260Sstevel@tonic-gate * Unfortunately, that could require spending way-too-much-time 19270Sstevel@tonic-gate * in here. For now, let the user retry. 19280Sstevel@tonic-gate */ 19290Sstevel@tonic-gate 19300Sstevel@tonic-gate if (newbie->ipsa_addrfam == AF_INET6) { 19313448Sdh155122 outbound = OUTBOUND_BUCKET_V6(&ahstack->ah_sadb.s_v6, 1932564Ssommerfe *(uint32_t *)(newbie->ipsa_dstaddr)); 19333448Sdh155122 inbound = INBOUND_BUCKET(&ahstack->ah_sadb.s_v6, 19343448Sdh155122 newbie->ipsa_spi); 19350Sstevel@tonic-gate } else { 19363448Sdh155122 outbound = OUTBOUND_BUCKET_V4(&ahstack->ah_sadb.s_v4, 1937564Ssommerfe *(uint32_t *)(newbie->ipsa_dstaddr)); 19383448Sdh155122 inbound = INBOUND_BUCKET(&ahstack->ah_sadb.s_v4, 19393448Sdh155122 newbie->ipsa_spi); 19400Sstevel@tonic-gate } 19410Sstevel@tonic-gate 19420Sstevel@tonic-gate mutex_enter(&outbound->isaf_lock); 19430Sstevel@tonic-gate mutex_enter(&inbound->isaf_lock); 19440Sstevel@tonic-gate 19450Sstevel@tonic-gate /* 19460Sstevel@tonic-gate * Check for collisions (i.e. did sadb_getspi() return with something 19470Sstevel@tonic-gate * that already exists?). 19480Sstevel@tonic-gate * 19490Sstevel@tonic-gate * Try outbound first. Even though SADB_GETSPI is traditionally 19500Sstevel@tonic-gate * for inbound SAs, you never know what a user might do. 19510Sstevel@tonic-gate */ 19520Sstevel@tonic-gate target = ipsec_getassocbyspi(outbound, newbie->ipsa_spi, 19530Sstevel@tonic-gate newbie->ipsa_srcaddr, newbie->ipsa_dstaddr, newbie->ipsa_addrfam); 19540Sstevel@tonic-gate if (target == NULL) { 19550Sstevel@tonic-gate target = ipsec_getassocbyspi(inbound, newbie->ipsa_spi, 19560Sstevel@tonic-gate newbie->ipsa_srcaddr, newbie->ipsa_dstaddr, 19570Sstevel@tonic-gate newbie->ipsa_addrfam); 19580Sstevel@tonic-gate } 19590Sstevel@tonic-gate 19600Sstevel@tonic-gate /* 19610Sstevel@tonic-gate * I don't have collisions elsewhere! 19620Sstevel@tonic-gate * (Nor will I because I'm still holding inbound/outbound locks.) 19630Sstevel@tonic-gate */ 19640Sstevel@tonic-gate 19650Sstevel@tonic-gate if (target != NULL) { 19660Sstevel@tonic-gate rc = EEXIST; 19670Sstevel@tonic-gate IPSA_REFRELE(target); 19680Sstevel@tonic-gate } else { 19690Sstevel@tonic-gate /* 19700Sstevel@tonic-gate * sadb_insertassoc() also checks for collisions, so 19710Sstevel@tonic-gate * if there's a colliding larval entry, rc will be set 19720Sstevel@tonic-gate * to EEXIST. 19730Sstevel@tonic-gate */ 19740Sstevel@tonic-gate rc = sadb_insertassoc(newbie, inbound); 1975*4987Sdanmcd newbie->ipsa_hardexpiretime = gethrestime_sec(); 19763448Sdh155122 newbie->ipsa_hardexpiretime += ahstack->ipsecah_larval_timeout; 19770Sstevel@tonic-gate } 19780Sstevel@tonic-gate 19790Sstevel@tonic-gate /* 19800Sstevel@tonic-gate * Can exit outbound mutex. Hold inbound until we're done with 19810Sstevel@tonic-gate * newbie. 19820Sstevel@tonic-gate */ 19830Sstevel@tonic-gate mutex_exit(&outbound->isaf_lock); 19840Sstevel@tonic-gate 19850Sstevel@tonic-gate if (rc != 0) { 19860Sstevel@tonic-gate mutex_exit(&inbound->isaf_lock); 19870Sstevel@tonic-gate IPSA_REFRELE(newbie); 19883448Sdh155122 sadb_pfkey_error(ahstack->ah_pfkey_q, mp, rc, 19893448Sdh155122 SADB_X_DIAGNOSTIC_NONE, ksi->ks_in_serial); 19900Sstevel@tonic-gate return; 19910Sstevel@tonic-gate } 19920Sstevel@tonic-gate 19930Sstevel@tonic-gate /* Can write here because I'm still holding the bucket lock. */ 19940Sstevel@tonic-gate newbie->ipsa_type = SADB_SATYPE_AH; 19950Sstevel@tonic-gate 19960Sstevel@tonic-gate /* 19970Sstevel@tonic-gate * Construct successful return message. We have one thing going 19980Sstevel@tonic-gate * for us in PF_KEY v2. That's the fact that 19990Sstevel@tonic-gate * sizeof (sadb_spirange_t) == sizeof (sadb_sa_t) 20000Sstevel@tonic-gate */ 20010Sstevel@tonic-gate assoc = (sadb_sa_t *)ksi->ks_in_extv[SADB_EXT_SPIRANGE]; 20020Sstevel@tonic-gate assoc->sadb_sa_exttype = SADB_EXT_SA; 20030Sstevel@tonic-gate assoc->sadb_sa_spi = newbie->ipsa_spi; 20040Sstevel@tonic-gate *((uint64_t *)(&assoc->sadb_sa_replay)) = 0; 20050Sstevel@tonic-gate mutex_exit(&inbound->isaf_lock); 20060Sstevel@tonic-gate 20070Sstevel@tonic-gate /* Convert KEYSOCK_IN to KEYSOCK_OUT. */ 20080Sstevel@tonic-gate kso = (keysock_out_t *)ksi; 20090Sstevel@tonic-gate kso->ks_out_len = sizeof (*kso); 20100Sstevel@tonic-gate kso->ks_out_serial = ksi->ks_in_serial; 20110Sstevel@tonic-gate kso->ks_out_type = KEYSOCK_OUT; 20120Sstevel@tonic-gate 20130Sstevel@tonic-gate /* 20140Sstevel@tonic-gate * Can safely putnext() to ah_pfkey_q, because this is a turnaround 20150Sstevel@tonic-gate * from the ah_pfkey_q. 20160Sstevel@tonic-gate */ 20173448Sdh155122 putnext(ahstack->ah_pfkey_q, mp); 20180Sstevel@tonic-gate } 20190Sstevel@tonic-gate 20200Sstevel@tonic-gate /* 20210Sstevel@tonic-gate * IPv6 sends up the ICMP errors for validation and the removal of the AH 20220Sstevel@tonic-gate * header. 20230Sstevel@tonic-gate */ 20240Sstevel@tonic-gate static ipsec_status_t 20253448Sdh155122 ah_icmp_error_v6(mblk_t *ipsec_mp, ipsecah_stack_t *ahstack) 20260Sstevel@tonic-gate { 20270Sstevel@tonic-gate mblk_t *mp; 20280Sstevel@tonic-gate ip6_t *ip6h, *oip6h; 20290Sstevel@tonic-gate uint16_t hdr_length, ah_length; 20300Sstevel@tonic-gate uint8_t *nexthdrp; 20310Sstevel@tonic-gate ah_t *ah; 20320Sstevel@tonic-gate icmp6_t *icmp6; 20330Sstevel@tonic-gate isaf_t *isaf; 20340Sstevel@tonic-gate ipsa_t *assoc; 20350Sstevel@tonic-gate uint8_t *post_ah_ptr; 20363448Sdh155122 ipsec_stack_t *ipss = ahstack->ipsecah_netstack->netstack_ipsec; 20370Sstevel@tonic-gate 20380Sstevel@tonic-gate mp = ipsec_mp->b_cont; 20390Sstevel@tonic-gate ASSERT(mp->b_datap->db_type == M_CTL); 20400Sstevel@tonic-gate 20410Sstevel@tonic-gate /* 20420Sstevel@tonic-gate * Change the type to M_DATA till we finish pullups. 20430Sstevel@tonic-gate */ 20440Sstevel@tonic-gate mp->b_datap->db_type = M_DATA; 20450Sstevel@tonic-gate 20460Sstevel@tonic-gate /* 20470Sstevel@tonic-gate * Eat the cost of a pullupmsg() for now. It makes the rest of this 20480Sstevel@tonic-gate * code far less convoluted. 20490Sstevel@tonic-gate */ 20500Sstevel@tonic-gate if (!pullupmsg(mp, -1) || 20510Sstevel@tonic-gate !ip_hdr_length_nexthdr_v6(mp, (ip6_t *)mp->b_rptr, &hdr_length, 2052*4987Sdanmcd &nexthdrp) || 20530Sstevel@tonic-gate mp->b_rptr + hdr_length + sizeof (icmp6_t) + sizeof (ip6_t) + 20540Sstevel@tonic-gate sizeof (ah_t) > mp->b_wptr) { 20553448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 20563448Sdh155122 ip_drop_packet(ipsec_mp, B_TRUE, NULL, NULL, 20573448Sdh155122 DROPPER(ipss, ipds_ah_nomem), 20583448Sdh155122 &ahstack->ah_dropper); 20590Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 20600Sstevel@tonic-gate } 20610Sstevel@tonic-gate 20620Sstevel@tonic-gate oip6h = (ip6_t *)mp->b_rptr; 20630Sstevel@tonic-gate icmp6 = (icmp6_t *)((uint8_t *)oip6h + hdr_length); 20640Sstevel@tonic-gate ip6h = (ip6_t *)(icmp6 + 1); 20650Sstevel@tonic-gate if (!ip_hdr_length_nexthdr_v6(mp, ip6h, &hdr_length, &nexthdrp)) { 20663448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 20670Sstevel@tonic-gate ip_drop_packet(ipsec_mp, B_TRUE, NULL, NULL, 20683448Sdh155122 DROPPER(ipss, ipds_ah_bad_v6_hdrs), 20693448Sdh155122 &ahstack->ah_dropper); 20700Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 20710Sstevel@tonic-gate } 20720Sstevel@tonic-gate ah = (ah_t *)((uint8_t *)ip6h + hdr_length); 20730Sstevel@tonic-gate 20743448Sdh155122 isaf = OUTBOUND_BUCKET_V6(&ahstack->ah_sadb.s_v6, ip6h->ip6_dst); 20750Sstevel@tonic-gate mutex_enter(&isaf->isaf_lock); 20760Sstevel@tonic-gate assoc = ipsec_getassocbyspi(isaf, ah->ah_spi, 20770Sstevel@tonic-gate (uint32_t *)&ip6h->ip6_src, (uint32_t *)&ip6h->ip6_dst, AF_INET6); 20780Sstevel@tonic-gate mutex_exit(&isaf->isaf_lock); 20790Sstevel@tonic-gate 20800Sstevel@tonic-gate if (assoc == NULL) { 20813448Sdh155122 IP_AH_BUMP_STAT(ipss, lookup_failure); 20823448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 20833448Sdh155122 if (ahstack->ipsecah_log_unknown_spi) { 20840Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, 20850Sstevel@tonic-gate SL_CONSOLE | SL_WARN | SL_ERROR, 20860Sstevel@tonic-gate "Bad ICMP message - No association for the " 20870Sstevel@tonic-gate "attached AH header whose spi is 0x%x, " 20880Sstevel@tonic-gate "sender is 0x%x\n", 20893448Sdh155122 ah->ah_spi, &oip6h->ip6_src, AF_INET6, 20903448Sdh155122 ahstack->ipsecah_netstack); 20910Sstevel@tonic-gate } 20923448Sdh155122 ip_drop_packet(ipsec_mp, B_TRUE, NULL, NULL, 20933448Sdh155122 DROPPER(ipss, ipds_ah_no_sa), 20943448Sdh155122 &ahstack->ah_dropper); 20950Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 20960Sstevel@tonic-gate } 20970Sstevel@tonic-gate 20980Sstevel@tonic-gate IPSA_REFRELE(assoc); 20990Sstevel@tonic-gate 21000Sstevel@tonic-gate /* 21010Sstevel@tonic-gate * There seems to be a valid association. If there is enough of AH 21020Sstevel@tonic-gate * header remove it, otherwise bail. One could check whether it has 21030Sstevel@tonic-gate * complete AH header plus 8 bytes but it does not make sense if an 21040Sstevel@tonic-gate * icmp error is returned for ICMP messages e.g ICMP time exceeded, 21050Sstevel@tonic-gate * that are being sent up. Let the caller figure out. 21060Sstevel@tonic-gate * 21070Sstevel@tonic-gate * NOTE: ah_length is the number of 32 bit words minus 2. 21080Sstevel@tonic-gate */ 21090Sstevel@tonic-gate ah_length = (ah->ah_length << 2) + 8; 21100Sstevel@tonic-gate post_ah_ptr = (uint8_t *)ah + ah_length; 21110Sstevel@tonic-gate 21120Sstevel@tonic-gate if (post_ah_ptr > mp->b_wptr) { 21133448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 21140Sstevel@tonic-gate ip_drop_packet(ipsec_mp, B_TRUE, NULL, NULL, 21153448Sdh155122 DROPPER(ipss, ipds_ah_bad_length), 21163448Sdh155122 &ahstack->ah_dropper); 21170Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 21180Sstevel@tonic-gate } 21190Sstevel@tonic-gate 21200Sstevel@tonic-gate ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) - ah_length); 21210Sstevel@tonic-gate *nexthdrp = ah->ah_nexthdr; 21220Sstevel@tonic-gate ovbcopy(post_ah_ptr, ah, 21230Sstevel@tonic-gate (size_t)((uintptr_t)mp->b_wptr - (uintptr_t)post_ah_ptr)); 21240Sstevel@tonic-gate mp->b_wptr -= ah_length; 21250Sstevel@tonic-gate /* Rewhack to be an ICMP error. */ 21260Sstevel@tonic-gate mp->b_datap->db_type = M_CTL; 21270Sstevel@tonic-gate 21280Sstevel@tonic-gate return (IPSEC_STATUS_SUCCESS); 21290Sstevel@tonic-gate } 21300Sstevel@tonic-gate 21310Sstevel@tonic-gate /* 21320Sstevel@tonic-gate * IP sends up the ICMP errors for validation and the removal of 21330Sstevel@tonic-gate * the AH header. 21340Sstevel@tonic-gate */ 21350Sstevel@tonic-gate static ipsec_status_t 21363448Sdh155122 ah_icmp_error_v4(mblk_t *ipsec_mp, ipsecah_stack_t *ahstack) 21370Sstevel@tonic-gate { 21380Sstevel@tonic-gate mblk_t *mp; 21390Sstevel@tonic-gate mblk_t *mp1; 21400Sstevel@tonic-gate icmph_t *icmph; 21410Sstevel@tonic-gate int iph_hdr_length; 21420Sstevel@tonic-gate int hdr_length; 21430Sstevel@tonic-gate isaf_t *hptr; 21440Sstevel@tonic-gate ipsa_t *assoc; 21450Sstevel@tonic-gate int ah_length; 21460Sstevel@tonic-gate ipha_t *ipha; 21470Sstevel@tonic-gate ipha_t *oipha; 21480Sstevel@tonic-gate ah_t *ah; 21490Sstevel@tonic-gate uint32_t length; 21500Sstevel@tonic-gate int alloc_size; 21510Sstevel@tonic-gate uint8_t nexthdr; 21523448Sdh155122 ipsec_stack_t *ipss = ahstack->ipsecah_netstack->netstack_ipsec; 21530Sstevel@tonic-gate 21540Sstevel@tonic-gate mp = ipsec_mp->b_cont; 21550Sstevel@tonic-gate ASSERT(mp->b_datap->db_type == M_CTL); 21560Sstevel@tonic-gate 21570Sstevel@tonic-gate /* 21580Sstevel@tonic-gate * Change the type to M_DATA till we finish pullups. 21590Sstevel@tonic-gate */ 21600Sstevel@tonic-gate mp->b_datap->db_type = M_DATA; 21610Sstevel@tonic-gate 21620Sstevel@tonic-gate oipha = ipha = (ipha_t *)mp->b_rptr; 21630Sstevel@tonic-gate iph_hdr_length = IPH_HDR_LENGTH(ipha); 21640Sstevel@tonic-gate icmph = (icmph_t *)&mp->b_rptr[iph_hdr_length]; 21650Sstevel@tonic-gate 21660Sstevel@tonic-gate ipha = (ipha_t *)&icmph[1]; 21670Sstevel@tonic-gate hdr_length = IPH_HDR_LENGTH(ipha); 21680Sstevel@tonic-gate 21690Sstevel@tonic-gate /* 21700Sstevel@tonic-gate * See if we have enough to locate the SPI 21710Sstevel@tonic-gate */ 21720Sstevel@tonic-gate if ((uchar_t *)ipha + hdr_length + 8 > mp->b_wptr) { 21730Sstevel@tonic-gate if (!pullupmsg(mp, (uchar_t *)ipha + hdr_length + 8 - 2174*4987Sdanmcd mp->b_rptr)) { 21753448Sdh155122 ipsec_rl_strlog(ahstack->ipsecah_netstack, 21763448Sdh155122 info.mi_idnum, 0, 0, 21770Sstevel@tonic-gate SL_WARN | SL_ERROR, 21780Sstevel@tonic-gate "ICMP error: Small AH header\n"); 21793448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 21800Sstevel@tonic-gate ip_drop_packet(ipsec_mp, B_TRUE, NULL, NULL, 21813448Sdh155122 DROPPER(ipss, ipds_ah_bad_length), 21823448Sdh155122 &ahstack->ah_dropper); 21830Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 21840Sstevel@tonic-gate } 21850Sstevel@tonic-gate icmph = (icmph_t *)&mp->b_rptr[iph_hdr_length]; 21860Sstevel@tonic-gate ipha = (ipha_t *)&icmph[1]; 21870Sstevel@tonic-gate } 21880Sstevel@tonic-gate 21890Sstevel@tonic-gate ah = (ah_t *)((uint8_t *)ipha + hdr_length); 21900Sstevel@tonic-gate nexthdr = ah->ah_nexthdr; 21910Sstevel@tonic-gate 21923448Sdh155122 hptr = OUTBOUND_BUCKET_V4(&ahstack->ah_sadb.s_v4, ipha->ipha_dst); 21930Sstevel@tonic-gate mutex_enter(&hptr->isaf_lock); 21940Sstevel@tonic-gate assoc = ipsec_getassocbyspi(hptr, ah->ah_spi, 21950Sstevel@tonic-gate (uint32_t *)&ipha->ipha_src, (uint32_t *)&ipha->ipha_dst, AF_INET); 21960Sstevel@tonic-gate mutex_exit(&hptr->isaf_lock); 21970Sstevel@tonic-gate 21980Sstevel@tonic-gate if (assoc == NULL) { 21993448Sdh155122 IP_AH_BUMP_STAT(ipss, lookup_failure); 22003448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 22013448Sdh155122 if (ahstack->ipsecah_log_unknown_spi) { 22020Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, 22030Sstevel@tonic-gate SL_CONSOLE | SL_WARN | SL_ERROR, 22040Sstevel@tonic-gate "Bad ICMP message - No association for the " 22050Sstevel@tonic-gate "attached AH header whose spi is 0x%x, " 22060Sstevel@tonic-gate "sender is 0x%x\n", 22073448Sdh155122 ah->ah_spi, &oipha->ipha_src, AF_INET, 22083448Sdh155122 ahstack->ipsecah_netstack); 22090Sstevel@tonic-gate } 22103448Sdh155122 ip_drop_packet(ipsec_mp, B_TRUE, NULL, NULL, 22113448Sdh155122 DROPPER(ipss, ipds_ah_no_sa), 22123448Sdh155122 &ahstack->ah_dropper); 22130Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 22140Sstevel@tonic-gate } 22150Sstevel@tonic-gate 22160Sstevel@tonic-gate IPSA_REFRELE(assoc); 22170Sstevel@tonic-gate /* 22180Sstevel@tonic-gate * There seems to be a valid association. If there 22190Sstevel@tonic-gate * is enough of AH header remove it, otherwise remove 22200Sstevel@tonic-gate * as much as possible and send it back. One could check 22210Sstevel@tonic-gate * whether it has complete AH header plus 8 bytes but it 22220Sstevel@tonic-gate * does not make sense if an icmp error is returned for 22230Sstevel@tonic-gate * ICMP messages e.g ICMP time exceeded, that are being 22240Sstevel@tonic-gate * sent up. Let the caller figure out. 22250Sstevel@tonic-gate * 22260Sstevel@tonic-gate * NOTE: ah_length is the number of 32 bit words minus 2. 22270Sstevel@tonic-gate */ 22280Sstevel@tonic-gate ah_length = (ah->ah_length << 2) + 8; 22290Sstevel@tonic-gate 22300Sstevel@tonic-gate if ((uchar_t *)ipha + hdr_length + ah_length > mp->b_wptr) { 22310Sstevel@tonic-gate if (mp->b_cont == NULL) { 22320Sstevel@tonic-gate /* 22330Sstevel@tonic-gate * There is nothing to pullup. Just remove as 22340Sstevel@tonic-gate * much as possible. This is a common case for 22350Sstevel@tonic-gate * IPV4. 22360Sstevel@tonic-gate */ 22370Sstevel@tonic-gate ah_length = (mp->b_wptr - ((uchar_t *)ipha + 22380Sstevel@tonic-gate hdr_length)); 22390Sstevel@tonic-gate goto done; 22400Sstevel@tonic-gate } 22410Sstevel@tonic-gate /* Pullup the full ah header */ 22420Sstevel@tonic-gate if (!pullupmsg(mp, (uchar_t *)ah + ah_length - mp->b_rptr)) { 22430Sstevel@tonic-gate /* 22440Sstevel@tonic-gate * pullupmsg could have failed if there was not 22450Sstevel@tonic-gate * enough to pullup or memory allocation failed. 22460Sstevel@tonic-gate * We tried hard, give up now. 22470Sstevel@tonic-gate */ 22483448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 22490Sstevel@tonic-gate ip_drop_packet(ipsec_mp, B_TRUE, NULL, NULL, 22503448Sdh155122 DROPPER(ipss, ipds_ah_nomem), 22513448Sdh155122 &ahstack->ah_dropper); 22520Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 22530Sstevel@tonic-gate } 22540Sstevel@tonic-gate icmph = (icmph_t *)&mp->b_rptr[iph_hdr_length]; 22550Sstevel@tonic-gate ipha = (ipha_t *)&icmph[1]; 22560Sstevel@tonic-gate } 22570Sstevel@tonic-gate done: 22580Sstevel@tonic-gate /* 22590Sstevel@tonic-gate * Remove the AH header and change the protocol. 22600Sstevel@tonic-gate * Don't update the spi fields in the ipsec_in 22610Sstevel@tonic-gate * message as we are called just to validate the 22620Sstevel@tonic-gate * message attached to the ICMP message. 22630Sstevel@tonic-gate * 22640Sstevel@tonic-gate * If we never pulled up since all of the message 22650Sstevel@tonic-gate * is in one single mblk, we can't remove the AH header 22660Sstevel@tonic-gate * by just setting the b_wptr to the beginning of the 22670Sstevel@tonic-gate * AH header. We need to allocate a mblk that can hold 22680Sstevel@tonic-gate * up until the inner IP header and copy them. 22690Sstevel@tonic-gate */ 22700Sstevel@tonic-gate alloc_size = iph_hdr_length + sizeof (icmph_t) + hdr_length; 22710Sstevel@tonic-gate 22720Sstevel@tonic-gate if ((mp1 = allocb(alloc_size, BPRI_LO)) == NULL) { 22733448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 22743448Sdh155122 ip_drop_packet(ipsec_mp, B_TRUE, NULL, NULL, 22753448Sdh155122 DROPPER(ipss, ipds_ah_nomem), 22763448Sdh155122 &ahstack->ah_dropper); 22770Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 22780Sstevel@tonic-gate } 22790Sstevel@tonic-gate /* ICMP errors are M_CTL messages */ 22800Sstevel@tonic-gate mp1->b_datap->db_type = M_CTL; 22810Sstevel@tonic-gate ipsec_mp->b_cont = mp1; 22820Sstevel@tonic-gate bcopy(mp->b_rptr, mp1->b_rptr, alloc_size); 22830Sstevel@tonic-gate mp1->b_wptr += alloc_size; 22840Sstevel@tonic-gate 22850Sstevel@tonic-gate /* 22860Sstevel@tonic-gate * Skip whatever we have copied and as much of AH header 22870Sstevel@tonic-gate * possible. If we still have something left in the original 22880Sstevel@tonic-gate * message, tag on. 22890Sstevel@tonic-gate */ 22900Sstevel@tonic-gate mp->b_rptr = (uchar_t *)ipha + hdr_length + ah_length; 22910Sstevel@tonic-gate 22920Sstevel@tonic-gate if (mp->b_rptr != mp->b_wptr) { 22930Sstevel@tonic-gate mp1->b_cont = mp; 22940Sstevel@tonic-gate } else { 22950Sstevel@tonic-gate if (mp->b_cont != NULL) 22960Sstevel@tonic-gate mp1->b_cont = mp->b_cont; 22970Sstevel@tonic-gate freeb(mp); 22980Sstevel@tonic-gate } 22990Sstevel@tonic-gate 23000Sstevel@tonic-gate ipha = (ipha_t *)(mp1->b_rptr + iph_hdr_length + sizeof (icmph_t)); 23010Sstevel@tonic-gate ipha->ipha_protocol = nexthdr; 23020Sstevel@tonic-gate length = ntohs(ipha->ipha_length); 23030Sstevel@tonic-gate length -= ah_length; 23040Sstevel@tonic-gate ipha->ipha_length = htons((uint16_t)length); 23050Sstevel@tonic-gate ipha->ipha_hdr_checksum = 0; 23060Sstevel@tonic-gate ipha->ipha_hdr_checksum = (uint16_t)ip_csum_hdr(ipha); 23070Sstevel@tonic-gate 23080Sstevel@tonic-gate return (IPSEC_STATUS_SUCCESS); 23090Sstevel@tonic-gate } 23100Sstevel@tonic-gate 23110Sstevel@tonic-gate /* 23120Sstevel@tonic-gate * IP calls this to validate the ICMP errors that 23130Sstevel@tonic-gate * we got from the network. 23140Sstevel@tonic-gate */ 23150Sstevel@tonic-gate ipsec_status_t 23160Sstevel@tonic-gate ipsecah_icmp_error(mblk_t *mp) 23170Sstevel@tonic-gate { 23180Sstevel@tonic-gate ipsec_in_t *ii = (ipsec_in_t *)mp->b_rptr; 23193448Sdh155122 netstack_t *ns = ii->ipsec_in_ns; 23203448Sdh155122 ipsecah_stack_t *ahstack = ns->netstack_ipsecah; 23210Sstevel@tonic-gate 23220Sstevel@tonic-gate if (ii->ipsec_in_v4) 23233448Sdh155122 return (ah_icmp_error_v4(mp, ahstack)); 23240Sstevel@tonic-gate else 23253448Sdh155122 return (ah_icmp_error_v6(mp, ahstack)); 23260Sstevel@tonic-gate } 23270Sstevel@tonic-gate 23280Sstevel@tonic-gate static int 23290Sstevel@tonic-gate ah_fix_tlv_options_v6(uint8_t *oi_opt, uint8_t *pi_opt, uint_t ehdrlen, 23300Sstevel@tonic-gate uint8_t hdr_type, boolean_t copy_always) 23310Sstevel@tonic-gate { 23320Sstevel@tonic-gate uint8_t opt_type; 23330Sstevel@tonic-gate uint_t optlen; 23340Sstevel@tonic-gate 23350Sstevel@tonic-gate ASSERT(hdr_type == IPPROTO_DSTOPTS || hdr_type == IPPROTO_HOPOPTS); 23360Sstevel@tonic-gate 23370Sstevel@tonic-gate /* 23380Sstevel@tonic-gate * Copy the next header and hdr ext. len of the HOP-by-HOP 23390Sstevel@tonic-gate * and Destination option. 23400Sstevel@tonic-gate */ 23410Sstevel@tonic-gate *pi_opt++ = *oi_opt++; 23420Sstevel@tonic-gate *pi_opt++ = *oi_opt++; 23430Sstevel@tonic-gate ehdrlen -= 2; 23440Sstevel@tonic-gate 23450Sstevel@tonic-gate /* 23460Sstevel@tonic-gate * Now handle all the TLV encoded options. 23470Sstevel@tonic-gate */ 23480Sstevel@tonic-gate while (ehdrlen != 0) { 23490Sstevel@tonic-gate opt_type = *oi_opt; 23500Sstevel@tonic-gate 23510Sstevel@tonic-gate if (opt_type == IP6OPT_PAD1) { 23520Sstevel@tonic-gate optlen = 1; 23530Sstevel@tonic-gate } else { 23540Sstevel@tonic-gate if (ehdrlen < 2) 23550Sstevel@tonic-gate goto bad_opt; 23560Sstevel@tonic-gate optlen = 2 + oi_opt[1]; 23570Sstevel@tonic-gate if (optlen > ehdrlen) 23580Sstevel@tonic-gate goto bad_opt; 23590Sstevel@tonic-gate } 23600Sstevel@tonic-gate if (copy_always || !(opt_type & IP6OPT_MUTABLE)) { 23610Sstevel@tonic-gate bcopy(oi_opt, pi_opt, optlen); 23620Sstevel@tonic-gate } else { 23630Sstevel@tonic-gate if (optlen == 1) { 23640Sstevel@tonic-gate *pi_opt = 0; 23650Sstevel@tonic-gate } else { 23660Sstevel@tonic-gate /* 23670Sstevel@tonic-gate * Copy the type and data length fields. 23680Sstevel@tonic-gate * Zero the option data by skipping 23690Sstevel@tonic-gate * option type and option data len 23700Sstevel@tonic-gate * fields. 23710Sstevel@tonic-gate */ 23720Sstevel@tonic-gate *pi_opt = *oi_opt; 23730Sstevel@tonic-gate *(pi_opt + 1) = *(oi_opt + 1); 23740Sstevel@tonic-gate bzero(pi_opt + 2, optlen - 2); 23750Sstevel@tonic-gate } 23760Sstevel@tonic-gate } 23770Sstevel@tonic-gate ehdrlen -= optlen; 23780Sstevel@tonic-gate oi_opt += optlen; 23790Sstevel@tonic-gate pi_opt += optlen; 23800Sstevel@tonic-gate } 23810Sstevel@tonic-gate return (0); 23820Sstevel@tonic-gate bad_opt: 23830Sstevel@tonic-gate return (-1); 23840Sstevel@tonic-gate } 23850Sstevel@tonic-gate 23860Sstevel@tonic-gate /* 23870Sstevel@tonic-gate * Construct a pseudo header for AH, processing all the options. 23880Sstevel@tonic-gate * 23890Sstevel@tonic-gate * oip6h is the IPv6 header of the incoming or outgoing packet. 23900Sstevel@tonic-gate * ip6h is the pointer to the pseudo headers IPV6 header. All 23910Sstevel@tonic-gate * the space needed for the options have been allocated including 23920Sstevel@tonic-gate * the AH header. 23930Sstevel@tonic-gate * 23940Sstevel@tonic-gate * If copy_always is set, all the options that appear before AH are copied 23950Sstevel@tonic-gate * blindly without checking for IP6OPT_MUTABLE. This is used by 23960Sstevel@tonic-gate * ah_auth_out_done(). Please refer to that function for details. 23970Sstevel@tonic-gate * 23980Sstevel@tonic-gate * NOTE : 23990Sstevel@tonic-gate * 24000Sstevel@tonic-gate * * AH header is never copied in this function even if copy_always 24010Sstevel@tonic-gate * is set. It just returns the ah_offset - offset of the AH header 24020Sstevel@tonic-gate * and the caller needs to do the copying. This is done so that we 24030Sstevel@tonic-gate * don't have pass extra arguments e.g. SA etc. and also, 24040Sstevel@tonic-gate * it is not needed when ah_auth_out_done is calling this function. 24050Sstevel@tonic-gate */ 24060Sstevel@tonic-gate static uint_t 24070Sstevel@tonic-gate ah_fix_phdr_v6(ip6_t *ip6h, ip6_t *oip6h, boolean_t outbound, 24080Sstevel@tonic-gate boolean_t copy_always) 24090Sstevel@tonic-gate { 24100Sstevel@tonic-gate uint8_t *oi_opt; 24110Sstevel@tonic-gate uint8_t *pi_opt; 24120Sstevel@tonic-gate uint8_t nexthdr; 24130Sstevel@tonic-gate uint8_t *prev_nexthdr; 24140Sstevel@tonic-gate ip6_hbh_t *hbhhdr; 24150Sstevel@tonic-gate ip6_dest_t *dsthdr = NULL; 24160Sstevel@tonic-gate ip6_rthdr0_t *rthdr; 24170Sstevel@tonic-gate int ehdrlen; 24180Sstevel@tonic-gate ah_t *ah; 24190Sstevel@tonic-gate int ret; 24200Sstevel@tonic-gate 24210Sstevel@tonic-gate /* 24220Sstevel@tonic-gate * In the outbound case for source route, ULP has already moved 24230Sstevel@tonic-gate * the first hop, which is now in ip6_dst. We need to re-arrange 24240Sstevel@tonic-gate * the header to make it look like how it would appear in the 24250Sstevel@tonic-gate * receiver i.e 24260Sstevel@tonic-gate * 24270Sstevel@tonic-gate * Because of ip_massage_options_v6 the header looks like 24280Sstevel@tonic-gate * this : 24290Sstevel@tonic-gate * 24300Sstevel@tonic-gate * ip6_src = S, ip6_dst = I1. followed by I2,I3,D. 24310Sstevel@tonic-gate * 24320Sstevel@tonic-gate * When it reaches the receiver, it would look like 24330Sstevel@tonic-gate * 24340Sstevel@tonic-gate * ip6_src = S, ip6_dst = D. followed by I1,I2,I3. 24350Sstevel@tonic-gate * 24360Sstevel@tonic-gate * NOTE : We assume that there are no problems with the options 24370Sstevel@tonic-gate * as IP should have already checked this. 24380Sstevel@tonic-gate */ 24390Sstevel@tonic-gate 24400Sstevel@tonic-gate oi_opt = (uchar_t *)&oip6h[1]; 24410Sstevel@tonic-gate pi_opt = (uchar_t *)&ip6h[1]; 24420Sstevel@tonic-gate 24430Sstevel@tonic-gate /* 24440Sstevel@tonic-gate * We set the prev_nexthdr properly in the pseudo header. 24450Sstevel@tonic-gate * After we finish authentication and come back from the 24460Sstevel@tonic-gate * algorithm module, pseudo header will become the real 24470Sstevel@tonic-gate * IP header. 24480Sstevel@tonic-gate */ 24490Sstevel@tonic-gate prev_nexthdr = (uint8_t *)&ip6h->ip6_nxt; 24500Sstevel@tonic-gate nexthdr = oip6h->ip6_nxt; 24510Sstevel@tonic-gate /* Assume IP has already stripped it */ 24520Sstevel@tonic-gate ASSERT(nexthdr != IPPROTO_FRAGMENT && nexthdr != IPPROTO_RAW); 24530Sstevel@tonic-gate ah = NULL; 24540Sstevel@tonic-gate dsthdr = NULL; 24550Sstevel@tonic-gate for (;;) { 24560Sstevel@tonic-gate switch (nexthdr) { 24570Sstevel@tonic-gate case IPPROTO_HOPOPTS: 24580Sstevel@tonic-gate hbhhdr = (ip6_hbh_t *)oi_opt; 24590Sstevel@tonic-gate nexthdr = hbhhdr->ip6h_nxt; 24600Sstevel@tonic-gate ehdrlen = 8 * (hbhhdr->ip6h_len + 1); 24610Sstevel@tonic-gate ret = ah_fix_tlv_options_v6(oi_opt, pi_opt, ehdrlen, 24620Sstevel@tonic-gate IPPROTO_HOPOPTS, copy_always); 24630Sstevel@tonic-gate /* 24640Sstevel@tonic-gate * Return a zero offset indicating error if there 24650Sstevel@tonic-gate * was error. 24660Sstevel@tonic-gate */ 24670Sstevel@tonic-gate if (ret == -1) 24680Sstevel@tonic-gate return (0); 24690Sstevel@tonic-gate hbhhdr = (ip6_hbh_t *)pi_opt; 24700Sstevel@tonic-gate prev_nexthdr = (uint8_t *)&hbhhdr->ip6h_nxt; 24710Sstevel@tonic-gate break; 24720Sstevel@tonic-gate case IPPROTO_ROUTING: 24730Sstevel@tonic-gate rthdr = (ip6_rthdr0_t *)oi_opt; 24740Sstevel@tonic-gate nexthdr = rthdr->ip6r0_nxt; 24750Sstevel@tonic-gate ehdrlen = 8 * (rthdr->ip6r0_len + 1); 24760Sstevel@tonic-gate if (!copy_always && outbound) { 24770Sstevel@tonic-gate int i, left; 24780Sstevel@tonic-gate ip6_rthdr0_t *prthdr; 24790Sstevel@tonic-gate in6_addr_t *ap, *pap; 24800Sstevel@tonic-gate 24810Sstevel@tonic-gate left = rthdr->ip6r0_segleft; 24820Sstevel@tonic-gate prthdr = (ip6_rthdr0_t *)pi_opt; 24830Sstevel@tonic-gate pap = (in6_addr_t *)(prthdr + 1); 24840Sstevel@tonic-gate ap = (in6_addr_t *)(rthdr + 1); 24850Sstevel@tonic-gate /* 24860Sstevel@tonic-gate * First eight bytes except seg_left 24870Sstevel@tonic-gate * does not change en route. 24880Sstevel@tonic-gate */ 24890Sstevel@tonic-gate bcopy(oi_opt, pi_opt, 8); 24900Sstevel@tonic-gate prthdr->ip6r0_segleft = 0; 24910Sstevel@tonic-gate /* 24920Sstevel@tonic-gate * First address has been moved to 24930Sstevel@tonic-gate * the destination address of the 24940Sstevel@tonic-gate * ip header by ip_massage_options_v6. 24950Sstevel@tonic-gate * And the real destination address is 24960Sstevel@tonic-gate * in the last address part of the 24970Sstevel@tonic-gate * option. 24980Sstevel@tonic-gate */ 24990Sstevel@tonic-gate *pap = oip6h->ip6_dst; 25000Sstevel@tonic-gate for (i = 1; i < left - 1; i++) 25010Sstevel@tonic-gate pap[i] = ap[i - 1]; 25020Sstevel@tonic-gate ip6h->ip6_dst = *(ap + left - 1); 25030Sstevel@tonic-gate } else { 25040Sstevel@tonic-gate bcopy(oi_opt, pi_opt, ehdrlen); 25050Sstevel@tonic-gate } 25060Sstevel@tonic-gate rthdr = (ip6_rthdr0_t *)pi_opt; 25070Sstevel@tonic-gate prev_nexthdr = (uint8_t *)&rthdr->ip6r0_nxt; 25080Sstevel@tonic-gate break; 25090Sstevel@tonic-gate case IPPROTO_DSTOPTS: 25100Sstevel@tonic-gate /* 25110Sstevel@tonic-gate * Destination options are tricky. If there is 25120Sstevel@tonic-gate * a terminal (e.g. non-IPv6-extension) header 25130Sstevel@tonic-gate * following the destination options, don't 25140Sstevel@tonic-gate * reset prev_nexthdr or advance the AH insertion 25150Sstevel@tonic-gate * point and just treat this as a terminal header. 25160Sstevel@tonic-gate * 25170Sstevel@tonic-gate * If this is an inbound packet, just deal with 25180Sstevel@tonic-gate * it as is. 25190Sstevel@tonic-gate */ 25200Sstevel@tonic-gate dsthdr = (ip6_dest_t *)oi_opt; 25210Sstevel@tonic-gate /* 25220Sstevel@tonic-gate * XXX I hope common-subexpression elimination 25230Sstevel@tonic-gate * saves us the double-evaluate. 25240Sstevel@tonic-gate */ 25250Sstevel@tonic-gate if (outbound && dsthdr->ip6d_nxt != IPPROTO_ROUTING && 25260Sstevel@tonic-gate dsthdr->ip6d_nxt != IPPROTO_HOPOPTS) 25270Sstevel@tonic-gate goto terminal_hdr; 25280Sstevel@tonic-gate nexthdr = dsthdr->ip6d_nxt; 25290Sstevel@tonic-gate ehdrlen = 8 * (dsthdr->ip6d_len + 1); 25300Sstevel@tonic-gate ret = ah_fix_tlv_options_v6(oi_opt, pi_opt, ehdrlen, 25310Sstevel@tonic-gate IPPROTO_DSTOPTS, copy_always); 25320Sstevel@tonic-gate /* 25330Sstevel@tonic-gate * Return a zero offset indicating error if there 25340Sstevel@tonic-gate * was error. 25350Sstevel@tonic-gate */ 25360Sstevel@tonic-gate if (ret == -1) 25370Sstevel@tonic-gate return (0); 25380Sstevel@tonic-gate break; 25390Sstevel@tonic-gate case IPPROTO_AH: 25400Sstevel@tonic-gate /* 25410Sstevel@tonic-gate * Be conservative in what you send. We shouldn't 25420Sstevel@tonic-gate * see two same-scoped AH's in one packet. 25430Sstevel@tonic-gate * (Inner-IP-scoped AH will be hit by terminal 25440Sstevel@tonic-gate * header of IP or IPv6.) 25450Sstevel@tonic-gate */ 25460Sstevel@tonic-gate ASSERT(!outbound); 25470Sstevel@tonic-gate return ((uint_t)(pi_opt - (uint8_t *)ip6h)); 25480Sstevel@tonic-gate default: 25490Sstevel@tonic-gate ASSERT(outbound); 25500Sstevel@tonic-gate terminal_hdr: 25510Sstevel@tonic-gate *prev_nexthdr = IPPROTO_AH; 25520Sstevel@tonic-gate ah = (ah_t *)pi_opt; 25530Sstevel@tonic-gate ah->ah_nexthdr = nexthdr; 25540Sstevel@tonic-gate return ((uint_t)(pi_opt - (uint8_t *)ip6h)); 25550Sstevel@tonic-gate } 25560Sstevel@tonic-gate pi_opt += ehdrlen; 25570Sstevel@tonic-gate oi_opt += ehdrlen; 25580Sstevel@tonic-gate } 25590Sstevel@tonic-gate /* NOTREACHED */ 25600Sstevel@tonic-gate } 25610Sstevel@tonic-gate 25620Sstevel@tonic-gate static boolean_t 25630Sstevel@tonic-gate ah_finish_up(ah_t *phdr_ah, ah_t *inbound_ah, ipsa_t *assoc, 25643448Sdh155122 int ah_data_sz, int ah_align_sz, ipsecah_stack_t *ahstack) 25650Sstevel@tonic-gate { 25660Sstevel@tonic-gate int i; 25670Sstevel@tonic-gate 25680Sstevel@tonic-gate /* 25690Sstevel@tonic-gate * Padding : 25700Sstevel@tonic-gate * 25710Sstevel@tonic-gate * 1) Authentication data may have to be padded 25720Sstevel@tonic-gate * before ICV calculation if ICV is not a multiple 25730Sstevel@tonic-gate * of 64 bits. This padding is arbitrary and transmitted 25740Sstevel@tonic-gate * with the packet at the end of the authentication data. 25750Sstevel@tonic-gate * Payload length should include the padding bytes. 25760Sstevel@tonic-gate * 25770Sstevel@tonic-gate * 2) Explicit padding of the whole datagram may be 25780Sstevel@tonic-gate * required by the algorithm which need not be 25790Sstevel@tonic-gate * transmitted. It is assumed that this will be taken 25800Sstevel@tonic-gate * care by the algorithm module. 25810Sstevel@tonic-gate */ 25820Sstevel@tonic-gate bzero(phdr_ah + 1, ah_data_sz); /* Zero out ICV for pseudo-hdr. */ 25830Sstevel@tonic-gate 25840Sstevel@tonic-gate if (inbound_ah == NULL) { 25850Sstevel@tonic-gate /* Outbound AH datagram. */ 25860Sstevel@tonic-gate 25870Sstevel@tonic-gate phdr_ah->ah_length = (ah_align_sz >> 2) + 1; 25880Sstevel@tonic-gate phdr_ah->ah_reserved = 0; 25890Sstevel@tonic-gate phdr_ah->ah_spi = assoc->ipsa_spi; 25900Sstevel@tonic-gate 25910Sstevel@tonic-gate phdr_ah->ah_replay = 25920Sstevel@tonic-gate htonl(atomic_add_32_nv(&assoc->ipsa_replay, 1)); 25930Sstevel@tonic-gate if (phdr_ah->ah_replay == 0 && assoc->ipsa_replay_wsize != 0) { 25940Sstevel@tonic-gate /* 25950Sstevel@tonic-gate * XXX We have replay counter wrapping. We probably 25960Sstevel@tonic-gate * want to nuke this SA (and its peer). 25970Sstevel@tonic-gate */ 25980Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, 25990Sstevel@tonic-gate SL_ERROR | SL_CONSOLE | SL_WARN, 26000Sstevel@tonic-gate "Outbound AH SA (0x%x), dst %s has wrapped " 26010Sstevel@tonic-gate "sequence.\n", phdr_ah->ah_spi, 26023448Sdh155122 assoc->ipsa_dstaddr, assoc->ipsa_addrfam, 26033448Sdh155122 ahstack->ipsecah_netstack); 26040Sstevel@tonic-gate 26050Sstevel@tonic-gate sadb_replay_delete(assoc); 26060Sstevel@tonic-gate /* Caller will free phdr_mp and return NULL. */ 26070Sstevel@tonic-gate return (B_FALSE); 26080Sstevel@tonic-gate } 26090Sstevel@tonic-gate 26100Sstevel@tonic-gate if (ah_data_sz != ah_align_sz) { 26110Sstevel@tonic-gate uchar_t *pad = ((uchar_t *)phdr_ah + sizeof (ah_t) + 26120Sstevel@tonic-gate ah_data_sz); 26130Sstevel@tonic-gate 26140Sstevel@tonic-gate for (i = 0; i < (ah_align_sz - ah_data_sz); i++) { 26150Sstevel@tonic-gate pad[i] = (uchar_t)i; /* Fill the padding */ 26160Sstevel@tonic-gate } 26170Sstevel@tonic-gate } 26180Sstevel@tonic-gate } else { 26190Sstevel@tonic-gate /* Inbound AH datagram. */ 26200Sstevel@tonic-gate phdr_ah->ah_nexthdr = inbound_ah->ah_nexthdr; 26210Sstevel@tonic-gate phdr_ah->ah_length = inbound_ah->ah_length; 26220Sstevel@tonic-gate phdr_ah->ah_reserved = 0; 26230Sstevel@tonic-gate ASSERT(inbound_ah->ah_spi == assoc->ipsa_spi); 26240Sstevel@tonic-gate phdr_ah->ah_spi = inbound_ah->ah_spi; 26250Sstevel@tonic-gate phdr_ah->ah_replay = inbound_ah->ah_replay; 26260Sstevel@tonic-gate 26270Sstevel@tonic-gate if (ah_data_sz != ah_align_sz) { 26283448Sdh155122 uchar_t *opad = ((uchar_t *)inbound_ah + 26293448Sdh155122 sizeof (ah_t) + ah_data_sz); 26300Sstevel@tonic-gate uchar_t *pad = ((uchar_t *)phdr_ah + sizeof (ah_t) + 26310Sstevel@tonic-gate ah_data_sz); 26320Sstevel@tonic-gate 26330Sstevel@tonic-gate for (i = 0; i < (ah_align_sz - ah_data_sz); i++) { 26340Sstevel@tonic-gate pad[i] = opad[i]; /* Copy the padding */ 26350Sstevel@tonic-gate } 26360Sstevel@tonic-gate } 26370Sstevel@tonic-gate } 26380Sstevel@tonic-gate 26390Sstevel@tonic-gate return (B_TRUE); 26400Sstevel@tonic-gate } 26410Sstevel@tonic-gate 26420Sstevel@tonic-gate /* 26430Sstevel@tonic-gate * Called upon failing the inbound ICV check. The message passed as 26440Sstevel@tonic-gate * argument is freed. 26450Sstevel@tonic-gate */ 26460Sstevel@tonic-gate static void 26470Sstevel@tonic-gate ah_log_bad_auth(mblk_t *ipsec_in) 26480Sstevel@tonic-gate { 26490Sstevel@tonic-gate mblk_t *mp = ipsec_in->b_cont->b_cont; 26500Sstevel@tonic-gate ipsec_in_t *ii = (ipsec_in_t *)ipsec_in->b_rptr; 26510Sstevel@tonic-gate boolean_t isv4 = ii->ipsec_in_v4; 26520Sstevel@tonic-gate ipsa_t *assoc = ii->ipsec_in_ah_sa; 26530Sstevel@tonic-gate int af; 26540Sstevel@tonic-gate void *addr; 26553448Sdh155122 netstack_t *ns = ii->ipsec_in_ns; 26563448Sdh155122 ipsecah_stack_t *ahstack = ns->netstack_ipsecah; 26573448Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 26580Sstevel@tonic-gate 26590Sstevel@tonic-gate mp->b_rptr -= ii->ipsec_in_skip_len; 26600Sstevel@tonic-gate 26610Sstevel@tonic-gate if (isv4) { 26620Sstevel@tonic-gate ipha_t *ipha = (ipha_t *)mp->b_rptr; 26630Sstevel@tonic-gate addr = &ipha->ipha_dst; 26640Sstevel@tonic-gate af = AF_INET; 26650Sstevel@tonic-gate } else { 26660Sstevel@tonic-gate ip6_t *ip6h = (ip6_t *)mp->b_rptr; 26670Sstevel@tonic-gate addr = &ip6h->ip6_dst; 26680Sstevel@tonic-gate af = AF_INET6; 26690Sstevel@tonic-gate } 26700Sstevel@tonic-gate 26710Sstevel@tonic-gate /* 26720Sstevel@tonic-gate * Log the event. Don't print to the console, block 26730Sstevel@tonic-gate * potential denial-of-service attack. 26740Sstevel@tonic-gate */ 26753448Sdh155122 AH_BUMP_STAT(ahstack, bad_auth); 26760Sstevel@tonic-gate 26770Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, SL_ERROR | SL_WARN, 26780Sstevel@tonic-gate "AH Authentication failed spi %x, dst_addr %s", 26793448Sdh155122 assoc->ipsa_spi, addr, af, ahstack->ipsecah_netstack); 26803448Sdh155122 26813448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 26823448Sdh155122 ip_drop_packet(ipsec_in, B_TRUE, NULL, NULL, 26833448Sdh155122 DROPPER(ipss, ipds_ah_bad_auth), 26843448Sdh155122 &ahstack->ah_dropper); 26850Sstevel@tonic-gate } 26860Sstevel@tonic-gate 26870Sstevel@tonic-gate /* 26880Sstevel@tonic-gate * Kernel crypto framework callback invoked after completion of async 26890Sstevel@tonic-gate * crypto requests. 26900Sstevel@tonic-gate */ 26910Sstevel@tonic-gate static void 26920Sstevel@tonic-gate ah_kcf_callback(void *arg, int status) 26930Sstevel@tonic-gate { 26940Sstevel@tonic-gate mblk_t *ipsec_mp = (mblk_t *)arg; 26950Sstevel@tonic-gate ipsec_in_t *ii = (ipsec_in_t *)ipsec_mp->b_rptr; 26960Sstevel@tonic-gate boolean_t is_inbound = (ii->ipsec_in_type == IPSEC_IN); 26973448Sdh155122 netstackid_t stackid; 26983448Sdh155122 netstack_t *ns, *ns_arg; 26993448Sdh155122 ipsec_stack_t *ipss; 27003448Sdh155122 ipsecah_stack_t *ahstack; 27013448Sdh155122 ipsec_out_t *io = (ipsec_out_t *)ii; 27020Sstevel@tonic-gate 27030Sstevel@tonic-gate ASSERT(ipsec_mp->b_cont != NULL); 27040Sstevel@tonic-gate 27053448Sdh155122 if (is_inbound) { 27063448Sdh155122 stackid = ii->ipsec_in_stackid; 27073448Sdh155122 ns_arg = ii->ipsec_in_ns; 27083448Sdh155122 } else { 27093448Sdh155122 stackid = io->ipsec_out_stackid; 27103448Sdh155122 ns_arg = io->ipsec_out_ns; 27113448Sdh155122 } 27123448Sdh155122 /* 27133448Sdh155122 * Verify that the netstack is still around; could have vanished 27143448Sdh155122 * while kEf was doing its work. 27153448Sdh155122 */ 27163448Sdh155122 ns = netstack_find_by_stackid(stackid); 27173448Sdh155122 if (ns == NULL || ns != ns_arg) { 27183448Sdh155122 /* Disappeared on us */ 27193448Sdh155122 if (ns != NULL) 27203448Sdh155122 netstack_rele(ns); 27213448Sdh155122 freemsg(ipsec_mp); 27223448Sdh155122 return; 27233448Sdh155122 } 27243448Sdh155122 27253448Sdh155122 ahstack = ns->netstack_ipsecah; 27263448Sdh155122 ipss = ns->netstack_ipsec; 27273448Sdh155122 27280Sstevel@tonic-gate if (status == CRYPTO_SUCCESS) { 27290Sstevel@tonic-gate if (is_inbound) { 27303448Sdh155122 if (ah_auth_in_done(ipsec_mp) != IPSEC_STATUS_SUCCESS) { 27313448Sdh155122 netstack_rele(ns); 27320Sstevel@tonic-gate return; 27333448Sdh155122 } 27340Sstevel@tonic-gate /* finish IPsec processing */ 27350Sstevel@tonic-gate ip_fanout_proto_again(ipsec_mp, NULL, NULL, NULL); 27360Sstevel@tonic-gate } else { 27370Sstevel@tonic-gate ipha_t *ipha; 27380Sstevel@tonic-gate 27393448Sdh155122 if (ah_auth_out_done(ipsec_mp) != 27403448Sdh155122 IPSEC_STATUS_SUCCESS) { 27413448Sdh155122 netstack_rele(ns); 27420Sstevel@tonic-gate return; 27433448Sdh155122 } 27440Sstevel@tonic-gate 27450Sstevel@tonic-gate /* finish IPsec processing */ 27460Sstevel@tonic-gate ipha = (ipha_t *)ipsec_mp->b_cont->b_rptr; 27470Sstevel@tonic-gate if (IPH_HDR_VERSION(ipha) == IP_VERSION) { 27480Sstevel@tonic-gate ip_wput_ipsec_out(NULL, ipsec_mp, ipha, NULL, 27490Sstevel@tonic-gate NULL); 27500Sstevel@tonic-gate } else { 27510Sstevel@tonic-gate ip6_t *ip6h = (ip6_t *)ipha; 27520Sstevel@tonic-gate ip_wput_ipsec_out_v6(NULL, ipsec_mp, ip6h, 27530Sstevel@tonic-gate NULL, NULL); 27540Sstevel@tonic-gate } 27550Sstevel@tonic-gate } 27560Sstevel@tonic-gate 27570Sstevel@tonic-gate } else if (status == CRYPTO_INVALID_MAC) { 27580Sstevel@tonic-gate ah_log_bad_auth(ipsec_mp); 27590Sstevel@tonic-gate } else { 27603448Sdh155122 ah1dbg(ahstack, ("ah_kcf_callback: crypto failed with 0x%x\n", 2761*4987Sdanmcd status)); 27623448Sdh155122 AH_BUMP_STAT(ahstack, crypto_failures); 27630Sstevel@tonic-gate if (is_inbound) 27643448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 27650Sstevel@tonic-gate else 27663448Sdh155122 AH_BUMP_STAT(ahstack, out_discards); 27670Sstevel@tonic-gate ip_drop_packet(ipsec_mp, is_inbound, NULL, NULL, 27683448Sdh155122 DROPPER(ipss, ipds_ah_crypto_failed), 27693448Sdh155122 &ahstack->ah_dropper); 27700Sstevel@tonic-gate } 27713448Sdh155122 netstack_rele(ns); 27720Sstevel@tonic-gate } 27730Sstevel@tonic-gate 27740Sstevel@tonic-gate /* 27750Sstevel@tonic-gate * Invoked on kernel crypto failure during inbound and outbound processing. 27760Sstevel@tonic-gate */ 27770Sstevel@tonic-gate static void 27783448Sdh155122 ah_crypto_failed(mblk_t *mp, boolean_t is_inbound, int kef_rc, 27793448Sdh155122 ipsecah_stack_t *ahstack) 27800Sstevel@tonic-gate { 27813448Sdh155122 ipsec_stack_t *ipss = ahstack->ipsecah_netstack->netstack_ipsec; 27823448Sdh155122 27833448Sdh155122 ah1dbg(ahstack, ("crypto failed for %s AH with 0x%x\n", 27840Sstevel@tonic-gate is_inbound ? "inbound" : "outbound", kef_rc)); 27853448Sdh155122 ip_drop_packet(mp, is_inbound, NULL, NULL, 27863448Sdh155122 DROPPER(ipss, ipds_ah_crypto_failed), 27873448Sdh155122 &ahstack->ah_dropper); 27883448Sdh155122 AH_BUMP_STAT(ahstack, crypto_failures); 27890Sstevel@tonic-gate if (is_inbound) 27903448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 27910Sstevel@tonic-gate else 27923448Sdh155122 AH_BUMP_STAT(ahstack, out_discards); 27930Sstevel@tonic-gate } 27940Sstevel@tonic-gate 27950Sstevel@tonic-gate /* 27960Sstevel@tonic-gate * Helper macros for the ah_submit_req_{inbound,outbound}() functions. 27970Sstevel@tonic-gate */ 27980Sstevel@tonic-gate 27993448Sdh155122 #define AH_INIT_CALLREQ(_cr, _ipss) { \ 28000Sstevel@tonic-gate (_cr)->cr_flag = CRYPTO_SKIP_REQID|CRYPTO_RESTRICTED; \ 28013448Sdh155122 if ((_ipss)->ipsec_algs_exec_mode[IPSEC_ALG_AUTH] == \ 28023448Sdh155122 IPSEC_ALGS_EXEC_ASYNC) \ 28030Sstevel@tonic-gate (_cr)->cr_flag |= CRYPTO_ALWAYS_QUEUE; \ 28040Sstevel@tonic-gate (_cr)->cr_callback_arg = ipsec_mp; \ 28050Sstevel@tonic-gate (_cr)->cr_callback_func = ah_kcf_callback; \ 28060Sstevel@tonic-gate } 28070Sstevel@tonic-gate 28080Sstevel@tonic-gate #define AH_INIT_CRYPTO_DATA(data, msglen, mblk) { \ 28090Sstevel@tonic-gate (data)->cd_format = CRYPTO_DATA_MBLK; \ 28100Sstevel@tonic-gate (data)->cd_mp = mblk; \ 28110Sstevel@tonic-gate (data)->cd_offset = 0; \ 28120Sstevel@tonic-gate (data)->cd_length = msglen; \ 28130Sstevel@tonic-gate } 28140Sstevel@tonic-gate 28150Sstevel@tonic-gate #define AH_INIT_CRYPTO_MAC(mac, icvlen, icvbuf) { \ 28160Sstevel@tonic-gate (mac)->cd_format = CRYPTO_DATA_RAW; \ 28170Sstevel@tonic-gate (mac)->cd_offset = 0; \ 28180Sstevel@tonic-gate (mac)->cd_length = icvlen; \ 28190Sstevel@tonic-gate (mac)->cd_raw.iov_base = icvbuf; \ 28200Sstevel@tonic-gate (mac)->cd_raw.iov_len = icvlen; \ 28210Sstevel@tonic-gate } 28220Sstevel@tonic-gate 28230Sstevel@tonic-gate /* 28240Sstevel@tonic-gate * Submit an inbound packet for processing by the crypto framework. 28250Sstevel@tonic-gate */ 28260Sstevel@tonic-gate static ipsec_status_t 28270Sstevel@tonic-gate ah_submit_req_inbound(mblk_t *ipsec_mp, size_t skip_len, uint32_t ah_offset, 28280Sstevel@tonic-gate ipsa_t *assoc) 28290Sstevel@tonic-gate { 28300Sstevel@tonic-gate int kef_rc; 28310Sstevel@tonic-gate mblk_t *phdr_mp; 28320Sstevel@tonic-gate crypto_call_req_t call_req; 28330Sstevel@tonic-gate ipsec_in_t *ii = (ipsec_in_t *)ipsec_mp->b_rptr; 28340Sstevel@tonic-gate uint_t icv_len = assoc->ipsa_mac_len; 28350Sstevel@tonic-gate crypto_ctx_template_t ctx_tmpl; 28363448Sdh155122 netstack_t *ns = ii->ipsec_in_ns; 28373448Sdh155122 ipsecah_stack_t *ahstack = ns->netstack_ipsecah; 28383448Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 28390Sstevel@tonic-gate 28400Sstevel@tonic-gate phdr_mp = ipsec_mp->b_cont; 28410Sstevel@tonic-gate ASSERT(phdr_mp != NULL); 28420Sstevel@tonic-gate ASSERT(ii->ipsec_in_type == IPSEC_IN); 28430Sstevel@tonic-gate 28443448Sdh155122 /* 28453448Sdh155122 * In case kEF queues and calls back, keep netstackid_t for 28463448Sdh155122 * verification that the IP instance is still around in 28473448Sdh155122 * ah_kcf_callback(). 28483448Sdh155122 */ 28493448Sdh155122 ii->ipsec_in_stackid = ns->netstack_stackid; 28503448Sdh155122 28510Sstevel@tonic-gate /* init arguments for the crypto framework */ 28520Sstevel@tonic-gate AH_INIT_CRYPTO_DATA(&ii->ipsec_in_crypto_data, AH_MSGSIZE(phdr_mp), 28530Sstevel@tonic-gate phdr_mp); 28540Sstevel@tonic-gate 28550Sstevel@tonic-gate AH_INIT_CRYPTO_MAC(&ii->ipsec_in_crypto_mac, icv_len, 28560Sstevel@tonic-gate (char *)phdr_mp->b_cont->b_rptr - skip_len + ah_offset + 28570Sstevel@tonic-gate sizeof (ah_t)); 28580Sstevel@tonic-gate 28593448Sdh155122 AH_INIT_CALLREQ(&call_req, ipss); 28600Sstevel@tonic-gate 28610Sstevel@tonic-gate ii->ipsec_in_skip_len = skip_len; 28620Sstevel@tonic-gate 28630Sstevel@tonic-gate IPSEC_CTX_TMPL(assoc, ipsa_authtmpl, IPSEC_ALG_AUTH, ctx_tmpl); 28640Sstevel@tonic-gate 28650Sstevel@tonic-gate /* call KEF to do the MAC operation */ 28660Sstevel@tonic-gate kef_rc = crypto_mac_verify(&assoc->ipsa_amech, 28670Sstevel@tonic-gate &ii->ipsec_in_crypto_data, &assoc->ipsa_kcfauthkey, ctx_tmpl, 28680Sstevel@tonic-gate &ii->ipsec_in_crypto_mac, &call_req); 28690Sstevel@tonic-gate 28700Sstevel@tonic-gate switch (kef_rc) { 28710Sstevel@tonic-gate case CRYPTO_SUCCESS: 28723448Sdh155122 AH_BUMP_STAT(ahstack, crypto_sync); 28730Sstevel@tonic-gate return (ah_auth_in_done(ipsec_mp)); 28740Sstevel@tonic-gate case CRYPTO_QUEUED: 28753448Sdh155122 /* ah_kcf_callback() will be invoked on completion */ 28763448Sdh155122 AH_BUMP_STAT(ahstack, crypto_async); 28770Sstevel@tonic-gate return (IPSEC_STATUS_PENDING); 28780Sstevel@tonic-gate case CRYPTO_INVALID_MAC: 28793448Sdh155122 AH_BUMP_STAT(ahstack, crypto_sync); 28800Sstevel@tonic-gate ah_log_bad_auth(ipsec_mp); 28810Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 28820Sstevel@tonic-gate } 28830Sstevel@tonic-gate 28843448Sdh155122 ah_crypto_failed(ipsec_mp, B_TRUE, kef_rc, ahstack); 28850Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 28860Sstevel@tonic-gate } 28870Sstevel@tonic-gate 28880Sstevel@tonic-gate /* 28890Sstevel@tonic-gate * Submit an outbound packet for processing by the crypto framework. 28900Sstevel@tonic-gate */ 28910Sstevel@tonic-gate static ipsec_status_t 28920Sstevel@tonic-gate ah_submit_req_outbound(mblk_t *ipsec_mp, size_t skip_len, ipsa_t *assoc) 28930Sstevel@tonic-gate { 28940Sstevel@tonic-gate int kef_rc; 28950Sstevel@tonic-gate mblk_t *phdr_mp; 28960Sstevel@tonic-gate crypto_call_req_t call_req; 28970Sstevel@tonic-gate ipsec_out_t *io = (ipsec_out_t *)ipsec_mp->b_rptr; 28980Sstevel@tonic-gate uint_t icv_len = assoc->ipsa_mac_len; 28993448Sdh155122 netstack_t *ns = io->ipsec_out_ns; 29003448Sdh155122 ipsecah_stack_t *ahstack = ns->netstack_ipsecah; 29013448Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 29020Sstevel@tonic-gate 29030Sstevel@tonic-gate phdr_mp = ipsec_mp->b_cont; 29040Sstevel@tonic-gate ASSERT(phdr_mp != NULL); 29050Sstevel@tonic-gate ASSERT(io->ipsec_out_type == IPSEC_OUT); 29060Sstevel@tonic-gate 29073448Sdh155122 /* 29083448Sdh155122 * In case kEF queues and calls back, keep netstackid_t for 29093448Sdh155122 * verification that the IP instance is still around in 29103448Sdh155122 * ah_kcf_callback(). 29113448Sdh155122 */ 29123448Sdh155122 io->ipsec_out_stackid = ns->netstack_stackid; 29133448Sdh155122 29140Sstevel@tonic-gate /* init arguments for the crypto framework */ 29150Sstevel@tonic-gate AH_INIT_CRYPTO_DATA(&io->ipsec_out_crypto_data, AH_MSGSIZE(phdr_mp), 29160Sstevel@tonic-gate phdr_mp); 29170Sstevel@tonic-gate 29180Sstevel@tonic-gate AH_INIT_CRYPTO_MAC(&io->ipsec_out_crypto_mac, icv_len, 29190Sstevel@tonic-gate (char *)phdr_mp->b_wptr); 29200Sstevel@tonic-gate 29213448Sdh155122 AH_INIT_CALLREQ(&call_req, ipss); 29220Sstevel@tonic-gate 29230Sstevel@tonic-gate io->ipsec_out_skip_len = skip_len; 29240Sstevel@tonic-gate 29250Sstevel@tonic-gate ASSERT(io->ipsec_out_ah_sa != NULL); 29260Sstevel@tonic-gate 29270Sstevel@tonic-gate /* call KEF to do the MAC operation */ 29280Sstevel@tonic-gate kef_rc = crypto_mac(&assoc->ipsa_amech, &io->ipsec_out_crypto_data, 29290Sstevel@tonic-gate &assoc->ipsa_kcfauthkey, assoc->ipsa_authtmpl, 29300Sstevel@tonic-gate &io->ipsec_out_crypto_mac, &call_req); 29310Sstevel@tonic-gate 29320Sstevel@tonic-gate switch (kef_rc) { 29330Sstevel@tonic-gate case CRYPTO_SUCCESS: 29343448Sdh155122 AH_BUMP_STAT(ahstack, crypto_sync); 29350Sstevel@tonic-gate return (ah_auth_out_done(ipsec_mp)); 29360Sstevel@tonic-gate case CRYPTO_QUEUED: 29373448Sdh155122 /* ah_kcf_callback() will be invoked on completion */ 29383448Sdh155122 AH_BUMP_STAT(ahstack, crypto_async); 29390Sstevel@tonic-gate return (IPSEC_STATUS_PENDING); 29400Sstevel@tonic-gate } 29410Sstevel@tonic-gate 29423448Sdh155122 ah_crypto_failed(ipsec_mp, B_FALSE, kef_rc, ahstack); 29430Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 29440Sstevel@tonic-gate } 29450Sstevel@tonic-gate 29460Sstevel@tonic-gate /* 29470Sstevel@tonic-gate * This function constructs a pseudo header by looking at the IP header 29480Sstevel@tonic-gate * and options if any. This is called for both outbound and inbound, 29490Sstevel@tonic-gate * before computing the ICV. 29500Sstevel@tonic-gate */ 29510Sstevel@tonic-gate static mblk_t * 29520Sstevel@tonic-gate ah_process_ip_options_v6(mblk_t *mp, ipsa_t *assoc, int *length_to_skip, 29533448Sdh155122 uint_t ah_data_sz, boolean_t outbound, ipsecah_stack_t *ahstack) 29540Sstevel@tonic-gate { 29550Sstevel@tonic-gate ip6_t *ip6h; 29560Sstevel@tonic-gate ip6_t *oip6h; 29570Sstevel@tonic-gate mblk_t *phdr_mp; 29580Sstevel@tonic-gate int option_length; 29590Sstevel@tonic-gate uint_t ah_align_sz; 29600Sstevel@tonic-gate uint_t ah_offset; 29610Sstevel@tonic-gate int hdr_size; 29623448Sdh155122 ipsec_stack_t *ipss = ahstack->ipsecah_netstack->netstack_ipsec; 29630Sstevel@tonic-gate 29640Sstevel@tonic-gate /* 29650Sstevel@tonic-gate * Allocate space for the authentication data also. It is 29660Sstevel@tonic-gate * useful both during the ICV calculation where we need to 29670Sstevel@tonic-gate * feed in zeroes and while sending the datagram back to IP 29680Sstevel@tonic-gate * where we will be using the same space. 29690Sstevel@tonic-gate * 29700Sstevel@tonic-gate * We need to allocate space for padding bytes if it is not 29710Sstevel@tonic-gate * a multiple of IPV6_PADDING_ALIGN. 29720Sstevel@tonic-gate * 29730Sstevel@tonic-gate * In addition, we allocate space for the ICV computed by 29740Sstevel@tonic-gate * the kernel crypto framework, saving us a separate kmem 29750Sstevel@tonic-gate * allocation down the road. 29760Sstevel@tonic-gate */ 29770Sstevel@tonic-gate 29780Sstevel@tonic-gate ah_align_sz = P2ALIGN(ah_data_sz + IPV6_PADDING_ALIGN - 1, 29790Sstevel@tonic-gate IPV6_PADDING_ALIGN); 29800Sstevel@tonic-gate 29810Sstevel@tonic-gate ASSERT(ah_align_sz >= ah_data_sz); 29820Sstevel@tonic-gate 29830Sstevel@tonic-gate hdr_size = ipsec_ah_get_hdr_size_v6(mp, B_FALSE); 29840Sstevel@tonic-gate option_length = hdr_size - IPV6_HDR_LEN; 29850Sstevel@tonic-gate 29860Sstevel@tonic-gate /* This was not included in ipsec_ah_get_hdr_size_v6() */ 29870Sstevel@tonic-gate hdr_size += (sizeof (ah_t) + ah_align_sz); 29880Sstevel@tonic-gate 29890Sstevel@tonic-gate if (!outbound && (MBLKL(mp) < hdr_size)) { 29900Sstevel@tonic-gate /* 29910Sstevel@tonic-gate * We have post-AH header options in a separate mblk, 29920Sstevel@tonic-gate * a pullup is required. 29930Sstevel@tonic-gate */ 29940Sstevel@tonic-gate if (!pullupmsg(mp, hdr_size)) 29950Sstevel@tonic-gate return (NULL); 29960Sstevel@tonic-gate } 29970Sstevel@tonic-gate 29980Sstevel@tonic-gate if ((phdr_mp = allocb(hdr_size + ah_data_sz, BPRI_HI)) == NULL) { 29990Sstevel@tonic-gate return (NULL); 30000Sstevel@tonic-gate } 30010Sstevel@tonic-gate 30020Sstevel@tonic-gate oip6h = (ip6_t *)mp->b_rptr; 30030Sstevel@tonic-gate 30040Sstevel@tonic-gate /* 30050Sstevel@tonic-gate * Form the basic IP header first. Zero out the header 30060Sstevel@tonic-gate * so that the mutable fields are zeroed out. 30070Sstevel@tonic-gate */ 30080Sstevel@tonic-gate ip6h = (ip6_t *)phdr_mp->b_rptr; 30090Sstevel@tonic-gate bzero(ip6h, sizeof (ip6_t)); 30100Sstevel@tonic-gate ip6h->ip6_vcf = IPV6_DEFAULT_VERS_AND_FLOW; 30110Sstevel@tonic-gate 30120Sstevel@tonic-gate if (outbound) { 30130Sstevel@tonic-gate /* 30140Sstevel@tonic-gate * Include the size of AH and authentication data. 30150Sstevel@tonic-gate * This is how our recipient would compute the 30160Sstevel@tonic-gate * authentication data. Look at what we do in the 30170Sstevel@tonic-gate * inbound case below. 30180Sstevel@tonic-gate */ 30190Sstevel@tonic-gate ip6h->ip6_plen = htons(ntohs(oip6h->ip6_plen) + 30200Sstevel@tonic-gate sizeof (ah_t) + ah_align_sz); 30210Sstevel@tonic-gate } else { 30220Sstevel@tonic-gate ip6h->ip6_plen = oip6h->ip6_plen; 30230Sstevel@tonic-gate } 30240Sstevel@tonic-gate 30250Sstevel@tonic-gate ip6h->ip6_src = oip6h->ip6_src; 30260Sstevel@tonic-gate ip6h->ip6_dst = oip6h->ip6_dst; 30270Sstevel@tonic-gate 30280Sstevel@tonic-gate *length_to_skip = IPV6_HDR_LEN; 30290Sstevel@tonic-gate if (option_length == 0) { 30300Sstevel@tonic-gate /* Form the AH header */ 30310Sstevel@tonic-gate ip6h->ip6_nxt = IPPROTO_AH; 30320Sstevel@tonic-gate ((ah_t *)(ip6h + 1))->ah_nexthdr = oip6h->ip6_nxt; 30330Sstevel@tonic-gate ah_offset = *length_to_skip; 30340Sstevel@tonic-gate } else { 30350Sstevel@tonic-gate ip6h->ip6_nxt = oip6h->ip6_nxt; 30360Sstevel@tonic-gate /* option_length does not include the AH header's size */ 30370Sstevel@tonic-gate *length_to_skip += option_length; 30380Sstevel@tonic-gate 30390Sstevel@tonic-gate ah_offset = ah_fix_phdr_v6(ip6h, oip6h, outbound, B_FALSE); 30400Sstevel@tonic-gate if (ah_offset == 0) { 30410Sstevel@tonic-gate ip_drop_packet(phdr_mp, !outbound, NULL, NULL, 30423448Sdh155122 DROPPER(ipss, ipds_ah_bad_v6_hdrs), 30433448Sdh155122 &ahstack->ah_dropper); 30440Sstevel@tonic-gate return (NULL); 30450Sstevel@tonic-gate } 30460Sstevel@tonic-gate } 30470Sstevel@tonic-gate 30480Sstevel@tonic-gate if (!ah_finish_up(((ah_t *)((uint8_t *)ip6h + ah_offset)), 30490Sstevel@tonic-gate (outbound ? NULL : ((ah_t *)((uint8_t *)oip6h + ah_offset))), 30503448Sdh155122 assoc, ah_data_sz, ah_align_sz, ahstack)) { 30510Sstevel@tonic-gate freeb(phdr_mp); 30520Sstevel@tonic-gate /* 30530Sstevel@tonic-gate * Returning NULL will tell the caller to 30540Sstevel@tonic-gate * IPSA_REFELE(), free the memory, etc. 30550Sstevel@tonic-gate */ 30560Sstevel@tonic-gate return (NULL); 30570Sstevel@tonic-gate } 30580Sstevel@tonic-gate 30590Sstevel@tonic-gate phdr_mp->b_wptr = ((uint8_t *)ip6h + ah_offset + sizeof (ah_t) + 30600Sstevel@tonic-gate ah_align_sz); 30610Sstevel@tonic-gate if (!outbound) 30620Sstevel@tonic-gate *length_to_skip += sizeof (ah_t) + ah_align_sz; 30630Sstevel@tonic-gate return (phdr_mp); 30640Sstevel@tonic-gate } 30650Sstevel@tonic-gate 30660Sstevel@tonic-gate /* 30670Sstevel@tonic-gate * This function constructs a pseudo header by looking at the IP header 30680Sstevel@tonic-gate * and options if any. This is called for both outbound and inbound, 30690Sstevel@tonic-gate * before computing the ICV. 30700Sstevel@tonic-gate */ 30710Sstevel@tonic-gate static mblk_t * 30720Sstevel@tonic-gate ah_process_ip_options_v4(mblk_t *mp, ipsa_t *assoc, int *length_to_skip, 30733448Sdh155122 uint_t ah_data_sz, boolean_t outbound, ipsecah_stack_t *ahstack) 30740Sstevel@tonic-gate { 30750Sstevel@tonic-gate ipoptp_t opts; 30760Sstevel@tonic-gate uint32_t option_length; 30770Sstevel@tonic-gate ipha_t *ipha; 30780Sstevel@tonic-gate ipha_t *oipha; 30790Sstevel@tonic-gate mblk_t *phdr_mp; 30800Sstevel@tonic-gate int size; 30810Sstevel@tonic-gate uchar_t *optptr; 30820Sstevel@tonic-gate uint8_t optval; 30830Sstevel@tonic-gate uint8_t optlen; 30840Sstevel@tonic-gate ipaddr_t dst; 30850Sstevel@tonic-gate uint32_t v_hlen_tos_len; 30860Sstevel@tonic-gate int ip_hdr_length; 30870Sstevel@tonic-gate uint_t ah_align_sz; 30880Sstevel@tonic-gate uint32_t off; 30890Sstevel@tonic-gate 30900Sstevel@tonic-gate #ifdef _BIG_ENDIAN 30910Sstevel@tonic-gate #define V_HLEN (v_hlen_tos_len >> 24) 30920Sstevel@tonic-gate #else 30930Sstevel@tonic-gate #define V_HLEN (v_hlen_tos_len & 0xFF) 30940Sstevel@tonic-gate #endif 30950Sstevel@tonic-gate 30960Sstevel@tonic-gate oipha = (ipha_t *)mp->b_rptr; 30970Sstevel@tonic-gate v_hlen_tos_len = ((uint32_t *)oipha)[0]; 30980Sstevel@tonic-gate 30990Sstevel@tonic-gate /* 31000Sstevel@tonic-gate * Allocate space for the authentication data also. It is 31010Sstevel@tonic-gate * useful both during the ICV calculation where we need to 31020Sstevel@tonic-gate * feed in zeroes and while sending the datagram back to IP 31030Sstevel@tonic-gate * where we will be using the same space. 31040Sstevel@tonic-gate * 31050Sstevel@tonic-gate * We need to allocate space for padding bytes if it is not 31060Sstevel@tonic-gate * a multiple of IPV4_PADDING_ALIGN. 31070Sstevel@tonic-gate * 31080Sstevel@tonic-gate * In addition, we allocate space for the ICV computed by 31090Sstevel@tonic-gate * the kernel crypto framework, saving us a separate kmem 31100Sstevel@tonic-gate * allocation down the road. 31110Sstevel@tonic-gate */ 31120Sstevel@tonic-gate 31130Sstevel@tonic-gate ah_align_sz = P2ALIGN(ah_data_sz + IPV4_PADDING_ALIGN - 1, 31140Sstevel@tonic-gate IPV4_PADDING_ALIGN); 31150Sstevel@tonic-gate 31160Sstevel@tonic-gate ASSERT(ah_align_sz >= ah_data_sz); 31170Sstevel@tonic-gate 31180Sstevel@tonic-gate size = IP_SIMPLE_HDR_LENGTH + sizeof (ah_t) + ah_align_sz + 31190Sstevel@tonic-gate ah_data_sz; 31200Sstevel@tonic-gate 31210Sstevel@tonic-gate if (V_HLEN != IP_SIMPLE_HDR_VERSION) { 31220Sstevel@tonic-gate option_length = oipha->ipha_version_and_hdr_length - 31230Sstevel@tonic-gate (uint8_t)((IP_VERSION << 4) + 31240Sstevel@tonic-gate IP_SIMPLE_HDR_LENGTH_IN_WORDS); 31250Sstevel@tonic-gate option_length <<= 2; 31260Sstevel@tonic-gate size += option_length; 31270Sstevel@tonic-gate } 31280Sstevel@tonic-gate 31290Sstevel@tonic-gate if ((phdr_mp = allocb(size, BPRI_HI)) == NULL) { 31300Sstevel@tonic-gate return (NULL); 31310Sstevel@tonic-gate } 31320Sstevel@tonic-gate 31330Sstevel@tonic-gate /* 31340Sstevel@tonic-gate * Form the basic IP header first. 31350Sstevel@tonic-gate */ 31360Sstevel@tonic-gate ipha = (ipha_t *)phdr_mp->b_rptr; 31370Sstevel@tonic-gate ipha->ipha_version_and_hdr_length = oipha->ipha_version_and_hdr_length; 31380Sstevel@tonic-gate ipha->ipha_type_of_service = 0; 31390Sstevel@tonic-gate 31400Sstevel@tonic-gate if (outbound) { 31410Sstevel@tonic-gate /* 31420Sstevel@tonic-gate * Include the size of AH and authentication data. 31430Sstevel@tonic-gate * This is how our recipient would compute the 31440Sstevel@tonic-gate * authentication data. Look at what we do in the 31450Sstevel@tonic-gate * inbound case below. 31460Sstevel@tonic-gate */ 31470Sstevel@tonic-gate ipha->ipha_length = ntohs(htons(oipha->ipha_length) + 31480Sstevel@tonic-gate sizeof (ah_t) + ah_align_sz); 31490Sstevel@tonic-gate } else { 31500Sstevel@tonic-gate ipha->ipha_length = oipha->ipha_length; 31510Sstevel@tonic-gate } 31520Sstevel@tonic-gate 31530Sstevel@tonic-gate ipha->ipha_ident = oipha->ipha_ident; 31540Sstevel@tonic-gate ipha->ipha_fragment_offset_and_flags = 0; 31550Sstevel@tonic-gate ipha->ipha_ttl = 0; 31560Sstevel@tonic-gate ipha->ipha_protocol = IPPROTO_AH; 31570Sstevel@tonic-gate ipha->ipha_hdr_checksum = 0; 31580Sstevel@tonic-gate ipha->ipha_src = oipha->ipha_src; 31590Sstevel@tonic-gate ipha->ipha_dst = dst = oipha->ipha_dst; 31600Sstevel@tonic-gate 31610Sstevel@tonic-gate /* 31620Sstevel@tonic-gate * If there is no option to process return now. 31630Sstevel@tonic-gate */ 31640Sstevel@tonic-gate ip_hdr_length = IP_SIMPLE_HDR_LENGTH; 31650Sstevel@tonic-gate 31660Sstevel@tonic-gate if (V_HLEN == IP_SIMPLE_HDR_VERSION) { 31670Sstevel@tonic-gate /* Form the AH header */ 31680Sstevel@tonic-gate goto ah_hdr; 31690Sstevel@tonic-gate } 31700Sstevel@tonic-gate 31710Sstevel@tonic-gate ip_hdr_length += option_length; 31720Sstevel@tonic-gate 31730Sstevel@tonic-gate /* 31740Sstevel@tonic-gate * We have options. In the outbound case for source route, 31750Sstevel@tonic-gate * ULP has already moved the first hop, which is now in 31760Sstevel@tonic-gate * ipha_dst. We need the final destination for the calculation 31770Sstevel@tonic-gate * of authentication data. And also make sure that mutable 31780Sstevel@tonic-gate * and experimental fields are zeroed out in the IP options. 31790Sstevel@tonic-gate */ 31800Sstevel@tonic-gate 31810Sstevel@tonic-gate bcopy(&oipha[1], &ipha[1], option_length); 31820Sstevel@tonic-gate 31830Sstevel@tonic-gate for (optval = ipoptp_first(&opts, ipha); 31840Sstevel@tonic-gate optval != IPOPT_EOL; 31850Sstevel@tonic-gate optval = ipoptp_next(&opts)) { 31860Sstevel@tonic-gate optptr = opts.ipoptp_cur; 31870Sstevel@tonic-gate optlen = opts.ipoptp_len; 31880Sstevel@tonic-gate switch (optval) { 31890Sstevel@tonic-gate case IPOPT_EXTSEC: 31900Sstevel@tonic-gate case IPOPT_COMSEC: 31910Sstevel@tonic-gate case IPOPT_RA: 31920Sstevel@tonic-gate case IPOPT_SDMDD: 31930Sstevel@tonic-gate case IPOPT_SECURITY: 31940Sstevel@tonic-gate /* 31950Sstevel@tonic-gate * These options are Immutable, leave them as-is. 31960Sstevel@tonic-gate * Note that IPOPT_NOP is also Immutable, but it 31970Sstevel@tonic-gate * was skipped by ipoptp_next() and thus remains 31980Sstevel@tonic-gate * intact in the header. 31990Sstevel@tonic-gate */ 32000Sstevel@tonic-gate break; 32010Sstevel@tonic-gate case IPOPT_SSRR: 32020Sstevel@tonic-gate case IPOPT_LSRR: 32030Sstevel@tonic-gate if ((opts.ipoptp_flags & IPOPTP_ERROR) != 0) 32040Sstevel@tonic-gate goto bad_ipv4opt; 32050Sstevel@tonic-gate /* 32060Sstevel@tonic-gate * These two are mutable and will be zeroed, but 32070Sstevel@tonic-gate * first get the final destination. 32080Sstevel@tonic-gate */ 32090Sstevel@tonic-gate off = optptr[IPOPT_OFFSET]; 32100Sstevel@tonic-gate /* 32110Sstevel@tonic-gate * If one of the conditions is true, it means 32120Sstevel@tonic-gate * end of options and dst already has the right 32130Sstevel@tonic-gate * value. So, just fall through. 32140Sstevel@tonic-gate */ 32150Sstevel@tonic-gate if (!(optlen < IP_ADDR_LEN || off > optlen - 3)) { 32160Sstevel@tonic-gate off = optlen - IP_ADDR_LEN; 32170Sstevel@tonic-gate bcopy(&optptr[off], &dst, IP_ADDR_LEN); 32180Sstevel@tonic-gate } 32190Sstevel@tonic-gate /* FALLTHRU */ 32200Sstevel@tonic-gate case IPOPT_RR: 32210Sstevel@tonic-gate case IPOPT_TS: 32220Sstevel@tonic-gate case IPOPT_SATID: 32230Sstevel@tonic-gate default: 32240Sstevel@tonic-gate /* 32250Sstevel@tonic-gate * optlen should include from the beginning of an 32260Sstevel@tonic-gate * option. 32270Sstevel@tonic-gate * NOTE : Stream Identifier Option (SID): RFC 791 32280Sstevel@tonic-gate * shows the bit pattern of optlen as 2 and documents 32290Sstevel@tonic-gate * the length as 4. We assume it to be 2 here. 32300Sstevel@tonic-gate */ 32310Sstevel@tonic-gate bzero(optptr, optlen); 32320Sstevel@tonic-gate break; 32330Sstevel@tonic-gate } 32340Sstevel@tonic-gate } 32350Sstevel@tonic-gate 32360Sstevel@tonic-gate if ((opts.ipoptp_flags & IPOPTP_ERROR) != 0) { 32370Sstevel@tonic-gate bad_ipv4opt: 32383448Sdh155122 ah1dbg(ahstack, ("AH : bad IPv4 option")); 32390Sstevel@tonic-gate freeb(phdr_mp); 32400Sstevel@tonic-gate return (NULL); 32410Sstevel@tonic-gate } 32420Sstevel@tonic-gate 32430Sstevel@tonic-gate /* 32440Sstevel@tonic-gate * Don't change ipha_dst for an inbound datagram as it points 32450Sstevel@tonic-gate * to the right value. Only for the outbound with LSRR/SSRR, 32460Sstevel@tonic-gate * because of ip_massage_options called by the ULP, ipha_dst 32470Sstevel@tonic-gate * points to the first hop and we need to use the final 32480Sstevel@tonic-gate * destination for computing the ICV. 32490Sstevel@tonic-gate */ 32500Sstevel@tonic-gate 32510Sstevel@tonic-gate if (outbound) 32520Sstevel@tonic-gate ipha->ipha_dst = dst; 32530Sstevel@tonic-gate ah_hdr: 32540Sstevel@tonic-gate ((ah_t *)((uint8_t *)ipha + ip_hdr_length))->ah_nexthdr = 32550Sstevel@tonic-gate oipha->ipha_protocol; 32560Sstevel@tonic-gate if (!ah_finish_up(((ah_t *)((uint8_t *)ipha + ip_hdr_length)), 32570Sstevel@tonic-gate (outbound ? NULL : ((ah_t *)((uint8_t *)oipha + ip_hdr_length))), 32583448Sdh155122 assoc, ah_data_sz, ah_align_sz, ahstack)) { 32590Sstevel@tonic-gate freeb(phdr_mp); 32600Sstevel@tonic-gate /* 32610Sstevel@tonic-gate * Returning NULL will tell the caller to IPSA_REFELE(), free 32620Sstevel@tonic-gate * the memory, etc. 32630Sstevel@tonic-gate */ 32640Sstevel@tonic-gate return (NULL); 32650Sstevel@tonic-gate } 32660Sstevel@tonic-gate 32670Sstevel@tonic-gate phdr_mp->b_wptr = ((uchar_t *)ipha + ip_hdr_length + 32680Sstevel@tonic-gate sizeof (ah_t) + ah_align_sz); 32690Sstevel@tonic-gate 32700Sstevel@tonic-gate ASSERT(phdr_mp->b_wptr <= phdr_mp->b_datap->db_lim); 32710Sstevel@tonic-gate if (outbound) 32720Sstevel@tonic-gate *length_to_skip = ip_hdr_length; 32730Sstevel@tonic-gate else 32740Sstevel@tonic-gate *length_to_skip = ip_hdr_length + sizeof (ah_t) + ah_align_sz; 32750Sstevel@tonic-gate return (phdr_mp); 32760Sstevel@tonic-gate } 32770Sstevel@tonic-gate 32780Sstevel@tonic-gate /* 32790Sstevel@tonic-gate * Authenticate an outbound datagram. This function is called 32800Sstevel@tonic-gate * whenever IP sends an outbound datagram that needs authentication. 32810Sstevel@tonic-gate */ 32820Sstevel@tonic-gate static ipsec_status_t 32830Sstevel@tonic-gate ah_outbound(mblk_t *ipsec_out) 32840Sstevel@tonic-gate { 32850Sstevel@tonic-gate mblk_t *mp; 32860Sstevel@tonic-gate mblk_t *phdr_mp; 32870Sstevel@tonic-gate ipsec_out_t *oi; 32880Sstevel@tonic-gate ipsa_t *assoc; 32890Sstevel@tonic-gate int length_to_skip; 32900Sstevel@tonic-gate uint_t ah_align_sz; 32910Sstevel@tonic-gate uint_t age_bytes; 32923448Sdh155122 netstack_t *ns; 32933448Sdh155122 ipsec_stack_t *ipss; 32943448Sdh155122 ipsecah_stack_t *ahstack; 32950Sstevel@tonic-gate 32960Sstevel@tonic-gate /* 32970Sstevel@tonic-gate * Construct the chain of mblks 32980Sstevel@tonic-gate * 32990Sstevel@tonic-gate * IPSEC_OUT->PSEUDO_HDR->DATA 33000Sstevel@tonic-gate * 33010Sstevel@tonic-gate * one by one. 33020Sstevel@tonic-gate */ 33030Sstevel@tonic-gate 33040Sstevel@tonic-gate ASSERT(ipsec_out->b_datap->db_type == M_CTL); 33050Sstevel@tonic-gate 33060Sstevel@tonic-gate ASSERT(MBLKL(ipsec_out) >= sizeof (ipsec_info_t)); 33070Sstevel@tonic-gate 33080Sstevel@tonic-gate mp = ipsec_out->b_cont; 33090Sstevel@tonic-gate oi = (ipsec_out_t *)ipsec_out->b_rptr; 33103448Sdh155122 ns = oi->ipsec_out_ns; 33113448Sdh155122 ipss = ns->netstack_ipsec; 33123448Sdh155122 ahstack = ns->netstack_ipsecah; 33133448Sdh155122 33143448Sdh155122 AH_BUMP_STAT(ahstack, out_requests); 33150Sstevel@tonic-gate 33160Sstevel@tonic-gate ASSERT(mp->b_datap->db_type == M_DATA); 33170Sstevel@tonic-gate 33180Sstevel@tonic-gate assoc = oi->ipsec_out_ah_sa; 33190Sstevel@tonic-gate ASSERT(assoc != NULL); 33200Sstevel@tonic-gate 33210Sstevel@tonic-gate /* 33220Sstevel@tonic-gate * Age SA according to number of bytes that will be sent after 33230Sstevel@tonic-gate * adding the AH header, ICV, and padding to the packet. 33240Sstevel@tonic-gate */ 33250Sstevel@tonic-gate 33260Sstevel@tonic-gate if (oi->ipsec_out_v4) { 33270Sstevel@tonic-gate ipha_t *ipha = (ipha_t *)mp->b_rptr; 33280Sstevel@tonic-gate ah_align_sz = P2ALIGN(assoc->ipsa_mac_len + 33290Sstevel@tonic-gate IPV4_PADDING_ALIGN - 1, IPV4_PADDING_ALIGN); 33300Sstevel@tonic-gate age_bytes = ntohs(ipha->ipha_length) + sizeof (ah_t) + 33310Sstevel@tonic-gate ah_align_sz; 33320Sstevel@tonic-gate } else { 33330Sstevel@tonic-gate ip6_t *ip6h = (ip6_t *)mp->b_rptr; 33340Sstevel@tonic-gate ah_align_sz = P2ALIGN(assoc->ipsa_mac_len + 33350Sstevel@tonic-gate IPV6_PADDING_ALIGN - 1, IPV6_PADDING_ALIGN); 33360Sstevel@tonic-gate age_bytes = sizeof (ip6_t) + ntohs(ip6h->ip6_plen) + 3337*4987Sdanmcd sizeof (ah_t) + ah_align_sz; 33380Sstevel@tonic-gate } 33390Sstevel@tonic-gate 33400Sstevel@tonic-gate if (!ah_age_bytes(assoc, age_bytes, B_FALSE)) { 33410Sstevel@tonic-gate /* rig things as if ipsec_getassocbyconn() failed */ 33420Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, SL_ERROR | SL_WARN, 33430Sstevel@tonic-gate "AH association 0x%x, dst %s had bytes expire.\n", 33443448Sdh155122 ntohl(assoc->ipsa_spi), assoc->ipsa_dstaddr, AF_INET, 33453448Sdh155122 ahstack->ipsecah_netstack); 33460Sstevel@tonic-gate freemsg(ipsec_out); 33470Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 33480Sstevel@tonic-gate } 33490Sstevel@tonic-gate 33500Sstevel@tonic-gate if (oi->ipsec_out_is_capab_ill) { 33513448Sdh155122 ah3dbg(ahstack, ("ah_outbound: pkt can be accelerated\n")); 33520Sstevel@tonic-gate if (oi->ipsec_out_v4) 33530Sstevel@tonic-gate return (ah_outbound_accelerated_v4(ipsec_out, assoc)); 33540Sstevel@tonic-gate else 33550Sstevel@tonic-gate return (ah_outbound_accelerated_v6(ipsec_out, assoc)); 33560Sstevel@tonic-gate } 33573448Sdh155122 AH_BUMP_STAT(ahstack, noaccel); 33580Sstevel@tonic-gate 33590Sstevel@tonic-gate /* 33600Sstevel@tonic-gate * Insert pseudo header: 33610Sstevel@tonic-gate * IPSEC_INFO -> [IP, ULP] => IPSEC_INFO -> [IP, AH, ICV] -> ULP 33620Sstevel@tonic-gate */ 33630Sstevel@tonic-gate 33640Sstevel@tonic-gate if (oi->ipsec_out_v4) { 33650Sstevel@tonic-gate phdr_mp = ah_process_ip_options_v4(mp, assoc, &length_to_skip, 33663448Sdh155122 assoc->ipsa_mac_len, B_TRUE, ahstack); 33670Sstevel@tonic-gate } else { 33680Sstevel@tonic-gate phdr_mp = ah_process_ip_options_v6(mp, assoc, &length_to_skip, 33693448Sdh155122 assoc->ipsa_mac_len, B_TRUE, ahstack); 33700Sstevel@tonic-gate } 33710Sstevel@tonic-gate 33720Sstevel@tonic-gate if (phdr_mp == NULL) { 33733448Sdh155122 AH_BUMP_STAT(ahstack, out_discards); 33740Sstevel@tonic-gate ip_drop_packet(ipsec_out, B_FALSE, NULL, NULL, 33753448Sdh155122 DROPPER(ipss, ipds_ah_bad_v4_opts), 33763448Sdh155122 &ahstack->ah_dropper); 33770Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 33780Sstevel@tonic-gate } 33790Sstevel@tonic-gate 33800Sstevel@tonic-gate ipsec_out->b_cont = phdr_mp; 33810Sstevel@tonic-gate phdr_mp->b_cont = mp; 33820Sstevel@tonic-gate mp->b_rptr += length_to_skip; 33830Sstevel@tonic-gate 33840Sstevel@tonic-gate /* 33850Sstevel@tonic-gate * At this point ipsec_out points to the IPSEC_OUT, new_mp 33860Sstevel@tonic-gate * points to an mblk containing the pseudo header (IP header, 33870Sstevel@tonic-gate * AH header, and ICV with mutable fields zero'ed out). 33880Sstevel@tonic-gate * mp points to the mblk containing the ULP data. The original 33890Sstevel@tonic-gate * IP header is kept before the ULP data in mp. 33900Sstevel@tonic-gate */ 33910Sstevel@tonic-gate 33920Sstevel@tonic-gate /* submit MAC request to KCF */ 33930Sstevel@tonic-gate return (ah_submit_req_outbound(ipsec_out, length_to_skip, assoc)); 33940Sstevel@tonic-gate } 33950Sstevel@tonic-gate 33960Sstevel@tonic-gate static ipsec_status_t 33970Sstevel@tonic-gate ah_inbound(mblk_t *ipsec_in_mp, void *arg) 33980Sstevel@tonic-gate { 33990Sstevel@tonic-gate mblk_t *data_mp = ipsec_in_mp->b_cont; 34000Sstevel@tonic-gate ipsec_in_t *ii = (ipsec_in_t *)ipsec_in_mp->b_rptr; 34010Sstevel@tonic-gate ah_t *ah = (ah_t *)arg; 34020Sstevel@tonic-gate ipsa_t *assoc = ii->ipsec_in_ah_sa; 34030Sstevel@tonic-gate int length_to_skip; 34040Sstevel@tonic-gate int ah_length; 34050Sstevel@tonic-gate mblk_t *phdr_mp; 34060Sstevel@tonic-gate uint32_t ah_offset; 34073448Sdh155122 netstack_t *ns = ii->ipsec_in_ns; 34083448Sdh155122 ipsecah_stack_t *ahstack = ns->netstack_ipsecah; 34093448Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 34100Sstevel@tonic-gate 34110Sstevel@tonic-gate ASSERT(assoc != NULL); 34120Sstevel@tonic-gate 34130Sstevel@tonic-gate /* 34140Sstevel@tonic-gate * We may wish to check replay in-range-only here as an optimization. 34150Sstevel@tonic-gate * Include the reality check of ipsa->ipsa_replay > 34160Sstevel@tonic-gate * ipsa->ipsa_replay_wsize for times when it's the first N packets, 34170Sstevel@tonic-gate * where N == ipsa->ipsa_replay_wsize. 34180Sstevel@tonic-gate * 34190Sstevel@tonic-gate * Another check that may come here later is the "collision" check. 34200Sstevel@tonic-gate * If legitimate packets flow quickly enough, this won't be a problem, 34210Sstevel@tonic-gate * but collisions may cause authentication algorithm crunching to 34220Sstevel@tonic-gate * take place when it doesn't need to. 34230Sstevel@tonic-gate */ 34240Sstevel@tonic-gate if (!sadb_replay_peek(assoc, ah->ah_replay)) { 34253448Sdh155122 AH_BUMP_STAT(ahstack, replay_early_failures); 34263448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 34270Sstevel@tonic-gate ip_drop_packet(ipsec_in_mp, B_TRUE, NULL, NULL, 34283448Sdh155122 DROPPER(ipss, ipds_ah_early_replay), 34293448Sdh155122 &ahstack->ah_dropper); 34300Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 34310Sstevel@tonic-gate } 34320Sstevel@tonic-gate 34330Sstevel@tonic-gate /* 34340Sstevel@tonic-gate * The offset of the AH header can be computed from its pointer 34350Sstevel@tonic-gate * within the data mblk, which was pulled up until the AH header 34360Sstevel@tonic-gate * by ipsec_inbound_ah_sa() during SA selection. 34370Sstevel@tonic-gate */ 34380Sstevel@tonic-gate ah_offset = (uchar_t *)ah - data_mp->b_rptr; 34390Sstevel@tonic-gate 34400Sstevel@tonic-gate /* 34410Sstevel@tonic-gate * Has this packet already been processed by a hardware 34420Sstevel@tonic-gate * IPsec accelerator? 34430Sstevel@tonic-gate */ 34440Sstevel@tonic-gate if (ii->ipsec_in_accelerated) { 34453448Sdh155122 ah3dbg(ahstack, 34463448Sdh155122 ("ah_inbound_v6: pkt processed by ill=%d isv6=%d\n", 34470Sstevel@tonic-gate ii->ipsec_in_ill_index, !ii->ipsec_in_v4)); 34480Sstevel@tonic-gate return (ah_inbound_accelerated(ipsec_in_mp, ii->ipsec_in_v4, 34490Sstevel@tonic-gate assoc, ah_offset)); 34500Sstevel@tonic-gate } 34513448Sdh155122 AH_BUMP_STAT(ahstack, noaccel); 34520Sstevel@tonic-gate 34530Sstevel@tonic-gate /* 34540Sstevel@tonic-gate * We need to pullup until the ICV before we call 34550Sstevel@tonic-gate * ah_process_ip_options_v6. 34560Sstevel@tonic-gate */ 34570Sstevel@tonic-gate ah_length = (ah->ah_length << 2) + 8; 34580Sstevel@tonic-gate 34590Sstevel@tonic-gate /* 34600Sstevel@tonic-gate * NOTE : If we want to use any field of IP/AH header, you need 34610Sstevel@tonic-gate * to re-assign following the pullup. 34620Sstevel@tonic-gate */ 34630Sstevel@tonic-gate if (((uchar_t *)ah + ah_length) > data_mp->b_wptr) { 34640Sstevel@tonic-gate if (!pullupmsg(data_mp, (uchar_t *)ah + ah_length - 34650Sstevel@tonic-gate data_mp->b_rptr)) { 34663448Sdh155122 (void) ipsec_rl_strlog(ns, info.mi_idnum, 0, 0, 34670Sstevel@tonic-gate SL_WARN | SL_ERROR, 34680Sstevel@tonic-gate "ah_inbound: Small AH header\n"); 34693448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 34700Sstevel@tonic-gate ip_drop_packet(ipsec_in_mp, B_TRUE, NULL, NULL, 34713448Sdh155122 DROPPER(ipss, ipds_ah_nomem), 34723448Sdh155122 &ahstack->ah_dropper); 34730Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 34740Sstevel@tonic-gate } 34750Sstevel@tonic-gate } 34760Sstevel@tonic-gate 34770Sstevel@tonic-gate /* 34780Sstevel@tonic-gate * Insert pseudo header: 34790Sstevel@tonic-gate * IPSEC_INFO -> [IP, ULP] => IPSEC_INFO -> [IP, AH, ICV] -> ULP 34800Sstevel@tonic-gate */ 34810Sstevel@tonic-gate if (ii->ipsec_in_v4) { 34820Sstevel@tonic-gate phdr_mp = ah_process_ip_options_v4(data_mp, assoc, 34833448Sdh155122 &length_to_skip, assoc->ipsa_mac_len, B_FALSE, ahstack); 34840Sstevel@tonic-gate } else { 34850Sstevel@tonic-gate phdr_mp = ah_process_ip_options_v6(data_mp, assoc, 34863448Sdh155122 &length_to_skip, assoc->ipsa_mac_len, B_FALSE, ahstack); 34870Sstevel@tonic-gate } 34880Sstevel@tonic-gate 34890Sstevel@tonic-gate if (phdr_mp == NULL) { 34903448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 34910Sstevel@tonic-gate ip_drop_packet(ipsec_in_mp, B_TRUE, NULL, NULL, 34923448Sdh155122 (ii->ipsec_in_v4 ? 34933448Sdh155122 DROPPER(ipss, ipds_ah_bad_v4_opts) : 34943448Sdh155122 DROPPER(ipss, ipds_ah_bad_v6_hdrs)), 34953448Sdh155122 &ahstack->ah_dropper); 34960Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 34970Sstevel@tonic-gate } 34980Sstevel@tonic-gate 34990Sstevel@tonic-gate ipsec_in_mp->b_cont = phdr_mp; 35000Sstevel@tonic-gate phdr_mp->b_cont = data_mp; 35010Sstevel@tonic-gate data_mp->b_rptr += length_to_skip; 35020Sstevel@tonic-gate 35030Sstevel@tonic-gate /* submit request to KCF */ 35040Sstevel@tonic-gate return (ah_submit_req_inbound(ipsec_in_mp, length_to_skip, ah_offset, 35050Sstevel@tonic-gate assoc)); 35060Sstevel@tonic-gate } 35070Sstevel@tonic-gate 35080Sstevel@tonic-gate /* 35090Sstevel@tonic-gate * ah_inbound_accelerated: 35100Sstevel@tonic-gate * Called from ah_inbound() to process IPsec packets that have been 35110Sstevel@tonic-gate * accelerated by hardware. 35120Sstevel@tonic-gate * 35130Sstevel@tonic-gate * Basically does what ah_auth_in_done() with some changes since 35140Sstevel@tonic-gate * no pseudo-headers are involved, i.e. the passed message is a 35150Sstevel@tonic-gate * IPSEC_INFO->DATA. 35160Sstevel@tonic-gate * 35170Sstevel@tonic-gate * It is assumed that only packets that have been successfully 35180Sstevel@tonic-gate * processed by the adapter come here. 35190Sstevel@tonic-gate * 35200Sstevel@tonic-gate * 1. get algorithm structure corresponding to association 35210Sstevel@tonic-gate * 2. calculate pointers to authentication header and ICV 35220Sstevel@tonic-gate * 3. compare ICV in AH header with ICV in data attributes 35230Sstevel@tonic-gate * 3.1 if different: 35240Sstevel@tonic-gate * 3.1.1 generate error 35250Sstevel@tonic-gate * 3.1.2 discard message 35260Sstevel@tonic-gate * 3.2 if ICV matches: 35270Sstevel@tonic-gate * 3.2.1 check replay 35280Sstevel@tonic-gate * 3.2.2 remove AH header 35290Sstevel@tonic-gate * 3.2.3 age SA byte 35300Sstevel@tonic-gate * 3.2.4 send to IP 35310Sstevel@tonic-gate */ 35320Sstevel@tonic-gate ipsec_status_t 35330Sstevel@tonic-gate ah_inbound_accelerated(mblk_t *ipsec_in, boolean_t isv4, ipsa_t *assoc, 35340Sstevel@tonic-gate uint32_t ah_offset) 35350Sstevel@tonic-gate { 35360Sstevel@tonic-gate mblk_t *mp; 35370Sstevel@tonic-gate ipha_t *ipha; 35380Sstevel@tonic-gate ah_t *ah; 35390Sstevel@tonic-gate ipsec_in_t *ii; 35400Sstevel@tonic-gate uint32_t icv_len; 35410Sstevel@tonic-gate uint32_t align_len; 35420Sstevel@tonic-gate uint32_t age_bytes; 35430Sstevel@tonic-gate ip6_t *ip6h; 35440Sstevel@tonic-gate uint8_t *in_icv; 35450Sstevel@tonic-gate mblk_t *hada_mp; 35460Sstevel@tonic-gate uint32_t next_hdr; 35470Sstevel@tonic-gate da_ipsec_t *hada; 35480Sstevel@tonic-gate kstat_named_t *counter; 35493448Sdh155122 ipsecah_stack_t *ahstack; 35503448Sdh155122 netstack_t *ns; 35513448Sdh155122 ipsec_stack_t *ipss; 35520Sstevel@tonic-gate 35530Sstevel@tonic-gate ii = (ipsec_in_t *)ipsec_in->b_rptr; 35543448Sdh155122 ns = ii->ipsec_in_ns; 35553448Sdh155122 ahstack = ns->netstack_ipsecah; 35563448Sdh155122 ipss = ns->netstack_ipsec; 35573448Sdh155122 35580Sstevel@tonic-gate mp = ipsec_in->b_cont; 35590Sstevel@tonic-gate hada_mp = ii->ipsec_in_da; 35600Sstevel@tonic-gate ASSERT(hada_mp != NULL); 35610Sstevel@tonic-gate hada = (da_ipsec_t *)hada_mp->b_rptr; 35620Sstevel@tonic-gate 35633448Sdh155122 AH_BUMP_STAT(ahstack, in_accelerated); 35643448Sdh155122 35650Sstevel@tonic-gate /* 35660Sstevel@tonic-gate * We only support one level of decapsulation in hardware, so 35670Sstevel@tonic-gate * nuke the pointer. 35680Sstevel@tonic-gate */ 35690Sstevel@tonic-gate ii->ipsec_in_da = NULL; 35700Sstevel@tonic-gate ii->ipsec_in_accelerated = B_FALSE; 35710Sstevel@tonic-gate 35720Sstevel@tonic-gate /* 35730Sstevel@tonic-gate * Extract ICV length from attributes M_CTL and sanity check 35740Sstevel@tonic-gate * its value. We allow the mblk to be smaller than da_ipsec_t 35750Sstevel@tonic-gate * for a small ICV, as long as the entire ICV fits within the mblk. 35760Sstevel@tonic-gate * Also ensures that the ICV length computed by Provider 35770Sstevel@tonic-gate * corresponds to the ICV length of the algorithm specified by the SA. 35780Sstevel@tonic-gate */ 35790Sstevel@tonic-gate icv_len = hada->da_icv_len; 35800Sstevel@tonic-gate if ((icv_len != assoc->ipsa_mac_len) || 35810Sstevel@tonic-gate (icv_len > DA_ICV_MAX_LEN) || (MBLKL(hada_mp) < 3582*4987Sdanmcd (sizeof (da_ipsec_t) - DA_ICV_MAX_LEN + icv_len))) { 35830Sstevel@tonic-gate ah0dbg(("ah_inbound_accelerated: " 35840Sstevel@tonic-gate "ICV len (%u) incorrect or mblk too small (%u)\n", 35850Sstevel@tonic-gate icv_len, (uint32_t)(MBLKL(hada_mp)))); 35863448Sdh155122 counter = DROPPER(ipss, ipds_ah_bad_length); 35870Sstevel@tonic-gate goto ah_in_discard; 35880Sstevel@tonic-gate } 35890Sstevel@tonic-gate ASSERT(icv_len != 0); 35900Sstevel@tonic-gate 35910Sstevel@tonic-gate /* compute the padded AH ICV len */ 35920Sstevel@tonic-gate if (isv4) { 35930Sstevel@tonic-gate ipha = (ipha_t *)mp->b_rptr; 35940Sstevel@tonic-gate align_len = (icv_len + IPV4_PADDING_ALIGN - 1) & 35950Sstevel@tonic-gate -IPV4_PADDING_ALIGN; 35960Sstevel@tonic-gate } else { 35970Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr; 35980Sstevel@tonic-gate align_len = (icv_len + IPV6_PADDING_ALIGN - 1) & 35990Sstevel@tonic-gate -IPV6_PADDING_ALIGN; 36000Sstevel@tonic-gate } 36010Sstevel@tonic-gate 36020Sstevel@tonic-gate ah = (ah_t *)(mp->b_rptr + ah_offset); 36030Sstevel@tonic-gate in_icv = (uint8_t *)ah + sizeof (ah_t); 36040Sstevel@tonic-gate 36050Sstevel@tonic-gate /* compare ICV in AH header vs ICV computed by adapter */ 36060Sstevel@tonic-gate if (bcmp(hada->da_icv, in_icv, icv_len)) { 36070Sstevel@tonic-gate int af; 36080Sstevel@tonic-gate void *addr; 36090Sstevel@tonic-gate 36100Sstevel@tonic-gate if (isv4) { 36110Sstevel@tonic-gate addr = &ipha->ipha_dst; 36120Sstevel@tonic-gate af = AF_INET; 36130Sstevel@tonic-gate } else { 36140Sstevel@tonic-gate addr = &ip6h->ip6_dst; 36150Sstevel@tonic-gate af = AF_INET6; 36160Sstevel@tonic-gate } 36170Sstevel@tonic-gate 36180Sstevel@tonic-gate /* 36190Sstevel@tonic-gate * Log the event. Don't print to the console, block 36200Sstevel@tonic-gate * potential denial-of-service attack. 36210Sstevel@tonic-gate */ 36223448Sdh155122 AH_BUMP_STAT(ahstack, bad_auth); 36230Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, SL_ERROR | SL_WARN, 36240Sstevel@tonic-gate "AH Authentication failed spi %x, dst_addr %s", 36253448Sdh155122 assoc->ipsa_spi, addr, af, ahstack->ipsecah_netstack); 36263448Sdh155122 counter = DROPPER(ipss, ipds_ah_bad_auth); 36270Sstevel@tonic-gate goto ah_in_discard; 36280Sstevel@tonic-gate } 36290Sstevel@tonic-gate 36303448Sdh155122 ah3dbg(ahstack, ("AH succeeded, checking replay\n")); 36313448Sdh155122 AH_BUMP_STAT(ahstack, good_auth); 36320Sstevel@tonic-gate 36330Sstevel@tonic-gate if (!sadb_replay_check(assoc, ah->ah_replay)) { 36340Sstevel@tonic-gate int af; 36350Sstevel@tonic-gate void *addr; 36360Sstevel@tonic-gate 36370Sstevel@tonic-gate if (isv4) { 36380Sstevel@tonic-gate addr = &ipha->ipha_dst; 36390Sstevel@tonic-gate af = AF_INET; 36400Sstevel@tonic-gate } else { 36410Sstevel@tonic-gate addr = &ip6h->ip6_dst; 36420Sstevel@tonic-gate af = AF_INET6; 36430Sstevel@tonic-gate } 36440Sstevel@tonic-gate 36450Sstevel@tonic-gate /* 36460Sstevel@tonic-gate * Log the event. As of now we print out an event. 36470Sstevel@tonic-gate * Do not print the replay failure number, or else 36480Sstevel@tonic-gate * syslog cannot collate the error messages. Printing 36490Sstevel@tonic-gate * the replay number that failed (or printing to the 36500Sstevel@tonic-gate * console) opens a denial-of-service attack. 36510Sstevel@tonic-gate */ 36523448Sdh155122 AH_BUMP_STAT(ahstack, replay_failures); 36530Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, 36540Sstevel@tonic-gate SL_ERROR | SL_WARN, 36550Sstevel@tonic-gate "Replay failed for AH spi %x, dst_addr %s", 36563448Sdh155122 assoc->ipsa_spi, addr, af, ahstack->ipsecah_netstack); 36573448Sdh155122 counter = DROPPER(ipss, ipds_ah_replay); 36580Sstevel@tonic-gate goto ah_in_discard; 36590Sstevel@tonic-gate } 36600Sstevel@tonic-gate 36610Sstevel@tonic-gate /* 36620Sstevel@tonic-gate * Remove AH header. We do this by copying everything before 36630Sstevel@tonic-gate * the AH header onto the AH header+ICV. 36640Sstevel@tonic-gate */ 36650Sstevel@tonic-gate /* overwrite AH with what was preceeding it (IP header) */ 36660Sstevel@tonic-gate next_hdr = ah->ah_nexthdr; 36670Sstevel@tonic-gate ovbcopy(mp->b_rptr, mp->b_rptr + sizeof (ah_t) + align_len, 36680Sstevel@tonic-gate ah_offset); 36690Sstevel@tonic-gate mp->b_rptr += sizeof (ah_t) + align_len; 36700Sstevel@tonic-gate if (isv4) { 36710Sstevel@tonic-gate /* adjust IP header next protocol */ 36720Sstevel@tonic-gate ipha = (ipha_t *)mp->b_rptr; 36730Sstevel@tonic-gate ipha->ipha_protocol = next_hdr; 36740Sstevel@tonic-gate 36750Sstevel@tonic-gate age_bytes = ipha->ipha_length; 36760Sstevel@tonic-gate 36770Sstevel@tonic-gate /* adjust length in IP header */ 36780Sstevel@tonic-gate ipha->ipha_length -= (sizeof (ah_t) + align_len); 36790Sstevel@tonic-gate 36800Sstevel@tonic-gate /* recalculate checksum */ 36810Sstevel@tonic-gate ipha->ipha_hdr_checksum = 0; 36820Sstevel@tonic-gate ipha->ipha_hdr_checksum = (uint16_t)ip_csum_hdr(ipha); 36830Sstevel@tonic-gate } else { 36840Sstevel@tonic-gate /* adjust IP header next protocol */ 36850Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr; 36860Sstevel@tonic-gate ip6h->ip6_nxt = next_hdr; 36870Sstevel@tonic-gate 36880Sstevel@tonic-gate age_bytes = sizeof (ip6_t) + ntohs(ip6h->ip6_plen) + 36890Sstevel@tonic-gate sizeof (ah_t); 36900Sstevel@tonic-gate 36910Sstevel@tonic-gate /* adjust length in IP header */ 36920Sstevel@tonic-gate ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) - 36930Sstevel@tonic-gate (sizeof (ah_t) + align_len)); 36940Sstevel@tonic-gate } 36950Sstevel@tonic-gate 36960Sstevel@tonic-gate /* age SA */ 36970Sstevel@tonic-gate if (!ah_age_bytes(assoc, age_bytes, B_TRUE)) { 36980Sstevel@tonic-gate /* The ipsa has hit hard expiration, LOG and AUDIT. */ 36990Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, 37000Sstevel@tonic-gate SL_ERROR | SL_WARN, 37010Sstevel@tonic-gate "AH Association 0x%x, dst %s had bytes expire.\n", 37020Sstevel@tonic-gate assoc->ipsa_spi, assoc->ipsa_dstaddr, 37033448Sdh155122 AF_INET, ahstack->ipsecah_netstack); 37043448Sdh155122 AH_BUMP_STAT(ahstack, bytes_expired); 37053448Sdh155122 counter = DROPPER(ipss, ipds_ah_bytes_expire); 37060Sstevel@tonic-gate goto ah_in_discard; 37070Sstevel@tonic-gate } 37080Sstevel@tonic-gate 37090Sstevel@tonic-gate freeb(hada_mp); 37100Sstevel@tonic-gate return (IPSEC_STATUS_SUCCESS); 37110Sstevel@tonic-gate 37120Sstevel@tonic-gate ah_in_discard: 37133448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 37140Sstevel@tonic-gate freeb(hada_mp); 37153448Sdh155122 ip_drop_packet(ipsec_in, B_TRUE, NULL, NULL, counter, 37163448Sdh155122 &ahstack->ah_dropper); 37170Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 37180Sstevel@tonic-gate } 37190Sstevel@tonic-gate 37200Sstevel@tonic-gate /* 37210Sstevel@tonic-gate * ah_outbound_accelerated_v4: 37220Sstevel@tonic-gate * Called from ah_outbound_v4() and once it is determined that the 37230Sstevel@tonic-gate * packet is elligible for hardware acceleration. 37240Sstevel@tonic-gate * 37250Sstevel@tonic-gate * We proceed as follows: 37260Sstevel@tonic-gate * 1. allocate and initialize attributes mblk 37270Sstevel@tonic-gate * 2. mark IPSEC_OUT to indicate that pkt is accelerated 37280Sstevel@tonic-gate * 3. insert AH header 37290Sstevel@tonic-gate */ 37300Sstevel@tonic-gate static ipsec_status_t 37310Sstevel@tonic-gate ah_outbound_accelerated_v4(mblk_t *ipsec_mp, ipsa_t *assoc) 37320Sstevel@tonic-gate { 37330Sstevel@tonic-gate mblk_t *mp, *new_mp; 37340Sstevel@tonic-gate ipsec_out_t *oi; 37350Sstevel@tonic-gate uint_t ah_data_sz; /* ICV length, algorithm dependent */ 37360Sstevel@tonic-gate uint_t ah_align_sz; /* ICV length + padding */ 37370Sstevel@tonic-gate uint32_t v_hlen_tos_len; /* from original IP header */ 37380Sstevel@tonic-gate ipha_t *oipha; /* original IP header */ 37390Sstevel@tonic-gate ipha_t *nipha; /* new IP header */ 37400Sstevel@tonic-gate uint_t option_length = 0; 37410Sstevel@tonic-gate uint_t new_hdr_len; /* new header length */ 37420Sstevel@tonic-gate uint_t iphdr_length; 37430Sstevel@tonic-gate ah_t *ah_hdr; /* ptr to AH header */ 37443448Sdh155122 netstack_t *ns; 37453448Sdh155122 ipsec_stack_t *ipss; 37463448Sdh155122 ipsecah_stack_t *ahstack; 37470Sstevel@tonic-gate 37480Sstevel@tonic-gate oi = (ipsec_out_t *)ipsec_mp->b_rptr; 37493448Sdh155122 ns = oi->ipsec_out_ns; 37503448Sdh155122 ipss = ns->netstack_ipsec; 37513448Sdh155122 ahstack = ns->netstack_ipsecah; 37523448Sdh155122 37530Sstevel@tonic-gate mp = ipsec_mp->b_cont; 37540Sstevel@tonic-gate 37553448Sdh155122 AH_BUMP_STAT(ahstack, out_accelerated); 37563448Sdh155122 37570Sstevel@tonic-gate oipha = (ipha_t *)mp->b_rptr; 37580Sstevel@tonic-gate v_hlen_tos_len = ((uint32_t *)oipha)[0]; 37590Sstevel@tonic-gate 37600Sstevel@tonic-gate /* mark packet as being accelerated in IPSEC_OUT */ 37610Sstevel@tonic-gate ASSERT(oi->ipsec_out_accelerated == B_FALSE); 37620Sstevel@tonic-gate oi->ipsec_out_accelerated = B_TRUE; 37630Sstevel@tonic-gate 37640Sstevel@tonic-gate /* calculate authentication data length, i.e. ICV + padding */ 37650Sstevel@tonic-gate ah_data_sz = assoc->ipsa_mac_len; 37660Sstevel@tonic-gate ah_align_sz = (ah_data_sz + IPV4_PADDING_ALIGN - 1) & 37670Sstevel@tonic-gate -IPV4_PADDING_ALIGN; 37680Sstevel@tonic-gate 37690Sstevel@tonic-gate /* 37700Sstevel@tonic-gate * Insert pseudo header: 37710Sstevel@tonic-gate * IPSEC_INFO -> [IP, ULP] => IPSEC_INFO -> [IP, AH, ICV] -> ULP 37720Sstevel@tonic-gate */ 37730Sstevel@tonic-gate 37740Sstevel@tonic-gate /* IP + AH + authentication + padding data length */ 37750Sstevel@tonic-gate new_hdr_len = IP_SIMPLE_HDR_LENGTH + sizeof (ah_t) + ah_align_sz; 37760Sstevel@tonic-gate if (V_HLEN != IP_SIMPLE_HDR_VERSION) { 37770Sstevel@tonic-gate option_length = oipha->ipha_version_and_hdr_length - 37780Sstevel@tonic-gate (uint8_t)((IP_VERSION << 4) + 37790Sstevel@tonic-gate IP_SIMPLE_HDR_LENGTH_IN_WORDS); 37800Sstevel@tonic-gate option_length <<= 2; 37810Sstevel@tonic-gate new_hdr_len += option_length; 37820Sstevel@tonic-gate } 37830Sstevel@tonic-gate 37840Sstevel@tonic-gate /* allocate pseudo-header mblk */ 37850Sstevel@tonic-gate if ((new_mp = allocb(new_hdr_len, BPRI_HI)) == NULL) { 37860Sstevel@tonic-gate /* IPsec kstats: bump bean counter here */ 37870Sstevel@tonic-gate ip_drop_packet(ipsec_mp, B_FALSE, NULL, NULL, 37883448Sdh155122 DROPPER(ipss, ipds_ah_nomem), 37893448Sdh155122 &ahstack->ah_dropper); 37900Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 37910Sstevel@tonic-gate } 37920Sstevel@tonic-gate 37930Sstevel@tonic-gate new_mp->b_cont = mp; 37940Sstevel@tonic-gate ipsec_mp->b_cont = new_mp; 37950Sstevel@tonic-gate new_mp->b_wptr += new_hdr_len; 37960Sstevel@tonic-gate 37970Sstevel@tonic-gate /* copy original IP header to new header */ 37980Sstevel@tonic-gate bcopy(mp->b_rptr, new_mp->b_rptr, IP_SIMPLE_HDR_LENGTH + 37990Sstevel@tonic-gate option_length); 38000Sstevel@tonic-gate 38010Sstevel@tonic-gate /* update IP header */ 38020Sstevel@tonic-gate nipha = (ipha_t *)new_mp->b_rptr; 38030Sstevel@tonic-gate nipha->ipha_protocol = IPPROTO_AH; 38040Sstevel@tonic-gate iphdr_length = ntohs(nipha->ipha_length); 38050Sstevel@tonic-gate iphdr_length += sizeof (ah_t) + ah_align_sz; 38060Sstevel@tonic-gate nipha->ipha_length = htons(iphdr_length); 38070Sstevel@tonic-gate nipha->ipha_hdr_checksum = 0; 38080Sstevel@tonic-gate nipha->ipha_hdr_checksum = (uint16_t)ip_csum_hdr(nipha); 38090Sstevel@tonic-gate 38100Sstevel@tonic-gate /* skip original IP header in mp */ 38110Sstevel@tonic-gate mp->b_rptr += IP_SIMPLE_HDR_LENGTH + option_length; 38120Sstevel@tonic-gate 38130Sstevel@tonic-gate /* initialize AH header */ 38140Sstevel@tonic-gate ah_hdr = (ah_t *)(new_mp->b_rptr + IP_SIMPLE_HDR_LENGTH + 38150Sstevel@tonic-gate option_length); 38160Sstevel@tonic-gate ah_hdr->ah_nexthdr = oipha->ipha_protocol; 38173448Sdh155122 if (!ah_finish_up(ah_hdr, NULL, assoc, ah_data_sz, ah_align_sz, 38183448Sdh155122 ahstack)) { 38190Sstevel@tonic-gate /* Only way this fails is if outbound replay counter wraps. */ 38200Sstevel@tonic-gate ip_drop_packet(ipsec_mp, B_FALSE, NULL, NULL, 38213448Sdh155122 DROPPER(ipss, ipds_ah_replay), 38223448Sdh155122 &ahstack->ah_dropper); 38230Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 38240Sstevel@tonic-gate } 38250Sstevel@tonic-gate 38260Sstevel@tonic-gate return (IPSEC_STATUS_SUCCESS); 38270Sstevel@tonic-gate } 38280Sstevel@tonic-gate 38290Sstevel@tonic-gate /* 38300Sstevel@tonic-gate * ah_outbound_accelerated_v6: 38310Sstevel@tonic-gate * 38320Sstevel@tonic-gate * Called from ah_outbound_v6() once it is determined that the packet 38330Sstevel@tonic-gate * is eligible for hardware acceleration. 38340Sstevel@tonic-gate * 38350Sstevel@tonic-gate * We proceed as follows: 38360Sstevel@tonic-gate * 1. allocate and initialize attributes mblk 38370Sstevel@tonic-gate * 2. mark IPSEC_OUT to indicate that pkt is accelerated 38380Sstevel@tonic-gate * 3. insert AH header 38390Sstevel@tonic-gate */ 38400Sstevel@tonic-gate static ipsec_status_t 38410Sstevel@tonic-gate ah_outbound_accelerated_v6(mblk_t *ipsec_mp, ipsa_t *assoc) 38420Sstevel@tonic-gate { 38430Sstevel@tonic-gate mblk_t *mp, *phdr_mp; 38440Sstevel@tonic-gate ipsec_out_t *oi; 38450Sstevel@tonic-gate uint_t ah_data_sz; /* ICV length, algorithm dependent */ 38460Sstevel@tonic-gate uint_t ah_align_sz; /* ICV length + padding */ 38470Sstevel@tonic-gate ip6_t *oip6h; /* original IP header */ 38480Sstevel@tonic-gate ip6_t *ip6h; /* new IP header */ 38490Sstevel@tonic-gate uint_t option_length = 0; 38500Sstevel@tonic-gate uint_t hdr_size; 38510Sstevel@tonic-gate uint_t ah_offset; 38520Sstevel@tonic-gate ah_t *ah_hdr; /* ptr to AH header */ 38533448Sdh155122 netstack_t *ns; 38543448Sdh155122 ipsec_stack_t *ipss; 38553448Sdh155122 ipsecah_stack_t *ahstack; 38560Sstevel@tonic-gate 38570Sstevel@tonic-gate oi = (ipsec_out_t *)ipsec_mp->b_rptr; 38583448Sdh155122 ns = oi->ipsec_out_ns; 38593448Sdh155122 ipss = ns->netstack_ipsec; 38603448Sdh155122 ahstack = ns->netstack_ipsecah; 38613448Sdh155122 38620Sstevel@tonic-gate mp = ipsec_mp->b_cont; 38630Sstevel@tonic-gate 38643448Sdh155122 AH_BUMP_STAT(ahstack, out_accelerated); 38653448Sdh155122 38660Sstevel@tonic-gate oip6h = (ip6_t *)mp->b_rptr; 38670Sstevel@tonic-gate 38680Sstevel@tonic-gate /* mark packet as being accelerated in IPSEC_OUT */ 38690Sstevel@tonic-gate ASSERT(oi->ipsec_out_accelerated == B_FALSE); 38700Sstevel@tonic-gate oi->ipsec_out_accelerated = B_TRUE; 38710Sstevel@tonic-gate 38720Sstevel@tonic-gate /* calculate authentication data length, i.e. ICV + padding */ 38730Sstevel@tonic-gate ah_data_sz = assoc->ipsa_mac_len; 38740Sstevel@tonic-gate ah_align_sz = (ah_data_sz + IPV4_PADDING_ALIGN - 1) & 38750Sstevel@tonic-gate -IPV4_PADDING_ALIGN; 38760Sstevel@tonic-gate 38770Sstevel@tonic-gate ASSERT(ah_align_sz >= ah_data_sz); 38780Sstevel@tonic-gate 38790Sstevel@tonic-gate hdr_size = ipsec_ah_get_hdr_size_v6(mp, B_FALSE); 38800Sstevel@tonic-gate option_length = hdr_size - IPV6_HDR_LEN; 38810Sstevel@tonic-gate 38820Sstevel@tonic-gate /* This was not included in ipsec_ah_get_hdr_size_v6() */ 38830Sstevel@tonic-gate hdr_size += (sizeof (ah_t) + ah_align_sz); 38840Sstevel@tonic-gate 38850Sstevel@tonic-gate if ((phdr_mp = allocb(hdr_size, BPRI_HI)) == NULL) { 38863448Sdh155122 ip_drop_packet(ipsec_mp, B_FALSE, NULL, NULL, 38873448Sdh155122 DROPPER(ipss, ipds_ah_nomem), 38883448Sdh155122 &ahstack->ah_dropper); 38890Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 38900Sstevel@tonic-gate } 38910Sstevel@tonic-gate phdr_mp->b_wptr += hdr_size; 38920Sstevel@tonic-gate 38930Sstevel@tonic-gate /* 38940Sstevel@tonic-gate * Form the basic IP header first. We always assign every bit 38950Sstevel@tonic-gate * of the v6 basic header, so a separate bzero is unneeded. 38960Sstevel@tonic-gate */ 38970Sstevel@tonic-gate ip6h = (ip6_t *)phdr_mp->b_rptr; 38980Sstevel@tonic-gate ip6h->ip6_vcf = oip6h->ip6_vcf; 38990Sstevel@tonic-gate ip6h->ip6_hlim = oip6h->ip6_hlim; 39000Sstevel@tonic-gate ip6h->ip6_src = oip6h->ip6_src; 39010Sstevel@tonic-gate ip6h->ip6_dst = oip6h->ip6_dst; 39020Sstevel@tonic-gate /* 39030Sstevel@tonic-gate * Include the size of AH and authentication data. 39040Sstevel@tonic-gate * This is how our recipient would compute the 39050Sstevel@tonic-gate * authentication data. Look at what we do in the 39060Sstevel@tonic-gate * inbound case below. 39070Sstevel@tonic-gate */ 39080Sstevel@tonic-gate ip6h->ip6_plen = htons(ntohs(oip6h->ip6_plen) + sizeof (ah_t) + 39090Sstevel@tonic-gate ah_align_sz); 39100Sstevel@tonic-gate 39110Sstevel@tonic-gate /* 39120Sstevel@tonic-gate * Insert pseudo header: 39130Sstevel@tonic-gate * IPSEC_INFO -> [IP6, LLH, ULP] => 39140Sstevel@tonic-gate * IPSEC_INFO -> [IP, LLH, AH, ICV] -> ULP 39150Sstevel@tonic-gate */ 39160Sstevel@tonic-gate 39170Sstevel@tonic-gate if (option_length == 0) { 39180Sstevel@tonic-gate /* Form the AH header */ 39190Sstevel@tonic-gate ip6h->ip6_nxt = IPPROTO_AH; 39200Sstevel@tonic-gate ((ah_t *)(ip6h + 1))->ah_nexthdr = oip6h->ip6_nxt; 39210Sstevel@tonic-gate ah_offset = IPV6_HDR_LEN; 39220Sstevel@tonic-gate } else { 39230Sstevel@tonic-gate ip6h->ip6_nxt = oip6h->ip6_nxt; 39240Sstevel@tonic-gate /* option_length does not include the AH header's size */ 39250Sstevel@tonic-gate ah_offset = ah_fix_phdr_v6(ip6h, oip6h, B_TRUE, B_FALSE); 39260Sstevel@tonic-gate if (ah_offset == 0) { 39270Sstevel@tonic-gate freemsg(phdr_mp); 39280Sstevel@tonic-gate ip_drop_packet(ipsec_mp, B_FALSE, NULL, NULL, 39293448Sdh155122 DROPPER(ipss, ipds_ah_bad_v6_hdrs), 39303448Sdh155122 &ahstack->ah_dropper); 39310Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 39320Sstevel@tonic-gate } 39330Sstevel@tonic-gate } 39340Sstevel@tonic-gate 39350Sstevel@tonic-gate phdr_mp->b_cont = mp; 39360Sstevel@tonic-gate ipsec_mp->b_cont = phdr_mp; 39370Sstevel@tonic-gate 39380Sstevel@tonic-gate /* skip original IP header in mp */ 39390Sstevel@tonic-gate mp->b_rptr += IPV6_HDR_LEN + option_length; 39400Sstevel@tonic-gate 39410Sstevel@tonic-gate /* initialize AH header */ 39420Sstevel@tonic-gate ah_hdr = (ah_t *)(phdr_mp->b_rptr + IPV6_HDR_LEN + option_length); 39430Sstevel@tonic-gate ah_hdr->ah_nexthdr = oip6h->ip6_nxt; 39440Sstevel@tonic-gate 39450Sstevel@tonic-gate if (!ah_finish_up(((ah_t *)((uint8_t *)ip6h + ah_offset)), NULL, 39463448Sdh155122 assoc, ah_data_sz, ah_align_sz, ahstack)) { 39470Sstevel@tonic-gate /* Only way this fails is if outbound replay counter wraps. */ 39480Sstevel@tonic-gate ip_drop_packet(ipsec_mp, B_FALSE, NULL, NULL, 39493448Sdh155122 DROPPER(ipss, ipds_ah_replay), 39503448Sdh155122 &ahstack->ah_dropper); 39510Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 39520Sstevel@tonic-gate } 39530Sstevel@tonic-gate 39540Sstevel@tonic-gate return (IPSEC_STATUS_SUCCESS); 39550Sstevel@tonic-gate } 39560Sstevel@tonic-gate 39570Sstevel@tonic-gate /* 39580Sstevel@tonic-gate * Invoked after processing of an inbound packet by the 39590Sstevel@tonic-gate * kernel crypto framework. Called by ah_submit_req() for a sync request, 39600Sstevel@tonic-gate * or by the kcf callback for an async request. 39610Sstevel@tonic-gate * Returns IPSEC_STATUS_SUCCESS on success, IPSEC_STATUS_FAILED on failure. 39620Sstevel@tonic-gate * On failure, the mblk chain ipsec_in is freed by this function. 39630Sstevel@tonic-gate */ 39640Sstevel@tonic-gate static ipsec_status_t 39650Sstevel@tonic-gate ah_auth_in_done(mblk_t *ipsec_in) 39660Sstevel@tonic-gate { 39670Sstevel@tonic-gate mblk_t *phdr_mp; 39680Sstevel@tonic-gate ipha_t *ipha; 39690Sstevel@tonic-gate uint_t ah_offset = 0; 39700Sstevel@tonic-gate mblk_t *mp; 39713192Sdanmcd int align_len, newpos; 39720Sstevel@tonic-gate ah_t *ah; 39730Sstevel@tonic-gate uint32_t length; 39743192Sdanmcd uint32_t *dest32; 39753192Sdanmcd uint8_t *dest; 39760Sstevel@tonic-gate ipsec_in_t *ii; 39770Sstevel@tonic-gate boolean_t isv4; 39780Sstevel@tonic-gate ip6_t *ip6h; 39790Sstevel@tonic-gate uint_t icv_len; 39800Sstevel@tonic-gate ipsa_t *assoc; 39810Sstevel@tonic-gate kstat_named_t *counter; 39823448Sdh155122 netstack_t *ns; 39833448Sdh155122 ipsecah_stack_t *ahstack; 39843448Sdh155122 ipsec_stack_t *ipss; 39850Sstevel@tonic-gate 39860Sstevel@tonic-gate ii = (ipsec_in_t *)ipsec_in->b_rptr; 39873448Sdh155122 ns = ii->ipsec_in_ns; 39883448Sdh155122 ahstack = ns->netstack_ipsecah; 39893448Sdh155122 ipss = ns->netstack_ipsec; 39903448Sdh155122 39910Sstevel@tonic-gate isv4 = ii->ipsec_in_v4; 39920Sstevel@tonic-gate assoc = ii->ipsec_in_ah_sa; 39930Sstevel@tonic-gate icv_len = (uint_t)ii->ipsec_in_crypto_mac.cd_raw.iov_len; 39940Sstevel@tonic-gate 39950Sstevel@tonic-gate phdr_mp = ipsec_in->b_cont; 39960Sstevel@tonic-gate if (phdr_mp == NULL) { 39973448Sdh155122 ip_drop_packet(ipsec_in, B_TRUE, NULL, NULL, 39983448Sdh155122 DROPPER(ipss, ipds_ah_nomem), 39993448Sdh155122 &ahstack->ah_dropper); 40000Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 40010Sstevel@tonic-gate } 40020Sstevel@tonic-gate 40030Sstevel@tonic-gate mp = phdr_mp->b_cont; 40040Sstevel@tonic-gate if (mp == NULL) { 40053448Sdh155122 ip_drop_packet(ipsec_in, B_TRUE, NULL, NULL, 40063448Sdh155122 DROPPER(ipss, ipds_ah_nomem), 40073448Sdh155122 &ahstack->ah_dropper); 40080Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 40090Sstevel@tonic-gate } 40100Sstevel@tonic-gate mp->b_rptr -= ii->ipsec_in_skip_len; 40110Sstevel@tonic-gate 4012*4987Sdanmcd ah_set_usetime(assoc, B_TRUE); 4013*4987Sdanmcd 40140Sstevel@tonic-gate if (isv4) { 40150Sstevel@tonic-gate ipha = (ipha_t *)mp->b_rptr; 40160Sstevel@tonic-gate ah_offset = ipha->ipha_version_and_hdr_length - 40170Sstevel@tonic-gate (uint8_t)((IP_VERSION << 4)); 40180Sstevel@tonic-gate ah_offset <<= 2; 40190Sstevel@tonic-gate align_len = P2ALIGN(icv_len + IPV4_PADDING_ALIGN - 1, 40200Sstevel@tonic-gate IPV4_PADDING_ALIGN); 40210Sstevel@tonic-gate } else { 40220Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr; 40230Sstevel@tonic-gate ah_offset = ipsec_ah_get_hdr_size_v6(mp, B_TRUE); 40240Sstevel@tonic-gate ASSERT((mp->b_wptr - mp->b_rptr) >= ah_offset); 40250Sstevel@tonic-gate align_len = P2ALIGN(icv_len + IPV6_PADDING_ALIGN - 1, 40260Sstevel@tonic-gate IPV6_PADDING_ALIGN); 40270Sstevel@tonic-gate } 40280Sstevel@tonic-gate 40290Sstevel@tonic-gate ah = (ah_t *)(mp->b_rptr + ah_offset); 40303192Sdanmcd newpos = sizeof (ah_t) + align_len; 40310Sstevel@tonic-gate 40320Sstevel@tonic-gate /* 40330Sstevel@tonic-gate * We get here only when authentication passed. 40340Sstevel@tonic-gate */ 40350Sstevel@tonic-gate 40363448Sdh155122 ah3dbg(ahstack, ("AH succeeded, checking replay\n")); 40373448Sdh155122 AH_BUMP_STAT(ahstack, good_auth); 40380Sstevel@tonic-gate 40390Sstevel@tonic-gate if (!sadb_replay_check(assoc, ah->ah_replay)) { 40400Sstevel@tonic-gate int af; 40410Sstevel@tonic-gate void *addr; 40420Sstevel@tonic-gate 40430Sstevel@tonic-gate if (isv4) { 40440Sstevel@tonic-gate addr = &ipha->ipha_dst; 40450Sstevel@tonic-gate af = AF_INET; 40460Sstevel@tonic-gate } else { 40470Sstevel@tonic-gate addr = &ip6h->ip6_dst; 40480Sstevel@tonic-gate af = AF_INET6; 40490Sstevel@tonic-gate } 40500Sstevel@tonic-gate 40510Sstevel@tonic-gate /* 40520Sstevel@tonic-gate * Log the event. As of now we print out an event. 40530Sstevel@tonic-gate * Do not print the replay failure number, or else 40540Sstevel@tonic-gate * syslog cannot collate the error messages. Printing 40550Sstevel@tonic-gate * the replay number that failed (or printing to the 40560Sstevel@tonic-gate * console) opens a denial-of-service attack. 40570Sstevel@tonic-gate */ 40583448Sdh155122 AH_BUMP_STAT(ahstack, replay_failures); 40590Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, 40600Sstevel@tonic-gate SL_ERROR | SL_WARN, 40610Sstevel@tonic-gate "Replay failed for AH spi %x, dst_addr %s", 40623448Sdh155122 assoc->ipsa_spi, addr, af, ahstack->ipsecah_netstack); 40633448Sdh155122 counter = DROPPER(ipss, ipds_ah_replay); 40640Sstevel@tonic-gate goto ah_in_discard; 40650Sstevel@tonic-gate } 40660Sstevel@tonic-gate 40670Sstevel@tonic-gate /* 40680Sstevel@tonic-gate * We need to remove the AH header from the original 40693192Sdanmcd * datagram. Best way to do this is to move the pre-AH headers 40703192Sdanmcd * forward in the (relatively simple) IPv4 case. In IPv6, it's 40713192Sdanmcd * a bit more complicated because of IPv6's next-header chaining, 40723192Sdanmcd * but it's doable. 40730Sstevel@tonic-gate */ 40740Sstevel@tonic-gate if (isv4) { 40750Sstevel@tonic-gate /* 40760Sstevel@tonic-gate * Assign the right protocol, adjust the length as we 40770Sstevel@tonic-gate * are removing the AH header and adjust the checksum to 40780Sstevel@tonic-gate * account for the protocol and length. 40790Sstevel@tonic-gate */ 40803192Sdanmcd length = ntohs(ipha->ipha_length); 40810Sstevel@tonic-gate if (!ah_age_bytes(assoc, length, B_TRUE)) { 40820Sstevel@tonic-gate /* The ipsa has hit hard expiration, LOG and AUDIT. */ 40830Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, 40840Sstevel@tonic-gate SL_ERROR | SL_WARN, 40850Sstevel@tonic-gate "AH Association 0x%x, dst %s had bytes expire.\n", 40860Sstevel@tonic-gate assoc->ipsa_spi, assoc->ipsa_dstaddr, 40873448Sdh155122 AF_INET, ahstack->ipsecah_netstack); 40883448Sdh155122 AH_BUMP_STAT(ahstack, bytes_expired); 40893448Sdh155122 counter = DROPPER(ipss, ipds_ah_bytes_expire); 40900Sstevel@tonic-gate goto ah_in_discard; 40910Sstevel@tonic-gate } 40923192Sdanmcd ipha->ipha_protocol = ah->ah_nexthdr; 40933192Sdanmcd length -= newpos; 40943192Sdanmcd 40953192Sdanmcd ipha->ipha_length = htons((uint16_t)length); 40963192Sdanmcd ipha->ipha_hdr_checksum = 0; 40973192Sdanmcd ipha->ipha_hdr_checksum = (uint16_t)ip_csum_hdr(ipha); 40980Sstevel@tonic-gate } else { 40990Sstevel@tonic-gate uchar_t *whereptr; 41000Sstevel@tonic-gate int hdrlen; 41010Sstevel@tonic-gate uint8_t *nexthdr; 41020Sstevel@tonic-gate ip6_hbh_t *hbhhdr; 41030Sstevel@tonic-gate ip6_dest_t *dsthdr; 41040Sstevel@tonic-gate ip6_rthdr0_t *rthdr; 41050Sstevel@tonic-gate 41060Sstevel@tonic-gate /* 41070Sstevel@tonic-gate * Make phdr_mp hold until the AH header and make 41080Sstevel@tonic-gate * mp hold everything past AH header. 41090Sstevel@tonic-gate */ 41103192Sdanmcd length = ntohs(ip6h->ip6_plen); 41110Sstevel@tonic-gate if (!ah_age_bytes(assoc, length + sizeof (ip6_t), B_TRUE)) { 41120Sstevel@tonic-gate /* The ipsa has hit hard expiration, LOG and AUDIT. */ 41130Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, 41140Sstevel@tonic-gate SL_ERROR | SL_WARN, 41150Sstevel@tonic-gate "AH Association 0x%x, dst %s had bytes " 41160Sstevel@tonic-gate "expire.\n", assoc->ipsa_spi, &ip6h->ip6_dst, 41173448Sdh155122 AF_INET6, ahstack->ipsecah_netstack); 41183448Sdh155122 AH_BUMP_STAT(ahstack, bytes_expired); 41193448Sdh155122 counter = DROPPER(ipss, ipds_ah_bytes_expire); 41200Sstevel@tonic-gate goto ah_in_discard; 41210Sstevel@tonic-gate } 41220Sstevel@tonic-gate 41230Sstevel@tonic-gate /* 41240Sstevel@tonic-gate * Update the next header field of the header preceding 41250Sstevel@tonic-gate * AH with the next header field of AH. Start with the 41260Sstevel@tonic-gate * IPv6 header and proceed with the extension headers 41270Sstevel@tonic-gate * until we find what we're looking for. 41280Sstevel@tonic-gate */ 41293192Sdanmcd nexthdr = &ip6h->ip6_nxt; 41303192Sdanmcd whereptr = (uchar_t *)ip6h; 41310Sstevel@tonic-gate hdrlen = sizeof (ip6_t); 41320Sstevel@tonic-gate 41330Sstevel@tonic-gate while (*nexthdr != IPPROTO_AH) { 41340Sstevel@tonic-gate whereptr += hdrlen; 41350Sstevel@tonic-gate /* Assume IP has already stripped it */ 41360Sstevel@tonic-gate ASSERT(*nexthdr != IPPROTO_FRAGMENT && 41370Sstevel@tonic-gate *nexthdr != IPPROTO_RAW); 41380Sstevel@tonic-gate switch (*nexthdr) { 41390Sstevel@tonic-gate case IPPROTO_HOPOPTS: 41400Sstevel@tonic-gate hbhhdr = (ip6_hbh_t *)whereptr; 41410Sstevel@tonic-gate nexthdr = &hbhhdr->ip6h_nxt; 41420Sstevel@tonic-gate hdrlen = 8 * (hbhhdr->ip6h_len + 1); 41430Sstevel@tonic-gate break; 41440Sstevel@tonic-gate case IPPROTO_DSTOPTS: 41450Sstevel@tonic-gate dsthdr = (ip6_dest_t *)whereptr; 41460Sstevel@tonic-gate nexthdr = &dsthdr->ip6d_nxt; 41470Sstevel@tonic-gate hdrlen = 8 * (dsthdr->ip6d_len + 1); 41480Sstevel@tonic-gate break; 41490Sstevel@tonic-gate case IPPROTO_ROUTING: 41500Sstevel@tonic-gate rthdr = (ip6_rthdr0_t *)whereptr; 41510Sstevel@tonic-gate nexthdr = &rthdr->ip6r0_nxt; 41520Sstevel@tonic-gate hdrlen = 8 * (rthdr->ip6r0_len + 1); 41530Sstevel@tonic-gate break; 41540Sstevel@tonic-gate } 41550Sstevel@tonic-gate } 41560Sstevel@tonic-gate *nexthdr = ah->ah_nexthdr; 41573192Sdanmcd length -= newpos; 41583192Sdanmcd ip6h->ip6_plen = htons((uint16_t)length); 41590Sstevel@tonic-gate } 41600Sstevel@tonic-gate 41613192Sdanmcd /* Now that we've fixed the IP header, move it forward. */ 41623192Sdanmcd mp->b_rptr += newpos; 41633192Sdanmcd if (IS_P2ALIGNED(mp->b_rptr, sizeof (uint32_t))) { 41643192Sdanmcd dest32 = (uint32_t *)(mp->b_rptr + ah_offset); 41653192Sdanmcd while (--dest32 >= (uint32_t *)mp->b_rptr) 41663192Sdanmcd *dest32 = *(dest32 - (newpos >> 2)); 41673192Sdanmcd } else { 41683192Sdanmcd dest = mp->b_rptr + ah_offset; 41693192Sdanmcd while (--dest >= mp->b_rptr) 41703192Sdanmcd *dest = *(dest - newpos); 41713192Sdanmcd } 41723192Sdanmcd freeb(phdr_mp); 41733192Sdanmcd ipsec_in->b_cont = mp; 41743192Sdanmcd 41751676Sjpk if (is_system_labeled()) { 41761676Sjpk /* 41771676Sjpk * inherit the label by setting it in the new ip header 41781676Sjpk */ 41793192Sdanmcd mblk_setcred(mp, DB_CRED(mp)); 41801676Sjpk } 41810Sstevel@tonic-gate return (IPSEC_STATUS_SUCCESS); 41820Sstevel@tonic-gate 41830Sstevel@tonic-gate ah_in_discard: 41843448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 41853448Sdh155122 ip_drop_packet(ipsec_in, B_TRUE, NULL, NULL, counter, 41863448Sdh155122 &ahstack->ah_dropper); 41870Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 41880Sstevel@tonic-gate } 41890Sstevel@tonic-gate 41900Sstevel@tonic-gate /* 41910Sstevel@tonic-gate * Invoked after processing of an outbound packet by the 41920Sstevel@tonic-gate * kernel crypto framework, either by ah_submit_req() for a request 41930Sstevel@tonic-gate * executed syncrhonously, or by the KEF callback for a request 41940Sstevel@tonic-gate * executed asynchronously. 41950Sstevel@tonic-gate */ 41960Sstevel@tonic-gate static ipsec_status_t 41970Sstevel@tonic-gate ah_auth_out_done(mblk_t *ipsec_out) 41980Sstevel@tonic-gate { 41990Sstevel@tonic-gate mblk_t *phdr_mp; 42000Sstevel@tonic-gate mblk_t *mp; 42010Sstevel@tonic-gate int align_len; 42020Sstevel@tonic-gate uint32_t hdrs_length; 42030Sstevel@tonic-gate uchar_t *ptr; 42040Sstevel@tonic-gate uint32_t length; 42050Sstevel@tonic-gate boolean_t isv4; 42060Sstevel@tonic-gate ipsec_out_t *io; 42070Sstevel@tonic-gate size_t icv_len; 42083448Sdh155122 netstack_t *ns; 42093448Sdh155122 ipsec_stack_t *ipss; 42103448Sdh155122 ipsecah_stack_t *ahstack; 42110Sstevel@tonic-gate 42120Sstevel@tonic-gate io = (ipsec_out_t *)ipsec_out->b_rptr; 42133448Sdh155122 ns = io->ipsec_out_ns; 42143448Sdh155122 ipss = ns->netstack_ipsec; 42153448Sdh155122 ahstack = ns->netstack_ipsecah; 42163448Sdh155122 42170Sstevel@tonic-gate isv4 = io->ipsec_out_v4; 42180Sstevel@tonic-gate icv_len = io->ipsec_out_crypto_mac.cd_raw.iov_len; 42190Sstevel@tonic-gate 42200Sstevel@tonic-gate phdr_mp = ipsec_out->b_cont; 42210Sstevel@tonic-gate if (phdr_mp == NULL) { 42220Sstevel@tonic-gate ip_drop_packet(ipsec_out, B_FALSE, NULL, NULL, 42233448Sdh155122 DROPPER(ipss, ipds_ah_nomem), 42243448Sdh155122 &ahstack->ah_dropper); 42250Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 42260Sstevel@tonic-gate } 42270Sstevel@tonic-gate 42280Sstevel@tonic-gate mp = phdr_mp->b_cont; 42290Sstevel@tonic-gate if (mp == NULL) { 42300Sstevel@tonic-gate ip_drop_packet(ipsec_out, B_FALSE, NULL, NULL, 42313448Sdh155122 DROPPER(ipss, ipds_ah_nomem), 42323448Sdh155122 &ahstack->ah_dropper); 42330Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 42340Sstevel@tonic-gate } 42350Sstevel@tonic-gate mp->b_rptr -= io->ipsec_out_skip_len; 42360Sstevel@tonic-gate 4237*4987Sdanmcd ASSERT(io->ipsec_out_ah_sa != NULL); 4238*4987Sdanmcd ah_set_usetime(io->ipsec_out_ah_sa, B_FALSE); 4239*4987Sdanmcd 42400Sstevel@tonic-gate if (isv4) { 42410Sstevel@tonic-gate ipha_t *ipha; 42420Sstevel@tonic-gate ipha_t *nipha; 42430Sstevel@tonic-gate 42440Sstevel@tonic-gate ipha = (ipha_t *)mp->b_rptr; 42450Sstevel@tonic-gate hdrs_length = ipha->ipha_version_and_hdr_length - 42460Sstevel@tonic-gate (uint8_t)((IP_VERSION << 4)); 42470Sstevel@tonic-gate hdrs_length <<= 2; 42480Sstevel@tonic-gate align_len = P2ALIGN(icv_len + IPV4_PADDING_ALIGN - 1, 42490Sstevel@tonic-gate IPV4_PADDING_ALIGN); 42500Sstevel@tonic-gate /* 42510Sstevel@tonic-gate * phdr_mp must have the right amount of space for the 42520Sstevel@tonic-gate * combined IP and AH header. Copy the IP header and 42530Sstevel@tonic-gate * the ack_data onto AH. Note that the AH header was 42540Sstevel@tonic-gate * already formed before the ICV calculation and hence 42550Sstevel@tonic-gate * you don't have to copy it here. 42560Sstevel@tonic-gate */ 42570Sstevel@tonic-gate bcopy(mp->b_rptr, phdr_mp->b_rptr, hdrs_length); 42580Sstevel@tonic-gate 42590Sstevel@tonic-gate ptr = phdr_mp->b_rptr + hdrs_length + sizeof (ah_t); 42600Sstevel@tonic-gate bcopy(phdr_mp->b_wptr, ptr, icv_len); 42610Sstevel@tonic-gate 42620Sstevel@tonic-gate /* 42630Sstevel@tonic-gate * Compute the new header checksum as we are assigning 42640Sstevel@tonic-gate * IPPROTO_AH and adjusting the length here. 42650Sstevel@tonic-gate */ 42660Sstevel@tonic-gate nipha = (ipha_t *)phdr_mp->b_rptr; 42670Sstevel@tonic-gate 42680Sstevel@tonic-gate nipha->ipha_protocol = IPPROTO_AH; 42690Sstevel@tonic-gate length = ntohs(nipha->ipha_length); 42700Sstevel@tonic-gate length += (sizeof (ah_t) + align_len); 42710Sstevel@tonic-gate nipha->ipha_length = htons((uint16_t)length); 42720Sstevel@tonic-gate nipha->ipha_hdr_checksum = 0; 42730Sstevel@tonic-gate nipha->ipha_hdr_checksum = (uint16_t)ip_csum_hdr(nipha); 42740Sstevel@tonic-gate } else { 42750Sstevel@tonic-gate ip6_t *ip6h; 42760Sstevel@tonic-gate ip6_t *nip6h; 42770Sstevel@tonic-gate uint_t ah_offset; 42780Sstevel@tonic-gate 42790Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr; 42800Sstevel@tonic-gate nip6h = (ip6_t *)phdr_mp->b_rptr; 42810Sstevel@tonic-gate align_len = P2ALIGN(icv_len + IPV6_PADDING_ALIGN - 1, 42820Sstevel@tonic-gate IPV6_PADDING_ALIGN); 42830Sstevel@tonic-gate /* 42840Sstevel@tonic-gate * phdr_mp must have the right amount of space for the 42850Sstevel@tonic-gate * combined IP and AH header. Copy the IP header with 42860Sstevel@tonic-gate * options into the pseudo header. When we constructed 42870Sstevel@tonic-gate * a pseudo header, we did not copy some of the mutable 42880Sstevel@tonic-gate * fields. We do it now by calling ah_fix_phdr_v6() 42890Sstevel@tonic-gate * with the last argument B_TRUE. It returns the 42900Sstevel@tonic-gate * ah_offset into the pseudo header. 42910Sstevel@tonic-gate */ 42920Sstevel@tonic-gate 42930Sstevel@tonic-gate bcopy(ip6h, nip6h, IPV6_HDR_LEN); 42940Sstevel@tonic-gate ah_offset = ah_fix_phdr_v6(nip6h, ip6h, B_TRUE, B_TRUE); 42950Sstevel@tonic-gate ASSERT(ah_offset != 0); 42960Sstevel@tonic-gate /* 42970Sstevel@tonic-gate * phdr_mp can hold exactly the whole IP header with options 42980Sstevel@tonic-gate * plus the AH header also. Thus subtracting the AH header's 42990Sstevel@tonic-gate * size should give exactly how much of the original header 43000Sstevel@tonic-gate * should be skipped. 43010Sstevel@tonic-gate */ 43020Sstevel@tonic-gate hdrs_length = (phdr_mp->b_wptr - phdr_mp->b_rptr) - 43030Sstevel@tonic-gate sizeof (ah_t) - icv_len; 43040Sstevel@tonic-gate bcopy(phdr_mp->b_wptr, ((uint8_t *)nip6h + ah_offset + 43050Sstevel@tonic-gate sizeof (ah_t)), icv_len); 43060Sstevel@tonic-gate length = ntohs(nip6h->ip6_plen); 43070Sstevel@tonic-gate length += (sizeof (ah_t) + align_len); 43080Sstevel@tonic-gate nip6h->ip6_plen = htons((uint16_t)length); 43090Sstevel@tonic-gate } 43100Sstevel@tonic-gate 43111676Sjpk if (is_system_labeled()) { 43121676Sjpk /* 43131676Sjpk * inherit the label by setting it in the new ip header 43141676Sjpk */ 43151676Sjpk mblk_setcred(phdr_mp, DB_CRED(mp)); 43161676Sjpk } 43171676Sjpk 43180Sstevel@tonic-gate /* Skip the original IP header */ 43190Sstevel@tonic-gate mp->b_rptr += hdrs_length; 43200Sstevel@tonic-gate if (mp->b_rptr == mp->b_wptr) { 43210Sstevel@tonic-gate phdr_mp->b_cont = mp->b_cont; 43220Sstevel@tonic-gate freeb(mp); 43230Sstevel@tonic-gate } 43240Sstevel@tonic-gate 43250Sstevel@tonic-gate return (IPSEC_STATUS_SUCCESS); 43260Sstevel@tonic-gate } 43270Sstevel@tonic-gate 43280Sstevel@tonic-gate /* 43290Sstevel@tonic-gate * Wrapper to allow IP to trigger an AH association failure message 43300Sstevel@tonic-gate * during SA inbound selection. 43310Sstevel@tonic-gate */ 43320Sstevel@tonic-gate void 43330Sstevel@tonic-gate ipsecah_in_assocfailure(mblk_t *mp, char level, ushort_t sl, char *fmt, 43343448Sdh155122 uint32_t spi, void *addr, int af, ipsecah_stack_t *ahstack) 43350Sstevel@tonic-gate { 43363448Sdh155122 ipsec_stack_t *ipss = ahstack->ipsecah_netstack->netstack_ipsec; 43373448Sdh155122 43383448Sdh155122 if (ahstack->ipsecah_log_unknown_spi) { 43390Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, level, sl, fmt, spi, 43403448Sdh155122 addr, af, ahstack->ipsecah_netstack); 43410Sstevel@tonic-gate } 43420Sstevel@tonic-gate 43433448Sdh155122 ip_drop_packet(mp, B_TRUE, NULL, NULL, 43443448Sdh155122 DROPPER(ipss, ipds_ah_no_sa), 43453448Sdh155122 &ahstack->ah_dropper); 43460Sstevel@tonic-gate } 43470Sstevel@tonic-gate 43480Sstevel@tonic-gate /* 43490Sstevel@tonic-gate * Initialize the AH input and output processing functions. 43500Sstevel@tonic-gate */ 43510Sstevel@tonic-gate void 43520Sstevel@tonic-gate ipsecah_init_funcs(ipsa_t *sa) 43530Sstevel@tonic-gate { 43540Sstevel@tonic-gate if (sa->ipsa_output_func == NULL) 43550Sstevel@tonic-gate sa->ipsa_output_func = ah_outbound; 43560Sstevel@tonic-gate if (sa->ipsa_input_func == NULL) 43570Sstevel@tonic-gate sa->ipsa_input_func = ah_inbound; 43580Sstevel@tonic-gate } 4359