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 /* 226048Swy83408 * Copyright 2008 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> 500Sstevel@tonic-gate 510Sstevel@tonic-gate #include <inet/common.h> 520Sstevel@tonic-gate #include <inet/mi.h> 530Sstevel@tonic-gate #include <inet/ip.h> 540Sstevel@tonic-gate #include <inet/ip6.h> 550Sstevel@tonic-gate #include <inet/nd.h> 560Sstevel@tonic-gate #include <inet/ipsec_info.h> 570Sstevel@tonic-gate #include <inet/ipsec_impl.h> 580Sstevel@tonic-gate #include <inet/sadb.h> 590Sstevel@tonic-gate #include <inet/ipsecah.h> 600Sstevel@tonic-gate #include <inet/ipsec_impl.h> 610Sstevel@tonic-gate #include <inet/ipdrop.h> 620Sstevel@tonic-gate #include <sys/taskq.h> 630Sstevel@tonic-gate #include <sys/policy.h> 640Sstevel@tonic-gate #include <sys/iphada.h> 650Sstevel@tonic-gate #include <sys/strsun.h> 660Sstevel@tonic-gate 670Sstevel@tonic-gate #include <sys/crypto/common.h> 680Sstevel@tonic-gate #include <sys/crypto/api.h> 690Sstevel@tonic-gate #include <sys/kstat.h> 701676Sjpk #include <sys/strsubr.h> 710Sstevel@tonic-gate 720Sstevel@tonic-gate /* 730Sstevel@tonic-gate * Table of ND variables supported by ipsecah. These are loaded into 740Sstevel@tonic-gate * ipsecah_g_nd in ipsecah_init_nd. 750Sstevel@tonic-gate * All of these are alterable, within the min/max values given, at run time. 760Sstevel@tonic-gate */ 773448Sdh155122 static ipsecahparam_t lcl_param_arr[] = { 780Sstevel@tonic-gate /* min max value name */ 790Sstevel@tonic-gate { 0, 3, 0, "ipsecah_debug"}, 800Sstevel@tonic-gate { 125, 32000, SADB_AGE_INTERVAL_DEFAULT, "ipsecah_age_interval"}, 810Sstevel@tonic-gate { 1, 10, 1, "ipsecah_reap_delay"}, 820Sstevel@tonic-gate { 1, SADB_MAX_REPLAY, 64, "ipsecah_replay_size"}, 830Sstevel@tonic-gate { 1, 300, 15, "ipsecah_acquire_timeout"}, 840Sstevel@tonic-gate { 1, 1800, 90, "ipsecah_larval_timeout"}, 850Sstevel@tonic-gate /* Default lifetime values for ACQUIRE messages. */ 860Sstevel@tonic-gate { 0, 0xffffffffU, 0, "ipsecah_default_soft_bytes"}, 870Sstevel@tonic-gate { 0, 0xffffffffU, 0, "ipsecah_default_hard_bytes"}, 880Sstevel@tonic-gate { 0, 0xffffffffU, 24000, "ipsecah_default_soft_addtime"}, 890Sstevel@tonic-gate { 0, 0xffffffffU, 28800, "ipsecah_default_hard_addtime"}, 900Sstevel@tonic-gate { 0, 0xffffffffU, 0, "ipsecah_default_soft_usetime"}, 910Sstevel@tonic-gate { 0, 0xffffffffU, 0, "ipsecah_default_hard_usetime"}, 920Sstevel@tonic-gate { 0, 1, 0, "ipsecah_log_unknown_spi"}, 930Sstevel@tonic-gate }; 943448Sdh155122 #define ipsecah_debug ipsecah_params[0].ipsecah_param_value 953448Sdh155122 #define ipsecah_age_interval ipsecah_params[1].ipsecah_param_value 963448Sdh155122 #define ipsecah_age_int_max ipsecah_params[1].ipsecah_param_max 973448Sdh155122 #define ipsecah_reap_delay ipsecah_params[2].ipsecah_param_value 983448Sdh155122 #define ipsecah_replay_size ipsecah_params[3].ipsecah_param_value 993448Sdh155122 #define ipsecah_acquire_timeout ipsecah_params[4].ipsecah_param_value 1003448Sdh155122 #define ipsecah_larval_timeout ipsecah_params[5].ipsecah_param_value 1013448Sdh155122 #define ipsecah_default_soft_bytes ipsecah_params[6].ipsecah_param_value 1023448Sdh155122 #define ipsecah_default_hard_bytes ipsecah_params[7].ipsecah_param_value 1033448Sdh155122 #define ipsecah_default_soft_addtime ipsecah_params[8].ipsecah_param_value 1043448Sdh155122 #define ipsecah_default_hard_addtime ipsecah_params[9].ipsecah_param_value 1053448Sdh155122 #define ipsecah_default_soft_usetime ipsecah_params[10].ipsecah_param_value 1063448Sdh155122 #define ipsecah_default_hard_usetime ipsecah_params[11].ipsecah_param_value 1073448Sdh155122 #define ipsecah_log_unknown_spi ipsecah_params[12].ipsecah_param_value 1080Sstevel@tonic-gate 1090Sstevel@tonic-gate #define ah0dbg(a) printf a 1100Sstevel@tonic-gate /* NOTE: != 0 instead of > 0 so lint doesn't complain. */ 1113448Sdh155122 #define ah1dbg(ahstack, a) if (ahstack->ipsecah_debug != 0) printf a 1123448Sdh155122 #define ah2dbg(ahstack, a) if (ahstack->ipsecah_debug > 1) printf a 1133448Sdh155122 #define ah3dbg(ahstack, a) if (ahstack->ipsecah_debug > 2) printf a 1140Sstevel@tonic-gate 1150Sstevel@tonic-gate /* 1160Sstevel@tonic-gate * XXX This is broken. Padding should be determined dynamically 1170Sstevel@tonic-gate * depending on the ICV size and IP version number so that the 1180Sstevel@tonic-gate * total AH header size is a multiple of 32 bits or 64 bits 1190Sstevel@tonic-gate * for V4 and V6 respectively. For 96bit ICVs we have no problems. 1200Sstevel@tonic-gate * Anything different from that, we need to fix our code. 1210Sstevel@tonic-gate */ 1220Sstevel@tonic-gate #define IPV4_PADDING_ALIGN 0x04 /* Multiple of 32 bits */ 1230Sstevel@tonic-gate #define IPV6_PADDING_ALIGN 0x04 /* Multiple of 32 bits */ 1240Sstevel@tonic-gate 1250Sstevel@tonic-gate /* 1260Sstevel@tonic-gate * Helper macro. Avoids a call to msgdsize if there is only one 1270Sstevel@tonic-gate * mblk in the chain. 1280Sstevel@tonic-gate */ 1290Sstevel@tonic-gate #define AH_MSGSIZE(mp) ((mp)->b_cont != NULL ? msgdsize(mp) : MBLKL(mp)) 1300Sstevel@tonic-gate 1313448Sdh155122 1320Sstevel@tonic-gate static ipsec_status_t ah_auth_out_done(mblk_t *); 1330Sstevel@tonic-gate static ipsec_status_t ah_auth_in_done(mblk_t *); 1340Sstevel@tonic-gate static mblk_t *ah_process_ip_options_v4(mblk_t *, ipsa_t *, int *, uint_t, 1353448Sdh155122 boolean_t, ipsecah_stack_t *); 1360Sstevel@tonic-gate static mblk_t *ah_process_ip_options_v6(mblk_t *, ipsa_t *, int *, uint_t, 1373448Sdh155122 boolean_t, ipsecah_stack_t *); 1383448Sdh155122 static void ah_getspi(mblk_t *, keysock_in_t *, ipsecah_stack_t *); 1390Sstevel@tonic-gate static ipsec_status_t ah_inbound_accelerated(mblk_t *, boolean_t, ipsa_t *, 1400Sstevel@tonic-gate uint32_t); 1410Sstevel@tonic-gate static ipsec_status_t ah_outbound_accelerated_v4(mblk_t *, ipsa_t *); 1420Sstevel@tonic-gate static ipsec_status_t ah_outbound_accelerated_v6(mblk_t *, ipsa_t *); 1430Sstevel@tonic-gate static ipsec_status_t ah_outbound(mblk_t *); 1440Sstevel@tonic-gate 1450Sstevel@tonic-gate static int ipsecah_open(queue_t *, dev_t *, int, int, cred_t *); 1460Sstevel@tonic-gate static int ipsecah_close(queue_t *); 1470Sstevel@tonic-gate static void ipsecah_rput(queue_t *, mblk_t *); 1480Sstevel@tonic-gate static void ipsecah_wput(queue_t *, mblk_t *); 1493448Sdh155122 static void ah_send_acquire(ipsacq_t *, mblk_t *, netstack_t *); 1503448Sdh155122 static boolean_t ah_register_out(uint32_t, uint32_t, uint_t, ipsecah_stack_t *); 1513448Sdh155122 static void *ipsecah_stack_init(netstackid_t stackid, netstack_t *ns); 1523448Sdh155122 static void ipsecah_stack_fini(netstackid_t stackid, void *arg); 1533448Sdh155122 154*8392SHuafeng.Lv@Sun.COM extern void (*cl_inet_getspi)(netstackid_t, uint8_t, uint8_t *, size_t, 155*8392SHuafeng.Lv@Sun.COM void *); 1567749SThejaswini.Singarajipura@Sun.COM 1573448Sdh155122 /* Setable in /etc/system */ 1583448Sdh155122 uint32_t ah_hash_size = IPSEC_DEFAULT_HASH_SIZE; 1593448Sdh155122 1603448Sdh155122 static taskq_t *ah_taskq; 1610Sstevel@tonic-gate 1620Sstevel@tonic-gate static struct module_info info = { 1630Sstevel@tonic-gate 5136, "ipsecah", 0, INFPSZ, 65536, 1024 1640Sstevel@tonic-gate }; 1650Sstevel@tonic-gate 1660Sstevel@tonic-gate static struct qinit rinit = { 1670Sstevel@tonic-gate (pfi_t)ipsecah_rput, NULL, ipsecah_open, ipsecah_close, NULL, &info, 1680Sstevel@tonic-gate NULL 1690Sstevel@tonic-gate }; 1700Sstevel@tonic-gate 1710Sstevel@tonic-gate static struct qinit winit = { 1720Sstevel@tonic-gate (pfi_t)ipsecah_wput, NULL, ipsecah_open, ipsecah_close, NULL, &info, 1730Sstevel@tonic-gate NULL 1740Sstevel@tonic-gate }; 1750Sstevel@tonic-gate 1760Sstevel@tonic-gate struct streamtab ipsecahinfo = { 1770Sstevel@tonic-gate &rinit, &winit, NULL, NULL 1780Sstevel@tonic-gate }; 1790Sstevel@tonic-gate 1800Sstevel@tonic-gate static int ah_kstat_update(kstat_t *, int); 1810Sstevel@tonic-gate 1821659Smarkfen uint64_t ipsacq_maxpackets = IPSACQ_MAXPACKETS; 1831659Smarkfen 1840Sstevel@tonic-gate static boolean_t 1853448Sdh155122 ah_kstat_init(ipsecah_stack_t *ahstack, netstackid_t stackid) 1860Sstevel@tonic-gate { 1873448Sdh155122 ipsec_stack_t *ipss = ahstack->ipsecah_netstack->netstack_ipsec; 1883448Sdh155122 1893448Sdh155122 ahstack->ah_ksp = kstat_create_netstack("ipsecah", 0, "ah_stat", "net", 1903448Sdh155122 KSTAT_TYPE_NAMED, sizeof (ah_kstats_t) / sizeof (kstat_named_t), 1913448Sdh155122 KSTAT_FLAG_PERSISTENT, stackid); 1923448Sdh155122 1933448Sdh155122 if (ahstack->ah_ksp == NULL || ahstack->ah_ksp->ks_data == NULL) 1940Sstevel@tonic-gate return (B_FALSE); 1950Sstevel@tonic-gate 1963448Sdh155122 ahstack->ah_kstats = ahstack->ah_ksp->ks_data; 1973448Sdh155122 1983448Sdh155122 ahstack->ah_ksp->ks_update = ah_kstat_update; 1993448Sdh155122 ahstack->ah_ksp->ks_private = (void *)(uintptr_t)stackid; 2000Sstevel@tonic-gate 2010Sstevel@tonic-gate #define K64 KSTAT_DATA_UINT64 2023448Sdh155122 #define KI(x) kstat_named_init(&(ahstack->ah_kstats->ah_stat_##x), #x, K64) 2030Sstevel@tonic-gate 2040Sstevel@tonic-gate KI(num_aalgs); 2050Sstevel@tonic-gate KI(good_auth); 2060Sstevel@tonic-gate KI(bad_auth); 2070Sstevel@tonic-gate KI(replay_failures); 2080Sstevel@tonic-gate KI(replay_early_failures); 2090Sstevel@tonic-gate KI(keysock_in); 2100Sstevel@tonic-gate KI(out_requests); 2110Sstevel@tonic-gate KI(acquire_requests); 2120Sstevel@tonic-gate KI(bytes_expired); 2130Sstevel@tonic-gate KI(out_discards); 2140Sstevel@tonic-gate KI(in_accelerated); 2150Sstevel@tonic-gate KI(out_accelerated); 2160Sstevel@tonic-gate KI(noaccel); 2170Sstevel@tonic-gate KI(crypto_sync); 2180Sstevel@tonic-gate KI(crypto_async); 2190Sstevel@tonic-gate KI(crypto_failures); 2200Sstevel@tonic-gate 2210Sstevel@tonic-gate #undef KI 2220Sstevel@tonic-gate #undef K64 2230Sstevel@tonic-gate 2243448Sdh155122 kstat_install(ahstack->ah_ksp); 2253448Sdh155122 IP_ACQUIRE_STAT(ipss, maxpackets, ipsacq_maxpackets); 2260Sstevel@tonic-gate return (B_TRUE); 2270Sstevel@tonic-gate } 2280Sstevel@tonic-gate 2290Sstevel@tonic-gate static int 2300Sstevel@tonic-gate ah_kstat_update(kstat_t *kp, int rw) 2310Sstevel@tonic-gate { 2323448Sdh155122 ah_kstats_t *ekp; 2333448Sdh155122 netstackid_t stackid = (netstackid_t)(uintptr_t)kp->ks_private; 2343448Sdh155122 netstack_t *ns; 2353448Sdh155122 ipsec_stack_t *ipss; 2360Sstevel@tonic-gate 2370Sstevel@tonic-gate if ((kp == NULL) || (kp->ks_data == NULL)) 2380Sstevel@tonic-gate return (EIO); 2390Sstevel@tonic-gate 2400Sstevel@tonic-gate if (rw == KSTAT_WRITE) 2410Sstevel@tonic-gate return (EACCES); 2420Sstevel@tonic-gate 2433448Sdh155122 ns = netstack_find_by_stackid(stackid); 2443448Sdh155122 if (ns == NULL) 2453448Sdh155122 return (-1); 2463448Sdh155122 ipss = ns->netstack_ipsec; 2473448Sdh155122 if (ipss == NULL) { 2483448Sdh155122 netstack_rele(ns); 2493448Sdh155122 return (-1); 2503448Sdh155122 } 2510Sstevel@tonic-gate ekp = (ah_kstats_t *)kp->ks_data; 2523448Sdh155122 2533448Sdh155122 mutex_enter(&ipss->ipsec_alg_lock); 2543448Sdh155122 ekp->ah_stat_num_aalgs.value.ui64 = ipss->ipsec_nalgs[IPSEC_ALG_AUTH]; 2553448Sdh155122 mutex_exit(&ipss->ipsec_alg_lock); 2563448Sdh155122 2573448Sdh155122 netstack_rele(ns); 2580Sstevel@tonic-gate return (0); 2590Sstevel@tonic-gate } 2600Sstevel@tonic-gate 2610Sstevel@tonic-gate /* 2620Sstevel@tonic-gate * Don't have to lock ipsec_age_interval, as only one thread will access it at 2630Sstevel@tonic-gate * a time, because I control the one function that does a qtimeout() on 2640Sstevel@tonic-gate * ah_pfkey_q. 2650Sstevel@tonic-gate */ 2660Sstevel@tonic-gate static void 2673448Sdh155122 ah_ager(void *arg) 2680Sstevel@tonic-gate { 2693448Sdh155122 ipsecah_stack_t *ahstack = (ipsecah_stack_t *)arg; 2703448Sdh155122 netstack_t *ns = ahstack->ipsecah_netstack; 2710Sstevel@tonic-gate hrtime_t begin = gethrtime(); 2720Sstevel@tonic-gate 2733448Sdh155122 sadb_ager(&ahstack->ah_sadb.s_v4, ahstack->ah_pfkey_q, 2743448Sdh155122 ahstack->ah_sadb.s_ip_q, ahstack->ipsecah_reap_delay, ns); 2753448Sdh155122 sadb_ager(&ahstack->ah_sadb.s_v6, ahstack->ah_pfkey_q, 2763448Sdh155122 ahstack->ah_sadb.s_ip_q, ahstack->ipsecah_reap_delay, ns); 2773448Sdh155122 2783448Sdh155122 ahstack->ah_event = sadb_retimeout(begin, ahstack->ah_pfkey_q, 2793448Sdh155122 ah_ager, ahstack, 2803448Sdh155122 &ahstack->ipsecah_age_interval, ahstack->ipsecah_age_int_max, 2813448Sdh155122 info.mi_idnum); 2820Sstevel@tonic-gate } 2830Sstevel@tonic-gate 2840Sstevel@tonic-gate /* 2850Sstevel@tonic-gate * Get an AH NDD parameter. 2860Sstevel@tonic-gate */ 2870Sstevel@tonic-gate /* ARGSUSED */ 2880Sstevel@tonic-gate static int 2890Sstevel@tonic-gate ipsecah_param_get(q, mp, cp, cr) 2900Sstevel@tonic-gate queue_t *q; 2910Sstevel@tonic-gate mblk_t *mp; 2920Sstevel@tonic-gate caddr_t cp; 2930Sstevel@tonic-gate cred_t *cr; 2940Sstevel@tonic-gate { 2950Sstevel@tonic-gate ipsecahparam_t *ipsecahpa = (ipsecahparam_t *)cp; 2960Sstevel@tonic-gate uint_t value; 2973448Sdh155122 ipsecah_stack_t *ahstack = (ipsecah_stack_t *)q->q_ptr; 2983448Sdh155122 2993448Sdh155122 mutex_enter(&ahstack->ipsecah_param_lock); 3000Sstevel@tonic-gate value = ipsecahpa->ipsecah_param_value; 3013448Sdh155122 mutex_exit(&ahstack->ipsecah_param_lock); 3020Sstevel@tonic-gate 3030Sstevel@tonic-gate (void) mi_mpprintf(mp, "%u", value); 3040Sstevel@tonic-gate return (0); 3050Sstevel@tonic-gate } 3060Sstevel@tonic-gate 3070Sstevel@tonic-gate /* 3080Sstevel@tonic-gate * This routine sets an NDD variable in a ipsecahparam_t structure. 3090Sstevel@tonic-gate */ 3100Sstevel@tonic-gate /* ARGSUSED */ 3110Sstevel@tonic-gate static int 3120Sstevel@tonic-gate ipsecah_param_set(q, mp, value, cp, cr) 3130Sstevel@tonic-gate queue_t *q; 3140Sstevel@tonic-gate mblk_t *mp; 3150Sstevel@tonic-gate char *value; 3160Sstevel@tonic-gate caddr_t cp; 3170Sstevel@tonic-gate cred_t *cr; 3180Sstevel@tonic-gate { 3190Sstevel@tonic-gate ulong_t new_value; 3200Sstevel@tonic-gate ipsecahparam_t *ipsecahpa = (ipsecahparam_t *)cp; 3213448Sdh155122 ipsecah_stack_t *ahstack = (ipsecah_stack_t *)q->q_ptr; 3220Sstevel@tonic-gate 3230Sstevel@tonic-gate /* 3240Sstevel@tonic-gate * Fail the request if the new value does not lie within the 3250Sstevel@tonic-gate * required bounds. 3260Sstevel@tonic-gate */ 3270Sstevel@tonic-gate if (ddi_strtoul(value, NULL, 10, &new_value) != 0 || 3280Sstevel@tonic-gate new_value < ipsecahpa->ipsecah_param_min || 3290Sstevel@tonic-gate new_value > ipsecahpa->ipsecah_param_max) { 3300Sstevel@tonic-gate return (EINVAL); 3310Sstevel@tonic-gate } 3320Sstevel@tonic-gate 3330Sstevel@tonic-gate /* Set the new value */ 3343448Sdh155122 mutex_enter(&ahstack->ipsecah_param_lock); 3350Sstevel@tonic-gate ipsecahpa->ipsecah_param_value = new_value; 3363448Sdh155122 mutex_exit(&ahstack->ipsecah_param_lock); 3370Sstevel@tonic-gate return (0); 3380Sstevel@tonic-gate } 3390Sstevel@tonic-gate 3400Sstevel@tonic-gate /* 3410Sstevel@tonic-gate * Using lifetime NDD variables, fill in an extended combination's 3420Sstevel@tonic-gate * lifetime information. 3430Sstevel@tonic-gate */ 3440Sstevel@tonic-gate void 3453448Sdh155122 ipsecah_fill_defs(sadb_x_ecomb_t *ecomb, netstack_t *ns) 3460Sstevel@tonic-gate { 3473448Sdh155122 ipsecah_stack_t *ahstack = ns->netstack_ipsecah; 3483448Sdh155122 3493448Sdh155122 ecomb->sadb_x_ecomb_soft_bytes = ahstack->ipsecah_default_soft_bytes; 3503448Sdh155122 ecomb->sadb_x_ecomb_hard_bytes = ahstack->ipsecah_default_hard_bytes; 3513448Sdh155122 ecomb->sadb_x_ecomb_soft_addtime = 3523448Sdh155122 ahstack->ipsecah_default_soft_addtime; 3533448Sdh155122 ecomb->sadb_x_ecomb_hard_addtime = 3543448Sdh155122 ahstack->ipsecah_default_hard_addtime; 3553448Sdh155122 ecomb->sadb_x_ecomb_soft_usetime = 3563448Sdh155122 ahstack->ipsecah_default_soft_usetime; 3573448Sdh155122 ecomb->sadb_x_ecomb_hard_usetime = 3583448Sdh155122 ahstack->ipsecah_default_hard_usetime; 3590Sstevel@tonic-gate } 3600Sstevel@tonic-gate 3610Sstevel@tonic-gate /* 3620Sstevel@tonic-gate * Initialize things for AH at module load time. 3630Sstevel@tonic-gate */ 3640Sstevel@tonic-gate boolean_t 3650Sstevel@tonic-gate ipsecah_ddi_init(void) 3660Sstevel@tonic-gate { 3673448Sdh155122 ah_taskq = taskq_create("ah_taskq", 1, minclsyspri, 3683448Sdh155122 IPSEC_TASKQ_MIN, IPSEC_TASKQ_MAX, 0); 3693448Sdh155122 3703448Sdh155122 /* 3713448Sdh155122 * We want to be informed each time a stack is created or 3723448Sdh155122 * destroyed in the kernel, so we can maintain the 3733448Sdh155122 * set of ipsecah_stack_t's. 3743448Sdh155122 */ 3753448Sdh155122 netstack_register(NS_IPSECAH, ipsecah_stack_init, NULL, 3763448Sdh155122 ipsecah_stack_fini); 3773448Sdh155122 3783448Sdh155122 return (B_TRUE); 3793448Sdh155122 } 3803448Sdh155122 3813448Sdh155122 /* 3823448Sdh155122 * Walk through the param array specified registering each element with the 3833448Sdh155122 * named dispatch handler. 3843448Sdh155122 */ 3853448Sdh155122 static boolean_t 3863448Sdh155122 ipsecah_param_register(IDP *ndp, ipsecahparam_t *ahp, int cnt) 3873448Sdh155122 { 3883448Sdh155122 for (; cnt-- > 0; ahp++) { 3890Sstevel@tonic-gate if (ahp->ipsecah_param_name != NULL && 3900Sstevel@tonic-gate ahp->ipsecah_param_name[0]) { 3913448Sdh155122 if (!nd_load(ndp, 3923448Sdh155122 ahp->ipsecah_param_name, 3930Sstevel@tonic-gate ipsecah_param_get, ipsecah_param_set, 3940Sstevel@tonic-gate (caddr_t)ahp)) { 3953448Sdh155122 nd_free(ndp); 3960Sstevel@tonic-gate return (B_FALSE); 3970Sstevel@tonic-gate } 3980Sstevel@tonic-gate } 3990Sstevel@tonic-gate } 4000Sstevel@tonic-gate return (B_TRUE); 4010Sstevel@tonic-gate } 4020Sstevel@tonic-gate 4030Sstevel@tonic-gate /* 4043448Sdh155122 * Initialize things for AH for each stack instance 4053448Sdh155122 */ 4063448Sdh155122 static void * 4073448Sdh155122 ipsecah_stack_init(netstackid_t stackid, netstack_t *ns) 4083448Sdh155122 { 4093448Sdh155122 ipsecah_stack_t *ahstack; 4103448Sdh155122 ipsecahparam_t *ahp; 4113448Sdh155122 4123448Sdh155122 ahstack = (ipsecah_stack_t *)kmem_zalloc(sizeof (*ahstack), KM_SLEEP); 4133448Sdh155122 ahstack->ipsecah_netstack = ns; 4143448Sdh155122 4153448Sdh155122 ahp = (ipsecahparam_t *)kmem_alloc(sizeof (lcl_param_arr), KM_SLEEP); 4163448Sdh155122 ahstack->ipsecah_params = ahp; 4173448Sdh155122 bcopy(lcl_param_arr, ahp, sizeof (lcl_param_arr)); 4183448Sdh155122 4193448Sdh155122 (void) ipsecah_param_register(&ahstack->ipsecah_g_nd, ahp, 4203448Sdh155122 A_CNT(lcl_param_arr)); 4213448Sdh155122 4223448Sdh155122 (void) ah_kstat_init(ahstack, stackid); 4233448Sdh155122 4243448Sdh155122 ahstack->ah_sadb.s_acquire_timeout = &ahstack->ipsecah_acquire_timeout; 4253448Sdh155122 ahstack->ah_sadb.s_acqfn = ah_send_acquire; 4263448Sdh155122 sadbp_init("AH", &ahstack->ah_sadb, SADB_SATYPE_AH, ah_hash_size, 4273448Sdh155122 ahstack->ipsecah_netstack); 4283448Sdh155122 4293448Sdh155122 mutex_init(&ahstack->ipsecah_param_lock, NULL, MUTEX_DEFAULT, 0); 4303448Sdh155122 4313448Sdh155122 ip_drop_register(&ahstack->ah_dropper, "IPsec AH"); 4323448Sdh155122 return (ahstack); 4333448Sdh155122 } 4343448Sdh155122 4353448Sdh155122 /* 4360Sstevel@tonic-gate * Destroy things for AH at module unload time. 4370Sstevel@tonic-gate */ 4380Sstevel@tonic-gate void 4390Sstevel@tonic-gate ipsecah_ddi_destroy(void) 4400Sstevel@tonic-gate { 4413448Sdh155122 netstack_unregister(NS_IPSECAH); 4420Sstevel@tonic-gate taskq_destroy(ah_taskq); 4433448Sdh155122 } 4443448Sdh155122 4453448Sdh155122 /* 4463448Sdh155122 * Destroy things for AH for one stack... Never called? 4473448Sdh155122 */ 4483448Sdh155122 static void 4493448Sdh155122 ipsecah_stack_fini(netstackid_t stackid, void *arg) 4503448Sdh155122 { 4513448Sdh155122 ipsecah_stack_t *ahstack = (ipsecah_stack_t *)arg; 4523448Sdh155122 4533448Sdh155122 if (ahstack->ah_pfkey_q != NULL) { 4543448Sdh155122 (void) quntimeout(ahstack->ah_pfkey_q, ahstack->ah_event); 4553448Sdh155122 } 4563448Sdh155122 ahstack->ah_sadb.s_acqfn = NULL; 4573448Sdh155122 ahstack->ah_sadb.s_acquire_timeout = NULL; 4583448Sdh155122 sadbp_destroy(&ahstack->ah_sadb, ahstack->ipsecah_netstack); 4593448Sdh155122 ip_drop_unregister(&ahstack->ah_dropper); 4603448Sdh155122 mutex_destroy(&ahstack->ipsecah_param_lock); 4613448Sdh155122 nd_free(&ahstack->ipsecah_g_nd); 4623448Sdh155122 4633448Sdh155122 kmem_free(ahstack->ipsecah_params, sizeof (lcl_param_arr)); 4643448Sdh155122 ahstack->ipsecah_params = NULL; 4653448Sdh155122 kstat_delete_netstack(ahstack->ah_ksp, stackid); 4663448Sdh155122 ahstack->ah_ksp = NULL; 4673448Sdh155122 ahstack->ah_kstats = NULL; 4683448Sdh155122 4693448Sdh155122 kmem_free(ahstack, sizeof (*ahstack)); 4700Sstevel@tonic-gate } 4710Sstevel@tonic-gate 4720Sstevel@tonic-gate /* 4730Sstevel@tonic-gate * AH module open routine. The module should be opened by keysock. 4740Sstevel@tonic-gate */ 4750Sstevel@tonic-gate /* ARGSUSED */ 4760Sstevel@tonic-gate static int 4770Sstevel@tonic-gate ipsecah_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp) 4780Sstevel@tonic-gate { 4793448Sdh155122 netstack_t *ns; 4803448Sdh155122 ipsecah_stack_t *ahstack; 4813448Sdh155122 4827118Ssommerfe if (secpolicy_ip_config(credp, B_FALSE) != 0) 4830Sstevel@tonic-gate return (EPERM); 4840Sstevel@tonic-gate 4850Sstevel@tonic-gate if (q->q_ptr != NULL) 4860Sstevel@tonic-gate return (0); /* Re-open of an already open instance. */ 4870Sstevel@tonic-gate 4880Sstevel@tonic-gate if (sflag != MODOPEN) 4890Sstevel@tonic-gate return (EINVAL); 4900Sstevel@tonic-gate 4913448Sdh155122 ns = netstack_find_by_cred(credp); 4923448Sdh155122 ASSERT(ns != NULL); 4933448Sdh155122 ahstack = ns->netstack_ipsecah; 4943448Sdh155122 ASSERT(ahstack != NULL); 4953448Sdh155122 4960Sstevel@tonic-gate /* 4970Sstevel@tonic-gate * ASSUMPTIONS (because I'm MT_OCEXCL): 4980Sstevel@tonic-gate * 4990Sstevel@tonic-gate * * I'm being pushed on top of IP for all my opens (incl. #1). 5000Sstevel@tonic-gate * * Only ipsecah_open() can write into ah_sadb.s_ip_q. 5010Sstevel@tonic-gate * * Because of this, I can check lazily for ah_sadb.s_ip_q. 5020Sstevel@tonic-gate * 5030Sstevel@tonic-gate * If these assumptions are wrong, I'm in BIG trouble... 5040Sstevel@tonic-gate */ 5050Sstevel@tonic-gate 5063448Sdh155122 q->q_ptr = ahstack; 5073448Sdh155122 WR(q)->q_ptr = q->q_ptr; 5083448Sdh155122 5093448Sdh155122 if (ahstack->ah_sadb.s_ip_q == NULL) { 5100Sstevel@tonic-gate struct T_unbind_req *tur; 5110Sstevel@tonic-gate 5123448Sdh155122 ahstack->ah_sadb.s_ip_q = WR(q); 5130Sstevel@tonic-gate /* Allocate an unbind... */ 5143448Sdh155122 ahstack->ah_ip_unbind = allocb(sizeof (struct T_unbind_req), 5153448Sdh155122 BPRI_HI); 5160Sstevel@tonic-gate 5170Sstevel@tonic-gate /* 5180Sstevel@tonic-gate * Send down T_BIND_REQ to bind IPPROTO_AH. 5190Sstevel@tonic-gate * Handle the ACK here in AH. 5200Sstevel@tonic-gate */ 5210Sstevel@tonic-gate qprocson(q); 5223448Sdh155122 if (ahstack->ah_ip_unbind == NULL || 5233448Sdh155122 !sadb_t_bind_req(ahstack->ah_sadb.s_ip_q, IPPROTO_AH)) { 5243448Sdh155122 if (ahstack->ah_ip_unbind != NULL) { 5253448Sdh155122 freeb(ahstack->ah_ip_unbind); 5263448Sdh155122 ahstack->ah_ip_unbind = NULL; 5270Sstevel@tonic-gate } 5280Sstevel@tonic-gate q->q_ptr = NULL; 5290Sstevel@tonic-gate qprocsoff(q); 5303448Sdh155122 netstack_rele(ahstack->ipsecah_netstack); 5310Sstevel@tonic-gate return (ENOMEM); 5320Sstevel@tonic-gate } 5330Sstevel@tonic-gate 5343448Sdh155122 ahstack->ah_ip_unbind->b_datap->db_type = M_PROTO; 5353448Sdh155122 tur = (struct T_unbind_req *)ahstack->ah_ip_unbind->b_rptr; 5360Sstevel@tonic-gate tur->PRIM_type = T_UNBIND_REQ; 5370Sstevel@tonic-gate } else { 5380Sstevel@tonic-gate qprocson(q); 5390Sstevel@tonic-gate } 5400Sstevel@tonic-gate 5410Sstevel@tonic-gate /* 5420Sstevel@tonic-gate * For now, there's not much I can do. I'll be getting a message 5430Sstevel@tonic-gate * passed down to me from keysock (in my wput), and a T_BIND_ACK 5440Sstevel@tonic-gate * up from IP (in my rput). 5450Sstevel@tonic-gate */ 5460Sstevel@tonic-gate 5470Sstevel@tonic-gate return (0); 5480Sstevel@tonic-gate } 5490Sstevel@tonic-gate 5500Sstevel@tonic-gate /* 5510Sstevel@tonic-gate * AH module close routine. 5520Sstevel@tonic-gate */ 5530Sstevel@tonic-gate static int 5540Sstevel@tonic-gate ipsecah_close(queue_t *q) 5550Sstevel@tonic-gate { 5563448Sdh155122 ipsecah_stack_t *ahstack = (ipsecah_stack_t *)q->q_ptr; 5573448Sdh155122 5580Sstevel@tonic-gate /* 5590Sstevel@tonic-gate * If ah_sadb.s_ip_q is attached to this instance, send a 5600Sstevel@tonic-gate * T_UNBIND_REQ to IP for the instance before doing 5610Sstevel@tonic-gate * a qprocsoff(). 5620Sstevel@tonic-gate */ 5633448Sdh155122 if (WR(q) == ahstack->ah_sadb.s_ip_q && 5643448Sdh155122 ahstack->ah_ip_unbind != NULL) { 5653448Sdh155122 putnext(WR(q), ahstack->ah_ip_unbind); 5663448Sdh155122 ahstack->ah_ip_unbind = NULL; 5670Sstevel@tonic-gate } 5680Sstevel@tonic-gate 5690Sstevel@tonic-gate /* 5700Sstevel@tonic-gate * Clean up q_ptr, if needed. 5710Sstevel@tonic-gate */ 5720Sstevel@tonic-gate qprocsoff(q); 5730Sstevel@tonic-gate 5740Sstevel@tonic-gate /* Keysock queue check is safe, because of OCEXCL perimeter. */ 5750Sstevel@tonic-gate 5763448Sdh155122 if (q == ahstack->ah_pfkey_q) { 5773448Sdh155122 ah1dbg(ahstack, 5783448Sdh155122 ("ipsecah_close: Ummm... keysock is closing AH.\n")); 5793448Sdh155122 ahstack->ah_pfkey_q = NULL; 5800Sstevel@tonic-gate /* Detach qtimeouts. */ 5813448Sdh155122 (void) quntimeout(q, ahstack->ah_event); 5820Sstevel@tonic-gate } 5830Sstevel@tonic-gate 5843448Sdh155122 if (WR(q) == ahstack->ah_sadb.s_ip_q) { 5850Sstevel@tonic-gate /* 5860Sstevel@tonic-gate * If the ah_sadb.s_ip_q is attached to this instance, find 5870Sstevel@tonic-gate * another. The OCEXCL outer perimeter helps us here. 5880Sstevel@tonic-gate */ 5890Sstevel@tonic-gate 5903448Sdh155122 ahstack->ah_sadb.s_ip_q = NULL; 5910Sstevel@tonic-gate 5920Sstevel@tonic-gate /* 5930Sstevel@tonic-gate * Find a replacement queue for ah_sadb.s_ip_q. 5940Sstevel@tonic-gate */ 5953448Sdh155122 if (ahstack->ah_pfkey_q != NULL && 5963448Sdh155122 ahstack->ah_pfkey_q != RD(q)) { 5970Sstevel@tonic-gate /* 5980Sstevel@tonic-gate * See if we can use the pfkey_q. 5990Sstevel@tonic-gate */ 6003448Sdh155122 ahstack->ah_sadb.s_ip_q = WR(ahstack->ah_pfkey_q); 6010Sstevel@tonic-gate } 6020Sstevel@tonic-gate 6033448Sdh155122 if (ahstack->ah_sadb.s_ip_q == NULL || 6043448Sdh155122 !sadb_t_bind_req(ahstack->ah_sadb.s_ip_q, IPPROTO_AH)) { 6053448Sdh155122 ah1dbg(ahstack, 6063448Sdh155122 ("ipsecah: Can't reassign ah_sadb.s_ip_q.\n")); 6073448Sdh155122 ahstack->ah_sadb.s_ip_q = NULL; 6080Sstevel@tonic-gate } else { 6093448Sdh155122 ahstack->ah_ip_unbind = 6103448Sdh155122 allocb(sizeof (struct T_unbind_req), BPRI_HI); 6113448Sdh155122 6123448Sdh155122 if (ahstack->ah_ip_unbind != NULL) { 6130Sstevel@tonic-gate struct T_unbind_req *tur; 6140Sstevel@tonic-gate 6153448Sdh155122 ahstack->ah_ip_unbind->b_datap->db_type = 6163448Sdh155122 M_PROTO; 6170Sstevel@tonic-gate tur = (struct T_unbind_req *) 6183448Sdh155122 ahstack->ah_ip_unbind->b_rptr; 6190Sstevel@tonic-gate tur->PRIM_type = T_UNBIND_REQ; 6200Sstevel@tonic-gate } 6210Sstevel@tonic-gate /* If it's NULL, I can't do much here. */ 6220Sstevel@tonic-gate } 6230Sstevel@tonic-gate } 6240Sstevel@tonic-gate 6253448Sdh155122 netstack_rele(ahstack->ipsecah_netstack); 6260Sstevel@tonic-gate return (0); 6270Sstevel@tonic-gate } 6280Sstevel@tonic-gate 6290Sstevel@tonic-gate /* 6300Sstevel@tonic-gate * AH module read put routine. 6310Sstevel@tonic-gate */ 6320Sstevel@tonic-gate /* ARGSUSED */ 6330Sstevel@tonic-gate static void 6340Sstevel@tonic-gate ipsecah_rput(queue_t *q, mblk_t *mp) 6350Sstevel@tonic-gate { 6363448Sdh155122 ipsecah_stack_t *ahstack = (ipsecah_stack_t *)q->q_ptr; 6373448Sdh155122 6383055Sdanmcd ASSERT(mp->b_datap->db_type != M_CTL); /* No more IRE_DB_REQ. */ 6393448Sdh155122 6400Sstevel@tonic-gate switch (mp->b_datap->db_type) { 6410Sstevel@tonic-gate case M_PROTO: 6420Sstevel@tonic-gate case M_PCPROTO: 6430Sstevel@tonic-gate /* TPI message of some sort. */ 6440Sstevel@tonic-gate switch (*((t_scalar_t *)mp->b_rptr)) { 6450Sstevel@tonic-gate case T_BIND_ACK: 6460Sstevel@tonic-gate /* We expect this. */ 6473448Sdh155122 ah3dbg(ahstack, 6483448Sdh155122 ("Thank you IP from AH for T_BIND_ACK\n")); 6490Sstevel@tonic-gate break; 6500Sstevel@tonic-gate case T_ERROR_ACK: 6510Sstevel@tonic-gate cmn_err(CE_WARN, 6520Sstevel@tonic-gate "ipsecah: AH received T_ERROR_ACK from IP."); 6530Sstevel@tonic-gate break; 6540Sstevel@tonic-gate case T_OK_ACK: 6550Sstevel@tonic-gate /* Probably from a (rarely sent) T_UNBIND_REQ. */ 6560Sstevel@tonic-gate break; 6570Sstevel@tonic-gate default: 6583448Sdh155122 ah1dbg(ahstack, ("Unknown M_{,PC}PROTO message.\n")); 6590Sstevel@tonic-gate } 6600Sstevel@tonic-gate freemsg(mp); 6610Sstevel@tonic-gate break; 6620Sstevel@tonic-gate default: 6630Sstevel@tonic-gate /* For now, passthru message. */ 6643448Sdh155122 ah2dbg(ahstack, ("AH got unknown mblk type %d.\n", 6650Sstevel@tonic-gate mp->b_datap->db_type)); 6660Sstevel@tonic-gate putnext(q, mp); 6670Sstevel@tonic-gate } 6680Sstevel@tonic-gate } 6690Sstevel@tonic-gate 6700Sstevel@tonic-gate /* 6710Sstevel@tonic-gate * Construct an SADB_REGISTER message with the current algorithms. 6720Sstevel@tonic-gate */ 6730Sstevel@tonic-gate static boolean_t 6743448Sdh155122 ah_register_out(uint32_t sequence, uint32_t pid, uint_t serial, 6753448Sdh155122 ipsecah_stack_t *ahstack) 6760Sstevel@tonic-gate { 6770Sstevel@tonic-gate mblk_t *mp; 6780Sstevel@tonic-gate boolean_t rc = B_TRUE; 6790Sstevel@tonic-gate sadb_msg_t *samsg; 6800Sstevel@tonic-gate sadb_supported_t *sasupp; 6810Sstevel@tonic-gate sadb_alg_t *saalg; 6820Sstevel@tonic-gate uint_t allocsize = sizeof (*samsg); 6830Sstevel@tonic-gate uint_t i, numalgs_snap; 6840Sstevel@tonic-gate ipsec_alginfo_t **authalgs; 6850Sstevel@tonic-gate uint_t num_aalgs; 6863448Sdh155122 ipsec_stack_t *ipss = ahstack->ipsecah_netstack->netstack_ipsec; 6870Sstevel@tonic-gate 6880Sstevel@tonic-gate /* Allocate the KEYSOCK_OUT. */ 6890Sstevel@tonic-gate mp = sadb_keysock_out(serial); 6900Sstevel@tonic-gate if (mp == NULL) { 6910Sstevel@tonic-gate ah0dbg(("ah_register_out: couldn't allocate mblk.\n")); 6920Sstevel@tonic-gate return (B_FALSE); 6930Sstevel@tonic-gate } 6940Sstevel@tonic-gate 6950Sstevel@tonic-gate /* 6960Sstevel@tonic-gate * Allocate the PF_KEY message that follows KEYSOCK_OUT. 6970Sstevel@tonic-gate * The alg reader lock needs to be held while allocating 6980Sstevel@tonic-gate * the variable part (i.e. the algorithms) of the message. 6990Sstevel@tonic-gate */ 7000Sstevel@tonic-gate 7013448Sdh155122 mutex_enter(&ipss->ipsec_alg_lock); 7020Sstevel@tonic-gate 7030Sstevel@tonic-gate /* 7040Sstevel@tonic-gate * Return only valid algorithms, so the number of algorithms 7050Sstevel@tonic-gate * to send up may be less than the number of algorithm entries 7060Sstevel@tonic-gate * in the table. 7070Sstevel@tonic-gate */ 7083448Sdh155122 authalgs = ipss->ipsec_alglists[IPSEC_ALG_AUTH]; 7090Sstevel@tonic-gate for (num_aalgs = 0, i = 0; i < IPSEC_MAX_ALGS; i++) 7100Sstevel@tonic-gate if (authalgs[i] != NULL && ALG_VALID(authalgs[i])) 7110Sstevel@tonic-gate num_aalgs++; 7120Sstevel@tonic-gate 7130Sstevel@tonic-gate /* 7140Sstevel@tonic-gate * Fill SADB_REGISTER message's algorithm descriptors. Hold 7150Sstevel@tonic-gate * down the lock while filling it. 7160Sstevel@tonic-gate */ 7170Sstevel@tonic-gate if (num_aalgs != 0) { 7180Sstevel@tonic-gate allocsize += (num_aalgs * sizeof (*saalg)); 7190Sstevel@tonic-gate allocsize += sizeof (*sasupp); 7200Sstevel@tonic-gate } 7210Sstevel@tonic-gate mp->b_cont = allocb(allocsize, BPRI_HI); 7220Sstevel@tonic-gate if (mp->b_cont == NULL) { 7233448Sdh155122 mutex_exit(&ipss->ipsec_alg_lock); 7240Sstevel@tonic-gate freemsg(mp); 7250Sstevel@tonic-gate return (B_FALSE); 7260Sstevel@tonic-gate } 7270Sstevel@tonic-gate 7280Sstevel@tonic-gate mp->b_cont->b_wptr += allocsize; 7290Sstevel@tonic-gate if (num_aalgs != 0) { 7300Sstevel@tonic-gate 7310Sstevel@tonic-gate saalg = (sadb_alg_t *)(mp->b_cont->b_rptr + sizeof (*samsg) + 7320Sstevel@tonic-gate sizeof (*sasupp)); 7330Sstevel@tonic-gate ASSERT(((ulong_t)saalg & 0x7) == 0); 7340Sstevel@tonic-gate 7350Sstevel@tonic-gate numalgs_snap = 0; 7360Sstevel@tonic-gate for (i = 0; 7373448Sdh155122 ((i < IPSEC_MAX_ALGS) && (numalgs_snap < num_aalgs)); 7383448Sdh155122 i++) { 7390Sstevel@tonic-gate if (authalgs[i] == NULL || !ALG_VALID(authalgs[i])) 7400Sstevel@tonic-gate continue; 7410Sstevel@tonic-gate 7420Sstevel@tonic-gate saalg->sadb_alg_id = authalgs[i]->alg_id; 7430Sstevel@tonic-gate saalg->sadb_alg_ivlen = 0; 7440Sstevel@tonic-gate saalg->sadb_alg_minbits = authalgs[i]->alg_ef_minbits; 7450Sstevel@tonic-gate saalg->sadb_alg_maxbits = authalgs[i]->alg_ef_maxbits; 7460Sstevel@tonic-gate saalg->sadb_x_alg_increment = 7470Sstevel@tonic-gate authalgs[i]->alg_increment; 7483448Sdh155122 saalg->sadb_x_alg_defincr = 7493448Sdh155122 authalgs[i]->alg_ef_default; 7500Sstevel@tonic-gate numalgs_snap++; 7510Sstevel@tonic-gate saalg++; 7520Sstevel@tonic-gate } 7530Sstevel@tonic-gate ASSERT(numalgs_snap == num_aalgs); 7540Sstevel@tonic-gate #ifdef DEBUG 7550Sstevel@tonic-gate /* 7560Sstevel@tonic-gate * Reality check to make sure I snagged all of the 7570Sstevel@tonic-gate * algorithms. 7580Sstevel@tonic-gate */ 7590Sstevel@tonic-gate for (; i < IPSEC_MAX_ALGS; i++) 7600Sstevel@tonic-gate if (authalgs[i] != NULL && ALG_VALID(authalgs[i])) 7610Sstevel@tonic-gate cmn_err(CE_PANIC, 7620Sstevel@tonic-gate "ah_register_out()! Missed #%d.\n", i); 7630Sstevel@tonic-gate #endif /* DEBUG */ 7640Sstevel@tonic-gate } 7650Sstevel@tonic-gate 7663448Sdh155122 mutex_exit(&ipss->ipsec_alg_lock); 7670Sstevel@tonic-gate 7680Sstevel@tonic-gate /* Now fill the restof the SADB_REGISTER message. */ 7690Sstevel@tonic-gate 7700Sstevel@tonic-gate samsg = (sadb_msg_t *)mp->b_cont->b_rptr; 7710Sstevel@tonic-gate samsg->sadb_msg_version = PF_KEY_V2; 7720Sstevel@tonic-gate samsg->sadb_msg_type = SADB_REGISTER; 7730Sstevel@tonic-gate samsg->sadb_msg_errno = 0; 7740Sstevel@tonic-gate samsg->sadb_msg_satype = SADB_SATYPE_AH; 7750Sstevel@tonic-gate samsg->sadb_msg_len = SADB_8TO64(allocsize); 7760Sstevel@tonic-gate samsg->sadb_msg_reserved = 0; 7770Sstevel@tonic-gate /* 7780Sstevel@tonic-gate * Assume caller has sufficient sequence/pid number info. If it's one 7790Sstevel@tonic-gate * from me over a new alg., I could give two hoots about sequence. 7800Sstevel@tonic-gate */ 7810Sstevel@tonic-gate samsg->sadb_msg_seq = sequence; 7820Sstevel@tonic-gate samsg->sadb_msg_pid = pid; 7830Sstevel@tonic-gate 7840Sstevel@tonic-gate if (allocsize > sizeof (*samsg)) { 7850Sstevel@tonic-gate sasupp = (sadb_supported_t *)(samsg + 1); 7860Sstevel@tonic-gate sasupp->sadb_supported_len = 7870Sstevel@tonic-gate SADB_8TO64(allocsize - sizeof (sadb_msg_t)); 7880Sstevel@tonic-gate sasupp->sadb_supported_exttype = SADB_EXT_SUPPORTED_AUTH; 7890Sstevel@tonic-gate sasupp->sadb_supported_reserved = 0; 7900Sstevel@tonic-gate } 7910Sstevel@tonic-gate 7923448Sdh155122 if (ahstack->ah_pfkey_q != NULL) 7933448Sdh155122 putnext(ahstack->ah_pfkey_q, mp); 7940Sstevel@tonic-gate else { 7950Sstevel@tonic-gate rc = B_FALSE; 7960Sstevel@tonic-gate freemsg(mp); 7970Sstevel@tonic-gate } 7980Sstevel@tonic-gate 7990Sstevel@tonic-gate return (rc); 8000Sstevel@tonic-gate } 8010Sstevel@tonic-gate 8020Sstevel@tonic-gate /* 8030Sstevel@tonic-gate * Invoked when the algorithm table changes. Causes SADB_REGISTER 8040Sstevel@tonic-gate * messages continaining the current list of algorithms to be 8050Sstevel@tonic-gate * sent up to the AH listeners. 8060Sstevel@tonic-gate */ 8070Sstevel@tonic-gate void 8083448Sdh155122 ipsecah_algs_changed(netstack_t *ns) 8090Sstevel@tonic-gate { 8103448Sdh155122 ipsecah_stack_t *ahstack = ns->netstack_ipsecah; 8113448Sdh155122 8120Sstevel@tonic-gate /* 8130Sstevel@tonic-gate * Time to send a PF_KEY SADB_REGISTER message to AH listeners 8140Sstevel@tonic-gate * everywhere. (The function itself checks for NULL ah_pfkey_q.) 8150Sstevel@tonic-gate */ 8163448Sdh155122 (void) ah_register_out(0, 0, 0, ahstack); 8170Sstevel@tonic-gate } 8180Sstevel@tonic-gate 8190Sstevel@tonic-gate /* 8200Sstevel@tonic-gate * Stub function that taskq_dispatch() invokes to take the mblk (in arg) 8210Sstevel@tonic-gate * and put() it into AH and STREAMS again. 8220Sstevel@tonic-gate */ 8230Sstevel@tonic-gate static void 8240Sstevel@tonic-gate inbound_task(void *arg) 8250Sstevel@tonic-gate { 8260Sstevel@tonic-gate ah_t *ah; 8270Sstevel@tonic-gate mblk_t *mp = (mblk_t *)arg; 8280Sstevel@tonic-gate ipsec_in_t *ii = (ipsec_in_t *)mp->b_rptr; 8290Sstevel@tonic-gate int ipsec_rc; 8303448Sdh155122 netstack_t *ns = ii->ipsec_in_ns; 8313448Sdh155122 ipsecah_stack_t *ahstack = ns->netstack_ipsecah; 8323448Sdh155122 8333448Sdh155122 ah2dbg(ahstack, ("in AH inbound_task")); 8343448Sdh155122 8353448Sdh155122 ASSERT(ahstack != NULL); 8363448Sdh155122 ah = ipsec_inbound_ah_sa(mp, ns); 8370Sstevel@tonic-gate if (ah == NULL) 8380Sstevel@tonic-gate return; 8390Sstevel@tonic-gate ASSERT(ii->ipsec_in_ah_sa != NULL); 8400Sstevel@tonic-gate ipsec_rc = ii->ipsec_in_ah_sa->ipsa_input_func(mp, ah); 8410Sstevel@tonic-gate if (ipsec_rc != IPSEC_STATUS_SUCCESS) 8420Sstevel@tonic-gate return; 8430Sstevel@tonic-gate ip_fanout_proto_again(mp, NULL, NULL, NULL); 8440Sstevel@tonic-gate } 8450Sstevel@tonic-gate 8460Sstevel@tonic-gate 8470Sstevel@tonic-gate /* 8480Sstevel@tonic-gate * Now that weak-key passed, actually ADD the security association, and 8490Sstevel@tonic-gate * send back a reply ADD message. 8500Sstevel@tonic-gate */ 8510Sstevel@tonic-gate static int 8523055Sdanmcd ah_add_sa_finish(mblk_t *mp, sadb_msg_t *samsg, keysock_in_t *ksi, 8533448Sdh155122 int *diagnostic, ipsecah_stack_t *ahstack) 8540Sstevel@tonic-gate { 8556668Smarkfen isaf_t *primary = NULL, *secondary, *inbound, *outbound; 8560Sstevel@tonic-gate sadb_sa_t *assoc = (sadb_sa_t *)ksi->ks_in_extv[SADB_EXT_SA]; 8570Sstevel@tonic-gate sadb_address_t *dstext = 8580Sstevel@tonic-gate (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_DST]; 8590Sstevel@tonic-gate struct sockaddr_in *dst; 8600Sstevel@tonic-gate struct sockaddr_in6 *dst6; 8610Sstevel@tonic-gate boolean_t is_ipv4, clone = B_FALSE, is_inbound = B_FALSE; 8620Sstevel@tonic-gate uint32_t *dstaddr; 8630Sstevel@tonic-gate ipsa_t *larval; 8640Sstevel@tonic-gate ipsacq_t *acqrec; 8650Sstevel@tonic-gate iacqf_t *acq_bucket; 8660Sstevel@tonic-gate mblk_t *acq_msgs = NULL; 8670Sstevel@tonic-gate mblk_t *lpkt; 8680Sstevel@tonic-gate int rc; 8690Sstevel@tonic-gate sadb_t *sp; 8700Sstevel@tonic-gate int outhash; 8713448Sdh155122 netstack_t *ns = ahstack->ipsecah_netstack; 8723448Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 8730Sstevel@tonic-gate 8740Sstevel@tonic-gate /* 8750Sstevel@tonic-gate * Locate the appropriate table(s). 8760Sstevel@tonic-gate */ 8770Sstevel@tonic-gate 8780Sstevel@tonic-gate dst = (struct sockaddr_in *)(dstext + 1); 8790Sstevel@tonic-gate dst6 = (struct sockaddr_in6 *)dst; 8800Sstevel@tonic-gate is_ipv4 = (dst->sin_family == AF_INET); 8810Sstevel@tonic-gate if (is_ipv4) { 8823448Sdh155122 sp = &ahstack->ah_sadb.s_v4; 8830Sstevel@tonic-gate dstaddr = (uint32_t *)(&dst->sin_addr); 884564Ssommerfe outhash = OUTBOUND_HASH_V4(sp, *(ipaddr_t *)dstaddr); 8850Sstevel@tonic-gate } else { 8860Sstevel@tonic-gate ASSERT(dst->sin_family == AF_INET6); 8873448Sdh155122 sp = &ahstack->ah_sadb.s_v6; 8880Sstevel@tonic-gate dstaddr = (uint32_t *)(&dst6->sin6_addr); 889564Ssommerfe outhash = OUTBOUND_HASH_V6(sp, *(in6_addr_t *)dstaddr); 8900Sstevel@tonic-gate } 8910Sstevel@tonic-gate 892564Ssommerfe inbound = INBOUND_BUCKET(sp, assoc->sadb_sa_spi); 893691Ssommerfe outbound = &sp->sdb_of[outhash]; 8946668Smarkfen /* 8956668Smarkfen * Use the direction flags provided by the KMD to determine 8966668Smarkfen * if the inbound or outbound table should be the primary 8976668Smarkfen * for this SA. If these flags were absent then make this 8986668Smarkfen * decision based on the addresses. 8996668Smarkfen */ 9006668Smarkfen if (assoc->sadb_sa_flags & IPSA_F_INBOUND) { 9010Sstevel@tonic-gate primary = inbound; 902691Ssommerfe secondary = outbound; 9036668Smarkfen is_inbound = B_TRUE; 9046668Smarkfen if (assoc->sadb_sa_flags & IPSA_F_OUTBOUND) 9056668Smarkfen clone = B_TRUE; 9066668Smarkfen } else { 9076668Smarkfen if (assoc->sadb_sa_flags & IPSA_F_OUTBOUND) { 9086668Smarkfen primary = outbound; 9096668Smarkfen secondary = inbound; 9106668Smarkfen } 9116668Smarkfen } 9126668Smarkfen 9136668Smarkfen if (primary == NULL) { 9146668Smarkfen /* 9156668Smarkfen * The KMD did not set a direction flag, determine which 9166668Smarkfen * table to insert the SA into based on addresses. 9176668Smarkfen */ 9186668Smarkfen switch (ksi->ks_in_dsttype) { 9196668Smarkfen case KS_IN_ADDR_MBCAST: 9206668Smarkfen clone = B_TRUE; /* All mcast SAs can be bidirectional */ 9216668Smarkfen assoc->sadb_sa_flags |= IPSA_F_OUTBOUND; 9226668Smarkfen /* FALLTHRU */ 9230Sstevel@tonic-gate /* 9240Sstevel@tonic-gate * If the source address is either one of mine, or unspecified 9250Sstevel@tonic-gate * (which is best summed up by saying "not 'not mine'"), 9260Sstevel@tonic-gate * then the association is potentially bi-directional, 9270Sstevel@tonic-gate * in that it can be used for inbound traffic and outbound 9280Sstevel@tonic-gate * traffic. The best example of such and SA is a multicast 9290Sstevel@tonic-gate * SA (which allows me to receive the outbound traffic). 9300Sstevel@tonic-gate */ 9316668Smarkfen case KS_IN_ADDR_ME: 9326668Smarkfen assoc->sadb_sa_flags |= IPSA_F_INBOUND; 9336668Smarkfen primary = inbound; 9346668Smarkfen secondary = outbound; 9356668Smarkfen if (ksi->ks_in_srctype != KS_IN_ADDR_NOTME) 9366668Smarkfen clone = B_TRUE; 9376668Smarkfen is_inbound = B_TRUE; 9386668Smarkfen break; 9390Sstevel@tonic-gate /* 9400Sstevel@tonic-gate * If the source address literally not mine (either 9410Sstevel@tonic-gate * unspecified or not mine), then this SA may have an 9420Sstevel@tonic-gate * address that WILL be mine after some configuration. 9430Sstevel@tonic-gate * We pay the price for this by making it a bi-directional 9440Sstevel@tonic-gate * SA. 9450Sstevel@tonic-gate */ 9466668Smarkfen case KS_IN_ADDR_NOTME: 9476668Smarkfen assoc->sadb_sa_flags |= IPSA_F_OUTBOUND; 9486668Smarkfen primary = outbound; 9496668Smarkfen secondary = inbound; 9506668Smarkfen if (ksi->ks_in_srctype != KS_IN_ADDR_ME) { 9516668Smarkfen assoc->sadb_sa_flags |= IPSA_F_INBOUND; 9526668Smarkfen clone = B_TRUE; 9536668Smarkfen } 9546668Smarkfen break; 9556668Smarkfen default: 9566668Smarkfen *diagnostic = SADB_X_DIAGNOSTIC_BAD_DST; 9576668Smarkfen return (EINVAL); 9586668Smarkfen } 9590Sstevel@tonic-gate } 9600Sstevel@tonic-gate 9610Sstevel@tonic-gate /* 9620Sstevel@tonic-gate * Find a ACQUIRE list entry if possible. If we've added an SA that 9630Sstevel@tonic-gate * suits the needs of an ACQUIRE list entry, we can eliminate the 9640Sstevel@tonic-gate * ACQUIRE list entry and transmit the enqueued packets. Use the 9650Sstevel@tonic-gate * high-bit of the sequence number to queue it. Key off destination 9660Sstevel@tonic-gate * addr, and change acqrec's state. 9670Sstevel@tonic-gate */ 9680Sstevel@tonic-gate 9690Sstevel@tonic-gate if (samsg->sadb_msg_seq & IACQF_LOWEST_SEQ) { 9700Sstevel@tonic-gate acq_bucket = &sp->sdb_acq[outhash]; 9710Sstevel@tonic-gate mutex_enter(&acq_bucket->iacqf_lock); 9720Sstevel@tonic-gate for (acqrec = acq_bucket->iacqf_ipsacq; acqrec != NULL; 9730Sstevel@tonic-gate acqrec = acqrec->ipsacq_next) { 9740Sstevel@tonic-gate mutex_enter(&acqrec->ipsacq_lock); 9750Sstevel@tonic-gate /* 9760Sstevel@tonic-gate * Q: I only check sequence. Should I check dst? 9770Sstevel@tonic-gate * A: Yes, check dest because those are the packets 9780Sstevel@tonic-gate * that are queued up. 9790Sstevel@tonic-gate */ 9800Sstevel@tonic-gate if (acqrec->ipsacq_seq == samsg->sadb_msg_seq && 9810Sstevel@tonic-gate IPSA_ARE_ADDR_EQUAL(dstaddr, 9824987Sdanmcd acqrec->ipsacq_dstaddr, acqrec->ipsacq_addrfam)) 9830Sstevel@tonic-gate break; 9840Sstevel@tonic-gate mutex_exit(&acqrec->ipsacq_lock); 9850Sstevel@tonic-gate } 9860Sstevel@tonic-gate if (acqrec != NULL) { 9870Sstevel@tonic-gate /* 9880Sstevel@tonic-gate * AHA! I found an ACQUIRE record for this SA. 9890Sstevel@tonic-gate * Grab the msg list, and free the acquire record. 9900Sstevel@tonic-gate * I already am holding the lock for this record, 9910Sstevel@tonic-gate * so all I have to do is free it. 9920Sstevel@tonic-gate */ 9930Sstevel@tonic-gate acq_msgs = acqrec->ipsacq_mp; 9940Sstevel@tonic-gate acqrec->ipsacq_mp = NULL; 9950Sstevel@tonic-gate mutex_exit(&acqrec->ipsacq_lock); 9963448Sdh155122 sadb_destroy_acquire(acqrec, ns); 9970Sstevel@tonic-gate } 9980Sstevel@tonic-gate mutex_exit(&acq_bucket->iacqf_lock); 9990Sstevel@tonic-gate } 10000Sstevel@tonic-gate 10010Sstevel@tonic-gate /* 10020Sstevel@tonic-gate * Find PF_KEY message, and see if I'm an update. If so, find entry 10030Sstevel@tonic-gate * in larval list (if there). 10040Sstevel@tonic-gate */ 10050Sstevel@tonic-gate 10060Sstevel@tonic-gate larval = NULL; 10070Sstevel@tonic-gate 10080Sstevel@tonic-gate if (samsg->sadb_msg_type == SADB_UPDATE) { 10090Sstevel@tonic-gate mutex_enter(&inbound->isaf_lock); 10100Sstevel@tonic-gate larval = ipsec_getassocbyspi(inbound, assoc->sadb_sa_spi, 10110Sstevel@tonic-gate ALL_ZEROES_PTR, dstaddr, dst->sin_family); 10120Sstevel@tonic-gate mutex_exit(&inbound->isaf_lock); 10130Sstevel@tonic-gate 10140Sstevel@tonic-gate if ((larval == NULL) || 10150Sstevel@tonic-gate (larval->ipsa_state != IPSA_STATE_LARVAL)) { 10166668Smarkfen *diagnostic = SADB_X_DIAGNOSTIC_SA_NOTFOUND; 10177110Sdanmcd if (larval != NULL) { 10187110Sdanmcd IPSA_REFRELE(larval); 10197110Sdanmcd } 10200Sstevel@tonic-gate ah0dbg(("Larval update, but larval disappeared.\n")); 10210Sstevel@tonic-gate return (ESRCH); 10220Sstevel@tonic-gate } /* Else sadb_common_add unlinks it for me! */ 10230Sstevel@tonic-gate } 10240Sstevel@tonic-gate 10250Sstevel@tonic-gate lpkt = NULL; 10260Sstevel@tonic-gate if (larval != NULL) 10270Sstevel@tonic-gate lpkt = sadb_clear_lpkt(larval); 10280Sstevel@tonic-gate 10293448Sdh155122 rc = sadb_common_add(ahstack->ah_sadb.s_ip_q, ahstack->ah_pfkey_q, mp, 10303448Sdh155122 samsg, ksi, primary, secondary, larval, clone, is_inbound, 10316668Smarkfen diagnostic, ns, &ahstack->ah_sadb); 10320Sstevel@tonic-gate 10330Sstevel@tonic-gate /* 10340Sstevel@tonic-gate * How much more stack will I create with all of these 10350Sstevel@tonic-gate * ah_inbound_* and ah_outbound_*() calls? 10360Sstevel@tonic-gate */ 10370Sstevel@tonic-gate 10380Sstevel@tonic-gate 10390Sstevel@tonic-gate if (rc == 0 && lpkt != NULL) 10400Sstevel@tonic-gate rc = !taskq_dispatch(ah_taskq, inbound_task, 10414987Sdanmcd (void *) lpkt, TQ_NOSLEEP); 10420Sstevel@tonic-gate 10430Sstevel@tonic-gate if (rc != 0) { 10440Sstevel@tonic-gate ip_drop_packet(lpkt, B_TRUE, NULL, NULL, 10453448Sdh155122 DROPPER(ipss, ipds_sadb_inlarval_timeout), 10463448Sdh155122 &ahstack->ah_dropper); 10470Sstevel@tonic-gate } 10480Sstevel@tonic-gate 10490Sstevel@tonic-gate while (acq_msgs != NULL) { 10500Sstevel@tonic-gate mblk_t *mp = acq_msgs; 10510Sstevel@tonic-gate 10520Sstevel@tonic-gate acq_msgs = acq_msgs->b_next; 10530Sstevel@tonic-gate mp->b_next = NULL; 10540Sstevel@tonic-gate if (rc == 0) { 10550Sstevel@tonic-gate ipsec_out_t *io = (ipsec_out_t *)mp->b_rptr; 10560Sstevel@tonic-gate 10573448Sdh155122 ASSERT(ahstack->ah_sadb.s_ip_q != NULL); 10580Sstevel@tonic-gate if (ipsec_outbound_sa(mp, IPPROTO_AH)) { 10590Sstevel@tonic-gate io->ipsec_out_ah_done = B_TRUE; 10600Sstevel@tonic-gate if (ah_outbound(mp) == IPSEC_STATUS_SUCCESS) { 10610Sstevel@tonic-gate ipha_t *ipha = (ipha_t *) 10620Sstevel@tonic-gate mp->b_cont->b_rptr; 10630Sstevel@tonic-gate if (is_ipv4) { 10640Sstevel@tonic-gate ip_wput_ipsec_out(NULL, mp, 10650Sstevel@tonic-gate ipha, NULL, NULL); 10660Sstevel@tonic-gate } else { 10670Sstevel@tonic-gate ip6_t *ip6h = (ip6_t *)ipha; 10680Sstevel@tonic-gate ip_wput_ipsec_out_v6(NULL, 10690Sstevel@tonic-gate mp, ip6h, NULL, NULL); 10700Sstevel@tonic-gate } 10710Sstevel@tonic-gate } 10720Sstevel@tonic-gate continue; 10730Sstevel@tonic-gate } 10740Sstevel@tonic-gate } 10753448Sdh155122 AH_BUMP_STAT(ahstack, out_discards); 10760Sstevel@tonic-gate ip_drop_packet(mp, B_FALSE, NULL, NULL, 10773448Sdh155122 DROPPER(ipss, ipds_sadb_acquire_timeout), 10783448Sdh155122 &ahstack->ah_dropper); 10790Sstevel@tonic-gate } 10800Sstevel@tonic-gate 10810Sstevel@tonic-gate return (rc); 10820Sstevel@tonic-gate } 10830Sstevel@tonic-gate 10840Sstevel@tonic-gate /* 10850Sstevel@tonic-gate * Add new AH security association. This may become a generic AH/ESP 10860Sstevel@tonic-gate * routine eventually. 10870Sstevel@tonic-gate */ 10880Sstevel@tonic-gate static int 10893448Sdh155122 ah_add_sa(mblk_t *mp, keysock_in_t *ksi, int *diagnostic, netstack_t *ns) 10900Sstevel@tonic-gate { 10910Sstevel@tonic-gate sadb_sa_t *assoc = (sadb_sa_t *)ksi->ks_in_extv[SADB_EXT_SA]; 10920Sstevel@tonic-gate sadb_address_t *srcext = 10930Sstevel@tonic-gate (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_SRC]; 10940Sstevel@tonic-gate sadb_address_t *dstext = 10950Sstevel@tonic-gate (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_DST]; 10963055Sdanmcd sadb_address_t *isrcext = 10973055Sdanmcd (sadb_address_t *)ksi->ks_in_extv[SADB_X_EXT_ADDRESS_INNER_SRC]; 10983055Sdanmcd sadb_address_t *idstext = 10993055Sdanmcd (sadb_address_t *)ksi->ks_in_extv[SADB_X_EXT_ADDRESS_INNER_DST]; 11000Sstevel@tonic-gate sadb_key_t *key = (sadb_key_t *)ksi->ks_in_extv[SADB_EXT_KEY_AUTH]; 11010Sstevel@tonic-gate struct sockaddr_in *src, *dst; 11020Sstevel@tonic-gate /* We don't need sockaddr_in6 for now. */ 11030Sstevel@tonic-gate sadb_lifetime_t *soft = 11040Sstevel@tonic-gate (sadb_lifetime_t *)ksi->ks_in_extv[SADB_EXT_LIFETIME_SOFT]; 11050Sstevel@tonic-gate sadb_lifetime_t *hard = 11060Sstevel@tonic-gate (sadb_lifetime_t *)ksi->ks_in_extv[SADB_EXT_LIFETIME_HARD]; 11077749SThejaswini.Singarajipura@Sun.COM sadb_lifetime_t *idle = 11087749SThejaswini.Singarajipura@Sun.COM (sadb_lifetime_t *)ksi->ks_in_extv[SADB_X_EXT_LIFETIME_IDLE]; 11090Sstevel@tonic-gate ipsec_alginfo_t *aalg; 11103448Sdh155122 ipsecah_stack_t *ahstack = ns->netstack_ipsecah; 11113448Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 11120Sstevel@tonic-gate 11130Sstevel@tonic-gate /* I need certain extensions present for an ADD message. */ 11140Sstevel@tonic-gate if (srcext == NULL) { 11150Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_MISSING_SRC; 11160Sstevel@tonic-gate return (EINVAL); 11170Sstevel@tonic-gate } 11180Sstevel@tonic-gate if (dstext == NULL) { 11190Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_MISSING_DST; 11200Sstevel@tonic-gate return (EINVAL); 11210Sstevel@tonic-gate } 11223055Sdanmcd if (isrcext == NULL && idstext != NULL) { 11233055Sdanmcd *diagnostic = SADB_X_DIAGNOSTIC_MISSING_INNER_SRC; 11243055Sdanmcd return (EINVAL); 11253055Sdanmcd } 11263055Sdanmcd if (isrcext != NULL && idstext == NULL) { 11273055Sdanmcd *diagnostic = SADB_X_DIAGNOSTIC_MISSING_INNER_DST; 11283055Sdanmcd return (EINVAL); 11293055Sdanmcd } 11300Sstevel@tonic-gate if (assoc == NULL) { 11310Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_MISSING_SA; 11320Sstevel@tonic-gate return (EINVAL); 11330Sstevel@tonic-gate } 11340Sstevel@tonic-gate if (key == NULL) { 11350Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_MISSING_AKEY; 11360Sstevel@tonic-gate return (EINVAL); 11370Sstevel@tonic-gate } 11380Sstevel@tonic-gate 11390Sstevel@tonic-gate src = (struct sockaddr_in *)(srcext + 1); 11400Sstevel@tonic-gate dst = (struct sockaddr_in *)(dstext + 1); 11410Sstevel@tonic-gate 11420Sstevel@tonic-gate /* Sundry ADD-specific reality checks. */ 11430Sstevel@tonic-gate /* XXX STATS : Logging/stats here? */ 11440Sstevel@tonic-gate 11457749SThejaswini.Singarajipura@Sun.COM if ((assoc->sadb_sa_state != SADB_SASTATE_MATURE) && 11467749SThejaswini.Singarajipura@Sun.COM (assoc->sadb_sa_state != SADB_X_SASTATE_ACTIVE_ELSEWHERE)) { 11470Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_BAD_SASTATE; 11480Sstevel@tonic-gate return (EINVAL); 11490Sstevel@tonic-gate } 11500Sstevel@tonic-gate if (assoc->sadb_sa_encrypt != SADB_EALG_NONE) { 11510Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_ENCR_NOTSUPP; 11520Sstevel@tonic-gate return (EINVAL); 11530Sstevel@tonic-gate } 11547110Sdanmcd if (assoc->sadb_sa_flags & ~ahstack->ah_sadb.s_addflags) { 11550Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_BAD_SAFLAGS; 11560Sstevel@tonic-gate return (EINVAL); 11570Sstevel@tonic-gate } 11587749SThejaswini.Singarajipura@Sun.COM if ((*diagnostic = sadb_hardsoftchk(hard, soft, idle)) != 0) 11590Sstevel@tonic-gate return (EINVAL); 11600Sstevel@tonic-gate 11613055Sdanmcd ASSERT(src->sin_family == dst->sin_family); 11620Sstevel@tonic-gate 11630Sstevel@tonic-gate /* Stuff I don't support, for now. XXX Diagnostic? */ 11640Sstevel@tonic-gate if (ksi->ks_in_extv[SADB_EXT_LIFETIME_CURRENT] != NULL || 11650Sstevel@tonic-gate ksi->ks_in_extv[SADB_EXT_SENSITIVITY] != NULL) 11660Sstevel@tonic-gate return (EOPNOTSUPP); 11670Sstevel@tonic-gate 11680Sstevel@tonic-gate /* 11690Sstevel@tonic-gate * XXX Policy : I'm not checking identities or sensitivity 11700Sstevel@tonic-gate * labels at this time, but if I did, I'd do them here, before I sent 11710Sstevel@tonic-gate * the weak key check up to the algorithm. 11720Sstevel@tonic-gate */ 11730Sstevel@tonic-gate 11740Sstevel@tonic-gate /* verify that there is a mapping for the specified algorithm */ 11753448Sdh155122 mutex_enter(&ipss->ipsec_alg_lock); 11763448Sdh155122 aalg = ipss->ipsec_alglists[IPSEC_ALG_AUTH][assoc->sadb_sa_auth]; 11770Sstevel@tonic-gate if (aalg == NULL || !ALG_VALID(aalg)) { 11783448Sdh155122 mutex_exit(&ipss->ipsec_alg_lock); 11793448Sdh155122 ah1dbg(ahstack, ("Couldn't find auth alg #%d.\n", 11804987Sdanmcd assoc->sadb_sa_auth)); 11810Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_BAD_AALG; 11820Sstevel@tonic-gate return (EINVAL); 11830Sstevel@tonic-gate } 11840Sstevel@tonic-gate ASSERT(aalg->alg_mech_type != CRYPTO_MECHANISM_INVALID); 11850Sstevel@tonic-gate 11860Sstevel@tonic-gate /* sanity check key sizes */ 11870Sstevel@tonic-gate if (!ipsec_valid_key_size(key->sadb_key_bits, aalg)) { 11883448Sdh155122 mutex_exit(&ipss->ipsec_alg_lock); 11890Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_BAD_AKEYBITS; 11900Sstevel@tonic-gate return (EINVAL); 11910Sstevel@tonic-gate } 11920Sstevel@tonic-gate 11930Sstevel@tonic-gate /* check key and fix parity if needed */ 11940Sstevel@tonic-gate if (ipsec_check_key(aalg->alg_mech_type, key, B_TRUE, 11950Sstevel@tonic-gate diagnostic) != 0) { 11963448Sdh155122 mutex_exit(&ipss->ipsec_alg_lock); 11970Sstevel@tonic-gate return (EINVAL); 11980Sstevel@tonic-gate } 11990Sstevel@tonic-gate 12003448Sdh155122 mutex_exit(&ipss->ipsec_alg_lock); 12010Sstevel@tonic-gate 12023055Sdanmcd return (ah_add_sa_finish(mp, (sadb_msg_t *)mp->b_cont->b_rptr, ksi, 12034987Sdanmcd diagnostic, ahstack)); 12040Sstevel@tonic-gate } 12050Sstevel@tonic-gate 12060Sstevel@tonic-gate /* 12070Sstevel@tonic-gate * Update a security association. Updates come in two varieties. The first 12080Sstevel@tonic-gate * is an update of lifetimes on a non-larval SA. The second is an update of 12090Sstevel@tonic-gate * a larval SA, which ends up looking a lot more like an add. 12100Sstevel@tonic-gate */ 12110Sstevel@tonic-gate static int 12123448Sdh155122 ah_update_sa(mblk_t *mp, keysock_in_t *ksi, int *diagnostic, 12136668Smarkfen ipsecah_stack_t *ahstack, uint8_t sadb_msg_type) 12140Sstevel@tonic-gate { 12157749SThejaswini.Singarajipura@Sun.COM sadb_sa_t *assoc = (sadb_sa_t *)ksi->ks_in_extv[SADB_EXT_SA]; 12160Sstevel@tonic-gate sadb_address_t *dstext = 12170Sstevel@tonic-gate (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_DST]; 12187749SThejaswini.Singarajipura@Sun.COM mblk_t *buf_pkt; 12197749SThejaswini.Singarajipura@Sun.COM int rcode; 12200Sstevel@tonic-gate 12210Sstevel@tonic-gate if (dstext == NULL) { 12220Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_MISSING_DST; 12230Sstevel@tonic-gate return (EINVAL); 12240Sstevel@tonic-gate } 12257749SThejaswini.Singarajipura@Sun.COM 12267749SThejaswini.Singarajipura@Sun.COM rcode = sadb_update_sa(mp, ksi, &buf_pkt, &ahstack->ah_sadb, 12277749SThejaswini.Singarajipura@Sun.COM diagnostic, ahstack->ah_pfkey_q, ah_add_sa, 12287749SThejaswini.Singarajipura@Sun.COM ahstack->ipsecah_netstack, sadb_msg_type); 12297749SThejaswini.Singarajipura@Sun.COM 12307749SThejaswini.Singarajipura@Sun.COM if ((assoc->sadb_sa_state != SADB_X_SASTATE_ACTIVE) || 12317749SThejaswini.Singarajipura@Sun.COM (rcode != 0)) { 12327749SThejaswini.Singarajipura@Sun.COM return (rcode); 12337749SThejaswini.Singarajipura@Sun.COM } 12347749SThejaswini.Singarajipura@Sun.COM 12357749SThejaswini.Singarajipura@Sun.COM HANDLE_BUF_PKT(ah_taskq, 12367749SThejaswini.Singarajipura@Sun.COM ahstack->ipsecah_netstack->netstack_ipsec, ahstack->ah_dropper, 12377749SThejaswini.Singarajipura@Sun.COM buf_pkt); 12387749SThejaswini.Singarajipura@Sun.COM 12397749SThejaswini.Singarajipura@Sun.COM return (rcode); 12400Sstevel@tonic-gate } 12410Sstevel@tonic-gate 12420Sstevel@tonic-gate /* 12430Sstevel@tonic-gate * Delete a security association. This is REALLY likely to be code common to 12440Sstevel@tonic-gate * both AH and ESP. Find the association, then unlink it. 12450Sstevel@tonic-gate */ 12460Sstevel@tonic-gate static int 12473448Sdh155122 ah_del_sa(mblk_t *mp, keysock_in_t *ksi, int *diagnostic, 12486668Smarkfen ipsecah_stack_t *ahstack, uint8_t sadb_msg_type) 12490Sstevel@tonic-gate { 12500Sstevel@tonic-gate 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]; 12530Sstevel@tonic-gate sadb_address_t *srcext = 12540Sstevel@tonic-gate (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_SRC]; 12550Sstevel@tonic-gate struct sockaddr_in *sin; 12560Sstevel@tonic-gate 12570Sstevel@tonic-gate if (assoc == NULL) { 12580Sstevel@tonic-gate if (dstext != NULL) 12590Sstevel@tonic-gate sin = (struct sockaddr_in *)(dstext + 1); 12600Sstevel@tonic-gate else if (srcext != NULL) 12610Sstevel@tonic-gate sin = (struct sockaddr_in *)(srcext + 1); 12620Sstevel@tonic-gate else { 12630Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_MISSING_SA; 12640Sstevel@tonic-gate return (EINVAL); 12650Sstevel@tonic-gate } 12663055Sdanmcd return (sadb_purge_sa(mp, ksi, 12673448Sdh155122 (sin->sin_family == AF_INET6) ? &ahstack->ah_sadb.s_v6 : 12683448Sdh155122 &ahstack->ah_sadb.s_v4, 12693448Sdh155122 ahstack->ah_pfkey_q, ahstack->ah_sadb.s_ip_q)); 12700Sstevel@tonic-gate } 12710Sstevel@tonic-gate 12726668Smarkfen return (sadb_delget_sa(mp, ksi, &ahstack->ah_sadb, diagnostic, 12736668Smarkfen ahstack->ah_pfkey_q, sadb_msg_type)); 12740Sstevel@tonic-gate } 12750Sstevel@tonic-gate 12760Sstevel@tonic-gate /* 12770Sstevel@tonic-gate * Convert the entire contents of all of AH's SA tables into PF_KEY SADB_DUMP 12780Sstevel@tonic-gate * messages. 12790Sstevel@tonic-gate */ 12800Sstevel@tonic-gate static void 12813448Sdh155122 ah_dump(mblk_t *mp, keysock_in_t *ksi, ipsecah_stack_t *ahstack) 12820Sstevel@tonic-gate { 12830Sstevel@tonic-gate int error; 12840Sstevel@tonic-gate sadb_msg_t *samsg; 12850Sstevel@tonic-gate 12860Sstevel@tonic-gate /* 12870Sstevel@tonic-gate * Dump each fanout, bailing if error is non-zero. 12880Sstevel@tonic-gate */ 12890Sstevel@tonic-gate 12907749SThejaswini.Singarajipura@Sun.COM error = sadb_dump(ahstack->ah_pfkey_q, mp, ksi, &ahstack->ah_sadb.s_v4); 12910Sstevel@tonic-gate if (error != 0) 12920Sstevel@tonic-gate goto bail; 12930Sstevel@tonic-gate 12947749SThejaswini.Singarajipura@Sun.COM error = sadb_dump(ahstack->ah_pfkey_q, mp, ksi, &ahstack->ah_sadb.s_v6); 12950Sstevel@tonic-gate bail: 12960Sstevel@tonic-gate ASSERT(mp->b_cont != NULL); 12970Sstevel@tonic-gate samsg = (sadb_msg_t *)mp->b_cont->b_rptr; 12980Sstevel@tonic-gate samsg->sadb_msg_errno = (uint8_t)error; 12993448Sdh155122 sadb_pfkey_echo(ahstack->ah_pfkey_q, mp, 13003448Sdh155122 (sadb_msg_t *)mp->b_cont->b_rptr, ksi, NULL); 13010Sstevel@tonic-gate } 13020Sstevel@tonic-gate 13030Sstevel@tonic-gate /* 13043055Sdanmcd * First-cut reality check for an inbound PF_KEY message. 13053055Sdanmcd */ 13063055Sdanmcd static boolean_t 13073448Sdh155122 ah_pfkey_reality_failures(mblk_t *mp, keysock_in_t *ksi, 13083448Sdh155122 ipsecah_stack_t *ahstack) 13093055Sdanmcd { 13103055Sdanmcd int diagnostic; 13113055Sdanmcd 13123055Sdanmcd if (mp->b_cont == NULL) { 13133055Sdanmcd freemsg(mp); 13143055Sdanmcd return (B_TRUE); 13153055Sdanmcd } 13163055Sdanmcd 13173055Sdanmcd if (ksi->ks_in_extv[SADB_EXT_KEY_ENCRYPT] != NULL) { 13183055Sdanmcd diagnostic = SADB_X_DIAGNOSTIC_EKEY_PRESENT; 13193055Sdanmcd goto badmsg; 13203055Sdanmcd } 13213055Sdanmcd if (ksi->ks_in_extv[SADB_EXT_PROPOSAL] != NULL) { 13223055Sdanmcd diagnostic = SADB_X_DIAGNOSTIC_PROP_PRESENT; 13233055Sdanmcd goto badmsg; 13243055Sdanmcd } 13253055Sdanmcd if (ksi->ks_in_extv[SADB_EXT_SUPPORTED_AUTH] != NULL || 13263055Sdanmcd ksi->ks_in_extv[SADB_EXT_SUPPORTED_ENCRYPT] != NULL) { 13273055Sdanmcd diagnostic = SADB_X_DIAGNOSTIC_SUPP_PRESENT; 13283055Sdanmcd goto badmsg; 13293055Sdanmcd } 13303055Sdanmcd return (B_FALSE); /* False ==> no failures */ 13313055Sdanmcd 13323055Sdanmcd badmsg: 13333448Sdh155122 sadb_pfkey_error(ahstack->ah_pfkey_q, mp, EINVAL, 13343448Sdh155122 diagnostic, ksi->ks_in_serial); 13353055Sdanmcd return (B_TRUE); /* True ==> failures */ 13363055Sdanmcd } 13373055Sdanmcd 13383055Sdanmcd /* 13390Sstevel@tonic-gate * AH parsing of PF_KEY messages. Keysock did most of the really silly 13400Sstevel@tonic-gate * error cases. What I receive is a fully-formed, syntactically legal 13410Sstevel@tonic-gate * PF_KEY message. I then need to check semantics... 13420Sstevel@tonic-gate * 13430Sstevel@tonic-gate * This code may become common to AH and ESP. Stay tuned. 13440Sstevel@tonic-gate * 13450Sstevel@tonic-gate * I also make the assumption that db_ref's are cool. If this assumption 13460Sstevel@tonic-gate * is wrong, this means that someone other than keysock or me has been 13470Sstevel@tonic-gate * mucking with PF_KEY messages. 13480Sstevel@tonic-gate */ 13490Sstevel@tonic-gate static void 13503448Sdh155122 ah_parse_pfkey(mblk_t *mp, ipsecah_stack_t *ahstack) 13510Sstevel@tonic-gate { 13520Sstevel@tonic-gate mblk_t *msg = mp->b_cont; 13530Sstevel@tonic-gate sadb_msg_t *samsg; 13540Sstevel@tonic-gate keysock_in_t *ksi; 13550Sstevel@tonic-gate int error; 13560Sstevel@tonic-gate int diagnostic = SADB_X_DIAGNOSTIC_NONE; 13570Sstevel@tonic-gate 13580Sstevel@tonic-gate ASSERT(msg != NULL); 13593448Sdh155122 13600Sstevel@tonic-gate samsg = (sadb_msg_t *)msg->b_rptr; 13610Sstevel@tonic-gate ksi = (keysock_in_t *)mp->b_rptr; 13620Sstevel@tonic-gate 13630Sstevel@tonic-gate /* 13640Sstevel@tonic-gate * If applicable, convert unspecified AF_INET6 to unspecified 13650Sstevel@tonic-gate * AF_INET. 13660Sstevel@tonic-gate */ 13673448Sdh155122 if (!sadb_addrfix(ksi, ahstack->ah_pfkey_q, mp, 13683448Sdh155122 ahstack->ipsecah_netstack) || 13693448Sdh155122 ah_pfkey_reality_failures(mp, ksi, ahstack)) { 13703055Sdanmcd return; 13713055Sdanmcd } 13720Sstevel@tonic-gate 13730Sstevel@tonic-gate switch (samsg->sadb_msg_type) { 13740Sstevel@tonic-gate case SADB_ADD: 13753448Sdh155122 error = ah_add_sa(mp, ksi, &diagnostic, 13763448Sdh155122 ahstack->ipsecah_netstack); 13770Sstevel@tonic-gate if (error != 0) { 13783448Sdh155122 sadb_pfkey_error(ahstack->ah_pfkey_q, mp, error, 13793448Sdh155122 diagnostic, ksi->ks_in_serial); 13800Sstevel@tonic-gate } 13810Sstevel@tonic-gate /* else ah_add_sa() took care of things. */ 13820Sstevel@tonic-gate break; 13830Sstevel@tonic-gate case SADB_DELETE: 13846668Smarkfen case SADB_X_DELPAIR: 13857749SThejaswini.Singarajipura@Sun.COM case SADB_X_DELPAIR_STATE: 13866668Smarkfen error = ah_del_sa(mp, ksi, &diagnostic, ahstack, 13876668Smarkfen samsg->sadb_msg_type); 13880Sstevel@tonic-gate if (error != 0) { 13893448Sdh155122 sadb_pfkey_error(ahstack->ah_pfkey_q, mp, error, 13903448Sdh155122 diagnostic, ksi->ks_in_serial); 13910Sstevel@tonic-gate } 13920Sstevel@tonic-gate /* Else ah_del_sa() took care of things. */ 13930Sstevel@tonic-gate break; 13940Sstevel@tonic-gate case SADB_GET: 13956668Smarkfen error = sadb_delget_sa(mp, ksi, &ahstack->ah_sadb, &diagnostic, 13966668Smarkfen ahstack->ah_pfkey_q, samsg->sadb_msg_type); 13970Sstevel@tonic-gate if (error != 0) { 13983448Sdh155122 sadb_pfkey_error(ahstack->ah_pfkey_q, mp, error, 13993448Sdh155122 diagnostic, ksi->ks_in_serial); 14000Sstevel@tonic-gate } 14010Sstevel@tonic-gate /* Else sadb_get_sa() took care of things. */ 14020Sstevel@tonic-gate break; 14030Sstevel@tonic-gate case SADB_FLUSH: 14043448Sdh155122 sadbp_flush(&ahstack->ah_sadb, ahstack->ipsecah_netstack); 14053448Sdh155122 sadb_pfkey_echo(ahstack->ah_pfkey_q, mp, samsg, ksi, NULL); 14060Sstevel@tonic-gate break; 14070Sstevel@tonic-gate case SADB_REGISTER: 14080Sstevel@tonic-gate /* 14090Sstevel@tonic-gate * Hmmm, let's do it! Check for extensions (there should 14100Sstevel@tonic-gate * be none), extract the fields, call ah_register_out(), 14110Sstevel@tonic-gate * then either free or report an error. 14120Sstevel@tonic-gate * 14130Sstevel@tonic-gate * Keysock takes care of the PF_KEY bookkeeping for this. 14140Sstevel@tonic-gate */ 14150Sstevel@tonic-gate if (ah_register_out(samsg->sadb_msg_seq, samsg->sadb_msg_pid, 14163448Sdh155122 ksi->ks_in_serial, ahstack)) { 14170Sstevel@tonic-gate freemsg(mp); 14180Sstevel@tonic-gate } else { 14190Sstevel@tonic-gate /* 14200Sstevel@tonic-gate * Only way this path hits is if there is a memory 14210Sstevel@tonic-gate * failure. It will not return B_FALSE because of 14220Sstevel@tonic-gate * lack of ah_pfkey_q if I am in wput(). 14230Sstevel@tonic-gate */ 14243448Sdh155122 sadb_pfkey_error(ahstack->ah_pfkey_q, mp, ENOMEM, 14253448Sdh155122 diagnostic, ksi->ks_in_serial); 14260Sstevel@tonic-gate } 14270Sstevel@tonic-gate break; 14280Sstevel@tonic-gate case SADB_UPDATE: 14296668Smarkfen case SADB_X_UPDATEPAIR: 14300Sstevel@tonic-gate /* 14310Sstevel@tonic-gate * Find a larval, if not there, find a full one and get 14320Sstevel@tonic-gate * strict. 14330Sstevel@tonic-gate */ 14346668Smarkfen error = ah_update_sa(mp, ksi, &diagnostic, ahstack, 14356668Smarkfen samsg->sadb_msg_type); 14360Sstevel@tonic-gate if (error != 0) { 14373448Sdh155122 sadb_pfkey_error(ahstack->ah_pfkey_q, mp, error, 14383448Sdh155122 diagnostic, ksi->ks_in_serial); 14390Sstevel@tonic-gate } 14400Sstevel@tonic-gate /* else ah_update_sa() took care of things. */ 14410Sstevel@tonic-gate break; 14420Sstevel@tonic-gate case SADB_GETSPI: 14430Sstevel@tonic-gate /* 14440Sstevel@tonic-gate * Reserve a new larval entry. 14450Sstevel@tonic-gate */ 14463448Sdh155122 ah_getspi(mp, ksi, ahstack); 14470Sstevel@tonic-gate break; 14480Sstevel@tonic-gate case SADB_ACQUIRE: 14490Sstevel@tonic-gate /* 14500Sstevel@tonic-gate * Find larval and/or ACQUIRE record and kill it (them), I'm 14510Sstevel@tonic-gate * most likely an error. Inbound ACQUIRE messages should only 14520Sstevel@tonic-gate * have the base header. 14530Sstevel@tonic-gate */ 14543448Sdh155122 sadb_in_acquire(samsg, &ahstack->ah_sadb, ahstack->ah_pfkey_q, 14553448Sdh155122 ahstack->ipsecah_netstack); 14560Sstevel@tonic-gate freemsg(mp); 14570Sstevel@tonic-gate break; 14580Sstevel@tonic-gate case SADB_DUMP: 14590Sstevel@tonic-gate /* 14600Sstevel@tonic-gate * Dump all entries. 14610Sstevel@tonic-gate */ 14623448Sdh155122 ah_dump(mp, ksi, ahstack); 14630Sstevel@tonic-gate /* ah_dump will take care of the return message, etc. */ 14640Sstevel@tonic-gate break; 14650Sstevel@tonic-gate case SADB_EXPIRE: 14660Sstevel@tonic-gate /* Should never reach me. */ 14673448Sdh155122 sadb_pfkey_error(ahstack->ah_pfkey_q, mp, EOPNOTSUPP, 14683448Sdh155122 diagnostic, ksi->ks_in_serial); 14690Sstevel@tonic-gate break; 14700Sstevel@tonic-gate default: 14713448Sdh155122 sadb_pfkey_error(ahstack->ah_pfkey_q, mp, EINVAL, 14720Sstevel@tonic-gate SADB_X_DIAGNOSTIC_UNKNOWN_MSG, ksi->ks_in_serial); 14730Sstevel@tonic-gate break; 14740Sstevel@tonic-gate } 14750Sstevel@tonic-gate } 14760Sstevel@tonic-gate 14770Sstevel@tonic-gate /* 14780Sstevel@tonic-gate * Handle case where PF_KEY says it can't find a keysock for one of my 14790Sstevel@tonic-gate * ACQUIRE messages. 14800Sstevel@tonic-gate */ 14810Sstevel@tonic-gate static void 14823448Sdh155122 ah_keysock_no_socket(mblk_t *mp, ipsecah_stack_t *ahstack) 14830Sstevel@tonic-gate { 14840Sstevel@tonic-gate sadb_msg_t *samsg; 14850Sstevel@tonic-gate keysock_out_err_t *kse = (keysock_out_err_t *)mp->b_rptr; 14860Sstevel@tonic-gate 14870Sstevel@tonic-gate if (mp->b_cont == NULL) { 14880Sstevel@tonic-gate freemsg(mp); 14890Sstevel@tonic-gate return; 14900Sstevel@tonic-gate } 14910Sstevel@tonic-gate samsg = (sadb_msg_t *)mp->b_cont->b_rptr; 14920Sstevel@tonic-gate 14930Sstevel@tonic-gate /* 14940Sstevel@tonic-gate * If keysock can't find any registered, delete the acquire record 14950Sstevel@tonic-gate * immediately, and handle errors. 14960Sstevel@tonic-gate */ 14970Sstevel@tonic-gate if (samsg->sadb_msg_type == SADB_ACQUIRE) { 14980Sstevel@tonic-gate samsg->sadb_msg_errno = kse->ks_err_errno; 14990Sstevel@tonic-gate samsg->sadb_msg_len = SADB_8TO64(sizeof (*samsg)); 15000Sstevel@tonic-gate /* 15010Sstevel@tonic-gate * Use the write-side of the ah_pfkey_q, in case there is 15023448Sdh155122 * no ahstack->ah_sadb.s_ip_q. 15030Sstevel@tonic-gate */ 15043448Sdh155122 sadb_in_acquire(samsg, &ahstack->ah_sadb, 15053448Sdh155122 WR(ahstack->ah_pfkey_q), ahstack->ipsecah_netstack); 15060Sstevel@tonic-gate } 15070Sstevel@tonic-gate 15080Sstevel@tonic-gate freemsg(mp); 15090Sstevel@tonic-gate } 15100Sstevel@tonic-gate 15110Sstevel@tonic-gate /* 15120Sstevel@tonic-gate * AH module write put routine. 15130Sstevel@tonic-gate */ 15140Sstevel@tonic-gate static void 15150Sstevel@tonic-gate ipsecah_wput(queue_t *q, mblk_t *mp) 15160Sstevel@tonic-gate { 15170Sstevel@tonic-gate ipsec_info_t *ii; 15180Sstevel@tonic-gate struct iocblk *iocp; 15193448Sdh155122 ipsecah_stack_t *ahstack = (ipsecah_stack_t *)q->q_ptr; 15203448Sdh155122 15213448Sdh155122 ah3dbg(ahstack, ("In ah_wput().\n")); 15220Sstevel@tonic-gate 15230Sstevel@tonic-gate /* NOTE: Each case must take care of freeing or passing mp. */ 15240Sstevel@tonic-gate switch (mp->b_datap->db_type) { 15250Sstevel@tonic-gate case M_CTL: 15260Sstevel@tonic-gate if ((mp->b_wptr - mp->b_rptr) < sizeof (ipsec_info_t)) { 15270Sstevel@tonic-gate /* Not big enough message. */ 15280Sstevel@tonic-gate freemsg(mp); 15290Sstevel@tonic-gate break; 15300Sstevel@tonic-gate } 15310Sstevel@tonic-gate ii = (ipsec_info_t *)mp->b_rptr; 15320Sstevel@tonic-gate 15330Sstevel@tonic-gate switch (ii->ipsec_info_type) { 15340Sstevel@tonic-gate case KEYSOCK_OUT_ERR: 15353448Sdh155122 ah1dbg(ahstack, ("Got KEYSOCK_OUT_ERR message.\n")); 15363448Sdh155122 ah_keysock_no_socket(mp, ahstack); 15370Sstevel@tonic-gate break; 15380Sstevel@tonic-gate case KEYSOCK_IN: 15393448Sdh155122 AH_BUMP_STAT(ahstack, keysock_in); 15403448Sdh155122 ah3dbg(ahstack, ("Got KEYSOCK_IN message.\n")); 15413055Sdanmcd 15423055Sdanmcd /* Parse the message. */ 15433448Sdh155122 ah_parse_pfkey(mp, ahstack); 15440Sstevel@tonic-gate break; 15450Sstevel@tonic-gate case KEYSOCK_HELLO: 15463448Sdh155122 sadb_keysock_hello(&ahstack->ah_pfkey_q, q, mp, 15473448Sdh155122 ah_ager, (void *)ahstack, &ahstack->ah_event, 15483448Sdh155122 SADB_SATYPE_AH); 15490Sstevel@tonic-gate break; 15500Sstevel@tonic-gate default: 15513448Sdh155122 ah1dbg(ahstack, ("Got M_CTL from above of 0x%x.\n", 15520Sstevel@tonic-gate ii->ipsec_info_type)); 15530Sstevel@tonic-gate freemsg(mp); 15540Sstevel@tonic-gate break; 15550Sstevel@tonic-gate } 15560Sstevel@tonic-gate break; 15570Sstevel@tonic-gate case M_IOCTL: 15580Sstevel@tonic-gate iocp = (struct iocblk *)mp->b_rptr; 15590Sstevel@tonic-gate switch (iocp->ioc_cmd) { 15600Sstevel@tonic-gate case ND_SET: 15610Sstevel@tonic-gate case ND_GET: 15623448Sdh155122 if (nd_getset(q, ahstack->ipsecah_g_nd, mp)) { 15630Sstevel@tonic-gate qreply(q, mp); 15640Sstevel@tonic-gate return; 15650Sstevel@tonic-gate } else { 15660Sstevel@tonic-gate iocp->ioc_error = ENOENT; 15670Sstevel@tonic-gate } 15680Sstevel@tonic-gate /* FALLTHRU */ 15690Sstevel@tonic-gate default: 15700Sstevel@tonic-gate /* We really don't support any other ioctls, do we? */ 15710Sstevel@tonic-gate 15720Sstevel@tonic-gate /* Return EINVAL */ 15730Sstevel@tonic-gate if (iocp->ioc_error != ENOENT) 15740Sstevel@tonic-gate iocp->ioc_error = EINVAL; 15750Sstevel@tonic-gate iocp->ioc_count = 0; 15760Sstevel@tonic-gate mp->b_datap->db_type = M_IOCACK; 15770Sstevel@tonic-gate qreply(q, mp); 15780Sstevel@tonic-gate return; 15790Sstevel@tonic-gate } 15800Sstevel@tonic-gate default: 15813448Sdh155122 ah3dbg(ahstack, 15823448Sdh155122 ("Got default message, type %d, passing to IP.\n", 15830Sstevel@tonic-gate mp->b_datap->db_type)); 15840Sstevel@tonic-gate putnext(q, mp); 15850Sstevel@tonic-gate } 15860Sstevel@tonic-gate } 15870Sstevel@tonic-gate 15880Sstevel@tonic-gate /* 15890Sstevel@tonic-gate * Updating use times can be tricky business if the ipsa_haspeer flag is 15900Sstevel@tonic-gate * set. This function is called once in an SA's lifetime. 15910Sstevel@tonic-gate * 15920Sstevel@tonic-gate * Caller has to REFRELE "assoc" which is passed in. This function has 15930Sstevel@tonic-gate * to REFRELE any peer SA that is obtained. 15940Sstevel@tonic-gate */ 15950Sstevel@tonic-gate static void 15960Sstevel@tonic-gate ah_set_usetime(ipsa_t *assoc, boolean_t inbound) 15970Sstevel@tonic-gate { 15980Sstevel@tonic-gate ipsa_t *inassoc, *outassoc; 15990Sstevel@tonic-gate isaf_t *bucket; 16000Sstevel@tonic-gate sadb_t *sp; 16010Sstevel@tonic-gate int outhash; 16020Sstevel@tonic-gate boolean_t isv6; 16033448Sdh155122 netstack_t *ns = assoc->ipsa_netstack; 16043448Sdh155122 ipsecah_stack_t *ahstack = ns->netstack_ipsecah; 16050Sstevel@tonic-gate 16060Sstevel@tonic-gate /* No peer? No problem! */ 16070Sstevel@tonic-gate if (!assoc->ipsa_haspeer) { 16080Sstevel@tonic-gate sadb_set_usetime(assoc); 16090Sstevel@tonic-gate return; 16100Sstevel@tonic-gate } 16110Sstevel@tonic-gate 16120Sstevel@tonic-gate /* 16130Sstevel@tonic-gate * Otherwise, we want to grab both the original assoc and its peer. 16140Sstevel@tonic-gate * There might be a race for this, but if it's a real race, the times 16150Sstevel@tonic-gate * will be out-of-synch by at most a second, and since our time 16160Sstevel@tonic-gate * granularity is a second, this won't be a problem. 16170Sstevel@tonic-gate * 16180Sstevel@tonic-gate * If we need tight synchronization on the peer SA, then we need to 16190Sstevel@tonic-gate * reconsider. 16200Sstevel@tonic-gate */ 16210Sstevel@tonic-gate 16220Sstevel@tonic-gate /* Use address family to select IPv6/IPv4 */ 16230Sstevel@tonic-gate isv6 = (assoc->ipsa_addrfam == AF_INET6); 16240Sstevel@tonic-gate if (isv6) { 16253448Sdh155122 sp = &ahstack->ah_sadb.s_v6; 16260Sstevel@tonic-gate } else { 16273448Sdh155122 sp = &ahstack->ah_sadb.s_v4; 16280Sstevel@tonic-gate ASSERT(assoc->ipsa_addrfam == AF_INET); 16290Sstevel@tonic-gate } 16300Sstevel@tonic-gate if (inbound) { 16310Sstevel@tonic-gate inassoc = assoc; 16320Sstevel@tonic-gate if (isv6) 16334987Sdanmcd outhash = OUTBOUND_HASH_V6(sp, 16344987Sdanmcd *((in6_addr_t *)&inassoc->ipsa_dstaddr)); 16350Sstevel@tonic-gate else 16364987Sdanmcd outhash = OUTBOUND_HASH_V4(sp, 16374987Sdanmcd *((ipaddr_t *)&inassoc->ipsa_dstaddr)); 16380Sstevel@tonic-gate bucket = &sp->sdb_of[outhash]; 16390Sstevel@tonic-gate 16400Sstevel@tonic-gate mutex_enter(&bucket->isaf_lock); 16410Sstevel@tonic-gate outassoc = ipsec_getassocbyspi(bucket, inassoc->ipsa_spi, 16420Sstevel@tonic-gate inassoc->ipsa_srcaddr, inassoc->ipsa_dstaddr, 16430Sstevel@tonic-gate inassoc->ipsa_addrfam); 16440Sstevel@tonic-gate mutex_exit(&bucket->isaf_lock); 16450Sstevel@tonic-gate if (outassoc == NULL) { 16460Sstevel@tonic-gate /* Q: Do we wish to set haspeer == B_FALSE? */ 16470Sstevel@tonic-gate ah0dbg(("ah_set_usetime: " 16480Sstevel@tonic-gate "can't find peer for inbound.\n")); 16490Sstevel@tonic-gate sadb_set_usetime(inassoc); 16500Sstevel@tonic-gate return; 16510Sstevel@tonic-gate } 16520Sstevel@tonic-gate } else { 16530Sstevel@tonic-gate outassoc = assoc; 1654564Ssommerfe bucket = INBOUND_BUCKET(sp, outassoc->ipsa_spi); 16550Sstevel@tonic-gate mutex_enter(&bucket->isaf_lock); 16560Sstevel@tonic-gate inassoc = ipsec_getassocbyspi(bucket, outassoc->ipsa_spi, 16570Sstevel@tonic-gate outassoc->ipsa_srcaddr, outassoc->ipsa_dstaddr, 16580Sstevel@tonic-gate outassoc->ipsa_addrfam); 16590Sstevel@tonic-gate mutex_exit(&bucket->isaf_lock); 16600Sstevel@tonic-gate if (inassoc == NULL) { 16610Sstevel@tonic-gate /* Q: Do we wish to set haspeer == B_FALSE? */ 16620Sstevel@tonic-gate ah0dbg(("ah_set_usetime: " 16630Sstevel@tonic-gate "can't find peer for outbound.\n")); 16640Sstevel@tonic-gate sadb_set_usetime(outassoc); 16650Sstevel@tonic-gate return; 16660Sstevel@tonic-gate } 16670Sstevel@tonic-gate } 16680Sstevel@tonic-gate 16690Sstevel@tonic-gate /* Update usetime on both. */ 16700Sstevel@tonic-gate sadb_set_usetime(inassoc); 16710Sstevel@tonic-gate sadb_set_usetime(outassoc); 16720Sstevel@tonic-gate 16730Sstevel@tonic-gate /* 16740Sstevel@tonic-gate * REFRELE any peer SA. 16750Sstevel@tonic-gate * 16760Sstevel@tonic-gate * Because of the multi-line macro nature of IPSA_REFRELE, keep 16770Sstevel@tonic-gate * them in { }. 16780Sstevel@tonic-gate */ 16790Sstevel@tonic-gate if (inbound) { 16800Sstevel@tonic-gate IPSA_REFRELE(outassoc); 16810Sstevel@tonic-gate } else { 16820Sstevel@tonic-gate IPSA_REFRELE(inassoc); 16830Sstevel@tonic-gate } 16840Sstevel@tonic-gate } 16850Sstevel@tonic-gate 16860Sstevel@tonic-gate /* 16870Sstevel@tonic-gate * Add a number of bytes to what the SA has protected so far. Return 16880Sstevel@tonic-gate * B_TRUE if the SA can still protect that many bytes. 16890Sstevel@tonic-gate * 16900Sstevel@tonic-gate * Caller must REFRELE the passed-in assoc. This function must REFRELE 16910Sstevel@tonic-gate * any obtained peer SA. 16920Sstevel@tonic-gate */ 16930Sstevel@tonic-gate static boolean_t 16940Sstevel@tonic-gate ah_age_bytes(ipsa_t *assoc, uint64_t bytes, boolean_t inbound) 16950Sstevel@tonic-gate { 16960Sstevel@tonic-gate ipsa_t *inassoc, *outassoc; 16970Sstevel@tonic-gate isaf_t *bucket; 16980Sstevel@tonic-gate boolean_t inrc, outrc, isv6; 16990Sstevel@tonic-gate sadb_t *sp; 17000Sstevel@tonic-gate int outhash; 17013448Sdh155122 netstack_t *ns = assoc->ipsa_netstack; 17023448Sdh155122 ipsecah_stack_t *ahstack = ns->netstack_ipsecah; 17030Sstevel@tonic-gate 17040Sstevel@tonic-gate /* No peer? No problem! */ 17050Sstevel@tonic-gate if (!assoc->ipsa_haspeer) { 17063448Sdh155122 return (sadb_age_bytes(ahstack->ah_pfkey_q, assoc, bytes, 17070Sstevel@tonic-gate B_TRUE)); 17080Sstevel@tonic-gate } 17090Sstevel@tonic-gate 17100Sstevel@tonic-gate /* 17110Sstevel@tonic-gate * Otherwise, we want to grab both the original assoc and its peer. 17120Sstevel@tonic-gate * There might be a race for this, but if it's a real race, two 17130Sstevel@tonic-gate * expire messages may occur. We limit this by only sending the 17140Sstevel@tonic-gate * expire message on one of the peers, we'll pick the inbound 17150Sstevel@tonic-gate * arbitrarily. 17160Sstevel@tonic-gate * 17170Sstevel@tonic-gate * If we need tight synchronization on the peer SA, then we need to 17180Sstevel@tonic-gate * reconsider. 17190Sstevel@tonic-gate */ 17200Sstevel@tonic-gate 17210Sstevel@tonic-gate /* Pick v4/v6 bucket based on addrfam. */ 17220Sstevel@tonic-gate isv6 = (assoc->ipsa_addrfam == AF_INET6); 17230Sstevel@tonic-gate if (isv6) { 17243448Sdh155122 sp = &ahstack->ah_sadb.s_v6; 17250Sstevel@tonic-gate } else { 17263448Sdh155122 sp = &ahstack->ah_sadb.s_v4; 17270Sstevel@tonic-gate ASSERT(assoc->ipsa_addrfam == AF_INET); 17280Sstevel@tonic-gate } 17290Sstevel@tonic-gate if (inbound) { 17300Sstevel@tonic-gate inassoc = assoc; 17310Sstevel@tonic-gate if (isv6) 17324987Sdanmcd outhash = OUTBOUND_HASH_V6(sp, 17334987Sdanmcd *((in6_addr_t *)&inassoc->ipsa_dstaddr)); 17340Sstevel@tonic-gate else 17354987Sdanmcd outhash = OUTBOUND_HASH_V4(sp, 17364987Sdanmcd *((ipaddr_t *)&inassoc->ipsa_dstaddr)); 17370Sstevel@tonic-gate bucket = &sp->sdb_of[outhash]; 17380Sstevel@tonic-gate mutex_enter(&bucket->isaf_lock); 17390Sstevel@tonic-gate outassoc = ipsec_getassocbyspi(bucket, inassoc->ipsa_spi, 17400Sstevel@tonic-gate inassoc->ipsa_srcaddr, inassoc->ipsa_dstaddr, 17410Sstevel@tonic-gate inassoc->ipsa_addrfam); 17420Sstevel@tonic-gate mutex_exit(&bucket->isaf_lock); 17430Sstevel@tonic-gate if (outassoc == NULL) { 17440Sstevel@tonic-gate /* Q: Do we wish to set haspeer == B_FALSE? */ 17450Sstevel@tonic-gate ah0dbg(("ah_age_bytes: " 17460Sstevel@tonic-gate "can't find peer for inbound.\n")); 17473448Sdh155122 return (sadb_age_bytes(ahstack->ah_pfkey_q, inassoc, 17480Sstevel@tonic-gate bytes, B_TRUE)); 17490Sstevel@tonic-gate } 17500Sstevel@tonic-gate } else { 17510Sstevel@tonic-gate outassoc = assoc; 1752564Ssommerfe bucket = INBOUND_BUCKET(sp, outassoc->ipsa_spi); 17530Sstevel@tonic-gate mutex_enter(&bucket->isaf_lock); 17540Sstevel@tonic-gate inassoc = ipsec_getassocbyspi(bucket, outassoc->ipsa_spi, 17550Sstevel@tonic-gate outassoc->ipsa_srcaddr, outassoc->ipsa_dstaddr, 17560Sstevel@tonic-gate outassoc->ipsa_addrfam); 17570Sstevel@tonic-gate mutex_exit(&bucket->isaf_lock); 17580Sstevel@tonic-gate if (inassoc == NULL) { 17590Sstevel@tonic-gate /* Q: Do we wish to set haspeer == B_FALSE? */ 17600Sstevel@tonic-gate ah0dbg(("ah_age_bytes: " 17610Sstevel@tonic-gate "can't find peer for outbound.\n")); 17623448Sdh155122 return (sadb_age_bytes(ahstack->ah_pfkey_q, outassoc, 17630Sstevel@tonic-gate bytes, B_TRUE)); 17640Sstevel@tonic-gate } 17650Sstevel@tonic-gate } 17660Sstevel@tonic-gate 17673448Sdh155122 inrc = sadb_age_bytes(ahstack->ah_pfkey_q, inassoc, bytes, B_TRUE); 17683448Sdh155122 outrc = sadb_age_bytes(ahstack->ah_pfkey_q, outassoc, bytes, B_FALSE); 17690Sstevel@tonic-gate 17700Sstevel@tonic-gate /* 17710Sstevel@tonic-gate * REFRELE any peer SA. 17720Sstevel@tonic-gate * 17730Sstevel@tonic-gate * Because of the multi-line macro nature of IPSA_REFRELE, keep 17740Sstevel@tonic-gate * them in { }. 17750Sstevel@tonic-gate */ 17760Sstevel@tonic-gate if (inbound) { 17770Sstevel@tonic-gate IPSA_REFRELE(outassoc); 17780Sstevel@tonic-gate } else { 17790Sstevel@tonic-gate IPSA_REFRELE(inassoc); 17800Sstevel@tonic-gate } 17810Sstevel@tonic-gate 17820Sstevel@tonic-gate return (inrc && outrc); 17830Sstevel@tonic-gate } 17840Sstevel@tonic-gate 17850Sstevel@tonic-gate /* 17860Sstevel@tonic-gate * Perform the really difficult work of inserting the proposed situation. 17870Sstevel@tonic-gate * Called while holding the algorithm lock. 17880Sstevel@tonic-gate */ 17890Sstevel@tonic-gate static void 17900Sstevel@tonic-gate ah_insert_prop(sadb_prop_t *prop, ipsacq_t *acqrec, uint_t combs) 17910Sstevel@tonic-gate { 17920Sstevel@tonic-gate sadb_comb_t *comb = (sadb_comb_t *)(prop + 1); 17930Sstevel@tonic-gate ipsec_out_t *io; 17940Sstevel@tonic-gate ipsec_action_t *ap; 17950Sstevel@tonic-gate ipsec_prot_t *prot; 17963448Sdh155122 ipsecah_stack_t *ahstack; 17973448Sdh155122 netstack_t *ns; 17983448Sdh155122 ipsec_stack_t *ipss; 17993448Sdh155122 18000Sstevel@tonic-gate io = (ipsec_out_t *)acqrec->ipsacq_mp->b_rptr; 18010Sstevel@tonic-gate ASSERT(io->ipsec_out_type == IPSEC_OUT); 18020Sstevel@tonic-gate 18033448Sdh155122 ns = io->ipsec_out_ns; 18043448Sdh155122 ipss = ns->netstack_ipsec; 18053448Sdh155122 ahstack = ns->netstack_ipsecah; 18063448Sdh155122 ASSERT(MUTEX_HELD(&ipss->ipsec_alg_lock)); 18073448Sdh155122 18080Sstevel@tonic-gate prop->sadb_prop_exttype = SADB_EXT_PROPOSAL; 18090Sstevel@tonic-gate prop->sadb_prop_len = SADB_8TO64(sizeof (sadb_prop_t)); 18100Sstevel@tonic-gate *(uint32_t *)(&prop->sadb_prop_replay) = 0; /* Quick zero-out! */ 18110Sstevel@tonic-gate 18123448Sdh155122 prop->sadb_prop_replay = ahstack->ipsecah_replay_size; 18130Sstevel@tonic-gate 18140Sstevel@tonic-gate /* 18150Sstevel@tonic-gate * Based upon algorithm properties, and what-not, prioritize a 18160Sstevel@tonic-gate * proposal, based on the ordering of the ah algorithms in the 18170Sstevel@tonic-gate * alternatives presented in the policy rule passed down 18180Sstevel@tonic-gate * through the ipsec_out_t and attached to the acquire record. 18190Sstevel@tonic-gate */ 18200Sstevel@tonic-gate 18210Sstevel@tonic-gate for (ap = acqrec->ipsacq_act; ap != NULL; 18220Sstevel@tonic-gate ap = ap->ipa_next) { 18230Sstevel@tonic-gate ipsec_alginfo_t *aalg; 18240Sstevel@tonic-gate 18250Sstevel@tonic-gate if ((ap->ipa_act.ipa_type != IPSEC_POLICY_APPLY) || 18260Sstevel@tonic-gate (!ap->ipa_act.ipa_apply.ipp_use_ah)) 18270Sstevel@tonic-gate continue; 18280Sstevel@tonic-gate 18290Sstevel@tonic-gate prot = &ap->ipa_act.ipa_apply; 18300Sstevel@tonic-gate 18310Sstevel@tonic-gate ASSERT(prot->ipp_auth_alg > 0); 18320Sstevel@tonic-gate 18333448Sdh155122 aalg = ipss->ipsec_alglists[IPSEC_ALG_AUTH] 18343448Sdh155122 [prot->ipp_auth_alg]; 18350Sstevel@tonic-gate if (aalg == NULL || !ALG_VALID(aalg)) 18360Sstevel@tonic-gate continue; 18370Sstevel@tonic-gate 18380Sstevel@tonic-gate /* XXX check aalg for duplicates??.. */ 18390Sstevel@tonic-gate 18400Sstevel@tonic-gate comb->sadb_comb_flags = 0; 18410Sstevel@tonic-gate comb->sadb_comb_reserved = 0; 18420Sstevel@tonic-gate comb->sadb_comb_encrypt = 0; 18430Sstevel@tonic-gate comb->sadb_comb_encrypt_minbits = 0; 18440Sstevel@tonic-gate comb->sadb_comb_encrypt_maxbits = 0; 18450Sstevel@tonic-gate 18460Sstevel@tonic-gate comb->sadb_comb_auth = aalg->alg_id; 18472751Sdanmcd comb->sadb_comb_auth_minbits = 18482751Sdanmcd MAX(prot->ipp_ah_minbits, aalg->alg_ef_minbits); 18492751Sdanmcd comb->sadb_comb_auth_maxbits = 18502751Sdanmcd MIN(prot->ipp_ah_maxbits, aalg->alg_ef_maxbits); 18510Sstevel@tonic-gate 18520Sstevel@tonic-gate /* 18530Sstevel@tonic-gate * The following may be based on algorithm 18540Sstevel@tonic-gate * properties, but in the meantime, we just pick 18550Sstevel@tonic-gate * some good, sensible numbers. Key mgmt. can 18560Sstevel@tonic-gate * (and perhaps should) be the place to finalize 18570Sstevel@tonic-gate * such decisions. 18580Sstevel@tonic-gate */ 18590Sstevel@tonic-gate 18600Sstevel@tonic-gate /* 18610Sstevel@tonic-gate * No limits on allocations, since we really don't 18620Sstevel@tonic-gate * support that concept currently. 18630Sstevel@tonic-gate */ 18640Sstevel@tonic-gate comb->sadb_comb_soft_allocations = 0; 18650Sstevel@tonic-gate comb->sadb_comb_hard_allocations = 0; 18660Sstevel@tonic-gate 18670Sstevel@tonic-gate /* 18680Sstevel@tonic-gate * These may want to come from policy rule.. 18690Sstevel@tonic-gate */ 18703448Sdh155122 comb->sadb_comb_soft_bytes = 18713448Sdh155122 ahstack->ipsecah_default_soft_bytes; 18723448Sdh155122 comb->sadb_comb_hard_bytes = 18733448Sdh155122 ahstack->ipsecah_default_hard_bytes; 18743448Sdh155122 comb->sadb_comb_soft_addtime = 18753448Sdh155122 ahstack->ipsecah_default_soft_addtime; 18763448Sdh155122 comb->sadb_comb_hard_addtime = 18773448Sdh155122 ahstack->ipsecah_default_hard_addtime; 18783448Sdh155122 comb->sadb_comb_soft_usetime = 18793448Sdh155122 ahstack->ipsecah_default_soft_usetime; 18803448Sdh155122 comb->sadb_comb_hard_usetime = 18813448Sdh155122 ahstack->ipsecah_default_hard_usetime; 18820Sstevel@tonic-gate 18830Sstevel@tonic-gate prop->sadb_prop_len += SADB_8TO64(sizeof (*comb)); 18840Sstevel@tonic-gate if (--combs == 0) 18850Sstevel@tonic-gate return; /* out of space.. */ 18860Sstevel@tonic-gate comb++; 18870Sstevel@tonic-gate } 18880Sstevel@tonic-gate } 18890Sstevel@tonic-gate 18900Sstevel@tonic-gate /* 18910Sstevel@tonic-gate * Prepare and actually send the SADB_ACQUIRE message to PF_KEY. 18920Sstevel@tonic-gate */ 18930Sstevel@tonic-gate static void 18943448Sdh155122 ah_send_acquire(ipsacq_t *acqrec, mblk_t *extended, netstack_t *ns) 18950Sstevel@tonic-gate { 18963055Sdanmcd uint_t combs; 18970Sstevel@tonic-gate sadb_msg_t *samsg; 18980Sstevel@tonic-gate sadb_prop_t *prop; 18993055Sdanmcd mblk_t *pfkeymp, *msgmp; 19003448Sdh155122 ipsecah_stack_t *ahstack = ns->netstack_ipsecah; 19013448Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 19023448Sdh155122 19033448Sdh155122 AH_BUMP_STAT(ahstack, acquire_requests); 19043448Sdh155122 19057073Spwernau if (ahstack->ah_pfkey_q == NULL) { 19067073Spwernau mutex_exit(&acqrec->ipsacq_lock); 19073055Sdanmcd return; 19087073Spwernau } 19090Sstevel@tonic-gate 19100Sstevel@tonic-gate /* Set up ACQUIRE. */ 19113448Sdh155122 pfkeymp = sadb_setup_acquire(acqrec, SADB_SATYPE_AH, 19123448Sdh155122 ns->netstack_ipsec); 19133055Sdanmcd if (pfkeymp == NULL) { 19140Sstevel@tonic-gate ah0dbg(("sadb_setup_acquire failed.\n")); 19157073Spwernau mutex_exit(&acqrec->ipsacq_lock); 19163055Sdanmcd return; 19170Sstevel@tonic-gate } 19183448Sdh155122 ASSERT(MUTEX_HELD(&ipss->ipsec_alg_lock)); 19193448Sdh155122 combs = ipss->ipsec_nalgs[IPSEC_ALG_AUTH]; 19203055Sdanmcd msgmp = pfkeymp->b_cont; 19213055Sdanmcd samsg = (sadb_msg_t *)(msgmp->b_rptr); 19220Sstevel@tonic-gate 19230Sstevel@tonic-gate /* Insert proposal here. */ 19240Sstevel@tonic-gate 19250Sstevel@tonic-gate prop = (sadb_prop_t *)(((uint64_t *)samsg) + samsg->sadb_msg_len); 19260Sstevel@tonic-gate ah_insert_prop(prop, acqrec, combs); 19270Sstevel@tonic-gate samsg->sadb_msg_len += prop->sadb_prop_len; 19280Sstevel@tonic-gate msgmp->b_wptr += SADB_64TO8(samsg->sadb_msg_len); 19290Sstevel@tonic-gate 19303448Sdh155122 mutex_exit(&ipss->ipsec_alg_lock); 19310Sstevel@tonic-gate 19320Sstevel@tonic-gate /* 19330Sstevel@tonic-gate * Must mutex_exit() before sending PF_KEY message up, in 19340Sstevel@tonic-gate * order to avoid recursive mutex_enter() if there are no registered 19350Sstevel@tonic-gate * listeners. 19360Sstevel@tonic-gate * 19370Sstevel@tonic-gate * Once I've sent the message, I'm cool anyway. 19380Sstevel@tonic-gate */ 19390Sstevel@tonic-gate mutex_exit(&acqrec->ipsacq_lock); 19403055Sdanmcd if (extended != NULL) { 19413448Sdh155122 putnext(ahstack->ah_pfkey_q, extended); 19420Sstevel@tonic-gate } 19433448Sdh155122 putnext(ahstack->ah_pfkey_q, pfkeymp); 19440Sstevel@tonic-gate } 19450Sstevel@tonic-gate 19460Sstevel@tonic-gate /* 19470Sstevel@tonic-gate * Handle the SADB_GETSPI message. Create a larval SA. 19480Sstevel@tonic-gate */ 19490Sstevel@tonic-gate static void 19503448Sdh155122 ah_getspi(mblk_t *mp, keysock_in_t *ksi, ipsecah_stack_t *ahstack) 19510Sstevel@tonic-gate { 19520Sstevel@tonic-gate ipsa_t *newbie, *target; 19530Sstevel@tonic-gate isaf_t *outbound, *inbound; 19540Sstevel@tonic-gate int rc, diagnostic; 19550Sstevel@tonic-gate sadb_sa_t *assoc; 19560Sstevel@tonic-gate keysock_out_t *kso; 19570Sstevel@tonic-gate uint32_t newspi; 19580Sstevel@tonic-gate 19590Sstevel@tonic-gate /* 19600Sstevel@tonic-gate * Randomly generate a proposed SPI value. 19610Sstevel@tonic-gate */ 19627749SThejaswini.Singarajipura@Sun.COM if (cl_inet_getspi != NULL) { 1963*8392SHuafeng.Lv@Sun.COM cl_inet_getspi(ahstack->ipsecah_netstack->netstack_stackid, 1964*8392SHuafeng.Lv@Sun.COM IPPROTO_AH, (uint8_t *)&newspi, sizeof (uint32_t), NULL); 19657749SThejaswini.Singarajipura@Sun.COM } else { 19667749SThejaswini.Singarajipura@Sun.COM (void) random_get_pseudo_bytes((uint8_t *)&newspi, 19677749SThejaswini.Singarajipura@Sun.COM sizeof (uint32_t)); 19687749SThejaswini.Singarajipura@Sun.COM } 19693448Sdh155122 newbie = sadb_getspi(ksi, newspi, &diagnostic, 19707749SThejaswini.Singarajipura@Sun.COM ahstack->ipsecah_netstack, IPPROTO_AH); 19710Sstevel@tonic-gate 19720Sstevel@tonic-gate if (newbie == NULL) { 19733448Sdh155122 sadb_pfkey_error(ahstack->ah_pfkey_q, mp, ENOMEM, diagnostic, 19740Sstevel@tonic-gate ksi->ks_in_serial); 19750Sstevel@tonic-gate return; 19760Sstevel@tonic-gate } else if (newbie == (ipsa_t *)-1) { 19773448Sdh155122 sadb_pfkey_error(ahstack->ah_pfkey_q, mp, EINVAL, diagnostic, 19780Sstevel@tonic-gate ksi->ks_in_serial); 19790Sstevel@tonic-gate return; 19800Sstevel@tonic-gate } 19810Sstevel@tonic-gate 19820Sstevel@tonic-gate /* 19830Sstevel@tonic-gate * XXX - We may randomly collide. We really should recover from this. 19840Sstevel@tonic-gate * Unfortunately, that could require spending way-too-much-time 19850Sstevel@tonic-gate * in here. For now, let the user retry. 19860Sstevel@tonic-gate */ 19870Sstevel@tonic-gate 19880Sstevel@tonic-gate if (newbie->ipsa_addrfam == AF_INET6) { 19893448Sdh155122 outbound = OUTBOUND_BUCKET_V6(&ahstack->ah_sadb.s_v6, 1990564Ssommerfe *(uint32_t *)(newbie->ipsa_dstaddr)); 19913448Sdh155122 inbound = INBOUND_BUCKET(&ahstack->ah_sadb.s_v6, 19923448Sdh155122 newbie->ipsa_spi); 19930Sstevel@tonic-gate } else { 19943448Sdh155122 outbound = OUTBOUND_BUCKET_V4(&ahstack->ah_sadb.s_v4, 1995564Ssommerfe *(uint32_t *)(newbie->ipsa_dstaddr)); 19963448Sdh155122 inbound = INBOUND_BUCKET(&ahstack->ah_sadb.s_v4, 19973448Sdh155122 newbie->ipsa_spi); 19980Sstevel@tonic-gate } 19990Sstevel@tonic-gate 20000Sstevel@tonic-gate mutex_enter(&outbound->isaf_lock); 20010Sstevel@tonic-gate mutex_enter(&inbound->isaf_lock); 20020Sstevel@tonic-gate 20030Sstevel@tonic-gate /* 20040Sstevel@tonic-gate * Check for collisions (i.e. did sadb_getspi() return with something 20050Sstevel@tonic-gate * that already exists?). 20060Sstevel@tonic-gate * 20070Sstevel@tonic-gate * Try outbound first. Even though SADB_GETSPI is traditionally 20080Sstevel@tonic-gate * for inbound SAs, you never know what a user might do. 20090Sstevel@tonic-gate */ 20100Sstevel@tonic-gate target = ipsec_getassocbyspi(outbound, newbie->ipsa_spi, 20110Sstevel@tonic-gate newbie->ipsa_srcaddr, newbie->ipsa_dstaddr, newbie->ipsa_addrfam); 20120Sstevel@tonic-gate if (target == NULL) { 20130Sstevel@tonic-gate target = ipsec_getassocbyspi(inbound, newbie->ipsa_spi, 20140Sstevel@tonic-gate newbie->ipsa_srcaddr, newbie->ipsa_dstaddr, 20150Sstevel@tonic-gate newbie->ipsa_addrfam); 20160Sstevel@tonic-gate } 20170Sstevel@tonic-gate 20180Sstevel@tonic-gate /* 20190Sstevel@tonic-gate * I don't have collisions elsewhere! 20200Sstevel@tonic-gate * (Nor will I because I'm still holding inbound/outbound locks.) 20210Sstevel@tonic-gate */ 20220Sstevel@tonic-gate 20230Sstevel@tonic-gate if (target != NULL) { 20240Sstevel@tonic-gate rc = EEXIST; 20250Sstevel@tonic-gate IPSA_REFRELE(target); 20260Sstevel@tonic-gate } else { 20270Sstevel@tonic-gate /* 20280Sstevel@tonic-gate * sadb_insertassoc() also checks for collisions, so 20290Sstevel@tonic-gate * if there's a colliding larval entry, rc will be set 20300Sstevel@tonic-gate * to EEXIST. 20310Sstevel@tonic-gate */ 20320Sstevel@tonic-gate rc = sadb_insertassoc(newbie, inbound); 20334987Sdanmcd newbie->ipsa_hardexpiretime = gethrestime_sec(); 20343448Sdh155122 newbie->ipsa_hardexpiretime += ahstack->ipsecah_larval_timeout; 20350Sstevel@tonic-gate } 20360Sstevel@tonic-gate 20370Sstevel@tonic-gate /* 20380Sstevel@tonic-gate * Can exit outbound mutex. Hold inbound until we're done with 20390Sstevel@tonic-gate * newbie. 20400Sstevel@tonic-gate */ 20410Sstevel@tonic-gate mutex_exit(&outbound->isaf_lock); 20420Sstevel@tonic-gate 20430Sstevel@tonic-gate if (rc != 0) { 20440Sstevel@tonic-gate mutex_exit(&inbound->isaf_lock); 20450Sstevel@tonic-gate IPSA_REFRELE(newbie); 20463448Sdh155122 sadb_pfkey_error(ahstack->ah_pfkey_q, mp, rc, 20473448Sdh155122 SADB_X_DIAGNOSTIC_NONE, ksi->ks_in_serial); 20480Sstevel@tonic-gate return; 20490Sstevel@tonic-gate } 20500Sstevel@tonic-gate 20510Sstevel@tonic-gate /* Can write here because I'm still holding the bucket lock. */ 20520Sstevel@tonic-gate newbie->ipsa_type = SADB_SATYPE_AH; 20530Sstevel@tonic-gate 20540Sstevel@tonic-gate /* 20550Sstevel@tonic-gate * Construct successful return message. We have one thing going 20560Sstevel@tonic-gate * for us in PF_KEY v2. That's the fact that 20570Sstevel@tonic-gate * sizeof (sadb_spirange_t) == sizeof (sadb_sa_t) 20580Sstevel@tonic-gate */ 20590Sstevel@tonic-gate assoc = (sadb_sa_t *)ksi->ks_in_extv[SADB_EXT_SPIRANGE]; 20600Sstevel@tonic-gate assoc->sadb_sa_exttype = SADB_EXT_SA; 20610Sstevel@tonic-gate assoc->sadb_sa_spi = newbie->ipsa_spi; 20620Sstevel@tonic-gate *((uint64_t *)(&assoc->sadb_sa_replay)) = 0; 20630Sstevel@tonic-gate mutex_exit(&inbound->isaf_lock); 20640Sstevel@tonic-gate 20650Sstevel@tonic-gate /* Convert KEYSOCK_IN to KEYSOCK_OUT. */ 20660Sstevel@tonic-gate kso = (keysock_out_t *)ksi; 20670Sstevel@tonic-gate kso->ks_out_len = sizeof (*kso); 20680Sstevel@tonic-gate kso->ks_out_serial = ksi->ks_in_serial; 20690Sstevel@tonic-gate kso->ks_out_type = KEYSOCK_OUT; 20700Sstevel@tonic-gate 20710Sstevel@tonic-gate /* 20720Sstevel@tonic-gate * Can safely putnext() to ah_pfkey_q, because this is a turnaround 20730Sstevel@tonic-gate * from the ah_pfkey_q. 20740Sstevel@tonic-gate */ 20753448Sdh155122 putnext(ahstack->ah_pfkey_q, mp); 20760Sstevel@tonic-gate } 20770Sstevel@tonic-gate 20780Sstevel@tonic-gate /* 20790Sstevel@tonic-gate * IPv6 sends up the ICMP errors for validation and the removal of the AH 20800Sstevel@tonic-gate * header. 20810Sstevel@tonic-gate */ 20820Sstevel@tonic-gate static ipsec_status_t 20833448Sdh155122 ah_icmp_error_v6(mblk_t *ipsec_mp, ipsecah_stack_t *ahstack) 20840Sstevel@tonic-gate { 20850Sstevel@tonic-gate mblk_t *mp; 20860Sstevel@tonic-gate ip6_t *ip6h, *oip6h; 20870Sstevel@tonic-gate uint16_t hdr_length, ah_length; 20880Sstevel@tonic-gate uint8_t *nexthdrp; 20890Sstevel@tonic-gate ah_t *ah; 20900Sstevel@tonic-gate icmp6_t *icmp6; 20910Sstevel@tonic-gate isaf_t *isaf; 20920Sstevel@tonic-gate ipsa_t *assoc; 20930Sstevel@tonic-gate uint8_t *post_ah_ptr; 20943448Sdh155122 ipsec_stack_t *ipss = ahstack->ipsecah_netstack->netstack_ipsec; 20950Sstevel@tonic-gate 20960Sstevel@tonic-gate mp = ipsec_mp->b_cont; 20970Sstevel@tonic-gate ASSERT(mp->b_datap->db_type == M_CTL); 20980Sstevel@tonic-gate 20990Sstevel@tonic-gate /* 21000Sstevel@tonic-gate * Change the type to M_DATA till we finish pullups. 21010Sstevel@tonic-gate */ 21020Sstevel@tonic-gate mp->b_datap->db_type = M_DATA; 21030Sstevel@tonic-gate 21040Sstevel@tonic-gate /* 21050Sstevel@tonic-gate * Eat the cost of a pullupmsg() for now. It makes the rest of this 21060Sstevel@tonic-gate * code far less convoluted. 21070Sstevel@tonic-gate */ 21080Sstevel@tonic-gate if (!pullupmsg(mp, -1) || 21090Sstevel@tonic-gate !ip_hdr_length_nexthdr_v6(mp, (ip6_t *)mp->b_rptr, &hdr_length, 21104987Sdanmcd &nexthdrp) || 21110Sstevel@tonic-gate mp->b_rptr + hdr_length + sizeof (icmp6_t) + sizeof (ip6_t) + 21120Sstevel@tonic-gate sizeof (ah_t) > mp->b_wptr) { 21133448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 21143448Sdh155122 ip_drop_packet(ipsec_mp, B_TRUE, NULL, NULL, 21153448Sdh155122 DROPPER(ipss, ipds_ah_nomem), 21163448Sdh155122 &ahstack->ah_dropper); 21170Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 21180Sstevel@tonic-gate } 21190Sstevel@tonic-gate 21200Sstevel@tonic-gate oip6h = (ip6_t *)mp->b_rptr; 21210Sstevel@tonic-gate icmp6 = (icmp6_t *)((uint8_t *)oip6h + hdr_length); 21220Sstevel@tonic-gate ip6h = (ip6_t *)(icmp6 + 1); 21230Sstevel@tonic-gate if (!ip_hdr_length_nexthdr_v6(mp, ip6h, &hdr_length, &nexthdrp)) { 21243448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 21250Sstevel@tonic-gate ip_drop_packet(ipsec_mp, B_TRUE, NULL, NULL, 21263448Sdh155122 DROPPER(ipss, ipds_ah_bad_v6_hdrs), 21273448Sdh155122 &ahstack->ah_dropper); 21280Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 21290Sstevel@tonic-gate } 21300Sstevel@tonic-gate ah = (ah_t *)((uint8_t *)ip6h + hdr_length); 21310Sstevel@tonic-gate 21323448Sdh155122 isaf = OUTBOUND_BUCKET_V6(&ahstack->ah_sadb.s_v6, ip6h->ip6_dst); 21330Sstevel@tonic-gate mutex_enter(&isaf->isaf_lock); 21340Sstevel@tonic-gate assoc = ipsec_getassocbyspi(isaf, ah->ah_spi, 21350Sstevel@tonic-gate (uint32_t *)&ip6h->ip6_src, (uint32_t *)&ip6h->ip6_dst, AF_INET6); 21360Sstevel@tonic-gate mutex_exit(&isaf->isaf_lock); 21370Sstevel@tonic-gate 21380Sstevel@tonic-gate if (assoc == NULL) { 21393448Sdh155122 IP_AH_BUMP_STAT(ipss, lookup_failure); 21403448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 21413448Sdh155122 if (ahstack->ipsecah_log_unknown_spi) { 21420Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, 21430Sstevel@tonic-gate SL_CONSOLE | SL_WARN | SL_ERROR, 21440Sstevel@tonic-gate "Bad ICMP message - No association for the " 21450Sstevel@tonic-gate "attached AH header whose spi is 0x%x, " 21460Sstevel@tonic-gate "sender is 0x%x\n", 21473448Sdh155122 ah->ah_spi, &oip6h->ip6_src, AF_INET6, 21483448Sdh155122 ahstack->ipsecah_netstack); 21490Sstevel@tonic-gate } 21503448Sdh155122 ip_drop_packet(ipsec_mp, B_TRUE, NULL, NULL, 21513448Sdh155122 DROPPER(ipss, ipds_ah_no_sa), 21523448Sdh155122 &ahstack->ah_dropper); 21530Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 21540Sstevel@tonic-gate } 21550Sstevel@tonic-gate 21560Sstevel@tonic-gate IPSA_REFRELE(assoc); 21570Sstevel@tonic-gate 21580Sstevel@tonic-gate /* 21590Sstevel@tonic-gate * There seems to be a valid association. If there is enough of AH 21600Sstevel@tonic-gate * header remove it, otherwise bail. One could check whether it has 21610Sstevel@tonic-gate * complete AH header plus 8 bytes but it does not make sense if an 21620Sstevel@tonic-gate * icmp error is returned for ICMP messages e.g ICMP time exceeded, 21630Sstevel@tonic-gate * that are being sent up. Let the caller figure out. 21640Sstevel@tonic-gate * 21650Sstevel@tonic-gate * NOTE: ah_length is the number of 32 bit words minus 2. 21660Sstevel@tonic-gate */ 21670Sstevel@tonic-gate ah_length = (ah->ah_length << 2) + 8; 21680Sstevel@tonic-gate post_ah_ptr = (uint8_t *)ah + ah_length; 21690Sstevel@tonic-gate 21700Sstevel@tonic-gate if (post_ah_ptr > mp->b_wptr) { 21713448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 21720Sstevel@tonic-gate ip_drop_packet(ipsec_mp, B_TRUE, NULL, NULL, 21733448Sdh155122 DROPPER(ipss, ipds_ah_bad_length), 21743448Sdh155122 &ahstack->ah_dropper); 21750Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 21760Sstevel@tonic-gate } 21770Sstevel@tonic-gate 21780Sstevel@tonic-gate ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) - ah_length); 21790Sstevel@tonic-gate *nexthdrp = ah->ah_nexthdr; 21800Sstevel@tonic-gate ovbcopy(post_ah_ptr, ah, 21810Sstevel@tonic-gate (size_t)((uintptr_t)mp->b_wptr - (uintptr_t)post_ah_ptr)); 21820Sstevel@tonic-gate mp->b_wptr -= ah_length; 21830Sstevel@tonic-gate /* Rewhack to be an ICMP error. */ 21840Sstevel@tonic-gate mp->b_datap->db_type = M_CTL; 21850Sstevel@tonic-gate 21860Sstevel@tonic-gate return (IPSEC_STATUS_SUCCESS); 21870Sstevel@tonic-gate } 21880Sstevel@tonic-gate 21890Sstevel@tonic-gate /* 21900Sstevel@tonic-gate * IP sends up the ICMP errors for validation and the removal of 21910Sstevel@tonic-gate * the AH header. 21920Sstevel@tonic-gate */ 21930Sstevel@tonic-gate static ipsec_status_t 21943448Sdh155122 ah_icmp_error_v4(mblk_t *ipsec_mp, ipsecah_stack_t *ahstack) 21950Sstevel@tonic-gate { 21960Sstevel@tonic-gate mblk_t *mp; 21970Sstevel@tonic-gate mblk_t *mp1; 21980Sstevel@tonic-gate icmph_t *icmph; 21990Sstevel@tonic-gate int iph_hdr_length; 22000Sstevel@tonic-gate int hdr_length; 22010Sstevel@tonic-gate isaf_t *hptr; 22020Sstevel@tonic-gate ipsa_t *assoc; 22030Sstevel@tonic-gate int ah_length; 22040Sstevel@tonic-gate ipha_t *ipha; 22050Sstevel@tonic-gate ipha_t *oipha; 22060Sstevel@tonic-gate ah_t *ah; 22070Sstevel@tonic-gate uint32_t length; 22080Sstevel@tonic-gate int alloc_size; 22090Sstevel@tonic-gate uint8_t nexthdr; 22103448Sdh155122 ipsec_stack_t *ipss = ahstack->ipsecah_netstack->netstack_ipsec; 22110Sstevel@tonic-gate 22120Sstevel@tonic-gate mp = ipsec_mp->b_cont; 22130Sstevel@tonic-gate ASSERT(mp->b_datap->db_type == M_CTL); 22140Sstevel@tonic-gate 22150Sstevel@tonic-gate /* 22160Sstevel@tonic-gate * Change the type to M_DATA till we finish pullups. 22170Sstevel@tonic-gate */ 22180Sstevel@tonic-gate mp->b_datap->db_type = M_DATA; 22190Sstevel@tonic-gate 22200Sstevel@tonic-gate oipha = ipha = (ipha_t *)mp->b_rptr; 22210Sstevel@tonic-gate iph_hdr_length = IPH_HDR_LENGTH(ipha); 22220Sstevel@tonic-gate icmph = (icmph_t *)&mp->b_rptr[iph_hdr_length]; 22230Sstevel@tonic-gate 22240Sstevel@tonic-gate ipha = (ipha_t *)&icmph[1]; 22250Sstevel@tonic-gate hdr_length = IPH_HDR_LENGTH(ipha); 22260Sstevel@tonic-gate 22270Sstevel@tonic-gate /* 22280Sstevel@tonic-gate * See if we have enough to locate the SPI 22290Sstevel@tonic-gate */ 22300Sstevel@tonic-gate if ((uchar_t *)ipha + hdr_length + 8 > mp->b_wptr) { 22310Sstevel@tonic-gate if (!pullupmsg(mp, (uchar_t *)ipha + hdr_length + 8 - 22324987Sdanmcd mp->b_rptr)) { 22333448Sdh155122 ipsec_rl_strlog(ahstack->ipsecah_netstack, 22343448Sdh155122 info.mi_idnum, 0, 0, 22350Sstevel@tonic-gate SL_WARN | SL_ERROR, 22360Sstevel@tonic-gate "ICMP error: Small AH header\n"); 22373448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 22380Sstevel@tonic-gate ip_drop_packet(ipsec_mp, B_TRUE, NULL, NULL, 22393448Sdh155122 DROPPER(ipss, ipds_ah_bad_length), 22403448Sdh155122 &ahstack->ah_dropper); 22410Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 22420Sstevel@tonic-gate } 22430Sstevel@tonic-gate icmph = (icmph_t *)&mp->b_rptr[iph_hdr_length]; 22440Sstevel@tonic-gate ipha = (ipha_t *)&icmph[1]; 22450Sstevel@tonic-gate } 22460Sstevel@tonic-gate 22470Sstevel@tonic-gate ah = (ah_t *)((uint8_t *)ipha + hdr_length); 22480Sstevel@tonic-gate nexthdr = ah->ah_nexthdr; 22490Sstevel@tonic-gate 22503448Sdh155122 hptr = OUTBOUND_BUCKET_V4(&ahstack->ah_sadb.s_v4, ipha->ipha_dst); 22510Sstevel@tonic-gate mutex_enter(&hptr->isaf_lock); 22520Sstevel@tonic-gate assoc = ipsec_getassocbyspi(hptr, ah->ah_spi, 22530Sstevel@tonic-gate (uint32_t *)&ipha->ipha_src, (uint32_t *)&ipha->ipha_dst, AF_INET); 22540Sstevel@tonic-gate mutex_exit(&hptr->isaf_lock); 22550Sstevel@tonic-gate 22560Sstevel@tonic-gate if (assoc == NULL) { 22573448Sdh155122 IP_AH_BUMP_STAT(ipss, lookup_failure); 22583448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 22593448Sdh155122 if (ahstack->ipsecah_log_unknown_spi) { 22600Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, 22610Sstevel@tonic-gate SL_CONSOLE | SL_WARN | SL_ERROR, 22620Sstevel@tonic-gate "Bad ICMP message - No association for the " 22630Sstevel@tonic-gate "attached AH header whose spi is 0x%x, " 22640Sstevel@tonic-gate "sender is 0x%x\n", 22653448Sdh155122 ah->ah_spi, &oipha->ipha_src, AF_INET, 22663448Sdh155122 ahstack->ipsecah_netstack); 22670Sstevel@tonic-gate } 22683448Sdh155122 ip_drop_packet(ipsec_mp, B_TRUE, NULL, NULL, 22693448Sdh155122 DROPPER(ipss, ipds_ah_no_sa), 22703448Sdh155122 &ahstack->ah_dropper); 22710Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 22720Sstevel@tonic-gate } 22730Sstevel@tonic-gate 22740Sstevel@tonic-gate IPSA_REFRELE(assoc); 22750Sstevel@tonic-gate /* 22760Sstevel@tonic-gate * There seems to be a valid association. If there 22770Sstevel@tonic-gate * is enough of AH header remove it, otherwise remove 22780Sstevel@tonic-gate * as much as possible and send it back. One could check 22790Sstevel@tonic-gate * whether it has complete AH header plus 8 bytes but it 22800Sstevel@tonic-gate * does not make sense if an icmp error is returned for 22810Sstevel@tonic-gate * ICMP messages e.g ICMP time exceeded, that are being 22820Sstevel@tonic-gate * sent up. Let the caller figure out. 22830Sstevel@tonic-gate * 22840Sstevel@tonic-gate * NOTE: ah_length is the number of 32 bit words minus 2. 22850Sstevel@tonic-gate */ 22860Sstevel@tonic-gate ah_length = (ah->ah_length << 2) + 8; 22870Sstevel@tonic-gate 22880Sstevel@tonic-gate if ((uchar_t *)ipha + hdr_length + ah_length > mp->b_wptr) { 22890Sstevel@tonic-gate if (mp->b_cont == NULL) { 22900Sstevel@tonic-gate /* 22910Sstevel@tonic-gate * There is nothing to pullup. Just remove as 22920Sstevel@tonic-gate * much as possible. This is a common case for 22930Sstevel@tonic-gate * IPV4. 22940Sstevel@tonic-gate */ 22950Sstevel@tonic-gate ah_length = (mp->b_wptr - ((uchar_t *)ipha + 22960Sstevel@tonic-gate hdr_length)); 22970Sstevel@tonic-gate goto done; 22980Sstevel@tonic-gate } 22990Sstevel@tonic-gate /* Pullup the full ah header */ 23000Sstevel@tonic-gate if (!pullupmsg(mp, (uchar_t *)ah + ah_length - mp->b_rptr)) { 23010Sstevel@tonic-gate /* 23020Sstevel@tonic-gate * pullupmsg could have failed if there was not 23030Sstevel@tonic-gate * enough to pullup or memory allocation failed. 23040Sstevel@tonic-gate * We tried hard, give up now. 23050Sstevel@tonic-gate */ 23063448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 23070Sstevel@tonic-gate ip_drop_packet(ipsec_mp, B_TRUE, NULL, NULL, 23083448Sdh155122 DROPPER(ipss, ipds_ah_nomem), 23093448Sdh155122 &ahstack->ah_dropper); 23100Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 23110Sstevel@tonic-gate } 23120Sstevel@tonic-gate icmph = (icmph_t *)&mp->b_rptr[iph_hdr_length]; 23130Sstevel@tonic-gate ipha = (ipha_t *)&icmph[1]; 23140Sstevel@tonic-gate } 23150Sstevel@tonic-gate done: 23160Sstevel@tonic-gate /* 23170Sstevel@tonic-gate * Remove the AH header and change the protocol. 23180Sstevel@tonic-gate * Don't update the spi fields in the ipsec_in 23190Sstevel@tonic-gate * message as we are called just to validate the 23200Sstevel@tonic-gate * message attached to the ICMP message. 23210Sstevel@tonic-gate * 23220Sstevel@tonic-gate * If we never pulled up since all of the message 23230Sstevel@tonic-gate * is in one single mblk, we can't remove the AH header 23240Sstevel@tonic-gate * by just setting the b_wptr to the beginning of the 23250Sstevel@tonic-gate * AH header. We need to allocate a mblk that can hold 23260Sstevel@tonic-gate * up until the inner IP header and copy them. 23270Sstevel@tonic-gate */ 23280Sstevel@tonic-gate alloc_size = iph_hdr_length + sizeof (icmph_t) + hdr_length; 23290Sstevel@tonic-gate 23300Sstevel@tonic-gate if ((mp1 = allocb(alloc_size, BPRI_LO)) == NULL) { 23313448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 23323448Sdh155122 ip_drop_packet(ipsec_mp, B_TRUE, NULL, NULL, 23333448Sdh155122 DROPPER(ipss, ipds_ah_nomem), 23343448Sdh155122 &ahstack->ah_dropper); 23350Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 23360Sstevel@tonic-gate } 23370Sstevel@tonic-gate /* ICMP errors are M_CTL messages */ 23380Sstevel@tonic-gate mp1->b_datap->db_type = M_CTL; 23390Sstevel@tonic-gate ipsec_mp->b_cont = mp1; 23400Sstevel@tonic-gate bcopy(mp->b_rptr, mp1->b_rptr, alloc_size); 23410Sstevel@tonic-gate mp1->b_wptr += alloc_size; 23420Sstevel@tonic-gate 23430Sstevel@tonic-gate /* 23440Sstevel@tonic-gate * Skip whatever we have copied and as much of AH header 23450Sstevel@tonic-gate * possible. If we still have something left in the original 23460Sstevel@tonic-gate * message, tag on. 23470Sstevel@tonic-gate */ 23480Sstevel@tonic-gate mp->b_rptr = (uchar_t *)ipha + hdr_length + ah_length; 23490Sstevel@tonic-gate 23500Sstevel@tonic-gate if (mp->b_rptr != mp->b_wptr) { 23510Sstevel@tonic-gate mp1->b_cont = mp; 23520Sstevel@tonic-gate } else { 23530Sstevel@tonic-gate if (mp->b_cont != NULL) 23540Sstevel@tonic-gate mp1->b_cont = mp->b_cont; 23550Sstevel@tonic-gate freeb(mp); 23560Sstevel@tonic-gate } 23570Sstevel@tonic-gate 23580Sstevel@tonic-gate ipha = (ipha_t *)(mp1->b_rptr + iph_hdr_length + sizeof (icmph_t)); 23590Sstevel@tonic-gate ipha->ipha_protocol = nexthdr; 23600Sstevel@tonic-gate length = ntohs(ipha->ipha_length); 23610Sstevel@tonic-gate length -= ah_length; 23620Sstevel@tonic-gate ipha->ipha_length = htons((uint16_t)length); 23630Sstevel@tonic-gate ipha->ipha_hdr_checksum = 0; 23640Sstevel@tonic-gate ipha->ipha_hdr_checksum = (uint16_t)ip_csum_hdr(ipha); 23650Sstevel@tonic-gate 23660Sstevel@tonic-gate return (IPSEC_STATUS_SUCCESS); 23670Sstevel@tonic-gate } 23680Sstevel@tonic-gate 23690Sstevel@tonic-gate /* 23700Sstevel@tonic-gate * IP calls this to validate the ICMP errors that 23710Sstevel@tonic-gate * we got from the network. 23720Sstevel@tonic-gate */ 23730Sstevel@tonic-gate ipsec_status_t 23740Sstevel@tonic-gate ipsecah_icmp_error(mblk_t *mp) 23750Sstevel@tonic-gate { 23760Sstevel@tonic-gate ipsec_in_t *ii = (ipsec_in_t *)mp->b_rptr; 23773448Sdh155122 netstack_t *ns = ii->ipsec_in_ns; 23783448Sdh155122 ipsecah_stack_t *ahstack = ns->netstack_ipsecah; 23790Sstevel@tonic-gate 23800Sstevel@tonic-gate if (ii->ipsec_in_v4) 23813448Sdh155122 return (ah_icmp_error_v4(mp, ahstack)); 23820Sstevel@tonic-gate else 23833448Sdh155122 return (ah_icmp_error_v6(mp, ahstack)); 23840Sstevel@tonic-gate } 23850Sstevel@tonic-gate 23860Sstevel@tonic-gate static int 23870Sstevel@tonic-gate ah_fix_tlv_options_v6(uint8_t *oi_opt, uint8_t *pi_opt, uint_t ehdrlen, 23880Sstevel@tonic-gate uint8_t hdr_type, boolean_t copy_always) 23890Sstevel@tonic-gate { 23900Sstevel@tonic-gate uint8_t opt_type; 23910Sstevel@tonic-gate uint_t optlen; 23920Sstevel@tonic-gate 23930Sstevel@tonic-gate ASSERT(hdr_type == IPPROTO_DSTOPTS || hdr_type == IPPROTO_HOPOPTS); 23940Sstevel@tonic-gate 23950Sstevel@tonic-gate /* 23960Sstevel@tonic-gate * Copy the next header and hdr ext. len of the HOP-by-HOP 23970Sstevel@tonic-gate * and Destination option. 23980Sstevel@tonic-gate */ 23990Sstevel@tonic-gate *pi_opt++ = *oi_opt++; 24000Sstevel@tonic-gate *pi_opt++ = *oi_opt++; 24010Sstevel@tonic-gate ehdrlen -= 2; 24020Sstevel@tonic-gate 24030Sstevel@tonic-gate /* 24040Sstevel@tonic-gate * Now handle all the TLV encoded options. 24050Sstevel@tonic-gate */ 24060Sstevel@tonic-gate while (ehdrlen != 0) { 24070Sstevel@tonic-gate opt_type = *oi_opt; 24080Sstevel@tonic-gate 24090Sstevel@tonic-gate if (opt_type == IP6OPT_PAD1) { 24100Sstevel@tonic-gate optlen = 1; 24110Sstevel@tonic-gate } else { 24120Sstevel@tonic-gate if (ehdrlen < 2) 24130Sstevel@tonic-gate goto bad_opt; 24140Sstevel@tonic-gate optlen = 2 + oi_opt[1]; 24150Sstevel@tonic-gate if (optlen > ehdrlen) 24160Sstevel@tonic-gate goto bad_opt; 24170Sstevel@tonic-gate } 24180Sstevel@tonic-gate if (copy_always || !(opt_type & IP6OPT_MUTABLE)) { 24190Sstevel@tonic-gate bcopy(oi_opt, pi_opt, optlen); 24200Sstevel@tonic-gate } else { 24210Sstevel@tonic-gate if (optlen == 1) { 24220Sstevel@tonic-gate *pi_opt = 0; 24230Sstevel@tonic-gate } else { 24240Sstevel@tonic-gate /* 24250Sstevel@tonic-gate * Copy the type and data length fields. 24260Sstevel@tonic-gate * Zero the option data by skipping 24270Sstevel@tonic-gate * option type and option data len 24280Sstevel@tonic-gate * fields. 24290Sstevel@tonic-gate */ 24300Sstevel@tonic-gate *pi_opt = *oi_opt; 24310Sstevel@tonic-gate *(pi_opt + 1) = *(oi_opt + 1); 24320Sstevel@tonic-gate bzero(pi_opt + 2, optlen - 2); 24330Sstevel@tonic-gate } 24340Sstevel@tonic-gate } 24350Sstevel@tonic-gate ehdrlen -= optlen; 24360Sstevel@tonic-gate oi_opt += optlen; 24370Sstevel@tonic-gate pi_opt += optlen; 24380Sstevel@tonic-gate } 24390Sstevel@tonic-gate return (0); 24400Sstevel@tonic-gate bad_opt: 24410Sstevel@tonic-gate return (-1); 24420Sstevel@tonic-gate } 24430Sstevel@tonic-gate 24440Sstevel@tonic-gate /* 24450Sstevel@tonic-gate * Construct a pseudo header for AH, processing all the options. 24460Sstevel@tonic-gate * 24470Sstevel@tonic-gate * oip6h is the IPv6 header of the incoming or outgoing packet. 24480Sstevel@tonic-gate * ip6h is the pointer to the pseudo headers IPV6 header. All 24490Sstevel@tonic-gate * the space needed for the options have been allocated including 24500Sstevel@tonic-gate * the AH header. 24510Sstevel@tonic-gate * 24520Sstevel@tonic-gate * If copy_always is set, all the options that appear before AH are copied 24530Sstevel@tonic-gate * blindly without checking for IP6OPT_MUTABLE. This is used by 24540Sstevel@tonic-gate * ah_auth_out_done(). Please refer to that function for details. 24550Sstevel@tonic-gate * 24560Sstevel@tonic-gate * NOTE : 24570Sstevel@tonic-gate * 24580Sstevel@tonic-gate * * AH header is never copied in this function even if copy_always 24590Sstevel@tonic-gate * is set. It just returns the ah_offset - offset of the AH header 24600Sstevel@tonic-gate * and the caller needs to do the copying. This is done so that we 24610Sstevel@tonic-gate * don't have pass extra arguments e.g. SA etc. and also, 24620Sstevel@tonic-gate * it is not needed when ah_auth_out_done is calling this function. 24630Sstevel@tonic-gate */ 24640Sstevel@tonic-gate static uint_t 24650Sstevel@tonic-gate ah_fix_phdr_v6(ip6_t *ip6h, ip6_t *oip6h, boolean_t outbound, 24660Sstevel@tonic-gate boolean_t copy_always) 24670Sstevel@tonic-gate { 24680Sstevel@tonic-gate uint8_t *oi_opt; 24690Sstevel@tonic-gate uint8_t *pi_opt; 24700Sstevel@tonic-gate uint8_t nexthdr; 24710Sstevel@tonic-gate uint8_t *prev_nexthdr; 24720Sstevel@tonic-gate ip6_hbh_t *hbhhdr; 24730Sstevel@tonic-gate ip6_dest_t *dsthdr = NULL; 24740Sstevel@tonic-gate ip6_rthdr0_t *rthdr; 24750Sstevel@tonic-gate int ehdrlen; 24760Sstevel@tonic-gate ah_t *ah; 24770Sstevel@tonic-gate int ret; 24780Sstevel@tonic-gate 24790Sstevel@tonic-gate /* 24800Sstevel@tonic-gate * In the outbound case for source route, ULP has already moved 24810Sstevel@tonic-gate * the first hop, which is now in ip6_dst. We need to re-arrange 24820Sstevel@tonic-gate * the header to make it look like how it would appear in the 24830Sstevel@tonic-gate * receiver i.e 24840Sstevel@tonic-gate * 24850Sstevel@tonic-gate * Because of ip_massage_options_v6 the header looks like 24860Sstevel@tonic-gate * this : 24870Sstevel@tonic-gate * 24880Sstevel@tonic-gate * ip6_src = S, ip6_dst = I1. followed by I2,I3,D. 24890Sstevel@tonic-gate * 24900Sstevel@tonic-gate * When it reaches the receiver, it would look like 24910Sstevel@tonic-gate * 24920Sstevel@tonic-gate * ip6_src = S, ip6_dst = D. followed by I1,I2,I3. 24930Sstevel@tonic-gate * 24940Sstevel@tonic-gate * NOTE : We assume that there are no problems with the options 24950Sstevel@tonic-gate * as IP should have already checked this. 24960Sstevel@tonic-gate */ 24970Sstevel@tonic-gate 24980Sstevel@tonic-gate oi_opt = (uchar_t *)&oip6h[1]; 24990Sstevel@tonic-gate pi_opt = (uchar_t *)&ip6h[1]; 25000Sstevel@tonic-gate 25010Sstevel@tonic-gate /* 25020Sstevel@tonic-gate * We set the prev_nexthdr properly in the pseudo header. 25030Sstevel@tonic-gate * After we finish authentication and come back from the 25040Sstevel@tonic-gate * algorithm module, pseudo header will become the real 25050Sstevel@tonic-gate * IP header. 25060Sstevel@tonic-gate */ 25070Sstevel@tonic-gate prev_nexthdr = (uint8_t *)&ip6h->ip6_nxt; 25080Sstevel@tonic-gate nexthdr = oip6h->ip6_nxt; 25090Sstevel@tonic-gate /* Assume IP has already stripped it */ 25100Sstevel@tonic-gate ASSERT(nexthdr != IPPROTO_FRAGMENT && nexthdr != IPPROTO_RAW); 25110Sstevel@tonic-gate ah = NULL; 25120Sstevel@tonic-gate dsthdr = NULL; 25130Sstevel@tonic-gate for (;;) { 25140Sstevel@tonic-gate switch (nexthdr) { 25150Sstevel@tonic-gate case IPPROTO_HOPOPTS: 25160Sstevel@tonic-gate hbhhdr = (ip6_hbh_t *)oi_opt; 25170Sstevel@tonic-gate nexthdr = hbhhdr->ip6h_nxt; 25180Sstevel@tonic-gate ehdrlen = 8 * (hbhhdr->ip6h_len + 1); 25190Sstevel@tonic-gate ret = ah_fix_tlv_options_v6(oi_opt, pi_opt, ehdrlen, 25200Sstevel@tonic-gate IPPROTO_HOPOPTS, copy_always); 25210Sstevel@tonic-gate /* 25220Sstevel@tonic-gate * Return a zero offset indicating error if there 25230Sstevel@tonic-gate * was error. 25240Sstevel@tonic-gate */ 25250Sstevel@tonic-gate if (ret == -1) 25260Sstevel@tonic-gate return (0); 25270Sstevel@tonic-gate hbhhdr = (ip6_hbh_t *)pi_opt; 25280Sstevel@tonic-gate prev_nexthdr = (uint8_t *)&hbhhdr->ip6h_nxt; 25290Sstevel@tonic-gate break; 25300Sstevel@tonic-gate case IPPROTO_ROUTING: 25310Sstevel@tonic-gate rthdr = (ip6_rthdr0_t *)oi_opt; 25320Sstevel@tonic-gate nexthdr = rthdr->ip6r0_nxt; 25330Sstevel@tonic-gate ehdrlen = 8 * (rthdr->ip6r0_len + 1); 25340Sstevel@tonic-gate if (!copy_always && outbound) { 25350Sstevel@tonic-gate int i, left; 25360Sstevel@tonic-gate ip6_rthdr0_t *prthdr; 25370Sstevel@tonic-gate in6_addr_t *ap, *pap; 25380Sstevel@tonic-gate 25390Sstevel@tonic-gate left = rthdr->ip6r0_segleft; 25400Sstevel@tonic-gate prthdr = (ip6_rthdr0_t *)pi_opt; 25410Sstevel@tonic-gate pap = (in6_addr_t *)(prthdr + 1); 25420Sstevel@tonic-gate ap = (in6_addr_t *)(rthdr + 1); 25430Sstevel@tonic-gate /* 25440Sstevel@tonic-gate * First eight bytes except seg_left 25450Sstevel@tonic-gate * does not change en route. 25460Sstevel@tonic-gate */ 25470Sstevel@tonic-gate bcopy(oi_opt, pi_opt, 8); 25480Sstevel@tonic-gate prthdr->ip6r0_segleft = 0; 25490Sstevel@tonic-gate /* 25500Sstevel@tonic-gate * First address has been moved to 25510Sstevel@tonic-gate * the destination address of the 25520Sstevel@tonic-gate * ip header by ip_massage_options_v6. 25530Sstevel@tonic-gate * And the real destination address is 25540Sstevel@tonic-gate * in the last address part of the 25550Sstevel@tonic-gate * option. 25560Sstevel@tonic-gate */ 25570Sstevel@tonic-gate *pap = oip6h->ip6_dst; 25580Sstevel@tonic-gate for (i = 1; i < left - 1; i++) 25590Sstevel@tonic-gate pap[i] = ap[i - 1]; 25600Sstevel@tonic-gate ip6h->ip6_dst = *(ap + left - 1); 25610Sstevel@tonic-gate } else { 25620Sstevel@tonic-gate bcopy(oi_opt, pi_opt, ehdrlen); 25630Sstevel@tonic-gate } 25640Sstevel@tonic-gate rthdr = (ip6_rthdr0_t *)pi_opt; 25650Sstevel@tonic-gate prev_nexthdr = (uint8_t *)&rthdr->ip6r0_nxt; 25660Sstevel@tonic-gate break; 25670Sstevel@tonic-gate case IPPROTO_DSTOPTS: 25680Sstevel@tonic-gate /* 25690Sstevel@tonic-gate * Destination options are tricky. If there is 25700Sstevel@tonic-gate * a terminal (e.g. non-IPv6-extension) header 25710Sstevel@tonic-gate * following the destination options, don't 25720Sstevel@tonic-gate * reset prev_nexthdr or advance the AH insertion 25730Sstevel@tonic-gate * point and just treat this as a terminal header. 25740Sstevel@tonic-gate * 25750Sstevel@tonic-gate * If this is an inbound packet, just deal with 25760Sstevel@tonic-gate * it as is. 25770Sstevel@tonic-gate */ 25780Sstevel@tonic-gate dsthdr = (ip6_dest_t *)oi_opt; 25790Sstevel@tonic-gate /* 25800Sstevel@tonic-gate * XXX I hope common-subexpression elimination 25810Sstevel@tonic-gate * saves us the double-evaluate. 25820Sstevel@tonic-gate */ 25830Sstevel@tonic-gate if (outbound && dsthdr->ip6d_nxt != IPPROTO_ROUTING && 25840Sstevel@tonic-gate dsthdr->ip6d_nxt != IPPROTO_HOPOPTS) 25850Sstevel@tonic-gate goto terminal_hdr; 25860Sstevel@tonic-gate nexthdr = dsthdr->ip6d_nxt; 25870Sstevel@tonic-gate ehdrlen = 8 * (dsthdr->ip6d_len + 1); 25880Sstevel@tonic-gate ret = ah_fix_tlv_options_v6(oi_opt, pi_opt, ehdrlen, 25890Sstevel@tonic-gate IPPROTO_DSTOPTS, copy_always); 25900Sstevel@tonic-gate /* 25910Sstevel@tonic-gate * Return a zero offset indicating error if there 25920Sstevel@tonic-gate * was error. 25930Sstevel@tonic-gate */ 25940Sstevel@tonic-gate if (ret == -1) 25950Sstevel@tonic-gate return (0); 25960Sstevel@tonic-gate break; 25970Sstevel@tonic-gate case IPPROTO_AH: 25980Sstevel@tonic-gate /* 25990Sstevel@tonic-gate * Be conservative in what you send. We shouldn't 26000Sstevel@tonic-gate * see two same-scoped AH's in one packet. 26010Sstevel@tonic-gate * (Inner-IP-scoped AH will be hit by terminal 26020Sstevel@tonic-gate * header of IP or IPv6.) 26030Sstevel@tonic-gate */ 26040Sstevel@tonic-gate ASSERT(!outbound); 26050Sstevel@tonic-gate return ((uint_t)(pi_opt - (uint8_t *)ip6h)); 26060Sstevel@tonic-gate default: 26070Sstevel@tonic-gate ASSERT(outbound); 26080Sstevel@tonic-gate terminal_hdr: 26090Sstevel@tonic-gate *prev_nexthdr = IPPROTO_AH; 26100Sstevel@tonic-gate ah = (ah_t *)pi_opt; 26110Sstevel@tonic-gate ah->ah_nexthdr = nexthdr; 26120Sstevel@tonic-gate return ((uint_t)(pi_opt - (uint8_t *)ip6h)); 26130Sstevel@tonic-gate } 26140Sstevel@tonic-gate pi_opt += ehdrlen; 26150Sstevel@tonic-gate oi_opt += ehdrlen; 26160Sstevel@tonic-gate } 26170Sstevel@tonic-gate /* NOTREACHED */ 26180Sstevel@tonic-gate } 26190Sstevel@tonic-gate 26200Sstevel@tonic-gate static boolean_t 26210Sstevel@tonic-gate ah_finish_up(ah_t *phdr_ah, ah_t *inbound_ah, ipsa_t *assoc, 26223448Sdh155122 int ah_data_sz, int ah_align_sz, ipsecah_stack_t *ahstack) 26230Sstevel@tonic-gate { 26240Sstevel@tonic-gate int i; 26250Sstevel@tonic-gate 26260Sstevel@tonic-gate /* 26270Sstevel@tonic-gate * Padding : 26280Sstevel@tonic-gate * 26290Sstevel@tonic-gate * 1) Authentication data may have to be padded 26300Sstevel@tonic-gate * before ICV calculation if ICV is not a multiple 26310Sstevel@tonic-gate * of 64 bits. This padding is arbitrary and transmitted 26320Sstevel@tonic-gate * with the packet at the end of the authentication data. 26330Sstevel@tonic-gate * Payload length should include the padding bytes. 26340Sstevel@tonic-gate * 26350Sstevel@tonic-gate * 2) Explicit padding of the whole datagram may be 26360Sstevel@tonic-gate * required by the algorithm which need not be 26370Sstevel@tonic-gate * transmitted. It is assumed that this will be taken 26380Sstevel@tonic-gate * care by the algorithm module. 26390Sstevel@tonic-gate */ 26400Sstevel@tonic-gate bzero(phdr_ah + 1, ah_data_sz); /* Zero out ICV for pseudo-hdr. */ 26410Sstevel@tonic-gate 26420Sstevel@tonic-gate if (inbound_ah == NULL) { 26430Sstevel@tonic-gate /* Outbound AH datagram. */ 26440Sstevel@tonic-gate 26450Sstevel@tonic-gate phdr_ah->ah_length = (ah_align_sz >> 2) + 1; 26460Sstevel@tonic-gate phdr_ah->ah_reserved = 0; 26470Sstevel@tonic-gate phdr_ah->ah_spi = assoc->ipsa_spi; 26480Sstevel@tonic-gate 26490Sstevel@tonic-gate phdr_ah->ah_replay = 26500Sstevel@tonic-gate htonl(atomic_add_32_nv(&assoc->ipsa_replay, 1)); 26510Sstevel@tonic-gate if (phdr_ah->ah_replay == 0 && assoc->ipsa_replay_wsize != 0) { 26520Sstevel@tonic-gate /* 26530Sstevel@tonic-gate * XXX We have replay counter wrapping. We probably 26540Sstevel@tonic-gate * want to nuke this SA (and its peer). 26550Sstevel@tonic-gate */ 26560Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, 26570Sstevel@tonic-gate SL_ERROR | SL_CONSOLE | SL_WARN, 26580Sstevel@tonic-gate "Outbound AH SA (0x%x), dst %s has wrapped " 26590Sstevel@tonic-gate "sequence.\n", phdr_ah->ah_spi, 26603448Sdh155122 assoc->ipsa_dstaddr, assoc->ipsa_addrfam, 26613448Sdh155122 ahstack->ipsecah_netstack); 26620Sstevel@tonic-gate 26630Sstevel@tonic-gate sadb_replay_delete(assoc); 26640Sstevel@tonic-gate /* Caller will free phdr_mp and return NULL. */ 26650Sstevel@tonic-gate return (B_FALSE); 26660Sstevel@tonic-gate } 26670Sstevel@tonic-gate 26680Sstevel@tonic-gate if (ah_data_sz != ah_align_sz) { 26690Sstevel@tonic-gate uchar_t *pad = ((uchar_t *)phdr_ah + sizeof (ah_t) + 26700Sstevel@tonic-gate ah_data_sz); 26710Sstevel@tonic-gate 26720Sstevel@tonic-gate for (i = 0; i < (ah_align_sz - ah_data_sz); i++) { 26730Sstevel@tonic-gate pad[i] = (uchar_t)i; /* Fill the padding */ 26740Sstevel@tonic-gate } 26750Sstevel@tonic-gate } 26760Sstevel@tonic-gate } else { 26770Sstevel@tonic-gate /* Inbound AH datagram. */ 26780Sstevel@tonic-gate phdr_ah->ah_nexthdr = inbound_ah->ah_nexthdr; 26790Sstevel@tonic-gate phdr_ah->ah_length = inbound_ah->ah_length; 26800Sstevel@tonic-gate phdr_ah->ah_reserved = 0; 26810Sstevel@tonic-gate ASSERT(inbound_ah->ah_spi == assoc->ipsa_spi); 26820Sstevel@tonic-gate phdr_ah->ah_spi = inbound_ah->ah_spi; 26830Sstevel@tonic-gate phdr_ah->ah_replay = inbound_ah->ah_replay; 26840Sstevel@tonic-gate 26850Sstevel@tonic-gate if (ah_data_sz != ah_align_sz) { 26863448Sdh155122 uchar_t *opad = ((uchar_t *)inbound_ah + 26873448Sdh155122 sizeof (ah_t) + ah_data_sz); 26880Sstevel@tonic-gate uchar_t *pad = ((uchar_t *)phdr_ah + sizeof (ah_t) + 26890Sstevel@tonic-gate ah_data_sz); 26900Sstevel@tonic-gate 26910Sstevel@tonic-gate for (i = 0; i < (ah_align_sz - ah_data_sz); i++) { 26920Sstevel@tonic-gate pad[i] = opad[i]; /* Copy the padding */ 26930Sstevel@tonic-gate } 26940Sstevel@tonic-gate } 26950Sstevel@tonic-gate } 26960Sstevel@tonic-gate 26970Sstevel@tonic-gate return (B_TRUE); 26980Sstevel@tonic-gate } 26990Sstevel@tonic-gate 27000Sstevel@tonic-gate /* 27010Sstevel@tonic-gate * Called upon failing the inbound ICV check. The message passed as 27020Sstevel@tonic-gate * argument is freed. 27030Sstevel@tonic-gate */ 27040Sstevel@tonic-gate static void 27050Sstevel@tonic-gate ah_log_bad_auth(mblk_t *ipsec_in) 27060Sstevel@tonic-gate { 27070Sstevel@tonic-gate mblk_t *mp = ipsec_in->b_cont->b_cont; 27080Sstevel@tonic-gate ipsec_in_t *ii = (ipsec_in_t *)ipsec_in->b_rptr; 27090Sstevel@tonic-gate boolean_t isv4 = ii->ipsec_in_v4; 27100Sstevel@tonic-gate ipsa_t *assoc = ii->ipsec_in_ah_sa; 27110Sstevel@tonic-gate int af; 27120Sstevel@tonic-gate void *addr; 27133448Sdh155122 netstack_t *ns = ii->ipsec_in_ns; 27143448Sdh155122 ipsecah_stack_t *ahstack = ns->netstack_ipsecah; 27153448Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 27160Sstevel@tonic-gate 27170Sstevel@tonic-gate mp->b_rptr -= ii->ipsec_in_skip_len; 27180Sstevel@tonic-gate 27190Sstevel@tonic-gate if (isv4) { 27200Sstevel@tonic-gate ipha_t *ipha = (ipha_t *)mp->b_rptr; 27210Sstevel@tonic-gate addr = &ipha->ipha_dst; 27220Sstevel@tonic-gate af = AF_INET; 27230Sstevel@tonic-gate } else { 27240Sstevel@tonic-gate ip6_t *ip6h = (ip6_t *)mp->b_rptr; 27250Sstevel@tonic-gate addr = &ip6h->ip6_dst; 27260Sstevel@tonic-gate af = AF_INET6; 27270Sstevel@tonic-gate } 27280Sstevel@tonic-gate 27290Sstevel@tonic-gate /* 27300Sstevel@tonic-gate * Log the event. Don't print to the console, block 27310Sstevel@tonic-gate * potential denial-of-service attack. 27320Sstevel@tonic-gate */ 27333448Sdh155122 AH_BUMP_STAT(ahstack, bad_auth); 27340Sstevel@tonic-gate 27350Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, SL_ERROR | SL_WARN, 27360Sstevel@tonic-gate "AH Authentication failed spi %x, dst_addr %s", 27373448Sdh155122 assoc->ipsa_spi, addr, af, ahstack->ipsecah_netstack); 27383448Sdh155122 27393448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 27403448Sdh155122 ip_drop_packet(ipsec_in, B_TRUE, NULL, NULL, 27413448Sdh155122 DROPPER(ipss, ipds_ah_bad_auth), 27423448Sdh155122 &ahstack->ah_dropper); 27430Sstevel@tonic-gate } 27440Sstevel@tonic-gate 27450Sstevel@tonic-gate /* 27460Sstevel@tonic-gate * Kernel crypto framework callback invoked after completion of async 27470Sstevel@tonic-gate * crypto requests. 27480Sstevel@tonic-gate */ 27490Sstevel@tonic-gate static void 27500Sstevel@tonic-gate ah_kcf_callback(void *arg, int status) 27510Sstevel@tonic-gate { 27520Sstevel@tonic-gate mblk_t *ipsec_mp = (mblk_t *)arg; 27530Sstevel@tonic-gate ipsec_in_t *ii = (ipsec_in_t *)ipsec_mp->b_rptr; 27540Sstevel@tonic-gate boolean_t is_inbound = (ii->ipsec_in_type == IPSEC_IN); 27553448Sdh155122 netstackid_t stackid; 27563448Sdh155122 netstack_t *ns, *ns_arg; 27573448Sdh155122 ipsec_stack_t *ipss; 27583448Sdh155122 ipsecah_stack_t *ahstack; 27593448Sdh155122 ipsec_out_t *io = (ipsec_out_t *)ii; 27600Sstevel@tonic-gate 27610Sstevel@tonic-gate ASSERT(ipsec_mp->b_cont != NULL); 27620Sstevel@tonic-gate 27633448Sdh155122 if (is_inbound) { 27643448Sdh155122 stackid = ii->ipsec_in_stackid; 27653448Sdh155122 ns_arg = ii->ipsec_in_ns; 27663448Sdh155122 } else { 27673448Sdh155122 stackid = io->ipsec_out_stackid; 27683448Sdh155122 ns_arg = io->ipsec_out_ns; 27693448Sdh155122 } 27703448Sdh155122 /* 27713448Sdh155122 * Verify that the netstack is still around; could have vanished 27723448Sdh155122 * while kEf was doing its work. 27733448Sdh155122 */ 27743448Sdh155122 ns = netstack_find_by_stackid(stackid); 27753448Sdh155122 if (ns == NULL || ns != ns_arg) { 27763448Sdh155122 /* Disappeared on us */ 27773448Sdh155122 if (ns != NULL) 27783448Sdh155122 netstack_rele(ns); 27793448Sdh155122 freemsg(ipsec_mp); 27803448Sdh155122 return; 27813448Sdh155122 } 27823448Sdh155122 27833448Sdh155122 ahstack = ns->netstack_ipsecah; 27843448Sdh155122 ipss = ns->netstack_ipsec; 27853448Sdh155122 27860Sstevel@tonic-gate if (status == CRYPTO_SUCCESS) { 27870Sstevel@tonic-gate if (is_inbound) { 27883448Sdh155122 if (ah_auth_in_done(ipsec_mp) != IPSEC_STATUS_SUCCESS) { 27893448Sdh155122 netstack_rele(ns); 27900Sstevel@tonic-gate return; 27913448Sdh155122 } 27920Sstevel@tonic-gate /* finish IPsec processing */ 27930Sstevel@tonic-gate ip_fanout_proto_again(ipsec_mp, NULL, NULL, NULL); 27940Sstevel@tonic-gate } else { 27950Sstevel@tonic-gate ipha_t *ipha; 27960Sstevel@tonic-gate 27973448Sdh155122 if (ah_auth_out_done(ipsec_mp) != 27983448Sdh155122 IPSEC_STATUS_SUCCESS) { 27993448Sdh155122 netstack_rele(ns); 28000Sstevel@tonic-gate return; 28013448Sdh155122 } 28020Sstevel@tonic-gate 28030Sstevel@tonic-gate /* finish IPsec processing */ 28040Sstevel@tonic-gate ipha = (ipha_t *)ipsec_mp->b_cont->b_rptr; 28050Sstevel@tonic-gate if (IPH_HDR_VERSION(ipha) == IP_VERSION) { 28060Sstevel@tonic-gate ip_wput_ipsec_out(NULL, ipsec_mp, ipha, NULL, 28070Sstevel@tonic-gate NULL); 28080Sstevel@tonic-gate } else { 28090Sstevel@tonic-gate ip6_t *ip6h = (ip6_t *)ipha; 28100Sstevel@tonic-gate ip_wput_ipsec_out_v6(NULL, ipsec_mp, ip6h, 28110Sstevel@tonic-gate NULL, NULL); 28120Sstevel@tonic-gate } 28130Sstevel@tonic-gate } 28140Sstevel@tonic-gate 28150Sstevel@tonic-gate } else if (status == CRYPTO_INVALID_MAC) { 28160Sstevel@tonic-gate ah_log_bad_auth(ipsec_mp); 28170Sstevel@tonic-gate } else { 28183448Sdh155122 ah1dbg(ahstack, ("ah_kcf_callback: crypto failed with 0x%x\n", 28194987Sdanmcd status)); 28203448Sdh155122 AH_BUMP_STAT(ahstack, crypto_failures); 28210Sstevel@tonic-gate if (is_inbound) 28223448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 28230Sstevel@tonic-gate else 28243448Sdh155122 AH_BUMP_STAT(ahstack, out_discards); 28250Sstevel@tonic-gate ip_drop_packet(ipsec_mp, is_inbound, NULL, NULL, 28263448Sdh155122 DROPPER(ipss, ipds_ah_crypto_failed), 28273448Sdh155122 &ahstack->ah_dropper); 28280Sstevel@tonic-gate } 28293448Sdh155122 netstack_rele(ns); 28300Sstevel@tonic-gate } 28310Sstevel@tonic-gate 28320Sstevel@tonic-gate /* 28330Sstevel@tonic-gate * Invoked on kernel crypto failure during inbound and outbound processing. 28340Sstevel@tonic-gate */ 28350Sstevel@tonic-gate static void 28363448Sdh155122 ah_crypto_failed(mblk_t *mp, boolean_t is_inbound, int kef_rc, 28373448Sdh155122 ipsecah_stack_t *ahstack) 28380Sstevel@tonic-gate { 28393448Sdh155122 ipsec_stack_t *ipss = ahstack->ipsecah_netstack->netstack_ipsec; 28403448Sdh155122 28413448Sdh155122 ah1dbg(ahstack, ("crypto failed for %s AH with 0x%x\n", 28420Sstevel@tonic-gate is_inbound ? "inbound" : "outbound", kef_rc)); 28433448Sdh155122 ip_drop_packet(mp, is_inbound, NULL, NULL, 28443448Sdh155122 DROPPER(ipss, ipds_ah_crypto_failed), 28453448Sdh155122 &ahstack->ah_dropper); 28463448Sdh155122 AH_BUMP_STAT(ahstack, crypto_failures); 28470Sstevel@tonic-gate if (is_inbound) 28483448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 28490Sstevel@tonic-gate else 28503448Sdh155122 AH_BUMP_STAT(ahstack, out_discards); 28510Sstevel@tonic-gate } 28520Sstevel@tonic-gate 28530Sstevel@tonic-gate /* 28540Sstevel@tonic-gate * Helper macros for the ah_submit_req_{inbound,outbound}() functions. 28550Sstevel@tonic-gate */ 28560Sstevel@tonic-gate 28573448Sdh155122 #define AH_INIT_CALLREQ(_cr, _ipss) { \ 28580Sstevel@tonic-gate (_cr)->cr_flag = CRYPTO_SKIP_REQID|CRYPTO_RESTRICTED; \ 28593448Sdh155122 if ((_ipss)->ipsec_algs_exec_mode[IPSEC_ALG_AUTH] == \ 28603448Sdh155122 IPSEC_ALGS_EXEC_ASYNC) \ 28610Sstevel@tonic-gate (_cr)->cr_flag |= CRYPTO_ALWAYS_QUEUE; \ 28620Sstevel@tonic-gate (_cr)->cr_callback_arg = ipsec_mp; \ 28630Sstevel@tonic-gate (_cr)->cr_callback_func = ah_kcf_callback; \ 28640Sstevel@tonic-gate } 28650Sstevel@tonic-gate 28660Sstevel@tonic-gate #define AH_INIT_CRYPTO_DATA(data, msglen, mblk) { \ 28670Sstevel@tonic-gate (data)->cd_format = CRYPTO_DATA_MBLK; \ 28680Sstevel@tonic-gate (data)->cd_mp = mblk; \ 28690Sstevel@tonic-gate (data)->cd_offset = 0; \ 28700Sstevel@tonic-gate (data)->cd_length = msglen; \ 28710Sstevel@tonic-gate } 28720Sstevel@tonic-gate 28730Sstevel@tonic-gate #define AH_INIT_CRYPTO_MAC(mac, icvlen, icvbuf) { \ 28740Sstevel@tonic-gate (mac)->cd_format = CRYPTO_DATA_RAW; \ 28750Sstevel@tonic-gate (mac)->cd_offset = 0; \ 28760Sstevel@tonic-gate (mac)->cd_length = icvlen; \ 28770Sstevel@tonic-gate (mac)->cd_raw.iov_base = icvbuf; \ 28780Sstevel@tonic-gate (mac)->cd_raw.iov_len = icvlen; \ 28790Sstevel@tonic-gate } 28800Sstevel@tonic-gate 28810Sstevel@tonic-gate /* 28820Sstevel@tonic-gate * Submit an inbound packet for processing by the crypto framework. 28830Sstevel@tonic-gate */ 28840Sstevel@tonic-gate static ipsec_status_t 28850Sstevel@tonic-gate ah_submit_req_inbound(mblk_t *ipsec_mp, size_t skip_len, uint32_t ah_offset, 28860Sstevel@tonic-gate ipsa_t *assoc) 28870Sstevel@tonic-gate { 28880Sstevel@tonic-gate int kef_rc; 28890Sstevel@tonic-gate mblk_t *phdr_mp; 28900Sstevel@tonic-gate crypto_call_req_t call_req; 28910Sstevel@tonic-gate ipsec_in_t *ii = (ipsec_in_t *)ipsec_mp->b_rptr; 28920Sstevel@tonic-gate uint_t icv_len = assoc->ipsa_mac_len; 28930Sstevel@tonic-gate crypto_ctx_template_t ctx_tmpl; 28943448Sdh155122 netstack_t *ns = ii->ipsec_in_ns; 28953448Sdh155122 ipsecah_stack_t *ahstack = ns->netstack_ipsecah; 28963448Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 28970Sstevel@tonic-gate 28980Sstevel@tonic-gate phdr_mp = ipsec_mp->b_cont; 28990Sstevel@tonic-gate ASSERT(phdr_mp != NULL); 29000Sstevel@tonic-gate ASSERT(ii->ipsec_in_type == IPSEC_IN); 29010Sstevel@tonic-gate 29023448Sdh155122 /* 29033448Sdh155122 * In case kEF queues and calls back, keep netstackid_t for 29043448Sdh155122 * verification that the IP instance is still around in 29053448Sdh155122 * ah_kcf_callback(). 29063448Sdh155122 */ 29073448Sdh155122 ii->ipsec_in_stackid = ns->netstack_stackid; 29083448Sdh155122 29090Sstevel@tonic-gate /* init arguments for the crypto framework */ 29100Sstevel@tonic-gate AH_INIT_CRYPTO_DATA(&ii->ipsec_in_crypto_data, AH_MSGSIZE(phdr_mp), 29110Sstevel@tonic-gate phdr_mp); 29120Sstevel@tonic-gate 29130Sstevel@tonic-gate AH_INIT_CRYPTO_MAC(&ii->ipsec_in_crypto_mac, icv_len, 29140Sstevel@tonic-gate (char *)phdr_mp->b_cont->b_rptr - skip_len + ah_offset + 29150Sstevel@tonic-gate sizeof (ah_t)); 29160Sstevel@tonic-gate 29173448Sdh155122 AH_INIT_CALLREQ(&call_req, ipss); 29180Sstevel@tonic-gate 29190Sstevel@tonic-gate ii->ipsec_in_skip_len = skip_len; 29200Sstevel@tonic-gate 29210Sstevel@tonic-gate IPSEC_CTX_TMPL(assoc, ipsa_authtmpl, IPSEC_ALG_AUTH, ctx_tmpl); 29220Sstevel@tonic-gate 29230Sstevel@tonic-gate /* call KEF to do the MAC operation */ 29240Sstevel@tonic-gate kef_rc = crypto_mac_verify(&assoc->ipsa_amech, 29250Sstevel@tonic-gate &ii->ipsec_in_crypto_data, &assoc->ipsa_kcfauthkey, ctx_tmpl, 29260Sstevel@tonic-gate &ii->ipsec_in_crypto_mac, &call_req); 29270Sstevel@tonic-gate 29280Sstevel@tonic-gate switch (kef_rc) { 29290Sstevel@tonic-gate case CRYPTO_SUCCESS: 29303448Sdh155122 AH_BUMP_STAT(ahstack, crypto_sync); 29310Sstevel@tonic-gate return (ah_auth_in_done(ipsec_mp)); 29320Sstevel@tonic-gate case CRYPTO_QUEUED: 29333448Sdh155122 /* ah_kcf_callback() will be invoked on completion */ 29343448Sdh155122 AH_BUMP_STAT(ahstack, crypto_async); 29350Sstevel@tonic-gate return (IPSEC_STATUS_PENDING); 29360Sstevel@tonic-gate case CRYPTO_INVALID_MAC: 29373448Sdh155122 AH_BUMP_STAT(ahstack, crypto_sync); 29380Sstevel@tonic-gate ah_log_bad_auth(ipsec_mp); 29390Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 29400Sstevel@tonic-gate } 29410Sstevel@tonic-gate 29423448Sdh155122 ah_crypto_failed(ipsec_mp, B_TRUE, kef_rc, ahstack); 29430Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 29440Sstevel@tonic-gate } 29450Sstevel@tonic-gate 29460Sstevel@tonic-gate /* 29470Sstevel@tonic-gate * Submit an outbound packet for processing by the crypto framework. 29480Sstevel@tonic-gate */ 29490Sstevel@tonic-gate static ipsec_status_t 29500Sstevel@tonic-gate ah_submit_req_outbound(mblk_t *ipsec_mp, size_t skip_len, ipsa_t *assoc) 29510Sstevel@tonic-gate { 29520Sstevel@tonic-gate int kef_rc; 29530Sstevel@tonic-gate mblk_t *phdr_mp; 29540Sstevel@tonic-gate crypto_call_req_t call_req; 29550Sstevel@tonic-gate ipsec_out_t *io = (ipsec_out_t *)ipsec_mp->b_rptr; 29560Sstevel@tonic-gate uint_t icv_len = assoc->ipsa_mac_len; 29573448Sdh155122 netstack_t *ns = io->ipsec_out_ns; 29583448Sdh155122 ipsecah_stack_t *ahstack = ns->netstack_ipsecah; 29593448Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 29600Sstevel@tonic-gate 29610Sstevel@tonic-gate phdr_mp = ipsec_mp->b_cont; 29620Sstevel@tonic-gate ASSERT(phdr_mp != NULL); 29630Sstevel@tonic-gate ASSERT(io->ipsec_out_type == IPSEC_OUT); 29640Sstevel@tonic-gate 29653448Sdh155122 /* 29663448Sdh155122 * In case kEF queues and calls back, keep netstackid_t for 29673448Sdh155122 * verification that the IP instance is still around in 29683448Sdh155122 * ah_kcf_callback(). 29693448Sdh155122 */ 29703448Sdh155122 io->ipsec_out_stackid = ns->netstack_stackid; 29713448Sdh155122 29720Sstevel@tonic-gate /* init arguments for the crypto framework */ 29730Sstevel@tonic-gate AH_INIT_CRYPTO_DATA(&io->ipsec_out_crypto_data, AH_MSGSIZE(phdr_mp), 29740Sstevel@tonic-gate phdr_mp); 29750Sstevel@tonic-gate 29760Sstevel@tonic-gate AH_INIT_CRYPTO_MAC(&io->ipsec_out_crypto_mac, icv_len, 29770Sstevel@tonic-gate (char *)phdr_mp->b_wptr); 29780Sstevel@tonic-gate 29793448Sdh155122 AH_INIT_CALLREQ(&call_req, ipss); 29800Sstevel@tonic-gate 29810Sstevel@tonic-gate io->ipsec_out_skip_len = skip_len; 29820Sstevel@tonic-gate 29830Sstevel@tonic-gate ASSERT(io->ipsec_out_ah_sa != NULL); 29840Sstevel@tonic-gate 29850Sstevel@tonic-gate /* call KEF to do the MAC operation */ 29860Sstevel@tonic-gate kef_rc = crypto_mac(&assoc->ipsa_amech, &io->ipsec_out_crypto_data, 29870Sstevel@tonic-gate &assoc->ipsa_kcfauthkey, assoc->ipsa_authtmpl, 29880Sstevel@tonic-gate &io->ipsec_out_crypto_mac, &call_req); 29890Sstevel@tonic-gate 29900Sstevel@tonic-gate switch (kef_rc) { 29910Sstevel@tonic-gate case CRYPTO_SUCCESS: 29923448Sdh155122 AH_BUMP_STAT(ahstack, crypto_sync); 29930Sstevel@tonic-gate return (ah_auth_out_done(ipsec_mp)); 29940Sstevel@tonic-gate case CRYPTO_QUEUED: 29953448Sdh155122 /* ah_kcf_callback() will be invoked on completion */ 29963448Sdh155122 AH_BUMP_STAT(ahstack, crypto_async); 29970Sstevel@tonic-gate return (IPSEC_STATUS_PENDING); 29980Sstevel@tonic-gate } 29990Sstevel@tonic-gate 30003448Sdh155122 ah_crypto_failed(ipsec_mp, B_FALSE, kef_rc, ahstack); 30010Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 30020Sstevel@tonic-gate } 30030Sstevel@tonic-gate 30040Sstevel@tonic-gate /* 30050Sstevel@tonic-gate * This function constructs a pseudo header by looking at the IP header 30060Sstevel@tonic-gate * and options if any. This is called for both outbound and inbound, 30070Sstevel@tonic-gate * before computing the ICV. 30080Sstevel@tonic-gate */ 30090Sstevel@tonic-gate static mblk_t * 30100Sstevel@tonic-gate ah_process_ip_options_v6(mblk_t *mp, ipsa_t *assoc, int *length_to_skip, 30113448Sdh155122 uint_t ah_data_sz, boolean_t outbound, ipsecah_stack_t *ahstack) 30120Sstevel@tonic-gate { 30130Sstevel@tonic-gate ip6_t *ip6h; 30140Sstevel@tonic-gate ip6_t *oip6h; 30150Sstevel@tonic-gate mblk_t *phdr_mp; 30160Sstevel@tonic-gate int option_length; 30170Sstevel@tonic-gate uint_t ah_align_sz; 30180Sstevel@tonic-gate uint_t ah_offset; 30190Sstevel@tonic-gate int hdr_size; 30203448Sdh155122 ipsec_stack_t *ipss = ahstack->ipsecah_netstack->netstack_ipsec; 30210Sstevel@tonic-gate 30220Sstevel@tonic-gate /* 30230Sstevel@tonic-gate * Allocate space for the authentication data also. It is 30240Sstevel@tonic-gate * useful both during the ICV calculation where we need to 30250Sstevel@tonic-gate * feed in zeroes and while sending the datagram back to IP 30260Sstevel@tonic-gate * where we will be using the same space. 30270Sstevel@tonic-gate * 30280Sstevel@tonic-gate * We need to allocate space for padding bytes if it is not 30290Sstevel@tonic-gate * a multiple of IPV6_PADDING_ALIGN. 30300Sstevel@tonic-gate * 30310Sstevel@tonic-gate * In addition, we allocate space for the ICV computed by 30320Sstevel@tonic-gate * the kernel crypto framework, saving us a separate kmem 30330Sstevel@tonic-gate * allocation down the road. 30340Sstevel@tonic-gate */ 30350Sstevel@tonic-gate 30360Sstevel@tonic-gate ah_align_sz = P2ALIGN(ah_data_sz + IPV6_PADDING_ALIGN - 1, 30370Sstevel@tonic-gate IPV6_PADDING_ALIGN); 30380Sstevel@tonic-gate 30390Sstevel@tonic-gate ASSERT(ah_align_sz >= ah_data_sz); 30400Sstevel@tonic-gate 30410Sstevel@tonic-gate hdr_size = ipsec_ah_get_hdr_size_v6(mp, B_FALSE); 30420Sstevel@tonic-gate option_length = hdr_size - IPV6_HDR_LEN; 30430Sstevel@tonic-gate 30440Sstevel@tonic-gate /* This was not included in ipsec_ah_get_hdr_size_v6() */ 30450Sstevel@tonic-gate hdr_size += (sizeof (ah_t) + ah_align_sz); 30460Sstevel@tonic-gate 30470Sstevel@tonic-gate if (!outbound && (MBLKL(mp) < hdr_size)) { 30480Sstevel@tonic-gate /* 30490Sstevel@tonic-gate * We have post-AH header options in a separate mblk, 30500Sstevel@tonic-gate * a pullup is required. 30510Sstevel@tonic-gate */ 30520Sstevel@tonic-gate if (!pullupmsg(mp, hdr_size)) 30530Sstevel@tonic-gate return (NULL); 30540Sstevel@tonic-gate } 30550Sstevel@tonic-gate 30566048Swy83408 if ((phdr_mp = allocb_cred(hdr_size + ah_data_sz, 30576048Swy83408 DB_CRED(mp))) == NULL) { 30580Sstevel@tonic-gate return (NULL); 30590Sstevel@tonic-gate } 30600Sstevel@tonic-gate 30610Sstevel@tonic-gate oip6h = (ip6_t *)mp->b_rptr; 30620Sstevel@tonic-gate 30630Sstevel@tonic-gate /* 30640Sstevel@tonic-gate * Form the basic IP header first. Zero out the header 30650Sstevel@tonic-gate * so that the mutable fields are zeroed out. 30660Sstevel@tonic-gate */ 30670Sstevel@tonic-gate ip6h = (ip6_t *)phdr_mp->b_rptr; 30680Sstevel@tonic-gate bzero(ip6h, sizeof (ip6_t)); 30690Sstevel@tonic-gate ip6h->ip6_vcf = IPV6_DEFAULT_VERS_AND_FLOW; 30700Sstevel@tonic-gate 30710Sstevel@tonic-gate if (outbound) { 30720Sstevel@tonic-gate /* 30730Sstevel@tonic-gate * Include the size of AH and authentication data. 30740Sstevel@tonic-gate * This is how our recipient would compute the 30750Sstevel@tonic-gate * authentication data. Look at what we do in the 30760Sstevel@tonic-gate * inbound case below. 30770Sstevel@tonic-gate */ 30780Sstevel@tonic-gate ip6h->ip6_plen = htons(ntohs(oip6h->ip6_plen) + 30790Sstevel@tonic-gate sizeof (ah_t) + ah_align_sz); 30800Sstevel@tonic-gate } else { 30810Sstevel@tonic-gate ip6h->ip6_plen = oip6h->ip6_plen; 30820Sstevel@tonic-gate } 30830Sstevel@tonic-gate 30840Sstevel@tonic-gate ip6h->ip6_src = oip6h->ip6_src; 30850Sstevel@tonic-gate ip6h->ip6_dst = oip6h->ip6_dst; 30860Sstevel@tonic-gate 30870Sstevel@tonic-gate *length_to_skip = IPV6_HDR_LEN; 30880Sstevel@tonic-gate if (option_length == 0) { 30890Sstevel@tonic-gate /* Form the AH header */ 30900Sstevel@tonic-gate ip6h->ip6_nxt = IPPROTO_AH; 30910Sstevel@tonic-gate ((ah_t *)(ip6h + 1))->ah_nexthdr = oip6h->ip6_nxt; 30920Sstevel@tonic-gate ah_offset = *length_to_skip; 30930Sstevel@tonic-gate } else { 30940Sstevel@tonic-gate ip6h->ip6_nxt = oip6h->ip6_nxt; 30950Sstevel@tonic-gate /* option_length does not include the AH header's size */ 30960Sstevel@tonic-gate *length_to_skip += option_length; 30970Sstevel@tonic-gate 30980Sstevel@tonic-gate ah_offset = ah_fix_phdr_v6(ip6h, oip6h, outbound, B_FALSE); 30990Sstevel@tonic-gate if (ah_offset == 0) { 31000Sstevel@tonic-gate ip_drop_packet(phdr_mp, !outbound, NULL, NULL, 31013448Sdh155122 DROPPER(ipss, ipds_ah_bad_v6_hdrs), 31023448Sdh155122 &ahstack->ah_dropper); 31030Sstevel@tonic-gate return (NULL); 31040Sstevel@tonic-gate } 31050Sstevel@tonic-gate } 31060Sstevel@tonic-gate 31070Sstevel@tonic-gate if (!ah_finish_up(((ah_t *)((uint8_t *)ip6h + ah_offset)), 31080Sstevel@tonic-gate (outbound ? NULL : ((ah_t *)((uint8_t *)oip6h + ah_offset))), 31093448Sdh155122 assoc, ah_data_sz, ah_align_sz, ahstack)) { 31100Sstevel@tonic-gate freeb(phdr_mp); 31110Sstevel@tonic-gate /* 31120Sstevel@tonic-gate * Returning NULL will tell the caller to 31130Sstevel@tonic-gate * IPSA_REFELE(), free the memory, etc. 31140Sstevel@tonic-gate */ 31150Sstevel@tonic-gate return (NULL); 31160Sstevel@tonic-gate } 31170Sstevel@tonic-gate 31180Sstevel@tonic-gate phdr_mp->b_wptr = ((uint8_t *)ip6h + ah_offset + sizeof (ah_t) + 31190Sstevel@tonic-gate ah_align_sz); 31200Sstevel@tonic-gate if (!outbound) 31210Sstevel@tonic-gate *length_to_skip += sizeof (ah_t) + ah_align_sz; 31220Sstevel@tonic-gate return (phdr_mp); 31230Sstevel@tonic-gate } 31240Sstevel@tonic-gate 31250Sstevel@tonic-gate /* 31260Sstevel@tonic-gate * This function constructs a pseudo header by looking at the IP header 31270Sstevel@tonic-gate * and options if any. This is called for both outbound and inbound, 31280Sstevel@tonic-gate * before computing the ICV. 31290Sstevel@tonic-gate */ 31300Sstevel@tonic-gate static mblk_t * 31310Sstevel@tonic-gate ah_process_ip_options_v4(mblk_t *mp, ipsa_t *assoc, int *length_to_skip, 31323448Sdh155122 uint_t ah_data_sz, boolean_t outbound, ipsecah_stack_t *ahstack) 31330Sstevel@tonic-gate { 31340Sstevel@tonic-gate ipoptp_t opts; 31350Sstevel@tonic-gate uint32_t option_length; 31360Sstevel@tonic-gate ipha_t *ipha; 31370Sstevel@tonic-gate ipha_t *oipha; 31380Sstevel@tonic-gate mblk_t *phdr_mp; 31390Sstevel@tonic-gate int size; 31400Sstevel@tonic-gate uchar_t *optptr; 31410Sstevel@tonic-gate uint8_t optval; 31420Sstevel@tonic-gate uint8_t optlen; 31430Sstevel@tonic-gate ipaddr_t dst; 31440Sstevel@tonic-gate uint32_t v_hlen_tos_len; 31450Sstevel@tonic-gate int ip_hdr_length; 31460Sstevel@tonic-gate uint_t ah_align_sz; 31470Sstevel@tonic-gate uint32_t off; 31480Sstevel@tonic-gate 31490Sstevel@tonic-gate #ifdef _BIG_ENDIAN 31500Sstevel@tonic-gate #define V_HLEN (v_hlen_tos_len >> 24) 31510Sstevel@tonic-gate #else 31520Sstevel@tonic-gate #define V_HLEN (v_hlen_tos_len & 0xFF) 31530Sstevel@tonic-gate #endif 31540Sstevel@tonic-gate 31550Sstevel@tonic-gate oipha = (ipha_t *)mp->b_rptr; 31560Sstevel@tonic-gate v_hlen_tos_len = ((uint32_t *)oipha)[0]; 31570Sstevel@tonic-gate 31580Sstevel@tonic-gate /* 31590Sstevel@tonic-gate * Allocate space for the authentication data also. It is 31600Sstevel@tonic-gate * useful both during the ICV calculation where we need to 31610Sstevel@tonic-gate * feed in zeroes and while sending the datagram back to IP 31620Sstevel@tonic-gate * where we will be using the same space. 31630Sstevel@tonic-gate * 31640Sstevel@tonic-gate * We need to allocate space for padding bytes if it is not 31650Sstevel@tonic-gate * a multiple of IPV4_PADDING_ALIGN. 31660Sstevel@tonic-gate * 31670Sstevel@tonic-gate * In addition, we allocate space for the ICV computed by 31680Sstevel@tonic-gate * the kernel crypto framework, saving us a separate kmem 31690Sstevel@tonic-gate * allocation down the road. 31700Sstevel@tonic-gate */ 31710Sstevel@tonic-gate 31720Sstevel@tonic-gate ah_align_sz = P2ALIGN(ah_data_sz + IPV4_PADDING_ALIGN - 1, 31730Sstevel@tonic-gate IPV4_PADDING_ALIGN); 31740Sstevel@tonic-gate 31750Sstevel@tonic-gate ASSERT(ah_align_sz >= ah_data_sz); 31760Sstevel@tonic-gate 31770Sstevel@tonic-gate size = IP_SIMPLE_HDR_LENGTH + sizeof (ah_t) + ah_align_sz + 31780Sstevel@tonic-gate ah_data_sz; 31790Sstevel@tonic-gate 31800Sstevel@tonic-gate if (V_HLEN != IP_SIMPLE_HDR_VERSION) { 31810Sstevel@tonic-gate option_length = oipha->ipha_version_and_hdr_length - 31820Sstevel@tonic-gate (uint8_t)((IP_VERSION << 4) + 31830Sstevel@tonic-gate IP_SIMPLE_HDR_LENGTH_IN_WORDS); 31840Sstevel@tonic-gate option_length <<= 2; 31850Sstevel@tonic-gate size += option_length; 31860Sstevel@tonic-gate } 31870Sstevel@tonic-gate 31886048Swy83408 if ((phdr_mp = allocb_cred(size, DB_CRED(mp))) == NULL) { 31890Sstevel@tonic-gate return (NULL); 31900Sstevel@tonic-gate } 31910Sstevel@tonic-gate 31920Sstevel@tonic-gate /* 31930Sstevel@tonic-gate * Form the basic IP header first. 31940Sstevel@tonic-gate */ 31950Sstevel@tonic-gate ipha = (ipha_t *)phdr_mp->b_rptr; 31960Sstevel@tonic-gate ipha->ipha_version_and_hdr_length = oipha->ipha_version_and_hdr_length; 31970Sstevel@tonic-gate ipha->ipha_type_of_service = 0; 31980Sstevel@tonic-gate 31990Sstevel@tonic-gate if (outbound) { 32000Sstevel@tonic-gate /* 32010Sstevel@tonic-gate * Include the size of AH and authentication data. 32020Sstevel@tonic-gate * This is how our recipient would compute the 32030Sstevel@tonic-gate * authentication data. Look at what we do in the 32040Sstevel@tonic-gate * inbound case below. 32050Sstevel@tonic-gate */ 32060Sstevel@tonic-gate ipha->ipha_length = ntohs(htons(oipha->ipha_length) + 32070Sstevel@tonic-gate sizeof (ah_t) + ah_align_sz); 32080Sstevel@tonic-gate } else { 32090Sstevel@tonic-gate ipha->ipha_length = oipha->ipha_length; 32100Sstevel@tonic-gate } 32110Sstevel@tonic-gate 32120Sstevel@tonic-gate ipha->ipha_ident = oipha->ipha_ident; 32130Sstevel@tonic-gate ipha->ipha_fragment_offset_and_flags = 0; 32140Sstevel@tonic-gate ipha->ipha_ttl = 0; 32150Sstevel@tonic-gate ipha->ipha_protocol = IPPROTO_AH; 32160Sstevel@tonic-gate ipha->ipha_hdr_checksum = 0; 32170Sstevel@tonic-gate ipha->ipha_src = oipha->ipha_src; 32180Sstevel@tonic-gate ipha->ipha_dst = dst = oipha->ipha_dst; 32190Sstevel@tonic-gate 32200Sstevel@tonic-gate /* 32210Sstevel@tonic-gate * If there is no option to process return now. 32220Sstevel@tonic-gate */ 32230Sstevel@tonic-gate ip_hdr_length = IP_SIMPLE_HDR_LENGTH; 32240Sstevel@tonic-gate 32250Sstevel@tonic-gate if (V_HLEN == IP_SIMPLE_HDR_VERSION) { 32260Sstevel@tonic-gate /* Form the AH header */ 32270Sstevel@tonic-gate goto ah_hdr; 32280Sstevel@tonic-gate } 32290Sstevel@tonic-gate 32300Sstevel@tonic-gate ip_hdr_length += option_length; 32310Sstevel@tonic-gate 32320Sstevel@tonic-gate /* 32330Sstevel@tonic-gate * We have options. In the outbound case for source route, 32340Sstevel@tonic-gate * ULP has already moved the first hop, which is now in 32350Sstevel@tonic-gate * ipha_dst. We need the final destination for the calculation 32360Sstevel@tonic-gate * of authentication data. And also make sure that mutable 32370Sstevel@tonic-gate * and experimental fields are zeroed out in the IP options. 32380Sstevel@tonic-gate */ 32390Sstevel@tonic-gate 32400Sstevel@tonic-gate bcopy(&oipha[1], &ipha[1], option_length); 32410Sstevel@tonic-gate 32420Sstevel@tonic-gate for (optval = ipoptp_first(&opts, ipha); 32430Sstevel@tonic-gate optval != IPOPT_EOL; 32440Sstevel@tonic-gate optval = ipoptp_next(&opts)) { 32450Sstevel@tonic-gate optptr = opts.ipoptp_cur; 32460Sstevel@tonic-gate optlen = opts.ipoptp_len; 32470Sstevel@tonic-gate switch (optval) { 32480Sstevel@tonic-gate case IPOPT_EXTSEC: 32490Sstevel@tonic-gate case IPOPT_COMSEC: 32500Sstevel@tonic-gate case IPOPT_RA: 32510Sstevel@tonic-gate case IPOPT_SDMDD: 32520Sstevel@tonic-gate case IPOPT_SECURITY: 32530Sstevel@tonic-gate /* 32540Sstevel@tonic-gate * These options are Immutable, leave them as-is. 32550Sstevel@tonic-gate * Note that IPOPT_NOP is also Immutable, but it 32560Sstevel@tonic-gate * was skipped by ipoptp_next() and thus remains 32570Sstevel@tonic-gate * intact in the header. 32580Sstevel@tonic-gate */ 32590Sstevel@tonic-gate break; 32600Sstevel@tonic-gate case IPOPT_SSRR: 32610Sstevel@tonic-gate case IPOPT_LSRR: 32620Sstevel@tonic-gate if ((opts.ipoptp_flags & IPOPTP_ERROR) != 0) 32630Sstevel@tonic-gate goto bad_ipv4opt; 32640Sstevel@tonic-gate /* 32650Sstevel@tonic-gate * These two are mutable and will be zeroed, but 32660Sstevel@tonic-gate * first get the final destination. 32670Sstevel@tonic-gate */ 32680Sstevel@tonic-gate off = optptr[IPOPT_OFFSET]; 32690Sstevel@tonic-gate /* 32700Sstevel@tonic-gate * If one of the conditions is true, it means 32710Sstevel@tonic-gate * end of options and dst already has the right 32720Sstevel@tonic-gate * value. So, just fall through. 32730Sstevel@tonic-gate */ 32740Sstevel@tonic-gate if (!(optlen < IP_ADDR_LEN || off > optlen - 3)) { 32750Sstevel@tonic-gate off = optlen - IP_ADDR_LEN; 32760Sstevel@tonic-gate bcopy(&optptr[off], &dst, IP_ADDR_LEN); 32770Sstevel@tonic-gate } 32780Sstevel@tonic-gate /* FALLTHRU */ 32790Sstevel@tonic-gate case IPOPT_RR: 32800Sstevel@tonic-gate case IPOPT_TS: 32810Sstevel@tonic-gate case IPOPT_SATID: 32820Sstevel@tonic-gate default: 32830Sstevel@tonic-gate /* 32840Sstevel@tonic-gate * optlen should include from the beginning of an 32850Sstevel@tonic-gate * option. 32860Sstevel@tonic-gate * NOTE : Stream Identifier Option (SID): RFC 791 32870Sstevel@tonic-gate * shows the bit pattern of optlen as 2 and documents 32880Sstevel@tonic-gate * the length as 4. We assume it to be 2 here. 32890Sstevel@tonic-gate */ 32900Sstevel@tonic-gate bzero(optptr, optlen); 32910Sstevel@tonic-gate break; 32920Sstevel@tonic-gate } 32930Sstevel@tonic-gate } 32940Sstevel@tonic-gate 32950Sstevel@tonic-gate if ((opts.ipoptp_flags & IPOPTP_ERROR) != 0) { 32960Sstevel@tonic-gate bad_ipv4opt: 32973448Sdh155122 ah1dbg(ahstack, ("AH : bad IPv4 option")); 32980Sstevel@tonic-gate freeb(phdr_mp); 32990Sstevel@tonic-gate return (NULL); 33000Sstevel@tonic-gate } 33010Sstevel@tonic-gate 33020Sstevel@tonic-gate /* 33030Sstevel@tonic-gate * Don't change ipha_dst for an inbound datagram as it points 33040Sstevel@tonic-gate * to the right value. Only for the outbound with LSRR/SSRR, 33050Sstevel@tonic-gate * because of ip_massage_options called by the ULP, ipha_dst 33060Sstevel@tonic-gate * points to the first hop and we need to use the final 33070Sstevel@tonic-gate * destination for computing the ICV. 33080Sstevel@tonic-gate */ 33090Sstevel@tonic-gate 33100Sstevel@tonic-gate if (outbound) 33110Sstevel@tonic-gate ipha->ipha_dst = dst; 33120Sstevel@tonic-gate ah_hdr: 33130Sstevel@tonic-gate ((ah_t *)((uint8_t *)ipha + ip_hdr_length))->ah_nexthdr = 33140Sstevel@tonic-gate oipha->ipha_protocol; 33150Sstevel@tonic-gate if (!ah_finish_up(((ah_t *)((uint8_t *)ipha + ip_hdr_length)), 33160Sstevel@tonic-gate (outbound ? NULL : ((ah_t *)((uint8_t *)oipha + ip_hdr_length))), 33173448Sdh155122 assoc, ah_data_sz, ah_align_sz, ahstack)) { 33180Sstevel@tonic-gate freeb(phdr_mp); 33190Sstevel@tonic-gate /* 33200Sstevel@tonic-gate * Returning NULL will tell the caller to IPSA_REFELE(), free 33210Sstevel@tonic-gate * the memory, etc. 33220Sstevel@tonic-gate */ 33230Sstevel@tonic-gate return (NULL); 33240Sstevel@tonic-gate } 33250Sstevel@tonic-gate 33260Sstevel@tonic-gate phdr_mp->b_wptr = ((uchar_t *)ipha + ip_hdr_length + 33270Sstevel@tonic-gate sizeof (ah_t) + ah_align_sz); 33280Sstevel@tonic-gate 33290Sstevel@tonic-gate ASSERT(phdr_mp->b_wptr <= phdr_mp->b_datap->db_lim); 33300Sstevel@tonic-gate if (outbound) 33310Sstevel@tonic-gate *length_to_skip = ip_hdr_length; 33320Sstevel@tonic-gate else 33330Sstevel@tonic-gate *length_to_skip = ip_hdr_length + sizeof (ah_t) + ah_align_sz; 33340Sstevel@tonic-gate return (phdr_mp); 33350Sstevel@tonic-gate } 33360Sstevel@tonic-gate 33370Sstevel@tonic-gate /* 33380Sstevel@tonic-gate * Authenticate an outbound datagram. This function is called 33390Sstevel@tonic-gate * whenever IP sends an outbound datagram that needs authentication. 33400Sstevel@tonic-gate */ 33410Sstevel@tonic-gate static ipsec_status_t 33420Sstevel@tonic-gate ah_outbound(mblk_t *ipsec_out) 33430Sstevel@tonic-gate { 33440Sstevel@tonic-gate mblk_t *mp; 33450Sstevel@tonic-gate mblk_t *phdr_mp; 33460Sstevel@tonic-gate ipsec_out_t *oi; 33470Sstevel@tonic-gate ipsa_t *assoc; 33480Sstevel@tonic-gate int length_to_skip; 33490Sstevel@tonic-gate uint_t ah_align_sz; 33500Sstevel@tonic-gate uint_t age_bytes; 33513448Sdh155122 netstack_t *ns; 33523448Sdh155122 ipsec_stack_t *ipss; 33533448Sdh155122 ipsecah_stack_t *ahstack; 33540Sstevel@tonic-gate 33550Sstevel@tonic-gate /* 33560Sstevel@tonic-gate * Construct the chain of mblks 33570Sstevel@tonic-gate * 33580Sstevel@tonic-gate * IPSEC_OUT->PSEUDO_HDR->DATA 33590Sstevel@tonic-gate * 33600Sstevel@tonic-gate * one by one. 33610Sstevel@tonic-gate */ 33620Sstevel@tonic-gate 33630Sstevel@tonic-gate ASSERT(ipsec_out->b_datap->db_type == M_CTL); 33640Sstevel@tonic-gate 33650Sstevel@tonic-gate ASSERT(MBLKL(ipsec_out) >= sizeof (ipsec_info_t)); 33660Sstevel@tonic-gate 33670Sstevel@tonic-gate mp = ipsec_out->b_cont; 33680Sstevel@tonic-gate oi = (ipsec_out_t *)ipsec_out->b_rptr; 33693448Sdh155122 ns = oi->ipsec_out_ns; 33703448Sdh155122 ipss = ns->netstack_ipsec; 33713448Sdh155122 ahstack = ns->netstack_ipsecah; 33723448Sdh155122 33733448Sdh155122 AH_BUMP_STAT(ahstack, out_requests); 33740Sstevel@tonic-gate 33750Sstevel@tonic-gate ASSERT(mp->b_datap->db_type == M_DATA); 33760Sstevel@tonic-gate 33770Sstevel@tonic-gate assoc = oi->ipsec_out_ah_sa; 33780Sstevel@tonic-gate ASSERT(assoc != NULL); 33790Sstevel@tonic-gate 33800Sstevel@tonic-gate /* 33810Sstevel@tonic-gate * Age SA according to number of bytes that will be sent after 33820Sstevel@tonic-gate * adding the AH header, ICV, and padding to the packet. 33830Sstevel@tonic-gate */ 33840Sstevel@tonic-gate 33850Sstevel@tonic-gate if (oi->ipsec_out_v4) { 33860Sstevel@tonic-gate ipha_t *ipha = (ipha_t *)mp->b_rptr; 33870Sstevel@tonic-gate ah_align_sz = P2ALIGN(assoc->ipsa_mac_len + 33880Sstevel@tonic-gate IPV4_PADDING_ALIGN - 1, IPV4_PADDING_ALIGN); 33890Sstevel@tonic-gate age_bytes = ntohs(ipha->ipha_length) + sizeof (ah_t) + 33900Sstevel@tonic-gate ah_align_sz; 33910Sstevel@tonic-gate } else { 33920Sstevel@tonic-gate ip6_t *ip6h = (ip6_t *)mp->b_rptr; 33930Sstevel@tonic-gate ah_align_sz = P2ALIGN(assoc->ipsa_mac_len + 33940Sstevel@tonic-gate IPV6_PADDING_ALIGN - 1, IPV6_PADDING_ALIGN); 33950Sstevel@tonic-gate age_bytes = sizeof (ip6_t) + ntohs(ip6h->ip6_plen) + 33964987Sdanmcd sizeof (ah_t) + ah_align_sz; 33970Sstevel@tonic-gate } 33980Sstevel@tonic-gate 33990Sstevel@tonic-gate if (!ah_age_bytes(assoc, age_bytes, B_FALSE)) { 34000Sstevel@tonic-gate /* rig things as if ipsec_getassocbyconn() failed */ 34010Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, SL_ERROR | SL_WARN, 34020Sstevel@tonic-gate "AH association 0x%x, dst %s had bytes expire.\n", 34033448Sdh155122 ntohl(assoc->ipsa_spi), assoc->ipsa_dstaddr, AF_INET, 34043448Sdh155122 ahstack->ipsecah_netstack); 34050Sstevel@tonic-gate freemsg(ipsec_out); 34060Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 34070Sstevel@tonic-gate } 34080Sstevel@tonic-gate 34090Sstevel@tonic-gate if (oi->ipsec_out_is_capab_ill) { 34103448Sdh155122 ah3dbg(ahstack, ("ah_outbound: pkt can be accelerated\n")); 34110Sstevel@tonic-gate if (oi->ipsec_out_v4) 34120Sstevel@tonic-gate return (ah_outbound_accelerated_v4(ipsec_out, assoc)); 34130Sstevel@tonic-gate else 34140Sstevel@tonic-gate return (ah_outbound_accelerated_v6(ipsec_out, assoc)); 34150Sstevel@tonic-gate } 34163448Sdh155122 AH_BUMP_STAT(ahstack, noaccel); 34170Sstevel@tonic-gate 34180Sstevel@tonic-gate /* 34190Sstevel@tonic-gate * Insert pseudo header: 34200Sstevel@tonic-gate * IPSEC_INFO -> [IP, ULP] => IPSEC_INFO -> [IP, AH, ICV] -> ULP 34210Sstevel@tonic-gate */ 34220Sstevel@tonic-gate 34230Sstevel@tonic-gate if (oi->ipsec_out_v4) { 34240Sstevel@tonic-gate phdr_mp = ah_process_ip_options_v4(mp, assoc, &length_to_skip, 34253448Sdh155122 assoc->ipsa_mac_len, B_TRUE, ahstack); 34260Sstevel@tonic-gate } else { 34270Sstevel@tonic-gate phdr_mp = ah_process_ip_options_v6(mp, assoc, &length_to_skip, 34283448Sdh155122 assoc->ipsa_mac_len, B_TRUE, ahstack); 34290Sstevel@tonic-gate } 34300Sstevel@tonic-gate 34310Sstevel@tonic-gate if (phdr_mp == NULL) { 34323448Sdh155122 AH_BUMP_STAT(ahstack, out_discards); 34330Sstevel@tonic-gate ip_drop_packet(ipsec_out, B_FALSE, NULL, NULL, 34343448Sdh155122 DROPPER(ipss, ipds_ah_bad_v4_opts), 34353448Sdh155122 &ahstack->ah_dropper); 34360Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 34370Sstevel@tonic-gate } 34380Sstevel@tonic-gate 34390Sstevel@tonic-gate ipsec_out->b_cont = phdr_mp; 34400Sstevel@tonic-gate phdr_mp->b_cont = mp; 34410Sstevel@tonic-gate mp->b_rptr += length_to_skip; 34420Sstevel@tonic-gate 34430Sstevel@tonic-gate /* 34440Sstevel@tonic-gate * At this point ipsec_out points to the IPSEC_OUT, new_mp 34450Sstevel@tonic-gate * points to an mblk containing the pseudo header (IP header, 34460Sstevel@tonic-gate * AH header, and ICV with mutable fields zero'ed out). 34470Sstevel@tonic-gate * mp points to the mblk containing the ULP data. The original 34480Sstevel@tonic-gate * IP header is kept before the ULP data in mp. 34490Sstevel@tonic-gate */ 34500Sstevel@tonic-gate 34510Sstevel@tonic-gate /* submit MAC request to KCF */ 34520Sstevel@tonic-gate return (ah_submit_req_outbound(ipsec_out, length_to_skip, assoc)); 34530Sstevel@tonic-gate } 34540Sstevel@tonic-gate 34550Sstevel@tonic-gate static ipsec_status_t 34560Sstevel@tonic-gate ah_inbound(mblk_t *ipsec_in_mp, void *arg) 34570Sstevel@tonic-gate { 34580Sstevel@tonic-gate mblk_t *data_mp = ipsec_in_mp->b_cont; 34590Sstevel@tonic-gate ipsec_in_t *ii = (ipsec_in_t *)ipsec_in_mp->b_rptr; 34600Sstevel@tonic-gate ah_t *ah = (ah_t *)arg; 34610Sstevel@tonic-gate ipsa_t *assoc = ii->ipsec_in_ah_sa; 34620Sstevel@tonic-gate int length_to_skip; 34630Sstevel@tonic-gate int ah_length; 34640Sstevel@tonic-gate mblk_t *phdr_mp; 34650Sstevel@tonic-gate uint32_t ah_offset; 34663448Sdh155122 netstack_t *ns = ii->ipsec_in_ns; 34673448Sdh155122 ipsecah_stack_t *ahstack = ns->netstack_ipsecah; 34683448Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec; 34690Sstevel@tonic-gate 34700Sstevel@tonic-gate ASSERT(assoc != NULL); 34710Sstevel@tonic-gate 34720Sstevel@tonic-gate /* 34730Sstevel@tonic-gate * We may wish to check replay in-range-only here as an optimization. 34740Sstevel@tonic-gate * Include the reality check of ipsa->ipsa_replay > 34750Sstevel@tonic-gate * ipsa->ipsa_replay_wsize for times when it's the first N packets, 34760Sstevel@tonic-gate * where N == ipsa->ipsa_replay_wsize. 34770Sstevel@tonic-gate * 34780Sstevel@tonic-gate * Another check that may come here later is the "collision" check. 34790Sstevel@tonic-gate * If legitimate packets flow quickly enough, this won't be a problem, 34800Sstevel@tonic-gate * but collisions may cause authentication algorithm crunching to 34810Sstevel@tonic-gate * take place when it doesn't need to. 34820Sstevel@tonic-gate */ 34830Sstevel@tonic-gate if (!sadb_replay_peek(assoc, ah->ah_replay)) { 34843448Sdh155122 AH_BUMP_STAT(ahstack, replay_early_failures); 34853448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 34860Sstevel@tonic-gate ip_drop_packet(ipsec_in_mp, B_TRUE, NULL, NULL, 34873448Sdh155122 DROPPER(ipss, ipds_ah_early_replay), 34883448Sdh155122 &ahstack->ah_dropper); 34890Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 34900Sstevel@tonic-gate } 34910Sstevel@tonic-gate 34920Sstevel@tonic-gate /* 34930Sstevel@tonic-gate * The offset of the AH header can be computed from its pointer 34940Sstevel@tonic-gate * within the data mblk, which was pulled up until the AH header 34950Sstevel@tonic-gate * by ipsec_inbound_ah_sa() during SA selection. 34960Sstevel@tonic-gate */ 34970Sstevel@tonic-gate ah_offset = (uchar_t *)ah - data_mp->b_rptr; 34980Sstevel@tonic-gate 34990Sstevel@tonic-gate /* 35000Sstevel@tonic-gate * Has this packet already been processed by a hardware 35010Sstevel@tonic-gate * IPsec accelerator? 35020Sstevel@tonic-gate */ 35030Sstevel@tonic-gate if (ii->ipsec_in_accelerated) { 35043448Sdh155122 ah3dbg(ahstack, 35053448Sdh155122 ("ah_inbound_v6: pkt processed by ill=%d isv6=%d\n", 35060Sstevel@tonic-gate ii->ipsec_in_ill_index, !ii->ipsec_in_v4)); 35070Sstevel@tonic-gate return (ah_inbound_accelerated(ipsec_in_mp, ii->ipsec_in_v4, 35080Sstevel@tonic-gate assoc, ah_offset)); 35090Sstevel@tonic-gate } 35103448Sdh155122 AH_BUMP_STAT(ahstack, noaccel); 35110Sstevel@tonic-gate 35120Sstevel@tonic-gate /* 35130Sstevel@tonic-gate * We need to pullup until the ICV before we call 35140Sstevel@tonic-gate * ah_process_ip_options_v6. 35150Sstevel@tonic-gate */ 35160Sstevel@tonic-gate ah_length = (ah->ah_length << 2) + 8; 35170Sstevel@tonic-gate 35180Sstevel@tonic-gate /* 35190Sstevel@tonic-gate * NOTE : If we want to use any field of IP/AH header, you need 35200Sstevel@tonic-gate * to re-assign following the pullup. 35210Sstevel@tonic-gate */ 35220Sstevel@tonic-gate if (((uchar_t *)ah + ah_length) > data_mp->b_wptr) { 35230Sstevel@tonic-gate if (!pullupmsg(data_mp, (uchar_t *)ah + ah_length - 35240Sstevel@tonic-gate data_mp->b_rptr)) { 35253448Sdh155122 (void) ipsec_rl_strlog(ns, info.mi_idnum, 0, 0, 35260Sstevel@tonic-gate SL_WARN | SL_ERROR, 35270Sstevel@tonic-gate "ah_inbound: Small AH header\n"); 35283448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 35290Sstevel@tonic-gate ip_drop_packet(ipsec_in_mp, B_TRUE, NULL, NULL, 35303448Sdh155122 DROPPER(ipss, ipds_ah_nomem), 35313448Sdh155122 &ahstack->ah_dropper); 35320Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 35330Sstevel@tonic-gate } 35340Sstevel@tonic-gate } 35350Sstevel@tonic-gate 35360Sstevel@tonic-gate /* 35370Sstevel@tonic-gate * Insert pseudo header: 35380Sstevel@tonic-gate * IPSEC_INFO -> [IP, ULP] => IPSEC_INFO -> [IP, AH, ICV] -> ULP 35390Sstevel@tonic-gate */ 35400Sstevel@tonic-gate if (ii->ipsec_in_v4) { 35410Sstevel@tonic-gate phdr_mp = ah_process_ip_options_v4(data_mp, assoc, 35423448Sdh155122 &length_to_skip, assoc->ipsa_mac_len, B_FALSE, ahstack); 35430Sstevel@tonic-gate } else { 35440Sstevel@tonic-gate phdr_mp = ah_process_ip_options_v6(data_mp, assoc, 35453448Sdh155122 &length_to_skip, assoc->ipsa_mac_len, B_FALSE, ahstack); 35460Sstevel@tonic-gate } 35470Sstevel@tonic-gate 35480Sstevel@tonic-gate if (phdr_mp == NULL) { 35493448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 35500Sstevel@tonic-gate ip_drop_packet(ipsec_in_mp, B_TRUE, NULL, NULL, 35513448Sdh155122 (ii->ipsec_in_v4 ? 35523448Sdh155122 DROPPER(ipss, ipds_ah_bad_v4_opts) : 35533448Sdh155122 DROPPER(ipss, ipds_ah_bad_v6_hdrs)), 35543448Sdh155122 &ahstack->ah_dropper); 35550Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 35560Sstevel@tonic-gate } 35570Sstevel@tonic-gate 35580Sstevel@tonic-gate ipsec_in_mp->b_cont = phdr_mp; 35590Sstevel@tonic-gate phdr_mp->b_cont = data_mp; 35600Sstevel@tonic-gate data_mp->b_rptr += length_to_skip; 35610Sstevel@tonic-gate 35620Sstevel@tonic-gate /* submit request to KCF */ 35630Sstevel@tonic-gate return (ah_submit_req_inbound(ipsec_in_mp, length_to_skip, ah_offset, 35640Sstevel@tonic-gate assoc)); 35650Sstevel@tonic-gate } 35660Sstevel@tonic-gate 35670Sstevel@tonic-gate /* 35680Sstevel@tonic-gate * ah_inbound_accelerated: 35690Sstevel@tonic-gate * Called from ah_inbound() to process IPsec packets that have been 35700Sstevel@tonic-gate * accelerated by hardware. 35710Sstevel@tonic-gate * 35720Sstevel@tonic-gate * Basically does what ah_auth_in_done() with some changes since 35730Sstevel@tonic-gate * no pseudo-headers are involved, i.e. the passed message is a 35740Sstevel@tonic-gate * IPSEC_INFO->DATA. 35750Sstevel@tonic-gate * 35760Sstevel@tonic-gate * It is assumed that only packets that have been successfully 35770Sstevel@tonic-gate * processed by the adapter come here. 35780Sstevel@tonic-gate * 35790Sstevel@tonic-gate * 1. get algorithm structure corresponding to association 35800Sstevel@tonic-gate * 2. calculate pointers to authentication header and ICV 35810Sstevel@tonic-gate * 3. compare ICV in AH header with ICV in data attributes 35820Sstevel@tonic-gate * 3.1 if different: 35830Sstevel@tonic-gate * 3.1.1 generate error 35840Sstevel@tonic-gate * 3.1.2 discard message 35850Sstevel@tonic-gate * 3.2 if ICV matches: 35860Sstevel@tonic-gate * 3.2.1 check replay 35870Sstevel@tonic-gate * 3.2.2 remove AH header 35880Sstevel@tonic-gate * 3.2.3 age SA byte 35890Sstevel@tonic-gate * 3.2.4 send to IP 35900Sstevel@tonic-gate */ 35910Sstevel@tonic-gate ipsec_status_t 35920Sstevel@tonic-gate ah_inbound_accelerated(mblk_t *ipsec_in, boolean_t isv4, ipsa_t *assoc, 35930Sstevel@tonic-gate uint32_t ah_offset) 35940Sstevel@tonic-gate { 35950Sstevel@tonic-gate mblk_t *mp; 35960Sstevel@tonic-gate ipha_t *ipha; 35970Sstevel@tonic-gate ah_t *ah; 35980Sstevel@tonic-gate ipsec_in_t *ii; 35990Sstevel@tonic-gate uint32_t icv_len; 36000Sstevel@tonic-gate uint32_t align_len; 36010Sstevel@tonic-gate uint32_t age_bytes; 36020Sstevel@tonic-gate ip6_t *ip6h; 36030Sstevel@tonic-gate uint8_t *in_icv; 36040Sstevel@tonic-gate mblk_t *hada_mp; 36050Sstevel@tonic-gate uint32_t next_hdr; 36060Sstevel@tonic-gate da_ipsec_t *hada; 36070Sstevel@tonic-gate kstat_named_t *counter; 36083448Sdh155122 ipsecah_stack_t *ahstack; 36093448Sdh155122 netstack_t *ns; 36103448Sdh155122 ipsec_stack_t *ipss; 36110Sstevel@tonic-gate 36120Sstevel@tonic-gate ii = (ipsec_in_t *)ipsec_in->b_rptr; 36133448Sdh155122 ns = ii->ipsec_in_ns; 36143448Sdh155122 ahstack = ns->netstack_ipsecah; 36153448Sdh155122 ipss = ns->netstack_ipsec; 36163448Sdh155122 36170Sstevel@tonic-gate mp = ipsec_in->b_cont; 36180Sstevel@tonic-gate hada_mp = ii->ipsec_in_da; 36190Sstevel@tonic-gate ASSERT(hada_mp != NULL); 36200Sstevel@tonic-gate hada = (da_ipsec_t *)hada_mp->b_rptr; 36210Sstevel@tonic-gate 36223448Sdh155122 AH_BUMP_STAT(ahstack, in_accelerated); 36233448Sdh155122 36240Sstevel@tonic-gate /* 36250Sstevel@tonic-gate * We only support one level of decapsulation in hardware, so 36260Sstevel@tonic-gate * nuke the pointer. 36270Sstevel@tonic-gate */ 36280Sstevel@tonic-gate ii->ipsec_in_da = NULL; 36290Sstevel@tonic-gate ii->ipsec_in_accelerated = B_FALSE; 36300Sstevel@tonic-gate 36310Sstevel@tonic-gate /* 36320Sstevel@tonic-gate * Extract ICV length from attributes M_CTL and sanity check 36330Sstevel@tonic-gate * its value. We allow the mblk to be smaller than da_ipsec_t 36340Sstevel@tonic-gate * for a small ICV, as long as the entire ICV fits within the mblk. 36350Sstevel@tonic-gate * Also ensures that the ICV length computed by Provider 36360Sstevel@tonic-gate * corresponds to the ICV length of the algorithm specified by the SA. 36370Sstevel@tonic-gate */ 36380Sstevel@tonic-gate icv_len = hada->da_icv_len; 36390Sstevel@tonic-gate if ((icv_len != assoc->ipsa_mac_len) || 36400Sstevel@tonic-gate (icv_len > DA_ICV_MAX_LEN) || (MBLKL(hada_mp) < 36414987Sdanmcd (sizeof (da_ipsec_t) - DA_ICV_MAX_LEN + icv_len))) { 36420Sstevel@tonic-gate ah0dbg(("ah_inbound_accelerated: " 36430Sstevel@tonic-gate "ICV len (%u) incorrect or mblk too small (%u)\n", 36440Sstevel@tonic-gate icv_len, (uint32_t)(MBLKL(hada_mp)))); 36453448Sdh155122 counter = DROPPER(ipss, ipds_ah_bad_length); 36460Sstevel@tonic-gate goto ah_in_discard; 36470Sstevel@tonic-gate } 36480Sstevel@tonic-gate ASSERT(icv_len != 0); 36490Sstevel@tonic-gate 36500Sstevel@tonic-gate /* compute the padded AH ICV len */ 36510Sstevel@tonic-gate if (isv4) { 36520Sstevel@tonic-gate ipha = (ipha_t *)mp->b_rptr; 36530Sstevel@tonic-gate align_len = (icv_len + IPV4_PADDING_ALIGN - 1) & 36540Sstevel@tonic-gate -IPV4_PADDING_ALIGN; 36550Sstevel@tonic-gate } else { 36560Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr; 36570Sstevel@tonic-gate align_len = (icv_len + IPV6_PADDING_ALIGN - 1) & 36580Sstevel@tonic-gate -IPV6_PADDING_ALIGN; 36590Sstevel@tonic-gate } 36600Sstevel@tonic-gate 36610Sstevel@tonic-gate ah = (ah_t *)(mp->b_rptr + ah_offset); 36620Sstevel@tonic-gate in_icv = (uint8_t *)ah + sizeof (ah_t); 36630Sstevel@tonic-gate 36640Sstevel@tonic-gate /* compare ICV in AH header vs ICV computed by adapter */ 36650Sstevel@tonic-gate if (bcmp(hada->da_icv, in_icv, icv_len)) { 36660Sstevel@tonic-gate int af; 36670Sstevel@tonic-gate void *addr; 36680Sstevel@tonic-gate 36690Sstevel@tonic-gate if (isv4) { 36700Sstevel@tonic-gate addr = &ipha->ipha_dst; 36710Sstevel@tonic-gate af = AF_INET; 36720Sstevel@tonic-gate } else { 36730Sstevel@tonic-gate addr = &ip6h->ip6_dst; 36740Sstevel@tonic-gate af = AF_INET6; 36750Sstevel@tonic-gate } 36760Sstevel@tonic-gate 36770Sstevel@tonic-gate /* 36780Sstevel@tonic-gate * Log the event. Don't print to the console, block 36790Sstevel@tonic-gate * potential denial-of-service attack. 36800Sstevel@tonic-gate */ 36813448Sdh155122 AH_BUMP_STAT(ahstack, bad_auth); 36820Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, SL_ERROR | SL_WARN, 36830Sstevel@tonic-gate "AH Authentication failed spi %x, dst_addr %s", 36843448Sdh155122 assoc->ipsa_spi, addr, af, ahstack->ipsecah_netstack); 36853448Sdh155122 counter = DROPPER(ipss, ipds_ah_bad_auth); 36860Sstevel@tonic-gate goto ah_in_discard; 36870Sstevel@tonic-gate } 36880Sstevel@tonic-gate 36893448Sdh155122 ah3dbg(ahstack, ("AH succeeded, checking replay\n")); 36903448Sdh155122 AH_BUMP_STAT(ahstack, good_auth); 36910Sstevel@tonic-gate 36920Sstevel@tonic-gate if (!sadb_replay_check(assoc, ah->ah_replay)) { 36930Sstevel@tonic-gate int af; 36940Sstevel@tonic-gate void *addr; 36950Sstevel@tonic-gate 36960Sstevel@tonic-gate if (isv4) { 36970Sstevel@tonic-gate addr = &ipha->ipha_dst; 36980Sstevel@tonic-gate af = AF_INET; 36990Sstevel@tonic-gate } else { 37000Sstevel@tonic-gate addr = &ip6h->ip6_dst; 37010Sstevel@tonic-gate af = AF_INET6; 37020Sstevel@tonic-gate } 37030Sstevel@tonic-gate 37040Sstevel@tonic-gate /* 37050Sstevel@tonic-gate * Log the event. As of now we print out an event. 37060Sstevel@tonic-gate * Do not print the replay failure number, or else 37070Sstevel@tonic-gate * syslog cannot collate the error messages. Printing 37080Sstevel@tonic-gate * the replay number that failed (or printing to the 37090Sstevel@tonic-gate * console) opens a denial-of-service attack. 37100Sstevel@tonic-gate */ 37113448Sdh155122 AH_BUMP_STAT(ahstack, replay_failures); 37120Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, 37130Sstevel@tonic-gate SL_ERROR | SL_WARN, 37140Sstevel@tonic-gate "Replay failed for AH spi %x, dst_addr %s", 37153448Sdh155122 assoc->ipsa_spi, addr, af, ahstack->ipsecah_netstack); 37163448Sdh155122 counter = DROPPER(ipss, ipds_ah_replay); 37170Sstevel@tonic-gate goto ah_in_discard; 37180Sstevel@tonic-gate } 37190Sstevel@tonic-gate 37200Sstevel@tonic-gate /* 37210Sstevel@tonic-gate * Remove AH header. We do this by copying everything before 37220Sstevel@tonic-gate * the AH header onto the AH header+ICV. 37230Sstevel@tonic-gate */ 37240Sstevel@tonic-gate /* overwrite AH with what was preceeding it (IP header) */ 37250Sstevel@tonic-gate next_hdr = ah->ah_nexthdr; 37260Sstevel@tonic-gate ovbcopy(mp->b_rptr, mp->b_rptr + sizeof (ah_t) + align_len, 37270Sstevel@tonic-gate ah_offset); 37280Sstevel@tonic-gate mp->b_rptr += sizeof (ah_t) + align_len; 37290Sstevel@tonic-gate if (isv4) { 37300Sstevel@tonic-gate /* adjust IP header next protocol */ 37310Sstevel@tonic-gate ipha = (ipha_t *)mp->b_rptr; 37320Sstevel@tonic-gate ipha->ipha_protocol = next_hdr; 37330Sstevel@tonic-gate 37340Sstevel@tonic-gate age_bytes = ipha->ipha_length; 37350Sstevel@tonic-gate 37360Sstevel@tonic-gate /* adjust length in IP header */ 37370Sstevel@tonic-gate ipha->ipha_length -= (sizeof (ah_t) + align_len); 37380Sstevel@tonic-gate 37390Sstevel@tonic-gate /* recalculate checksum */ 37400Sstevel@tonic-gate ipha->ipha_hdr_checksum = 0; 37410Sstevel@tonic-gate ipha->ipha_hdr_checksum = (uint16_t)ip_csum_hdr(ipha); 37420Sstevel@tonic-gate } else { 37430Sstevel@tonic-gate /* adjust IP header next protocol */ 37440Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr; 37450Sstevel@tonic-gate ip6h->ip6_nxt = next_hdr; 37460Sstevel@tonic-gate 37470Sstevel@tonic-gate age_bytes = sizeof (ip6_t) + ntohs(ip6h->ip6_plen) + 37480Sstevel@tonic-gate sizeof (ah_t); 37490Sstevel@tonic-gate 37500Sstevel@tonic-gate /* adjust length in IP header */ 37510Sstevel@tonic-gate ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) - 37520Sstevel@tonic-gate (sizeof (ah_t) + align_len)); 37530Sstevel@tonic-gate } 37540Sstevel@tonic-gate 37550Sstevel@tonic-gate /* age SA */ 37560Sstevel@tonic-gate if (!ah_age_bytes(assoc, age_bytes, B_TRUE)) { 37570Sstevel@tonic-gate /* The ipsa has hit hard expiration, LOG and AUDIT. */ 37580Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, 37590Sstevel@tonic-gate SL_ERROR | SL_WARN, 37600Sstevel@tonic-gate "AH Association 0x%x, dst %s had bytes expire.\n", 37610Sstevel@tonic-gate assoc->ipsa_spi, assoc->ipsa_dstaddr, 37623448Sdh155122 AF_INET, ahstack->ipsecah_netstack); 37633448Sdh155122 AH_BUMP_STAT(ahstack, bytes_expired); 37643448Sdh155122 counter = DROPPER(ipss, ipds_ah_bytes_expire); 37650Sstevel@tonic-gate goto ah_in_discard; 37660Sstevel@tonic-gate } 37670Sstevel@tonic-gate 37680Sstevel@tonic-gate freeb(hada_mp); 37690Sstevel@tonic-gate return (IPSEC_STATUS_SUCCESS); 37700Sstevel@tonic-gate 37710Sstevel@tonic-gate ah_in_discard: 37723448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 37730Sstevel@tonic-gate freeb(hada_mp); 37743448Sdh155122 ip_drop_packet(ipsec_in, B_TRUE, NULL, NULL, counter, 37753448Sdh155122 &ahstack->ah_dropper); 37760Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 37770Sstevel@tonic-gate } 37780Sstevel@tonic-gate 37790Sstevel@tonic-gate /* 37800Sstevel@tonic-gate * ah_outbound_accelerated_v4: 37810Sstevel@tonic-gate * Called from ah_outbound_v4() and once it is determined that the 37820Sstevel@tonic-gate * packet is elligible for hardware acceleration. 37830Sstevel@tonic-gate * 37840Sstevel@tonic-gate * We proceed as follows: 37850Sstevel@tonic-gate * 1. allocate and initialize attributes mblk 37860Sstevel@tonic-gate * 2. mark IPSEC_OUT to indicate that pkt is accelerated 37870Sstevel@tonic-gate * 3. insert AH header 37880Sstevel@tonic-gate */ 37890Sstevel@tonic-gate static ipsec_status_t 37900Sstevel@tonic-gate ah_outbound_accelerated_v4(mblk_t *ipsec_mp, ipsa_t *assoc) 37910Sstevel@tonic-gate { 37920Sstevel@tonic-gate mblk_t *mp, *new_mp; 37930Sstevel@tonic-gate ipsec_out_t *oi; 37940Sstevel@tonic-gate uint_t ah_data_sz; /* ICV length, algorithm dependent */ 37950Sstevel@tonic-gate uint_t ah_align_sz; /* ICV length + padding */ 37960Sstevel@tonic-gate uint32_t v_hlen_tos_len; /* from original IP header */ 37970Sstevel@tonic-gate ipha_t *oipha; /* original IP header */ 37980Sstevel@tonic-gate ipha_t *nipha; /* new IP header */ 37990Sstevel@tonic-gate uint_t option_length = 0; 38000Sstevel@tonic-gate uint_t new_hdr_len; /* new header length */ 38010Sstevel@tonic-gate uint_t iphdr_length; 38020Sstevel@tonic-gate ah_t *ah_hdr; /* ptr to AH header */ 38033448Sdh155122 netstack_t *ns; 38043448Sdh155122 ipsec_stack_t *ipss; 38053448Sdh155122 ipsecah_stack_t *ahstack; 38060Sstevel@tonic-gate 38070Sstevel@tonic-gate oi = (ipsec_out_t *)ipsec_mp->b_rptr; 38083448Sdh155122 ns = oi->ipsec_out_ns; 38093448Sdh155122 ipss = ns->netstack_ipsec; 38103448Sdh155122 ahstack = ns->netstack_ipsecah; 38113448Sdh155122 38120Sstevel@tonic-gate mp = ipsec_mp->b_cont; 38130Sstevel@tonic-gate 38143448Sdh155122 AH_BUMP_STAT(ahstack, out_accelerated); 38153448Sdh155122 38160Sstevel@tonic-gate oipha = (ipha_t *)mp->b_rptr; 38170Sstevel@tonic-gate v_hlen_tos_len = ((uint32_t *)oipha)[0]; 38180Sstevel@tonic-gate 38190Sstevel@tonic-gate /* mark packet as being accelerated in IPSEC_OUT */ 38200Sstevel@tonic-gate ASSERT(oi->ipsec_out_accelerated == B_FALSE); 38210Sstevel@tonic-gate oi->ipsec_out_accelerated = B_TRUE; 38220Sstevel@tonic-gate 38230Sstevel@tonic-gate /* calculate authentication data length, i.e. ICV + padding */ 38240Sstevel@tonic-gate ah_data_sz = assoc->ipsa_mac_len; 38250Sstevel@tonic-gate ah_align_sz = (ah_data_sz + IPV4_PADDING_ALIGN - 1) & 38260Sstevel@tonic-gate -IPV4_PADDING_ALIGN; 38270Sstevel@tonic-gate 38280Sstevel@tonic-gate /* 38290Sstevel@tonic-gate * Insert pseudo header: 38300Sstevel@tonic-gate * IPSEC_INFO -> [IP, ULP] => IPSEC_INFO -> [IP, AH, ICV] -> ULP 38310Sstevel@tonic-gate */ 38320Sstevel@tonic-gate 38330Sstevel@tonic-gate /* IP + AH + authentication + padding data length */ 38340Sstevel@tonic-gate new_hdr_len = IP_SIMPLE_HDR_LENGTH + sizeof (ah_t) + ah_align_sz; 38350Sstevel@tonic-gate if (V_HLEN != IP_SIMPLE_HDR_VERSION) { 38360Sstevel@tonic-gate option_length = oipha->ipha_version_and_hdr_length - 38370Sstevel@tonic-gate (uint8_t)((IP_VERSION << 4) + 38380Sstevel@tonic-gate IP_SIMPLE_HDR_LENGTH_IN_WORDS); 38390Sstevel@tonic-gate option_length <<= 2; 38400Sstevel@tonic-gate new_hdr_len += option_length; 38410Sstevel@tonic-gate } 38420Sstevel@tonic-gate 38430Sstevel@tonic-gate /* allocate pseudo-header mblk */ 38440Sstevel@tonic-gate if ((new_mp = allocb(new_hdr_len, BPRI_HI)) == NULL) { 38450Sstevel@tonic-gate /* IPsec kstats: bump bean counter here */ 38460Sstevel@tonic-gate ip_drop_packet(ipsec_mp, B_FALSE, NULL, NULL, 38473448Sdh155122 DROPPER(ipss, ipds_ah_nomem), 38483448Sdh155122 &ahstack->ah_dropper); 38490Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 38500Sstevel@tonic-gate } 38510Sstevel@tonic-gate 38520Sstevel@tonic-gate new_mp->b_cont = mp; 38530Sstevel@tonic-gate ipsec_mp->b_cont = new_mp; 38540Sstevel@tonic-gate new_mp->b_wptr += new_hdr_len; 38550Sstevel@tonic-gate 38560Sstevel@tonic-gate /* copy original IP header to new header */ 38570Sstevel@tonic-gate bcopy(mp->b_rptr, new_mp->b_rptr, IP_SIMPLE_HDR_LENGTH + 38580Sstevel@tonic-gate option_length); 38590Sstevel@tonic-gate 38600Sstevel@tonic-gate /* update IP header */ 38610Sstevel@tonic-gate nipha = (ipha_t *)new_mp->b_rptr; 38620Sstevel@tonic-gate nipha->ipha_protocol = IPPROTO_AH; 38630Sstevel@tonic-gate iphdr_length = ntohs(nipha->ipha_length); 38640Sstevel@tonic-gate iphdr_length += sizeof (ah_t) + ah_align_sz; 38650Sstevel@tonic-gate nipha->ipha_length = htons(iphdr_length); 38660Sstevel@tonic-gate nipha->ipha_hdr_checksum = 0; 38670Sstevel@tonic-gate nipha->ipha_hdr_checksum = (uint16_t)ip_csum_hdr(nipha); 38680Sstevel@tonic-gate 38690Sstevel@tonic-gate /* skip original IP header in mp */ 38700Sstevel@tonic-gate mp->b_rptr += IP_SIMPLE_HDR_LENGTH + option_length; 38710Sstevel@tonic-gate 38720Sstevel@tonic-gate /* initialize AH header */ 38730Sstevel@tonic-gate ah_hdr = (ah_t *)(new_mp->b_rptr + IP_SIMPLE_HDR_LENGTH + 38740Sstevel@tonic-gate option_length); 38750Sstevel@tonic-gate ah_hdr->ah_nexthdr = oipha->ipha_protocol; 38763448Sdh155122 if (!ah_finish_up(ah_hdr, NULL, assoc, ah_data_sz, ah_align_sz, 38773448Sdh155122 ahstack)) { 38780Sstevel@tonic-gate /* Only way this fails is if outbound replay counter wraps. */ 38790Sstevel@tonic-gate ip_drop_packet(ipsec_mp, B_FALSE, NULL, NULL, 38803448Sdh155122 DROPPER(ipss, ipds_ah_replay), 38813448Sdh155122 &ahstack->ah_dropper); 38820Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 38830Sstevel@tonic-gate } 38840Sstevel@tonic-gate 38850Sstevel@tonic-gate return (IPSEC_STATUS_SUCCESS); 38860Sstevel@tonic-gate } 38870Sstevel@tonic-gate 38880Sstevel@tonic-gate /* 38890Sstevel@tonic-gate * ah_outbound_accelerated_v6: 38900Sstevel@tonic-gate * 38910Sstevel@tonic-gate * Called from ah_outbound_v6() once it is determined that the packet 38920Sstevel@tonic-gate * is eligible for hardware acceleration. 38930Sstevel@tonic-gate * 38940Sstevel@tonic-gate * We proceed as follows: 38950Sstevel@tonic-gate * 1. allocate and initialize attributes mblk 38960Sstevel@tonic-gate * 2. mark IPSEC_OUT to indicate that pkt is accelerated 38970Sstevel@tonic-gate * 3. insert AH header 38980Sstevel@tonic-gate */ 38990Sstevel@tonic-gate static ipsec_status_t 39000Sstevel@tonic-gate ah_outbound_accelerated_v6(mblk_t *ipsec_mp, ipsa_t *assoc) 39010Sstevel@tonic-gate { 39020Sstevel@tonic-gate mblk_t *mp, *phdr_mp; 39030Sstevel@tonic-gate ipsec_out_t *oi; 39040Sstevel@tonic-gate uint_t ah_data_sz; /* ICV length, algorithm dependent */ 39050Sstevel@tonic-gate uint_t ah_align_sz; /* ICV length + padding */ 39060Sstevel@tonic-gate ip6_t *oip6h; /* original IP header */ 39070Sstevel@tonic-gate ip6_t *ip6h; /* new IP header */ 39080Sstevel@tonic-gate uint_t option_length = 0; 39090Sstevel@tonic-gate uint_t hdr_size; 39100Sstevel@tonic-gate uint_t ah_offset; 39110Sstevel@tonic-gate ah_t *ah_hdr; /* ptr to AH header */ 39123448Sdh155122 netstack_t *ns; 39133448Sdh155122 ipsec_stack_t *ipss; 39143448Sdh155122 ipsecah_stack_t *ahstack; 39150Sstevel@tonic-gate 39160Sstevel@tonic-gate oi = (ipsec_out_t *)ipsec_mp->b_rptr; 39173448Sdh155122 ns = oi->ipsec_out_ns; 39183448Sdh155122 ipss = ns->netstack_ipsec; 39193448Sdh155122 ahstack = ns->netstack_ipsecah; 39203448Sdh155122 39210Sstevel@tonic-gate mp = ipsec_mp->b_cont; 39220Sstevel@tonic-gate 39233448Sdh155122 AH_BUMP_STAT(ahstack, out_accelerated); 39243448Sdh155122 39250Sstevel@tonic-gate oip6h = (ip6_t *)mp->b_rptr; 39260Sstevel@tonic-gate 39270Sstevel@tonic-gate /* mark packet as being accelerated in IPSEC_OUT */ 39280Sstevel@tonic-gate ASSERT(oi->ipsec_out_accelerated == B_FALSE); 39290Sstevel@tonic-gate oi->ipsec_out_accelerated = B_TRUE; 39300Sstevel@tonic-gate 39310Sstevel@tonic-gate /* calculate authentication data length, i.e. ICV + padding */ 39320Sstevel@tonic-gate ah_data_sz = assoc->ipsa_mac_len; 39330Sstevel@tonic-gate ah_align_sz = (ah_data_sz + IPV4_PADDING_ALIGN - 1) & 39340Sstevel@tonic-gate -IPV4_PADDING_ALIGN; 39350Sstevel@tonic-gate 39360Sstevel@tonic-gate ASSERT(ah_align_sz >= ah_data_sz); 39370Sstevel@tonic-gate 39380Sstevel@tonic-gate hdr_size = ipsec_ah_get_hdr_size_v6(mp, B_FALSE); 39390Sstevel@tonic-gate option_length = hdr_size - IPV6_HDR_LEN; 39400Sstevel@tonic-gate 39410Sstevel@tonic-gate /* This was not included in ipsec_ah_get_hdr_size_v6() */ 39420Sstevel@tonic-gate hdr_size += (sizeof (ah_t) + ah_align_sz); 39430Sstevel@tonic-gate 39440Sstevel@tonic-gate if ((phdr_mp = allocb(hdr_size, BPRI_HI)) == NULL) { 39453448Sdh155122 ip_drop_packet(ipsec_mp, B_FALSE, NULL, NULL, 39463448Sdh155122 DROPPER(ipss, ipds_ah_nomem), 39473448Sdh155122 &ahstack->ah_dropper); 39480Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 39490Sstevel@tonic-gate } 39500Sstevel@tonic-gate phdr_mp->b_wptr += hdr_size; 39510Sstevel@tonic-gate 39520Sstevel@tonic-gate /* 39530Sstevel@tonic-gate * Form the basic IP header first. We always assign every bit 39540Sstevel@tonic-gate * of the v6 basic header, so a separate bzero is unneeded. 39550Sstevel@tonic-gate */ 39560Sstevel@tonic-gate ip6h = (ip6_t *)phdr_mp->b_rptr; 39570Sstevel@tonic-gate ip6h->ip6_vcf = oip6h->ip6_vcf; 39580Sstevel@tonic-gate ip6h->ip6_hlim = oip6h->ip6_hlim; 39590Sstevel@tonic-gate ip6h->ip6_src = oip6h->ip6_src; 39600Sstevel@tonic-gate ip6h->ip6_dst = oip6h->ip6_dst; 39610Sstevel@tonic-gate /* 39620Sstevel@tonic-gate * Include the size of AH and authentication data. 39630Sstevel@tonic-gate * This is how our recipient would compute the 39640Sstevel@tonic-gate * authentication data. Look at what we do in the 39650Sstevel@tonic-gate * inbound case below. 39660Sstevel@tonic-gate */ 39670Sstevel@tonic-gate ip6h->ip6_plen = htons(ntohs(oip6h->ip6_plen) + sizeof (ah_t) + 39680Sstevel@tonic-gate ah_align_sz); 39690Sstevel@tonic-gate 39700Sstevel@tonic-gate /* 39710Sstevel@tonic-gate * Insert pseudo header: 39720Sstevel@tonic-gate * IPSEC_INFO -> [IP6, LLH, ULP] => 39730Sstevel@tonic-gate * IPSEC_INFO -> [IP, LLH, AH, ICV] -> ULP 39740Sstevel@tonic-gate */ 39750Sstevel@tonic-gate 39760Sstevel@tonic-gate if (option_length == 0) { 39770Sstevel@tonic-gate /* Form the AH header */ 39780Sstevel@tonic-gate ip6h->ip6_nxt = IPPROTO_AH; 39790Sstevel@tonic-gate ((ah_t *)(ip6h + 1))->ah_nexthdr = oip6h->ip6_nxt; 39800Sstevel@tonic-gate ah_offset = IPV6_HDR_LEN; 39810Sstevel@tonic-gate } else { 39820Sstevel@tonic-gate ip6h->ip6_nxt = oip6h->ip6_nxt; 39830Sstevel@tonic-gate /* option_length does not include the AH header's size */ 39840Sstevel@tonic-gate ah_offset = ah_fix_phdr_v6(ip6h, oip6h, B_TRUE, B_FALSE); 39850Sstevel@tonic-gate if (ah_offset == 0) { 39860Sstevel@tonic-gate freemsg(phdr_mp); 39870Sstevel@tonic-gate ip_drop_packet(ipsec_mp, B_FALSE, NULL, NULL, 39883448Sdh155122 DROPPER(ipss, ipds_ah_bad_v6_hdrs), 39893448Sdh155122 &ahstack->ah_dropper); 39900Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 39910Sstevel@tonic-gate } 39920Sstevel@tonic-gate } 39930Sstevel@tonic-gate 39940Sstevel@tonic-gate phdr_mp->b_cont = mp; 39950Sstevel@tonic-gate ipsec_mp->b_cont = phdr_mp; 39960Sstevel@tonic-gate 39970Sstevel@tonic-gate /* skip original IP header in mp */ 39980Sstevel@tonic-gate mp->b_rptr += IPV6_HDR_LEN + option_length; 39990Sstevel@tonic-gate 40000Sstevel@tonic-gate /* initialize AH header */ 40010Sstevel@tonic-gate ah_hdr = (ah_t *)(phdr_mp->b_rptr + IPV6_HDR_LEN + option_length); 40020Sstevel@tonic-gate ah_hdr->ah_nexthdr = oip6h->ip6_nxt; 40030Sstevel@tonic-gate 40040Sstevel@tonic-gate if (!ah_finish_up(((ah_t *)((uint8_t *)ip6h + ah_offset)), NULL, 40053448Sdh155122 assoc, ah_data_sz, ah_align_sz, ahstack)) { 40060Sstevel@tonic-gate /* Only way this fails is if outbound replay counter wraps. */ 40070Sstevel@tonic-gate ip_drop_packet(ipsec_mp, B_FALSE, NULL, NULL, 40083448Sdh155122 DROPPER(ipss, ipds_ah_replay), 40093448Sdh155122 &ahstack->ah_dropper); 40100Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 40110Sstevel@tonic-gate } 40120Sstevel@tonic-gate 40130Sstevel@tonic-gate return (IPSEC_STATUS_SUCCESS); 40140Sstevel@tonic-gate } 40150Sstevel@tonic-gate 40160Sstevel@tonic-gate /* 40170Sstevel@tonic-gate * Invoked after processing of an inbound packet by the 40180Sstevel@tonic-gate * kernel crypto framework. Called by ah_submit_req() for a sync request, 40190Sstevel@tonic-gate * or by the kcf callback for an async request. 40200Sstevel@tonic-gate * Returns IPSEC_STATUS_SUCCESS on success, IPSEC_STATUS_FAILED on failure. 40210Sstevel@tonic-gate * On failure, the mblk chain ipsec_in is freed by this function. 40220Sstevel@tonic-gate */ 40230Sstevel@tonic-gate static ipsec_status_t 40240Sstevel@tonic-gate ah_auth_in_done(mblk_t *ipsec_in) 40250Sstevel@tonic-gate { 40260Sstevel@tonic-gate mblk_t *phdr_mp; 40270Sstevel@tonic-gate ipha_t *ipha; 40280Sstevel@tonic-gate uint_t ah_offset = 0; 40290Sstevel@tonic-gate mblk_t *mp; 40303192Sdanmcd int align_len, newpos; 40310Sstevel@tonic-gate ah_t *ah; 40320Sstevel@tonic-gate uint32_t length; 40333192Sdanmcd uint32_t *dest32; 40343192Sdanmcd uint8_t *dest; 40350Sstevel@tonic-gate ipsec_in_t *ii; 40360Sstevel@tonic-gate boolean_t isv4; 40370Sstevel@tonic-gate ip6_t *ip6h; 40380Sstevel@tonic-gate uint_t icv_len; 40390Sstevel@tonic-gate ipsa_t *assoc; 40400Sstevel@tonic-gate kstat_named_t *counter; 40413448Sdh155122 netstack_t *ns; 40423448Sdh155122 ipsecah_stack_t *ahstack; 40433448Sdh155122 ipsec_stack_t *ipss; 40440Sstevel@tonic-gate 40450Sstevel@tonic-gate ii = (ipsec_in_t *)ipsec_in->b_rptr; 40463448Sdh155122 ns = ii->ipsec_in_ns; 40473448Sdh155122 ahstack = ns->netstack_ipsecah; 40483448Sdh155122 ipss = ns->netstack_ipsec; 40493448Sdh155122 40500Sstevel@tonic-gate isv4 = ii->ipsec_in_v4; 40510Sstevel@tonic-gate assoc = ii->ipsec_in_ah_sa; 40520Sstevel@tonic-gate icv_len = (uint_t)ii->ipsec_in_crypto_mac.cd_raw.iov_len; 40530Sstevel@tonic-gate 40540Sstevel@tonic-gate phdr_mp = ipsec_in->b_cont; 40550Sstevel@tonic-gate if (phdr_mp == NULL) { 40563448Sdh155122 ip_drop_packet(ipsec_in, B_TRUE, NULL, NULL, 40573448Sdh155122 DROPPER(ipss, ipds_ah_nomem), 40583448Sdh155122 &ahstack->ah_dropper); 40590Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 40600Sstevel@tonic-gate } 40610Sstevel@tonic-gate 40620Sstevel@tonic-gate mp = phdr_mp->b_cont; 40630Sstevel@tonic-gate if (mp == NULL) { 40643448Sdh155122 ip_drop_packet(ipsec_in, B_TRUE, NULL, NULL, 40653448Sdh155122 DROPPER(ipss, ipds_ah_nomem), 40663448Sdh155122 &ahstack->ah_dropper); 40670Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 40680Sstevel@tonic-gate } 40690Sstevel@tonic-gate mp->b_rptr -= ii->ipsec_in_skip_len; 40700Sstevel@tonic-gate 40714987Sdanmcd ah_set_usetime(assoc, B_TRUE); 40724987Sdanmcd 40730Sstevel@tonic-gate if (isv4) { 40740Sstevel@tonic-gate ipha = (ipha_t *)mp->b_rptr; 40750Sstevel@tonic-gate ah_offset = ipha->ipha_version_and_hdr_length - 40760Sstevel@tonic-gate (uint8_t)((IP_VERSION << 4)); 40770Sstevel@tonic-gate ah_offset <<= 2; 40780Sstevel@tonic-gate align_len = P2ALIGN(icv_len + IPV4_PADDING_ALIGN - 1, 40790Sstevel@tonic-gate IPV4_PADDING_ALIGN); 40800Sstevel@tonic-gate } else { 40810Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr; 40820Sstevel@tonic-gate ah_offset = ipsec_ah_get_hdr_size_v6(mp, B_TRUE); 40830Sstevel@tonic-gate ASSERT((mp->b_wptr - mp->b_rptr) >= ah_offset); 40840Sstevel@tonic-gate align_len = P2ALIGN(icv_len + IPV6_PADDING_ALIGN - 1, 40850Sstevel@tonic-gate IPV6_PADDING_ALIGN); 40860Sstevel@tonic-gate } 40870Sstevel@tonic-gate 40880Sstevel@tonic-gate ah = (ah_t *)(mp->b_rptr + ah_offset); 40893192Sdanmcd newpos = sizeof (ah_t) + align_len; 40900Sstevel@tonic-gate 40910Sstevel@tonic-gate /* 40920Sstevel@tonic-gate * We get here only when authentication passed. 40930Sstevel@tonic-gate */ 40940Sstevel@tonic-gate 40953448Sdh155122 ah3dbg(ahstack, ("AH succeeded, checking replay\n")); 40963448Sdh155122 AH_BUMP_STAT(ahstack, good_auth); 40970Sstevel@tonic-gate 40980Sstevel@tonic-gate if (!sadb_replay_check(assoc, ah->ah_replay)) { 40990Sstevel@tonic-gate int af; 41000Sstevel@tonic-gate void *addr; 41010Sstevel@tonic-gate 41020Sstevel@tonic-gate if (isv4) { 41030Sstevel@tonic-gate addr = &ipha->ipha_dst; 41040Sstevel@tonic-gate af = AF_INET; 41050Sstevel@tonic-gate } else { 41060Sstevel@tonic-gate addr = &ip6h->ip6_dst; 41070Sstevel@tonic-gate af = AF_INET6; 41080Sstevel@tonic-gate } 41090Sstevel@tonic-gate 41100Sstevel@tonic-gate /* 41110Sstevel@tonic-gate * Log the event. As of now we print out an event. 41120Sstevel@tonic-gate * Do not print the replay failure number, or else 41130Sstevel@tonic-gate * syslog cannot collate the error messages. Printing 41140Sstevel@tonic-gate * the replay number that failed (or printing to the 41150Sstevel@tonic-gate * console) opens a denial-of-service attack. 41160Sstevel@tonic-gate */ 41173448Sdh155122 AH_BUMP_STAT(ahstack, replay_failures); 41180Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, 41190Sstevel@tonic-gate SL_ERROR | SL_WARN, 41200Sstevel@tonic-gate "Replay failed for AH spi %x, dst_addr %s", 41213448Sdh155122 assoc->ipsa_spi, addr, af, ahstack->ipsecah_netstack); 41223448Sdh155122 counter = DROPPER(ipss, ipds_ah_replay); 41230Sstevel@tonic-gate goto ah_in_discard; 41240Sstevel@tonic-gate } 41250Sstevel@tonic-gate 41260Sstevel@tonic-gate /* 41270Sstevel@tonic-gate * We need to remove the AH header from the original 41283192Sdanmcd * datagram. Best way to do this is to move the pre-AH headers 41293192Sdanmcd * forward in the (relatively simple) IPv4 case. In IPv6, it's 41303192Sdanmcd * a bit more complicated because of IPv6's next-header chaining, 41313192Sdanmcd * but it's doable. 41320Sstevel@tonic-gate */ 41330Sstevel@tonic-gate if (isv4) { 41340Sstevel@tonic-gate /* 41350Sstevel@tonic-gate * Assign the right protocol, adjust the length as we 41360Sstevel@tonic-gate * are removing the AH header and adjust the checksum to 41370Sstevel@tonic-gate * account for the protocol and length. 41380Sstevel@tonic-gate */ 41393192Sdanmcd length = ntohs(ipha->ipha_length); 41400Sstevel@tonic-gate if (!ah_age_bytes(assoc, length, B_TRUE)) { 41410Sstevel@tonic-gate /* The ipsa has hit hard expiration, LOG and AUDIT. */ 41420Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, 41430Sstevel@tonic-gate SL_ERROR | SL_WARN, 41440Sstevel@tonic-gate "AH Association 0x%x, dst %s had bytes expire.\n", 41450Sstevel@tonic-gate assoc->ipsa_spi, assoc->ipsa_dstaddr, 41463448Sdh155122 AF_INET, ahstack->ipsecah_netstack); 41473448Sdh155122 AH_BUMP_STAT(ahstack, bytes_expired); 41483448Sdh155122 counter = DROPPER(ipss, ipds_ah_bytes_expire); 41490Sstevel@tonic-gate goto ah_in_discard; 41500Sstevel@tonic-gate } 41513192Sdanmcd ipha->ipha_protocol = ah->ah_nexthdr; 41523192Sdanmcd length -= newpos; 41533192Sdanmcd 41543192Sdanmcd ipha->ipha_length = htons((uint16_t)length); 41553192Sdanmcd ipha->ipha_hdr_checksum = 0; 41563192Sdanmcd ipha->ipha_hdr_checksum = (uint16_t)ip_csum_hdr(ipha); 41570Sstevel@tonic-gate } else { 41580Sstevel@tonic-gate uchar_t *whereptr; 41590Sstevel@tonic-gate int hdrlen; 41600Sstevel@tonic-gate uint8_t *nexthdr; 41610Sstevel@tonic-gate ip6_hbh_t *hbhhdr; 41620Sstevel@tonic-gate ip6_dest_t *dsthdr; 41630Sstevel@tonic-gate ip6_rthdr0_t *rthdr; 41640Sstevel@tonic-gate 41650Sstevel@tonic-gate /* 41660Sstevel@tonic-gate * Make phdr_mp hold until the AH header and make 41670Sstevel@tonic-gate * mp hold everything past AH header. 41680Sstevel@tonic-gate */ 41693192Sdanmcd length = ntohs(ip6h->ip6_plen); 41700Sstevel@tonic-gate if (!ah_age_bytes(assoc, length + sizeof (ip6_t), B_TRUE)) { 41710Sstevel@tonic-gate /* The ipsa has hit hard expiration, LOG and AUDIT. */ 41720Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, 41730Sstevel@tonic-gate SL_ERROR | SL_WARN, 41740Sstevel@tonic-gate "AH Association 0x%x, dst %s had bytes " 41750Sstevel@tonic-gate "expire.\n", assoc->ipsa_spi, &ip6h->ip6_dst, 41763448Sdh155122 AF_INET6, ahstack->ipsecah_netstack); 41773448Sdh155122 AH_BUMP_STAT(ahstack, bytes_expired); 41783448Sdh155122 counter = DROPPER(ipss, ipds_ah_bytes_expire); 41790Sstevel@tonic-gate goto ah_in_discard; 41800Sstevel@tonic-gate } 41810Sstevel@tonic-gate 41820Sstevel@tonic-gate /* 41830Sstevel@tonic-gate * Update the next header field of the header preceding 41840Sstevel@tonic-gate * AH with the next header field of AH. Start with the 41850Sstevel@tonic-gate * IPv6 header and proceed with the extension headers 41860Sstevel@tonic-gate * until we find what we're looking for. 41870Sstevel@tonic-gate */ 41883192Sdanmcd nexthdr = &ip6h->ip6_nxt; 41893192Sdanmcd whereptr = (uchar_t *)ip6h; 41900Sstevel@tonic-gate hdrlen = sizeof (ip6_t); 41910Sstevel@tonic-gate 41920Sstevel@tonic-gate while (*nexthdr != IPPROTO_AH) { 41930Sstevel@tonic-gate whereptr += hdrlen; 41940Sstevel@tonic-gate /* Assume IP has already stripped it */ 41950Sstevel@tonic-gate ASSERT(*nexthdr != IPPROTO_FRAGMENT && 41960Sstevel@tonic-gate *nexthdr != IPPROTO_RAW); 41970Sstevel@tonic-gate switch (*nexthdr) { 41980Sstevel@tonic-gate case IPPROTO_HOPOPTS: 41990Sstevel@tonic-gate hbhhdr = (ip6_hbh_t *)whereptr; 42000Sstevel@tonic-gate nexthdr = &hbhhdr->ip6h_nxt; 42010Sstevel@tonic-gate hdrlen = 8 * (hbhhdr->ip6h_len + 1); 42020Sstevel@tonic-gate break; 42030Sstevel@tonic-gate case IPPROTO_DSTOPTS: 42040Sstevel@tonic-gate dsthdr = (ip6_dest_t *)whereptr; 42050Sstevel@tonic-gate nexthdr = &dsthdr->ip6d_nxt; 42060Sstevel@tonic-gate hdrlen = 8 * (dsthdr->ip6d_len + 1); 42070Sstevel@tonic-gate break; 42080Sstevel@tonic-gate case IPPROTO_ROUTING: 42090Sstevel@tonic-gate rthdr = (ip6_rthdr0_t *)whereptr; 42100Sstevel@tonic-gate nexthdr = &rthdr->ip6r0_nxt; 42110Sstevel@tonic-gate hdrlen = 8 * (rthdr->ip6r0_len + 1); 42120Sstevel@tonic-gate break; 42130Sstevel@tonic-gate } 42140Sstevel@tonic-gate } 42150Sstevel@tonic-gate *nexthdr = ah->ah_nexthdr; 42163192Sdanmcd length -= newpos; 42173192Sdanmcd ip6h->ip6_plen = htons((uint16_t)length); 42180Sstevel@tonic-gate } 42190Sstevel@tonic-gate 42203192Sdanmcd /* Now that we've fixed the IP header, move it forward. */ 42213192Sdanmcd mp->b_rptr += newpos; 42223192Sdanmcd if (IS_P2ALIGNED(mp->b_rptr, sizeof (uint32_t))) { 42233192Sdanmcd dest32 = (uint32_t *)(mp->b_rptr + ah_offset); 42243192Sdanmcd while (--dest32 >= (uint32_t *)mp->b_rptr) 42253192Sdanmcd *dest32 = *(dest32 - (newpos >> 2)); 42263192Sdanmcd } else { 42273192Sdanmcd dest = mp->b_rptr + ah_offset; 42283192Sdanmcd while (--dest >= mp->b_rptr) 42293192Sdanmcd *dest = *(dest - newpos); 42303192Sdanmcd } 42313192Sdanmcd freeb(phdr_mp); 42323192Sdanmcd ipsec_in->b_cont = mp; 42337885Sdanmcd@sun.com if (assoc->ipsa_state == IPSA_STATE_IDLE) { 42347885Sdanmcd@sun.com /* 42357885Sdanmcd@sun.com * Cluster buffering case. Tell caller that we're 42367885Sdanmcd@sun.com * handling the packet. 42377885Sdanmcd@sun.com */ 42387885Sdanmcd@sun.com sadb_buf_pkt(assoc, ipsec_in, ns); 42397885Sdanmcd@sun.com return (IPSEC_STATUS_PENDING); 42407885Sdanmcd@sun.com } 42410Sstevel@tonic-gate return (IPSEC_STATUS_SUCCESS); 42420Sstevel@tonic-gate 42430Sstevel@tonic-gate ah_in_discard: 42443448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards); 42453448Sdh155122 ip_drop_packet(ipsec_in, B_TRUE, NULL, NULL, counter, 42463448Sdh155122 &ahstack->ah_dropper); 42470Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 42480Sstevel@tonic-gate } 42490Sstevel@tonic-gate 42500Sstevel@tonic-gate /* 42510Sstevel@tonic-gate * Invoked after processing of an outbound packet by the 42520Sstevel@tonic-gate * kernel crypto framework, either by ah_submit_req() for a request 42530Sstevel@tonic-gate * executed syncrhonously, or by the KEF callback for a request 42540Sstevel@tonic-gate * executed asynchronously. 42550Sstevel@tonic-gate */ 42560Sstevel@tonic-gate static ipsec_status_t 42570Sstevel@tonic-gate ah_auth_out_done(mblk_t *ipsec_out) 42580Sstevel@tonic-gate { 42590Sstevel@tonic-gate mblk_t *phdr_mp; 42600Sstevel@tonic-gate mblk_t *mp; 42610Sstevel@tonic-gate int align_len; 42620Sstevel@tonic-gate uint32_t hdrs_length; 42630Sstevel@tonic-gate uchar_t *ptr; 42640Sstevel@tonic-gate uint32_t length; 42650Sstevel@tonic-gate boolean_t isv4; 42660Sstevel@tonic-gate ipsec_out_t *io; 42670Sstevel@tonic-gate size_t icv_len; 42683448Sdh155122 netstack_t *ns; 42693448Sdh155122 ipsec_stack_t *ipss; 42703448Sdh155122 ipsecah_stack_t *ahstack; 42710Sstevel@tonic-gate 42720Sstevel@tonic-gate io = (ipsec_out_t *)ipsec_out->b_rptr; 42733448Sdh155122 ns = io->ipsec_out_ns; 42743448Sdh155122 ipss = ns->netstack_ipsec; 42753448Sdh155122 ahstack = ns->netstack_ipsecah; 42763448Sdh155122 42770Sstevel@tonic-gate isv4 = io->ipsec_out_v4; 42780Sstevel@tonic-gate icv_len = io->ipsec_out_crypto_mac.cd_raw.iov_len; 42790Sstevel@tonic-gate 42800Sstevel@tonic-gate phdr_mp = ipsec_out->b_cont; 42810Sstevel@tonic-gate if (phdr_mp == NULL) { 42820Sstevel@tonic-gate ip_drop_packet(ipsec_out, B_FALSE, NULL, NULL, 42833448Sdh155122 DROPPER(ipss, ipds_ah_nomem), 42843448Sdh155122 &ahstack->ah_dropper); 42850Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 42860Sstevel@tonic-gate } 42870Sstevel@tonic-gate 42880Sstevel@tonic-gate mp = phdr_mp->b_cont; 42890Sstevel@tonic-gate if (mp == NULL) { 42900Sstevel@tonic-gate ip_drop_packet(ipsec_out, B_FALSE, NULL, NULL, 42913448Sdh155122 DROPPER(ipss, ipds_ah_nomem), 42923448Sdh155122 &ahstack->ah_dropper); 42930Sstevel@tonic-gate return (IPSEC_STATUS_FAILED); 42940Sstevel@tonic-gate } 42950Sstevel@tonic-gate mp->b_rptr -= io->ipsec_out_skip_len; 42960Sstevel@tonic-gate 42974987Sdanmcd ASSERT(io->ipsec_out_ah_sa != NULL); 42984987Sdanmcd ah_set_usetime(io->ipsec_out_ah_sa, B_FALSE); 42994987Sdanmcd 43000Sstevel@tonic-gate if (isv4) { 43010Sstevel@tonic-gate ipha_t *ipha; 43020Sstevel@tonic-gate ipha_t *nipha; 43030Sstevel@tonic-gate 43040Sstevel@tonic-gate ipha = (ipha_t *)mp->b_rptr; 43050Sstevel@tonic-gate hdrs_length = ipha->ipha_version_and_hdr_length - 43060Sstevel@tonic-gate (uint8_t)((IP_VERSION << 4)); 43070Sstevel@tonic-gate hdrs_length <<= 2; 43080Sstevel@tonic-gate align_len = P2ALIGN(icv_len + IPV4_PADDING_ALIGN - 1, 43090Sstevel@tonic-gate IPV4_PADDING_ALIGN); 43100Sstevel@tonic-gate /* 43110Sstevel@tonic-gate * phdr_mp must have the right amount of space for the 43120Sstevel@tonic-gate * combined IP and AH header. Copy the IP header and 43130Sstevel@tonic-gate * the ack_data onto AH. Note that the AH header was 43140Sstevel@tonic-gate * already formed before the ICV calculation and hence 43150Sstevel@tonic-gate * you don't have to copy it here. 43160Sstevel@tonic-gate */ 43170Sstevel@tonic-gate bcopy(mp->b_rptr, phdr_mp->b_rptr, hdrs_length); 43180Sstevel@tonic-gate 43190Sstevel@tonic-gate ptr = phdr_mp->b_rptr + hdrs_length + sizeof (ah_t); 43200Sstevel@tonic-gate bcopy(phdr_mp->b_wptr, ptr, icv_len); 43210Sstevel@tonic-gate 43220Sstevel@tonic-gate /* 43230Sstevel@tonic-gate * Compute the new header checksum as we are assigning 43240Sstevel@tonic-gate * IPPROTO_AH and adjusting the length here. 43250Sstevel@tonic-gate */ 43260Sstevel@tonic-gate nipha = (ipha_t *)phdr_mp->b_rptr; 43270Sstevel@tonic-gate 43280Sstevel@tonic-gate nipha->ipha_protocol = IPPROTO_AH; 43290Sstevel@tonic-gate length = ntohs(nipha->ipha_length); 43300Sstevel@tonic-gate length += (sizeof (ah_t) + align_len); 43310Sstevel@tonic-gate nipha->ipha_length = htons((uint16_t)length); 43320Sstevel@tonic-gate nipha->ipha_hdr_checksum = 0; 43330Sstevel@tonic-gate nipha->ipha_hdr_checksum = (uint16_t)ip_csum_hdr(nipha); 43340Sstevel@tonic-gate } else { 43350Sstevel@tonic-gate ip6_t *ip6h; 43360Sstevel@tonic-gate ip6_t *nip6h; 43370Sstevel@tonic-gate uint_t ah_offset; 43380Sstevel@tonic-gate 43390Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr; 43400Sstevel@tonic-gate nip6h = (ip6_t *)phdr_mp->b_rptr; 43410Sstevel@tonic-gate align_len = P2ALIGN(icv_len + IPV6_PADDING_ALIGN - 1, 43420Sstevel@tonic-gate IPV6_PADDING_ALIGN); 43430Sstevel@tonic-gate /* 43440Sstevel@tonic-gate * phdr_mp must have the right amount of space for the 43450Sstevel@tonic-gate * combined IP and AH header. Copy the IP header with 43460Sstevel@tonic-gate * options into the pseudo header. When we constructed 43470Sstevel@tonic-gate * a pseudo header, we did not copy some of the mutable 43480Sstevel@tonic-gate * fields. We do it now by calling ah_fix_phdr_v6() 43490Sstevel@tonic-gate * with the last argument B_TRUE. It returns the 43500Sstevel@tonic-gate * ah_offset into the pseudo header. 43510Sstevel@tonic-gate */ 43520Sstevel@tonic-gate 43530Sstevel@tonic-gate bcopy(ip6h, nip6h, IPV6_HDR_LEN); 43540Sstevel@tonic-gate ah_offset = ah_fix_phdr_v6(nip6h, ip6h, B_TRUE, B_TRUE); 43550Sstevel@tonic-gate ASSERT(ah_offset != 0); 43560Sstevel@tonic-gate /* 43570Sstevel@tonic-gate * phdr_mp can hold exactly the whole IP header with options 43580Sstevel@tonic-gate * plus the AH header also. Thus subtracting the AH header's 43590Sstevel@tonic-gate * size should give exactly how much of the original header 43600Sstevel@tonic-gate * should be skipped. 43610Sstevel@tonic-gate */ 43620Sstevel@tonic-gate hdrs_length = (phdr_mp->b_wptr - phdr_mp->b_rptr) - 43630Sstevel@tonic-gate sizeof (ah_t) - icv_len; 43640Sstevel@tonic-gate bcopy(phdr_mp->b_wptr, ((uint8_t *)nip6h + ah_offset + 43650Sstevel@tonic-gate sizeof (ah_t)), icv_len); 43660Sstevel@tonic-gate length = ntohs(nip6h->ip6_plen); 43670Sstevel@tonic-gate length += (sizeof (ah_t) + align_len); 43680Sstevel@tonic-gate nip6h->ip6_plen = htons((uint16_t)length); 43690Sstevel@tonic-gate } 43700Sstevel@tonic-gate 43710Sstevel@tonic-gate /* Skip the original IP header */ 43720Sstevel@tonic-gate mp->b_rptr += hdrs_length; 43730Sstevel@tonic-gate if (mp->b_rptr == mp->b_wptr) { 43740Sstevel@tonic-gate phdr_mp->b_cont = mp->b_cont; 43750Sstevel@tonic-gate freeb(mp); 43760Sstevel@tonic-gate } 43770Sstevel@tonic-gate 43780Sstevel@tonic-gate return (IPSEC_STATUS_SUCCESS); 43790Sstevel@tonic-gate } 43800Sstevel@tonic-gate 43810Sstevel@tonic-gate /* 43820Sstevel@tonic-gate * Wrapper to allow IP to trigger an AH association failure message 43830Sstevel@tonic-gate * during SA inbound selection. 43840Sstevel@tonic-gate */ 43850Sstevel@tonic-gate void 43860Sstevel@tonic-gate ipsecah_in_assocfailure(mblk_t *mp, char level, ushort_t sl, char *fmt, 43873448Sdh155122 uint32_t spi, void *addr, int af, ipsecah_stack_t *ahstack) 43880Sstevel@tonic-gate { 43893448Sdh155122 ipsec_stack_t *ipss = ahstack->ipsecah_netstack->netstack_ipsec; 43903448Sdh155122 43913448Sdh155122 if (ahstack->ipsecah_log_unknown_spi) { 43920Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, level, sl, fmt, spi, 43933448Sdh155122 addr, af, ahstack->ipsecah_netstack); 43940Sstevel@tonic-gate } 43950Sstevel@tonic-gate 43963448Sdh155122 ip_drop_packet(mp, B_TRUE, NULL, NULL, 43973448Sdh155122 DROPPER(ipss, ipds_ah_no_sa), 43983448Sdh155122 &ahstack->ah_dropper); 43990Sstevel@tonic-gate } 44000Sstevel@tonic-gate 44010Sstevel@tonic-gate /* 44020Sstevel@tonic-gate * Initialize the AH input and output processing functions. 44030Sstevel@tonic-gate */ 44040Sstevel@tonic-gate void 44050Sstevel@tonic-gate ipsecah_init_funcs(ipsa_t *sa) 44060Sstevel@tonic-gate { 44070Sstevel@tonic-gate if (sa->ipsa_output_func == NULL) 44080Sstevel@tonic-gate sa->ipsa_output_func = ah_outbound; 44090Sstevel@tonic-gate if (sa->ipsa_input_func == NULL) 44100Sstevel@tonic-gate sa->ipsa_input_func = ah_inbound; 44110Sstevel@tonic-gate } 4412