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 /* 228704Sdanmcd@sun.com * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate #include <sys/types.h> 270Sstevel@tonic-gate #include <sys/stream.h> 280Sstevel@tonic-gate #include <sys/stropts.h> 290Sstevel@tonic-gate #include <sys/errno.h> 300Sstevel@tonic-gate #include <sys/strlog.h> 310Sstevel@tonic-gate #include <sys/tihdr.h> 320Sstevel@tonic-gate #include <sys/socket.h> 330Sstevel@tonic-gate #include <sys/ddi.h> 340Sstevel@tonic-gate #include <sys/sunddi.h> 357749SThejaswini.Singarajipura@Sun.COM #include <sys/mkdev.h> 360Sstevel@tonic-gate #include <sys/kmem.h> 373448Sdh155122 #include <sys/zone.h> 383192Sdanmcd #include <sys/sysmacros.h> 390Sstevel@tonic-gate #include <sys/cmn_err.h> 400Sstevel@tonic-gate #include <sys/vtrace.h> 410Sstevel@tonic-gate #include <sys/debug.h> 420Sstevel@tonic-gate #include <sys/atomic.h> 430Sstevel@tonic-gate #include <sys/strsun.h> 440Sstevel@tonic-gate #include <sys/random.h> 450Sstevel@tonic-gate #include <netinet/in.h> 460Sstevel@tonic-gate #include <net/if.h> 470Sstevel@tonic-gate #include <netinet/ip6.h> 480Sstevel@tonic-gate #include <netinet/icmp6.h> 490Sstevel@tonic-gate #include <net/pfkeyv2.h> 5010824SMark.Fenwick@Sun.COM #include <net/pfpolicy.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 73*10934Ssommerfeld@sun.com #include <sys/tsol/tnet.h> 74*10934Ssommerfeld@sun.com 750Sstevel@tonic-gate /* 760Sstevel@tonic-gate * Table of ND variables supported by ipsecah. These are loaded into 770Sstevel@tonic-gate * ipsecah_g_nd in ipsecah_init_nd. 780Sstevel@tonic-gate * All of these are alterable, within the min/max values given, at run time. 790Sstevel@tonic-gate */ 803448Sdh155122 static ipsecahparam_t lcl_param_arr[] = { 810Sstevel@tonic-gate /* min max value name */ 820Sstevel@tonic-gate { 0, 3, 0, "ipsecah_debug"}, 830Sstevel@tonic-gate { 125, 32000, SADB_AGE_INTERVAL_DEFAULT, "ipsecah_age_interval"}, 840Sstevel@tonic-gate { 1, 10, 1, "ipsecah_reap_delay"}, 850Sstevel@tonic-gate { 1, SADB_MAX_REPLAY, 64, "ipsecah_replay_size"}, 860Sstevel@tonic-gate { 1, 300, 15, "ipsecah_acquire_timeout"}, 870Sstevel@tonic-gate { 1, 1800, 90, "ipsecah_larval_timeout"}, 880Sstevel@tonic-gate /* Default lifetime values for ACQUIRE messages. */ 890Sstevel@tonic-gate { 0, 0xffffffffU, 0, "ipsecah_default_soft_bytes"}, 900Sstevel@tonic-gate { 0, 0xffffffffU, 0, "ipsecah_default_hard_bytes"}, 910Sstevel@tonic-gate { 0, 0xffffffffU, 24000, "ipsecah_default_soft_addtime"}, 920Sstevel@tonic-gate { 0, 0xffffffffU, 28800, "ipsecah_default_hard_addtime"}, 930Sstevel@tonic-gate { 0, 0xffffffffU, 0, "ipsecah_default_soft_usetime"}, 940Sstevel@tonic-gate { 0, 0xffffffffU, 0, "ipsecah_default_hard_usetime"}, 950Sstevel@tonic-gate { 0, 1, 0, "ipsecah_log_unknown_spi"}, 960Sstevel@tonic-gate }; 973448Sdh155122 #define ipsecah_debug ipsecah_params[0].ipsecah_param_value 983448Sdh155122 #define ipsecah_age_interval ipsecah_params[1].ipsecah_param_value 993448Sdh155122 #define ipsecah_age_int_max ipsecah_params[1].ipsecah_param_max 1003448Sdh155122 #define ipsecah_reap_delay ipsecah_params[2].ipsecah_param_value 1013448Sdh155122 #define ipsecah_replay_size ipsecah_params[3].ipsecah_param_value 1023448Sdh155122 #define ipsecah_acquire_timeout ipsecah_params[4].ipsecah_param_value 1033448Sdh155122 #define ipsecah_larval_timeout ipsecah_params[5].ipsecah_param_value 1043448Sdh155122 #define ipsecah_default_soft_bytes ipsecah_params[6].ipsecah_param_value 1053448Sdh155122 #define ipsecah_default_hard_bytes ipsecah_params[7].ipsecah_param_value 1063448Sdh155122 #define ipsecah_default_soft_addtime ipsecah_params[8].ipsecah_param_value 1073448Sdh155122 #define ipsecah_default_hard_addtime ipsecah_params[9].ipsecah_param_value 1083448Sdh155122 #define ipsecah_default_soft_usetime ipsecah_params[10].ipsecah_param_value 1093448Sdh155122 #define ipsecah_default_hard_usetime ipsecah_params[11].ipsecah_param_value 1103448Sdh155122 #define ipsecah_log_unknown_spi ipsecah_params[12].ipsecah_param_value 1110Sstevel@tonic-gate 1120Sstevel@tonic-gate #define ah0dbg(a) printf a 1130Sstevel@tonic-gate /* NOTE: != 0 instead of > 0 so lint doesn't complain. */ 1143448Sdh155122 #define ah1dbg(ahstack, a) if (ahstack->ipsecah_debug != 0) printf a 1153448Sdh155122 #define ah2dbg(ahstack, a) if (ahstack->ipsecah_debug > 1) printf a 1163448Sdh155122 #define ah3dbg(ahstack, a) if (ahstack->ipsecah_debug > 2) printf a 1170Sstevel@tonic-gate 1180Sstevel@tonic-gate /* 1190Sstevel@tonic-gate * XXX This is broken. Padding should be determined dynamically 1200Sstevel@tonic-gate * depending on the ICV size and IP version number so that the 1210Sstevel@tonic-gate * total AH header size is a multiple of 32 bits or 64 bits 1220Sstevel@tonic-gate * for V4 and V6 respectively. For 96bit ICVs we have no problems. 1230Sstevel@tonic-gate * Anything different from that, we need to fix our code. 1240Sstevel@tonic-gate */ 1250Sstevel@tonic-gate #define IPV4_PADDING_ALIGN 0x04 /* Multiple of 32 bits */ 1260Sstevel@tonic-gate #define IPV6_PADDING_ALIGN 0x04 /* Multiple of 32 bits */ 1270Sstevel@tonic-gate 1280Sstevel@tonic-gate /* 1290Sstevel@tonic-gate * Helper macro. Avoids a call to msgdsize if there is only one 1300Sstevel@tonic-gate * mblk in the chain. 1310Sstevel@tonic-gate */ 1320Sstevel@tonic-gate #define AH_MSGSIZE(mp) ((mp)->b_cont != NULL ? msgdsize(mp) : MBLKL(mp)) 1330Sstevel@tonic-gate 1343448Sdh155122 1350Sstevel@tonic-gate static ipsec_status_t ah_auth_out_done(mblk_t *); 1360Sstevel@tonic-gate static ipsec_status_t ah_auth_in_done(mblk_t *); 1370Sstevel@tonic-gate static mblk_t *ah_process_ip_options_v4(mblk_t *, ipsa_t *, int *, uint_t, 1383448Sdh155122 boolean_t, ipsecah_stack_t *); 1390Sstevel@tonic-gate static mblk_t *ah_process_ip_options_v6(mblk_t *, ipsa_t *, int *, uint_t, 1403448Sdh155122 boolean_t, ipsecah_stack_t *); 1413448Sdh155122 static void ah_getspi(mblk_t *, keysock_in_t *, ipsecah_stack_t *); 1420Sstevel@tonic-gate static ipsec_status_t ah_inbound_accelerated(mblk_t *, boolean_t, ipsa_t *, 1430Sstevel@tonic-gate uint32_t); 1440Sstevel@tonic-gate static ipsec_status_t ah_outbound_accelerated_v4(mblk_t *, ipsa_t *); 1450Sstevel@tonic-gate static ipsec_status_t ah_outbound_accelerated_v6(mblk_t *, ipsa_t *); 1460Sstevel@tonic-gate static ipsec_status_t ah_outbound(mblk_t *); 1470Sstevel@tonic-gate 1480Sstevel@tonic-gate static int ipsecah_open(queue_t *, dev_t *, int, int, cred_t *); 1490Sstevel@tonic-gate static int ipsecah_close(queue_t *); 1500Sstevel@tonic-gate static void ipsecah_rput(queue_t *, mblk_t *); 1510Sstevel@tonic-gate static void ipsecah_wput(queue_t *, mblk_t *); 1523448Sdh155122 static void ah_send_acquire(ipsacq_t *, mblk_t *, netstack_t *); 153*10934Ssommerfeld@sun.com static boolean_t ah_register_out(uint32_t, uint32_t, uint_t, ipsecah_stack_t *, 154*10934Ssommerfeld@sun.com mblk_t *); 1553448Sdh155122 static void *ipsecah_stack_init(netstackid_t stackid, netstack_t *ns); 1563448Sdh155122 static void ipsecah_stack_fini(netstackid_t stackid, void *arg); 1573448Sdh155122 1588392SHuafeng.Lv@Sun.COM extern void (*cl_inet_getspi)(netstackid_t, uint8_t, uint8_t *, size_t, 1598392SHuafeng.Lv@Sun.COM void *); 1607749SThejaswini.Singarajipura@Sun.COM 1613448Sdh155122 /* Setable in /etc/system */ 1623448Sdh155122 uint32_t ah_hash_size = IPSEC_DEFAULT_HASH_SIZE; 1633448Sdh155122 1643448Sdh155122 static taskq_t *ah_taskq; 1650Sstevel@tonic-gate 1660Sstevel@tonic-gate static struct module_info info = { 1670Sstevel@tonic-gate 5136, "ipsecah", 0, INFPSZ, 65536, 1024 1680Sstevel@tonic-gate }; 1690Sstevel@tonic-gate 1700Sstevel@tonic-gate static struct qinit rinit = { 1710Sstevel@tonic-gate (pfi_t)ipsecah_rput, NULL, ipsecah_open, ipsecah_close, NULL, &info, 1720Sstevel@tonic-gate NULL 1730Sstevel@tonic-gate }; 1740Sstevel@tonic-gate 1750Sstevel@tonic-gate static struct qinit winit = { 1760Sstevel@tonic-gate (pfi_t)ipsecah_wput, NULL, ipsecah_open, ipsecah_close, NULL, &info, 1770Sstevel@tonic-gate NULL 1780Sstevel@tonic-gate }; 1790Sstevel@tonic-gate 1800Sstevel@tonic-gate struct streamtab ipsecahinfo = { 1810Sstevel@tonic-gate &rinit, &winit, NULL, NULL 1820Sstevel@tonic-gate }; 1830Sstevel@tonic-gate 1840Sstevel@tonic-gate static int ah_kstat_update(kstat_t *, int); 1850Sstevel@tonic-gate 1861659Smarkfen uint64_t ipsacq_maxpackets = IPSACQ_MAXPACKETS; 1871659Smarkfen 1880Sstevel@tonic-gate static boolean_t 1893448Sdh155122 ah_kstat_init(ipsecah_stack_t *ahstack, netstackid_t stackid) 1900Sstevel@tonic-gate { 1913448Sdh155122 ipsec_stack_t *ipss = ahstack->ipsecah_netstack->netstack_ipsec; 1923448Sdh155122 1933448Sdh155122 ahstack->ah_ksp = kstat_create_netstack("ipsecah", 0, "ah_stat", "net", 1943448Sdh155122 KSTAT_TYPE_NAMED, sizeof (ah_kstats_t) / sizeof (kstat_named_t), 1953448Sdh155122 KSTAT_FLAG_PERSISTENT, stackid); 1963448Sdh155122 1973448Sdh155122 if (ahstack->ah_ksp == NULL || ahstack->ah_ksp->ks_data == NULL) 1980Sstevel@tonic-gate return (B_FALSE); 1990Sstevel@tonic-gate 2003448Sdh155122 ahstack->ah_kstats = ahstack->ah_ksp->ks_data; 2013448Sdh155122 2023448Sdh155122 ahstack->ah_ksp->ks_update = ah_kstat_update; 2033448Sdh155122 ahstack->ah_ksp->ks_private = (void *)(uintptr_t)stackid; 2040Sstevel@tonic-gate 2050Sstevel@tonic-gate #define K64 KSTAT_DATA_UINT64 2063448Sdh155122 #define KI(x) kstat_named_init(&(ahstack->ah_kstats->ah_stat_##x), #x, K64) 2070Sstevel@tonic-gate 2080Sstevel@tonic-gate KI(num_aalgs); 2090Sstevel@tonic-gate KI(good_auth); 2100Sstevel@tonic-gate KI(bad_auth); 2110Sstevel@tonic-gate KI(replay_failures); 2120Sstevel@tonic-gate KI(replay_early_failures); 2130Sstevel@tonic-gate KI(keysock_in); 2140Sstevel@tonic-gate KI(out_requests); 2150Sstevel@tonic-gate KI(acquire_requests); 2160Sstevel@tonic-gate KI(bytes_expired); 2170Sstevel@tonic-gate KI(out_discards); 2180Sstevel@tonic-gate KI(in_accelerated); 2190Sstevel@tonic-gate KI(out_accelerated); 2200Sstevel@tonic-gate KI(noaccel); 2210Sstevel@tonic-gate KI(crypto_sync); 2220Sstevel@tonic-gate KI(crypto_async); 2230Sstevel@tonic-gate KI(crypto_failures); 2240Sstevel@tonic-gate 2250Sstevel@tonic-gate #undef KI 2260Sstevel@tonic-gate #undef K64 2270Sstevel@tonic-gate 2283448Sdh155122 kstat_install(ahstack->ah_ksp); 2293448Sdh155122 IP_ACQUIRE_STAT(ipss, maxpackets, ipsacq_maxpackets); 2300Sstevel@tonic-gate return (B_TRUE); 2310Sstevel@tonic-gate } 2320Sstevel@tonic-gate 2330Sstevel@tonic-gate static int 2340Sstevel@tonic-gate ah_kstat_update(kstat_t *kp, int rw) 2350Sstevel@tonic-gate { 2363448Sdh155122 ah_kstats_t *ekp; 2373448Sdh155122 netstackid_t stackid = (netstackid_t)(uintptr_t)kp->ks_private; 2383448Sdh155122 netstack_t *ns; 2393448Sdh155122 ipsec_stack_t *ipss; 2400Sstevel@tonic-gate 2410Sstevel@tonic-gate if ((kp == NULL) || (kp->ks_data == NULL)) 2420Sstevel@tonic-gate return (EIO); 2430Sstevel@tonic-gate 2440Sstevel@tonic-gate if (rw == KSTAT_WRITE) 2450Sstevel@tonic-gate return (EACCES); 2460Sstevel@tonic-gate 2473448Sdh155122 ns = netstack_find_by_stackid(stackid); 2483448Sdh155122 if (ns == NULL) 2493448Sdh155122 return (-1); 2503448Sdh155122 ipss = ns->netstack_ipsec; 2513448Sdh155122 if (ipss == NULL) { 2523448Sdh155122 netstack_rele(ns); 2533448Sdh155122 return (-1); 2543448Sdh155122 } 2550Sstevel@tonic-gate ekp = (ah_kstats_t *)kp->ks_data; 2563448Sdh155122 2573448Sdh155122 mutex_enter(&ipss->ipsec_alg_lock); 2583448Sdh155122 ekp->ah_stat_num_aalgs.value.ui64 = ipss->ipsec_nalgs[IPSEC_ALG_AUTH]; 2593448Sdh155122 mutex_exit(&ipss->ipsec_alg_lock); 2603448Sdh155122 2613448Sdh155122 netstack_rele(ns); 2620Sstevel@tonic-gate return (0); 2630Sstevel@tonic-gate } 2640Sstevel@tonic-gate 2650Sstevel@tonic-gate /* 2660Sstevel@tonic-gate * Don't have to lock ipsec_age_interval, as only one thread will access it at 2670Sstevel@tonic-gate * a time, because I control the one function that does a qtimeout() on 2680Sstevel@tonic-gate * ah_pfkey_q. 2690Sstevel@tonic-gate */ 2700Sstevel@tonic-gate static void 2713448Sdh155122 ah_ager(void *arg) 2720Sstevel@tonic-gate { 2733448Sdh155122 ipsecah_stack_t *ahstack = (ipsecah_stack_t *)arg; 2743448Sdh155122 netstack_t *ns = ahstack->ipsecah_netstack; 2750Sstevel@tonic-gate hrtime_t begin = gethrtime(); 2760Sstevel@tonic-gate 2773448Sdh155122 sadb_ager(&ahstack->ah_sadb.s_v4, ahstack->ah_pfkey_q, 2783448Sdh155122 ahstack->ah_sadb.s_ip_q, ahstack->ipsecah_reap_delay, ns); 2793448Sdh155122 sadb_ager(&ahstack->ah_sadb.s_v6, ahstack->ah_pfkey_q, 2803448Sdh155122 ahstack->ah_sadb.s_ip_q, ahstack->ipsecah_reap_delay, ns); 2813448Sdh155122 2823448Sdh155122 ahstack->ah_event = sadb_retimeout(begin, ahstack->ah_pfkey_q, 2833448Sdh155122 ah_ager, ahstack, 2843448Sdh155122 &ahstack->ipsecah_age_interval, ahstack->ipsecah_age_int_max, 2853448Sdh155122 info.mi_idnum); 2860Sstevel@tonic-gate } 2870Sstevel@tonic-gate 2880Sstevel@tonic-gate /* 2890Sstevel@tonic-gate * Get an AH NDD parameter. 2900Sstevel@tonic-gate */ 2910Sstevel@tonic-gate /* ARGSUSED */ 2920Sstevel@tonic-gate static int 2930Sstevel@tonic-gate ipsecah_param_get(q, mp, cp, cr) 2940Sstevel@tonic-gate queue_t *q; 2950Sstevel@tonic-gate mblk_t *mp; 2960Sstevel@tonic-gate caddr_t cp; 2970Sstevel@tonic-gate cred_t *cr; 2980Sstevel@tonic-gate { 2990Sstevel@tonic-gate ipsecahparam_t *ipsecahpa = (ipsecahparam_t *)cp; 3000Sstevel@tonic-gate uint_t value; 3013448Sdh155122 ipsecah_stack_t *ahstack = (ipsecah_stack_t *)q->q_ptr; 3023448Sdh155122 3033448Sdh155122 mutex_enter(&ahstack->ipsecah_param_lock); 3040Sstevel@tonic-gate value = ipsecahpa->ipsecah_param_value; 3053448Sdh155122 mutex_exit(&ahstack->ipsecah_param_lock); 3060Sstevel@tonic-gate 3070Sstevel@tonic-gate (void) mi_mpprintf(mp, "%u", value); 3080Sstevel@tonic-gate return (0); 3090Sstevel@tonic-gate } 3100Sstevel@tonic-gate 3110Sstevel@tonic-gate /* 3120Sstevel@tonic-gate * This routine sets an NDD variable in a ipsecahparam_t structure. 3130Sstevel@tonic-gate */ 3140Sstevel@tonic-gate /* ARGSUSED */ 3150Sstevel@tonic-gate static int 3160Sstevel@tonic-gate ipsecah_param_set(q, mp, value, cp, cr) 3170Sstevel@tonic-gate queue_t *q; 3180Sstevel@tonic-gate mblk_t *mp; 3190Sstevel@tonic-gate char *value; 3200Sstevel@tonic-gate caddr_t cp; 3210Sstevel@tonic-gate cred_t *cr; 3220Sstevel@tonic-gate { 3230Sstevel@tonic-gate ulong_t new_value; 3240Sstevel@tonic-gate ipsecahparam_t *ipsecahpa = (ipsecahparam_t *)cp; 3253448Sdh155122 ipsecah_stack_t *ahstack = (ipsecah_stack_t *)q->q_ptr; 3260Sstevel@tonic-gate 3270Sstevel@tonic-gate /* 3280Sstevel@tonic-gate * Fail the request if the new value does not lie within the 3290Sstevel@tonic-gate * required bounds. 3300Sstevel@tonic-gate */ 3310Sstevel@tonic-gate if (ddi_strtoul(value, NULL, 10, &new_value) != 0 || 3320Sstevel@tonic-gate new_value < ipsecahpa->ipsecah_param_min || 3330Sstevel@tonic-gate new_value > ipsecahpa->ipsecah_param_max) { 3340Sstevel@tonic-gate return (EINVAL); 3350Sstevel@tonic-gate } 3360Sstevel@tonic-gate 3370Sstevel@tonic-gate /* Set the new value */ 3383448Sdh155122 mutex_enter(&ahstack->ipsecah_param_lock); 3390Sstevel@tonic-gate ipsecahpa->ipsecah_param_value = new_value; 3403448Sdh155122 mutex_exit(&ahstack->ipsecah_param_lock); 3410Sstevel@tonic-gate return (0); 3420Sstevel@tonic-gate } 3430Sstevel@tonic-gate 3440Sstevel@tonic-gate /* 3450Sstevel@tonic-gate * Using lifetime NDD variables, fill in an extended combination's 3460Sstevel@tonic-gate * lifetime information. 3470Sstevel@tonic-gate */ 3480Sstevel@tonic-gate void 3493448Sdh155122 ipsecah_fill_defs(sadb_x_ecomb_t *ecomb, netstack_t *ns) 3500Sstevel@tonic-gate { 3513448Sdh155122 ipsecah_stack_t *ahstack = ns->netstack_ipsecah; 3523448Sdh155122 3533448Sdh155122 ecomb->sadb_x_ecomb_soft_bytes = ahstack->ipsecah_default_soft_bytes; 3543448Sdh155122 ecomb->sadb_x_ecomb_hard_bytes = ahstack->ipsecah_default_hard_bytes; 3553448Sdh155122 ecomb->sadb_x_ecomb_soft_addtime = 3563448Sdh155122 ahstack->ipsecah_default_soft_addtime; 3573448Sdh155122 ecomb->sadb_x_ecomb_hard_addtime = 3583448Sdh155122 ahstack->ipsecah_default_hard_addtime; 3593448Sdh155122 ecomb->sadb_x_ecomb_soft_usetime = 3603448Sdh155122 ahstack->ipsecah_default_soft_usetime; 3613448Sdh155122 ecomb->sadb_x_ecomb_hard_usetime = 3623448Sdh155122 ahstack->ipsecah_default_hard_usetime; 3630Sstevel@tonic-gate } 3640Sstevel@tonic-gate 3650Sstevel@tonic-gate /* 3660Sstevel@tonic-gate * Initialize things for AH at module load time. 3670Sstevel@tonic-gate */ 3680Sstevel@tonic-gate boolean_t 3690Sstevel@tonic-gate ipsecah_ddi_init(void) 3700Sstevel@tonic-gate { 3713448Sdh155122 ah_taskq = taskq_create("ah_taskq", 1, minclsyspri, 3723448Sdh155122 IPSEC_TASKQ_MIN, IPSEC_TASKQ_MAX, 0); 3733448Sdh155122 3743448Sdh155122 /* 3753448Sdh155122 * We want to be informed each time a stack is created or 3763448Sdh155122 * destroyed in the kernel, so we can maintain the 3773448Sdh155122 * set of ipsecah_stack_t's. 3783448Sdh155122 */ 3793448Sdh155122 netstack_register(NS_IPSECAH, ipsecah_stack_init, NULL, 3803448Sdh155122 ipsecah_stack_fini); 3813448Sdh155122 3823448Sdh155122 return (B_TRUE); 3833448Sdh155122 } 3843448Sdh155122 3853448Sdh155122 /* 3863448Sdh155122 * Walk through the param array specified registering each element with the 3873448Sdh155122 * named dispatch handler. 3883448Sdh155122 */ 3893448Sdh155122 static boolean_t 3903448Sdh155122 ipsecah_param_register(IDP *ndp, ipsecahparam_t *ahp, int cnt) 3913448Sdh155122 { 3923448Sdh155122 for (; cnt-- > 0; ahp++) { 3930Sstevel@tonic-gate if (ahp->ipsecah_param_name != NULL && 3940Sstevel@tonic-gate ahp->ipsecah_param_name[0]) { 3953448Sdh155122 if (!nd_load(ndp, 3963448Sdh155122 ahp->ipsecah_param_name, 3970Sstevel@tonic-gate ipsecah_param_get, ipsecah_param_set, 3980Sstevel@tonic-gate (caddr_t)ahp)) { 3993448Sdh155122 nd_free(ndp); 4000Sstevel@tonic-gate return (B_FALSE); 4010Sstevel@tonic-gate } 4020Sstevel@tonic-gate } 4030Sstevel@tonic-gate } 4040Sstevel@tonic-gate return (B_TRUE); 4050Sstevel@tonic-gate } 4060Sstevel@tonic-gate 4070Sstevel@tonic-gate /* 4083448Sdh155122 * Initialize things for AH for each stack instance 4093448Sdh155122 */ 4103448Sdh155122 static void * 4113448Sdh155122 ipsecah_stack_init(netstackid_t stackid, netstack_t *ns) 4123448Sdh155122 { 4133448Sdh155122 ipsecah_stack_t *ahstack; 4143448Sdh155122 ipsecahparam_t *ahp; 4153448Sdh155122 4163448Sdh155122 ahstack = (ipsecah_stack_t *)kmem_zalloc(sizeof (*ahstack), KM_SLEEP); 4173448Sdh155122 ahstack->ipsecah_netstack = ns; 4183448Sdh155122 4193448Sdh155122 ahp = (ipsecahparam_t *)kmem_alloc(sizeof (lcl_param_arr), KM_SLEEP); 4203448Sdh155122 ahstack->ipsecah_params = ahp; 4213448Sdh155122 bcopy(lcl_param_arr, ahp, sizeof (lcl_param_arr)); 4223448Sdh155122 4233448Sdh155122 (void) ipsecah_param_register(&ahstack->ipsecah_g_nd, ahp, 4243448Sdh155122 A_CNT(lcl_param_arr)); 4253448Sdh155122 4263448Sdh155122 (void) ah_kstat_init(ahstack, stackid); 4273448Sdh155122 4283448Sdh155122 ahstack->ah_sadb.s_acquire_timeout = &ahstack->ipsecah_acquire_timeout; 4293448Sdh155122 ahstack->ah_sadb.s_acqfn = ah_send_acquire; 4303448Sdh155122 sadbp_init("AH", &ahstack->ah_sadb, SADB_SATYPE_AH, ah_hash_size, 4313448Sdh155122 ahstack->ipsecah_netstack); 4323448Sdh155122 4333448Sdh155122 mutex_init(&ahstack->ipsecah_param_lock, NULL, MUTEX_DEFAULT, 0); 4343448Sdh155122 4353448Sdh155122 ip_drop_register(&ahstack->ah_dropper, "IPsec AH"); 4363448Sdh155122 return (ahstack); 4373448Sdh155122 } 4383448Sdh155122 4393448Sdh155122 /* 4400Sstevel@tonic-gate * Destroy things for AH at module unload time. 4410Sstevel@tonic-gate */ 4420Sstevel@tonic-gate void 4430Sstevel@tonic-gate ipsecah_ddi_destroy(void) 4440Sstevel@tonic-gate { 4453448Sdh155122 netstack_unregister(NS_IPSECAH); 4460Sstevel@tonic-gate taskq_destroy(ah_taskq); 4473448Sdh155122 } 4483448Sdh155122 4493448Sdh155122 /* 4503448Sdh155122 * Destroy things for AH for one stack... Never called? 4513448Sdh155122 */ 4523448Sdh155122 static void 4533448Sdh155122 ipsecah_stack_fini(netstackid_t stackid, void *arg) 4543448Sdh155122 { 4553448Sdh155122 ipsecah_stack_t *ahstack = (ipsecah_stack_t *)arg; 4563448Sdh155122 4573448Sdh155122 if (ahstack->ah_pfkey_q != NULL) { 4583448Sdh155122 (void) quntimeout(ahstack->ah_pfkey_q, ahstack->ah_event); 4593448Sdh155122 } 4603448Sdh155122 ahstack->ah_sadb.s_acqfn = NULL; 4613448Sdh155122 ahstack->ah_sadb.s_acquire_timeout = NULL; 4623448Sdh155122 sadbp_destroy(&ahstack->ah_sadb, ahstack->ipsecah_netstack); 4633448Sdh155122 ip_drop_unregister(&ahstack->ah_dropper); 4643448Sdh155122 mutex_destroy(&ahstack->ipsecah_param_lock); 4653448Sdh155122 nd_free(&ahstack->ipsecah_g_nd); 4663448Sdh155122 4673448Sdh155122 kmem_free(ahstack->ipsecah_params, sizeof (lcl_param_arr)); 4683448Sdh155122 ahstack->ipsecah_params = NULL; 4693448Sdh155122 kstat_delete_netstack(ahstack->ah_ksp, stackid); 4703448Sdh155122 ahstack->ah_ksp = NULL; 4713448Sdh155122 ahstack->ah_kstats = NULL; 4723448Sdh155122 4733448Sdh155122 kmem_free(ahstack, sizeof (*ahstack)); 4740Sstevel@tonic-gate } 4750Sstevel@tonic-gate 4760Sstevel@tonic-gate /* 4770Sstevel@tonic-gate * AH module open routine. The module should be opened by keysock. 4780Sstevel@tonic-gate */ 4790Sstevel@tonic-gate /* ARGSUSED */ 4800Sstevel@tonic-gate static int 4810Sstevel@tonic-gate ipsecah_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp) 4820Sstevel@tonic-gate { 4833448Sdh155122 netstack_t *ns; 4843448Sdh155122 ipsecah_stack_t *ahstack; 4853448Sdh155122 4867118Ssommerfe if (secpolicy_ip_config(credp, B_FALSE) != 0) 4870Sstevel@tonic-gate return (EPERM); 4880Sstevel@tonic-gate 4890Sstevel@tonic-gate if (q->q_ptr != NULL) 4900Sstevel@tonic-gate return (0); /* Re-open of an already open instance. */ 4910Sstevel@tonic-gate 4920Sstevel@tonic-gate if (sflag != MODOPEN) 4930Sstevel@tonic-gate return (EINVAL); 4940Sstevel@tonic-gate 4953448Sdh155122 ns = netstack_find_by_cred(credp); 4963448Sdh155122 ASSERT(ns != NULL); 4973448Sdh155122 ahstack = ns->netstack_ipsecah; 4983448Sdh155122 ASSERT(ahstack != NULL); 4993448Sdh155122 5000Sstevel@tonic-gate /* 5010Sstevel@tonic-gate * ASSUMPTIONS (because I'm MT_OCEXCL): 5020Sstevel@tonic-gate * 5030Sstevel@tonic-gate * * I'm being pushed on top of IP for all my opens (incl. #1). 5040Sstevel@tonic-gate * * Only ipsecah_open() can write into ah_sadb.s_ip_q. 5050Sstevel@tonic-gate * * Because of this, I can check lazily for ah_sadb.s_ip_q. 5060Sstevel@tonic-gate * 5070Sstevel@tonic-gate * If these assumptions are wrong, I'm in BIG trouble... 5080Sstevel@tonic-gate */ 5090Sstevel@tonic-gate 5103448Sdh155122 q->q_ptr = ahstack; 5113448Sdh155122 WR(q)->q_ptr = q->q_ptr; 5123448Sdh155122 5133448Sdh155122 if (ahstack->ah_sadb.s_ip_q == NULL) { 5140Sstevel@tonic-gate struct T_unbind_req *tur; 5150Sstevel@tonic-gate 5163448Sdh155122 ahstack->ah_sadb.s_ip_q = WR(q); 5170Sstevel@tonic-gate /* Allocate an unbind... */ 5183448Sdh155122 ahstack->ah_ip_unbind = allocb(sizeof (struct T_unbind_req), 5193448Sdh155122 BPRI_HI); 5200Sstevel@tonic-gate 5210Sstevel@tonic-gate /* 5220Sstevel@tonic-gate * Send down T_BIND_REQ to bind IPPROTO_AH. 5230Sstevel@tonic-gate * Handle the ACK here in AH. 5240Sstevel@tonic-gate */ 5250Sstevel@tonic-gate qprocson(q); 5263448Sdh155122 if (ahstack->ah_ip_unbind == NULL || 5273448Sdh155122 !sadb_t_bind_req(ahstack->ah_sadb.s_ip_q, IPPROTO_AH)) { 5283448Sdh155122 if (ahstack->ah_ip_unbind != NULL) { 5293448Sdh155122 freeb(ahstack->ah_ip_unbind); 5303448Sdh155122 ahstack->ah_ip_unbind = NULL; 5310Sstevel@tonic-gate } 5320Sstevel@tonic-gate q->q_ptr = NULL; 5330Sstevel@tonic-gate qprocsoff(q); 5343448Sdh155122 netstack_rele(ahstack->ipsecah_netstack); 5350Sstevel@tonic-gate return (ENOMEM); 5360Sstevel@tonic-gate } 5370Sstevel@tonic-gate 5383448Sdh155122 ahstack->ah_ip_unbind->b_datap->db_type = M_PROTO; 5393448Sdh155122 tur = (struct T_unbind_req *)ahstack->ah_ip_unbind->b_rptr; 5400Sstevel@tonic-gate tur->PRIM_type = T_UNBIND_REQ; 5410Sstevel@tonic-gate } else { 5420Sstevel@tonic-gate qprocson(q); 5430Sstevel@tonic-gate } 5440Sstevel@tonic-gate 5450Sstevel@tonic-gate /* 5460Sstevel@tonic-gate * For now, there's not much I can do. I'll be getting a message 5470Sstevel@tonic-gate * passed down to me from keysock (in my wput), and a T_BIND_ACK 5480Sstevel@tonic-gate * up from IP (in my rput). 5490Sstevel@tonic-gate */ 5500Sstevel@tonic-gate 5510Sstevel@tonic-gate return (0); 5520Sstevel@tonic-gate } 5530Sstevel@tonic-gate 5540Sstevel@tonic-gate /* 5550Sstevel@tonic-gate * AH module close routine. 5560Sstevel@tonic-gate */ 5570Sstevel@tonic-gate static int 5580Sstevel@tonic-gate ipsecah_close(queue_t *q) 5590Sstevel@tonic-gate { 5603448Sdh155122 ipsecah_stack_t *ahstack = (ipsecah_stack_t *)q->q_ptr; 5613448Sdh155122 5620Sstevel@tonic-gate /* 5630Sstevel@tonic-gate * If ah_sadb.s_ip_q is attached to this instance, send a 5640Sstevel@tonic-gate * T_UNBIND_REQ to IP for the instance before doing 5650Sstevel@tonic-gate * a qprocsoff(). 5660Sstevel@tonic-gate */ 5673448Sdh155122 if (WR(q) == ahstack->ah_sadb.s_ip_q && 5683448Sdh155122 ahstack->ah_ip_unbind != NULL) { 5693448Sdh155122 putnext(WR(q), ahstack->ah_ip_unbind); 5703448Sdh155122 ahstack->ah_ip_unbind = NULL; 5710Sstevel@tonic-gate } 5720Sstevel@tonic-gate 5730Sstevel@tonic-gate /* 5740Sstevel@tonic-gate * Clean up q_ptr, if needed. 5750Sstevel@tonic-gate */ 5760Sstevel@tonic-gate qprocsoff(q); 5770Sstevel@tonic-gate 5780Sstevel@tonic-gate /* Keysock queue check is safe, because of OCEXCL perimeter. */ 5790Sstevel@tonic-gate 5803448Sdh155122 if (q == ahstack->ah_pfkey_q) { 5813448Sdh155122 ah1dbg(ahstack, 5823448Sdh155122 ("ipsecah_close: Ummm... keysock is closing AH.\n")); 5833448Sdh155122 ahstack->ah_pfkey_q = NULL; 5840Sstevel@tonic-gate /* Detach qtimeouts. */ 5853448Sdh155122 (void) quntimeout(q, ahstack->ah_event); 5860Sstevel@tonic-gate } 5870Sstevel@tonic-gate 5883448Sdh155122 if (WR(q) == ahstack->ah_sadb.s_ip_q) { 5890Sstevel@tonic-gate /* 5900Sstevel@tonic-gate * If the ah_sadb.s_ip_q is attached to this instance, find 5910Sstevel@tonic-gate * another. The OCEXCL outer perimeter helps us here. 5920Sstevel@tonic-gate */ 5930Sstevel@tonic-gate 5943448Sdh155122 ahstack->ah_sadb.s_ip_q = NULL; 5950Sstevel@tonic-gate 5960Sstevel@tonic-gate /* 5970Sstevel@tonic-gate * Find a replacement queue for ah_sadb.s_ip_q. 5980Sstevel@tonic-gate */ 5993448Sdh155122 if (ahstack->ah_pfkey_q != NULL && 6003448Sdh155122 ahstack->ah_pfkey_q != RD(q)) { 6010Sstevel@tonic-gate /* 6020Sstevel@tonic-gate * See if we can use the pfkey_q. 6030Sstevel@tonic-gate */ 6043448Sdh155122 ahstack->ah_sadb.s_ip_q = WR(ahstack->ah_pfkey_q); 6050Sstevel@tonic-gate } 6060Sstevel@tonic-gate 6073448Sdh155122 if (ahstack->ah_sadb.s_ip_q == NULL || 6083448Sdh155122 !sadb_t_bind_req(ahstack->ah_sadb.s_ip_q, IPPROTO_AH)) { 6093448Sdh155122 ah1dbg(ahstack, 6103448Sdh155122 ("ipsecah: Can't reassign ah_sadb.s_ip_q.\n")); 6113448Sdh155122 ahstack->ah_sadb.s_ip_q = NULL; 6120Sstevel@tonic-gate } else { 6133448Sdh155122 ahstack->ah_ip_unbind = 6143448Sdh155122 allocb(sizeof (struct T_unbind_req), BPRI_HI); 6153448Sdh155122 6163448Sdh155122 if (ahstack->ah_ip_unbind != NULL) { 6170Sstevel@tonic-gate struct T_unbind_req *tur; 6180Sstevel@tonic-gate 6193448Sdh155122 ahstack->ah_ip_unbind->b_datap->db_type = 6203448Sdh155122 M_PROTO; 6210Sstevel@tonic-gate tur = (struct T_unbind_req *) 6223448Sdh155122 ahstack->ah_ip_unbind->b_rptr; 6230Sstevel@tonic-gate tur->PRIM_type = T_UNBIND_REQ; 6240Sstevel@tonic-gate } 6250Sstevel@tonic-gate /* If it's NULL, I can't do much here. */ 6260Sstevel@tonic-gate } 6270Sstevel@tonic-gate } 6280Sstevel@tonic-gate 6293448Sdh155122 netstack_rele(ahstack->ipsecah_netstack); 6300Sstevel@tonic-gate return (0); 6310Sstevel@tonic-gate } 6320Sstevel@tonic-gate 6330Sstevel@tonic-gate /* 6340Sstevel@tonic-gate * AH module read put routine. 6350Sstevel@tonic-gate */ 6360Sstevel@tonic-gate /* ARGSUSED */ 6370Sstevel@tonic-gate static void 6380Sstevel@tonic-gate ipsecah_rput(queue_t *q, mblk_t *mp) 6390Sstevel@tonic-gate { 6403448Sdh155122 ipsecah_stack_t *ahstack = (ipsecah_stack_t *)q->q_ptr; 6413448Sdh155122 6423055Sdanmcd ASSERT(mp->b_datap->db_type != M_CTL); /* No more IRE_DB_REQ. */ 6433448Sdh155122 6440Sstevel@tonic-gate switch (mp->b_datap->db_type) { 6450Sstevel@tonic-gate case M_PROTO: 6460Sstevel@tonic-gate case M_PCPROTO: 6470Sstevel@tonic-gate /* TPI message of some sort. */ 6480Sstevel@tonic-gate switch (*((t_scalar_t *)mp->b_rptr)) { 6490Sstevel@tonic-gate case T_BIND_ACK: 6500Sstevel@tonic-gate /* We expect this. */ 6513448Sdh155122 ah3dbg(ahstack, 6523448Sdh155122 ("Thank you IP from AH for T_BIND_ACK\n")); 6530Sstevel@tonic-gate break; 6540Sstevel@tonic-gate case T_ERROR_ACK: 6550Sstevel@tonic-gate cmn_err(CE_WARN, 6560Sstevel@tonic-gate "ipsecah: AH received T_ERROR_ACK from IP."); 6570Sstevel@tonic-gate break; 6580Sstevel@tonic-gate case T_OK_ACK: 6590Sstevel@tonic-gate /* Probably from a (rarely sent) T_UNBIND_REQ. */ 6600Sstevel@tonic-gate break; 6610Sstevel@tonic-gate default: 6623448Sdh155122 ah1dbg(ahstack, ("Unknown M_{,PC}PROTO message.\n")); 6630Sstevel@tonic-gate } 6640Sstevel@tonic-gate freemsg(mp); 6650Sstevel@tonic-gate break; 6660Sstevel@tonic-gate default: 6670Sstevel@tonic-gate /* For now, passthru message. */ 6683448Sdh155122 ah2dbg(ahstack, ("AH got unknown mblk type %d.\n", 6690Sstevel@tonic-gate mp->b_datap->db_type)); 6700Sstevel@tonic-gate putnext(q, mp); 6710Sstevel@tonic-gate } 6720Sstevel@tonic-gate } 6730Sstevel@tonic-gate 6740Sstevel@tonic-gate /* 6750Sstevel@tonic-gate * Construct an SADB_REGISTER message with the current algorithms. 6760Sstevel@tonic-gate */ 6770Sstevel@tonic-gate static boolean_t 6783448Sdh155122 ah_register_out(uint32_t sequence, uint32_t pid, uint_t serial, 679*10934Ssommerfeld@sun.com ipsecah_stack_t *ahstack, mblk_t *in_mp) 6800Sstevel@tonic-gate { 6810Sstevel@tonic-gate mblk_t *mp; 6820Sstevel@tonic-gate boolean_t rc = B_TRUE; 6830Sstevel@tonic-gate sadb_msg_t *samsg; 6840Sstevel@tonic-gate sadb_supported_t *sasupp; 6850Sstevel@tonic-gate sadb_alg_t *saalg; 6860Sstevel@tonic-gate uint_t allocsize = sizeof (*samsg); 6870Sstevel@tonic-gate uint_t i, numalgs_snap; 6880Sstevel@tonic-gate ipsec_alginfo_t **authalgs; 6890Sstevel@tonic-gate uint_t num_aalgs; 6903448Sdh155122 ipsec_stack_t *ipss = ahstack->ipsecah_netstack->netstack_ipsec; 691*10934Ssommerfeld@sun.com sadb_sens_t *sens; 692*10934Ssommerfeld@sun.com size_t sens_len = 0; 693*10934Ssommerfeld@sun.com sadb_ext_t *nextext; 694*10934Ssommerfeld@sun.com cred_t *sens_cr = NULL; 6950Sstevel@tonic-gate 6960Sstevel@tonic-gate /* Allocate the KEYSOCK_OUT. */ 6970Sstevel@tonic-gate mp = sadb_keysock_out(serial); 6980Sstevel@tonic-gate if (mp == NULL) { 6990Sstevel@tonic-gate ah0dbg(("ah_register_out: couldn't allocate mblk.\n")); 7000Sstevel@tonic-gate return (B_FALSE); 7010Sstevel@tonic-gate } 7020Sstevel@tonic-gate 703*10934Ssommerfeld@sun.com if (is_system_labeled() && (in_mp != NULL)) { 704*10934Ssommerfeld@sun.com sens_cr = msg_getcred(in_mp, NULL); 705*10934Ssommerfeld@sun.com 706*10934Ssommerfeld@sun.com if (sens_cr != NULL) { 707*10934Ssommerfeld@sun.com sens_len = sadb_sens_len_from_cred(sens_cr); 708*10934Ssommerfeld@sun.com allocsize += sens_len; 709*10934Ssommerfeld@sun.com } 710*10934Ssommerfeld@sun.com } 711*10934Ssommerfeld@sun.com 7120Sstevel@tonic-gate /* 7130Sstevel@tonic-gate * Allocate the PF_KEY message that follows KEYSOCK_OUT. 7140Sstevel@tonic-gate * The alg reader lock needs to be held while allocating 7150Sstevel@tonic-gate * the variable part (i.e. the algorithms) of the message. 7160Sstevel@tonic-gate */ 7170Sstevel@tonic-gate 7183448Sdh155122 mutex_enter(&ipss->ipsec_alg_lock); 7190Sstevel@tonic-gate 7200Sstevel@tonic-gate /* 7210Sstevel@tonic-gate * Return only valid algorithms, so the number of algorithms 7220Sstevel@tonic-gate * to send up may be less than the number of algorithm entries 7230Sstevel@tonic-gate * in the table. 7240Sstevel@tonic-gate */ 7253448Sdh155122 authalgs = ipss->ipsec_alglists[IPSEC_ALG_AUTH]; 7260Sstevel@tonic-gate for (num_aalgs = 0, i = 0; i < IPSEC_MAX_ALGS; i++) 7270Sstevel@tonic-gate if (authalgs[i] != NULL && ALG_VALID(authalgs[i])) 7280Sstevel@tonic-gate num_aalgs++; 7290Sstevel@tonic-gate 7300Sstevel@tonic-gate /* 7310Sstevel@tonic-gate * Fill SADB_REGISTER message's algorithm descriptors. Hold 7320Sstevel@tonic-gate * down the lock while filling it. 7330Sstevel@tonic-gate */ 7340Sstevel@tonic-gate if (num_aalgs != 0) { 7350Sstevel@tonic-gate allocsize += (num_aalgs * sizeof (*saalg)); 7360Sstevel@tonic-gate allocsize += sizeof (*sasupp); 7370Sstevel@tonic-gate } 7380Sstevel@tonic-gate mp->b_cont = allocb(allocsize, BPRI_HI); 7390Sstevel@tonic-gate if (mp->b_cont == NULL) { 7403448Sdh155122 mutex_exit(&ipss->ipsec_alg_lock); 7410Sstevel@tonic-gate freemsg(mp); 7420Sstevel@tonic-gate return (B_FALSE); 7430Sstevel@tonic-gate } 7440Sstevel@tonic-gate 7450Sstevel@tonic-gate mp->b_cont->b_wptr += allocsize; 746*10934Ssommerfeld@sun.com nextext = (sadb_ext_t *)(mp->b_cont->b_rptr + sizeof (*samsg)); 747*10934Ssommerfeld@sun.com 7480Sstevel@tonic-gate if (num_aalgs != 0) { 7490Sstevel@tonic-gate 750*10934Ssommerfeld@sun.com saalg = (sadb_alg_t *)(((uint8_t *)nextext) + sizeof (*sasupp)); 7510Sstevel@tonic-gate ASSERT(((ulong_t)saalg & 0x7) == 0); 7520Sstevel@tonic-gate 7530Sstevel@tonic-gate numalgs_snap = 0; 7540Sstevel@tonic-gate for (i = 0; 7553448Sdh155122 ((i < IPSEC_MAX_ALGS) && (numalgs_snap < num_aalgs)); 7563448Sdh155122 i++) { 7570Sstevel@tonic-gate if (authalgs[i] == NULL || !ALG_VALID(authalgs[i])) 7580Sstevel@tonic-gate continue; 7590Sstevel@tonic-gate 7600Sstevel@tonic-gate saalg->sadb_alg_id = authalgs[i]->alg_id; 7610Sstevel@tonic-gate saalg->sadb_alg_ivlen = 0; 7620Sstevel@tonic-gate saalg->sadb_alg_minbits = authalgs[i]->alg_ef_minbits; 7630Sstevel@tonic-gate saalg->sadb_alg_maxbits = authalgs[i]->alg_ef_maxbits; 7640Sstevel@tonic-gate saalg->sadb_x_alg_increment = 7650Sstevel@tonic-gate authalgs[i]->alg_increment; 76610824SMark.Fenwick@Sun.COM /* For now, salt is meaningless in AH. */ 76710824SMark.Fenwick@Sun.COM ASSERT(authalgs[i]->alg_saltlen == 0); 76810824SMark.Fenwick@Sun.COM saalg->sadb_x_alg_saltbits = 76910824SMark.Fenwick@Sun.COM SADB_8TO1(authalgs[i]->alg_saltlen); 7700Sstevel@tonic-gate numalgs_snap++; 7710Sstevel@tonic-gate saalg++; 7720Sstevel@tonic-gate } 7730Sstevel@tonic-gate ASSERT(numalgs_snap == num_aalgs); 7740Sstevel@tonic-gate #ifdef DEBUG 7750Sstevel@tonic-gate /* 7760Sstevel@tonic-gate * Reality check to make sure I snagged all of the 7770Sstevel@tonic-gate * algorithms. 7780Sstevel@tonic-gate */ 7790Sstevel@tonic-gate for (; i < IPSEC_MAX_ALGS; i++) 7800Sstevel@tonic-gate if (authalgs[i] != NULL && ALG_VALID(authalgs[i])) 7810Sstevel@tonic-gate cmn_err(CE_PANIC, 7820Sstevel@tonic-gate "ah_register_out()! Missed #%d.\n", i); 7830Sstevel@tonic-gate #endif /* DEBUG */ 784*10934Ssommerfeld@sun.com nextext = (sadb_ext_t *)saalg; 7850Sstevel@tonic-gate } 7860Sstevel@tonic-gate 7873448Sdh155122 mutex_exit(&ipss->ipsec_alg_lock); 7880Sstevel@tonic-gate 789*10934Ssommerfeld@sun.com if (sens_cr != NULL) { 790*10934Ssommerfeld@sun.com sens = (sadb_sens_t *)nextext; 791*10934Ssommerfeld@sun.com sadb_sens_from_cred(sens, SADB_EXT_SENSITIVITY, 792*10934Ssommerfeld@sun.com sens_cr, sens_len); 793*10934Ssommerfeld@sun.com 794*10934Ssommerfeld@sun.com nextext = (sadb_ext_t *)(((uint8_t *)sens) + sens_len); 795*10934Ssommerfeld@sun.com } 796*10934Ssommerfeld@sun.com 7970Sstevel@tonic-gate /* Now fill the restof the SADB_REGISTER message. */ 7980Sstevel@tonic-gate 7990Sstevel@tonic-gate samsg = (sadb_msg_t *)mp->b_cont->b_rptr; 8000Sstevel@tonic-gate samsg->sadb_msg_version = PF_KEY_V2; 8010Sstevel@tonic-gate samsg->sadb_msg_type = SADB_REGISTER; 8020Sstevel@tonic-gate samsg->sadb_msg_errno = 0; 8030Sstevel@tonic-gate samsg->sadb_msg_satype = SADB_SATYPE_AH; 8040Sstevel@tonic-gate samsg->sadb_msg_len = SADB_8TO64(allocsize); 8050Sstevel@tonic-gate samsg->sadb_msg_reserved = 0; 8060Sstevel@tonic-gate /* 8070Sstevel@tonic-gate * Assume caller has sufficient sequence/pid number info. If it's one 8080Sstevel@tonic-gate * from me over a new alg., I could give two hoots about sequence. 8090Sstevel@tonic-gate */ 8100Sstevel@tonic-gate samsg->sadb_msg_seq = sequence; 8110Sstevel@tonic-gate samsg->sadb_msg_pid = pid; 8120Sstevel@tonic-gate 813*10934Ssommerfeld@sun.com if (num_aalgs != 0) { 8140Sstevel@tonic-gate sasupp = (sadb_supported_t *)(samsg + 1); 815*10934Ssommerfeld@sun.com sasupp->sadb_supported_len = SADB_8TO64( 816*10934Ssommerfeld@sun.com sizeof (*sasupp) + sizeof (*saalg) * num_aalgs); 8170Sstevel@tonic-gate sasupp->sadb_supported_exttype = SADB_EXT_SUPPORTED_AUTH; 8180Sstevel@tonic-gate sasupp->sadb_supported_reserved = 0; 8190Sstevel@tonic-gate } 8200Sstevel@tonic-gate 8213448Sdh155122 if (ahstack->ah_pfkey_q != NULL) 8223448Sdh155122 putnext(ahstack->ah_pfkey_q, mp); 8230Sstevel@tonic-gate else { 8240Sstevel@tonic-gate rc = B_FALSE; 8250Sstevel@tonic-gate freemsg(mp); 8260Sstevel@tonic-gate } 8270Sstevel@tonic-gate 8280Sstevel@tonic-gate return (rc); 8290Sstevel@tonic-gate } 8300Sstevel@tonic-gate 8310Sstevel@tonic-gate /* 8320Sstevel@tonic-gate * Invoked when the algorithm table changes. Causes SADB_REGISTER 8330Sstevel@tonic-gate * messages continaining the current list of algorithms to be 8340Sstevel@tonic-gate * sent up to the AH listeners. 8350Sstevel@tonic-gate */ 8360Sstevel@tonic-gate void 8373448Sdh155122 ipsecah_algs_changed(netstack_t *ns) 8380Sstevel@tonic-gate { 8393448Sdh155122 ipsecah_stack_t *ahstack = ns->netstack_ipsecah; 8403448Sdh155122 8410Sstevel@tonic-gate /* 8420Sstevel@tonic-gate * Time to send a PF_KEY SADB_REGISTER message to AH listeners 8430Sstevel@tonic-gate * everywhere. (The function itself checks for NULL ah_pfkey_q.) 8440Sstevel@tonic-gate */ 845*10934Ssommerfeld@sun.com (void) ah_register_out(0, 0, 0, ahstack, NULL); 8460Sstevel@tonic-gate } 8470Sstevel@tonic-gate 8480Sstevel@tonic-gate /* 8490Sstevel@tonic-gate * Stub function that taskq_dispatch() invokes to take the mblk (in arg) 8500Sstevel@tonic-gate * and put() it into AH and STREAMS again. 8510Sstevel@tonic-gate */ 8520Sstevel@tonic-gate static void 8530Sstevel@tonic-gate inbound_task(void *arg) 8540Sstevel@tonic-gate { 8550Sstevel@tonic-gate ah_t *ah; 8560Sstevel@tonic-gate mblk_t *mp = (mblk_t *)arg; 8570Sstevel@tonic-gate ipsec_in_t *ii = (ipsec_in_t *)mp->b_rptr; 8580Sstevel@tonic-gate int ipsec_rc; 8598704Sdanmcd@sun.com netstack_t *ns; 8608704Sdanmcd@sun.com ipsecah_stack_t *ahstack; 8618704Sdanmcd@sun.com 8628704Sdanmcd@sun.com ns = netstack_find_by_stackid(ii->ipsec_in_stackid); 8638704Sdanmcd@sun.com if (ns == NULL || ns != ii->ipsec_in_ns) { 8648704Sdanmcd@sun.com /* Just freemsg(). */ 8658704Sdanmcd@sun.com if (ns != NULL) 8668704Sdanmcd@sun.com netstack_rele(ns); 8678704Sdanmcd@sun.com freemsg(mp); 8688704Sdanmcd@sun.com return; 8698704Sdanmcd@sun.com } 8708704Sdanmcd@sun.com 8718704Sdanmcd@sun.com ahstack = ns->netstack_ipsecah; 8723448Sdh155122 8733448Sdh155122 ah2dbg(ahstack, ("in AH inbound_task")); 8743448Sdh155122 8753448Sdh155122 ASSERT(ahstack != NULL); 8763448Sdh155122 ah = ipsec_inbound_ah_sa(mp, ns); 8778704Sdanmcd@sun.com if (ah != NULL) { 8788704Sdanmcd@sun.com ASSERT(ii->ipsec_in_ah_sa != NULL); 8798704Sdanmcd@sun.com ipsec_rc = ii->ipsec_in_ah_sa->ipsa_input_func(mp, ah); 8808704Sdanmcd@sun.com if (ipsec_rc == IPSEC_STATUS_SUCCESS) 8818704Sdanmcd@sun.com ip_fanout_proto_again(mp, NULL, NULL, NULL); 8828704Sdanmcd@sun.com } 8838704Sdanmcd@sun.com netstack_rele(ns); 8840Sstevel@tonic-gate } 8850Sstevel@tonic-gate 8860Sstevel@tonic-gate /* 8870Sstevel@tonic-gate * Now that weak-key passed, actually ADD the security association, and 8880Sstevel@tonic-gate * send back a reply ADD message. 8890Sstevel@tonic-gate */ 8900Sstevel@tonic-gate static int 8913055Sdanmcd ah_add_sa_finish(mblk_t *mp, sadb_msg_t *samsg, keysock_in_t *ksi, 8923448Sdh155122 int *diagnostic, ipsecah_stack_t *ahstack) 8930Sstevel@tonic-gate { 894*10934Ssommerfeld@sun.com isaf_t *primary = NULL, *secondary; 895*10934Ssommerfeld@sun.com boolean_t clone = B_FALSE, is_inbound = B_FALSE; 8960Sstevel@tonic-gate sadb_sa_t *assoc = (sadb_sa_t *)ksi->ks_in_extv[SADB_EXT_SA]; 8970Sstevel@tonic-gate ipsa_t *larval; 8980Sstevel@tonic-gate ipsacq_t *acqrec; 8990Sstevel@tonic-gate iacqf_t *acq_bucket; 9000Sstevel@tonic-gate mblk_t *acq_msgs = NULL; 9010Sstevel@tonic-gate mblk_t *lpkt; 9020Sstevel@tonic-gate int rc; 903*10934Ssommerfeld@sun.com ipsa_query_t sq; 904*10934Ssommerfeld@sun.com int error; 9053448Sdh155122 netstack_t *ns = ahstack->ipsecah_netstack; 9063448Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 9070Sstevel@tonic-gate 9080Sstevel@tonic-gate /* 9090Sstevel@tonic-gate * Locate the appropriate table(s). 9100Sstevel@tonic-gate */ 9110Sstevel@tonic-gate 912*10934Ssommerfeld@sun.com sq.spp = &ahstack->ah_sadb; 913*10934Ssommerfeld@sun.com error = sadb_form_query(ksi, IPSA_Q_SA|IPSA_Q_DST, 914*10934Ssommerfeld@sun.com IPSA_Q_SA|IPSA_Q_DST|IPSA_Q_INBOUND|IPSA_Q_OUTBOUND, 915*10934Ssommerfeld@sun.com &sq, diagnostic); 916*10934Ssommerfeld@sun.com if (error) 917*10934Ssommerfeld@sun.com return (error); 918*10934Ssommerfeld@sun.com 9196668Smarkfen /* 9206668Smarkfen * Use the direction flags provided by the KMD to determine 9216668Smarkfen * if the inbound or outbound table should be the primary 9226668Smarkfen * for this SA. If these flags were absent then make this 9236668Smarkfen * decision based on the addresses. 9246668Smarkfen */ 9256668Smarkfen if (assoc->sadb_sa_flags & IPSA_F_INBOUND) { 926*10934Ssommerfeld@sun.com primary = sq.inbound; 927*10934Ssommerfeld@sun.com secondary = sq.outbound; 9286668Smarkfen is_inbound = B_TRUE; 9296668Smarkfen if (assoc->sadb_sa_flags & IPSA_F_OUTBOUND) 9306668Smarkfen clone = B_TRUE; 9316668Smarkfen } else { 9326668Smarkfen if (assoc->sadb_sa_flags & IPSA_F_OUTBOUND) { 933*10934Ssommerfeld@sun.com primary = sq.outbound; 934*10934Ssommerfeld@sun.com secondary = sq.inbound; 9356668Smarkfen } 9366668Smarkfen } 9376668Smarkfen if (primary == NULL) { 9386668Smarkfen /* 9396668Smarkfen * The KMD did not set a direction flag, determine which 9406668Smarkfen * table to insert the SA into based on addresses. 9416668Smarkfen */ 9426668Smarkfen switch (ksi->ks_in_dsttype) { 9436668Smarkfen case KS_IN_ADDR_MBCAST: 9446668Smarkfen clone = B_TRUE; /* All mcast SAs can be bidirectional */ 9456668Smarkfen assoc->sadb_sa_flags |= IPSA_F_OUTBOUND; 9466668Smarkfen /* FALLTHRU */ 9470Sstevel@tonic-gate /* 9480Sstevel@tonic-gate * If the source address is either one of mine, or unspecified 9490Sstevel@tonic-gate * (which is best summed up by saying "not 'not mine'"), 9500Sstevel@tonic-gate * then the association is potentially bi-directional, 9510Sstevel@tonic-gate * in that it can be used for inbound traffic and outbound 9520Sstevel@tonic-gate * traffic. The best example of such and SA is a multicast 9530Sstevel@tonic-gate * SA (which allows me to receive the outbound traffic). 9540Sstevel@tonic-gate */ 9556668Smarkfen case KS_IN_ADDR_ME: 9566668Smarkfen assoc->sadb_sa_flags |= IPSA_F_INBOUND; 957*10934Ssommerfeld@sun.com primary = sq.inbound; 958*10934Ssommerfeld@sun.com secondary = sq.outbound; 9596668Smarkfen if (ksi->ks_in_srctype != KS_IN_ADDR_NOTME) 9606668Smarkfen clone = B_TRUE; 9616668Smarkfen is_inbound = B_TRUE; 9626668Smarkfen break; 963*10934Ssommerfeld@sun.com 9640Sstevel@tonic-gate /* 9650Sstevel@tonic-gate * If the source address literally not mine (either 9660Sstevel@tonic-gate * unspecified or not mine), then this SA may have an 9670Sstevel@tonic-gate * address that WILL be mine after some configuration. 9680Sstevel@tonic-gate * We pay the price for this by making it a bi-directional 9690Sstevel@tonic-gate * SA. 9700Sstevel@tonic-gate */ 9716668Smarkfen case KS_IN_ADDR_NOTME: 9726668Smarkfen assoc->sadb_sa_flags |= IPSA_F_OUTBOUND; 973*10934Ssommerfeld@sun.com primary = sq.outbound; 974*10934Ssommerfeld@sun.com secondary = sq.inbound; 9756668Smarkfen if (ksi->ks_in_srctype != KS_IN_ADDR_ME) { 9766668Smarkfen assoc->sadb_sa_flags |= IPSA_F_INBOUND; 9776668Smarkfen clone = B_TRUE; 9786668Smarkfen } 9796668Smarkfen break; 9806668Smarkfen default: 9816668Smarkfen *diagnostic = SADB_X_DIAGNOSTIC_BAD_DST; 9826668Smarkfen return (EINVAL); 9836668Smarkfen } 9840Sstevel@tonic-gate } 9850Sstevel@tonic-gate 9860Sstevel@tonic-gate /* 9870Sstevel@tonic-gate * Find a ACQUIRE list entry if possible. If we've added an SA that 9880Sstevel@tonic-gate * suits the needs of an ACQUIRE list entry, we can eliminate the 9890Sstevel@tonic-gate * ACQUIRE list entry and transmit the enqueued packets. Use the 9900Sstevel@tonic-gate * high-bit of the sequence number to queue it. Key off destination 9910Sstevel@tonic-gate * addr, and change acqrec's state. 9920Sstevel@tonic-gate */ 9930Sstevel@tonic-gate 9940Sstevel@tonic-gate if (samsg->sadb_msg_seq & IACQF_LOWEST_SEQ) { 995*10934Ssommerfeld@sun.com acq_bucket = &(sq.sp->sdb_acq[sq.outhash]); 9960Sstevel@tonic-gate mutex_enter(&acq_bucket->iacqf_lock); 9970Sstevel@tonic-gate for (acqrec = acq_bucket->iacqf_ipsacq; acqrec != NULL; 9980Sstevel@tonic-gate acqrec = acqrec->ipsacq_next) { 9990Sstevel@tonic-gate mutex_enter(&acqrec->ipsacq_lock); 10000Sstevel@tonic-gate /* 10010Sstevel@tonic-gate * Q: I only check sequence. Should I check dst? 10020Sstevel@tonic-gate * A: Yes, check dest because those are the packets 10030Sstevel@tonic-gate * that are queued up. 10040Sstevel@tonic-gate */ 10050Sstevel@tonic-gate if (acqrec->ipsacq_seq == samsg->sadb_msg_seq && 1006*10934Ssommerfeld@sun.com IPSA_ARE_ADDR_EQUAL(sq.dstaddr, 10074987Sdanmcd acqrec->ipsacq_dstaddr, acqrec->ipsacq_addrfam)) 10080Sstevel@tonic-gate break; 10090Sstevel@tonic-gate mutex_exit(&acqrec->ipsacq_lock); 10100Sstevel@tonic-gate } 10110Sstevel@tonic-gate if (acqrec != NULL) { 10120Sstevel@tonic-gate /* 10130Sstevel@tonic-gate * AHA! I found an ACQUIRE record for this SA. 10140Sstevel@tonic-gate * Grab the msg list, and free the acquire record. 10150Sstevel@tonic-gate * I already am holding the lock for this record, 10160Sstevel@tonic-gate * so all I have to do is free it. 10170Sstevel@tonic-gate */ 10180Sstevel@tonic-gate acq_msgs = acqrec->ipsacq_mp; 10190Sstevel@tonic-gate acqrec->ipsacq_mp = NULL; 10200Sstevel@tonic-gate mutex_exit(&acqrec->ipsacq_lock); 10213448Sdh155122 sadb_destroy_acquire(acqrec, ns); 10220Sstevel@tonic-gate } 10230Sstevel@tonic-gate mutex_exit(&acq_bucket->iacqf_lock); 10240Sstevel@tonic-gate } 10250Sstevel@tonic-gate 10260Sstevel@tonic-gate /* 10270Sstevel@tonic-gate * Find PF_KEY message, and see if I'm an update. If so, find entry 10280Sstevel@tonic-gate * in larval list (if there). 10290Sstevel@tonic-gate */ 10300Sstevel@tonic-gate 10310Sstevel@tonic-gate larval = NULL; 10320Sstevel@tonic-gate 10330Sstevel@tonic-gate if (samsg->sadb_msg_type == SADB_UPDATE) { 1034*10934Ssommerfeld@sun.com mutex_enter(&sq.inbound->isaf_lock); 1035*10934Ssommerfeld@sun.com larval = ipsec_getassocbyspi(sq.inbound, sq.assoc->sadb_sa_spi, 1036*10934Ssommerfeld@sun.com ALL_ZEROES_PTR, sq.dstaddr, sq.dst->sin_family); 1037*10934Ssommerfeld@sun.com mutex_exit(&sq.inbound->isaf_lock); 10380Sstevel@tonic-gate 10390Sstevel@tonic-gate if ((larval == NULL) || 10400Sstevel@tonic-gate (larval->ipsa_state != IPSA_STATE_LARVAL)) { 10416668Smarkfen *diagnostic = SADB_X_DIAGNOSTIC_SA_NOTFOUND; 10427110Sdanmcd if (larval != NULL) { 10437110Sdanmcd IPSA_REFRELE(larval); 10447110Sdanmcd } 10450Sstevel@tonic-gate ah0dbg(("Larval update, but larval disappeared.\n")); 10460Sstevel@tonic-gate return (ESRCH); 10470Sstevel@tonic-gate } /* Else sadb_common_add unlinks it for me! */ 10480Sstevel@tonic-gate } 10490Sstevel@tonic-gate 10500Sstevel@tonic-gate lpkt = NULL; 10510Sstevel@tonic-gate if (larval != NULL) 10520Sstevel@tonic-gate lpkt = sadb_clear_lpkt(larval); 10530Sstevel@tonic-gate 10543448Sdh155122 rc = sadb_common_add(ahstack->ah_sadb.s_ip_q, ahstack->ah_pfkey_q, mp, 10553448Sdh155122 samsg, ksi, primary, secondary, larval, clone, is_inbound, 10566668Smarkfen diagnostic, ns, &ahstack->ah_sadb); 10570Sstevel@tonic-gate 10580Sstevel@tonic-gate /* 10590Sstevel@tonic-gate * How much more stack will I create with all of these 10600Sstevel@tonic-gate * ah_inbound_* and ah_outbound_*() calls? 10610Sstevel@tonic-gate */ 10620Sstevel@tonic-gate 10630Sstevel@tonic-gate if (rc == 0 && lpkt != NULL) 10648704Sdanmcd@sun.com rc = !taskq_dispatch(ah_taskq, inbound_task, lpkt, TQ_NOSLEEP); 10650Sstevel@tonic-gate 10660Sstevel@tonic-gate if (rc != 0) { 10670Sstevel@tonic-gate ip_drop_packet(lpkt, B_TRUE, NULL, NULL, 10683448Sdh155122 DROPPER(ipss, ipds_sadb_inlarval_timeout), 10693448Sdh155122 &ahstack->ah_dropper); 10700Sstevel@tonic-gate } 10710Sstevel@tonic-gate 10720Sstevel@tonic-gate while (acq_msgs != NULL) { 10730Sstevel@tonic-gate mblk_t *mp = acq_msgs; 10740Sstevel@tonic-gate 10750Sstevel@tonic-gate acq_msgs = acq_msgs->b_next; 10760Sstevel@tonic-gate mp->b_next = NULL; 10770Sstevel@tonic-gate if (rc == 0) { 10780Sstevel@tonic-gate ipsec_out_t *io = (ipsec_out_t *)mp->b_rptr; 10790Sstevel@tonic-gate 10803448Sdh155122 ASSERT(ahstack->ah_sadb.s_ip_q != NULL); 10810Sstevel@tonic-gate if (ipsec_outbound_sa(mp, IPPROTO_AH)) { 10820Sstevel@tonic-gate io->ipsec_out_ah_done = B_TRUE; 10830Sstevel@tonic-gate if (ah_outbound(mp) == IPSEC_STATUS_SUCCESS) { 10840Sstevel@tonic-gate ipha_t *ipha = (ipha_t *) 10850Sstevel@tonic-gate mp->b_cont->b_rptr; 1086*10934Ssommerfeld@sun.com if (sq.af == AF_INET) { 10870Sstevel@tonic-gate ip_wput_ipsec_out(NULL, mp, 10880Sstevel@tonic-gate ipha, NULL, NULL); 10890Sstevel@tonic-gate } else { 10900Sstevel@tonic-gate ip6_t *ip6h = (ip6_t *)ipha; 1091*10934Ssommerfeld@sun.com 1092*10934Ssommerfeld@sun.com ASSERT(sq.af == AF_INET6); 1093*10934Ssommerfeld@sun.com 10940Sstevel@tonic-gate ip_wput_ipsec_out_v6(NULL, 10950Sstevel@tonic-gate mp, ip6h, NULL, NULL); 10960Sstevel@tonic-gate } 10970Sstevel@tonic-gate } 10980Sstevel@tonic-gate continue; 10990Sstevel@tonic-gate } 11000Sstevel@tonic-gate } 11013448Sdh155122 AH_BUMP_STAT(ahstack, out_discards); 11020Sstevel@tonic-gate ip_drop_packet(mp, B_FALSE, NULL, NULL, 11033448Sdh155122 DROPPER(ipss, ipds_sadb_acquire_timeout), 11043448Sdh155122 &ahstack->ah_dropper); 11050Sstevel@tonic-gate } 11060Sstevel@tonic-gate 11070Sstevel@tonic-gate return (rc); 11080Sstevel@tonic-gate } 11090Sstevel@tonic-gate 11100Sstevel@tonic-gate /* 11110Sstevel@tonic-gate * Add new AH security association. This may become a generic AH/ESP 11120Sstevel@tonic-gate * routine eventually. 11130Sstevel@tonic-gate */ 11140Sstevel@tonic-gate static int 11153448Sdh155122 ah_add_sa(mblk_t *mp, keysock_in_t *ksi, int *diagnostic, netstack_t *ns) 11160Sstevel@tonic-gate { 11170Sstevel@tonic-gate sadb_sa_t *assoc = (sadb_sa_t *)ksi->ks_in_extv[SADB_EXT_SA]; 11180Sstevel@tonic-gate sadb_address_t *srcext = 11190Sstevel@tonic-gate (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_SRC]; 11200Sstevel@tonic-gate sadb_address_t *dstext = 11210Sstevel@tonic-gate (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_DST]; 11223055Sdanmcd sadb_address_t *isrcext = 11233055Sdanmcd (sadb_address_t *)ksi->ks_in_extv[SADB_X_EXT_ADDRESS_INNER_SRC]; 11243055Sdanmcd sadb_address_t *idstext = 11253055Sdanmcd (sadb_address_t *)ksi->ks_in_extv[SADB_X_EXT_ADDRESS_INNER_DST]; 11260Sstevel@tonic-gate sadb_key_t *key = (sadb_key_t *)ksi->ks_in_extv[SADB_EXT_KEY_AUTH]; 11270Sstevel@tonic-gate struct sockaddr_in *src, *dst; 11280Sstevel@tonic-gate /* We don't need sockaddr_in6 for now. */ 11290Sstevel@tonic-gate sadb_lifetime_t *soft = 11300Sstevel@tonic-gate (sadb_lifetime_t *)ksi->ks_in_extv[SADB_EXT_LIFETIME_SOFT]; 11310Sstevel@tonic-gate sadb_lifetime_t *hard = 11320Sstevel@tonic-gate (sadb_lifetime_t *)ksi->ks_in_extv[SADB_EXT_LIFETIME_HARD]; 11337749SThejaswini.Singarajipura@Sun.COM sadb_lifetime_t *idle = 11347749SThejaswini.Singarajipura@Sun.COM (sadb_lifetime_t *)ksi->ks_in_extv[SADB_X_EXT_LIFETIME_IDLE]; 11350Sstevel@tonic-gate ipsec_alginfo_t *aalg; 11363448Sdh155122 ipsecah_stack_t *ahstack = ns->netstack_ipsecah; 11373448Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 11380Sstevel@tonic-gate 11390Sstevel@tonic-gate /* I need certain extensions present for an ADD message. */ 11400Sstevel@tonic-gate if (srcext == NULL) { 11410Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_MISSING_SRC; 11420Sstevel@tonic-gate return (EINVAL); 11430Sstevel@tonic-gate } 11440Sstevel@tonic-gate if (dstext == NULL) { 11450Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_MISSING_DST; 11460Sstevel@tonic-gate return (EINVAL); 11470Sstevel@tonic-gate } 11483055Sdanmcd if (isrcext == NULL && idstext != NULL) { 11493055Sdanmcd *diagnostic = SADB_X_DIAGNOSTIC_MISSING_INNER_SRC; 11503055Sdanmcd return (EINVAL); 11513055Sdanmcd } 11523055Sdanmcd if (isrcext != NULL && idstext == NULL) { 11533055Sdanmcd *diagnostic = SADB_X_DIAGNOSTIC_MISSING_INNER_DST; 11543055Sdanmcd return (EINVAL); 11553055Sdanmcd } 11560Sstevel@tonic-gate if (assoc == NULL) { 11570Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_MISSING_SA; 11580Sstevel@tonic-gate return (EINVAL); 11590Sstevel@tonic-gate } 11600Sstevel@tonic-gate if (key == NULL) { 11610Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_MISSING_AKEY; 11620Sstevel@tonic-gate return (EINVAL); 11630Sstevel@tonic-gate } 11640Sstevel@tonic-gate 11650Sstevel@tonic-gate src = (struct sockaddr_in *)(srcext + 1); 11660Sstevel@tonic-gate dst = (struct sockaddr_in *)(dstext + 1); 11670Sstevel@tonic-gate 11680Sstevel@tonic-gate /* Sundry ADD-specific reality checks. */ 11690Sstevel@tonic-gate /* XXX STATS : Logging/stats here? */ 11700Sstevel@tonic-gate 11717749SThejaswini.Singarajipura@Sun.COM if ((assoc->sadb_sa_state != SADB_SASTATE_MATURE) && 11727749SThejaswini.Singarajipura@Sun.COM (assoc->sadb_sa_state != SADB_X_SASTATE_ACTIVE_ELSEWHERE)) { 11730Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_BAD_SASTATE; 11740Sstevel@tonic-gate return (EINVAL); 11750Sstevel@tonic-gate } 11760Sstevel@tonic-gate if (assoc->sadb_sa_encrypt != SADB_EALG_NONE) { 11770Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_ENCR_NOTSUPP; 11780Sstevel@tonic-gate return (EINVAL); 11790Sstevel@tonic-gate } 11807110Sdanmcd if (assoc->sadb_sa_flags & ~ahstack->ah_sadb.s_addflags) { 11810Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_BAD_SAFLAGS; 11820Sstevel@tonic-gate return (EINVAL); 11830Sstevel@tonic-gate } 11847749SThejaswini.Singarajipura@Sun.COM if ((*diagnostic = sadb_hardsoftchk(hard, soft, idle)) != 0) 11850Sstevel@tonic-gate return (EINVAL); 11860Sstevel@tonic-gate 11873055Sdanmcd ASSERT(src->sin_family == dst->sin_family); 11880Sstevel@tonic-gate 11890Sstevel@tonic-gate /* Stuff I don't support, for now. XXX Diagnostic? */ 1190*10934Ssommerfeld@sun.com if (ksi->ks_in_extv[SADB_EXT_LIFETIME_CURRENT] != NULL) 11910Sstevel@tonic-gate return (EOPNOTSUPP); 11920Sstevel@tonic-gate 1193*10934Ssommerfeld@sun.com if (ksi->ks_in_extv[SADB_EXT_SENSITIVITY] != NULL) { 1194*10934Ssommerfeld@sun.com if (!is_system_labeled()) 1195*10934Ssommerfeld@sun.com return (EOPNOTSUPP); 1196*10934Ssommerfeld@sun.com } 1197*10934Ssommerfeld@sun.com 1198*10934Ssommerfeld@sun.com if (ksi->ks_in_extv[SADB_X_EXT_OUTER_SENS] != NULL) { 1199*10934Ssommerfeld@sun.com if (!is_system_labeled()) 1200*10934Ssommerfeld@sun.com return (EOPNOTSUPP); 1201*10934Ssommerfeld@sun.com } 12020Sstevel@tonic-gate /* 1203*10934Ssommerfeld@sun.com * XXX Policy : I'm not checking identities at this time, but 1204*10934Ssommerfeld@sun.com * if I did, I'd do them here, before I sent the weak key 1205*10934Ssommerfeld@sun.com * check up to the algorithm. 12060Sstevel@tonic-gate */ 12070Sstevel@tonic-gate 12080Sstevel@tonic-gate /* verify that there is a mapping for the specified algorithm */ 12093448Sdh155122 mutex_enter(&ipss->ipsec_alg_lock); 12103448Sdh155122 aalg = ipss->ipsec_alglists[IPSEC_ALG_AUTH][assoc->sadb_sa_auth]; 12110Sstevel@tonic-gate if (aalg == NULL || !ALG_VALID(aalg)) { 12123448Sdh155122 mutex_exit(&ipss->ipsec_alg_lock); 12133448Sdh155122 ah1dbg(ahstack, ("Couldn't find auth alg #%d.\n", 12144987Sdanmcd assoc->sadb_sa_auth)); 12150Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_BAD_AALG; 12160Sstevel@tonic-gate return (EINVAL); 12170Sstevel@tonic-gate } 12180Sstevel@tonic-gate ASSERT(aalg->alg_mech_type != CRYPTO_MECHANISM_INVALID); 12190Sstevel@tonic-gate 12200Sstevel@tonic-gate /* sanity check key sizes */ 12210Sstevel@tonic-gate if (!ipsec_valid_key_size(key->sadb_key_bits, aalg)) { 12223448Sdh155122 mutex_exit(&ipss->ipsec_alg_lock); 12230Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_BAD_AKEYBITS; 12240Sstevel@tonic-gate return (EINVAL); 12250Sstevel@tonic-gate } 12260Sstevel@tonic-gate 12270Sstevel@tonic-gate /* check key and fix parity if needed */ 12280Sstevel@tonic-gate if (ipsec_check_key(aalg->alg_mech_type, key, B_TRUE, 12290Sstevel@tonic-gate diagnostic) != 0) { 12303448Sdh155122 mutex_exit(&ipss->ipsec_alg_lock); 12310Sstevel@tonic-gate return (EINVAL); 12320Sstevel@tonic-gate } 12330Sstevel@tonic-gate 12343448Sdh155122 mutex_exit(&ipss->ipsec_alg_lock); 12350Sstevel@tonic-gate 12363055Sdanmcd return (ah_add_sa_finish(mp, (sadb_msg_t *)mp->b_cont->b_rptr, ksi, 12374987Sdanmcd diagnostic, ahstack)); 12380Sstevel@tonic-gate } 12390Sstevel@tonic-gate 1240*10934Ssommerfeld@sun.com /* Refactor me */ 12410Sstevel@tonic-gate /* 12420Sstevel@tonic-gate * Update a security association. Updates come in two varieties. The first 12430Sstevel@tonic-gate * is an update of lifetimes on a non-larval SA. The second is an update of 12440Sstevel@tonic-gate * a larval SA, which ends up looking a lot more like an add. 12450Sstevel@tonic-gate */ 12460Sstevel@tonic-gate static int 12473448Sdh155122 ah_update_sa(mblk_t *mp, keysock_in_t *ksi, int *diagnostic, 12486668Smarkfen ipsecah_stack_t *ahstack, uint8_t sadb_msg_type) 12490Sstevel@tonic-gate { 12507749SThejaswini.Singarajipura@Sun.COM sadb_sa_t *assoc = (sadb_sa_t *)ksi->ks_in_extv[SADB_EXT_SA]; 12510Sstevel@tonic-gate sadb_address_t *dstext = 12520Sstevel@tonic-gate (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_DST]; 12537749SThejaswini.Singarajipura@Sun.COM mblk_t *buf_pkt; 12547749SThejaswini.Singarajipura@Sun.COM int rcode; 12550Sstevel@tonic-gate 12560Sstevel@tonic-gate if (dstext == NULL) { 12570Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_MISSING_DST; 12580Sstevel@tonic-gate return (EINVAL); 12590Sstevel@tonic-gate } 12607749SThejaswini.Singarajipura@Sun.COM 12617749SThejaswini.Singarajipura@Sun.COM rcode = sadb_update_sa(mp, ksi, &buf_pkt, &ahstack->ah_sadb, 12627749SThejaswini.Singarajipura@Sun.COM diagnostic, ahstack->ah_pfkey_q, ah_add_sa, 12637749SThejaswini.Singarajipura@Sun.COM ahstack->ipsecah_netstack, sadb_msg_type); 12647749SThejaswini.Singarajipura@Sun.COM 12657749SThejaswini.Singarajipura@Sun.COM if ((assoc->sadb_sa_state != SADB_X_SASTATE_ACTIVE) || 12667749SThejaswini.Singarajipura@Sun.COM (rcode != 0)) { 12677749SThejaswini.Singarajipura@Sun.COM return (rcode); 12687749SThejaswini.Singarajipura@Sun.COM } 12697749SThejaswini.Singarajipura@Sun.COM 12708704Sdanmcd@sun.com HANDLE_BUF_PKT(ah_taskq, ahstack->ipsecah_netstack->netstack_ipsec, 12718704Sdanmcd@sun.com ahstack->ah_dropper, buf_pkt); 12727749SThejaswini.Singarajipura@Sun.COM 12737749SThejaswini.Singarajipura@Sun.COM return (rcode); 12740Sstevel@tonic-gate } 12750Sstevel@tonic-gate 1276*10934Ssommerfeld@sun.com /* Refactor me */ 12770Sstevel@tonic-gate /* 12780Sstevel@tonic-gate * Delete a security association. This is REALLY likely to be code common to 12790Sstevel@tonic-gate * both AH and ESP. Find the association, then unlink it. 12800Sstevel@tonic-gate */ 12810Sstevel@tonic-gate static int 12823448Sdh155122 ah_del_sa(mblk_t *mp, keysock_in_t *ksi, int *diagnostic, 12836668Smarkfen ipsecah_stack_t *ahstack, uint8_t sadb_msg_type) 12840Sstevel@tonic-gate { 12850Sstevel@tonic-gate sadb_sa_t *assoc = (sadb_sa_t *)ksi->ks_in_extv[SADB_EXT_SA]; 12860Sstevel@tonic-gate sadb_address_t *dstext = 12870Sstevel@tonic-gate (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_DST]; 12880Sstevel@tonic-gate sadb_address_t *srcext = 12890Sstevel@tonic-gate (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_SRC]; 12900Sstevel@tonic-gate struct sockaddr_in *sin; 12910Sstevel@tonic-gate 12920Sstevel@tonic-gate if (assoc == NULL) { 12930Sstevel@tonic-gate if (dstext != NULL) 12940Sstevel@tonic-gate sin = (struct sockaddr_in *)(dstext + 1); 12950Sstevel@tonic-gate else if (srcext != NULL) 12960Sstevel@tonic-gate sin = (struct sockaddr_in *)(srcext + 1); 12970Sstevel@tonic-gate else { 12980Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_MISSING_SA; 12990Sstevel@tonic-gate return (EINVAL); 13000Sstevel@tonic-gate } 13013055Sdanmcd return (sadb_purge_sa(mp, ksi, 13023448Sdh155122 (sin->sin_family == AF_INET6) ? &ahstack->ah_sadb.s_v6 : 1303*10934Ssommerfeld@sun.com &ahstack->ah_sadb.s_v4, diagnostic, ahstack->ah_pfkey_q, 1304*10934Ssommerfeld@sun.com ahstack->ah_sadb.s_ip_q)); 13050Sstevel@tonic-gate } 13060Sstevel@tonic-gate 13076668Smarkfen return (sadb_delget_sa(mp, ksi, &ahstack->ah_sadb, diagnostic, 13086668Smarkfen ahstack->ah_pfkey_q, sadb_msg_type)); 13090Sstevel@tonic-gate } 13100Sstevel@tonic-gate 1311*10934Ssommerfeld@sun.com /* Refactor me */ 13120Sstevel@tonic-gate /* 13130Sstevel@tonic-gate * Convert the entire contents of all of AH's SA tables into PF_KEY SADB_DUMP 13140Sstevel@tonic-gate * messages. 13150Sstevel@tonic-gate */ 13160Sstevel@tonic-gate static void 13173448Sdh155122 ah_dump(mblk_t *mp, keysock_in_t *ksi, ipsecah_stack_t *ahstack) 13180Sstevel@tonic-gate { 13190Sstevel@tonic-gate int error; 13200Sstevel@tonic-gate sadb_msg_t *samsg; 13210Sstevel@tonic-gate 13220Sstevel@tonic-gate /* 13230Sstevel@tonic-gate * Dump each fanout, bailing if error is non-zero. 13240Sstevel@tonic-gate */ 13250Sstevel@tonic-gate 13267749SThejaswini.Singarajipura@Sun.COM error = sadb_dump(ahstack->ah_pfkey_q, mp, ksi, &ahstack->ah_sadb.s_v4); 13270Sstevel@tonic-gate if (error != 0) 13280Sstevel@tonic-gate goto bail; 13290Sstevel@tonic-gate 13307749SThejaswini.Singarajipura@Sun.COM error = sadb_dump(ahstack->ah_pfkey_q, mp, ksi, &ahstack->ah_sadb.s_v6); 13310Sstevel@tonic-gate bail: 13320Sstevel@tonic-gate ASSERT(mp->b_cont != NULL); 13330Sstevel@tonic-gate samsg = (sadb_msg_t *)mp->b_cont->b_rptr; 13340Sstevel@tonic-gate samsg->sadb_msg_errno = (uint8_t)error; 13353448Sdh155122 sadb_pfkey_echo(ahstack->ah_pfkey_q, mp, 13363448Sdh155122 (sadb_msg_t *)mp->b_cont->b_rptr, ksi, NULL); 13370Sstevel@tonic-gate } 13380Sstevel@tonic-gate 13390Sstevel@tonic-gate /* 13403055Sdanmcd * First-cut reality check for an inbound PF_KEY message. 13413055Sdanmcd */ 13423055Sdanmcd static boolean_t 13433448Sdh155122 ah_pfkey_reality_failures(mblk_t *mp, keysock_in_t *ksi, 13443448Sdh155122 ipsecah_stack_t *ahstack) 13453055Sdanmcd { 13463055Sdanmcd int diagnostic; 13473055Sdanmcd 13483055Sdanmcd if (mp->b_cont == NULL) { 13493055Sdanmcd freemsg(mp); 13503055Sdanmcd return (B_TRUE); 13513055Sdanmcd } 13523055Sdanmcd 13533055Sdanmcd if (ksi->ks_in_extv[SADB_EXT_KEY_ENCRYPT] != NULL) { 13543055Sdanmcd diagnostic = SADB_X_DIAGNOSTIC_EKEY_PRESENT; 13553055Sdanmcd goto badmsg; 13563055Sdanmcd } 13573055Sdanmcd if (ksi->ks_in_extv[SADB_EXT_PROPOSAL] != NULL) { 13583055Sdanmcd diagnostic = SADB_X_DIAGNOSTIC_PROP_PRESENT; 13593055Sdanmcd goto badmsg; 13603055Sdanmcd } 13613055Sdanmcd if (ksi->ks_in_extv[SADB_EXT_SUPPORTED_AUTH] != NULL || 13623055Sdanmcd ksi->ks_in_extv[SADB_EXT_SUPPORTED_ENCRYPT] != NULL) { 13633055Sdanmcd diagnostic = SADB_X_DIAGNOSTIC_SUPP_PRESENT; 13643055Sdanmcd goto badmsg; 13653055Sdanmcd } 13663055Sdanmcd return (B_FALSE); /* False ==> no failures */ 13673055Sdanmcd 13683055Sdanmcd badmsg: 13693448Sdh155122 sadb_pfkey_error(ahstack->ah_pfkey_q, mp, EINVAL, 13703448Sdh155122 diagnostic, ksi->ks_in_serial); 13713055Sdanmcd return (B_TRUE); /* True ==> failures */ 13723055Sdanmcd } 13733055Sdanmcd 13743055Sdanmcd /* 13750Sstevel@tonic-gate * AH parsing of PF_KEY messages. Keysock did most of the really silly 13760Sstevel@tonic-gate * error cases. What I receive is a fully-formed, syntactically legal 13770Sstevel@tonic-gate * PF_KEY message. I then need to check semantics... 13780Sstevel@tonic-gate * 13790Sstevel@tonic-gate * This code may become common to AH and ESP. Stay tuned. 13800Sstevel@tonic-gate * 13810Sstevel@tonic-gate * I also make the assumption that db_ref's are cool. If this assumption 13820Sstevel@tonic-gate * is wrong, this means that someone other than keysock or me has been 13830Sstevel@tonic-gate * mucking with PF_KEY messages. 13840Sstevel@tonic-gate */ 13850Sstevel@tonic-gate static void 13863448Sdh155122 ah_parse_pfkey(mblk_t *mp, ipsecah_stack_t *ahstack) 13870Sstevel@tonic-gate { 13880Sstevel@tonic-gate mblk_t *msg = mp->b_cont; 13890Sstevel@tonic-gate sadb_msg_t *samsg; 13900Sstevel@tonic-gate keysock_in_t *ksi; 13910Sstevel@tonic-gate int error; 13920Sstevel@tonic-gate int diagnostic = SADB_X_DIAGNOSTIC_NONE; 13930Sstevel@tonic-gate 13940Sstevel@tonic-gate ASSERT(msg != NULL); 13953448Sdh155122 13960Sstevel@tonic-gate samsg = (sadb_msg_t *)msg->b_rptr; 13970Sstevel@tonic-gate ksi = (keysock_in_t *)mp->b_rptr; 13980Sstevel@tonic-gate 13990Sstevel@tonic-gate /* 14000Sstevel@tonic-gate * If applicable, convert unspecified AF_INET6 to unspecified 14010Sstevel@tonic-gate * AF_INET. 14020Sstevel@tonic-gate */ 14033448Sdh155122 if (!sadb_addrfix(ksi, ahstack->ah_pfkey_q, mp, 14043448Sdh155122 ahstack->ipsecah_netstack) || 14053448Sdh155122 ah_pfkey_reality_failures(mp, ksi, ahstack)) { 14063055Sdanmcd return; 14073055Sdanmcd } 14080Sstevel@tonic-gate 14090Sstevel@tonic-gate switch (samsg->sadb_msg_type) { 14100Sstevel@tonic-gate case SADB_ADD: 14113448Sdh155122 error = ah_add_sa(mp, ksi, &diagnostic, 14123448Sdh155122 ahstack->ipsecah_netstack); 14130Sstevel@tonic-gate if (error != 0) { 14143448Sdh155122 sadb_pfkey_error(ahstack->ah_pfkey_q, mp, error, 14153448Sdh155122 diagnostic, ksi->ks_in_serial); 14160Sstevel@tonic-gate } 14170Sstevel@tonic-gate /* else ah_add_sa() took care of things. */ 14180Sstevel@tonic-gate break; 14190Sstevel@tonic-gate case SADB_DELETE: 14206668Smarkfen case SADB_X_DELPAIR: 14217749SThejaswini.Singarajipura@Sun.COM case SADB_X_DELPAIR_STATE: 14226668Smarkfen error = ah_del_sa(mp, ksi, &diagnostic, ahstack, 14236668Smarkfen samsg->sadb_msg_type); 14240Sstevel@tonic-gate if (error != 0) { 14253448Sdh155122 sadb_pfkey_error(ahstack->ah_pfkey_q, mp, error, 14263448Sdh155122 diagnostic, ksi->ks_in_serial); 14270Sstevel@tonic-gate } 14280Sstevel@tonic-gate /* Else ah_del_sa() took care of things. */ 14290Sstevel@tonic-gate break; 14300Sstevel@tonic-gate case SADB_GET: 14316668Smarkfen error = sadb_delget_sa(mp, ksi, &ahstack->ah_sadb, &diagnostic, 14326668Smarkfen ahstack->ah_pfkey_q, samsg->sadb_msg_type); 14330Sstevel@tonic-gate if (error != 0) { 14343448Sdh155122 sadb_pfkey_error(ahstack->ah_pfkey_q, mp, error, 14353448Sdh155122 diagnostic, ksi->ks_in_serial); 14360Sstevel@tonic-gate } 14370Sstevel@tonic-gate /* Else sadb_get_sa() took care of things. */ 14380Sstevel@tonic-gate break; 14390Sstevel@tonic-gate case SADB_FLUSH: 14403448Sdh155122 sadbp_flush(&ahstack->ah_sadb, ahstack->ipsecah_netstack); 14413448Sdh155122 sadb_pfkey_echo(ahstack->ah_pfkey_q, mp, samsg, ksi, NULL); 14420Sstevel@tonic-gate break; 14430Sstevel@tonic-gate case SADB_REGISTER: 14440Sstevel@tonic-gate /* 14450Sstevel@tonic-gate * Hmmm, let's do it! Check for extensions (there should 14460Sstevel@tonic-gate * be none), extract the fields, call ah_register_out(), 14470Sstevel@tonic-gate * then either free or report an error. 14480Sstevel@tonic-gate * 14490Sstevel@tonic-gate * Keysock takes care of the PF_KEY bookkeeping for this. 14500Sstevel@tonic-gate */ 14510Sstevel@tonic-gate if (ah_register_out(samsg->sadb_msg_seq, samsg->sadb_msg_pid, 1452*10934Ssommerfeld@sun.com ksi->ks_in_serial, ahstack, mp)) { 14530Sstevel@tonic-gate freemsg(mp); 14540Sstevel@tonic-gate } else { 14550Sstevel@tonic-gate /* 14560Sstevel@tonic-gate * Only way this path hits is if there is a memory 14570Sstevel@tonic-gate * failure. It will not return B_FALSE because of 14580Sstevel@tonic-gate * lack of ah_pfkey_q if I am in wput(). 14590Sstevel@tonic-gate */ 14603448Sdh155122 sadb_pfkey_error(ahstack->ah_pfkey_q, mp, ENOMEM, 14613448Sdh155122 diagnostic, ksi->ks_in_serial); 14620Sstevel@tonic-gate } 14630Sstevel@tonic-gate break; 14640Sstevel@tonic-gate case SADB_UPDATE: 14656668Smarkfen case SADB_X_UPDATEPAIR: 14660Sstevel@tonic-gate /* 14670Sstevel@tonic-gate * Find a larval, if not there, find a full one and get 14680Sstevel@tonic-gate * strict. 14690Sstevel@tonic-gate */ 14706668Smarkfen error = ah_update_sa(mp, ksi, &diagnostic, ahstack, 14716668Smarkfen samsg->sadb_msg_type); 14720Sstevel@tonic-gate if (error != 0) { 14733448Sdh155122 sadb_pfkey_error(ahstack->ah_pfkey_q, mp, error, 14743448Sdh155122 diagnostic, ksi->ks_in_serial); 14750Sstevel@tonic-gate } 14760Sstevel@tonic-gate /* else ah_update_sa() took care of things. */ 14770Sstevel@tonic-gate break; 14780Sstevel@tonic-gate case SADB_GETSPI: 14790Sstevel@tonic-gate /* 14800Sstevel@tonic-gate * Reserve a new larval entry. 14810Sstevel@tonic-gate */ 14823448Sdh155122 ah_getspi(mp, ksi, ahstack); 14830Sstevel@tonic-gate break; 14840Sstevel@tonic-gate case SADB_ACQUIRE: 14850Sstevel@tonic-gate /* 14860Sstevel@tonic-gate * Find larval and/or ACQUIRE record and kill it (them), I'm 14870Sstevel@tonic-gate * most likely an error. Inbound ACQUIRE messages should only 14880Sstevel@tonic-gate * have the base header. 14890Sstevel@tonic-gate */ 14903448Sdh155122 sadb_in_acquire(samsg, &ahstack->ah_sadb, ahstack->ah_pfkey_q, 14913448Sdh155122 ahstack->ipsecah_netstack); 14920Sstevel@tonic-gate freemsg(mp); 14930Sstevel@tonic-gate break; 14940Sstevel@tonic-gate case SADB_DUMP: 14950Sstevel@tonic-gate /* 14960Sstevel@tonic-gate * Dump all entries. 14970Sstevel@tonic-gate */ 14983448Sdh155122 ah_dump(mp, ksi, ahstack); 14990Sstevel@tonic-gate /* ah_dump will take care of the return message, etc. */ 15000Sstevel@tonic-gate break; 15010Sstevel@tonic-gate case SADB_EXPIRE: 15020Sstevel@tonic-gate /* Should never reach me. */ 15033448Sdh155122 sadb_pfkey_error(ahstack->ah_pfkey_q, mp, EOPNOTSUPP, 15043448Sdh155122 diagnostic, ksi->ks_in_serial); 15050Sstevel@tonic-gate break; 15060Sstevel@tonic-gate default: 15073448Sdh155122 sadb_pfkey_error(ahstack->ah_pfkey_q, mp, EINVAL, 15080Sstevel@tonic-gate SADB_X_DIAGNOSTIC_UNKNOWN_MSG, ksi->ks_in_serial); 15090Sstevel@tonic-gate break; 15100Sstevel@tonic-gate } 15110Sstevel@tonic-gate } 15120Sstevel@tonic-gate 15130Sstevel@tonic-gate /* 15140Sstevel@tonic-gate * Handle case where PF_KEY says it can't find a keysock for one of my 15150Sstevel@tonic-gate * ACQUIRE messages. 15160Sstevel@tonic-gate */ 15170Sstevel@tonic-gate static void 15183448Sdh155122 ah_keysock_no_socket(mblk_t *mp, ipsecah_stack_t *ahstack) 15190Sstevel@tonic-gate { 15200Sstevel@tonic-gate sadb_msg_t *samsg; 15210Sstevel@tonic-gate keysock_out_err_t *kse = (keysock_out_err_t *)mp->b_rptr; 15220Sstevel@tonic-gate 15230Sstevel@tonic-gate if (mp->b_cont == NULL) { 15240Sstevel@tonic-gate freemsg(mp); 15250Sstevel@tonic-gate return; 15260Sstevel@tonic-gate } 15270Sstevel@tonic-gate samsg = (sadb_msg_t *)mp->b_cont->b_rptr; 15280Sstevel@tonic-gate 15290Sstevel@tonic-gate /* 15300Sstevel@tonic-gate * If keysock can't find any registered, delete the acquire record 15310Sstevel@tonic-gate * immediately, and handle errors. 15320Sstevel@tonic-gate */ 15330Sstevel@tonic-gate if (samsg->sadb_msg_type == SADB_ACQUIRE) { 15340Sstevel@tonic-gate samsg->sadb_msg_errno = kse->ks_err_errno; 15350Sstevel@tonic-gate samsg->sadb_msg_len = SADB_8TO64(sizeof (*samsg)); 15360Sstevel@tonic-gate /* 15370Sstevel@tonic-gate * Use the write-side of the ah_pfkey_q, in case there is 15383448Sdh155122 * no ahstack->ah_sadb.s_ip_q. 15390Sstevel@tonic-gate */ 15403448Sdh155122 sadb_in_acquire(samsg, &ahstack->ah_sadb, 15413448Sdh155122 WR(ahstack->ah_pfkey_q), ahstack->ipsecah_netstack); 15420Sstevel@tonic-gate } 15430Sstevel@tonic-gate 15440Sstevel@tonic-gate freemsg(mp); 15450Sstevel@tonic-gate } 15460Sstevel@tonic-gate 15470Sstevel@tonic-gate /* 15480Sstevel@tonic-gate * AH module write put routine. 15490Sstevel@tonic-gate */ 15500Sstevel@tonic-gate static void 15510Sstevel@tonic-gate ipsecah_wput(queue_t *q, mblk_t *mp) 15520Sstevel@tonic-gate { 15530Sstevel@tonic-gate ipsec_info_t *ii; 15540Sstevel@tonic-gate struct iocblk *iocp; 15553448Sdh155122 ipsecah_stack_t *ahstack = (ipsecah_stack_t *)q->q_ptr; 15563448Sdh155122 15573448Sdh155122 ah3dbg(ahstack, ("In ah_wput().\n")); 15580Sstevel@tonic-gate 15590Sstevel@tonic-gate /* NOTE: Each case must take care of freeing or passing mp. */ 15600Sstevel@tonic-gate switch (mp->b_datap->db_type) { 15610Sstevel@tonic-gate case M_CTL: 15620Sstevel@tonic-gate if ((mp->b_wptr - mp->b_rptr) < sizeof (ipsec_info_t)) { 15630Sstevel@tonic-gate /* Not big enough message. */ 15640Sstevel@tonic-gate freemsg(mp); 15650Sstevel@tonic-gate break; 15660Sstevel@tonic-gate } 15670Sstevel@tonic-gate ii = (ipsec_info_t *)mp->b_rptr; 15680Sstevel@tonic-gate 15690Sstevel@tonic-gate switch (ii->ipsec_info_type) { 15700Sstevel@tonic-gate case KEYSOCK_OUT_ERR: 15713448Sdh155122 ah1dbg(ahstack, ("Got KEYSOCK_OUT_ERR message.\n")); 15723448Sdh155122 ah_keysock_no_socket(mp, ahstack); 15730Sstevel@tonic-gate break; 15740Sstevel@tonic-gate case KEYSOCK_IN: 15753448Sdh155122 AH_BUMP_STAT(ahstack, keysock_in); 15763448Sdh155122 ah3dbg(ahstack, ("Got KEYSOCK_IN message.\n")); 15773055Sdanmcd 15783055Sdanmcd /* Parse the message. */ 15793448Sdh155122 ah_parse_pfkey(mp, ahstack); 15800Sstevel@tonic-gate break; 15810Sstevel@tonic-gate case KEYSOCK_HELLO: 15823448Sdh155122 sadb_keysock_hello(&ahstack->ah_pfkey_q, q, mp, 15833448Sdh155122 ah_ager, (void *)ahstack, &ahstack->ah_event, 15843448Sdh155122 SADB_SATYPE_AH); 15850Sstevel@tonic-gate break; 15860Sstevel@tonic-gate default: 15873448Sdh155122 ah1dbg(ahstack, ("Got M_CTL from above of 0x%x.\n", 15880Sstevel@tonic-gate ii->ipsec_info_type)); 15890Sstevel@tonic-gate freemsg(mp); 15900Sstevel@tonic-gate break; 15910Sstevel@tonic-gate } 15920Sstevel@tonic-gate break; 15930Sstevel@tonic-gate case M_IOCTL: 15940Sstevel@tonic-gate iocp = (struct iocblk *)mp->b_rptr; 15950Sstevel@tonic-gate switch (iocp->ioc_cmd) { 15960Sstevel@tonic-gate case ND_SET: 15970Sstevel@tonic-gate case ND_GET: 15983448Sdh155122 if (nd_getset(q, ahstack->ipsecah_g_nd, mp)) { 15990Sstevel@tonic-gate qreply(q, mp); 16000Sstevel@tonic-gate return; 16010Sstevel@tonic-gate } else { 16020Sstevel@tonic-gate iocp->ioc_error = ENOENT; 16030Sstevel@tonic-gate } 16040Sstevel@tonic-gate /* FALLTHRU */ 16050Sstevel@tonic-gate default: 16060Sstevel@tonic-gate /* We really don't support any other ioctls, do we? */ 16070Sstevel@tonic-gate 16080Sstevel@tonic-gate /* Return EINVAL */ 16090Sstevel@tonic-gate if (iocp->ioc_error != ENOENT) 16100Sstevel@tonic-gate iocp->ioc_error = EINVAL; 16110Sstevel@tonic-gate iocp->ioc_count = 0; 16120Sstevel@tonic-gate mp->b_datap->db_type = M_IOCACK; 16130Sstevel@tonic-gate qreply(q, mp); 16140Sstevel@tonic-gate return; 16150Sstevel@tonic-gate } 16160Sstevel@tonic-gate default: 16173448Sdh155122 ah3dbg(ahstack, 16183448Sdh155122 ("Got default message, type %d, passing to IP.\n", 16190Sstevel@tonic-gate mp->b_datap->db_type)); 16200Sstevel@tonic-gate putnext(q, mp); 16210Sstevel@tonic-gate } 16220Sstevel@tonic-gate } 16230Sstevel@tonic-gate 1624*10934Ssommerfeld@sun.com /* Refactor me */ 16250Sstevel@tonic-gate /* 16260Sstevel@tonic-gate * Updating use times can be tricky business if the ipsa_haspeer flag is 16270Sstevel@tonic-gate * set. This function is called once in an SA's lifetime. 16280Sstevel@tonic-gate * 16290Sstevel@tonic-gate * Caller has to REFRELE "assoc" which is passed in. This function has 16300Sstevel@tonic-gate * to REFRELE any peer SA that is obtained. 16310Sstevel@tonic-gate */ 16320Sstevel@tonic-gate static void 16330Sstevel@tonic-gate ah_set_usetime(ipsa_t *assoc, boolean_t inbound) 16340Sstevel@tonic-gate { 16350Sstevel@tonic-gate ipsa_t *inassoc, *outassoc; 16360Sstevel@tonic-gate isaf_t *bucket; 16370Sstevel@tonic-gate sadb_t *sp; 16380Sstevel@tonic-gate int outhash; 16390Sstevel@tonic-gate boolean_t isv6; 16403448Sdh155122 netstack_t *ns = assoc->ipsa_netstack; 16413448Sdh155122 ipsecah_stack_t *ahstack = ns->netstack_ipsecah; 16420Sstevel@tonic-gate 16430Sstevel@tonic-gate /* No peer? No problem! */ 16440Sstevel@tonic-gate if (!assoc->ipsa_haspeer) { 16450Sstevel@tonic-gate sadb_set_usetime(assoc); 16460Sstevel@tonic-gate return; 16470Sstevel@tonic-gate } 16480Sstevel@tonic-gate 16490Sstevel@tonic-gate /* 16500Sstevel@tonic-gate * Otherwise, we want to grab both the original assoc and its peer. 16510Sstevel@tonic-gate * There might be a race for this, but if it's a real race, the times 16520Sstevel@tonic-gate * will be out-of-synch by at most a second, and since our time 16530Sstevel@tonic-gate * granularity is a second, this won't be a problem. 16540Sstevel@tonic-gate * 16550Sstevel@tonic-gate * If we need tight synchronization on the peer SA, then we need to 16560Sstevel@tonic-gate * reconsider. 16570Sstevel@tonic-gate */ 16580Sstevel@tonic-gate 16590Sstevel@tonic-gate /* Use address family to select IPv6/IPv4 */ 16600Sstevel@tonic-gate isv6 = (assoc->ipsa_addrfam == AF_INET6); 16610Sstevel@tonic-gate if (isv6) { 16623448Sdh155122 sp = &ahstack->ah_sadb.s_v6; 16630Sstevel@tonic-gate } else { 16643448Sdh155122 sp = &ahstack->ah_sadb.s_v4; 16650Sstevel@tonic-gate ASSERT(assoc->ipsa_addrfam == AF_INET); 16660Sstevel@tonic-gate } 16670Sstevel@tonic-gate if (inbound) { 16680Sstevel@tonic-gate inassoc = assoc; 16690Sstevel@tonic-gate if (isv6) 16704987Sdanmcd outhash = OUTBOUND_HASH_V6(sp, 16714987Sdanmcd *((in6_addr_t *)&inassoc->ipsa_dstaddr)); 16720Sstevel@tonic-gate else 16734987Sdanmcd outhash = OUTBOUND_HASH_V4(sp, 16744987Sdanmcd *((ipaddr_t *)&inassoc->ipsa_dstaddr)); 16750Sstevel@tonic-gate bucket = &sp->sdb_of[outhash]; 16760Sstevel@tonic-gate 16770Sstevel@tonic-gate mutex_enter(&bucket->isaf_lock); 16780Sstevel@tonic-gate outassoc = ipsec_getassocbyspi(bucket, inassoc->ipsa_spi, 16790Sstevel@tonic-gate inassoc->ipsa_srcaddr, inassoc->ipsa_dstaddr, 16800Sstevel@tonic-gate inassoc->ipsa_addrfam); 16810Sstevel@tonic-gate mutex_exit(&bucket->isaf_lock); 16820Sstevel@tonic-gate if (outassoc == NULL) { 16830Sstevel@tonic-gate /* Q: Do we wish to set haspeer == B_FALSE? */ 16840Sstevel@tonic-gate ah0dbg(("ah_set_usetime: " 16850Sstevel@tonic-gate "can't find peer for inbound.\n")); 16860Sstevel@tonic-gate sadb_set_usetime(inassoc); 16870Sstevel@tonic-gate return; 16880Sstevel@tonic-gate } 16890Sstevel@tonic-gate } else { 16900Sstevel@tonic-gate outassoc = assoc; 1691564Ssommerfe bucket = INBOUND_BUCKET(sp, outassoc->ipsa_spi); 16920Sstevel@tonic-gate mutex_enter(&bucket->isaf_lock); 16930Sstevel@tonic-gate inassoc = ipsec_getassocbyspi(bucket, outassoc->ipsa_spi, 16940Sstevel@tonic-gate outassoc->ipsa_srcaddr, outassoc->ipsa_dstaddr, 16950Sstevel@tonic-gate outassoc->ipsa_addrfam); 16960Sstevel@tonic-gate mutex_exit(&bucket->isaf_lock); 16970Sstevel@tonic-gate if (inassoc == NULL) { 16980Sstevel@tonic-gate /* Q: Do we wish to set haspeer == B_FALSE? */ 16990Sstevel@tonic-gate ah0dbg(("ah_set_usetime: " 17000Sstevel@tonic-gate "can't find peer for outbound.\n")); 17010Sstevel@tonic-gate sadb_set_usetime(outassoc); 17020Sstevel@tonic-gate return; 17030Sstevel@tonic-gate } 17040Sstevel@tonic-gate } 17050Sstevel@tonic-gate 17060Sstevel@tonic-gate /* Update usetime on both. */ 17070Sstevel@tonic-gate sadb_set_usetime(inassoc); 17080Sstevel@tonic-gate sadb_set_usetime(outassoc); 17090Sstevel@tonic-gate 17100Sstevel@tonic-gate /* 17110Sstevel@tonic-gate * REFRELE any peer SA. 17120Sstevel@tonic-gate * 17130Sstevel@tonic-gate * Because of the multi-line macro nature of IPSA_REFRELE, keep 17140Sstevel@tonic-gate * them in { }. 17150Sstevel@tonic-gate */ 17160Sstevel@tonic-gate if (inbound) { 17170Sstevel@tonic-gate IPSA_REFRELE(outassoc); 17180Sstevel@tonic-gate } else { 17190Sstevel@tonic-gate IPSA_REFRELE(inassoc); 17200Sstevel@tonic-gate } 17210Sstevel@tonic-gate } 17220Sstevel@tonic-gate 1723*10934Ssommerfeld@sun.com /* Refactor me */ 17240Sstevel@tonic-gate /* 17250Sstevel@tonic-gate * Add a number of bytes to what the SA has protected so far. Return 17260Sstevel@tonic-gate * B_TRUE if the SA can still protect that many bytes. 17270Sstevel@tonic-gate * 17280Sstevel@tonic-gate * Caller must REFRELE the passed-in assoc. This function must REFRELE 17290Sstevel@tonic-gate * any obtained peer SA. 17300Sstevel@tonic-gate */ 17310Sstevel@tonic-gate static boolean_t 17320Sstevel@tonic-gate ah_age_bytes(ipsa_t *assoc, uint64_t bytes, boolean_t inbound) 17330Sstevel@tonic-gate { 17340Sstevel@tonic-gate ipsa_t *inassoc, *outassoc; 17350Sstevel@tonic-gate isaf_t *bucket; 17360Sstevel@tonic-gate boolean_t inrc, outrc, isv6; 17370Sstevel@tonic-gate sadb_t *sp; 17380Sstevel@tonic-gate int outhash; 17393448Sdh155122 netstack_t *ns = assoc->ipsa_netstack; 17403448Sdh155122 ipsecah_stack_t *ahstack = ns->netstack_ipsecah; 17410Sstevel@tonic-gate 17420Sstevel@tonic-gate /* No peer? No problem! */ 17430Sstevel@tonic-gate if (!assoc->ipsa_haspeer) { 17443448Sdh155122 return (sadb_age_bytes(ahstack->ah_pfkey_q, assoc, bytes, 17450Sstevel@tonic-gate B_TRUE)); 17460Sstevel@tonic-gate } 17470Sstevel@tonic-gate 17480Sstevel@tonic-gate /* 17490Sstevel@tonic-gate * Otherwise, we want to grab both the original assoc and its peer. 17500Sstevel@tonic-gate * There might be a race for this, but if it's a real race, two 17510Sstevel@tonic-gate * expire messages may occur. We limit this by only sending the 17520Sstevel@tonic-gate * expire message on one of the peers, we'll pick the inbound 17530Sstevel@tonic-gate * arbitrarily. 17540Sstevel@tonic-gate * 17550Sstevel@tonic-gate * If we need tight synchronization on the peer SA, then we need to 17560Sstevel@tonic-gate * reconsider. 17570Sstevel@tonic-gate */ 17580Sstevel@tonic-gate 17590Sstevel@tonic-gate /* Pick v4/v6 bucket based on addrfam. */ 17600Sstevel@tonic-gate isv6 = (assoc->ipsa_addrfam == AF_INET6); 17610Sstevel@tonic-gate if (isv6) { 17623448Sdh155122 sp = &ahstack->ah_sadb.s_v6; 17630Sstevel@tonic-gate } else { 17643448Sdh155122 sp = &ahstack->ah_sadb.s_v4; 17650Sstevel@tonic-gate ASSERT(assoc->ipsa_addrfam == AF_INET); 17660Sstevel@tonic-gate } 17670Sstevel@tonic-gate if (inbound) { 17680Sstevel@tonic-gate inassoc = assoc; 17690Sstevel@tonic-gate if (isv6) 17704987Sdanmcd outhash = OUTBOUND_HASH_V6(sp, 17714987Sdanmcd *((in6_addr_t *)&inassoc->ipsa_dstaddr)); 17720Sstevel@tonic-gate else 17734987Sdanmcd outhash = OUTBOUND_HASH_V4(sp, 17744987Sdanmcd *((ipaddr_t *)&inassoc->ipsa_dstaddr)); 17750Sstevel@tonic-gate bucket = &sp->sdb_of[outhash]; 17760Sstevel@tonic-gate mutex_enter(&bucket->isaf_lock); 17770Sstevel@tonic-gate outassoc = ipsec_getassocbyspi(bucket, inassoc->ipsa_spi, 17780Sstevel@tonic-gate inassoc->ipsa_srcaddr, inassoc->ipsa_dstaddr, 17790Sstevel@tonic-gate inassoc->ipsa_addrfam); 17800Sstevel@tonic-gate mutex_exit(&bucket->isaf_lock); 17810Sstevel@tonic-gate if (outassoc == NULL) { 17820Sstevel@tonic-gate /* Q: Do we wish to set haspeer == B_FALSE? */ 17830Sstevel@tonic-gate ah0dbg(("ah_age_bytes: " 17840Sstevel@tonic-gate "can't find peer for inbound.\n")); 17853448Sdh155122 return (sadb_age_bytes(ahstack->ah_pfkey_q, inassoc, 17860Sstevel@tonic-gate bytes, B_TRUE)); 17870Sstevel@tonic-gate } 17880Sstevel@tonic-gate } else { 17890Sstevel@tonic-gate outassoc = assoc; 1790564Ssommerfe bucket = INBOUND_BUCKET(sp, outassoc->ipsa_spi); 17910Sstevel@tonic-gate mutex_enter(&bucket->isaf_lock); 17920Sstevel@tonic-gate inassoc = ipsec_getassocbyspi(bucket, outassoc->ipsa_spi, 17930Sstevel@tonic-gate outassoc->ipsa_srcaddr, outassoc->ipsa_dstaddr, 17940Sstevel@tonic-gate outassoc->ipsa_addrfam); 17950Sstevel@tonic-gate mutex_exit(&bucket->isaf_lock); 17960Sstevel@tonic-gate if (inassoc == NULL) { 17970Sstevel@tonic-gate /* Q: Do we wish to set haspeer == B_FALSE? */ 17980Sstevel@tonic-gate ah0dbg(("ah_age_bytes: " 17990Sstevel@tonic-gate "can't find peer for outbound.\n")); 18003448Sdh155122 return (sadb_age_bytes(ahstack->ah_pfkey_q, outassoc, 18010Sstevel@tonic-gate bytes, B_TRUE)); 18020Sstevel@tonic-gate } 18030Sstevel@tonic-gate } 18040Sstevel@tonic-gate 18053448Sdh155122 inrc = sadb_age_bytes(ahstack->ah_pfkey_q, inassoc, bytes, B_TRUE); 18063448Sdh155122 outrc = sadb_age_bytes(ahstack->ah_pfkey_q, outassoc, bytes, B_FALSE); 18070Sstevel@tonic-gate 18080Sstevel@tonic-gate /* 18090Sstevel@tonic-gate * REFRELE any peer SA. 18100Sstevel@tonic-gate * 18110Sstevel@tonic-gate * Because of the multi-line macro nature of IPSA_REFRELE, keep 18120Sstevel@tonic-gate * them in { }. 18130Sstevel@tonic-gate */ 18140Sstevel@tonic-gate if (inbound) { 18150Sstevel@tonic-gate IPSA_REFRELE(outassoc); 18160Sstevel@tonic-gate } else { 18170Sstevel@tonic-gate IPSA_REFRELE(inassoc); 18180Sstevel@tonic-gate } 18190Sstevel@tonic-gate 18200Sstevel@tonic-gate return (inrc && outrc); 18210Sstevel@tonic-gate } 18220Sstevel@tonic-gate 18230Sstevel@tonic-gate /* 18240Sstevel@tonic-gate * Perform the really difficult work of inserting the proposed situation. 18250Sstevel@tonic-gate * Called while holding the algorithm lock. 18260Sstevel@tonic-gate */ 18270Sstevel@tonic-gate static void 18280Sstevel@tonic-gate ah_insert_prop(sadb_prop_t *prop, ipsacq_t *acqrec, uint_t combs) 18290Sstevel@tonic-gate { 18300Sstevel@tonic-gate sadb_comb_t *comb = (sadb_comb_t *)(prop + 1); 18310Sstevel@tonic-gate ipsec_out_t *io; 18320Sstevel@tonic-gate ipsec_action_t *ap; 18330Sstevel@tonic-gate ipsec_prot_t *prot; 18343448Sdh155122 ipsecah_stack_t *ahstack; 18353448Sdh155122 netstack_t *ns; 18363448Sdh155122 ipsec_stack_t *ipss; 18373448Sdh155122 18380Sstevel@tonic-gate io = (ipsec_out_t *)acqrec->ipsacq_mp->b_rptr; 18390Sstevel@tonic-gate ASSERT(io->ipsec_out_type == IPSEC_OUT); 18400Sstevel@tonic-gate 18413448Sdh155122 ns = io->ipsec_out_ns; 18423448Sdh155122 ipss = ns->netstack_ipsec; 18433448Sdh155122 ahstack = ns->netstack_ipsecah; 18443448Sdh155122 ASSERT(MUTEX_HELD(&ipss->ipsec_alg_lock)); 18453448Sdh155122 18460Sstevel@tonic-gate prop->sadb_prop_exttype = SADB_EXT_PROPOSAL; 18470Sstevel@tonic-gate prop->sadb_prop_len = SADB_8TO64(sizeof (sadb_prop_t)); 18480Sstevel@tonic-gate *(uint32_t *)(&prop->sadb_prop_replay) = 0; /* Quick zero-out! */ 18490Sstevel@tonic-gate 18503448Sdh155122 prop->sadb_prop_replay = ahstack->ipsecah_replay_size; 18510Sstevel@tonic-gate 18520Sstevel@tonic-gate /* 18530Sstevel@tonic-gate * Based upon algorithm properties, and what-not, prioritize a 18540Sstevel@tonic-gate * proposal, based on the ordering of the ah algorithms in the 18550Sstevel@tonic-gate * alternatives presented in the policy rule passed down 18560Sstevel@tonic-gate * through the ipsec_out_t and attached to the acquire record. 18570Sstevel@tonic-gate */ 18580Sstevel@tonic-gate 18590Sstevel@tonic-gate for (ap = acqrec->ipsacq_act; ap != NULL; 18600Sstevel@tonic-gate ap = ap->ipa_next) { 18610Sstevel@tonic-gate ipsec_alginfo_t *aalg; 18620Sstevel@tonic-gate 18630Sstevel@tonic-gate if ((ap->ipa_act.ipa_type != IPSEC_POLICY_APPLY) || 18640Sstevel@tonic-gate (!ap->ipa_act.ipa_apply.ipp_use_ah)) 18650Sstevel@tonic-gate continue; 18660Sstevel@tonic-gate 18670Sstevel@tonic-gate prot = &ap->ipa_act.ipa_apply; 18680Sstevel@tonic-gate 18690Sstevel@tonic-gate ASSERT(prot->ipp_auth_alg > 0); 18700Sstevel@tonic-gate 18713448Sdh155122 aalg = ipss->ipsec_alglists[IPSEC_ALG_AUTH] 18723448Sdh155122 [prot->ipp_auth_alg]; 18730Sstevel@tonic-gate if (aalg == NULL || !ALG_VALID(aalg)) 18740Sstevel@tonic-gate continue; 18750Sstevel@tonic-gate 18760Sstevel@tonic-gate /* XXX check aalg for duplicates??.. */ 18770Sstevel@tonic-gate 18780Sstevel@tonic-gate comb->sadb_comb_flags = 0; 18790Sstevel@tonic-gate comb->sadb_comb_reserved = 0; 18800Sstevel@tonic-gate comb->sadb_comb_encrypt = 0; 18810Sstevel@tonic-gate comb->sadb_comb_encrypt_minbits = 0; 18820Sstevel@tonic-gate comb->sadb_comb_encrypt_maxbits = 0; 18830Sstevel@tonic-gate 18840Sstevel@tonic-gate comb->sadb_comb_auth = aalg->alg_id; 18852751Sdanmcd comb->sadb_comb_auth_minbits = 18862751Sdanmcd MAX(prot->ipp_ah_minbits, aalg->alg_ef_minbits); 18872751Sdanmcd comb->sadb_comb_auth_maxbits = 18882751Sdanmcd MIN(prot->ipp_ah_maxbits, aalg->alg_ef_maxbits); 18890Sstevel@tonic-gate 18900Sstevel@tonic-gate /* 18910Sstevel@tonic-gate * The following may be based on algorithm 18920Sstevel@tonic-gate * properties, but in the meantime, we just pick 18930Sstevel@tonic-gate * some good, sensible numbers. Key mgmt. can 18940Sstevel@tonic-gate * (and perhaps should) be the place to finalize 18950Sstevel@tonic-gate * such decisions. 18960Sstevel@tonic-gate */ 18970Sstevel@tonic-gate 18980Sstevel@tonic-gate /* 18990Sstevel@tonic-gate * No limits on allocations, since we really don't 19000Sstevel@tonic-gate * support that concept currently. 19010Sstevel@tonic-gate */ 19020Sstevel@tonic-gate comb->sadb_comb_soft_allocations = 0; 19030Sstevel@tonic-gate comb->sadb_comb_hard_allocations = 0; 19040Sstevel@tonic-gate 19050Sstevel@tonic-gate /* 19060Sstevel@tonic-gate * These may want to come from policy rule.. 19070Sstevel@tonic-gate */ 19083448Sdh155122 comb->sadb_comb_soft_bytes = 19093448Sdh155122 ahstack->ipsecah_default_soft_bytes; 19103448Sdh155122 comb->sadb_comb_hard_bytes = 19113448Sdh155122 ahstack->ipsecah_default_hard_bytes; 19123448Sdh155122 comb->sadb_comb_soft_addtime = 19133448Sdh155122 ahstack->ipsecah_default_soft_addtime; 19143448Sdh155122 comb->sadb_comb_hard_addtime = 19153448Sdh155122 ahstack->ipsecah_default_hard_addtime; 19163448Sdh155122 comb->sadb_comb_soft_usetime = 19173448Sdh155122 ahstack->ipsecah_default_soft_usetime; 19183448Sdh155122 comb->sadb_comb_hard_usetime = 19193448Sdh155122 ahstack->ipsecah_default_hard_usetime; 19200Sstevel@tonic-gate 19210Sstevel@tonic-gate prop->sadb_prop_len += SADB_8TO64(sizeof (*comb)); 19220Sstevel@tonic-gate if (--combs == 0) 19230Sstevel@tonic-gate return; /* out of space.. */ 19240Sstevel@tonic-gate comb++; 19250Sstevel@tonic-gate } 19260Sstevel@tonic-gate } 19270Sstevel@tonic-gate 19280Sstevel@tonic-gate /* 19290Sstevel@tonic-gate * Prepare and actually send the SADB_ACQUIRE message to PF_KEY. 19300Sstevel@tonic-gate */ 19310Sstevel@tonic-gate static void 19323448Sdh155122 ah_send_acquire(ipsacq_t *acqrec, mblk_t *extended, netstack_t *ns) 19330Sstevel@tonic-gate { 19343055Sdanmcd uint_t combs; 19350Sstevel@tonic-gate sadb_msg_t *samsg; 19360Sstevel@tonic-gate sadb_prop_t *prop; 19373055Sdanmcd mblk_t *pfkeymp, *msgmp; 19383448Sdh155122 ipsecah_stack_t *ahstack = ns->netstack_ipsecah; 19393448Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 19403448Sdh155122 19413448Sdh155122 AH_BUMP_STAT(ahstack, acquire_requests); 19423448Sdh155122 19437073Spwernau if (ahstack->ah_pfkey_q == NULL) { 19447073Spwernau mutex_exit(&acqrec->ipsacq_lock); 19453055Sdanmcd return; 19467073Spwernau } 19470Sstevel@tonic-gate 19480Sstevel@tonic-gate /* Set up ACQUIRE. */ 19493448Sdh155122 pfkeymp = sadb_setup_acquire(acqrec, SADB_SATYPE_AH, 19503448Sdh155122 ns->netstack_ipsec); 19513055Sdanmcd if (pfkeymp == NULL) { 19520Sstevel@tonic-gate ah0dbg(("sadb_setup_acquire failed.\n")); 19537073Spwernau mutex_exit(&acqrec->ipsacq_lock); 19543055Sdanmcd return; 19550Sstevel@tonic-gate } 19563448Sdh155122 ASSERT(MUTEX_HELD(&ipss->ipsec_alg_lock)); 19573448Sdh155122 combs = ipss->ipsec_nalgs[IPSEC_ALG_AUTH]; 19583055Sdanmcd msgmp = pfkeymp->b_cont; 19593055Sdanmcd samsg = (sadb_msg_t *)(msgmp->b_rptr); 19600Sstevel@tonic-gate 19610Sstevel@tonic-gate /* Insert proposal here. */ 19620Sstevel@tonic-gate 19630Sstevel@tonic-gate prop = (sadb_prop_t *)(((uint64_t *)samsg) + samsg->sadb_msg_len); 19640Sstevel@tonic-gate ah_insert_prop(prop, acqrec, combs); 19650Sstevel@tonic-gate samsg->sadb_msg_len += prop->sadb_prop_len; 19660Sstevel@tonic-gate msgmp->b_wptr += SADB_64TO8(samsg->sadb_msg_len); 19670Sstevel@tonic-gate 19683448Sdh155122 mutex_exit(&ipss->ipsec_alg_lock); 19690Sstevel@tonic-gate 19700Sstevel@tonic-gate /* 19710Sstevel@tonic-gate * Must mutex_exit() before sending PF_KEY message up, in 19720Sstevel@tonic-gate * order to avoid recursive mutex_enter() if there are no registered 19730Sstevel@tonic-gate * listeners. 19740Sstevel@tonic-gate * 19750Sstevel@tonic-gate * Once I've sent the message, I'm cool anyway. 19760Sstevel@tonic-gate */ 19770Sstevel@tonic-gate mutex_exit(&acqrec->ipsacq_lock); 19783055Sdanmcd if (extended != NULL) { 19793448Sdh155122 putnext(ahstack->ah_pfkey_q, extended); 19800Sstevel@tonic-gate } 19813448Sdh155122 putnext(ahstack->ah_pfkey_q, pfkeymp); 19820Sstevel@tonic-gate } 19830Sstevel@tonic-gate 1984*10934Ssommerfeld@sun.com /* Refactor me */ 19850Sstevel@tonic-gate /* 19860Sstevel@tonic-gate * Handle the SADB_GETSPI message. Create a larval SA. 19870Sstevel@tonic-gate */ 19880Sstevel@tonic-gate static void 19893448Sdh155122 ah_getspi(mblk_t *mp, keysock_in_t *ksi, ipsecah_stack_t *ahstack) 19900Sstevel@tonic-gate { 19910Sstevel@tonic-gate ipsa_t *newbie, *target; 19920Sstevel@tonic-gate isaf_t *outbound, *inbound; 19930Sstevel@tonic-gate int rc, diagnostic; 19940Sstevel@tonic-gate sadb_sa_t *assoc; 19950Sstevel@tonic-gate keysock_out_t *kso; 19960Sstevel@tonic-gate uint32_t newspi; 19970Sstevel@tonic-gate 19980Sstevel@tonic-gate /* 19990Sstevel@tonic-gate * Randomly generate a proposed SPI value. 20000Sstevel@tonic-gate */ 20017749SThejaswini.Singarajipura@Sun.COM if (cl_inet_getspi != NULL) { 20028392SHuafeng.Lv@Sun.COM cl_inet_getspi(ahstack->ipsecah_netstack->netstack_stackid, 20038392SHuafeng.Lv@Sun.COM IPPROTO_AH, (uint8_t *)&newspi, sizeof (uint32_t), NULL); 20047749SThejaswini.Singarajipura@Sun.COM } else { 20057749SThejaswini.Singarajipura@Sun.COM (void) random_get_pseudo_bytes((uint8_t *)&newspi, 20067749SThejaswini.Singarajipura@Sun.COM sizeof (uint32_t)); 20077749SThejaswini.Singarajipura@Sun.COM } 20083448Sdh155122 newbie = sadb_getspi(ksi, newspi, &diagnostic, 20097749SThejaswini.Singarajipura@Sun.COM ahstack->ipsecah_netstack, IPPROTO_AH); 20100Sstevel@tonic-gate 20110Sstevel@tonic-gate if (newbie == NULL) { 20123448Sdh155122 sadb_pfkey_error(ahstack->ah_pfkey_q, mp, ENOMEM, diagnostic, 20130Sstevel@tonic-gate ksi->ks_in_serial); 20140Sstevel@tonic-gate return; 20150Sstevel@tonic-gate } else if (newbie == (ipsa_t *)-1) { 20163448Sdh155122 sadb_pfkey_error(ahstack->ah_pfkey_q, mp, EINVAL, diagnostic, 20170Sstevel@tonic-gate ksi->ks_in_serial); 20180Sstevel@tonic-gate return; 20190Sstevel@tonic-gate } 20200Sstevel@tonic-gate 20210Sstevel@tonic-gate /* 20220Sstevel@tonic-gate * XXX - We may randomly collide. We really should recover from this. 20230Sstevel@tonic-gate * Unfortunately, that could require spending way-too-much-time 20240Sstevel@tonic-gate * in here. For now, let the user retry. 20250Sstevel@tonic-gate */ 20260Sstevel@tonic-gate 20270Sstevel@tonic-gate if (newbie->ipsa_addrfam == AF_INET6) { 20283448Sdh155122 outbound = OUTBOUND_BUCKET_V6(&ahstack->ah_sadb.s_v6, 2029564Ssommerfe *(uint32_t *)(newbie->ipsa_dstaddr)); 20303448Sdh155122 inbound = INBOUND_BUCKET(&ahstack->ah_sadb.s_v6, 20313448Sdh155122 newbie->ipsa_spi); 20320Sstevel@tonic-gate } else { 20333448Sdh155122 outbound = OUTBOUND_BUCKET_V4(&ahstack->ah_sadb.s_v4, 2034564Ssommerfe *(uint32_t *)(newbie->ipsa_dstaddr)); 20353448Sdh155122 inbound = INBOUND_BUCKET(&ahstack->ah_sadb.s_v4, 20363448Sdh155122 newbie->ipsa_spi); 20370Sstevel@tonic-gate } 20380Sstevel@tonic-gate 20390Sstevel@tonic-gate mutex_enter(&outbound->isaf_lock); 20400Sstevel@tonic-gate mutex_enter(&inbound->isaf_lock); 20410Sstevel@tonic-gate 20420Sstevel@tonic-gate /* 20430Sstevel@tonic-gate * Check for collisions (i.e. did sadb_getspi() return with something 20440Sstevel@tonic-gate * that already exists?). 20450Sstevel@tonic-gate * 20460Sstevel@tonic-gate * Try outbound first. Even though SADB_GETSPI is traditionally 20470Sstevel@tonic-gate * for inbound SAs, you never know what a user might do. 20480Sstevel@tonic-gate */ 20490Sstevel@tonic-gate target = ipsec_getassocbyspi(outbound, newbie->ipsa_spi, 20500Sstevel@tonic-gate newbie->ipsa_srcaddr, newbie->ipsa_dstaddr, newbie->ipsa_addrfam); 20510Sstevel@tonic-gate if (target == NULL) { 20520Sstevel@tonic-gate target = ipsec_getassocbyspi(inbound, newbie->ipsa_spi, 20530Sstevel@tonic-gate newbie->ipsa_srcaddr, newbie->ipsa_dstaddr, 20540Sstevel@tonic-gate newbie->ipsa_addrfam); 20550Sstevel@tonic-gate } 20560Sstevel@tonic-gate 20570Sstevel@tonic-gate /* 20580Sstevel@tonic-gate * I don't have collisions elsewhere! 20590Sstevel@tonic-gate * (Nor will I because I'm still holding inbound/outbound locks.) 20600Sstevel@tonic-gate */ 20610Sstevel@tonic-gate 20620Sstevel@tonic-gate if (target != NULL) { 20630Sstevel@tonic-gate rc = EEXIST; 20640Sstevel@tonic-gate IPSA_REFRELE(target); 20650Sstevel@tonic-gate } else { 20660Sstevel@tonic-gate /* 20670Sstevel@tonic-gate * sadb_insertassoc() also checks for collisions, so 20680Sstevel@tonic-gate * if there's a colliding larval entry, rc will be set 20690Sstevel@tonic-gate * to EEXIST. 20700Sstevel@tonic-gate */ 20710Sstevel@tonic-gate rc = sadb_insertassoc(newbie, inbound); 20724987Sdanmcd newbie->ipsa_hardexpiretime = gethrestime_sec(); 20733448Sdh155122 newbie->ipsa_hardexpiretime += ahstack->ipsecah_larval_timeout; 20740Sstevel@tonic-gate } 20750Sstevel@tonic-gate 20760Sstevel@tonic-gate /* 20770Sstevel@tonic-gate * Can exit outbound mutex. Hold inbound until we're done with 20780Sstevel@tonic-gate * newbie. 20790Sstevel@tonic-gate */ 20800Sstevel@tonic-gate mutex_exit(&outbound->isaf_lock); 20810Sstevel@tonic-gate 20820Sstevel@tonic-gate if (rc != 0) { 20830Sstevel@tonic-gate mutex_exit(&inbound->isaf_lock); 20840Sstevel@tonic-gate IPSA_REFRELE(newbie); 20853448Sdh155122 sadb_pfkey_error(ahstack->ah_pfkey_q, mp, rc, 20863448Sdh155122 SADB_X_DIAGNOSTIC_NONE, ksi->ks_in_serial); 20870Sstevel@tonic-gate return; 20880Sstevel@tonic-gate } 20890Sstevel@tonic-gate 20900Sstevel@tonic-gate /* Can write here because I'm still holding the bucket lock. */ 20910Sstevel@tonic-gate newbie->ipsa_type = SADB_SATYPE_AH; 20920Sstevel@tonic-gate 20930Sstevel@tonic-gate /* 20940Sstevel@tonic-gate * Construct successful return message. We have one thing going 20950Sstevel@tonic-gate * for us in PF_KEY v2. That's the fact that 20960Sstevel@tonic-gate * sizeof (sadb_spirange_t) == sizeof (sadb_sa_t) 20970Sstevel@tonic-gate */ 20980Sstevel@tonic-gate assoc = (sadb_sa_t *)ksi->ks_in_extv[SADB_EXT_SPIRANGE]; 20990Sstevel@tonic-gate assoc->sadb_sa_exttype = SADB_EXT_SA; 21000Sstevel@tonic-gate assoc->sadb_sa_spi = newbie->ipsa_spi; 21010Sstevel@tonic-gate *((uint64_t *)(&assoc->sadb_sa_replay)) = 0; 21020Sstevel@tonic-gate mutex_exit(&inbound->isaf_lock); 21030Sstevel@tonic-gate 21040Sstevel@tonic-gate /* Convert KEYSOCK_IN to KEYSOCK_OUT. */ 21050Sstevel@tonic-gate kso = (keysock_out_t *)ksi; 21060Sstevel@tonic-gate kso->ks_out_len = sizeof (*kso); 21070Sstevel@tonic-gate kso->ks_out_serial = ksi->ks_in_serial; 21080Sstevel@tonic-gate kso->ks_out_type = KEYSOCK_OUT; 21090Sstevel@tonic-gate 21100Sstevel@tonic-gate /* 21110Sstevel@tonic-gate * Can safely putnext() to ah_pfkey_q, because this is a turnaround 21120Sstevel@tonic-gate * from the ah_pfkey_q. 21130Sstevel@tonic-gate */ 21143448Sdh155122 putnext(ahstack->ah_pfkey_q, mp); 21150Sstevel@tonic-gate } 21160Sstevel@tonic-gate 21170Sstevel@tonic-gate /* 21180Sstevel@tonic-gate * IPv6 sends up the ICMP errors for validation and the removal of the AH 21190Sstevel@tonic-gate * header. 21200Sstevel@tonic-gate */ 21210Sstevel@tonic-gate static ipsec_status_t 21223448Sdh155122 ah_icmp_error_v6(mblk_t *ipsec_mp, ipsecah_stack_t *ahstack) 21230Sstevel@tonic-gate { 21240Sstevel@tonic-gate mblk_t *mp; 21250Sstevel@tonic-gate ip6_t *ip6h, *oip6h; 21260Sstevel@tonic-gate uint16_t hdr_length, ah_length; 21270Sstevel@tonic-gate uint8_t *nexthdrp; 21280Sstevel@tonic-gate ah_t *ah; 21290Sstevel@tonic-gate icmp6_t *icmp6; 21300Sstevel@tonic-gate isaf_t *isaf; 21310Sstevel@tonic-gate ipsa_t *assoc; 21320Sstevel@tonic-gate uint8_t *post_ah_ptr; 21333448Sdh155122 ipsec_stack_t *ipss = ahstack->ipsecah_netstack->netstack_ipsec; 21340Sstevel@tonic-gate 21350Sstevel@tonic-gate mp = ipsec_mp->b_cont; 21360Sstevel@tonic-gate ASSERT(mp->b_datap->db_type == M_CTL); 21370Sstevel@tonic-gate 21380Sstevel@tonic-gate /* 21390Sstevel@tonic-gate * Change the type to M_DATA till we finish pullups. 21400Sstevel@tonic-gate */ 21410Sstevel@tonic-gate mp->b_datap->db_type = M_DATA; 21420Sstevel@tonic-gate 21430Sstevel@tonic-gate /* 21440Sstevel@tonic-gate * Eat the cost of a pullupmsg() for now. It makes the rest of this 21450Sstevel@tonic-gate * code far less convoluted. 21460Sstevel@tonic-gate */ 21470Sstevel@tonic-gate if (!pullupmsg(mp, -1) || 21480Sstevel@tonic-gate !ip_hdr_length_nexthdr_v6(mp, (ip6_t *)mp->b_rptr, &hdr_length, 21494987Sdanmcd &nexthdrp) || 21500Sstevel@tonic-gate mp->b_rptr + hdr_length + sizeof (icmp6_t) + sizeof (ip6_t) + 21510Sstevel@tonic-gate sizeof (ah_t) > mp->b_wptr) { 21523448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 21533448Sdh155122 ip_drop_packet(ipsec_mp, B_TRUE, NULL, NULL, 21543448Sdh155122 DROPPER(ipss, ipds_ah_nomem), 21553448Sdh155122 &ahstack->ah_dropper); 21560Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 21570Sstevel@tonic-gate } 21580Sstevel@tonic-gate 21590Sstevel@tonic-gate oip6h = (ip6_t *)mp->b_rptr; 21600Sstevel@tonic-gate icmp6 = (icmp6_t *)((uint8_t *)oip6h + hdr_length); 21610Sstevel@tonic-gate ip6h = (ip6_t *)(icmp6 + 1); 21620Sstevel@tonic-gate if (!ip_hdr_length_nexthdr_v6(mp, ip6h, &hdr_length, &nexthdrp)) { 21633448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 21640Sstevel@tonic-gate ip_drop_packet(ipsec_mp, B_TRUE, NULL, NULL, 21653448Sdh155122 DROPPER(ipss, ipds_ah_bad_v6_hdrs), 21663448Sdh155122 &ahstack->ah_dropper); 21670Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 21680Sstevel@tonic-gate } 21690Sstevel@tonic-gate ah = (ah_t *)((uint8_t *)ip6h + hdr_length); 21700Sstevel@tonic-gate 21713448Sdh155122 isaf = OUTBOUND_BUCKET_V6(&ahstack->ah_sadb.s_v6, ip6h->ip6_dst); 21720Sstevel@tonic-gate mutex_enter(&isaf->isaf_lock); 21730Sstevel@tonic-gate assoc = ipsec_getassocbyspi(isaf, ah->ah_spi, 21740Sstevel@tonic-gate (uint32_t *)&ip6h->ip6_src, (uint32_t *)&ip6h->ip6_dst, AF_INET6); 21750Sstevel@tonic-gate mutex_exit(&isaf->isaf_lock); 21760Sstevel@tonic-gate 21770Sstevel@tonic-gate if (assoc == NULL) { 21783448Sdh155122 IP_AH_BUMP_STAT(ipss, lookup_failure); 21793448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 21803448Sdh155122 if (ahstack->ipsecah_log_unknown_spi) { 21810Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, 21820Sstevel@tonic-gate SL_CONSOLE | SL_WARN | SL_ERROR, 21830Sstevel@tonic-gate "Bad ICMP message - No association for the " 21840Sstevel@tonic-gate "attached AH header whose spi is 0x%x, " 21850Sstevel@tonic-gate "sender is 0x%x\n", 21863448Sdh155122 ah->ah_spi, &oip6h->ip6_src, AF_INET6, 21873448Sdh155122 ahstack->ipsecah_netstack); 21880Sstevel@tonic-gate } 21893448Sdh155122 ip_drop_packet(ipsec_mp, B_TRUE, NULL, NULL, 21903448Sdh155122 DROPPER(ipss, ipds_ah_no_sa), 21913448Sdh155122 &ahstack->ah_dropper); 21920Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 21930Sstevel@tonic-gate } 21940Sstevel@tonic-gate 21950Sstevel@tonic-gate IPSA_REFRELE(assoc); 21960Sstevel@tonic-gate 21970Sstevel@tonic-gate /* 21980Sstevel@tonic-gate * There seems to be a valid association. If there is enough of AH 21990Sstevel@tonic-gate * header remove it, otherwise bail. One could check whether it has 22000Sstevel@tonic-gate * complete AH header plus 8 bytes but it does not make sense if an 22010Sstevel@tonic-gate * icmp error is returned for ICMP messages e.g ICMP time exceeded, 22020Sstevel@tonic-gate * that are being sent up. Let the caller figure out. 22030Sstevel@tonic-gate * 22040Sstevel@tonic-gate * NOTE: ah_length is the number of 32 bit words minus 2. 22050Sstevel@tonic-gate */ 22060Sstevel@tonic-gate ah_length = (ah->ah_length << 2) + 8; 22070Sstevel@tonic-gate post_ah_ptr = (uint8_t *)ah + ah_length; 22080Sstevel@tonic-gate 22090Sstevel@tonic-gate if (post_ah_ptr > mp->b_wptr) { 22103448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 22110Sstevel@tonic-gate ip_drop_packet(ipsec_mp, B_TRUE, NULL, NULL, 22123448Sdh155122 DROPPER(ipss, ipds_ah_bad_length), 22133448Sdh155122 &ahstack->ah_dropper); 22140Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 22150Sstevel@tonic-gate } 22160Sstevel@tonic-gate 22170Sstevel@tonic-gate ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) - ah_length); 22180Sstevel@tonic-gate *nexthdrp = ah->ah_nexthdr; 22190Sstevel@tonic-gate ovbcopy(post_ah_ptr, ah, 22200Sstevel@tonic-gate (size_t)((uintptr_t)mp->b_wptr - (uintptr_t)post_ah_ptr)); 22210Sstevel@tonic-gate mp->b_wptr -= ah_length; 22220Sstevel@tonic-gate /* Rewhack to be an ICMP error. */ 22230Sstevel@tonic-gate mp->b_datap->db_type = M_CTL; 22240Sstevel@tonic-gate 22250Sstevel@tonic-gate return (IPSEC_STATUS_SUCCESS); 22260Sstevel@tonic-gate } 22270Sstevel@tonic-gate 22280Sstevel@tonic-gate /* 22290Sstevel@tonic-gate * IP sends up the ICMP errors for validation and the removal of 22300Sstevel@tonic-gate * the AH header. 22310Sstevel@tonic-gate */ 22320Sstevel@tonic-gate static ipsec_status_t 22333448Sdh155122 ah_icmp_error_v4(mblk_t *ipsec_mp, ipsecah_stack_t *ahstack) 22340Sstevel@tonic-gate { 22350Sstevel@tonic-gate mblk_t *mp; 22360Sstevel@tonic-gate mblk_t *mp1; 22370Sstevel@tonic-gate icmph_t *icmph; 22380Sstevel@tonic-gate int iph_hdr_length; 22390Sstevel@tonic-gate int hdr_length; 22400Sstevel@tonic-gate isaf_t *hptr; 22410Sstevel@tonic-gate ipsa_t *assoc; 22420Sstevel@tonic-gate int ah_length; 22430Sstevel@tonic-gate ipha_t *ipha; 22440Sstevel@tonic-gate ipha_t *oipha; 22450Sstevel@tonic-gate ah_t *ah; 22460Sstevel@tonic-gate uint32_t length; 22470Sstevel@tonic-gate int alloc_size; 22480Sstevel@tonic-gate uint8_t nexthdr; 22493448Sdh155122 ipsec_stack_t *ipss = ahstack->ipsecah_netstack->netstack_ipsec; 22500Sstevel@tonic-gate 22510Sstevel@tonic-gate mp = ipsec_mp->b_cont; 22520Sstevel@tonic-gate ASSERT(mp->b_datap->db_type == M_CTL); 22530Sstevel@tonic-gate 22540Sstevel@tonic-gate /* 22550Sstevel@tonic-gate * Change the type to M_DATA till we finish pullups. 22560Sstevel@tonic-gate */ 22570Sstevel@tonic-gate mp->b_datap->db_type = M_DATA; 22580Sstevel@tonic-gate 22590Sstevel@tonic-gate oipha = ipha = (ipha_t *)mp->b_rptr; 22600Sstevel@tonic-gate iph_hdr_length = IPH_HDR_LENGTH(ipha); 22610Sstevel@tonic-gate icmph = (icmph_t *)&mp->b_rptr[iph_hdr_length]; 22620Sstevel@tonic-gate 22630Sstevel@tonic-gate ipha = (ipha_t *)&icmph[1]; 22640Sstevel@tonic-gate hdr_length = IPH_HDR_LENGTH(ipha); 22650Sstevel@tonic-gate 22660Sstevel@tonic-gate /* 22670Sstevel@tonic-gate * See if we have enough to locate the SPI 22680Sstevel@tonic-gate */ 22690Sstevel@tonic-gate if ((uchar_t *)ipha + hdr_length + 8 > mp->b_wptr) { 22700Sstevel@tonic-gate if (!pullupmsg(mp, (uchar_t *)ipha + hdr_length + 8 - 22714987Sdanmcd mp->b_rptr)) { 22723448Sdh155122 ipsec_rl_strlog(ahstack->ipsecah_netstack, 22733448Sdh155122 info.mi_idnum, 0, 0, 22740Sstevel@tonic-gate SL_WARN | SL_ERROR, 22750Sstevel@tonic-gate "ICMP error: Small AH header\n"); 22763448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 22770Sstevel@tonic-gate ip_drop_packet(ipsec_mp, B_TRUE, NULL, NULL, 22783448Sdh155122 DROPPER(ipss, ipds_ah_bad_length), 22793448Sdh155122 &ahstack->ah_dropper); 22800Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 22810Sstevel@tonic-gate } 22820Sstevel@tonic-gate icmph = (icmph_t *)&mp->b_rptr[iph_hdr_length]; 22830Sstevel@tonic-gate ipha = (ipha_t *)&icmph[1]; 22840Sstevel@tonic-gate } 22850Sstevel@tonic-gate 22860Sstevel@tonic-gate ah = (ah_t *)((uint8_t *)ipha + hdr_length); 22870Sstevel@tonic-gate nexthdr = ah->ah_nexthdr; 22880Sstevel@tonic-gate 22893448Sdh155122 hptr = OUTBOUND_BUCKET_V4(&ahstack->ah_sadb.s_v4, ipha->ipha_dst); 22900Sstevel@tonic-gate mutex_enter(&hptr->isaf_lock); 22910Sstevel@tonic-gate assoc = ipsec_getassocbyspi(hptr, ah->ah_spi, 22920Sstevel@tonic-gate (uint32_t *)&ipha->ipha_src, (uint32_t *)&ipha->ipha_dst, AF_INET); 22930Sstevel@tonic-gate mutex_exit(&hptr->isaf_lock); 22940Sstevel@tonic-gate 22950Sstevel@tonic-gate if (assoc == NULL) { 22963448Sdh155122 IP_AH_BUMP_STAT(ipss, lookup_failure); 22973448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 22983448Sdh155122 if (ahstack->ipsecah_log_unknown_spi) { 22990Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, 23000Sstevel@tonic-gate SL_CONSOLE | SL_WARN | SL_ERROR, 23010Sstevel@tonic-gate "Bad ICMP message - No association for the " 23020Sstevel@tonic-gate "attached AH header whose spi is 0x%x, " 23030Sstevel@tonic-gate "sender is 0x%x\n", 23043448Sdh155122 ah->ah_spi, &oipha->ipha_src, AF_INET, 23053448Sdh155122 ahstack->ipsecah_netstack); 23060Sstevel@tonic-gate } 23073448Sdh155122 ip_drop_packet(ipsec_mp, B_TRUE, NULL, NULL, 23083448Sdh155122 DROPPER(ipss, ipds_ah_no_sa), 23093448Sdh155122 &ahstack->ah_dropper); 23100Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 23110Sstevel@tonic-gate } 23120Sstevel@tonic-gate 23130Sstevel@tonic-gate IPSA_REFRELE(assoc); 23140Sstevel@tonic-gate /* 23150Sstevel@tonic-gate * There seems to be a valid association. If there 23160Sstevel@tonic-gate * is enough of AH header remove it, otherwise remove 23170Sstevel@tonic-gate * as much as possible and send it back. One could check 23180Sstevel@tonic-gate * whether it has complete AH header plus 8 bytes but it 23190Sstevel@tonic-gate * does not make sense if an icmp error is returned for 23200Sstevel@tonic-gate * ICMP messages e.g ICMP time exceeded, that are being 23210Sstevel@tonic-gate * sent up. Let the caller figure out. 23220Sstevel@tonic-gate * 23230Sstevel@tonic-gate * NOTE: ah_length is the number of 32 bit words minus 2. 23240Sstevel@tonic-gate */ 23250Sstevel@tonic-gate ah_length = (ah->ah_length << 2) + 8; 23260Sstevel@tonic-gate 23270Sstevel@tonic-gate if ((uchar_t *)ipha + hdr_length + ah_length > mp->b_wptr) { 23280Sstevel@tonic-gate if (mp->b_cont == NULL) { 23290Sstevel@tonic-gate /* 23300Sstevel@tonic-gate * There is nothing to pullup. Just remove as 23310Sstevel@tonic-gate * much as possible. This is a common case for 23320Sstevel@tonic-gate * IPV4. 23330Sstevel@tonic-gate */ 23340Sstevel@tonic-gate ah_length = (mp->b_wptr - ((uchar_t *)ipha + 23350Sstevel@tonic-gate hdr_length)); 23360Sstevel@tonic-gate goto done; 23370Sstevel@tonic-gate } 23380Sstevel@tonic-gate /* Pullup the full ah header */ 23390Sstevel@tonic-gate if (!pullupmsg(mp, (uchar_t *)ah + ah_length - mp->b_rptr)) { 23400Sstevel@tonic-gate /* 23410Sstevel@tonic-gate * pullupmsg could have failed if there was not 23420Sstevel@tonic-gate * enough to pullup or memory allocation failed. 23430Sstevel@tonic-gate * We tried hard, give up now. 23440Sstevel@tonic-gate */ 23453448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 23460Sstevel@tonic-gate ip_drop_packet(ipsec_mp, B_TRUE, NULL, NULL, 23473448Sdh155122 DROPPER(ipss, ipds_ah_nomem), 23483448Sdh155122 &ahstack->ah_dropper); 23490Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 23500Sstevel@tonic-gate } 23510Sstevel@tonic-gate icmph = (icmph_t *)&mp->b_rptr[iph_hdr_length]; 23520Sstevel@tonic-gate ipha = (ipha_t *)&icmph[1]; 23530Sstevel@tonic-gate } 23540Sstevel@tonic-gate done: 23550Sstevel@tonic-gate /* 23560Sstevel@tonic-gate * Remove the AH header and change the protocol. 23570Sstevel@tonic-gate * Don't update the spi fields in the ipsec_in 23580Sstevel@tonic-gate * message as we are called just to validate the 23590Sstevel@tonic-gate * message attached to the ICMP message. 23600Sstevel@tonic-gate * 23610Sstevel@tonic-gate * If we never pulled up since all of the message 23620Sstevel@tonic-gate * is in one single mblk, we can't remove the AH header 23630Sstevel@tonic-gate * by just setting the b_wptr to the beginning of the 23640Sstevel@tonic-gate * AH header. We need to allocate a mblk that can hold 23650Sstevel@tonic-gate * up until the inner IP header and copy them. 23660Sstevel@tonic-gate */ 23670Sstevel@tonic-gate alloc_size = iph_hdr_length + sizeof (icmph_t) + hdr_length; 23680Sstevel@tonic-gate 23690Sstevel@tonic-gate if ((mp1 = allocb(alloc_size, BPRI_LO)) == NULL) { 23703448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 23713448Sdh155122 ip_drop_packet(ipsec_mp, B_TRUE, NULL, NULL, 23723448Sdh155122 DROPPER(ipss, ipds_ah_nomem), 23733448Sdh155122 &ahstack->ah_dropper); 23740Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 23750Sstevel@tonic-gate } 23760Sstevel@tonic-gate /* ICMP errors are M_CTL messages */ 23770Sstevel@tonic-gate mp1->b_datap->db_type = M_CTL; 23780Sstevel@tonic-gate ipsec_mp->b_cont = mp1; 23790Sstevel@tonic-gate bcopy(mp->b_rptr, mp1->b_rptr, alloc_size); 23800Sstevel@tonic-gate mp1->b_wptr += alloc_size; 23810Sstevel@tonic-gate 23820Sstevel@tonic-gate /* 23830Sstevel@tonic-gate * Skip whatever we have copied and as much of AH header 23840Sstevel@tonic-gate * possible. If we still have something left in the original 23850Sstevel@tonic-gate * message, tag on. 23860Sstevel@tonic-gate */ 23870Sstevel@tonic-gate mp->b_rptr = (uchar_t *)ipha + hdr_length + ah_length; 23880Sstevel@tonic-gate 23890Sstevel@tonic-gate if (mp->b_rptr != mp->b_wptr) { 23900Sstevel@tonic-gate mp1->b_cont = mp; 23910Sstevel@tonic-gate } else { 23920Sstevel@tonic-gate if (mp->b_cont != NULL) 23930Sstevel@tonic-gate mp1->b_cont = mp->b_cont; 23940Sstevel@tonic-gate freeb(mp); 23950Sstevel@tonic-gate } 23960Sstevel@tonic-gate 23970Sstevel@tonic-gate ipha = (ipha_t *)(mp1->b_rptr + iph_hdr_length + sizeof (icmph_t)); 23980Sstevel@tonic-gate ipha->ipha_protocol = nexthdr; 23990Sstevel@tonic-gate length = ntohs(ipha->ipha_length); 24000Sstevel@tonic-gate length -= ah_length; 24010Sstevel@tonic-gate ipha->ipha_length = htons((uint16_t)length); 24020Sstevel@tonic-gate ipha->ipha_hdr_checksum = 0; 24030Sstevel@tonic-gate ipha->ipha_hdr_checksum = (uint16_t)ip_csum_hdr(ipha); 24040Sstevel@tonic-gate 24050Sstevel@tonic-gate return (IPSEC_STATUS_SUCCESS); 24060Sstevel@tonic-gate } 24070Sstevel@tonic-gate 24080Sstevel@tonic-gate /* 24090Sstevel@tonic-gate * IP calls this to validate the ICMP errors that 24100Sstevel@tonic-gate * we got from the network. 24110Sstevel@tonic-gate */ 24120Sstevel@tonic-gate ipsec_status_t 24130Sstevel@tonic-gate ipsecah_icmp_error(mblk_t *mp) 24140Sstevel@tonic-gate { 24150Sstevel@tonic-gate ipsec_in_t *ii = (ipsec_in_t *)mp->b_rptr; 24163448Sdh155122 netstack_t *ns = ii->ipsec_in_ns; 24173448Sdh155122 ipsecah_stack_t *ahstack = ns->netstack_ipsecah; 24180Sstevel@tonic-gate 24190Sstevel@tonic-gate if (ii->ipsec_in_v4) 24203448Sdh155122 return (ah_icmp_error_v4(mp, ahstack)); 24210Sstevel@tonic-gate else 24223448Sdh155122 return (ah_icmp_error_v6(mp, ahstack)); 24230Sstevel@tonic-gate } 24240Sstevel@tonic-gate 24250Sstevel@tonic-gate static int 24260Sstevel@tonic-gate ah_fix_tlv_options_v6(uint8_t *oi_opt, uint8_t *pi_opt, uint_t ehdrlen, 24270Sstevel@tonic-gate uint8_t hdr_type, boolean_t copy_always) 24280Sstevel@tonic-gate { 24290Sstevel@tonic-gate uint8_t opt_type; 24300Sstevel@tonic-gate uint_t optlen; 24310Sstevel@tonic-gate 24320Sstevel@tonic-gate ASSERT(hdr_type == IPPROTO_DSTOPTS || hdr_type == IPPROTO_HOPOPTS); 24330Sstevel@tonic-gate 24340Sstevel@tonic-gate /* 24350Sstevel@tonic-gate * Copy the next header and hdr ext. len of the HOP-by-HOP 24360Sstevel@tonic-gate * and Destination option. 24370Sstevel@tonic-gate */ 24380Sstevel@tonic-gate *pi_opt++ = *oi_opt++; 24390Sstevel@tonic-gate *pi_opt++ = *oi_opt++; 24400Sstevel@tonic-gate ehdrlen -= 2; 24410Sstevel@tonic-gate 24420Sstevel@tonic-gate /* 24430Sstevel@tonic-gate * Now handle all the TLV encoded options. 24440Sstevel@tonic-gate */ 24450Sstevel@tonic-gate while (ehdrlen != 0) { 24460Sstevel@tonic-gate opt_type = *oi_opt; 24470Sstevel@tonic-gate 24480Sstevel@tonic-gate if (opt_type == IP6OPT_PAD1) { 24490Sstevel@tonic-gate optlen = 1; 24500Sstevel@tonic-gate } else { 24510Sstevel@tonic-gate if (ehdrlen < 2) 24520Sstevel@tonic-gate goto bad_opt; 24530Sstevel@tonic-gate optlen = 2 + oi_opt[1]; 24540Sstevel@tonic-gate if (optlen > ehdrlen) 24550Sstevel@tonic-gate goto bad_opt; 24560Sstevel@tonic-gate } 24570Sstevel@tonic-gate if (copy_always || !(opt_type & IP6OPT_MUTABLE)) { 24580Sstevel@tonic-gate bcopy(oi_opt, pi_opt, optlen); 24590Sstevel@tonic-gate } else { 24600Sstevel@tonic-gate if (optlen == 1) { 24610Sstevel@tonic-gate *pi_opt = 0; 24620Sstevel@tonic-gate } else { 24630Sstevel@tonic-gate /* 24640Sstevel@tonic-gate * Copy the type and data length fields. 24650Sstevel@tonic-gate * Zero the option data by skipping 24660Sstevel@tonic-gate * option type and option data len 24670Sstevel@tonic-gate * fields. 24680Sstevel@tonic-gate */ 24690Sstevel@tonic-gate *pi_opt = *oi_opt; 24700Sstevel@tonic-gate *(pi_opt + 1) = *(oi_opt + 1); 24710Sstevel@tonic-gate bzero(pi_opt + 2, optlen - 2); 24720Sstevel@tonic-gate } 24730Sstevel@tonic-gate } 24740Sstevel@tonic-gate ehdrlen -= optlen; 24750Sstevel@tonic-gate oi_opt += optlen; 24760Sstevel@tonic-gate pi_opt += optlen; 24770Sstevel@tonic-gate } 24780Sstevel@tonic-gate return (0); 24790Sstevel@tonic-gate bad_opt: 24800Sstevel@tonic-gate return (-1); 24810Sstevel@tonic-gate } 24820Sstevel@tonic-gate 24830Sstevel@tonic-gate /* 24840Sstevel@tonic-gate * Construct a pseudo header for AH, processing all the options. 24850Sstevel@tonic-gate * 24860Sstevel@tonic-gate * oip6h is the IPv6 header of the incoming or outgoing packet. 24870Sstevel@tonic-gate * ip6h is the pointer to the pseudo headers IPV6 header. All 24880Sstevel@tonic-gate * the space needed for the options have been allocated including 24890Sstevel@tonic-gate * the AH header. 24900Sstevel@tonic-gate * 24910Sstevel@tonic-gate * If copy_always is set, all the options that appear before AH are copied 24920Sstevel@tonic-gate * blindly without checking for IP6OPT_MUTABLE. This is used by 24930Sstevel@tonic-gate * ah_auth_out_done(). Please refer to that function for details. 24940Sstevel@tonic-gate * 24950Sstevel@tonic-gate * NOTE : 24960Sstevel@tonic-gate * 24970Sstevel@tonic-gate * * AH header is never copied in this function even if copy_always 24980Sstevel@tonic-gate * is set. It just returns the ah_offset - offset of the AH header 24990Sstevel@tonic-gate * and the caller needs to do the copying. This is done so that we 25000Sstevel@tonic-gate * don't have pass extra arguments e.g. SA etc. and also, 25010Sstevel@tonic-gate * it is not needed when ah_auth_out_done is calling this function. 25020Sstevel@tonic-gate */ 25030Sstevel@tonic-gate static uint_t 25040Sstevel@tonic-gate ah_fix_phdr_v6(ip6_t *ip6h, ip6_t *oip6h, boolean_t outbound, 25050Sstevel@tonic-gate boolean_t copy_always) 25060Sstevel@tonic-gate { 25070Sstevel@tonic-gate uint8_t *oi_opt; 25080Sstevel@tonic-gate uint8_t *pi_opt; 25090Sstevel@tonic-gate uint8_t nexthdr; 25100Sstevel@tonic-gate uint8_t *prev_nexthdr; 25110Sstevel@tonic-gate ip6_hbh_t *hbhhdr; 25120Sstevel@tonic-gate ip6_dest_t *dsthdr = NULL; 25130Sstevel@tonic-gate ip6_rthdr0_t *rthdr; 25140Sstevel@tonic-gate int ehdrlen; 25150Sstevel@tonic-gate ah_t *ah; 25160Sstevel@tonic-gate int ret; 25170Sstevel@tonic-gate 25180Sstevel@tonic-gate /* 25190Sstevel@tonic-gate * In the outbound case for source route, ULP has already moved 25200Sstevel@tonic-gate * the first hop, which is now in ip6_dst. We need to re-arrange 25210Sstevel@tonic-gate * the header to make it look like how it would appear in the 25220Sstevel@tonic-gate * receiver i.e 25230Sstevel@tonic-gate * 25240Sstevel@tonic-gate * Because of ip_massage_options_v6 the header looks like 25250Sstevel@tonic-gate * this : 25260Sstevel@tonic-gate * 25270Sstevel@tonic-gate * ip6_src = S, ip6_dst = I1. followed by I2,I3,D. 25280Sstevel@tonic-gate * 25290Sstevel@tonic-gate * When it reaches the receiver, it would look like 25300Sstevel@tonic-gate * 25310Sstevel@tonic-gate * ip6_src = S, ip6_dst = D. followed by I1,I2,I3. 25320Sstevel@tonic-gate * 25330Sstevel@tonic-gate * NOTE : We assume that there are no problems with the options 25340Sstevel@tonic-gate * as IP should have already checked this. 25350Sstevel@tonic-gate */ 25360Sstevel@tonic-gate 25370Sstevel@tonic-gate oi_opt = (uchar_t *)&oip6h[1]; 25380Sstevel@tonic-gate pi_opt = (uchar_t *)&ip6h[1]; 25390Sstevel@tonic-gate 25400Sstevel@tonic-gate /* 25410Sstevel@tonic-gate * We set the prev_nexthdr properly in the pseudo header. 25420Sstevel@tonic-gate * After we finish authentication and come back from the 25430Sstevel@tonic-gate * algorithm module, pseudo header will become the real 25440Sstevel@tonic-gate * IP header. 25450Sstevel@tonic-gate */ 25460Sstevel@tonic-gate prev_nexthdr = (uint8_t *)&ip6h->ip6_nxt; 25470Sstevel@tonic-gate nexthdr = oip6h->ip6_nxt; 25480Sstevel@tonic-gate /* Assume IP has already stripped it */ 25490Sstevel@tonic-gate ASSERT(nexthdr != IPPROTO_FRAGMENT && nexthdr != IPPROTO_RAW); 25500Sstevel@tonic-gate ah = NULL; 25510Sstevel@tonic-gate dsthdr = NULL; 25520Sstevel@tonic-gate for (;;) { 25530Sstevel@tonic-gate switch (nexthdr) { 25540Sstevel@tonic-gate case IPPROTO_HOPOPTS: 25550Sstevel@tonic-gate hbhhdr = (ip6_hbh_t *)oi_opt; 25560Sstevel@tonic-gate nexthdr = hbhhdr->ip6h_nxt; 25570Sstevel@tonic-gate ehdrlen = 8 * (hbhhdr->ip6h_len + 1); 25580Sstevel@tonic-gate ret = ah_fix_tlv_options_v6(oi_opt, pi_opt, ehdrlen, 25590Sstevel@tonic-gate IPPROTO_HOPOPTS, copy_always); 25600Sstevel@tonic-gate /* 25610Sstevel@tonic-gate * Return a zero offset indicating error if there 25620Sstevel@tonic-gate * was error. 25630Sstevel@tonic-gate */ 25640Sstevel@tonic-gate if (ret == -1) 25650Sstevel@tonic-gate return (0); 25660Sstevel@tonic-gate hbhhdr = (ip6_hbh_t *)pi_opt; 25670Sstevel@tonic-gate prev_nexthdr = (uint8_t *)&hbhhdr->ip6h_nxt; 25680Sstevel@tonic-gate break; 25690Sstevel@tonic-gate case IPPROTO_ROUTING: 25700Sstevel@tonic-gate rthdr = (ip6_rthdr0_t *)oi_opt; 25710Sstevel@tonic-gate nexthdr = rthdr->ip6r0_nxt; 25720Sstevel@tonic-gate ehdrlen = 8 * (rthdr->ip6r0_len + 1); 25730Sstevel@tonic-gate if (!copy_always && outbound) { 25740Sstevel@tonic-gate int i, left; 25750Sstevel@tonic-gate ip6_rthdr0_t *prthdr; 25760Sstevel@tonic-gate in6_addr_t *ap, *pap; 25770Sstevel@tonic-gate 25780Sstevel@tonic-gate left = rthdr->ip6r0_segleft; 25790Sstevel@tonic-gate prthdr = (ip6_rthdr0_t *)pi_opt; 25800Sstevel@tonic-gate pap = (in6_addr_t *)(prthdr + 1); 25810Sstevel@tonic-gate ap = (in6_addr_t *)(rthdr + 1); 25820Sstevel@tonic-gate /* 25830Sstevel@tonic-gate * First eight bytes except seg_left 25840Sstevel@tonic-gate * does not change en route. 25850Sstevel@tonic-gate */ 25860Sstevel@tonic-gate bcopy(oi_opt, pi_opt, 8); 25870Sstevel@tonic-gate prthdr->ip6r0_segleft = 0; 25880Sstevel@tonic-gate /* 25890Sstevel@tonic-gate * First address has been moved to 25900Sstevel@tonic-gate * the destination address of the 25910Sstevel@tonic-gate * ip header by ip_massage_options_v6. 25920Sstevel@tonic-gate * And the real destination address is 25930Sstevel@tonic-gate * in the last address part of the 25940Sstevel@tonic-gate * option. 25950Sstevel@tonic-gate */ 25960Sstevel@tonic-gate *pap = oip6h->ip6_dst; 25970Sstevel@tonic-gate for (i = 1; i < left - 1; i++) 25980Sstevel@tonic-gate pap[i] = ap[i - 1]; 25990Sstevel@tonic-gate ip6h->ip6_dst = *(ap + left - 1); 26000Sstevel@tonic-gate } else { 26010Sstevel@tonic-gate bcopy(oi_opt, pi_opt, ehdrlen); 26020Sstevel@tonic-gate } 26030Sstevel@tonic-gate rthdr = (ip6_rthdr0_t *)pi_opt; 26040Sstevel@tonic-gate prev_nexthdr = (uint8_t *)&rthdr->ip6r0_nxt; 26050Sstevel@tonic-gate break; 26060Sstevel@tonic-gate case IPPROTO_DSTOPTS: 26070Sstevel@tonic-gate /* 26080Sstevel@tonic-gate * Destination options are tricky. If there is 26090Sstevel@tonic-gate * a terminal (e.g. non-IPv6-extension) header 26100Sstevel@tonic-gate * following the destination options, don't 26110Sstevel@tonic-gate * reset prev_nexthdr or advance the AH insertion 26120Sstevel@tonic-gate * point and just treat this as a terminal header. 26130Sstevel@tonic-gate * 26140Sstevel@tonic-gate * If this is an inbound packet, just deal with 26150Sstevel@tonic-gate * it as is. 26160Sstevel@tonic-gate */ 26170Sstevel@tonic-gate dsthdr = (ip6_dest_t *)oi_opt; 26180Sstevel@tonic-gate /* 26190Sstevel@tonic-gate * XXX I hope common-subexpression elimination 26200Sstevel@tonic-gate * saves us the double-evaluate. 26210Sstevel@tonic-gate */ 26220Sstevel@tonic-gate if (outbound && dsthdr->ip6d_nxt != IPPROTO_ROUTING && 26230Sstevel@tonic-gate dsthdr->ip6d_nxt != IPPROTO_HOPOPTS) 26240Sstevel@tonic-gate goto terminal_hdr; 26250Sstevel@tonic-gate nexthdr = dsthdr->ip6d_nxt; 26260Sstevel@tonic-gate ehdrlen = 8 * (dsthdr->ip6d_len + 1); 26270Sstevel@tonic-gate ret = ah_fix_tlv_options_v6(oi_opt, pi_opt, ehdrlen, 26280Sstevel@tonic-gate IPPROTO_DSTOPTS, copy_always); 26290Sstevel@tonic-gate /* 26300Sstevel@tonic-gate * Return a zero offset indicating error if there 26310Sstevel@tonic-gate * was error. 26320Sstevel@tonic-gate */ 26330Sstevel@tonic-gate if (ret == -1) 26340Sstevel@tonic-gate return (0); 26350Sstevel@tonic-gate break; 26360Sstevel@tonic-gate case IPPROTO_AH: 26370Sstevel@tonic-gate /* 26380Sstevel@tonic-gate * Be conservative in what you send. We shouldn't 26390Sstevel@tonic-gate * see two same-scoped AH's in one packet. 26400Sstevel@tonic-gate * (Inner-IP-scoped AH will be hit by terminal 26410Sstevel@tonic-gate * header of IP or IPv6.) 26420Sstevel@tonic-gate */ 26430Sstevel@tonic-gate ASSERT(!outbound); 26440Sstevel@tonic-gate return ((uint_t)(pi_opt - (uint8_t *)ip6h)); 26450Sstevel@tonic-gate default: 26460Sstevel@tonic-gate ASSERT(outbound); 26470Sstevel@tonic-gate terminal_hdr: 26480Sstevel@tonic-gate *prev_nexthdr = IPPROTO_AH; 26490Sstevel@tonic-gate ah = (ah_t *)pi_opt; 26500Sstevel@tonic-gate ah->ah_nexthdr = nexthdr; 26510Sstevel@tonic-gate return ((uint_t)(pi_opt - (uint8_t *)ip6h)); 26520Sstevel@tonic-gate } 26530Sstevel@tonic-gate pi_opt += ehdrlen; 26540Sstevel@tonic-gate oi_opt += ehdrlen; 26550Sstevel@tonic-gate } 26560Sstevel@tonic-gate /* NOTREACHED */ 26570Sstevel@tonic-gate } 26580Sstevel@tonic-gate 26590Sstevel@tonic-gate static boolean_t 26600Sstevel@tonic-gate ah_finish_up(ah_t *phdr_ah, ah_t *inbound_ah, ipsa_t *assoc, 26613448Sdh155122 int ah_data_sz, int ah_align_sz, ipsecah_stack_t *ahstack) 26620Sstevel@tonic-gate { 26630Sstevel@tonic-gate int i; 26640Sstevel@tonic-gate 26650Sstevel@tonic-gate /* 26660Sstevel@tonic-gate * Padding : 26670Sstevel@tonic-gate * 26680Sstevel@tonic-gate * 1) Authentication data may have to be padded 26690Sstevel@tonic-gate * before ICV calculation if ICV is not a multiple 26700Sstevel@tonic-gate * of 64 bits. This padding is arbitrary and transmitted 26710Sstevel@tonic-gate * with the packet at the end of the authentication data. 26720Sstevel@tonic-gate * Payload length should include the padding bytes. 26730Sstevel@tonic-gate * 26740Sstevel@tonic-gate * 2) Explicit padding of the whole datagram may be 26750Sstevel@tonic-gate * required by the algorithm which need not be 26760Sstevel@tonic-gate * transmitted. It is assumed that this will be taken 26770Sstevel@tonic-gate * care by the algorithm module. 26780Sstevel@tonic-gate */ 26790Sstevel@tonic-gate bzero(phdr_ah + 1, ah_data_sz); /* Zero out ICV for pseudo-hdr. */ 26800Sstevel@tonic-gate 26810Sstevel@tonic-gate if (inbound_ah == NULL) { 26820Sstevel@tonic-gate /* Outbound AH datagram. */ 26830Sstevel@tonic-gate 26840Sstevel@tonic-gate phdr_ah->ah_length = (ah_align_sz >> 2) + 1; 26850Sstevel@tonic-gate phdr_ah->ah_reserved = 0; 26860Sstevel@tonic-gate phdr_ah->ah_spi = assoc->ipsa_spi; 26870Sstevel@tonic-gate 26880Sstevel@tonic-gate phdr_ah->ah_replay = 26890Sstevel@tonic-gate htonl(atomic_add_32_nv(&assoc->ipsa_replay, 1)); 26900Sstevel@tonic-gate if (phdr_ah->ah_replay == 0 && assoc->ipsa_replay_wsize != 0) { 26910Sstevel@tonic-gate /* 26920Sstevel@tonic-gate * XXX We have replay counter wrapping. We probably 26930Sstevel@tonic-gate * want to nuke this SA (and its peer). 26940Sstevel@tonic-gate */ 26950Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, 26960Sstevel@tonic-gate SL_ERROR | SL_CONSOLE | SL_WARN, 26970Sstevel@tonic-gate "Outbound AH SA (0x%x), dst %s has wrapped " 26980Sstevel@tonic-gate "sequence.\n", phdr_ah->ah_spi, 26993448Sdh155122 assoc->ipsa_dstaddr, assoc->ipsa_addrfam, 27003448Sdh155122 ahstack->ipsecah_netstack); 27010Sstevel@tonic-gate 27020Sstevel@tonic-gate sadb_replay_delete(assoc); 27030Sstevel@tonic-gate /* Caller will free phdr_mp and return NULL. */ 27040Sstevel@tonic-gate return (B_FALSE); 27050Sstevel@tonic-gate } 27060Sstevel@tonic-gate 27070Sstevel@tonic-gate if (ah_data_sz != ah_align_sz) { 27080Sstevel@tonic-gate uchar_t *pad = ((uchar_t *)phdr_ah + sizeof (ah_t) + 27090Sstevel@tonic-gate ah_data_sz); 27100Sstevel@tonic-gate 27110Sstevel@tonic-gate for (i = 0; i < (ah_align_sz - ah_data_sz); i++) { 27120Sstevel@tonic-gate pad[i] = (uchar_t)i; /* Fill the padding */ 27130Sstevel@tonic-gate } 27140Sstevel@tonic-gate } 27150Sstevel@tonic-gate } else { 27160Sstevel@tonic-gate /* Inbound AH datagram. */ 27170Sstevel@tonic-gate phdr_ah->ah_nexthdr = inbound_ah->ah_nexthdr; 27180Sstevel@tonic-gate phdr_ah->ah_length = inbound_ah->ah_length; 27190Sstevel@tonic-gate phdr_ah->ah_reserved = 0; 27200Sstevel@tonic-gate ASSERT(inbound_ah->ah_spi == assoc->ipsa_spi); 27210Sstevel@tonic-gate phdr_ah->ah_spi = inbound_ah->ah_spi; 27220Sstevel@tonic-gate phdr_ah->ah_replay = inbound_ah->ah_replay; 27230Sstevel@tonic-gate 27240Sstevel@tonic-gate if (ah_data_sz != ah_align_sz) { 27253448Sdh155122 uchar_t *opad = ((uchar_t *)inbound_ah + 27263448Sdh155122 sizeof (ah_t) + ah_data_sz); 27270Sstevel@tonic-gate uchar_t *pad = ((uchar_t *)phdr_ah + sizeof (ah_t) + 27280Sstevel@tonic-gate ah_data_sz); 27290Sstevel@tonic-gate 27300Sstevel@tonic-gate for (i = 0; i < (ah_align_sz - ah_data_sz); i++) { 27310Sstevel@tonic-gate pad[i] = opad[i]; /* Copy the padding */ 27320Sstevel@tonic-gate } 27330Sstevel@tonic-gate } 27340Sstevel@tonic-gate } 27350Sstevel@tonic-gate 27360Sstevel@tonic-gate return (B_TRUE); 27370Sstevel@tonic-gate } 27380Sstevel@tonic-gate 27390Sstevel@tonic-gate /* 27400Sstevel@tonic-gate * Called upon failing the inbound ICV check. The message passed as 27410Sstevel@tonic-gate * argument is freed. 27420Sstevel@tonic-gate */ 27430Sstevel@tonic-gate static void 27440Sstevel@tonic-gate ah_log_bad_auth(mblk_t *ipsec_in) 27450Sstevel@tonic-gate { 27460Sstevel@tonic-gate mblk_t *mp = ipsec_in->b_cont->b_cont; 27470Sstevel@tonic-gate ipsec_in_t *ii = (ipsec_in_t *)ipsec_in->b_rptr; 27480Sstevel@tonic-gate boolean_t isv4 = ii->ipsec_in_v4; 27490Sstevel@tonic-gate ipsa_t *assoc = ii->ipsec_in_ah_sa; 27500Sstevel@tonic-gate int af; 27510Sstevel@tonic-gate void *addr; 27523448Sdh155122 netstack_t *ns = ii->ipsec_in_ns; 27533448Sdh155122 ipsecah_stack_t *ahstack = ns->netstack_ipsecah; 27543448Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 27550Sstevel@tonic-gate 27560Sstevel@tonic-gate mp->b_rptr -= ii->ipsec_in_skip_len; 27570Sstevel@tonic-gate 27580Sstevel@tonic-gate if (isv4) { 27590Sstevel@tonic-gate ipha_t *ipha = (ipha_t *)mp->b_rptr; 27600Sstevel@tonic-gate addr = &ipha->ipha_dst; 27610Sstevel@tonic-gate af = AF_INET; 27620Sstevel@tonic-gate } else { 27630Sstevel@tonic-gate ip6_t *ip6h = (ip6_t *)mp->b_rptr; 27640Sstevel@tonic-gate addr = &ip6h->ip6_dst; 27650Sstevel@tonic-gate af = AF_INET6; 27660Sstevel@tonic-gate } 27670Sstevel@tonic-gate 27680Sstevel@tonic-gate /* 27690Sstevel@tonic-gate * Log the event. Don't print to the console, block 27700Sstevel@tonic-gate * potential denial-of-service attack. 27710Sstevel@tonic-gate */ 27723448Sdh155122 AH_BUMP_STAT(ahstack, bad_auth); 27730Sstevel@tonic-gate 27740Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, SL_ERROR | SL_WARN, 27750Sstevel@tonic-gate "AH Authentication failed spi %x, dst_addr %s", 27763448Sdh155122 assoc->ipsa_spi, addr, af, ahstack->ipsecah_netstack); 27773448Sdh155122 27783448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 27793448Sdh155122 ip_drop_packet(ipsec_in, B_TRUE, NULL, NULL, 27803448Sdh155122 DROPPER(ipss, ipds_ah_bad_auth), 27813448Sdh155122 &ahstack->ah_dropper); 27820Sstevel@tonic-gate } 27830Sstevel@tonic-gate 27840Sstevel@tonic-gate /* 27850Sstevel@tonic-gate * Kernel crypto framework callback invoked after completion of async 27860Sstevel@tonic-gate * crypto requests. 27870Sstevel@tonic-gate */ 27880Sstevel@tonic-gate static void 27890Sstevel@tonic-gate ah_kcf_callback(void *arg, int status) 27900Sstevel@tonic-gate { 27910Sstevel@tonic-gate mblk_t *ipsec_mp = (mblk_t *)arg; 27920Sstevel@tonic-gate ipsec_in_t *ii = (ipsec_in_t *)ipsec_mp->b_rptr; 27930Sstevel@tonic-gate boolean_t is_inbound = (ii->ipsec_in_type == IPSEC_IN); 27943448Sdh155122 netstackid_t stackid; 27953448Sdh155122 netstack_t *ns, *ns_arg; 27963448Sdh155122 ipsec_stack_t *ipss; 27973448Sdh155122 ipsecah_stack_t *ahstack; 27983448Sdh155122 ipsec_out_t *io = (ipsec_out_t *)ii; 27990Sstevel@tonic-gate 28000Sstevel@tonic-gate ASSERT(ipsec_mp->b_cont != NULL); 28010Sstevel@tonic-gate 28023448Sdh155122 if (is_inbound) { 28033448Sdh155122 stackid = ii->ipsec_in_stackid; 28043448Sdh155122 ns_arg = ii->ipsec_in_ns; 28053448Sdh155122 } else { 28063448Sdh155122 stackid = io->ipsec_out_stackid; 28073448Sdh155122 ns_arg = io->ipsec_out_ns; 28083448Sdh155122 } 28093448Sdh155122 /* 28103448Sdh155122 * Verify that the netstack is still around; could have vanished 28113448Sdh155122 * while kEf was doing its work. 28123448Sdh155122 */ 28133448Sdh155122 ns = netstack_find_by_stackid(stackid); 28143448Sdh155122 if (ns == NULL || ns != ns_arg) { 28153448Sdh155122 /* Disappeared on us */ 28163448Sdh155122 if (ns != NULL) 28173448Sdh155122 netstack_rele(ns); 28183448Sdh155122 freemsg(ipsec_mp); 28193448Sdh155122 return; 28203448Sdh155122 } 28213448Sdh155122 28223448Sdh155122 ahstack = ns->netstack_ipsecah; 28233448Sdh155122 ipss = ns->netstack_ipsec; 28243448Sdh155122 28250Sstevel@tonic-gate if (status == CRYPTO_SUCCESS) { 28260Sstevel@tonic-gate if (is_inbound) { 28273448Sdh155122 if (ah_auth_in_done(ipsec_mp) != IPSEC_STATUS_SUCCESS) { 28283448Sdh155122 netstack_rele(ns); 28290Sstevel@tonic-gate return; 28303448Sdh155122 } 28310Sstevel@tonic-gate /* finish IPsec processing */ 28320Sstevel@tonic-gate ip_fanout_proto_again(ipsec_mp, NULL, NULL, NULL); 28330Sstevel@tonic-gate } else { 28340Sstevel@tonic-gate ipha_t *ipha; 28350Sstevel@tonic-gate 28363448Sdh155122 if (ah_auth_out_done(ipsec_mp) != 28373448Sdh155122 IPSEC_STATUS_SUCCESS) { 28383448Sdh155122 netstack_rele(ns); 28390Sstevel@tonic-gate return; 28403448Sdh155122 } 28410Sstevel@tonic-gate 28420Sstevel@tonic-gate /* finish IPsec processing */ 28430Sstevel@tonic-gate ipha = (ipha_t *)ipsec_mp->b_cont->b_rptr; 28440Sstevel@tonic-gate if (IPH_HDR_VERSION(ipha) == IP_VERSION) { 28450Sstevel@tonic-gate ip_wput_ipsec_out(NULL, ipsec_mp, ipha, NULL, 28460Sstevel@tonic-gate NULL); 28470Sstevel@tonic-gate } else { 28480Sstevel@tonic-gate ip6_t *ip6h = (ip6_t *)ipha; 28490Sstevel@tonic-gate ip_wput_ipsec_out_v6(NULL, ipsec_mp, ip6h, 28500Sstevel@tonic-gate NULL, NULL); 28510Sstevel@tonic-gate } 28520Sstevel@tonic-gate } 28530Sstevel@tonic-gate 28540Sstevel@tonic-gate } else if (status == CRYPTO_INVALID_MAC) { 28550Sstevel@tonic-gate ah_log_bad_auth(ipsec_mp); 28560Sstevel@tonic-gate } else { 28573448Sdh155122 ah1dbg(ahstack, ("ah_kcf_callback: crypto failed with 0x%x\n", 28584987Sdanmcd status)); 28593448Sdh155122 AH_BUMP_STAT(ahstack, crypto_failures); 28600Sstevel@tonic-gate if (is_inbound) 28613448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 28620Sstevel@tonic-gate else 28633448Sdh155122 AH_BUMP_STAT(ahstack, out_discards); 28640Sstevel@tonic-gate ip_drop_packet(ipsec_mp, is_inbound, NULL, NULL, 28653448Sdh155122 DROPPER(ipss, ipds_ah_crypto_failed), 28663448Sdh155122 &ahstack->ah_dropper); 28670Sstevel@tonic-gate } 28683448Sdh155122 netstack_rele(ns); 28690Sstevel@tonic-gate } 28700Sstevel@tonic-gate 28710Sstevel@tonic-gate /* 28720Sstevel@tonic-gate * Invoked on kernel crypto failure during inbound and outbound processing. 28730Sstevel@tonic-gate */ 28740Sstevel@tonic-gate static void 28753448Sdh155122 ah_crypto_failed(mblk_t *mp, boolean_t is_inbound, int kef_rc, 28763448Sdh155122 ipsecah_stack_t *ahstack) 28770Sstevel@tonic-gate { 28783448Sdh155122 ipsec_stack_t *ipss = ahstack->ipsecah_netstack->netstack_ipsec; 28793448Sdh155122 28803448Sdh155122 ah1dbg(ahstack, ("crypto failed for %s AH with 0x%x\n", 28810Sstevel@tonic-gate is_inbound ? "inbound" : "outbound", kef_rc)); 28823448Sdh155122 ip_drop_packet(mp, is_inbound, NULL, NULL, 28833448Sdh155122 DROPPER(ipss, ipds_ah_crypto_failed), 28843448Sdh155122 &ahstack->ah_dropper); 28853448Sdh155122 AH_BUMP_STAT(ahstack, crypto_failures); 28860Sstevel@tonic-gate if (is_inbound) 28873448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 28880Sstevel@tonic-gate else 28893448Sdh155122 AH_BUMP_STAT(ahstack, out_discards); 28900Sstevel@tonic-gate } 28910Sstevel@tonic-gate 28920Sstevel@tonic-gate /* 28930Sstevel@tonic-gate * Helper macros for the ah_submit_req_{inbound,outbound}() functions. 28940Sstevel@tonic-gate */ 28950Sstevel@tonic-gate 28963448Sdh155122 #define AH_INIT_CALLREQ(_cr, _ipss) { \ 28970Sstevel@tonic-gate (_cr)->cr_flag = CRYPTO_SKIP_REQID|CRYPTO_RESTRICTED; \ 28983448Sdh155122 if ((_ipss)->ipsec_algs_exec_mode[IPSEC_ALG_AUTH] == \ 28993448Sdh155122 IPSEC_ALGS_EXEC_ASYNC) \ 29000Sstevel@tonic-gate (_cr)->cr_flag |= CRYPTO_ALWAYS_QUEUE; \ 29010Sstevel@tonic-gate (_cr)->cr_callback_arg = ipsec_mp; \ 29020Sstevel@tonic-gate (_cr)->cr_callback_func = ah_kcf_callback; \ 29030Sstevel@tonic-gate } 29040Sstevel@tonic-gate 29050Sstevel@tonic-gate #define AH_INIT_CRYPTO_DATA(data, msglen, mblk) { \ 29060Sstevel@tonic-gate (data)->cd_format = CRYPTO_DATA_MBLK; \ 29070Sstevel@tonic-gate (data)->cd_mp = mblk; \ 29080Sstevel@tonic-gate (data)->cd_offset = 0; \ 29090Sstevel@tonic-gate (data)->cd_length = msglen; \ 29100Sstevel@tonic-gate } 29110Sstevel@tonic-gate 29120Sstevel@tonic-gate #define AH_INIT_CRYPTO_MAC(mac, icvlen, icvbuf) { \ 29130Sstevel@tonic-gate (mac)->cd_format = CRYPTO_DATA_RAW; \ 29140Sstevel@tonic-gate (mac)->cd_offset = 0; \ 29150Sstevel@tonic-gate (mac)->cd_length = icvlen; \ 29160Sstevel@tonic-gate (mac)->cd_raw.iov_base = icvbuf; \ 29170Sstevel@tonic-gate (mac)->cd_raw.iov_len = icvlen; \ 29180Sstevel@tonic-gate } 29190Sstevel@tonic-gate 29200Sstevel@tonic-gate /* 29210Sstevel@tonic-gate * Submit an inbound packet for processing by the crypto framework. 29220Sstevel@tonic-gate */ 29230Sstevel@tonic-gate static ipsec_status_t 29240Sstevel@tonic-gate ah_submit_req_inbound(mblk_t *ipsec_mp, size_t skip_len, uint32_t ah_offset, 29250Sstevel@tonic-gate ipsa_t *assoc) 29260Sstevel@tonic-gate { 29270Sstevel@tonic-gate int kef_rc; 29280Sstevel@tonic-gate mblk_t *phdr_mp; 29290Sstevel@tonic-gate crypto_call_req_t call_req; 29300Sstevel@tonic-gate ipsec_in_t *ii = (ipsec_in_t *)ipsec_mp->b_rptr; 29310Sstevel@tonic-gate uint_t icv_len = assoc->ipsa_mac_len; 29320Sstevel@tonic-gate crypto_ctx_template_t ctx_tmpl; 29333448Sdh155122 netstack_t *ns = ii->ipsec_in_ns; 29343448Sdh155122 ipsecah_stack_t *ahstack = ns->netstack_ipsecah; 29353448Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 29360Sstevel@tonic-gate 29370Sstevel@tonic-gate phdr_mp = ipsec_mp->b_cont; 29380Sstevel@tonic-gate ASSERT(phdr_mp != NULL); 29390Sstevel@tonic-gate ASSERT(ii->ipsec_in_type == IPSEC_IN); 29400Sstevel@tonic-gate 29413448Sdh155122 /* 29428704Sdanmcd@sun.com * In case kEF queues and calls back, make sure we have the 29438704Sdanmcd@sun.com * netstackid_t for verification that the IP instance is still around 29448704Sdanmcd@sun.com * in esp_kcf_callback(). 29453448Sdh155122 */ 29468704Sdanmcd@sun.com ASSERT(ii->ipsec_in_stackid == ns->netstack_stackid); 29473448Sdh155122 29480Sstevel@tonic-gate /* init arguments for the crypto framework */ 29490Sstevel@tonic-gate AH_INIT_CRYPTO_DATA(&ii->ipsec_in_crypto_data, AH_MSGSIZE(phdr_mp), 29500Sstevel@tonic-gate phdr_mp); 29510Sstevel@tonic-gate 29520Sstevel@tonic-gate AH_INIT_CRYPTO_MAC(&ii->ipsec_in_crypto_mac, icv_len, 29530Sstevel@tonic-gate (char *)phdr_mp->b_cont->b_rptr - skip_len + ah_offset + 29540Sstevel@tonic-gate sizeof (ah_t)); 29550Sstevel@tonic-gate 29563448Sdh155122 AH_INIT_CALLREQ(&call_req, ipss); 29570Sstevel@tonic-gate 29580Sstevel@tonic-gate ii->ipsec_in_skip_len = skip_len; 29590Sstevel@tonic-gate 29600Sstevel@tonic-gate IPSEC_CTX_TMPL(assoc, ipsa_authtmpl, IPSEC_ALG_AUTH, ctx_tmpl); 29610Sstevel@tonic-gate 29620Sstevel@tonic-gate /* call KEF to do the MAC operation */ 29630Sstevel@tonic-gate kef_rc = crypto_mac_verify(&assoc->ipsa_amech, 29640Sstevel@tonic-gate &ii->ipsec_in_crypto_data, &assoc->ipsa_kcfauthkey, ctx_tmpl, 29650Sstevel@tonic-gate &ii->ipsec_in_crypto_mac, &call_req); 29660Sstevel@tonic-gate 29670Sstevel@tonic-gate switch (kef_rc) { 29680Sstevel@tonic-gate case CRYPTO_SUCCESS: 29693448Sdh155122 AH_BUMP_STAT(ahstack, crypto_sync); 29700Sstevel@tonic-gate return (ah_auth_in_done(ipsec_mp)); 29710Sstevel@tonic-gate case CRYPTO_QUEUED: 29723448Sdh155122 /* ah_kcf_callback() will be invoked on completion */ 29733448Sdh155122 AH_BUMP_STAT(ahstack, crypto_async); 29740Sstevel@tonic-gate return (IPSEC_STATUS_PENDING); 29750Sstevel@tonic-gate case CRYPTO_INVALID_MAC: 29763448Sdh155122 AH_BUMP_STAT(ahstack, crypto_sync); 29770Sstevel@tonic-gate ah_log_bad_auth(ipsec_mp); 29780Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 29790Sstevel@tonic-gate } 29800Sstevel@tonic-gate 29813448Sdh155122 ah_crypto_failed(ipsec_mp, B_TRUE, kef_rc, ahstack); 29820Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 29830Sstevel@tonic-gate } 29840Sstevel@tonic-gate 29850Sstevel@tonic-gate /* 29860Sstevel@tonic-gate * Submit an outbound packet for processing by the crypto framework. 29870Sstevel@tonic-gate */ 29880Sstevel@tonic-gate static ipsec_status_t 29890Sstevel@tonic-gate ah_submit_req_outbound(mblk_t *ipsec_mp, size_t skip_len, ipsa_t *assoc) 29900Sstevel@tonic-gate { 29910Sstevel@tonic-gate int kef_rc; 29920Sstevel@tonic-gate mblk_t *phdr_mp; 29930Sstevel@tonic-gate crypto_call_req_t call_req; 29940Sstevel@tonic-gate ipsec_out_t *io = (ipsec_out_t *)ipsec_mp->b_rptr; 29950Sstevel@tonic-gate uint_t icv_len = assoc->ipsa_mac_len; 29963448Sdh155122 netstack_t *ns = io->ipsec_out_ns; 29973448Sdh155122 ipsecah_stack_t *ahstack = ns->netstack_ipsecah; 29983448Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 29990Sstevel@tonic-gate 30000Sstevel@tonic-gate phdr_mp = ipsec_mp->b_cont; 30010Sstevel@tonic-gate ASSERT(phdr_mp != NULL); 30020Sstevel@tonic-gate ASSERT(io->ipsec_out_type == IPSEC_OUT); 30030Sstevel@tonic-gate 30043448Sdh155122 /* 30053448Sdh155122 * In case kEF queues and calls back, keep netstackid_t for 30063448Sdh155122 * verification that the IP instance is still around in 30073448Sdh155122 * ah_kcf_callback(). 30083448Sdh155122 */ 30093448Sdh155122 io->ipsec_out_stackid = ns->netstack_stackid; 30103448Sdh155122 30110Sstevel@tonic-gate /* init arguments for the crypto framework */ 30120Sstevel@tonic-gate AH_INIT_CRYPTO_DATA(&io->ipsec_out_crypto_data, AH_MSGSIZE(phdr_mp), 30130Sstevel@tonic-gate phdr_mp); 30140Sstevel@tonic-gate 30150Sstevel@tonic-gate AH_INIT_CRYPTO_MAC(&io->ipsec_out_crypto_mac, icv_len, 30160Sstevel@tonic-gate (char *)phdr_mp->b_wptr); 30170Sstevel@tonic-gate 30183448Sdh155122 AH_INIT_CALLREQ(&call_req, ipss); 30190Sstevel@tonic-gate 30200Sstevel@tonic-gate io->ipsec_out_skip_len = skip_len; 30210Sstevel@tonic-gate 30220Sstevel@tonic-gate ASSERT(io->ipsec_out_ah_sa != NULL); 30230Sstevel@tonic-gate 30240Sstevel@tonic-gate /* call KEF to do the MAC operation */ 30250Sstevel@tonic-gate kef_rc = crypto_mac(&assoc->ipsa_amech, &io->ipsec_out_crypto_data, 30260Sstevel@tonic-gate &assoc->ipsa_kcfauthkey, assoc->ipsa_authtmpl, 30270Sstevel@tonic-gate &io->ipsec_out_crypto_mac, &call_req); 30280Sstevel@tonic-gate 30290Sstevel@tonic-gate switch (kef_rc) { 30300Sstevel@tonic-gate case CRYPTO_SUCCESS: 30313448Sdh155122 AH_BUMP_STAT(ahstack, crypto_sync); 30320Sstevel@tonic-gate return (ah_auth_out_done(ipsec_mp)); 30330Sstevel@tonic-gate case CRYPTO_QUEUED: 30343448Sdh155122 /* ah_kcf_callback() will be invoked on completion */ 30353448Sdh155122 AH_BUMP_STAT(ahstack, crypto_async); 30360Sstevel@tonic-gate return (IPSEC_STATUS_PENDING); 30370Sstevel@tonic-gate } 30380Sstevel@tonic-gate 30393448Sdh155122 ah_crypto_failed(ipsec_mp, B_FALSE, kef_rc, ahstack); 30400Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 30410Sstevel@tonic-gate } 30420Sstevel@tonic-gate 30430Sstevel@tonic-gate /* 30440Sstevel@tonic-gate * This function constructs a pseudo header by looking at the IP header 30450Sstevel@tonic-gate * and options if any. This is called for both outbound and inbound, 30460Sstevel@tonic-gate * before computing the ICV. 30470Sstevel@tonic-gate */ 30480Sstevel@tonic-gate static mblk_t * 30490Sstevel@tonic-gate ah_process_ip_options_v6(mblk_t *mp, ipsa_t *assoc, int *length_to_skip, 30503448Sdh155122 uint_t ah_data_sz, boolean_t outbound, ipsecah_stack_t *ahstack) 30510Sstevel@tonic-gate { 30520Sstevel@tonic-gate ip6_t *ip6h; 30530Sstevel@tonic-gate ip6_t *oip6h; 30540Sstevel@tonic-gate mblk_t *phdr_mp; 30550Sstevel@tonic-gate int option_length; 30560Sstevel@tonic-gate uint_t ah_align_sz; 30570Sstevel@tonic-gate uint_t ah_offset; 30580Sstevel@tonic-gate int hdr_size; 30593448Sdh155122 ipsec_stack_t *ipss = ahstack->ipsecah_netstack->netstack_ipsec; 30600Sstevel@tonic-gate 30610Sstevel@tonic-gate /* 30620Sstevel@tonic-gate * Allocate space for the authentication data also. It is 30630Sstevel@tonic-gate * useful both during the ICV calculation where we need to 30640Sstevel@tonic-gate * feed in zeroes and while sending the datagram back to IP 30650Sstevel@tonic-gate * where we will be using the same space. 30660Sstevel@tonic-gate * 30670Sstevel@tonic-gate * We need to allocate space for padding bytes if it is not 30680Sstevel@tonic-gate * a multiple of IPV6_PADDING_ALIGN. 30690Sstevel@tonic-gate * 30700Sstevel@tonic-gate * In addition, we allocate space for the ICV computed by 30710Sstevel@tonic-gate * the kernel crypto framework, saving us a separate kmem 30720Sstevel@tonic-gate * allocation down the road. 30730Sstevel@tonic-gate */ 30740Sstevel@tonic-gate 30750Sstevel@tonic-gate ah_align_sz = P2ALIGN(ah_data_sz + IPV6_PADDING_ALIGN - 1, 30760Sstevel@tonic-gate IPV6_PADDING_ALIGN); 30770Sstevel@tonic-gate 30780Sstevel@tonic-gate ASSERT(ah_align_sz >= ah_data_sz); 30790Sstevel@tonic-gate 30800Sstevel@tonic-gate hdr_size = ipsec_ah_get_hdr_size_v6(mp, B_FALSE); 30810Sstevel@tonic-gate option_length = hdr_size - IPV6_HDR_LEN; 30820Sstevel@tonic-gate 30830Sstevel@tonic-gate /* This was not included in ipsec_ah_get_hdr_size_v6() */ 30840Sstevel@tonic-gate hdr_size += (sizeof (ah_t) + ah_align_sz); 30850Sstevel@tonic-gate 30860Sstevel@tonic-gate if (!outbound && (MBLKL(mp) < hdr_size)) { 30870Sstevel@tonic-gate /* 30880Sstevel@tonic-gate * We have post-AH header options in a separate mblk, 30890Sstevel@tonic-gate * a pullup is required. 30900Sstevel@tonic-gate */ 30910Sstevel@tonic-gate if (!pullupmsg(mp, hdr_size)) 30920Sstevel@tonic-gate return (NULL); 30930Sstevel@tonic-gate } 30940Sstevel@tonic-gate 30958778SErik.Nordmark@Sun.COM if ((phdr_mp = allocb_tmpl(hdr_size + ah_data_sz, mp)) == NULL) { 30960Sstevel@tonic-gate return (NULL); 30970Sstevel@tonic-gate } 30980Sstevel@tonic-gate 30990Sstevel@tonic-gate oip6h = (ip6_t *)mp->b_rptr; 31000Sstevel@tonic-gate 31010Sstevel@tonic-gate /* 31020Sstevel@tonic-gate * Form the basic IP header first. Zero out the header 31030Sstevel@tonic-gate * so that the mutable fields are zeroed out. 31040Sstevel@tonic-gate */ 31050Sstevel@tonic-gate ip6h = (ip6_t *)phdr_mp->b_rptr; 31060Sstevel@tonic-gate bzero(ip6h, sizeof (ip6_t)); 31070Sstevel@tonic-gate ip6h->ip6_vcf = IPV6_DEFAULT_VERS_AND_FLOW; 31080Sstevel@tonic-gate 31090Sstevel@tonic-gate if (outbound) { 31100Sstevel@tonic-gate /* 31110Sstevel@tonic-gate * Include the size of AH and authentication data. 31120Sstevel@tonic-gate * This is how our recipient would compute the 31130Sstevel@tonic-gate * authentication data. Look at what we do in the 31140Sstevel@tonic-gate * inbound case below. 31150Sstevel@tonic-gate */ 31160Sstevel@tonic-gate ip6h->ip6_plen = htons(ntohs(oip6h->ip6_plen) + 31170Sstevel@tonic-gate sizeof (ah_t) + ah_align_sz); 31180Sstevel@tonic-gate } else { 31190Sstevel@tonic-gate ip6h->ip6_plen = oip6h->ip6_plen; 31200Sstevel@tonic-gate } 31210Sstevel@tonic-gate 31220Sstevel@tonic-gate ip6h->ip6_src = oip6h->ip6_src; 31230Sstevel@tonic-gate ip6h->ip6_dst = oip6h->ip6_dst; 31240Sstevel@tonic-gate 31250Sstevel@tonic-gate *length_to_skip = IPV6_HDR_LEN; 31260Sstevel@tonic-gate if (option_length == 0) { 31270Sstevel@tonic-gate /* Form the AH header */ 31280Sstevel@tonic-gate ip6h->ip6_nxt = IPPROTO_AH; 31290Sstevel@tonic-gate ((ah_t *)(ip6h + 1))->ah_nexthdr = oip6h->ip6_nxt; 31300Sstevel@tonic-gate ah_offset = *length_to_skip; 31310Sstevel@tonic-gate } else { 31320Sstevel@tonic-gate ip6h->ip6_nxt = oip6h->ip6_nxt; 31330Sstevel@tonic-gate /* option_length does not include the AH header's size */ 31340Sstevel@tonic-gate *length_to_skip += option_length; 31350Sstevel@tonic-gate 31360Sstevel@tonic-gate ah_offset = ah_fix_phdr_v6(ip6h, oip6h, outbound, B_FALSE); 31370Sstevel@tonic-gate if (ah_offset == 0) { 31380Sstevel@tonic-gate ip_drop_packet(phdr_mp, !outbound, NULL, NULL, 31393448Sdh155122 DROPPER(ipss, ipds_ah_bad_v6_hdrs), 31403448Sdh155122 &ahstack->ah_dropper); 31410Sstevel@tonic-gate return (NULL); 31420Sstevel@tonic-gate } 31430Sstevel@tonic-gate } 31440Sstevel@tonic-gate 31450Sstevel@tonic-gate if (!ah_finish_up(((ah_t *)((uint8_t *)ip6h + ah_offset)), 31460Sstevel@tonic-gate (outbound ? NULL : ((ah_t *)((uint8_t *)oip6h + ah_offset))), 31473448Sdh155122 assoc, ah_data_sz, ah_align_sz, ahstack)) { 31480Sstevel@tonic-gate freeb(phdr_mp); 31490Sstevel@tonic-gate /* 31500Sstevel@tonic-gate * Returning NULL will tell the caller to 31510Sstevel@tonic-gate * IPSA_REFELE(), free the memory, etc. 31520Sstevel@tonic-gate */ 31530Sstevel@tonic-gate return (NULL); 31540Sstevel@tonic-gate } 31550Sstevel@tonic-gate 31560Sstevel@tonic-gate phdr_mp->b_wptr = ((uint8_t *)ip6h + ah_offset + sizeof (ah_t) + 31570Sstevel@tonic-gate ah_align_sz); 31580Sstevel@tonic-gate if (!outbound) 31590Sstevel@tonic-gate *length_to_skip += sizeof (ah_t) + ah_align_sz; 31600Sstevel@tonic-gate return (phdr_mp); 31610Sstevel@tonic-gate } 31620Sstevel@tonic-gate 31630Sstevel@tonic-gate /* 31640Sstevel@tonic-gate * This function constructs a pseudo header by looking at the IP header 31650Sstevel@tonic-gate * and options if any. This is called for both outbound and inbound, 31660Sstevel@tonic-gate * before computing the ICV. 31670Sstevel@tonic-gate */ 31680Sstevel@tonic-gate static mblk_t * 31690Sstevel@tonic-gate ah_process_ip_options_v4(mblk_t *mp, ipsa_t *assoc, int *length_to_skip, 31703448Sdh155122 uint_t ah_data_sz, boolean_t outbound, ipsecah_stack_t *ahstack) 31710Sstevel@tonic-gate { 31720Sstevel@tonic-gate ipoptp_t opts; 31730Sstevel@tonic-gate uint32_t option_length; 31740Sstevel@tonic-gate ipha_t *ipha; 31750Sstevel@tonic-gate ipha_t *oipha; 31760Sstevel@tonic-gate mblk_t *phdr_mp; 31770Sstevel@tonic-gate int size; 31780Sstevel@tonic-gate uchar_t *optptr; 31790Sstevel@tonic-gate uint8_t optval; 31800Sstevel@tonic-gate uint8_t optlen; 31810Sstevel@tonic-gate ipaddr_t dst; 31820Sstevel@tonic-gate uint32_t v_hlen_tos_len; 31830Sstevel@tonic-gate int ip_hdr_length; 31840Sstevel@tonic-gate uint_t ah_align_sz; 31850Sstevel@tonic-gate uint32_t off; 31860Sstevel@tonic-gate 31870Sstevel@tonic-gate #ifdef _BIG_ENDIAN 31880Sstevel@tonic-gate #define V_HLEN (v_hlen_tos_len >> 24) 31890Sstevel@tonic-gate #else 31900Sstevel@tonic-gate #define V_HLEN (v_hlen_tos_len & 0xFF) 31910Sstevel@tonic-gate #endif 31920Sstevel@tonic-gate 31930Sstevel@tonic-gate oipha = (ipha_t *)mp->b_rptr; 31940Sstevel@tonic-gate v_hlen_tos_len = ((uint32_t *)oipha)[0]; 31950Sstevel@tonic-gate 31960Sstevel@tonic-gate /* 31970Sstevel@tonic-gate * Allocate space for the authentication data also. It is 31980Sstevel@tonic-gate * useful both during the ICV calculation where we need to 31990Sstevel@tonic-gate * feed in zeroes and while sending the datagram back to IP 32000Sstevel@tonic-gate * where we will be using the same space. 32010Sstevel@tonic-gate * 32020Sstevel@tonic-gate * We need to allocate space for padding bytes if it is not 32030Sstevel@tonic-gate * a multiple of IPV4_PADDING_ALIGN. 32040Sstevel@tonic-gate * 32050Sstevel@tonic-gate * In addition, we allocate space for the ICV computed by 32060Sstevel@tonic-gate * the kernel crypto framework, saving us a separate kmem 32070Sstevel@tonic-gate * allocation down the road. 32080Sstevel@tonic-gate */ 32090Sstevel@tonic-gate 32100Sstevel@tonic-gate ah_align_sz = P2ALIGN(ah_data_sz + IPV4_PADDING_ALIGN - 1, 32110Sstevel@tonic-gate IPV4_PADDING_ALIGN); 32120Sstevel@tonic-gate 32130Sstevel@tonic-gate ASSERT(ah_align_sz >= ah_data_sz); 32140Sstevel@tonic-gate 32150Sstevel@tonic-gate size = IP_SIMPLE_HDR_LENGTH + sizeof (ah_t) + ah_align_sz + 32160Sstevel@tonic-gate ah_data_sz; 32170Sstevel@tonic-gate 32180Sstevel@tonic-gate if (V_HLEN != IP_SIMPLE_HDR_VERSION) { 32190Sstevel@tonic-gate option_length = oipha->ipha_version_and_hdr_length - 32200Sstevel@tonic-gate (uint8_t)((IP_VERSION << 4) + 32210Sstevel@tonic-gate IP_SIMPLE_HDR_LENGTH_IN_WORDS); 32220Sstevel@tonic-gate option_length <<= 2; 32230Sstevel@tonic-gate size += option_length; 32240Sstevel@tonic-gate } 32250Sstevel@tonic-gate 32268778SErik.Nordmark@Sun.COM if ((phdr_mp = allocb_tmpl(size, mp)) == NULL) { 32270Sstevel@tonic-gate return (NULL); 32280Sstevel@tonic-gate } 32290Sstevel@tonic-gate 32300Sstevel@tonic-gate /* 32310Sstevel@tonic-gate * Form the basic IP header first. 32320Sstevel@tonic-gate */ 32330Sstevel@tonic-gate ipha = (ipha_t *)phdr_mp->b_rptr; 32340Sstevel@tonic-gate ipha->ipha_version_and_hdr_length = oipha->ipha_version_and_hdr_length; 32350Sstevel@tonic-gate ipha->ipha_type_of_service = 0; 32360Sstevel@tonic-gate 32370Sstevel@tonic-gate if (outbound) { 32380Sstevel@tonic-gate /* 32390Sstevel@tonic-gate * Include the size of AH and authentication data. 32400Sstevel@tonic-gate * This is how our recipient would compute the 32410Sstevel@tonic-gate * authentication data. Look at what we do in the 32420Sstevel@tonic-gate * inbound case below. 32430Sstevel@tonic-gate */ 32440Sstevel@tonic-gate ipha->ipha_length = ntohs(htons(oipha->ipha_length) + 32450Sstevel@tonic-gate sizeof (ah_t) + ah_align_sz); 32460Sstevel@tonic-gate } else { 32470Sstevel@tonic-gate ipha->ipha_length = oipha->ipha_length; 32480Sstevel@tonic-gate } 32490Sstevel@tonic-gate 32500Sstevel@tonic-gate ipha->ipha_ident = oipha->ipha_ident; 32510Sstevel@tonic-gate ipha->ipha_fragment_offset_and_flags = 0; 32520Sstevel@tonic-gate ipha->ipha_ttl = 0; 32530Sstevel@tonic-gate ipha->ipha_protocol = IPPROTO_AH; 32540Sstevel@tonic-gate ipha->ipha_hdr_checksum = 0; 32550Sstevel@tonic-gate ipha->ipha_src = oipha->ipha_src; 32560Sstevel@tonic-gate ipha->ipha_dst = dst = oipha->ipha_dst; 32570Sstevel@tonic-gate 32580Sstevel@tonic-gate /* 32590Sstevel@tonic-gate * If there is no option to process return now. 32600Sstevel@tonic-gate */ 32610Sstevel@tonic-gate ip_hdr_length = IP_SIMPLE_HDR_LENGTH; 32620Sstevel@tonic-gate 32630Sstevel@tonic-gate if (V_HLEN == IP_SIMPLE_HDR_VERSION) { 32640Sstevel@tonic-gate /* Form the AH header */ 32650Sstevel@tonic-gate goto ah_hdr; 32660Sstevel@tonic-gate } 32670Sstevel@tonic-gate 32680Sstevel@tonic-gate ip_hdr_length += option_length; 32690Sstevel@tonic-gate 32700Sstevel@tonic-gate /* 32710Sstevel@tonic-gate * We have options. In the outbound case for source route, 32720Sstevel@tonic-gate * ULP has already moved the first hop, which is now in 32730Sstevel@tonic-gate * ipha_dst. We need the final destination for the calculation 32740Sstevel@tonic-gate * of authentication data. And also make sure that mutable 32750Sstevel@tonic-gate * and experimental fields are zeroed out in the IP options. 32760Sstevel@tonic-gate */ 32770Sstevel@tonic-gate 32780Sstevel@tonic-gate bcopy(&oipha[1], &ipha[1], option_length); 32790Sstevel@tonic-gate 32800Sstevel@tonic-gate for (optval = ipoptp_first(&opts, ipha); 32810Sstevel@tonic-gate optval != IPOPT_EOL; 32820Sstevel@tonic-gate optval = ipoptp_next(&opts)) { 32830Sstevel@tonic-gate optptr = opts.ipoptp_cur; 32840Sstevel@tonic-gate optlen = opts.ipoptp_len; 32850Sstevel@tonic-gate switch (optval) { 32860Sstevel@tonic-gate case IPOPT_EXTSEC: 32870Sstevel@tonic-gate case IPOPT_COMSEC: 32880Sstevel@tonic-gate case IPOPT_RA: 32890Sstevel@tonic-gate case IPOPT_SDMDD: 32900Sstevel@tonic-gate case IPOPT_SECURITY: 32910Sstevel@tonic-gate /* 32920Sstevel@tonic-gate * These options are Immutable, leave them as-is. 32930Sstevel@tonic-gate * Note that IPOPT_NOP is also Immutable, but it 32940Sstevel@tonic-gate * was skipped by ipoptp_next() and thus remains 32950Sstevel@tonic-gate * intact in the header. 32960Sstevel@tonic-gate */ 32970Sstevel@tonic-gate break; 32980Sstevel@tonic-gate case IPOPT_SSRR: 32990Sstevel@tonic-gate case IPOPT_LSRR: 33000Sstevel@tonic-gate if ((opts.ipoptp_flags & IPOPTP_ERROR) != 0) 33010Sstevel@tonic-gate goto bad_ipv4opt; 33020Sstevel@tonic-gate /* 33030Sstevel@tonic-gate * These two are mutable and will be zeroed, but 33040Sstevel@tonic-gate * first get the final destination. 33050Sstevel@tonic-gate */ 33060Sstevel@tonic-gate off = optptr[IPOPT_OFFSET]; 33070Sstevel@tonic-gate /* 33080Sstevel@tonic-gate * If one of the conditions is true, it means 33090Sstevel@tonic-gate * end of options and dst already has the right 33100Sstevel@tonic-gate * value. So, just fall through. 33110Sstevel@tonic-gate */ 33120Sstevel@tonic-gate if (!(optlen < IP_ADDR_LEN || off > optlen - 3)) { 33130Sstevel@tonic-gate off = optlen - IP_ADDR_LEN; 33140Sstevel@tonic-gate bcopy(&optptr[off], &dst, IP_ADDR_LEN); 33150Sstevel@tonic-gate } 33160Sstevel@tonic-gate /* FALLTHRU */ 33170Sstevel@tonic-gate case IPOPT_RR: 33180Sstevel@tonic-gate case IPOPT_TS: 33190Sstevel@tonic-gate case IPOPT_SATID: 33200Sstevel@tonic-gate default: 33210Sstevel@tonic-gate /* 33220Sstevel@tonic-gate * optlen should include from the beginning of an 33230Sstevel@tonic-gate * option. 33240Sstevel@tonic-gate * NOTE : Stream Identifier Option (SID): RFC 791 33250Sstevel@tonic-gate * shows the bit pattern of optlen as 2 and documents 33260Sstevel@tonic-gate * the length as 4. We assume it to be 2 here. 33270Sstevel@tonic-gate */ 33280Sstevel@tonic-gate bzero(optptr, optlen); 33290Sstevel@tonic-gate break; 33300Sstevel@tonic-gate } 33310Sstevel@tonic-gate } 33320Sstevel@tonic-gate 33330Sstevel@tonic-gate if ((opts.ipoptp_flags & IPOPTP_ERROR) != 0) { 33340Sstevel@tonic-gate bad_ipv4opt: 33353448Sdh155122 ah1dbg(ahstack, ("AH : bad IPv4 option")); 33360Sstevel@tonic-gate freeb(phdr_mp); 33370Sstevel@tonic-gate return (NULL); 33380Sstevel@tonic-gate } 33390Sstevel@tonic-gate 33400Sstevel@tonic-gate /* 33410Sstevel@tonic-gate * Don't change ipha_dst for an inbound datagram as it points 33420Sstevel@tonic-gate * to the right value. Only for the outbound with LSRR/SSRR, 33430Sstevel@tonic-gate * because of ip_massage_options called by the ULP, ipha_dst 33440Sstevel@tonic-gate * points to the first hop and we need to use the final 33450Sstevel@tonic-gate * destination for computing the ICV. 33460Sstevel@tonic-gate */ 33470Sstevel@tonic-gate 33480Sstevel@tonic-gate if (outbound) 33490Sstevel@tonic-gate ipha->ipha_dst = dst; 33500Sstevel@tonic-gate ah_hdr: 33510Sstevel@tonic-gate ((ah_t *)((uint8_t *)ipha + ip_hdr_length))->ah_nexthdr = 33520Sstevel@tonic-gate oipha->ipha_protocol; 33530Sstevel@tonic-gate if (!ah_finish_up(((ah_t *)((uint8_t *)ipha + ip_hdr_length)), 33540Sstevel@tonic-gate (outbound ? NULL : ((ah_t *)((uint8_t *)oipha + ip_hdr_length))), 33553448Sdh155122 assoc, ah_data_sz, ah_align_sz, ahstack)) { 33560Sstevel@tonic-gate freeb(phdr_mp); 33570Sstevel@tonic-gate /* 33580Sstevel@tonic-gate * Returning NULL will tell the caller to IPSA_REFELE(), free 33590Sstevel@tonic-gate * the memory, etc. 33600Sstevel@tonic-gate */ 33610Sstevel@tonic-gate return (NULL); 33620Sstevel@tonic-gate } 33630Sstevel@tonic-gate 33640Sstevel@tonic-gate phdr_mp->b_wptr = ((uchar_t *)ipha + ip_hdr_length + 33650Sstevel@tonic-gate sizeof (ah_t) + ah_align_sz); 33660Sstevel@tonic-gate 33670Sstevel@tonic-gate ASSERT(phdr_mp->b_wptr <= phdr_mp->b_datap->db_lim); 33680Sstevel@tonic-gate if (outbound) 33690Sstevel@tonic-gate *length_to_skip = ip_hdr_length; 33700Sstevel@tonic-gate else 33710Sstevel@tonic-gate *length_to_skip = ip_hdr_length + sizeof (ah_t) + ah_align_sz; 33720Sstevel@tonic-gate return (phdr_mp); 33730Sstevel@tonic-gate } 33740Sstevel@tonic-gate 33750Sstevel@tonic-gate /* 33760Sstevel@tonic-gate * Authenticate an outbound datagram. This function is called 33770Sstevel@tonic-gate * whenever IP sends an outbound datagram that needs authentication. 33780Sstevel@tonic-gate */ 33790Sstevel@tonic-gate static ipsec_status_t 33800Sstevel@tonic-gate ah_outbound(mblk_t *ipsec_out) 33810Sstevel@tonic-gate { 33820Sstevel@tonic-gate mblk_t *mp; 33830Sstevel@tonic-gate mblk_t *phdr_mp; 33840Sstevel@tonic-gate ipsec_out_t *oi; 33850Sstevel@tonic-gate ipsa_t *assoc; 33860Sstevel@tonic-gate int length_to_skip; 33870Sstevel@tonic-gate uint_t ah_align_sz; 33880Sstevel@tonic-gate uint_t age_bytes; 33893448Sdh155122 netstack_t *ns; 33903448Sdh155122 ipsec_stack_t *ipss; 33913448Sdh155122 ipsecah_stack_t *ahstack; 33920Sstevel@tonic-gate 33930Sstevel@tonic-gate /* 33940Sstevel@tonic-gate * Construct the chain of mblks 33950Sstevel@tonic-gate * 33960Sstevel@tonic-gate * IPSEC_OUT->PSEUDO_HDR->DATA 33970Sstevel@tonic-gate * 33980Sstevel@tonic-gate * one by one. 33990Sstevel@tonic-gate */ 34000Sstevel@tonic-gate 34010Sstevel@tonic-gate ASSERT(ipsec_out->b_datap->db_type == M_CTL); 34020Sstevel@tonic-gate 34030Sstevel@tonic-gate ASSERT(MBLKL(ipsec_out) >= sizeof (ipsec_info_t)); 34040Sstevel@tonic-gate 34050Sstevel@tonic-gate mp = ipsec_out->b_cont; 34060Sstevel@tonic-gate oi = (ipsec_out_t *)ipsec_out->b_rptr; 34073448Sdh155122 ns = oi->ipsec_out_ns; 34083448Sdh155122 ipss = ns->netstack_ipsec; 34093448Sdh155122 ahstack = ns->netstack_ipsecah; 34103448Sdh155122 34113448Sdh155122 AH_BUMP_STAT(ahstack, out_requests); 34120Sstevel@tonic-gate 34130Sstevel@tonic-gate ASSERT(mp->b_datap->db_type == M_DATA); 34140Sstevel@tonic-gate 34150Sstevel@tonic-gate assoc = oi->ipsec_out_ah_sa; 34160Sstevel@tonic-gate ASSERT(assoc != NULL); 34170Sstevel@tonic-gate 3418*10934Ssommerfeld@sun.com 3419*10934Ssommerfeld@sun.com /* 3420*10934Ssommerfeld@sun.com * Get the outer IP header in shape to escape this system.. 3421*10934Ssommerfeld@sun.com */ 3422*10934Ssommerfeld@sun.com if (is_system_labeled() && (assoc->ipsa_ocred != NULL)) { 3423*10934Ssommerfeld@sun.com int whack; 3424*10934Ssommerfeld@sun.com 3425*10934Ssommerfeld@sun.com mblk_setcred(mp, assoc->ipsa_ocred, NOPID); 3426*10934Ssommerfeld@sun.com if (oi->ipsec_out_v4) 3427*10934Ssommerfeld@sun.com whack = sadb_whack_label(&mp, assoc); 3428*10934Ssommerfeld@sun.com else 3429*10934Ssommerfeld@sun.com whack = sadb_whack_label_v6(&mp, assoc); 3430*10934Ssommerfeld@sun.com if (whack != 0) { 3431*10934Ssommerfeld@sun.com ip_drop_packet(ipsec_out, B_FALSE, NULL, 3432*10934Ssommerfeld@sun.com NULL, DROPPER(ipss, ipds_ah_nomem), 3433*10934Ssommerfeld@sun.com &ahstack->ah_dropper); 3434*10934Ssommerfeld@sun.com return (IPSEC_STATUS_FAILED); 3435*10934Ssommerfeld@sun.com } 3436*10934Ssommerfeld@sun.com ipsec_out->b_cont = mp; 3437*10934Ssommerfeld@sun.com } 3438*10934Ssommerfeld@sun.com 34390Sstevel@tonic-gate /* 34400Sstevel@tonic-gate * Age SA according to number of bytes that will be sent after 34410Sstevel@tonic-gate * adding the AH header, ICV, and padding to the packet. 34420Sstevel@tonic-gate */ 34430Sstevel@tonic-gate 34440Sstevel@tonic-gate if (oi->ipsec_out_v4) { 34450Sstevel@tonic-gate ipha_t *ipha = (ipha_t *)mp->b_rptr; 34460Sstevel@tonic-gate ah_align_sz = P2ALIGN(assoc->ipsa_mac_len + 34470Sstevel@tonic-gate IPV4_PADDING_ALIGN - 1, IPV4_PADDING_ALIGN); 34480Sstevel@tonic-gate age_bytes = ntohs(ipha->ipha_length) + sizeof (ah_t) + 34490Sstevel@tonic-gate ah_align_sz; 34500Sstevel@tonic-gate } else { 34510Sstevel@tonic-gate ip6_t *ip6h = (ip6_t *)mp->b_rptr; 34520Sstevel@tonic-gate ah_align_sz = P2ALIGN(assoc->ipsa_mac_len + 34530Sstevel@tonic-gate IPV6_PADDING_ALIGN - 1, IPV6_PADDING_ALIGN); 34540Sstevel@tonic-gate age_bytes = sizeof (ip6_t) + ntohs(ip6h->ip6_plen) + 34554987Sdanmcd sizeof (ah_t) + ah_align_sz; 34560Sstevel@tonic-gate } 34570Sstevel@tonic-gate 34580Sstevel@tonic-gate if (!ah_age_bytes(assoc, age_bytes, B_FALSE)) { 34590Sstevel@tonic-gate /* rig things as if ipsec_getassocbyconn() failed */ 34600Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, SL_ERROR | SL_WARN, 34610Sstevel@tonic-gate "AH association 0x%x, dst %s had bytes expire.\n", 34623448Sdh155122 ntohl(assoc->ipsa_spi), assoc->ipsa_dstaddr, AF_INET, 34633448Sdh155122 ahstack->ipsecah_netstack); 34640Sstevel@tonic-gate freemsg(ipsec_out); 34650Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 34660Sstevel@tonic-gate } 34670Sstevel@tonic-gate 3468*10934Ssommerfeld@sun.com /* 3469*10934Ssommerfeld@sun.com * XXX We need to have fixed up the outer label before we get here. 3470*10934Ssommerfeld@sun.com * (AH is computing the checksum over the outer label). 3471*10934Ssommerfeld@sun.com */ 3472*10934Ssommerfeld@sun.com 34730Sstevel@tonic-gate if (oi->ipsec_out_is_capab_ill) { 34743448Sdh155122 ah3dbg(ahstack, ("ah_outbound: pkt can be accelerated\n")); 34750Sstevel@tonic-gate if (oi->ipsec_out_v4) 34760Sstevel@tonic-gate return (ah_outbound_accelerated_v4(ipsec_out, assoc)); 34770Sstevel@tonic-gate else 34780Sstevel@tonic-gate return (ah_outbound_accelerated_v6(ipsec_out, assoc)); 34790Sstevel@tonic-gate } 34803448Sdh155122 AH_BUMP_STAT(ahstack, noaccel); 34810Sstevel@tonic-gate 34820Sstevel@tonic-gate /* 34830Sstevel@tonic-gate * Insert pseudo header: 34840Sstevel@tonic-gate * IPSEC_INFO -> [IP, ULP] => IPSEC_INFO -> [IP, AH, ICV] -> ULP 34850Sstevel@tonic-gate */ 34860Sstevel@tonic-gate 34870Sstevel@tonic-gate if (oi->ipsec_out_v4) { 34880Sstevel@tonic-gate phdr_mp = ah_process_ip_options_v4(mp, assoc, &length_to_skip, 34893448Sdh155122 assoc->ipsa_mac_len, B_TRUE, ahstack); 34900Sstevel@tonic-gate } else { 34910Sstevel@tonic-gate phdr_mp = ah_process_ip_options_v6(mp, assoc, &length_to_skip, 34923448Sdh155122 assoc->ipsa_mac_len, B_TRUE, ahstack); 34930Sstevel@tonic-gate } 34940Sstevel@tonic-gate 34950Sstevel@tonic-gate if (phdr_mp == NULL) { 34963448Sdh155122 AH_BUMP_STAT(ahstack, out_discards); 34970Sstevel@tonic-gate ip_drop_packet(ipsec_out, B_FALSE, NULL, NULL, 34983448Sdh155122 DROPPER(ipss, ipds_ah_bad_v4_opts), 34993448Sdh155122 &ahstack->ah_dropper); 35000Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 35010Sstevel@tonic-gate } 35020Sstevel@tonic-gate 35030Sstevel@tonic-gate ipsec_out->b_cont = phdr_mp; 35040Sstevel@tonic-gate phdr_mp->b_cont = mp; 35050Sstevel@tonic-gate mp->b_rptr += length_to_skip; 35060Sstevel@tonic-gate 35070Sstevel@tonic-gate /* 35080Sstevel@tonic-gate * At this point ipsec_out points to the IPSEC_OUT, new_mp 35090Sstevel@tonic-gate * points to an mblk containing the pseudo header (IP header, 35100Sstevel@tonic-gate * AH header, and ICV with mutable fields zero'ed out). 35110Sstevel@tonic-gate * mp points to the mblk containing the ULP data. The original 35120Sstevel@tonic-gate * IP header is kept before the ULP data in mp. 35130Sstevel@tonic-gate */ 35140Sstevel@tonic-gate 35150Sstevel@tonic-gate /* submit MAC request to KCF */ 35160Sstevel@tonic-gate return (ah_submit_req_outbound(ipsec_out, length_to_skip, assoc)); 35170Sstevel@tonic-gate } 35180Sstevel@tonic-gate 35190Sstevel@tonic-gate static ipsec_status_t 35200Sstevel@tonic-gate ah_inbound(mblk_t *ipsec_in_mp, void *arg) 35210Sstevel@tonic-gate { 35220Sstevel@tonic-gate mblk_t *data_mp = ipsec_in_mp->b_cont; 35230Sstevel@tonic-gate ipsec_in_t *ii = (ipsec_in_t *)ipsec_in_mp->b_rptr; 35240Sstevel@tonic-gate ah_t *ah = (ah_t *)arg; 35250Sstevel@tonic-gate ipsa_t *assoc = ii->ipsec_in_ah_sa; 35260Sstevel@tonic-gate int length_to_skip; 35270Sstevel@tonic-gate int ah_length; 35280Sstevel@tonic-gate mblk_t *phdr_mp; 35290Sstevel@tonic-gate uint32_t ah_offset; 35303448Sdh155122 netstack_t *ns = ii->ipsec_in_ns; 35313448Sdh155122 ipsecah_stack_t *ahstack = ns->netstack_ipsecah; 35323448Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 35330Sstevel@tonic-gate 35340Sstevel@tonic-gate ASSERT(assoc != NULL); 35350Sstevel@tonic-gate 35360Sstevel@tonic-gate /* 35370Sstevel@tonic-gate * We may wish to check replay in-range-only here as an optimization. 35380Sstevel@tonic-gate * Include the reality check of ipsa->ipsa_replay > 35390Sstevel@tonic-gate * ipsa->ipsa_replay_wsize for times when it's the first N packets, 35400Sstevel@tonic-gate * where N == ipsa->ipsa_replay_wsize. 35410Sstevel@tonic-gate * 35420Sstevel@tonic-gate * Another check that may come here later is the "collision" check. 35430Sstevel@tonic-gate * If legitimate packets flow quickly enough, this won't be a problem, 35440Sstevel@tonic-gate * but collisions may cause authentication algorithm crunching to 35450Sstevel@tonic-gate * take place when it doesn't need to. 35460Sstevel@tonic-gate */ 35470Sstevel@tonic-gate if (!sadb_replay_peek(assoc, ah->ah_replay)) { 35483448Sdh155122 AH_BUMP_STAT(ahstack, replay_early_failures); 35493448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 35500Sstevel@tonic-gate ip_drop_packet(ipsec_in_mp, B_TRUE, NULL, NULL, 35513448Sdh155122 DROPPER(ipss, ipds_ah_early_replay), 35523448Sdh155122 &ahstack->ah_dropper); 35530Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 35540Sstevel@tonic-gate } 35550Sstevel@tonic-gate 35560Sstevel@tonic-gate /* 35570Sstevel@tonic-gate * The offset of the AH header can be computed from its pointer 35580Sstevel@tonic-gate * within the data mblk, which was pulled up until the AH header 35590Sstevel@tonic-gate * by ipsec_inbound_ah_sa() during SA selection. 35600Sstevel@tonic-gate */ 35610Sstevel@tonic-gate ah_offset = (uchar_t *)ah - data_mp->b_rptr; 35620Sstevel@tonic-gate 35630Sstevel@tonic-gate /* 35640Sstevel@tonic-gate * Has this packet already been processed by a hardware 35650Sstevel@tonic-gate * IPsec accelerator? 35660Sstevel@tonic-gate */ 35670Sstevel@tonic-gate if (ii->ipsec_in_accelerated) { 35683448Sdh155122 ah3dbg(ahstack, 35693448Sdh155122 ("ah_inbound_v6: pkt processed by ill=%d isv6=%d\n", 35700Sstevel@tonic-gate ii->ipsec_in_ill_index, !ii->ipsec_in_v4)); 35710Sstevel@tonic-gate return (ah_inbound_accelerated(ipsec_in_mp, ii->ipsec_in_v4, 35720Sstevel@tonic-gate assoc, ah_offset)); 35730Sstevel@tonic-gate } 35743448Sdh155122 AH_BUMP_STAT(ahstack, noaccel); 35750Sstevel@tonic-gate 35760Sstevel@tonic-gate /* 35770Sstevel@tonic-gate * We need to pullup until the ICV before we call 35780Sstevel@tonic-gate * ah_process_ip_options_v6. 35790Sstevel@tonic-gate */ 35800Sstevel@tonic-gate ah_length = (ah->ah_length << 2) + 8; 35810Sstevel@tonic-gate 35820Sstevel@tonic-gate /* 35830Sstevel@tonic-gate * NOTE : If we want to use any field of IP/AH header, you need 35840Sstevel@tonic-gate * to re-assign following the pullup. 35850Sstevel@tonic-gate */ 35860Sstevel@tonic-gate if (((uchar_t *)ah + ah_length) > data_mp->b_wptr) { 35870Sstevel@tonic-gate if (!pullupmsg(data_mp, (uchar_t *)ah + ah_length - 35880Sstevel@tonic-gate data_mp->b_rptr)) { 35893448Sdh155122 (void) ipsec_rl_strlog(ns, info.mi_idnum, 0, 0, 35900Sstevel@tonic-gate SL_WARN | SL_ERROR, 35910Sstevel@tonic-gate "ah_inbound: Small AH header\n"); 35923448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 35930Sstevel@tonic-gate ip_drop_packet(ipsec_in_mp, B_TRUE, NULL, NULL, 35943448Sdh155122 DROPPER(ipss, ipds_ah_nomem), 35953448Sdh155122 &ahstack->ah_dropper); 35960Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 35970Sstevel@tonic-gate } 35980Sstevel@tonic-gate } 35990Sstevel@tonic-gate 36000Sstevel@tonic-gate /* 36010Sstevel@tonic-gate * Insert pseudo header: 36020Sstevel@tonic-gate * IPSEC_INFO -> [IP, ULP] => IPSEC_INFO -> [IP, AH, ICV] -> ULP 36030Sstevel@tonic-gate */ 36040Sstevel@tonic-gate if (ii->ipsec_in_v4) { 36050Sstevel@tonic-gate phdr_mp = ah_process_ip_options_v4(data_mp, assoc, 36063448Sdh155122 &length_to_skip, assoc->ipsa_mac_len, B_FALSE, ahstack); 36070Sstevel@tonic-gate } else { 36080Sstevel@tonic-gate phdr_mp = ah_process_ip_options_v6(data_mp, assoc, 36093448Sdh155122 &length_to_skip, assoc->ipsa_mac_len, B_FALSE, ahstack); 36100Sstevel@tonic-gate } 36110Sstevel@tonic-gate 36120Sstevel@tonic-gate if (phdr_mp == NULL) { 36133448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 36140Sstevel@tonic-gate ip_drop_packet(ipsec_in_mp, B_TRUE, NULL, NULL, 36153448Sdh155122 (ii->ipsec_in_v4 ? 36163448Sdh155122 DROPPER(ipss, ipds_ah_bad_v4_opts) : 36173448Sdh155122 DROPPER(ipss, ipds_ah_bad_v6_hdrs)), 36183448Sdh155122 &ahstack->ah_dropper); 36190Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 36200Sstevel@tonic-gate } 36210Sstevel@tonic-gate 36220Sstevel@tonic-gate ipsec_in_mp->b_cont = phdr_mp; 36230Sstevel@tonic-gate phdr_mp->b_cont = data_mp; 36240Sstevel@tonic-gate data_mp->b_rptr += length_to_skip; 36250Sstevel@tonic-gate 36260Sstevel@tonic-gate /* submit request to KCF */ 36270Sstevel@tonic-gate return (ah_submit_req_inbound(ipsec_in_mp, length_to_skip, ah_offset, 36280Sstevel@tonic-gate assoc)); 36290Sstevel@tonic-gate } 36300Sstevel@tonic-gate 36310Sstevel@tonic-gate /* 36320Sstevel@tonic-gate * ah_inbound_accelerated: 36330Sstevel@tonic-gate * Called from ah_inbound() to process IPsec packets that have been 36340Sstevel@tonic-gate * accelerated by hardware. 36350Sstevel@tonic-gate * 36360Sstevel@tonic-gate * Basically does what ah_auth_in_done() with some changes since 36370Sstevel@tonic-gate * no pseudo-headers are involved, i.e. the passed message is a 36380Sstevel@tonic-gate * IPSEC_INFO->DATA. 36390Sstevel@tonic-gate * 36400Sstevel@tonic-gate * It is assumed that only packets that have been successfully 36410Sstevel@tonic-gate * processed by the adapter come here. 36420Sstevel@tonic-gate * 36430Sstevel@tonic-gate * 1. get algorithm structure corresponding to association 36440Sstevel@tonic-gate * 2. calculate pointers to authentication header and ICV 36450Sstevel@tonic-gate * 3. compare ICV in AH header with ICV in data attributes 36460Sstevel@tonic-gate * 3.1 if different: 36470Sstevel@tonic-gate * 3.1.1 generate error 36480Sstevel@tonic-gate * 3.1.2 discard message 36490Sstevel@tonic-gate * 3.2 if ICV matches: 36500Sstevel@tonic-gate * 3.2.1 check replay 36510Sstevel@tonic-gate * 3.2.2 remove AH header 36520Sstevel@tonic-gate * 3.2.3 age SA byte 36530Sstevel@tonic-gate * 3.2.4 send to IP 36540Sstevel@tonic-gate */ 36550Sstevel@tonic-gate ipsec_status_t 36560Sstevel@tonic-gate ah_inbound_accelerated(mblk_t *ipsec_in, boolean_t isv4, ipsa_t *assoc, 36570Sstevel@tonic-gate uint32_t ah_offset) 36580Sstevel@tonic-gate { 36590Sstevel@tonic-gate mblk_t *mp; 36600Sstevel@tonic-gate ipha_t *ipha; 36610Sstevel@tonic-gate ah_t *ah; 36620Sstevel@tonic-gate ipsec_in_t *ii; 36630Sstevel@tonic-gate uint32_t icv_len; 36640Sstevel@tonic-gate uint32_t align_len; 36650Sstevel@tonic-gate uint32_t age_bytes; 36660Sstevel@tonic-gate ip6_t *ip6h; 36670Sstevel@tonic-gate uint8_t *in_icv; 36680Sstevel@tonic-gate mblk_t *hada_mp; 36690Sstevel@tonic-gate uint32_t next_hdr; 36700Sstevel@tonic-gate da_ipsec_t *hada; 36710Sstevel@tonic-gate kstat_named_t *counter; 36723448Sdh155122 ipsecah_stack_t *ahstack; 36733448Sdh155122 netstack_t *ns; 36743448Sdh155122 ipsec_stack_t *ipss; 36750Sstevel@tonic-gate 36760Sstevel@tonic-gate ii = (ipsec_in_t *)ipsec_in->b_rptr; 36773448Sdh155122 ns = ii->ipsec_in_ns; 36783448Sdh155122 ahstack = ns->netstack_ipsecah; 36793448Sdh155122 ipss = ns->netstack_ipsec; 36803448Sdh155122 36810Sstevel@tonic-gate mp = ipsec_in->b_cont; 36820Sstevel@tonic-gate hada_mp = ii->ipsec_in_da; 36830Sstevel@tonic-gate ASSERT(hada_mp != NULL); 36840Sstevel@tonic-gate hada = (da_ipsec_t *)hada_mp->b_rptr; 36850Sstevel@tonic-gate 36863448Sdh155122 AH_BUMP_STAT(ahstack, in_accelerated); 36873448Sdh155122 36880Sstevel@tonic-gate /* 36890Sstevel@tonic-gate * We only support one level of decapsulation in hardware, so 36900Sstevel@tonic-gate * nuke the pointer. 36910Sstevel@tonic-gate */ 36920Sstevel@tonic-gate ii->ipsec_in_da = NULL; 36930Sstevel@tonic-gate ii->ipsec_in_accelerated = B_FALSE; 36940Sstevel@tonic-gate 36950Sstevel@tonic-gate /* 36960Sstevel@tonic-gate * Extract ICV length from attributes M_CTL and sanity check 36970Sstevel@tonic-gate * its value. We allow the mblk to be smaller than da_ipsec_t 36980Sstevel@tonic-gate * for a small ICV, as long as the entire ICV fits within the mblk. 36990Sstevel@tonic-gate * Also ensures that the ICV length computed by Provider 37000Sstevel@tonic-gate * corresponds to the ICV length of the algorithm specified by the SA. 37010Sstevel@tonic-gate */ 37020Sstevel@tonic-gate icv_len = hada->da_icv_len; 37030Sstevel@tonic-gate if ((icv_len != assoc->ipsa_mac_len) || 37040Sstevel@tonic-gate (icv_len > DA_ICV_MAX_LEN) || (MBLKL(hada_mp) < 37054987Sdanmcd (sizeof (da_ipsec_t) - DA_ICV_MAX_LEN + icv_len))) { 37060Sstevel@tonic-gate ah0dbg(("ah_inbound_accelerated: " 37070Sstevel@tonic-gate "ICV len (%u) incorrect or mblk too small (%u)\n", 37080Sstevel@tonic-gate icv_len, (uint32_t)(MBLKL(hada_mp)))); 37093448Sdh155122 counter = DROPPER(ipss, ipds_ah_bad_length); 37100Sstevel@tonic-gate goto ah_in_discard; 37110Sstevel@tonic-gate } 37120Sstevel@tonic-gate ASSERT(icv_len != 0); 37130Sstevel@tonic-gate 37140Sstevel@tonic-gate /* compute the padded AH ICV len */ 37150Sstevel@tonic-gate if (isv4) { 37160Sstevel@tonic-gate ipha = (ipha_t *)mp->b_rptr; 37170Sstevel@tonic-gate align_len = (icv_len + IPV4_PADDING_ALIGN - 1) & 37180Sstevel@tonic-gate -IPV4_PADDING_ALIGN; 37190Sstevel@tonic-gate } else { 37200Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr; 37210Sstevel@tonic-gate align_len = (icv_len + IPV6_PADDING_ALIGN - 1) & 37220Sstevel@tonic-gate -IPV6_PADDING_ALIGN; 37230Sstevel@tonic-gate } 37240Sstevel@tonic-gate 37250Sstevel@tonic-gate ah = (ah_t *)(mp->b_rptr + ah_offset); 37260Sstevel@tonic-gate in_icv = (uint8_t *)ah + sizeof (ah_t); 37270Sstevel@tonic-gate 37280Sstevel@tonic-gate /* compare ICV in AH header vs ICV computed by adapter */ 37290Sstevel@tonic-gate if (bcmp(hada->da_icv, in_icv, icv_len)) { 37300Sstevel@tonic-gate int af; 37310Sstevel@tonic-gate void *addr; 37320Sstevel@tonic-gate 37330Sstevel@tonic-gate if (isv4) { 37340Sstevel@tonic-gate addr = &ipha->ipha_dst; 37350Sstevel@tonic-gate af = AF_INET; 37360Sstevel@tonic-gate } else { 37370Sstevel@tonic-gate addr = &ip6h->ip6_dst; 37380Sstevel@tonic-gate af = AF_INET6; 37390Sstevel@tonic-gate } 37400Sstevel@tonic-gate 37410Sstevel@tonic-gate /* 37420Sstevel@tonic-gate * Log the event. Don't print to the console, block 37430Sstevel@tonic-gate * potential denial-of-service attack. 37440Sstevel@tonic-gate */ 37453448Sdh155122 AH_BUMP_STAT(ahstack, bad_auth); 37460Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, SL_ERROR | SL_WARN, 37470Sstevel@tonic-gate "AH Authentication failed spi %x, dst_addr %s", 37483448Sdh155122 assoc->ipsa_spi, addr, af, ahstack->ipsecah_netstack); 37493448Sdh155122 counter = DROPPER(ipss, ipds_ah_bad_auth); 37500Sstevel@tonic-gate goto ah_in_discard; 37510Sstevel@tonic-gate } 37520Sstevel@tonic-gate 37533448Sdh155122 ah3dbg(ahstack, ("AH succeeded, checking replay\n")); 37543448Sdh155122 AH_BUMP_STAT(ahstack, good_auth); 37550Sstevel@tonic-gate 37560Sstevel@tonic-gate if (!sadb_replay_check(assoc, ah->ah_replay)) { 37570Sstevel@tonic-gate int af; 37580Sstevel@tonic-gate void *addr; 37590Sstevel@tonic-gate 37600Sstevel@tonic-gate if (isv4) { 37610Sstevel@tonic-gate addr = &ipha->ipha_dst; 37620Sstevel@tonic-gate af = AF_INET; 37630Sstevel@tonic-gate } else { 37640Sstevel@tonic-gate addr = &ip6h->ip6_dst; 37650Sstevel@tonic-gate af = AF_INET6; 37660Sstevel@tonic-gate } 37670Sstevel@tonic-gate 37680Sstevel@tonic-gate /* 37690Sstevel@tonic-gate * Log the event. As of now we print out an event. 37700Sstevel@tonic-gate * Do not print the replay failure number, or else 37710Sstevel@tonic-gate * syslog cannot collate the error messages. Printing 37720Sstevel@tonic-gate * the replay number that failed (or printing to the 37730Sstevel@tonic-gate * console) opens a denial-of-service attack. 37740Sstevel@tonic-gate */ 37753448Sdh155122 AH_BUMP_STAT(ahstack, replay_failures); 37760Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, 37770Sstevel@tonic-gate SL_ERROR | SL_WARN, 37780Sstevel@tonic-gate "Replay failed for AH spi %x, dst_addr %s", 37793448Sdh155122 assoc->ipsa_spi, addr, af, ahstack->ipsecah_netstack); 37803448Sdh155122 counter = DROPPER(ipss, ipds_ah_replay); 37810Sstevel@tonic-gate goto ah_in_discard; 37820Sstevel@tonic-gate } 37830Sstevel@tonic-gate 37840Sstevel@tonic-gate /* 37850Sstevel@tonic-gate * Remove AH header. We do this by copying everything before 37860Sstevel@tonic-gate * the AH header onto the AH header+ICV. 37870Sstevel@tonic-gate */ 37880Sstevel@tonic-gate /* overwrite AH with what was preceeding it (IP header) */ 37890Sstevel@tonic-gate next_hdr = ah->ah_nexthdr; 37900Sstevel@tonic-gate ovbcopy(mp->b_rptr, mp->b_rptr + sizeof (ah_t) + align_len, 37910Sstevel@tonic-gate ah_offset); 37920Sstevel@tonic-gate mp->b_rptr += sizeof (ah_t) + align_len; 37930Sstevel@tonic-gate if (isv4) { 37940Sstevel@tonic-gate /* adjust IP header next protocol */ 37950Sstevel@tonic-gate ipha = (ipha_t *)mp->b_rptr; 37960Sstevel@tonic-gate ipha->ipha_protocol = next_hdr; 37970Sstevel@tonic-gate 37980Sstevel@tonic-gate age_bytes = ipha->ipha_length; 37990Sstevel@tonic-gate 38000Sstevel@tonic-gate /* adjust length in IP header */ 38010Sstevel@tonic-gate ipha->ipha_length -= (sizeof (ah_t) + align_len); 38020Sstevel@tonic-gate 38030Sstevel@tonic-gate /* recalculate checksum */ 38040Sstevel@tonic-gate ipha->ipha_hdr_checksum = 0; 38050Sstevel@tonic-gate ipha->ipha_hdr_checksum = (uint16_t)ip_csum_hdr(ipha); 38060Sstevel@tonic-gate } else { 38070Sstevel@tonic-gate /* adjust IP header next protocol */ 38080Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr; 38090Sstevel@tonic-gate ip6h->ip6_nxt = next_hdr; 38100Sstevel@tonic-gate 38110Sstevel@tonic-gate age_bytes = sizeof (ip6_t) + ntohs(ip6h->ip6_plen) + 38120Sstevel@tonic-gate sizeof (ah_t); 38130Sstevel@tonic-gate 38140Sstevel@tonic-gate /* adjust length in IP header */ 38150Sstevel@tonic-gate ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) - 38160Sstevel@tonic-gate (sizeof (ah_t) + align_len)); 38170Sstevel@tonic-gate } 38180Sstevel@tonic-gate 38190Sstevel@tonic-gate /* age SA */ 38200Sstevel@tonic-gate if (!ah_age_bytes(assoc, age_bytes, B_TRUE)) { 38210Sstevel@tonic-gate /* The ipsa has hit hard expiration, LOG and AUDIT. */ 38220Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, 38230Sstevel@tonic-gate SL_ERROR | SL_WARN, 38240Sstevel@tonic-gate "AH Association 0x%x, dst %s had bytes expire.\n", 38250Sstevel@tonic-gate assoc->ipsa_spi, assoc->ipsa_dstaddr, 38263448Sdh155122 AF_INET, ahstack->ipsecah_netstack); 38273448Sdh155122 AH_BUMP_STAT(ahstack, bytes_expired); 38283448Sdh155122 counter = DROPPER(ipss, ipds_ah_bytes_expire); 38290Sstevel@tonic-gate goto ah_in_discard; 38300Sstevel@tonic-gate } 38310Sstevel@tonic-gate 38320Sstevel@tonic-gate freeb(hada_mp); 38330Sstevel@tonic-gate return (IPSEC_STATUS_SUCCESS); 38340Sstevel@tonic-gate 38350Sstevel@tonic-gate ah_in_discard: 38363448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 38370Sstevel@tonic-gate freeb(hada_mp); 38383448Sdh155122 ip_drop_packet(ipsec_in, B_TRUE, NULL, NULL, counter, 38393448Sdh155122 &ahstack->ah_dropper); 38400Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 38410Sstevel@tonic-gate } 38420Sstevel@tonic-gate 38430Sstevel@tonic-gate /* 38440Sstevel@tonic-gate * ah_outbound_accelerated_v4: 38450Sstevel@tonic-gate * Called from ah_outbound_v4() and once it is determined that the 38460Sstevel@tonic-gate * packet is elligible for hardware acceleration. 38470Sstevel@tonic-gate * 38480Sstevel@tonic-gate * We proceed as follows: 38490Sstevel@tonic-gate * 1. allocate and initialize attributes mblk 38500Sstevel@tonic-gate * 2. mark IPSEC_OUT to indicate that pkt is accelerated 38510Sstevel@tonic-gate * 3. insert AH header 38520Sstevel@tonic-gate */ 38530Sstevel@tonic-gate static ipsec_status_t 38540Sstevel@tonic-gate ah_outbound_accelerated_v4(mblk_t *ipsec_mp, ipsa_t *assoc) 38550Sstevel@tonic-gate { 38560Sstevel@tonic-gate mblk_t *mp, *new_mp; 38570Sstevel@tonic-gate ipsec_out_t *oi; 38580Sstevel@tonic-gate uint_t ah_data_sz; /* ICV length, algorithm dependent */ 38590Sstevel@tonic-gate uint_t ah_align_sz; /* ICV length + padding */ 38600Sstevel@tonic-gate uint32_t v_hlen_tos_len; /* from original IP header */ 38610Sstevel@tonic-gate ipha_t *oipha; /* original IP header */ 38620Sstevel@tonic-gate ipha_t *nipha; /* new IP header */ 38630Sstevel@tonic-gate uint_t option_length = 0; 38640Sstevel@tonic-gate uint_t new_hdr_len; /* new header length */ 38650Sstevel@tonic-gate uint_t iphdr_length; 38660Sstevel@tonic-gate ah_t *ah_hdr; /* ptr to AH header */ 38673448Sdh155122 netstack_t *ns; 38683448Sdh155122 ipsec_stack_t *ipss; 38693448Sdh155122 ipsecah_stack_t *ahstack; 38700Sstevel@tonic-gate 38710Sstevel@tonic-gate oi = (ipsec_out_t *)ipsec_mp->b_rptr; 38723448Sdh155122 ns = oi->ipsec_out_ns; 38733448Sdh155122 ipss = ns->netstack_ipsec; 38743448Sdh155122 ahstack = ns->netstack_ipsecah; 38753448Sdh155122 38760Sstevel@tonic-gate mp = ipsec_mp->b_cont; 38770Sstevel@tonic-gate 38783448Sdh155122 AH_BUMP_STAT(ahstack, out_accelerated); 38793448Sdh155122 38800Sstevel@tonic-gate oipha = (ipha_t *)mp->b_rptr; 38810Sstevel@tonic-gate v_hlen_tos_len = ((uint32_t *)oipha)[0]; 38820Sstevel@tonic-gate 38830Sstevel@tonic-gate /* mark packet as being accelerated in IPSEC_OUT */ 38840Sstevel@tonic-gate ASSERT(oi->ipsec_out_accelerated == B_FALSE); 38850Sstevel@tonic-gate oi->ipsec_out_accelerated = B_TRUE; 38860Sstevel@tonic-gate 38870Sstevel@tonic-gate /* calculate authentication data length, i.e. ICV + padding */ 38880Sstevel@tonic-gate ah_data_sz = assoc->ipsa_mac_len; 38890Sstevel@tonic-gate ah_align_sz = (ah_data_sz + IPV4_PADDING_ALIGN - 1) & 38900Sstevel@tonic-gate -IPV4_PADDING_ALIGN; 38910Sstevel@tonic-gate 38920Sstevel@tonic-gate /* 38930Sstevel@tonic-gate * Insert pseudo header: 38940Sstevel@tonic-gate * IPSEC_INFO -> [IP, ULP] => IPSEC_INFO -> [IP, AH, ICV] -> ULP 38950Sstevel@tonic-gate */ 38960Sstevel@tonic-gate 38970Sstevel@tonic-gate /* IP + AH + authentication + padding data length */ 38980Sstevel@tonic-gate new_hdr_len = IP_SIMPLE_HDR_LENGTH + sizeof (ah_t) + ah_align_sz; 38990Sstevel@tonic-gate if (V_HLEN != IP_SIMPLE_HDR_VERSION) { 39000Sstevel@tonic-gate option_length = oipha->ipha_version_and_hdr_length - 39010Sstevel@tonic-gate (uint8_t)((IP_VERSION << 4) + 39020Sstevel@tonic-gate IP_SIMPLE_HDR_LENGTH_IN_WORDS); 39030Sstevel@tonic-gate option_length <<= 2; 39040Sstevel@tonic-gate new_hdr_len += option_length; 39050Sstevel@tonic-gate } 39060Sstevel@tonic-gate 39070Sstevel@tonic-gate /* allocate pseudo-header mblk */ 39080Sstevel@tonic-gate if ((new_mp = allocb(new_hdr_len, BPRI_HI)) == NULL) { 39090Sstevel@tonic-gate /* IPsec kstats: bump bean counter here */ 39100Sstevel@tonic-gate ip_drop_packet(ipsec_mp, B_FALSE, NULL, NULL, 39113448Sdh155122 DROPPER(ipss, ipds_ah_nomem), 39123448Sdh155122 &ahstack->ah_dropper); 39130Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 39140Sstevel@tonic-gate } 39150Sstevel@tonic-gate 39160Sstevel@tonic-gate new_mp->b_cont = mp; 39170Sstevel@tonic-gate ipsec_mp->b_cont = new_mp; 39180Sstevel@tonic-gate new_mp->b_wptr += new_hdr_len; 39190Sstevel@tonic-gate 39200Sstevel@tonic-gate /* copy original IP header to new header */ 39210Sstevel@tonic-gate bcopy(mp->b_rptr, new_mp->b_rptr, IP_SIMPLE_HDR_LENGTH + 39220Sstevel@tonic-gate option_length); 39230Sstevel@tonic-gate 39240Sstevel@tonic-gate /* update IP header */ 39250Sstevel@tonic-gate nipha = (ipha_t *)new_mp->b_rptr; 39260Sstevel@tonic-gate nipha->ipha_protocol = IPPROTO_AH; 39270Sstevel@tonic-gate iphdr_length = ntohs(nipha->ipha_length); 39280Sstevel@tonic-gate iphdr_length += sizeof (ah_t) + ah_align_sz; 39290Sstevel@tonic-gate nipha->ipha_length = htons(iphdr_length); 39300Sstevel@tonic-gate nipha->ipha_hdr_checksum = 0; 39310Sstevel@tonic-gate nipha->ipha_hdr_checksum = (uint16_t)ip_csum_hdr(nipha); 39320Sstevel@tonic-gate 39330Sstevel@tonic-gate /* skip original IP header in mp */ 39340Sstevel@tonic-gate mp->b_rptr += IP_SIMPLE_HDR_LENGTH + option_length; 39350Sstevel@tonic-gate 39360Sstevel@tonic-gate /* initialize AH header */ 39370Sstevel@tonic-gate ah_hdr = (ah_t *)(new_mp->b_rptr + IP_SIMPLE_HDR_LENGTH + 39380Sstevel@tonic-gate option_length); 39390Sstevel@tonic-gate ah_hdr->ah_nexthdr = oipha->ipha_protocol; 39403448Sdh155122 if (!ah_finish_up(ah_hdr, NULL, assoc, ah_data_sz, ah_align_sz, 39413448Sdh155122 ahstack)) { 39420Sstevel@tonic-gate /* Only way this fails is if outbound replay counter wraps. */ 39430Sstevel@tonic-gate ip_drop_packet(ipsec_mp, B_FALSE, NULL, NULL, 39443448Sdh155122 DROPPER(ipss, ipds_ah_replay), 39453448Sdh155122 &ahstack->ah_dropper); 39460Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 39470Sstevel@tonic-gate } 39480Sstevel@tonic-gate 39490Sstevel@tonic-gate return (IPSEC_STATUS_SUCCESS); 39500Sstevel@tonic-gate } 39510Sstevel@tonic-gate 39520Sstevel@tonic-gate /* 39530Sstevel@tonic-gate * ah_outbound_accelerated_v6: 39540Sstevel@tonic-gate * 39550Sstevel@tonic-gate * Called from ah_outbound_v6() once it is determined that the packet 39560Sstevel@tonic-gate * is eligible for hardware acceleration. 39570Sstevel@tonic-gate * 39580Sstevel@tonic-gate * We proceed as follows: 39590Sstevel@tonic-gate * 1. allocate and initialize attributes mblk 39600Sstevel@tonic-gate * 2. mark IPSEC_OUT to indicate that pkt is accelerated 39610Sstevel@tonic-gate * 3. insert AH header 39620Sstevel@tonic-gate */ 39630Sstevel@tonic-gate static ipsec_status_t 39640Sstevel@tonic-gate ah_outbound_accelerated_v6(mblk_t *ipsec_mp, ipsa_t *assoc) 39650Sstevel@tonic-gate { 39660Sstevel@tonic-gate mblk_t *mp, *phdr_mp; 39670Sstevel@tonic-gate ipsec_out_t *oi; 39680Sstevel@tonic-gate uint_t ah_data_sz; /* ICV length, algorithm dependent */ 39690Sstevel@tonic-gate uint_t ah_align_sz; /* ICV length + padding */ 39700Sstevel@tonic-gate ip6_t *oip6h; /* original IP header */ 39710Sstevel@tonic-gate ip6_t *ip6h; /* new IP header */ 39720Sstevel@tonic-gate uint_t option_length = 0; 39730Sstevel@tonic-gate uint_t hdr_size; 39740Sstevel@tonic-gate uint_t ah_offset; 39750Sstevel@tonic-gate ah_t *ah_hdr; /* ptr to AH header */ 39763448Sdh155122 netstack_t *ns; 39773448Sdh155122 ipsec_stack_t *ipss; 39783448Sdh155122 ipsecah_stack_t *ahstack; 39790Sstevel@tonic-gate 39800Sstevel@tonic-gate oi = (ipsec_out_t *)ipsec_mp->b_rptr; 39813448Sdh155122 ns = oi->ipsec_out_ns; 39823448Sdh155122 ipss = ns->netstack_ipsec; 39833448Sdh155122 ahstack = ns->netstack_ipsecah; 39843448Sdh155122 39850Sstevel@tonic-gate mp = ipsec_mp->b_cont; 39860Sstevel@tonic-gate 39873448Sdh155122 AH_BUMP_STAT(ahstack, out_accelerated); 39883448Sdh155122 39890Sstevel@tonic-gate oip6h = (ip6_t *)mp->b_rptr; 39900Sstevel@tonic-gate 39910Sstevel@tonic-gate /* mark packet as being accelerated in IPSEC_OUT */ 39920Sstevel@tonic-gate ASSERT(oi->ipsec_out_accelerated == B_FALSE); 39930Sstevel@tonic-gate oi->ipsec_out_accelerated = B_TRUE; 39940Sstevel@tonic-gate 39950Sstevel@tonic-gate /* calculate authentication data length, i.e. ICV + padding */ 39960Sstevel@tonic-gate ah_data_sz = assoc->ipsa_mac_len; 39970Sstevel@tonic-gate ah_align_sz = (ah_data_sz + IPV4_PADDING_ALIGN - 1) & 39980Sstevel@tonic-gate -IPV4_PADDING_ALIGN; 39990Sstevel@tonic-gate 40000Sstevel@tonic-gate ASSERT(ah_align_sz >= ah_data_sz); 40010Sstevel@tonic-gate 40020Sstevel@tonic-gate hdr_size = ipsec_ah_get_hdr_size_v6(mp, B_FALSE); 40030Sstevel@tonic-gate option_length = hdr_size - IPV6_HDR_LEN; 40040Sstevel@tonic-gate 40050Sstevel@tonic-gate /* This was not included in ipsec_ah_get_hdr_size_v6() */ 40060Sstevel@tonic-gate hdr_size += (sizeof (ah_t) + ah_align_sz); 40070Sstevel@tonic-gate 40080Sstevel@tonic-gate if ((phdr_mp = allocb(hdr_size, BPRI_HI)) == NULL) { 40093448Sdh155122 ip_drop_packet(ipsec_mp, B_FALSE, NULL, NULL, 40103448Sdh155122 DROPPER(ipss, ipds_ah_nomem), 40113448Sdh155122 &ahstack->ah_dropper); 40120Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 40130Sstevel@tonic-gate } 40140Sstevel@tonic-gate phdr_mp->b_wptr += hdr_size; 40150Sstevel@tonic-gate 40160Sstevel@tonic-gate /* 40170Sstevel@tonic-gate * Form the basic IP header first. We always assign every bit 40180Sstevel@tonic-gate * of the v6 basic header, so a separate bzero is unneeded. 40190Sstevel@tonic-gate */ 40200Sstevel@tonic-gate ip6h = (ip6_t *)phdr_mp->b_rptr; 40210Sstevel@tonic-gate ip6h->ip6_vcf = oip6h->ip6_vcf; 40220Sstevel@tonic-gate ip6h->ip6_hlim = oip6h->ip6_hlim; 40230Sstevel@tonic-gate ip6h->ip6_src = oip6h->ip6_src; 40240Sstevel@tonic-gate ip6h->ip6_dst = oip6h->ip6_dst; 40250Sstevel@tonic-gate /* 40260Sstevel@tonic-gate * Include the size of AH and authentication data. 40270Sstevel@tonic-gate * This is how our recipient would compute the 40280Sstevel@tonic-gate * authentication data. Look at what we do in the 40290Sstevel@tonic-gate * inbound case below. 40300Sstevel@tonic-gate */ 40310Sstevel@tonic-gate ip6h->ip6_plen = htons(ntohs(oip6h->ip6_plen) + sizeof (ah_t) + 40320Sstevel@tonic-gate ah_align_sz); 40330Sstevel@tonic-gate 40340Sstevel@tonic-gate /* 40350Sstevel@tonic-gate * Insert pseudo header: 40360Sstevel@tonic-gate * IPSEC_INFO -> [IP6, LLH, ULP] => 40370Sstevel@tonic-gate * IPSEC_INFO -> [IP, LLH, AH, ICV] -> ULP 40380Sstevel@tonic-gate */ 40390Sstevel@tonic-gate 40400Sstevel@tonic-gate if (option_length == 0) { 40410Sstevel@tonic-gate /* Form the AH header */ 40420Sstevel@tonic-gate ip6h->ip6_nxt = IPPROTO_AH; 40430Sstevel@tonic-gate ((ah_t *)(ip6h + 1))->ah_nexthdr = oip6h->ip6_nxt; 40440Sstevel@tonic-gate ah_offset = IPV6_HDR_LEN; 40450Sstevel@tonic-gate } else { 40460Sstevel@tonic-gate ip6h->ip6_nxt = oip6h->ip6_nxt; 40470Sstevel@tonic-gate /* option_length does not include the AH header's size */ 40480Sstevel@tonic-gate ah_offset = ah_fix_phdr_v6(ip6h, oip6h, B_TRUE, B_FALSE); 40490Sstevel@tonic-gate if (ah_offset == 0) { 40500Sstevel@tonic-gate freemsg(phdr_mp); 40510Sstevel@tonic-gate ip_drop_packet(ipsec_mp, B_FALSE, NULL, NULL, 40523448Sdh155122 DROPPER(ipss, ipds_ah_bad_v6_hdrs), 40533448Sdh155122 &ahstack->ah_dropper); 40540Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 40550Sstevel@tonic-gate } 40560Sstevel@tonic-gate } 40570Sstevel@tonic-gate 40580Sstevel@tonic-gate phdr_mp->b_cont = mp; 40590Sstevel@tonic-gate ipsec_mp->b_cont = phdr_mp; 40600Sstevel@tonic-gate 40610Sstevel@tonic-gate /* skip original IP header in mp */ 40620Sstevel@tonic-gate mp->b_rptr += IPV6_HDR_LEN + option_length; 40630Sstevel@tonic-gate 40640Sstevel@tonic-gate /* initialize AH header */ 40650Sstevel@tonic-gate ah_hdr = (ah_t *)(phdr_mp->b_rptr + IPV6_HDR_LEN + option_length); 40660Sstevel@tonic-gate ah_hdr->ah_nexthdr = oip6h->ip6_nxt; 40670Sstevel@tonic-gate 40680Sstevel@tonic-gate if (!ah_finish_up(((ah_t *)((uint8_t *)ip6h + ah_offset)), NULL, 40693448Sdh155122 assoc, ah_data_sz, ah_align_sz, ahstack)) { 40700Sstevel@tonic-gate /* Only way this fails is if outbound replay counter wraps. */ 40710Sstevel@tonic-gate ip_drop_packet(ipsec_mp, B_FALSE, NULL, NULL, 40723448Sdh155122 DROPPER(ipss, ipds_ah_replay), 40733448Sdh155122 &ahstack->ah_dropper); 40740Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 40750Sstevel@tonic-gate } 40760Sstevel@tonic-gate 40770Sstevel@tonic-gate return (IPSEC_STATUS_SUCCESS); 40780Sstevel@tonic-gate } 40790Sstevel@tonic-gate 40800Sstevel@tonic-gate /* 40810Sstevel@tonic-gate * Invoked after processing of an inbound packet by the 40820Sstevel@tonic-gate * kernel crypto framework. Called by ah_submit_req() for a sync request, 40830Sstevel@tonic-gate * or by the kcf callback for an async request. 40840Sstevel@tonic-gate * Returns IPSEC_STATUS_SUCCESS on success, IPSEC_STATUS_FAILED on failure. 40850Sstevel@tonic-gate * On failure, the mblk chain ipsec_in is freed by this function. 40860Sstevel@tonic-gate */ 40870Sstevel@tonic-gate static ipsec_status_t 40880Sstevel@tonic-gate ah_auth_in_done(mblk_t *ipsec_in) 40890Sstevel@tonic-gate { 40900Sstevel@tonic-gate mblk_t *phdr_mp; 40910Sstevel@tonic-gate ipha_t *ipha; 40920Sstevel@tonic-gate uint_t ah_offset = 0; 40930Sstevel@tonic-gate mblk_t *mp; 40943192Sdanmcd int align_len, newpos; 40950Sstevel@tonic-gate ah_t *ah; 40960Sstevel@tonic-gate uint32_t length; 40973192Sdanmcd uint32_t *dest32; 40983192Sdanmcd uint8_t *dest; 40990Sstevel@tonic-gate ipsec_in_t *ii; 41000Sstevel@tonic-gate boolean_t isv4; 41010Sstevel@tonic-gate ip6_t *ip6h; 41020Sstevel@tonic-gate uint_t icv_len; 41030Sstevel@tonic-gate ipsa_t *assoc; 41040Sstevel@tonic-gate kstat_named_t *counter; 41053448Sdh155122 netstack_t *ns; 41063448Sdh155122 ipsecah_stack_t *ahstack; 41073448Sdh155122 ipsec_stack_t *ipss; 41080Sstevel@tonic-gate 41090Sstevel@tonic-gate ii = (ipsec_in_t *)ipsec_in->b_rptr; 41103448Sdh155122 ns = ii->ipsec_in_ns; 41113448Sdh155122 ahstack = ns->netstack_ipsecah; 41123448Sdh155122 ipss = ns->netstack_ipsec; 41133448Sdh155122 41140Sstevel@tonic-gate isv4 = ii->ipsec_in_v4; 41150Sstevel@tonic-gate assoc = ii->ipsec_in_ah_sa; 41160Sstevel@tonic-gate icv_len = (uint_t)ii->ipsec_in_crypto_mac.cd_raw.iov_len; 41170Sstevel@tonic-gate 41180Sstevel@tonic-gate phdr_mp = ipsec_in->b_cont; 41190Sstevel@tonic-gate if (phdr_mp == NULL) { 41203448Sdh155122 ip_drop_packet(ipsec_in, B_TRUE, NULL, NULL, 41213448Sdh155122 DROPPER(ipss, ipds_ah_nomem), 41223448Sdh155122 &ahstack->ah_dropper); 41230Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 41240Sstevel@tonic-gate } 41250Sstevel@tonic-gate 41260Sstevel@tonic-gate mp = phdr_mp->b_cont; 41270Sstevel@tonic-gate if (mp == NULL) { 41283448Sdh155122 ip_drop_packet(ipsec_in, B_TRUE, NULL, NULL, 41293448Sdh155122 DROPPER(ipss, ipds_ah_nomem), 41303448Sdh155122 &ahstack->ah_dropper); 41310Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 41320Sstevel@tonic-gate } 41330Sstevel@tonic-gate mp->b_rptr -= ii->ipsec_in_skip_len; 41340Sstevel@tonic-gate 41354987Sdanmcd ah_set_usetime(assoc, B_TRUE); 41364987Sdanmcd 41370Sstevel@tonic-gate if (isv4) { 41380Sstevel@tonic-gate ipha = (ipha_t *)mp->b_rptr; 41390Sstevel@tonic-gate ah_offset = ipha->ipha_version_and_hdr_length - 41400Sstevel@tonic-gate (uint8_t)((IP_VERSION << 4)); 41410Sstevel@tonic-gate ah_offset <<= 2; 41420Sstevel@tonic-gate align_len = P2ALIGN(icv_len + IPV4_PADDING_ALIGN - 1, 41430Sstevel@tonic-gate IPV4_PADDING_ALIGN); 41440Sstevel@tonic-gate } else { 41450Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr; 41460Sstevel@tonic-gate ah_offset = ipsec_ah_get_hdr_size_v6(mp, B_TRUE); 41470Sstevel@tonic-gate ASSERT((mp->b_wptr - mp->b_rptr) >= ah_offset); 41480Sstevel@tonic-gate align_len = P2ALIGN(icv_len + IPV6_PADDING_ALIGN - 1, 41490Sstevel@tonic-gate IPV6_PADDING_ALIGN); 41500Sstevel@tonic-gate } 41510Sstevel@tonic-gate 41520Sstevel@tonic-gate ah = (ah_t *)(mp->b_rptr + ah_offset); 41533192Sdanmcd newpos = sizeof (ah_t) + align_len; 41540Sstevel@tonic-gate 41550Sstevel@tonic-gate /* 41560Sstevel@tonic-gate * We get here only when authentication passed. 41570Sstevel@tonic-gate */ 41580Sstevel@tonic-gate 41593448Sdh155122 ah3dbg(ahstack, ("AH succeeded, checking replay\n")); 41603448Sdh155122 AH_BUMP_STAT(ahstack, good_auth); 41610Sstevel@tonic-gate 41620Sstevel@tonic-gate if (!sadb_replay_check(assoc, ah->ah_replay)) { 41630Sstevel@tonic-gate int af; 41640Sstevel@tonic-gate void *addr; 41650Sstevel@tonic-gate 41660Sstevel@tonic-gate if (isv4) { 41670Sstevel@tonic-gate addr = &ipha->ipha_dst; 41680Sstevel@tonic-gate af = AF_INET; 41690Sstevel@tonic-gate } else { 41700Sstevel@tonic-gate addr = &ip6h->ip6_dst; 41710Sstevel@tonic-gate af = AF_INET6; 41720Sstevel@tonic-gate } 41730Sstevel@tonic-gate 41740Sstevel@tonic-gate /* 41750Sstevel@tonic-gate * Log the event. As of now we print out an event. 41760Sstevel@tonic-gate * Do not print the replay failure number, or else 41770Sstevel@tonic-gate * syslog cannot collate the error messages. Printing 41780Sstevel@tonic-gate * the replay number that failed (or printing to the 41790Sstevel@tonic-gate * console) opens a denial-of-service attack. 41800Sstevel@tonic-gate */ 41813448Sdh155122 AH_BUMP_STAT(ahstack, replay_failures); 41820Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, 41830Sstevel@tonic-gate SL_ERROR | SL_WARN, 41840Sstevel@tonic-gate "Replay failed for AH spi %x, dst_addr %s", 41853448Sdh155122 assoc->ipsa_spi, addr, af, ahstack->ipsecah_netstack); 41863448Sdh155122 counter = DROPPER(ipss, ipds_ah_replay); 41870Sstevel@tonic-gate goto ah_in_discard; 41880Sstevel@tonic-gate } 41890Sstevel@tonic-gate 41900Sstevel@tonic-gate /* 41910Sstevel@tonic-gate * We need to remove the AH header from the original 41923192Sdanmcd * datagram. Best way to do this is to move the pre-AH headers 41933192Sdanmcd * forward in the (relatively simple) IPv4 case. In IPv6, it's 41943192Sdanmcd * a bit more complicated because of IPv6's next-header chaining, 41953192Sdanmcd * but it's doable. 41960Sstevel@tonic-gate */ 41970Sstevel@tonic-gate if (isv4) { 41980Sstevel@tonic-gate /* 41990Sstevel@tonic-gate * Assign the right protocol, adjust the length as we 42000Sstevel@tonic-gate * are removing the AH header and adjust the checksum to 42010Sstevel@tonic-gate * account for the protocol and length. 42020Sstevel@tonic-gate */ 42033192Sdanmcd length = ntohs(ipha->ipha_length); 42040Sstevel@tonic-gate if (!ah_age_bytes(assoc, length, B_TRUE)) { 42050Sstevel@tonic-gate /* The ipsa has hit hard expiration, LOG and AUDIT. */ 42060Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, 42070Sstevel@tonic-gate SL_ERROR | SL_WARN, 42080Sstevel@tonic-gate "AH Association 0x%x, dst %s had bytes expire.\n", 42090Sstevel@tonic-gate assoc->ipsa_spi, assoc->ipsa_dstaddr, 42103448Sdh155122 AF_INET, ahstack->ipsecah_netstack); 42113448Sdh155122 AH_BUMP_STAT(ahstack, bytes_expired); 42123448Sdh155122 counter = DROPPER(ipss, ipds_ah_bytes_expire); 42130Sstevel@tonic-gate goto ah_in_discard; 42140Sstevel@tonic-gate } 42153192Sdanmcd ipha->ipha_protocol = ah->ah_nexthdr; 42163192Sdanmcd length -= newpos; 42173192Sdanmcd 42183192Sdanmcd ipha->ipha_length = htons((uint16_t)length); 42193192Sdanmcd ipha->ipha_hdr_checksum = 0; 42203192Sdanmcd ipha->ipha_hdr_checksum = (uint16_t)ip_csum_hdr(ipha); 42210Sstevel@tonic-gate } else { 42220Sstevel@tonic-gate uchar_t *whereptr; 42230Sstevel@tonic-gate int hdrlen; 42240Sstevel@tonic-gate uint8_t *nexthdr; 42250Sstevel@tonic-gate ip6_hbh_t *hbhhdr; 42260Sstevel@tonic-gate ip6_dest_t *dsthdr; 42270Sstevel@tonic-gate ip6_rthdr0_t *rthdr; 42280Sstevel@tonic-gate 42290Sstevel@tonic-gate /* 42300Sstevel@tonic-gate * Make phdr_mp hold until the AH header and make 42310Sstevel@tonic-gate * mp hold everything past AH header. 42320Sstevel@tonic-gate */ 42333192Sdanmcd length = ntohs(ip6h->ip6_plen); 42340Sstevel@tonic-gate if (!ah_age_bytes(assoc, length + sizeof (ip6_t), B_TRUE)) { 42350Sstevel@tonic-gate /* The ipsa has hit hard expiration, LOG and AUDIT. */ 42360Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, 42370Sstevel@tonic-gate SL_ERROR | SL_WARN, 42380Sstevel@tonic-gate "AH Association 0x%x, dst %s had bytes " 42390Sstevel@tonic-gate "expire.\n", assoc->ipsa_spi, &ip6h->ip6_dst, 42403448Sdh155122 AF_INET6, ahstack->ipsecah_netstack); 42413448Sdh155122 AH_BUMP_STAT(ahstack, bytes_expired); 42423448Sdh155122 counter = DROPPER(ipss, ipds_ah_bytes_expire); 42430Sstevel@tonic-gate goto ah_in_discard; 42440Sstevel@tonic-gate } 42450Sstevel@tonic-gate 42460Sstevel@tonic-gate /* 42470Sstevel@tonic-gate * Update the next header field of the header preceding 42480Sstevel@tonic-gate * AH with the next header field of AH. Start with the 42490Sstevel@tonic-gate * IPv6 header and proceed with the extension headers 42500Sstevel@tonic-gate * until we find what we're looking for. 42510Sstevel@tonic-gate */ 42523192Sdanmcd nexthdr = &ip6h->ip6_nxt; 42533192Sdanmcd whereptr = (uchar_t *)ip6h; 42540Sstevel@tonic-gate hdrlen = sizeof (ip6_t); 42550Sstevel@tonic-gate 42560Sstevel@tonic-gate while (*nexthdr != IPPROTO_AH) { 42570Sstevel@tonic-gate whereptr += hdrlen; 42580Sstevel@tonic-gate /* Assume IP has already stripped it */ 42590Sstevel@tonic-gate ASSERT(*nexthdr != IPPROTO_FRAGMENT && 42600Sstevel@tonic-gate *nexthdr != IPPROTO_RAW); 42610Sstevel@tonic-gate switch (*nexthdr) { 42620Sstevel@tonic-gate case IPPROTO_HOPOPTS: 42630Sstevel@tonic-gate hbhhdr = (ip6_hbh_t *)whereptr; 42640Sstevel@tonic-gate nexthdr = &hbhhdr->ip6h_nxt; 42650Sstevel@tonic-gate hdrlen = 8 * (hbhhdr->ip6h_len + 1); 42660Sstevel@tonic-gate break; 42670Sstevel@tonic-gate case IPPROTO_DSTOPTS: 42680Sstevel@tonic-gate dsthdr = (ip6_dest_t *)whereptr; 42690Sstevel@tonic-gate nexthdr = &dsthdr->ip6d_nxt; 42700Sstevel@tonic-gate hdrlen = 8 * (dsthdr->ip6d_len + 1); 42710Sstevel@tonic-gate break; 42720Sstevel@tonic-gate case IPPROTO_ROUTING: 42730Sstevel@tonic-gate rthdr = (ip6_rthdr0_t *)whereptr; 42740Sstevel@tonic-gate nexthdr = &rthdr->ip6r0_nxt; 42750Sstevel@tonic-gate hdrlen = 8 * (rthdr->ip6r0_len + 1); 42760Sstevel@tonic-gate break; 42770Sstevel@tonic-gate } 42780Sstevel@tonic-gate } 42790Sstevel@tonic-gate *nexthdr = ah->ah_nexthdr; 42803192Sdanmcd length -= newpos; 42813192Sdanmcd ip6h->ip6_plen = htons((uint16_t)length); 42820Sstevel@tonic-gate } 42830Sstevel@tonic-gate 42843192Sdanmcd /* Now that we've fixed the IP header, move it forward. */ 42853192Sdanmcd mp->b_rptr += newpos; 42863192Sdanmcd if (IS_P2ALIGNED(mp->b_rptr, sizeof (uint32_t))) { 42873192Sdanmcd dest32 = (uint32_t *)(mp->b_rptr + ah_offset); 42883192Sdanmcd while (--dest32 >= (uint32_t *)mp->b_rptr) 42893192Sdanmcd *dest32 = *(dest32 - (newpos >> 2)); 42903192Sdanmcd } else { 42913192Sdanmcd dest = mp->b_rptr + ah_offset; 42923192Sdanmcd while (--dest >= mp->b_rptr) 42933192Sdanmcd *dest = *(dest - newpos); 42943192Sdanmcd } 4295*10934Ssommerfeld@sun.com ipsec_in->b_cont = mp; 4296*10934Ssommerfeld@sun.com phdr_mp->b_cont = NULL; 42978778SErik.Nordmark@Sun.COM /* 42989710SKen.Powell@Sun.COM * If a db_credp exists in phdr_mp, it must also exist in mp. 42998778SErik.Nordmark@Sun.COM */ 43009710SKen.Powell@Sun.COM ASSERT(DB_CRED(phdr_mp) == NULL || 43019710SKen.Powell@Sun.COM msg_getcred(mp, NULL) != NULL); 43023192Sdanmcd freeb(phdr_mp); 4303*10934Ssommerfeld@sun.com 4304*10934Ssommerfeld@sun.com /* 4305*10934Ssommerfeld@sun.com * If SA is labelled, use its label, else inherit the label 4306*10934Ssommerfeld@sun.com */ 4307*10934Ssommerfeld@sun.com if (is_system_labeled() && (assoc->ipsa_cred != NULL)) { 4308*10934Ssommerfeld@sun.com mblk_setcred(mp, assoc->ipsa_cred, NOPID); 4309*10934Ssommerfeld@sun.com } 4310*10934Ssommerfeld@sun.com 43117885Sdanmcd@sun.com if (assoc->ipsa_state == IPSA_STATE_IDLE) { 43127885Sdanmcd@sun.com /* 43137885Sdanmcd@sun.com * Cluster buffering case. Tell caller that we're 43147885Sdanmcd@sun.com * handling the packet. 43157885Sdanmcd@sun.com */ 43167885Sdanmcd@sun.com sadb_buf_pkt(assoc, ipsec_in, ns); 43177885Sdanmcd@sun.com return (IPSEC_STATUS_PENDING); 43187885Sdanmcd@sun.com } 4319*10934Ssommerfeld@sun.com 43200Sstevel@tonic-gate return (IPSEC_STATUS_SUCCESS); 43210Sstevel@tonic-gate 43220Sstevel@tonic-gate ah_in_discard: 43233448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 43243448Sdh155122 ip_drop_packet(ipsec_in, B_TRUE, NULL, NULL, counter, 43253448Sdh155122 &ahstack->ah_dropper); 43260Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 43270Sstevel@tonic-gate } 43280Sstevel@tonic-gate 43290Sstevel@tonic-gate /* 43300Sstevel@tonic-gate * Invoked after processing of an outbound packet by the 43310Sstevel@tonic-gate * kernel crypto framework, either by ah_submit_req() for a request 43320Sstevel@tonic-gate * executed syncrhonously, or by the KEF callback for a request 43330Sstevel@tonic-gate * executed asynchronously. 43340Sstevel@tonic-gate */ 43350Sstevel@tonic-gate static ipsec_status_t 43360Sstevel@tonic-gate ah_auth_out_done(mblk_t *ipsec_out) 43370Sstevel@tonic-gate { 43380Sstevel@tonic-gate mblk_t *phdr_mp; 43390Sstevel@tonic-gate mblk_t *mp; 43400Sstevel@tonic-gate int align_len; 43410Sstevel@tonic-gate uint32_t hdrs_length; 43420Sstevel@tonic-gate uchar_t *ptr; 43430Sstevel@tonic-gate uint32_t length; 43440Sstevel@tonic-gate boolean_t isv4; 43450Sstevel@tonic-gate ipsec_out_t *io; 43460Sstevel@tonic-gate size_t icv_len; 43473448Sdh155122 netstack_t *ns; 43483448Sdh155122 ipsec_stack_t *ipss; 43493448Sdh155122 ipsecah_stack_t *ahstack; 43500Sstevel@tonic-gate 43510Sstevel@tonic-gate io = (ipsec_out_t *)ipsec_out->b_rptr; 43523448Sdh155122 ns = io->ipsec_out_ns; 43533448Sdh155122 ipss = ns->netstack_ipsec; 43543448Sdh155122 ahstack = ns->netstack_ipsecah; 43553448Sdh155122 43560Sstevel@tonic-gate isv4 = io->ipsec_out_v4; 43570Sstevel@tonic-gate icv_len = io->ipsec_out_crypto_mac.cd_raw.iov_len; 43580Sstevel@tonic-gate 43590Sstevel@tonic-gate phdr_mp = ipsec_out->b_cont; 43600Sstevel@tonic-gate if (phdr_mp == NULL) { 43610Sstevel@tonic-gate ip_drop_packet(ipsec_out, B_FALSE, NULL, NULL, 43623448Sdh155122 DROPPER(ipss, ipds_ah_nomem), 43633448Sdh155122 &ahstack->ah_dropper); 43640Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 43650Sstevel@tonic-gate } 43660Sstevel@tonic-gate 43670Sstevel@tonic-gate mp = phdr_mp->b_cont; 43680Sstevel@tonic-gate if (mp == NULL) { 43690Sstevel@tonic-gate ip_drop_packet(ipsec_out, B_FALSE, NULL, NULL, 43703448Sdh155122 DROPPER(ipss, ipds_ah_nomem), 43713448Sdh155122 &ahstack->ah_dropper); 43720Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 43730Sstevel@tonic-gate } 43740Sstevel@tonic-gate mp->b_rptr -= io->ipsec_out_skip_len; 43750Sstevel@tonic-gate 43764987Sdanmcd ASSERT(io->ipsec_out_ah_sa != NULL); 43774987Sdanmcd ah_set_usetime(io->ipsec_out_ah_sa, B_FALSE); 43784987Sdanmcd 43790Sstevel@tonic-gate if (isv4) { 43800Sstevel@tonic-gate ipha_t *ipha; 43810Sstevel@tonic-gate ipha_t *nipha; 43820Sstevel@tonic-gate 43830Sstevel@tonic-gate ipha = (ipha_t *)mp->b_rptr; 43840Sstevel@tonic-gate hdrs_length = ipha->ipha_version_and_hdr_length - 43850Sstevel@tonic-gate (uint8_t)((IP_VERSION << 4)); 43860Sstevel@tonic-gate hdrs_length <<= 2; 43870Sstevel@tonic-gate align_len = P2ALIGN(icv_len + IPV4_PADDING_ALIGN - 1, 43880Sstevel@tonic-gate IPV4_PADDING_ALIGN); 43890Sstevel@tonic-gate /* 43900Sstevel@tonic-gate * phdr_mp must have the right amount of space for the 43910Sstevel@tonic-gate * combined IP and AH header. Copy the IP header and 43920Sstevel@tonic-gate * the ack_data onto AH. Note that the AH header was 43930Sstevel@tonic-gate * already formed before the ICV calculation and hence 43940Sstevel@tonic-gate * you don't have to copy it here. 43950Sstevel@tonic-gate */ 43960Sstevel@tonic-gate bcopy(mp->b_rptr, phdr_mp->b_rptr, hdrs_length); 43970Sstevel@tonic-gate 43980Sstevel@tonic-gate ptr = phdr_mp->b_rptr + hdrs_length + sizeof (ah_t); 43990Sstevel@tonic-gate bcopy(phdr_mp->b_wptr, ptr, icv_len); 44000Sstevel@tonic-gate 44010Sstevel@tonic-gate /* 44020Sstevel@tonic-gate * Compute the new header checksum as we are assigning 44030Sstevel@tonic-gate * IPPROTO_AH and adjusting the length here. 44040Sstevel@tonic-gate */ 44050Sstevel@tonic-gate nipha = (ipha_t *)phdr_mp->b_rptr; 44060Sstevel@tonic-gate 44070Sstevel@tonic-gate nipha->ipha_protocol = IPPROTO_AH; 44080Sstevel@tonic-gate length = ntohs(nipha->ipha_length); 44090Sstevel@tonic-gate length += (sizeof (ah_t) + align_len); 44100Sstevel@tonic-gate nipha->ipha_length = htons((uint16_t)length); 44110Sstevel@tonic-gate nipha->ipha_hdr_checksum = 0; 44120Sstevel@tonic-gate nipha->ipha_hdr_checksum = (uint16_t)ip_csum_hdr(nipha); 44130Sstevel@tonic-gate } else { 44140Sstevel@tonic-gate ip6_t *ip6h; 44150Sstevel@tonic-gate ip6_t *nip6h; 44160Sstevel@tonic-gate uint_t ah_offset; 44170Sstevel@tonic-gate 44180Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr; 44190Sstevel@tonic-gate nip6h = (ip6_t *)phdr_mp->b_rptr; 44200Sstevel@tonic-gate align_len = P2ALIGN(icv_len + IPV6_PADDING_ALIGN - 1, 44210Sstevel@tonic-gate IPV6_PADDING_ALIGN); 44220Sstevel@tonic-gate /* 44230Sstevel@tonic-gate * phdr_mp must have the right amount of space for the 44240Sstevel@tonic-gate * combined IP and AH header. Copy the IP header with 44250Sstevel@tonic-gate * options into the pseudo header. When we constructed 44260Sstevel@tonic-gate * a pseudo header, we did not copy some of the mutable 44270Sstevel@tonic-gate * fields. We do it now by calling ah_fix_phdr_v6() 44280Sstevel@tonic-gate * with the last argument B_TRUE. It returns the 44290Sstevel@tonic-gate * ah_offset into the pseudo header. 44300Sstevel@tonic-gate */ 44310Sstevel@tonic-gate 44320Sstevel@tonic-gate bcopy(ip6h, nip6h, IPV6_HDR_LEN); 44330Sstevel@tonic-gate ah_offset = ah_fix_phdr_v6(nip6h, ip6h, B_TRUE, B_TRUE); 44340Sstevel@tonic-gate ASSERT(ah_offset != 0); 44350Sstevel@tonic-gate /* 44360Sstevel@tonic-gate * phdr_mp can hold exactly the whole IP header with options 44370Sstevel@tonic-gate * plus the AH header also. Thus subtracting the AH header's 44380Sstevel@tonic-gate * size should give exactly how much of the original header 44390Sstevel@tonic-gate * should be skipped. 44400Sstevel@tonic-gate */ 44410Sstevel@tonic-gate hdrs_length = (phdr_mp->b_wptr - phdr_mp->b_rptr) - 44420Sstevel@tonic-gate sizeof (ah_t) - icv_len; 44430Sstevel@tonic-gate bcopy(phdr_mp->b_wptr, ((uint8_t *)nip6h + ah_offset + 44440Sstevel@tonic-gate sizeof (ah_t)), icv_len); 44450Sstevel@tonic-gate length = ntohs(nip6h->ip6_plen); 44460Sstevel@tonic-gate length += (sizeof (ah_t) + align_len); 44470Sstevel@tonic-gate nip6h->ip6_plen = htons((uint16_t)length); 44480Sstevel@tonic-gate } 44490Sstevel@tonic-gate 44500Sstevel@tonic-gate /* Skip the original IP header */ 44510Sstevel@tonic-gate mp->b_rptr += hdrs_length; 44520Sstevel@tonic-gate if (mp->b_rptr == mp->b_wptr) { 44530Sstevel@tonic-gate phdr_mp->b_cont = mp->b_cont; 44540Sstevel@tonic-gate freeb(mp); 44550Sstevel@tonic-gate } 44560Sstevel@tonic-gate 44570Sstevel@tonic-gate return (IPSEC_STATUS_SUCCESS); 44580Sstevel@tonic-gate } 44590Sstevel@tonic-gate 4460*10934Ssommerfeld@sun.com /* Refactor me */ 44610Sstevel@tonic-gate /* 44620Sstevel@tonic-gate * Wrapper to allow IP to trigger an AH association failure message 44630Sstevel@tonic-gate * during SA inbound selection. 44640Sstevel@tonic-gate */ 44650Sstevel@tonic-gate void 44660Sstevel@tonic-gate ipsecah_in_assocfailure(mblk_t *mp, char level, ushort_t sl, char *fmt, 44673448Sdh155122 uint32_t spi, void *addr, int af, ipsecah_stack_t *ahstack) 44680Sstevel@tonic-gate { 44693448Sdh155122 ipsec_stack_t *ipss = ahstack->ipsecah_netstack->netstack_ipsec; 44703448Sdh155122 44713448Sdh155122 if (ahstack->ipsecah_log_unknown_spi) { 44720Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, level, sl, fmt, spi, 44733448Sdh155122 addr, af, ahstack->ipsecah_netstack); 44740Sstevel@tonic-gate } 44750Sstevel@tonic-gate 44763448Sdh155122 ip_drop_packet(mp, B_TRUE, NULL, NULL, 44773448Sdh155122 DROPPER(ipss, ipds_ah_no_sa), 44783448Sdh155122 &ahstack->ah_dropper); 44790Sstevel@tonic-gate } 44800Sstevel@tonic-gate 44810Sstevel@tonic-gate /* 44820Sstevel@tonic-gate * Initialize the AH input and output processing functions. 44830Sstevel@tonic-gate */ 44840Sstevel@tonic-gate void 44850Sstevel@tonic-gate ipsecah_init_funcs(ipsa_t *sa) 44860Sstevel@tonic-gate { 44870Sstevel@tonic-gate if (sa->ipsa_output_func == NULL) 44880Sstevel@tonic-gate sa->ipsa_output_func = ah_outbound; 44890Sstevel@tonic-gate if (sa->ipsa_input_func == NULL) 44900Sstevel@tonic-gate sa->ipsa_input_func = ah_inbound; 44910Sstevel@tonic-gate } 4492