17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate * CDDL HEADER START
37c478bd9Sstevel@tonic-gate *
47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the
57845d282Sdanmcd * Common Development and Distribution License (the "License").
67845d282Sdanmcd * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate *
87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate * and limitations under the License.
127c478bd9Sstevel@tonic-gate *
137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate *
197c478bd9Sstevel@tonic-gate * CDDL HEADER END
207c478bd9Sstevel@tonic-gate */
217c478bd9Sstevel@tonic-gate /*
22930af642SDan McDonald * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
237c478bd9Sstevel@tonic-gate * Use is subject to license terms.
24b7daf799SDan McDonald * Copyright (c) 2012 Nexenta Systems, Inc. All rights reserved.
25b7daf799SDan McDonald * Copyright (c) 2017 Joyent, Inc.
267c478bd9Sstevel@tonic-gate */
277c478bd9Sstevel@tonic-gate
287c478bd9Sstevel@tonic-gate #include <sys/types.h>
297c478bd9Sstevel@tonic-gate #include <sys/stream.h>
307c478bd9Sstevel@tonic-gate #include <sys/stropts.h>
317c478bd9Sstevel@tonic-gate #include <sys/errno.h>
327c478bd9Sstevel@tonic-gate #include <sys/strlog.h>
337c478bd9Sstevel@tonic-gate #include <sys/tihdr.h>
347c478bd9Sstevel@tonic-gate #include <sys/socket.h>
357c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
367c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
377c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
38f4b3ec61Sdh #include <sys/zone.h>
397c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
407c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
417c478bd9Sstevel@tonic-gate #include <sys/vtrace.h>
427c478bd9Sstevel@tonic-gate #include <sys/debug.h>
437c478bd9Sstevel@tonic-gate #include <sys/atomic.h>
447c478bd9Sstevel@tonic-gate #include <sys/strsun.h>
457c478bd9Sstevel@tonic-gate #include <sys/random.h>
467c478bd9Sstevel@tonic-gate #include <netinet/in.h>
477c478bd9Sstevel@tonic-gate #include <net/if.h>
487c478bd9Sstevel@tonic-gate #include <netinet/ip6.h>
497c478bd9Sstevel@tonic-gate #include <net/pfkeyv2.h>
50628b0c67SMark Fenwick #include <net/pfpolicy.h>
517c478bd9Sstevel@tonic-gate
527c478bd9Sstevel@tonic-gate #include <inet/common.h>
537c478bd9Sstevel@tonic-gate #include <inet/mi.h>
547c478bd9Sstevel@tonic-gate #include <inet/nd.h>
557c478bd9Sstevel@tonic-gate #include <inet/ip.h>
56437220cdSdanmcd #include <inet/ip_impl.h>
577c478bd9Sstevel@tonic-gate #include <inet/ip6.h>
58bd670b35SErik Nordmark #include <inet/ip_if.h>
59bd670b35SErik Nordmark #include <inet/ip_ndp.h>
607c478bd9Sstevel@tonic-gate #include <inet/sadb.h>
617c478bd9Sstevel@tonic-gate #include <inet/ipsec_info.h>
627c478bd9Sstevel@tonic-gate #include <inet/ipsec_impl.h>
637c478bd9Sstevel@tonic-gate #include <inet/ipsecesp.h>
647c478bd9Sstevel@tonic-gate #include <inet/ipdrop.h>
657c478bd9Sstevel@tonic-gate #include <inet/tcp.h>
667c478bd9Sstevel@tonic-gate #include <sys/kstat.h>
677c478bd9Sstevel@tonic-gate #include <sys/policy.h>
687c478bd9Sstevel@tonic-gate #include <sys/strsun.h>
695d3b8cb7SBill Sommerfeld #include <sys/strsubr.h>
707c478bd9Sstevel@tonic-gate #include <inet/udp_impl.h>
717c478bd9Sstevel@tonic-gate #include <sys/taskq.h>
72f4b3ec61Sdh #include <sys/note.h>
737c478bd9Sstevel@tonic-gate
745d3b8cb7SBill Sommerfeld #include <sys/tsol/tnet.h>
755d3b8cb7SBill Sommerfeld
767c478bd9Sstevel@tonic-gate /*
777c478bd9Sstevel@tonic-gate * Table of ND variables supported by ipsecesp. These are loaded into
787c478bd9Sstevel@tonic-gate * ipsecesp_g_nd in ipsecesp_init_nd.
797c478bd9Sstevel@tonic-gate * All of these are alterable, within the min/max values given, at run time.
807c478bd9Sstevel@tonic-gate */
81f4b3ec61Sdh static ipsecespparam_t lcl_param_arr[] = {
827c478bd9Sstevel@tonic-gate /* min max value name */
837c478bd9Sstevel@tonic-gate { 0, 3, 0, "ipsecesp_debug"},
847c478bd9Sstevel@tonic-gate { 125, 32000, SADB_AGE_INTERVAL_DEFAULT, "ipsecesp_age_interval"},
857c478bd9Sstevel@tonic-gate { 1, 10, 1, "ipsecesp_reap_delay"},
867c478bd9Sstevel@tonic-gate { 1, SADB_MAX_REPLAY, 64, "ipsecesp_replay_size"},
877c478bd9Sstevel@tonic-gate { 1, 300, 15, "ipsecesp_acquire_timeout"},
887c478bd9Sstevel@tonic-gate { 1, 1800, 90, "ipsecesp_larval_timeout"},
897c478bd9Sstevel@tonic-gate /* Default lifetime values for ACQUIRE messages. */
907c478bd9Sstevel@tonic-gate { 0, 0xffffffffU, 0, "ipsecesp_default_soft_bytes"},
917c478bd9Sstevel@tonic-gate { 0, 0xffffffffU, 0, "ipsecesp_default_hard_bytes"},
927c478bd9Sstevel@tonic-gate { 0, 0xffffffffU, 24000, "ipsecesp_default_soft_addtime"},
937c478bd9Sstevel@tonic-gate { 0, 0xffffffffU, 28800, "ipsecesp_default_hard_addtime"},
947c478bd9Sstevel@tonic-gate { 0, 0xffffffffU, 0, "ipsecesp_default_soft_usetime"},
957c478bd9Sstevel@tonic-gate { 0, 0xffffffffU, 0, "ipsecesp_default_hard_usetime"},
967c478bd9Sstevel@tonic-gate { 0, 1, 0, "ipsecesp_log_unknown_spi"},
977c478bd9Sstevel@tonic-gate { 0, 2, 1, "ipsecesp_padding_check"},
98437220cdSdanmcd { 0, 600, 20, "ipsecesp_nat_keepalive_interval"},
997c478bd9Sstevel@tonic-gate };
100437220cdSdanmcd /* For ipsecesp_nat_keepalive_interval, see ipsecesp.h. */
1017c478bd9Sstevel@tonic-gate
1027c478bd9Sstevel@tonic-gate #define esp0dbg(a) printf a
1037c478bd9Sstevel@tonic-gate /* NOTE: != 0 instead of > 0 so lint doesn't complain. */
104f4b3ec61Sdh #define esp1dbg(espstack, a) if (espstack->ipsecesp_debug != 0) printf a
105f4b3ec61Sdh #define esp2dbg(espstack, a) if (espstack->ipsecesp_debug > 1) printf a
106f4b3ec61Sdh #define esp3dbg(espstack, a) if (espstack->ipsecesp_debug > 2) printf a
1077c478bd9Sstevel@tonic-gate
1087c478bd9Sstevel@tonic-gate static int ipsecesp_open(queue_t *, dev_t *, int, int, cred_t *);
1095e1743f0SToomas Soome static int ipsecesp_close(queue_t *, int, cred_t *);
1109c451ec7SToomas Soome static int ipsecesp_rput(queue_t *, mblk_t *);
1119c451ec7SToomas Soome static int ipsecesp_wput(queue_t *, mblk_t *);
112f4b3ec61Sdh static void *ipsecesp_stack_init(netstackid_t stackid, netstack_t *ns);
113f4b3ec61Sdh static void ipsecesp_stack_fini(netstackid_t stackid, void *arg);
1147c478bd9Sstevel@tonic-gate
115437220cdSdanmcd static void esp_prepare_udp(netstack_t *, mblk_t *, ipha_t *);
116bd670b35SErik Nordmark static void esp_outbound_finish(mblk_t *, ip_xmit_attr_t *);
117bd670b35SErik Nordmark static void esp_inbound_restart(mblk_t *, ip_recv_attr_t *);
1187c478bd9Sstevel@tonic-gate
119f4b3ec61Sdh static boolean_t esp_register_out(uint32_t, uint32_t, uint_t,
120bd670b35SErik Nordmark ipsecesp_stack_t *, cred_t *);
1217c478bd9Sstevel@tonic-gate static boolean_t esp_strip_header(mblk_t *, boolean_t, uint32_t,
122f4b3ec61Sdh kstat_named_t **, ipsecesp_stack_t *);
123bd670b35SErik Nordmark static mblk_t *esp_submit_req_inbound(mblk_t *, ip_recv_attr_t *,
124bd670b35SErik Nordmark ipsa_t *, uint_t);
125bd670b35SErik Nordmark static mblk_t *esp_submit_req_outbound(mblk_t *, ip_xmit_attr_t *,
126bd670b35SErik Nordmark ipsa_t *, uchar_t *, uint_t);
1279c2c14abSThejaswini Singarajipura
128f4b3ec61Sdh /* Setable in /etc/system */
129f4b3ec61Sdh uint32_t esp_hash_size = IPSEC_DEFAULT_HASH_SIZE;
130f4b3ec61Sdh
1317c478bd9Sstevel@tonic-gate static struct module_info info = {
1327c478bd9Sstevel@tonic-gate 5137, "ipsecesp", 0, INFPSZ, 65536, 1024
1337c478bd9Sstevel@tonic-gate };
1347c478bd9Sstevel@tonic-gate
1357c478bd9Sstevel@tonic-gate static struct qinit rinit = {
1369c451ec7SToomas Soome ipsecesp_rput, NULL, ipsecesp_open, ipsecesp_close, NULL, &info,
1377c478bd9Sstevel@tonic-gate NULL
1387c478bd9Sstevel@tonic-gate };
1397c478bd9Sstevel@tonic-gate
1407c478bd9Sstevel@tonic-gate static struct qinit winit = {
1419c451ec7SToomas Soome ipsecesp_wput, NULL, ipsecesp_open, ipsecesp_close, NULL, &info,
1427c478bd9Sstevel@tonic-gate NULL
1437c478bd9Sstevel@tonic-gate };
1447c478bd9Sstevel@tonic-gate
1457c478bd9Sstevel@tonic-gate struct streamtab ipsecespinfo = {
1467c478bd9Sstevel@tonic-gate &rinit, &winit, NULL, NULL
1477c478bd9Sstevel@tonic-gate };
1487c478bd9Sstevel@tonic-gate
1497c478bd9Sstevel@tonic-gate static taskq_t *esp_taskq;
1507c478bd9Sstevel@tonic-gate
1517c478bd9Sstevel@tonic-gate /*
1527c478bd9Sstevel@tonic-gate * OTOH, this one is set at open/close, and I'm D_MTQPAIR for now.
1537c478bd9Sstevel@tonic-gate *
1547c478bd9Sstevel@tonic-gate * Question: Do I need this, given that all instance's esps->esps_wq point
1557c478bd9Sstevel@tonic-gate * to IP?
1567c478bd9Sstevel@tonic-gate *
1577c478bd9Sstevel@tonic-gate * Answer: Yes, because I need to know which queue is BOUND to
1587c478bd9Sstevel@tonic-gate * IPPROTO_ESP
1597c478bd9Sstevel@tonic-gate */
1607c478bd9Sstevel@tonic-gate
1617c478bd9Sstevel@tonic-gate static int esp_kstat_update(kstat_t *, int);
1627c478bd9Sstevel@tonic-gate
1637c478bd9Sstevel@tonic-gate static boolean_t
esp_kstat_init(ipsecesp_stack_t * espstack,netstackid_t stackid)164f4b3ec61Sdh esp_kstat_init(ipsecesp_stack_t *espstack, netstackid_t stackid)
1657c478bd9Sstevel@tonic-gate {
166f4b3ec61Sdh espstack->esp_ksp = kstat_create_netstack("ipsecesp", 0, "esp_stat",
167f4b3ec61Sdh "net", KSTAT_TYPE_NAMED,
168281819e5SDan McDonald sizeof (esp_kstats_t) / sizeof (kstat_named_t), 0, stackid);
1697c478bd9Sstevel@tonic-gate
170f4b3ec61Sdh if (espstack->esp_ksp == NULL || espstack->esp_ksp->ks_data == NULL)
1717c478bd9Sstevel@tonic-gate return (B_FALSE);
1727c478bd9Sstevel@tonic-gate
173f4b3ec61Sdh espstack->esp_kstats = espstack->esp_ksp->ks_data;
1747c478bd9Sstevel@tonic-gate
175f4b3ec61Sdh espstack->esp_ksp->ks_update = esp_kstat_update;
176f4b3ec61Sdh espstack->esp_ksp->ks_private = (void *)(uintptr_t)stackid;
1777c478bd9Sstevel@tonic-gate
1787c478bd9Sstevel@tonic-gate #define K64 KSTAT_DATA_UINT64
179f4b3ec61Sdh #define KI(x) kstat_named_init(&(espstack->esp_kstats->esp_stat_##x), #x, K64)
1807c478bd9Sstevel@tonic-gate
1817c478bd9Sstevel@tonic-gate KI(num_aalgs);
1827c478bd9Sstevel@tonic-gate KI(num_ealgs);
1837c478bd9Sstevel@tonic-gate KI(good_auth);
1847c478bd9Sstevel@tonic-gate KI(bad_auth);
1857c478bd9Sstevel@tonic-gate KI(bad_padding);
1867c478bd9Sstevel@tonic-gate KI(replay_failures);
1877c478bd9Sstevel@tonic-gate KI(replay_early_failures);
1887c478bd9Sstevel@tonic-gate KI(keysock_in);
1897c478bd9Sstevel@tonic-gate KI(out_requests);
1907c478bd9Sstevel@tonic-gate KI(acquire_requests);
1917c478bd9Sstevel@tonic-gate KI(bytes_expired);
1927c478bd9Sstevel@tonic-gate KI(out_discards);
1937c478bd9Sstevel@tonic-gate KI(crypto_sync);
1947c478bd9Sstevel@tonic-gate KI(crypto_async);
1957c478bd9Sstevel@tonic-gate KI(crypto_failures);
1967c478bd9Sstevel@tonic-gate KI(bad_decrypt);
1974a179720Sdanmcd KI(sa_port_renumbers);
1987c478bd9Sstevel@tonic-gate
1997c478bd9Sstevel@tonic-gate #undef KI
2007c478bd9Sstevel@tonic-gate #undef K64
2017c478bd9Sstevel@tonic-gate
202f4b3ec61Sdh kstat_install(espstack->esp_ksp);
2037c478bd9Sstevel@tonic-gate
2047c478bd9Sstevel@tonic-gate return (B_TRUE);
2057c478bd9Sstevel@tonic-gate }
2067c478bd9Sstevel@tonic-gate
2077c478bd9Sstevel@tonic-gate static int
esp_kstat_update(kstat_t * kp,int rw)2087c478bd9Sstevel@tonic-gate esp_kstat_update(kstat_t *kp, int rw)
2097c478bd9Sstevel@tonic-gate {
2107c478bd9Sstevel@tonic-gate esp_kstats_t *ekp;
211a23b3b1bSToomas Soome netstackid_t stackid;
212f4b3ec61Sdh netstack_t *ns;
213f4b3ec61Sdh ipsec_stack_t *ipss;
2147c478bd9Sstevel@tonic-gate
2157c478bd9Sstevel@tonic-gate if ((kp == NULL) || (kp->ks_data == NULL))
2167c478bd9Sstevel@tonic-gate return (EIO);
2177c478bd9Sstevel@tonic-gate
2187c478bd9Sstevel@tonic-gate if (rw == KSTAT_WRITE)
2197c478bd9Sstevel@tonic-gate return (EACCES);
2207c478bd9Sstevel@tonic-gate
221a23b3b1bSToomas Soome stackid = (zoneid_t)(uintptr_t)kp->ks_private;
222f4b3ec61Sdh ns = netstack_find_by_stackid(stackid);
223f4b3ec61Sdh if (ns == NULL)
224f4b3ec61Sdh return (-1);
225f4b3ec61Sdh ipss = ns->netstack_ipsec;
226f4b3ec61Sdh if (ipss == NULL) {
227f4b3ec61Sdh netstack_rele(ns);
228f4b3ec61Sdh return (-1);
229f4b3ec61Sdh }
2307c478bd9Sstevel@tonic-gate ekp = (esp_kstats_t *)kp->ks_data;
2317c478bd9Sstevel@tonic-gate
23269e71331SBayard Bell rw_enter(&ipss->ipsec_alg_lock, RW_READER);
233f4b3ec61Sdh ekp->esp_stat_num_aalgs.value.ui64 =
234f4b3ec61Sdh ipss->ipsec_nalgs[IPSEC_ALG_AUTH];
235f4b3ec61Sdh ekp->esp_stat_num_ealgs.value.ui64 =
236f4b3ec61Sdh ipss->ipsec_nalgs[IPSEC_ALG_ENCR];
23769e71331SBayard Bell rw_exit(&ipss->ipsec_alg_lock);
2387c478bd9Sstevel@tonic-gate
239f4b3ec61Sdh netstack_rele(ns);
2407c478bd9Sstevel@tonic-gate return (0);
2417c478bd9Sstevel@tonic-gate }
2427c478bd9Sstevel@tonic-gate
2437c478bd9Sstevel@tonic-gate #ifdef DEBUG
2447c478bd9Sstevel@tonic-gate /*
2457c478bd9Sstevel@tonic-gate * Debug routine, useful to see pre-encryption data.
2467c478bd9Sstevel@tonic-gate */
2477c478bd9Sstevel@tonic-gate static char *
dump_msg(mblk_t * mp)2487c478bd9Sstevel@tonic-gate dump_msg(mblk_t *mp)
2497c478bd9Sstevel@tonic-gate {
2507c478bd9Sstevel@tonic-gate char tmp_str[3], tmp_line[256];
2517c478bd9Sstevel@tonic-gate
2527c478bd9Sstevel@tonic-gate while (mp != NULL) {
2537c478bd9Sstevel@tonic-gate unsigned char *ptr;
2547c478bd9Sstevel@tonic-gate
2557c478bd9Sstevel@tonic-gate printf("mblk address 0x%p, length %ld, db_ref %d "
2567c478bd9Sstevel@tonic-gate "type %d, base 0x%p, lim 0x%p\n",
2577c478bd9Sstevel@tonic-gate (void *) mp, (long)(mp->b_wptr - mp->b_rptr),
2587c478bd9Sstevel@tonic-gate mp->b_datap->db_ref, mp->b_datap->db_type,
2597c478bd9Sstevel@tonic-gate (void *)mp->b_datap->db_base, (void *)mp->b_datap->db_lim);
2607c478bd9Sstevel@tonic-gate ptr = mp->b_rptr;
2617c478bd9Sstevel@tonic-gate
2627c478bd9Sstevel@tonic-gate tmp_line[0] = '\0';
2637c478bd9Sstevel@tonic-gate while (ptr < mp->b_wptr) {
2647c478bd9Sstevel@tonic-gate uint_t diff;
2657c478bd9Sstevel@tonic-gate
2667c478bd9Sstevel@tonic-gate diff = (ptr - mp->b_rptr);
2677c478bd9Sstevel@tonic-gate if (!(diff & 0x1f)) {
2687c478bd9Sstevel@tonic-gate if (strlen(tmp_line) > 0) {
2697c478bd9Sstevel@tonic-gate printf("bytes: %s\n", tmp_line);
2707c478bd9Sstevel@tonic-gate tmp_line[0] = '\0';
2717c478bd9Sstevel@tonic-gate }
2727c478bd9Sstevel@tonic-gate }
2737c478bd9Sstevel@tonic-gate if (!(diff & 0x3))
2747c478bd9Sstevel@tonic-gate (void) strcat(tmp_line, " ");
2757c478bd9Sstevel@tonic-gate (void) sprintf(tmp_str, "%02x", *ptr);
2767c478bd9Sstevel@tonic-gate (void) strcat(tmp_line, tmp_str);
2777c478bd9Sstevel@tonic-gate ptr++;
2787c478bd9Sstevel@tonic-gate }
2797c478bd9Sstevel@tonic-gate if (strlen(tmp_line) > 0)
2807c478bd9Sstevel@tonic-gate printf("bytes: %s\n", tmp_line);
2817c478bd9Sstevel@tonic-gate
2827c478bd9Sstevel@tonic-gate mp = mp->b_cont;
2837c478bd9Sstevel@tonic-gate }
2847c478bd9Sstevel@tonic-gate
2857c478bd9Sstevel@tonic-gate return ("\n");
2867c478bd9Sstevel@tonic-gate }
2877c478bd9Sstevel@tonic-gate
2887c478bd9Sstevel@tonic-gate #else /* DEBUG */
2897c478bd9Sstevel@tonic-gate static char *
dump_msg(mblk_t * mp)2907c478bd9Sstevel@tonic-gate dump_msg(mblk_t *mp)
2917c478bd9Sstevel@tonic-gate {
2927c478bd9Sstevel@tonic-gate printf("Find value of mp %p.\n", mp);
2937c478bd9Sstevel@tonic-gate return ("\n");
2947c478bd9Sstevel@tonic-gate }
2957c478bd9Sstevel@tonic-gate #endif /* DEBUG */
2967c478bd9Sstevel@tonic-gate
2977c478bd9Sstevel@tonic-gate /*
2987c478bd9Sstevel@tonic-gate * Don't have to lock age_interval, as only one thread will access it at
2997c478bd9Sstevel@tonic-gate * a time, because I control the one function that does with timeout().
3007c478bd9Sstevel@tonic-gate */
3017c478bd9Sstevel@tonic-gate static void
esp_ager(void * arg)302f4b3ec61Sdh esp_ager(void *arg)
3037c478bd9Sstevel@tonic-gate {
304f4b3ec61Sdh ipsecesp_stack_t *espstack = (ipsecesp_stack_t *)arg;
305f4b3ec61Sdh netstack_t *ns = espstack->ipsecesp_netstack;
3067c478bd9Sstevel@tonic-gate hrtime_t begin = gethrtime();
3077c478bd9Sstevel@tonic-gate
308f4b3ec61Sdh sadb_ager(&espstack->esp_sadb.s_v4, espstack->esp_pfkey_q,
309bd670b35SErik Nordmark espstack->ipsecesp_reap_delay, ns);
310f4b3ec61Sdh sadb_ager(&espstack->esp_sadb.s_v6, espstack->esp_pfkey_q,
311bd670b35SErik Nordmark espstack->ipsecesp_reap_delay, ns);
3127c478bd9Sstevel@tonic-gate
313f4b3ec61Sdh espstack->esp_event = sadb_retimeout(begin, espstack->esp_pfkey_q,
314f4b3ec61Sdh esp_ager, espstack,
315f4b3ec61Sdh &espstack->ipsecesp_age_interval, espstack->ipsecesp_age_int_max,
316f4b3ec61Sdh info.mi_idnum);
3177c478bd9Sstevel@tonic-gate }
3187c478bd9Sstevel@tonic-gate
3197c478bd9Sstevel@tonic-gate /*
3207c478bd9Sstevel@tonic-gate * Get an ESP NDD parameter.
3217c478bd9Sstevel@tonic-gate */
3227c478bd9Sstevel@tonic-gate /* ARGSUSED */
3237c478bd9Sstevel@tonic-gate static int
ipsecesp_param_get(queue_t * q,mblk_t * mp,caddr_t cp,cred_t * cr)32469e71331SBayard Bell ipsecesp_param_get(
32569e71331SBayard Bell queue_t *q,
32669e71331SBayard Bell mblk_t *mp,
32769e71331SBayard Bell caddr_t cp,
32869e71331SBayard Bell cred_t *cr)
3297c478bd9Sstevel@tonic-gate {
3307c478bd9Sstevel@tonic-gate ipsecespparam_t *ipsecesppa = (ipsecespparam_t *)cp;
3317c478bd9Sstevel@tonic-gate uint_t value;
332f4b3ec61Sdh ipsecesp_stack_t *espstack = (ipsecesp_stack_t *)q->q_ptr;
3337c478bd9Sstevel@tonic-gate
334f4b3ec61Sdh mutex_enter(&espstack->ipsecesp_param_lock);
3357c478bd9Sstevel@tonic-gate value = ipsecesppa->ipsecesp_param_value;
336f4b3ec61Sdh mutex_exit(&espstack->ipsecesp_param_lock);
3377c478bd9Sstevel@tonic-gate
3387c478bd9Sstevel@tonic-gate (void) mi_mpprintf(mp, "%u", value);
3397c478bd9Sstevel@tonic-gate return (0);
3407c478bd9Sstevel@tonic-gate }
3417c478bd9Sstevel@tonic-gate
3427c478bd9Sstevel@tonic-gate /*
3437c478bd9Sstevel@tonic-gate * This routine sets an NDD variable in a ipsecespparam_t structure.
3447c478bd9Sstevel@tonic-gate */
3457c478bd9Sstevel@tonic-gate /* ARGSUSED */
3467c478bd9Sstevel@tonic-gate static int
ipsecesp_param_set(queue_t * q,mblk_t * mp,char * value,caddr_t cp,cred_t * cr)34769e71331SBayard Bell ipsecesp_param_set(
34869e71331SBayard Bell queue_t *q,
34969e71331SBayard Bell mblk_t *mp,
35069e71331SBayard Bell char *value,
35169e71331SBayard Bell caddr_t cp,
35269e71331SBayard Bell cred_t *cr)
3537c478bd9Sstevel@tonic-gate {
3547c478bd9Sstevel@tonic-gate ulong_t new_value;
3557c478bd9Sstevel@tonic-gate ipsecespparam_t *ipsecesppa = (ipsecespparam_t *)cp;
356f4b3ec61Sdh ipsecesp_stack_t *espstack = (ipsecesp_stack_t *)q->q_ptr;
3577c478bd9Sstevel@tonic-gate
3587c478bd9Sstevel@tonic-gate /*
3597c478bd9Sstevel@tonic-gate * Fail the request if the new value does not lie within the
3607c478bd9Sstevel@tonic-gate * required bounds.
3617c478bd9Sstevel@tonic-gate */
3627c478bd9Sstevel@tonic-gate if (ddi_strtoul(value, NULL, 10, &new_value) != 0 ||
3637c478bd9Sstevel@tonic-gate new_value < ipsecesppa->ipsecesp_param_min ||
3647c478bd9Sstevel@tonic-gate new_value > ipsecesppa->ipsecesp_param_max) {
3657c478bd9Sstevel@tonic-gate return (EINVAL);
3667c478bd9Sstevel@tonic-gate }
3677c478bd9Sstevel@tonic-gate
3687c478bd9Sstevel@tonic-gate /* Set the new value */
369f4b3ec61Sdh mutex_enter(&espstack->ipsecesp_param_lock);
3707c478bd9Sstevel@tonic-gate ipsecesppa->ipsecesp_param_value = new_value;
371f4b3ec61Sdh mutex_exit(&espstack->ipsecesp_param_lock);
3727c478bd9Sstevel@tonic-gate return (0);
3737c478bd9Sstevel@tonic-gate }
3747c478bd9Sstevel@tonic-gate
3757c478bd9Sstevel@tonic-gate /*
3767c478bd9Sstevel@tonic-gate * Using lifetime NDD variables, fill in an extended combination's
3777c478bd9Sstevel@tonic-gate * lifetime information.
3787c478bd9Sstevel@tonic-gate */
3797c478bd9Sstevel@tonic-gate void
ipsecesp_fill_defs(sadb_x_ecomb_t * ecomb,netstack_t * ns)380f4b3ec61Sdh ipsecesp_fill_defs(sadb_x_ecomb_t *ecomb, netstack_t *ns)
3817c478bd9Sstevel@tonic-gate {
382f4b3ec61Sdh ipsecesp_stack_t *espstack = ns->netstack_ipsecesp;
383f4b3ec61Sdh
384f4b3ec61Sdh ecomb->sadb_x_ecomb_soft_bytes = espstack->ipsecesp_default_soft_bytes;
385f4b3ec61Sdh ecomb->sadb_x_ecomb_hard_bytes = espstack->ipsecesp_default_hard_bytes;
386f4b3ec61Sdh ecomb->sadb_x_ecomb_soft_addtime =
387f4b3ec61Sdh espstack->ipsecesp_default_soft_addtime;
388f4b3ec61Sdh ecomb->sadb_x_ecomb_hard_addtime =
389f4b3ec61Sdh espstack->ipsecesp_default_hard_addtime;
390f4b3ec61Sdh ecomb->sadb_x_ecomb_soft_usetime =
391f4b3ec61Sdh espstack->ipsecesp_default_soft_usetime;
392f4b3ec61Sdh ecomb->sadb_x_ecomb_hard_usetime =
393f4b3ec61Sdh espstack->ipsecesp_default_hard_usetime;
3947c478bd9Sstevel@tonic-gate }
3957c478bd9Sstevel@tonic-gate
3967c478bd9Sstevel@tonic-gate /*
3977c478bd9Sstevel@tonic-gate * Initialize things for ESP at module load time.
3987c478bd9Sstevel@tonic-gate */
3997c478bd9Sstevel@tonic-gate boolean_t
ipsecesp_ddi_init(void)4007c478bd9Sstevel@tonic-gate ipsecesp_ddi_init(void)
4017c478bd9Sstevel@tonic-gate {
402f4b3ec61Sdh esp_taskq = taskq_create("esp_taskq", 1, minclsyspri,
403f4b3ec61Sdh IPSEC_TASKQ_MIN, IPSEC_TASKQ_MAX, 0);
404f4b3ec61Sdh
405f4b3ec61Sdh /*
406f4b3ec61Sdh * We want to be informed each time a stack is created or
407f4b3ec61Sdh * destroyed in the kernel, so we can maintain the
408f4b3ec61Sdh * set of ipsecesp_stack_t's.
409f4b3ec61Sdh */
410f4b3ec61Sdh netstack_register(NS_IPSECESP, ipsecesp_stack_init, NULL,
411f4b3ec61Sdh ipsecesp_stack_fini);
412f4b3ec61Sdh
413f4b3ec61Sdh return (B_TRUE);
414f4b3ec61Sdh }
4157c478bd9Sstevel@tonic-gate
416f4b3ec61Sdh /*
417f4b3ec61Sdh * Walk through the param array specified registering each element with the
418f4b3ec61Sdh * named dispatch handler.
419f4b3ec61Sdh */
420f4b3ec61Sdh static boolean_t
ipsecesp_param_register(IDP * ndp,ipsecespparam_t * espp,int cnt)421f4b3ec61Sdh ipsecesp_param_register(IDP *ndp, ipsecespparam_t *espp, int cnt)
422f4b3ec61Sdh {
423f4b3ec61Sdh for (; cnt-- > 0; espp++) {
4247c478bd9Sstevel@tonic-gate if (espp->ipsecesp_param_name != NULL &&
4257c478bd9Sstevel@tonic-gate espp->ipsecesp_param_name[0]) {
426f4b3ec61Sdh if (!nd_load(ndp,
427f4b3ec61Sdh espp->ipsecesp_param_name,
4287c478bd9Sstevel@tonic-gate ipsecesp_param_get, ipsecesp_param_set,
4297c478bd9Sstevel@tonic-gate (caddr_t)espp)) {
430f4b3ec61Sdh nd_free(ndp);
4317c478bd9Sstevel@tonic-gate return (B_FALSE);
4327c478bd9Sstevel@tonic-gate }
4337c478bd9Sstevel@tonic-gate }
4347c478bd9Sstevel@tonic-gate }
435f4b3ec61Sdh return (B_TRUE);
436f4b3ec61Sdh }
437b7daf799SDan McDonald
438f4b3ec61Sdh /*
439f4b3ec61Sdh * Initialize things for ESP for each stack instance
440f4b3ec61Sdh */
441f4b3ec61Sdh static void *
ipsecesp_stack_init(netstackid_t stackid,netstack_t * ns)442f4b3ec61Sdh ipsecesp_stack_init(netstackid_t stackid, netstack_t *ns)
443f4b3ec61Sdh {
444f4b3ec61Sdh ipsecesp_stack_t *espstack;
445f4b3ec61Sdh ipsecespparam_t *espp;
4467c478bd9Sstevel@tonic-gate
447f4b3ec61Sdh espstack = (ipsecesp_stack_t *)kmem_zalloc(sizeof (*espstack),
448f4b3ec61Sdh KM_SLEEP);
449f4b3ec61Sdh espstack->ipsecesp_netstack = ns;
4507c478bd9Sstevel@tonic-gate
451f4b3ec61Sdh espp = (ipsecespparam_t *)kmem_alloc(sizeof (lcl_param_arr), KM_SLEEP);
452f4b3ec61Sdh espstack->ipsecesp_params = espp;
453f4b3ec61Sdh bcopy(lcl_param_arr, espp, sizeof (lcl_param_arr));
4547c478bd9Sstevel@tonic-gate
455f4b3ec61Sdh (void) ipsecesp_param_register(&espstack->ipsecesp_g_nd, espp,
456f4b3ec61Sdh A_CNT(lcl_param_arr));
4577c478bd9Sstevel@tonic-gate
458f4b3ec61Sdh (void) esp_kstat_init(espstack, stackid);
4597c478bd9Sstevel@tonic-gate
460f4b3ec61Sdh espstack->esp_sadb.s_acquire_timeout =
461f4b3ec61Sdh &espstack->ipsecesp_acquire_timeout;
462f4b3ec61Sdh sadbp_init("ESP", &espstack->esp_sadb, SADB_SATYPE_ESP, esp_hash_size,
463f4b3ec61Sdh espstack->ipsecesp_netstack);
4647c478bd9Sstevel@tonic-gate
465f4b3ec61Sdh mutex_init(&espstack->ipsecesp_param_lock, NULL, MUTEX_DEFAULT, 0);
466f4b3ec61Sdh
467f4b3ec61Sdh ip_drop_register(&espstack->esp_dropper, "IPsec ESP");
468f4b3ec61Sdh return (espstack);
4697c478bd9Sstevel@tonic-gate }
4707c478bd9Sstevel@tonic-gate
4717c478bd9Sstevel@tonic-gate /*
4727c478bd9Sstevel@tonic-gate * Destroy things for ESP at module unload time.
4737c478bd9Sstevel@tonic-gate */
4747c478bd9Sstevel@tonic-gate void
ipsecesp_ddi_destroy(void)4757c478bd9Sstevel@tonic-gate ipsecesp_ddi_destroy(void)
4767c478bd9Sstevel@tonic-gate {
477f4b3ec61Sdh netstack_unregister(NS_IPSECESP);
4787c478bd9Sstevel@tonic-gate taskq_destroy(esp_taskq);
479f4b3ec61Sdh }
480f4b3ec61Sdh
481f4b3ec61Sdh /*
482f4b3ec61Sdh * Destroy things for ESP for one stack instance
483f4b3ec61Sdh */
484f4b3ec61Sdh static void
ipsecesp_stack_fini(netstackid_t stackid,void * arg)485f4b3ec61Sdh ipsecesp_stack_fini(netstackid_t stackid, void *arg)
486f4b3ec61Sdh {
487f4b3ec61Sdh ipsecesp_stack_t *espstack = (ipsecesp_stack_t *)arg;
488f4b3ec61Sdh
489f4b3ec61Sdh if (espstack->esp_pfkey_q != NULL) {
490f4b3ec61Sdh (void) quntimeout(espstack->esp_pfkey_q, espstack->esp_event);
491f4b3ec61Sdh }
492f4b3ec61Sdh espstack->esp_sadb.s_acquire_timeout = NULL;
493f4b3ec61Sdh sadbp_destroy(&espstack->esp_sadb, espstack->ipsecesp_netstack);
494f4b3ec61Sdh ip_drop_unregister(&espstack->esp_dropper);
495f4b3ec61Sdh mutex_destroy(&espstack->ipsecesp_param_lock);
496f4b3ec61Sdh nd_free(&espstack->ipsecesp_g_nd);
497f4b3ec61Sdh
498f4b3ec61Sdh kmem_free(espstack->ipsecesp_params, sizeof (lcl_param_arr));
499f4b3ec61Sdh espstack->ipsecesp_params = NULL;
500f4b3ec61Sdh kstat_delete_netstack(espstack->esp_ksp, stackid);
501f4b3ec61Sdh espstack->esp_ksp = NULL;
502f4b3ec61Sdh espstack->esp_kstats = NULL;
503f4b3ec61Sdh kmem_free(espstack, sizeof (*espstack));
5047c478bd9Sstevel@tonic-gate }
5057c478bd9Sstevel@tonic-gate
5067c478bd9Sstevel@tonic-gate /*
507bd670b35SErik Nordmark * ESP module open routine, which is here for keysock plumbing.
508bd670b35SErik Nordmark * Keysock is pushed over {AH,ESP} which is an artifact from the Bad Old
509bd670b35SErik Nordmark * Days of export control, and fears that ESP would not be allowed
510bd670b35SErik Nordmark * to be shipped at all by default. Eventually, keysock should
511bd670b35SErik Nordmark * either access AH and ESP via modstubs or krtld dependencies, or
512bd670b35SErik Nordmark * perhaps be folded in with AH and ESP into a single IPsec/netsec
513bd670b35SErik Nordmark * module ("netsec" if PF_KEY provides more than AH/ESP keying tables).
5147c478bd9Sstevel@tonic-gate */
5157c478bd9Sstevel@tonic-gate /* ARGSUSED */
5167c478bd9Sstevel@tonic-gate static int
ipsecesp_open(queue_t * q,dev_t * devp,int flag,int sflag,cred_t * credp)5177c478bd9Sstevel@tonic-gate ipsecesp_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp)
5187c478bd9Sstevel@tonic-gate {
519f4b3ec61Sdh netstack_t *ns;
520f4b3ec61Sdh ipsecesp_stack_t *espstack;
521f4b3ec61Sdh
522d2370ffeSsommerfe if (secpolicy_ip_config(credp, B_FALSE) != 0)
5237c478bd9Sstevel@tonic-gate return (EPERM);
5247c478bd9Sstevel@tonic-gate
5257c478bd9Sstevel@tonic-gate if (q->q_ptr != NULL)
5267c478bd9Sstevel@tonic-gate return (0); /* Re-open of an already open instance. */
5277c478bd9Sstevel@tonic-gate
5287c478bd9Sstevel@tonic-gate if (sflag != MODOPEN)
5297c478bd9Sstevel@tonic-gate return (EINVAL);
5307c478bd9Sstevel@tonic-gate
531f4b3ec61Sdh ns = netstack_find_by_cred(credp);
532f4b3ec61Sdh ASSERT(ns != NULL);
533f4b3ec61Sdh espstack = ns->netstack_ipsecesp;
534f4b3ec61Sdh ASSERT(espstack != NULL);
535f4b3ec61Sdh
536f4b3ec61Sdh q->q_ptr = espstack;
537f4b3ec61Sdh WR(q)->q_ptr = q->q_ptr;
5387c478bd9Sstevel@tonic-gate
539bd670b35SErik Nordmark qprocson(q);
5407c478bd9Sstevel@tonic-gate return (0);
5417c478bd9Sstevel@tonic-gate }
5427c478bd9Sstevel@tonic-gate
5437c478bd9Sstevel@tonic-gate /*
5447c478bd9Sstevel@tonic-gate * ESP module close routine.
5457c478bd9Sstevel@tonic-gate */
5465e1743f0SToomas Soome /* ARGSUSED */
5477c478bd9Sstevel@tonic-gate static int
ipsecesp_close(queue_t * q,int flags __unused,cred_t * credp __unused)5485e1743f0SToomas Soome ipsecesp_close(queue_t *q, int flags __unused, cred_t *credp __unused)
5497c478bd9Sstevel@tonic-gate {
550f4b3ec61Sdh ipsecesp_stack_t *espstack = (ipsecesp_stack_t *)q->q_ptr;
551f4b3ec61Sdh
5527c478bd9Sstevel@tonic-gate /*
5537c478bd9Sstevel@tonic-gate * Clean up q_ptr, if needed.
5547c478bd9Sstevel@tonic-gate */
5557c478bd9Sstevel@tonic-gate qprocsoff(q);
5567c478bd9Sstevel@tonic-gate
5577c478bd9Sstevel@tonic-gate /* Keysock queue check is safe, because of OCEXCL perimeter. */
5587c478bd9Sstevel@tonic-gate
559f4b3ec61Sdh if (q == espstack->esp_pfkey_q) {
560f4b3ec61Sdh esp1dbg(espstack,
561f4b3ec61Sdh ("ipsecesp_close: Ummm... keysock is closing ESP.\n"));
562f4b3ec61Sdh espstack->esp_pfkey_q = NULL;
5637c478bd9Sstevel@tonic-gate /* Detach qtimeouts. */
564f4b3ec61Sdh (void) quntimeout(q, espstack->esp_event);
5657c478bd9Sstevel@tonic-gate }
5667c478bd9Sstevel@tonic-gate
567f4b3ec61Sdh netstack_rele(espstack->ipsecesp_netstack);
5687c478bd9Sstevel@tonic-gate return (0);
5697c478bd9Sstevel@tonic-gate }
5707c478bd9Sstevel@tonic-gate
5717c478bd9Sstevel@tonic-gate /*
5727c478bd9Sstevel@tonic-gate * Add a number of bytes to what the SA has protected so far. Return
5737c478bd9Sstevel@tonic-gate * B_TRUE if the SA can still protect that many bytes.
5747c478bd9Sstevel@tonic-gate *
5757c478bd9Sstevel@tonic-gate * Caller must REFRELE the passed-in assoc. This function must REFRELE
5767c478bd9Sstevel@tonic-gate * any obtained peer SA.
5777c478bd9Sstevel@tonic-gate */
5787c478bd9Sstevel@tonic-gate static boolean_t
esp_age_bytes(ipsa_t * assoc,uint64_t bytes,boolean_t inbound)5797c478bd9Sstevel@tonic-gate esp_age_bytes(ipsa_t *assoc, uint64_t bytes, boolean_t inbound)
5807c478bd9Sstevel@tonic-gate {
5817c478bd9Sstevel@tonic-gate ipsa_t *inassoc, *outassoc;
5827c478bd9Sstevel@tonic-gate isaf_t *bucket;
5837c478bd9Sstevel@tonic-gate boolean_t inrc, outrc, isv6;
5847c478bd9Sstevel@tonic-gate sadb_t *sp;
5857c478bd9Sstevel@tonic-gate int outhash;
586f4b3ec61Sdh netstack_t *ns = assoc->ipsa_netstack;
587f4b3ec61Sdh ipsecesp_stack_t *espstack = ns->netstack_ipsecesp;
5887c478bd9Sstevel@tonic-gate
5897c478bd9Sstevel@tonic-gate /* No peer? No problem! */
5907c478bd9Sstevel@tonic-gate if (!assoc->ipsa_haspeer) {
591f4b3ec61Sdh return (sadb_age_bytes(espstack->esp_pfkey_q, assoc, bytes,
5927c478bd9Sstevel@tonic-gate B_TRUE));
5937c478bd9Sstevel@tonic-gate }
5947c478bd9Sstevel@tonic-gate
5957c478bd9Sstevel@tonic-gate /*
5967c478bd9Sstevel@tonic-gate * Otherwise, we want to grab both the original assoc and its peer.
5977c478bd9Sstevel@tonic-gate * There might be a race for this, but if it's a real race, two
5987c478bd9Sstevel@tonic-gate * expire messages may occur. We limit this by only sending the
5997c478bd9Sstevel@tonic-gate * expire message on one of the peers, we'll pick the inbound
6007c478bd9Sstevel@tonic-gate * arbitrarily.
6017c478bd9Sstevel@tonic-gate *
6027c478bd9Sstevel@tonic-gate * If we need tight synchronization on the peer SA, then we need to
6037c478bd9Sstevel@tonic-gate * reconsider.
6047c478bd9Sstevel@tonic-gate */
6057c478bd9Sstevel@tonic-gate
6067c478bd9Sstevel@tonic-gate /* Use address length to select IPv6/IPv4 */
6077c478bd9Sstevel@tonic-gate isv6 = (assoc->ipsa_addrfam == AF_INET6);
608f4b3ec61Sdh sp = isv6 ? &espstack->esp_sadb.s_v6 : &espstack->esp_sadb.s_v4;
6097c478bd9Sstevel@tonic-gate
6107c478bd9Sstevel@tonic-gate if (inbound) {
6117c478bd9Sstevel@tonic-gate inassoc = assoc;
6127c478bd9Sstevel@tonic-gate if (isv6) {
613fb87b5d2Ssommerfe outhash = OUTBOUND_HASH_V6(sp, *((in6_addr_t *)
6147c478bd9Sstevel@tonic-gate &inassoc->ipsa_dstaddr));
6157c478bd9Sstevel@tonic-gate } else {
616fb87b5d2Ssommerfe outhash = OUTBOUND_HASH_V4(sp, *((ipaddr_t *)
617437220cdSdanmcd &inassoc->ipsa_dstaddr));
6187c478bd9Sstevel@tonic-gate }
6197c478bd9Sstevel@tonic-gate bucket = &sp->sdb_of[outhash];
6207c478bd9Sstevel@tonic-gate mutex_enter(&bucket->isaf_lock);
6217c478bd9Sstevel@tonic-gate outassoc = ipsec_getassocbyspi(bucket, inassoc->ipsa_spi,
6227c478bd9Sstevel@tonic-gate inassoc->ipsa_srcaddr, inassoc->ipsa_dstaddr,
6237c478bd9Sstevel@tonic-gate inassoc->ipsa_addrfam);
6247c478bd9Sstevel@tonic-gate mutex_exit(&bucket->isaf_lock);
6257c478bd9Sstevel@tonic-gate if (outassoc == NULL) {
6267c478bd9Sstevel@tonic-gate /* Q: Do we wish to set haspeer == B_FALSE? */
6277c478bd9Sstevel@tonic-gate esp0dbg(("esp_age_bytes: "
6287c478bd9Sstevel@tonic-gate "can't find peer for inbound.\n"));
629f4b3ec61Sdh return (sadb_age_bytes(espstack->esp_pfkey_q, inassoc,
6307c478bd9Sstevel@tonic-gate bytes, B_TRUE));
6317c478bd9Sstevel@tonic-gate }
6327c478bd9Sstevel@tonic-gate } else {
6337c478bd9Sstevel@tonic-gate outassoc = assoc;
634fb87b5d2Ssommerfe bucket = INBOUND_BUCKET(sp, outassoc->ipsa_spi);
6357c478bd9Sstevel@tonic-gate mutex_enter(&bucket->isaf_lock);
6367c478bd9Sstevel@tonic-gate inassoc = ipsec_getassocbyspi(bucket, outassoc->ipsa_spi,
6377c478bd9Sstevel@tonic-gate outassoc->ipsa_srcaddr, outassoc->ipsa_dstaddr,
6387c478bd9Sstevel@tonic-gate outassoc->ipsa_addrfam);
6397c478bd9Sstevel@tonic-gate mutex_exit(&bucket->isaf_lock);
6407c478bd9Sstevel@tonic-gate if (inassoc == NULL) {
6417c478bd9Sstevel@tonic-gate /* Q: Do we wish to set haspeer == B_FALSE? */
6427c478bd9Sstevel@tonic-gate esp0dbg(("esp_age_bytes: "
6437c478bd9Sstevel@tonic-gate "can't find peer for outbound.\n"));
644f4b3ec61Sdh return (sadb_age_bytes(espstack->esp_pfkey_q, outassoc,
6457c478bd9Sstevel@tonic-gate bytes, B_TRUE));
6467c478bd9Sstevel@tonic-gate }
6477c478bd9Sstevel@tonic-gate }
6487c478bd9Sstevel@tonic-gate
649f4b3ec61Sdh inrc = sadb_age_bytes(espstack->esp_pfkey_q, inassoc, bytes, B_TRUE);
650f4b3ec61Sdh outrc = sadb_age_bytes(espstack->esp_pfkey_q, outassoc, bytes, B_FALSE);
6517c478bd9Sstevel@tonic-gate
6527c478bd9Sstevel@tonic-gate /*
6537c478bd9Sstevel@tonic-gate * REFRELE any peer SA.
6547c478bd9Sstevel@tonic-gate *
6557c478bd9Sstevel@tonic-gate * Because of the multi-line macro nature of IPSA_REFRELE, keep
6567c478bd9Sstevel@tonic-gate * them in { }.
6577c478bd9Sstevel@tonic-gate */
6587c478bd9Sstevel@tonic-gate if (inbound) {
6597c478bd9Sstevel@tonic-gate IPSA_REFRELE(outassoc);
6607c478bd9Sstevel@tonic-gate } else {
6617c478bd9Sstevel@tonic-gate IPSA_REFRELE(inassoc);
6627c478bd9Sstevel@tonic-gate }
6637c478bd9Sstevel@tonic-gate
6647c478bd9Sstevel@tonic-gate return (inrc && outrc);
6657c478bd9Sstevel@tonic-gate }
6667c478bd9Sstevel@tonic-gate
6677c478bd9Sstevel@tonic-gate /*
6687c478bd9Sstevel@tonic-gate * Do incoming NAT-T manipulations for packet.
669bd670b35SErik Nordmark * Returns NULL if the mblk chain is consumed.
6707c478bd9Sstevel@tonic-gate */
671bd670b35SErik Nordmark static mblk_t *
esp_fix_natt_checksums(mblk_t * data_mp,ipsa_t * assoc)6727c478bd9Sstevel@tonic-gate esp_fix_natt_checksums(mblk_t *data_mp, ipsa_t *assoc)
6737c478bd9Sstevel@tonic-gate {
6747c478bd9Sstevel@tonic-gate ipha_t *ipha = (ipha_t *)data_mp->b_rptr;
675bd670b35SErik Nordmark tcpha_t *tcpha;
6767c478bd9Sstevel@tonic-gate udpha_t *udpha;
6777c478bd9Sstevel@tonic-gate /* Initialize to our inbound cksum adjustment... */
6787c478bd9Sstevel@tonic-gate uint32_t sum = assoc->ipsa_inbound_cksum;
6797c478bd9Sstevel@tonic-gate
6807c478bd9Sstevel@tonic-gate switch (ipha->ipha_protocol) {
6817c478bd9Sstevel@tonic-gate case IPPROTO_TCP:
682bd670b35SErik Nordmark tcpha = (tcpha_t *)(data_mp->b_rptr +
6837c478bd9Sstevel@tonic-gate IPH_HDR_LENGTH(ipha));
6847c478bd9Sstevel@tonic-gate
6857c478bd9Sstevel@tonic-gate #define DOWN_SUM(x) (x) = ((x) & 0xFFFF) + ((x) >> 16)
686bd670b35SErik Nordmark sum += ~ntohs(tcpha->tha_sum) & 0xFFFF;
6877c478bd9Sstevel@tonic-gate DOWN_SUM(sum);
6887c478bd9Sstevel@tonic-gate DOWN_SUM(sum);
689bd670b35SErik Nordmark tcpha->tha_sum = ~htons(sum);
6907c478bd9Sstevel@tonic-gate break;
6917c478bd9Sstevel@tonic-gate case IPPROTO_UDP:
6927c478bd9Sstevel@tonic-gate udpha = (udpha_t *)(data_mp->b_rptr + IPH_HDR_LENGTH(ipha));
6937c478bd9Sstevel@tonic-gate
6947c478bd9Sstevel@tonic-gate if (udpha->uha_checksum != 0) {
6957c478bd9Sstevel@tonic-gate /* Adujst if the inbound one was not zero. */
6967c478bd9Sstevel@tonic-gate sum += ~ntohs(udpha->uha_checksum) & 0xFFFF;
6977c478bd9Sstevel@tonic-gate DOWN_SUM(sum);
6987c478bd9Sstevel@tonic-gate DOWN_SUM(sum);
6997c478bd9Sstevel@tonic-gate udpha->uha_checksum = ~htons(sum);
7007c478bd9Sstevel@tonic-gate if (udpha->uha_checksum == 0)
7017c478bd9Sstevel@tonic-gate udpha->uha_checksum = 0xFFFF;
7027c478bd9Sstevel@tonic-gate }
7037c478bd9Sstevel@tonic-gate #undef DOWN_SUM
7047c478bd9Sstevel@tonic-gate break;
7057c478bd9Sstevel@tonic-gate case IPPROTO_IP:
7067c478bd9Sstevel@tonic-gate /*
7077c478bd9Sstevel@tonic-gate * This case is only an issue for self-encapsulated
7087c478bd9Sstevel@tonic-gate * packets. So for now, fall through.
7097c478bd9Sstevel@tonic-gate */
7107c478bd9Sstevel@tonic-gate break;
7117c478bd9Sstevel@tonic-gate }
712bd670b35SErik Nordmark return (data_mp);
7137c478bd9Sstevel@tonic-gate }
7147c478bd9Sstevel@tonic-gate
7157c478bd9Sstevel@tonic-gate
7167c478bd9Sstevel@tonic-gate /*
71732350c00Sdanmcd * Strip ESP header, check padding, and fix IP header.
7187c478bd9Sstevel@tonic-gate * Returns B_TRUE on success, B_FALSE if an error occured.
7197c478bd9Sstevel@tonic-gate */
7207c478bd9Sstevel@tonic-gate static boolean_t
esp_strip_header(mblk_t * data_mp,boolean_t isv4,uint32_t ivlen,kstat_named_t ** counter,ipsecesp_stack_t * espstack)7217c478bd9Sstevel@tonic-gate esp_strip_header(mblk_t *data_mp, boolean_t isv4, uint32_t ivlen,
722f4b3ec61Sdh kstat_named_t **counter, ipsecesp_stack_t *espstack)
7237c478bd9Sstevel@tonic-gate {
7247c478bd9Sstevel@tonic-gate ipha_t *ipha;
7257c478bd9Sstevel@tonic-gate ip6_t *ip6h;
7267c478bd9Sstevel@tonic-gate uint_t divpoint;
7277c478bd9Sstevel@tonic-gate mblk_t *scratch;
7287c478bd9Sstevel@tonic-gate uint8_t nexthdr, padlen;
7297c478bd9Sstevel@tonic-gate uint8_t lastpad;
730f4b3ec61Sdh ipsec_stack_t *ipss = espstack->ipsecesp_netstack->netstack_ipsec;
73132350c00Sdanmcd uint8_t *lastbyte;
7327c478bd9Sstevel@tonic-gate
7337c478bd9Sstevel@tonic-gate /*
7347c478bd9Sstevel@tonic-gate * Strip ESP data and fix IP header.
7357c478bd9Sstevel@tonic-gate *
7367c478bd9Sstevel@tonic-gate * XXX In case the beginning of esp_inbound() changes to not do a
7377c478bd9Sstevel@tonic-gate * pullup, this part of the code can remain unchanged.
7387c478bd9Sstevel@tonic-gate */
7397c478bd9Sstevel@tonic-gate if (isv4) {
7407c478bd9Sstevel@tonic-gate ASSERT((data_mp->b_wptr - data_mp->b_rptr) >= sizeof (ipha_t));
7417c478bd9Sstevel@tonic-gate ipha = (ipha_t *)data_mp->b_rptr;
7427c478bd9Sstevel@tonic-gate ASSERT((data_mp->b_wptr - data_mp->b_rptr) >= sizeof (esph_t) +
7437c478bd9Sstevel@tonic-gate IPH_HDR_LENGTH(ipha));
7447c478bd9Sstevel@tonic-gate divpoint = IPH_HDR_LENGTH(ipha);
7457c478bd9Sstevel@tonic-gate } else {
7467c478bd9Sstevel@tonic-gate ASSERT((data_mp->b_wptr - data_mp->b_rptr) >= sizeof (ip6_t));
7477c478bd9Sstevel@tonic-gate ip6h = (ip6_t *)data_mp->b_rptr;
7487c478bd9Sstevel@tonic-gate divpoint = ip_hdr_length_v6(data_mp, ip6h);
7497c478bd9Sstevel@tonic-gate }
7507c478bd9Sstevel@tonic-gate
7517c478bd9Sstevel@tonic-gate scratch = data_mp;
7527c478bd9Sstevel@tonic-gate while (scratch->b_cont != NULL)
7537c478bd9Sstevel@tonic-gate scratch = scratch->b_cont;
7547c478bd9Sstevel@tonic-gate
7557c478bd9Sstevel@tonic-gate ASSERT((scratch->b_wptr - scratch->b_rptr) >= 3);
7567c478bd9Sstevel@tonic-gate
7577c478bd9Sstevel@tonic-gate /*
7587c478bd9Sstevel@tonic-gate * "Next header" and padding length are the last two bytes in the
7597c478bd9Sstevel@tonic-gate * ESP-protected datagram, thus the explicit - 1 and - 2.
7607c478bd9Sstevel@tonic-gate * lastpad is the last byte of the padding, which can be used for
7617c478bd9Sstevel@tonic-gate * a quick check to see if the padding is correct.
7627c478bd9Sstevel@tonic-gate */
76332350c00Sdanmcd lastbyte = scratch->b_wptr - 1;
76432350c00Sdanmcd nexthdr = *lastbyte--;
76532350c00Sdanmcd padlen = *lastbyte--;
7667c478bd9Sstevel@tonic-gate
7677c478bd9Sstevel@tonic-gate if (isv4) {
7687c478bd9Sstevel@tonic-gate /* Fix part of the IP header. */
7697c478bd9Sstevel@tonic-gate ipha->ipha_protocol = nexthdr;
7707c478bd9Sstevel@tonic-gate /*
7717c478bd9Sstevel@tonic-gate * Reality check the padlen. The explicit - 2 is for the
7727c478bd9Sstevel@tonic-gate * padding length and the next-header bytes.
7737c478bd9Sstevel@tonic-gate */
7747c478bd9Sstevel@tonic-gate if (padlen >= ntohs(ipha->ipha_length) - sizeof (ipha_t) - 2 -
7757c478bd9Sstevel@tonic-gate sizeof (esph_t) - ivlen) {
776f4b3ec61Sdh ESP_BUMP_STAT(espstack, bad_decrypt);
777f4b3ec61Sdh ipsec_rl_strlog(espstack->ipsecesp_netstack,
778f4b3ec61Sdh info.mi_idnum, 0, 0,
779f4b3ec61Sdh SL_ERROR | SL_WARN,
78032350c00Sdanmcd "Corrupt ESP packet (padlen too big).\n");
781f4b3ec61Sdh esp1dbg(espstack, ("padlen (%d) is greater than:\n",
782437220cdSdanmcd padlen));
783f4b3ec61Sdh esp1dbg(espstack, ("pkt len(%d) - ip hdr - esp "
784f4b3ec61Sdh "hdr - ivlen(%d) = %d.\n",
785f4b3ec61Sdh ntohs(ipha->ipha_length), ivlen,
7867c478bd9Sstevel@tonic-gate (int)(ntohs(ipha->ipha_length) - sizeof (ipha_t) -
787f4b3ec61Sdh 2 - sizeof (esph_t) - ivlen)));
788f4b3ec61Sdh *counter = DROPPER(ipss, ipds_esp_bad_padlen);
7897c478bd9Sstevel@tonic-gate return (B_FALSE);
7907c478bd9Sstevel@tonic-gate }
7917c478bd9Sstevel@tonic-gate
7927c478bd9Sstevel@tonic-gate /*
7937c478bd9Sstevel@tonic-gate * Fix the rest of the header. The explicit - 2 is for the
7947c478bd9Sstevel@tonic-gate * padding length and the next-header bytes.
7957c478bd9Sstevel@tonic-gate */
7967c478bd9Sstevel@tonic-gate ipha->ipha_length = htons(ntohs(ipha->ipha_length) - padlen -
7977c478bd9Sstevel@tonic-gate 2 - sizeof (esph_t) - ivlen);
7987c478bd9Sstevel@tonic-gate ipha->ipha_hdr_checksum = 0;
7997c478bd9Sstevel@tonic-gate ipha->ipha_hdr_checksum = (uint16_t)ip_csum_hdr(ipha);
8007c478bd9Sstevel@tonic-gate } else {
8017c478bd9Sstevel@tonic-gate if (ip6h->ip6_nxt == IPPROTO_ESP) {
8027c478bd9Sstevel@tonic-gate ip6h->ip6_nxt = nexthdr;
8037c478bd9Sstevel@tonic-gate } else {
804bd670b35SErik Nordmark ip_pkt_t ipp;
8057c478bd9Sstevel@tonic-gate
8067c478bd9Sstevel@tonic-gate bzero(&ipp, sizeof (ipp));
807bd670b35SErik Nordmark (void) ip_find_hdr_v6(data_mp, ip6h, B_FALSE, &ipp,
808bd670b35SErik Nordmark NULL);
8097c478bd9Sstevel@tonic-gate if (ipp.ipp_dstopts != NULL) {
8107c478bd9Sstevel@tonic-gate ipp.ipp_dstopts->ip6d_nxt = nexthdr;
8117c478bd9Sstevel@tonic-gate } else if (ipp.ipp_rthdr != NULL) {
8127c478bd9Sstevel@tonic-gate ipp.ipp_rthdr->ip6r_nxt = nexthdr;
8137c478bd9Sstevel@tonic-gate } else if (ipp.ipp_hopopts != NULL) {
8147c478bd9Sstevel@tonic-gate ipp.ipp_hopopts->ip6h_nxt = nexthdr;
8157c478bd9Sstevel@tonic-gate } else {
8167c478bd9Sstevel@tonic-gate /* Panic a DEBUG kernel. */
8177c478bd9Sstevel@tonic-gate ASSERT(ipp.ipp_hopopts != NULL);
8187c478bd9Sstevel@tonic-gate /* Otherwise, pretend it's IP + ESP. */
8197c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "ESP IPv6 headers wrong.\n");
8207c478bd9Sstevel@tonic-gate ip6h->ip6_nxt = nexthdr;
8217c478bd9Sstevel@tonic-gate }
8227c478bd9Sstevel@tonic-gate }
8237c478bd9Sstevel@tonic-gate
8247c478bd9Sstevel@tonic-gate if (padlen >= ntohs(ip6h->ip6_plen) - 2 - sizeof (esph_t) -
8257c478bd9Sstevel@tonic-gate ivlen) {
826f4b3ec61Sdh ESP_BUMP_STAT(espstack, bad_decrypt);
827f4b3ec61Sdh ipsec_rl_strlog(espstack->ipsecesp_netstack,
828f4b3ec61Sdh info.mi_idnum, 0, 0,
829f4b3ec61Sdh SL_ERROR | SL_WARN,
83032350c00Sdanmcd "Corrupt ESP packet (v6 padlen too big).\n");
831f4b3ec61Sdh esp1dbg(espstack, ("padlen (%d) is greater than:\n",
832437220cdSdanmcd padlen));
833437220cdSdanmcd esp1dbg(espstack,
834437220cdSdanmcd ("pkt len(%u) - ip hdr - esp hdr - ivlen(%d) = "
835437220cdSdanmcd "%u.\n", (unsigned)(ntohs(ip6h->ip6_plen)
836437220cdSdanmcd + sizeof (ip6_t)), ivlen,
837437220cdSdanmcd (unsigned)(ntohs(ip6h->ip6_plen) - 2 -
838437220cdSdanmcd sizeof (esph_t) - ivlen)));
839f4b3ec61Sdh *counter = DROPPER(ipss, ipds_esp_bad_padlen);
8407c478bd9Sstevel@tonic-gate return (B_FALSE);
8417c478bd9Sstevel@tonic-gate }
8427c478bd9Sstevel@tonic-gate
8437c478bd9Sstevel@tonic-gate
8447c478bd9Sstevel@tonic-gate /*
8457c478bd9Sstevel@tonic-gate * Fix the rest of the header. The explicit - 2 is for the
8467c478bd9Sstevel@tonic-gate * padding length and the next-header bytes. IPv6 is nice,
8477c478bd9Sstevel@tonic-gate * because there's no hdr checksum!
8487c478bd9Sstevel@tonic-gate */
8497c478bd9Sstevel@tonic-gate ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) - padlen -
8507c478bd9Sstevel@tonic-gate 2 - sizeof (esph_t) - ivlen);
8517c478bd9Sstevel@tonic-gate }
8527c478bd9Sstevel@tonic-gate
853f4b3ec61Sdh if (espstack->ipsecesp_padding_check > 0 && padlen > 0) {
85432350c00Sdanmcd /*
85532350c00Sdanmcd * Weak padding check: compare last-byte to length, they
85632350c00Sdanmcd * should be equal.
85732350c00Sdanmcd */
85832350c00Sdanmcd lastpad = *lastbyte--;
8597c478bd9Sstevel@tonic-gate
86032350c00Sdanmcd if (padlen != lastpad) {
861f4b3ec61Sdh ipsec_rl_strlog(espstack->ipsecesp_netstack,
862f4b3ec61Sdh info.mi_idnum, 0, 0, SL_ERROR | SL_WARN,
86332350c00Sdanmcd "Corrupt ESP packet (lastpad != padlen).\n");
864f4b3ec61Sdh esp1dbg(espstack,
865f4b3ec61Sdh ("lastpad (%d) not equal to padlen (%d):\n",
866f4b3ec61Sdh lastpad, padlen));
867f4b3ec61Sdh ESP_BUMP_STAT(espstack, bad_padding);
868f4b3ec61Sdh *counter = DROPPER(ipss, ipds_esp_bad_padding);
86932350c00Sdanmcd return (B_FALSE);
87032350c00Sdanmcd }
8717c478bd9Sstevel@tonic-gate
8727c478bd9Sstevel@tonic-gate /*
87332350c00Sdanmcd * Strong padding check: Check all pad bytes to see that
87432350c00Sdanmcd * they're ascending. Go backwards using a descending counter
87532350c00Sdanmcd * to verify. padlen == 1 is checked by previous block, so
87632350c00Sdanmcd * only bother if we've more than 1 byte of padding.
87732350c00Sdanmcd * Consequently, start the check one byte before the location
87832350c00Sdanmcd * of "lastpad".
8797c478bd9Sstevel@tonic-gate */
880f4b3ec61Sdh if (espstack->ipsecesp_padding_check > 1) {
88132350c00Sdanmcd /*
88232350c00Sdanmcd * This assert may have to become an if and a pullup
88332350c00Sdanmcd * if we start accepting multi-dblk mblks. For now,
88432350c00Sdanmcd * though, any packet here will have been pulled up in
88532350c00Sdanmcd * esp_inbound.
88632350c00Sdanmcd */
88732350c00Sdanmcd ASSERT(MBLKL(scratch) >= lastpad + 3);
88832350c00Sdanmcd
88932350c00Sdanmcd /*
89032350c00Sdanmcd * Use "--lastpad" because we already checked the very
89132350c00Sdanmcd * last pad byte previously.
89232350c00Sdanmcd */
89332350c00Sdanmcd while (--lastpad != 0) {
89432350c00Sdanmcd if (lastpad != *lastbyte) {
895f4b3ec61Sdh ipsec_rl_strlog(
896f4b3ec61Sdh espstack->ipsecesp_netstack,
897f4b3ec61Sdh info.mi_idnum, 0, 0,
89832350c00Sdanmcd SL_ERROR | SL_WARN, "Corrupt ESP "
89932350c00Sdanmcd "packet (bad padding).\n");
900f4b3ec61Sdh esp1dbg(espstack,
901f4b3ec61Sdh ("padding not in correct"
902f4b3ec61Sdh " format:\n"));
903f4b3ec61Sdh ESP_BUMP_STAT(espstack, bad_padding);
904f4b3ec61Sdh *counter = DROPPER(ipss,
905f4b3ec61Sdh ipds_esp_bad_padding);
90632350c00Sdanmcd return (B_FALSE);
90732350c00Sdanmcd }
90832350c00Sdanmcd lastbyte--;
9097c478bd9Sstevel@tonic-gate }
9107c478bd9Sstevel@tonic-gate }
9117c478bd9Sstevel@tonic-gate }
9127c478bd9Sstevel@tonic-gate
9137c478bd9Sstevel@tonic-gate /* Trim off the padding. */
9147c478bd9Sstevel@tonic-gate ASSERT(data_mp->b_cont == NULL);
9157c478bd9Sstevel@tonic-gate data_mp->b_wptr -= (padlen + 2);
9167c478bd9Sstevel@tonic-gate
9177c478bd9Sstevel@tonic-gate /*
9187c478bd9Sstevel@tonic-gate * Remove the ESP header.
9197c478bd9Sstevel@tonic-gate *
9207c478bd9Sstevel@tonic-gate * The above assertions about data_mp's size will make this work.
9217c478bd9Sstevel@tonic-gate *
9227c478bd9Sstevel@tonic-gate * XXX Question: If I send up and get back a contiguous mblk,
9237c478bd9Sstevel@tonic-gate * would it be quicker to bcopy over, or keep doing the dupb stuff?
9247c478bd9Sstevel@tonic-gate * I go with copying for now.
9257c478bd9Sstevel@tonic-gate */
9267c478bd9Sstevel@tonic-gate
9277c478bd9Sstevel@tonic-gate if (IS_P2ALIGNED(data_mp->b_rptr, sizeof (uint32_t)) &&
9287c478bd9Sstevel@tonic-gate IS_P2ALIGNED(ivlen, sizeof (uint32_t))) {
9297c478bd9Sstevel@tonic-gate uint8_t *start = data_mp->b_rptr;
9307c478bd9Sstevel@tonic-gate uint32_t *src, *dst;
9317c478bd9Sstevel@tonic-gate
9327c478bd9Sstevel@tonic-gate src = (uint32_t *)(start + divpoint);
9337c478bd9Sstevel@tonic-gate dst = (uint32_t *)(start + divpoint + sizeof (esph_t) + ivlen);
9347c478bd9Sstevel@tonic-gate
9357c478bd9Sstevel@tonic-gate ASSERT(IS_P2ALIGNED(dst, sizeof (uint32_t)) &&
9367c478bd9Sstevel@tonic-gate IS_P2ALIGNED(src, sizeof (uint32_t)));
9377c478bd9Sstevel@tonic-gate
9387c478bd9Sstevel@tonic-gate do {
9397c478bd9Sstevel@tonic-gate src--;
9407c478bd9Sstevel@tonic-gate dst--;
9417c478bd9Sstevel@tonic-gate *dst = *src;
9427c478bd9Sstevel@tonic-gate } while (src != (uint32_t *)start);
9437c478bd9Sstevel@tonic-gate
9447c478bd9Sstevel@tonic-gate data_mp->b_rptr = (uchar_t *)dst;
9457c478bd9Sstevel@tonic-gate } else {
9467c478bd9Sstevel@tonic-gate uint8_t *start = data_mp->b_rptr;
9477c478bd9Sstevel@tonic-gate uint8_t *src, *dst;
9487c478bd9Sstevel@tonic-gate
9497c478bd9Sstevel@tonic-gate src = start + divpoint;
9507c478bd9Sstevel@tonic-gate dst = src + sizeof (esph_t) + ivlen;
9517c478bd9Sstevel@tonic-gate
9527c478bd9Sstevel@tonic-gate do {
9537c478bd9Sstevel@tonic-gate src--;
9547c478bd9Sstevel@tonic-gate dst--;
9557c478bd9Sstevel@tonic-gate *dst = *src;
9567c478bd9Sstevel@tonic-gate } while (src != start);
9577c478bd9Sstevel@tonic-gate
9587c478bd9Sstevel@tonic-gate data_mp->b_rptr = dst;
9597c478bd9Sstevel@tonic-gate }
9607c478bd9Sstevel@tonic-gate
961f4b3ec61Sdh esp2dbg(espstack, ("data_mp after inbound ESP adjustment:\n"));
962f4b3ec61Sdh esp2dbg(espstack, (dump_msg(data_mp)));
9637c478bd9Sstevel@tonic-gate
9647c478bd9Sstevel@tonic-gate return (B_TRUE);
9657c478bd9Sstevel@tonic-gate }
9667c478bd9Sstevel@tonic-gate
9677c478bd9Sstevel@tonic-gate /*
9687c478bd9Sstevel@tonic-gate * Updating use times can be tricky business if the ipsa_haspeer flag is
9697c478bd9Sstevel@tonic-gate * set. This function is called once in an SA's lifetime.
9707c478bd9Sstevel@tonic-gate *
9717c478bd9Sstevel@tonic-gate * Caller has to REFRELE "assoc" which is passed in. This function has
9727c478bd9Sstevel@tonic-gate * to REFRELE any peer SA that is obtained.
9737c478bd9Sstevel@tonic-gate */
9747c478bd9Sstevel@tonic-gate static void
esp_set_usetime(ipsa_t * assoc,boolean_t inbound)9757c478bd9Sstevel@tonic-gate esp_set_usetime(ipsa_t *assoc, boolean_t inbound)
9767c478bd9Sstevel@tonic-gate {
9777c478bd9Sstevel@tonic-gate ipsa_t *inassoc, *outassoc;
9787c478bd9Sstevel@tonic-gate isaf_t *bucket;
9797c478bd9Sstevel@tonic-gate sadb_t *sp;
9807c478bd9Sstevel@tonic-gate int outhash;
9817c478bd9Sstevel@tonic-gate boolean_t isv6;
982f4b3ec61Sdh netstack_t *ns = assoc->ipsa_netstack;
983f4b3ec61Sdh ipsecesp_stack_t *espstack = ns->netstack_ipsecesp;
9847c478bd9Sstevel@tonic-gate
9857c478bd9Sstevel@tonic-gate /* No peer? No problem! */
9867c478bd9Sstevel@tonic-gate if (!assoc->ipsa_haspeer) {
9877c478bd9Sstevel@tonic-gate sadb_set_usetime(assoc);
9887c478bd9Sstevel@tonic-gate return;
9897c478bd9Sstevel@tonic-gate }
9907c478bd9Sstevel@tonic-gate
9917c478bd9Sstevel@tonic-gate /*
9927c478bd9Sstevel@tonic-gate * Otherwise, we want to grab both the original assoc and its peer.
9937c478bd9Sstevel@tonic-gate * There might be a race for this, but if it's a real race, the times
9947c478bd9Sstevel@tonic-gate * will be out-of-synch by at most a second, and since our time
9957c478bd9Sstevel@tonic-gate * granularity is a second, this won't be a problem.
9967c478bd9Sstevel@tonic-gate *
9977c478bd9Sstevel@tonic-gate * If we need tight synchronization on the peer SA, then we need to
9987c478bd9Sstevel@tonic-gate * reconsider.
9997c478bd9Sstevel@tonic-gate */
10007c478bd9Sstevel@tonic-gate
10017c478bd9Sstevel@tonic-gate /* Use address length to select IPv6/IPv4 */
10027c478bd9Sstevel@tonic-gate isv6 = (assoc->ipsa_addrfam == AF_INET6);
1003f4b3ec61Sdh sp = isv6 ? &espstack->esp_sadb.s_v6 : &espstack->esp_sadb.s_v4;
10047c478bd9Sstevel@tonic-gate
10057c478bd9Sstevel@tonic-gate if (inbound) {
10067c478bd9Sstevel@tonic-gate inassoc = assoc;
10077c478bd9Sstevel@tonic-gate if (isv6) {
1008fb87b5d2Ssommerfe outhash = OUTBOUND_HASH_V6(sp, *((in6_addr_t *)
10097c478bd9Sstevel@tonic-gate &inassoc->ipsa_dstaddr));
10107c478bd9Sstevel@tonic-gate } else {
1011fb87b5d2Ssommerfe outhash = OUTBOUND_HASH_V4(sp, *((ipaddr_t *)
1012437220cdSdanmcd &inassoc->ipsa_dstaddr));
10137c478bd9Sstevel@tonic-gate }
10147c478bd9Sstevel@tonic-gate bucket = &sp->sdb_of[outhash];
10157c478bd9Sstevel@tonic-gate mutex_enter(&bucket->isaf_lock);
10167c478bd9Sstevel@tonic-gate outassoc = ipsec_getassocbyspi(bucket, inassoc->ipsa_spi,
10177c478bd9Sstevel@tonic-gate inassoc->ipsa_srcaddr, inassoc->ipsa_dstaddr,
10187c478bd9Sstevel@tonic-gate inassoc->ipsa_addrfam);
10197c478bd9Sstevel@tonic-gate mutex_exit(&bucket->isaf_lock);
10207c478bd9Sstevel@tonic-gate if (outassoc == NULL) {
10217c478bd9Sstevel@tonic-gate /* Q: Do we wish to set haspeer == B_FALSE? */
10227c478bd9Sstevel@tonic-gate esp0dbg(("esp_set_usetime: "
10237c478bd9Sstevel@tonic-gate "can't find peer for inbound.\n"));
10247c478bd9Sstevel@tonic-gate sadb_set_usetime(inassoc);
10257c478bd9Sstevel@tonic-gate return;
10267c478bd9Sstevel@tonic-gate }
10277c478bd9Sstevel@tonic-gate } else {
10287c478bd9Sstevel@tonic-gate outassoc = assoc;
1029fb87b5d2Ssommerfe bucket = INBOUND_BUCKET(sp, outassoc->ipsa_spi);
10307c478bd9Sstevel@tonic-gate mutex_enter(&bucket->isaf_lock);
10317c478bd9Sstevel@tonic-gate inassoc = ipsec_getassocbyspi(bucket, outassoc->ipsa_spi,
10327c478bd9Sstevel@tonic-gate outassoc->ipsa_srcaddr, outassoc->ipsa_dstaddr,
10337c478bd9Sstevel@tonic-gate outassoc->ipsa_addrfam);
10347c478bd9Sstevel@tonic-gate mutex_exit(&bucket->isaf_lock);
10357c478bd9Sstevel@tonic-gate if (inassoc == NULL) {
10367c478bd9Sstevel@tonic-gate /* Q: Do we wish to set haspeer == B_FALSE? */
10377c478bd9Sstevel@tonic-gate esp0dbg(("esp_set_usetime: "
10387c478bd9Sstevel@tonic-gate "can't find peer for outbound.\n"));
10397c478bd9Sstevel@tonic-gate sadb_set_usetime(outassoc);
10407c478bd9Sstevel@tonic-gate return;
10417c478bd9Sstevel@tonic-gate }
10427c478bd9Sstevel@tonic-gate }
10437c478bd9Sstevel@tonic-gate
10447c478bd9Sstevel@tonic-gate /* Update usetime on both. */
10457c478bd9Sstevel@tonic-gate sadb_set_usetime(inassoc);
10467c478bd9Sstevel@tonic-gate sadb_set_usetime(outassoc);
10477c478bd9Sstevel@tonic-gate
10487c478bd9Sstevel@tonic-gate /*
10497c478bd9Sstevel@tonic-gate * REFRELE any peer SA.
10507c478bd9Sstevel@tonic-gate *
10517c478bd9Sstevel@tonic-gate * Because of the multi-line macro nature of IPSA_REFRELE, keep
10527c478bd9Sstevel@tonic-gate * them in { }.
10537c478bd9Sstevel@tonic-gate */
10547c478bd9Sstevel@tonic-gate if (inbound) {
10557c478bd9Sstevel@tonic-gate IPSA_REFRELE(outassoc);
10567c478bd9Sstevel@tonic-gate } else {
10577c478bd9Sstevel@tonic-gate IPSA_REFRELE(inassoc);
10587c478bd9Sstevel@tonic-gate }
10597c478bd9Sstevel@tonic-gate }
10607c478bd9Sstevel@tonic-gate
10617c478bd9Sstevel@tonic-gate /*
10627c478bd9Sstevel@tonic-gate * Handle ESP inbound data for IPv4 and IPv6.
10637c478bd9Sstevel@tonic-gate * On success returns B_TRUE, on failure returns B_FALSE and frees the
1064bd670b35SErik Nordmark * mblk chain data_mp.
10657c478bd9Sstevel@tonic-gate */
1066bd670b35SErik Nordmark mblk_t *
esp_inbound(mblk_t * data_mp,void * arg,ip_recv_attr_t * ira)1067bd670b35SErik Nordmark esp_inbound(mblk_t *data_mp, void *arg, ip_recv_attr_t *ira)
10687c478bd9Sstevel@tonic-gate {
10697c478bd9Sstevel@tonic-gate esph_t *esph = (esph_t *)arg;
1070bd670b35SErik Nordmark ipsa_t *ipsa = ira->ira_ipsec_esp_sa;
1071bd670b35SErik Nordmark netstack_t *ns = ira->ira_ill->ill_ipst->ips_netstack;
1072f4b3ec61Sdh ipsecesp_stack_t *espstack = ns->netstack_ipsecesp;
1073f4b3ec61Sdh ipsec_stack_t *ipss = ns->netstack_ipsec;
10747c478bd9Sstevel@tonic-gate
10757c478bd9Sstevel@tonic-gate /*
10767c478bd9Sstevel@tonic-gate * We may wish to check replay in-range-only here as an optimization.
10777c478bd9Sstevel@tonic-gate * Include the reality check of ipsa->ipsa_replay >
10787c478bd9Sstevel@tonic-gate * ipsa->ipsa_replay_wsize for times when it's the first N packets,
10797c478bd9Sstevel@tonic-gate * where N == ipsa->ipsa_replay_wsize.
10807c478bd9Sstevel@tonic-gate *
10817c478bd9Sstevel@tonic-gate * Another check that may come here later is the "collision" check.
10827c478bd9Sstevel@tonic-gate * If legitimate packets flow quickly enough, this won't be a problem,
10837c478bd9Sstevel@tonic-gate * but collisions may cause authentication algorithm crunching to
10847c478bd9Sstevel@tonic-gate * take place when it doesn't need to.
10857c478bd9Sstevel@tonic-gate */
10867c478bd9Sstevel@tonic-gate if (!sadb_replay_peek(ipsa, esph->esph_replay)) {
1087f4b3ec61Sdh ESP_BUMP_STAT(espstack, replay_early_failures);
1088f4b3ec61Sdh IP_ESP_BUMP_STAT(ipss, in_discards);
1089bd670b35SErik Nordmark ip_drop_packet(data_mp, B_TRUE, ira->ira_ill,
1090f4b3ec61Sdh DROPPER(ipss, ipds_esp_early_replay),
1091f4b3ec61Sdh &espstack->esp_dropper);
1092bd670b35SErik Nordmark BUMP_MIB(ira->ira_ill->ill_ip_mib, ipIfStatsInDiscards);
1093bd670b35SErik Nordmark return (NULL);
10947c478bd9Sstevel@tonic-gate }
10957c478bd9Sstevel@tonic-gate
10967c478bd9Sstevel@tonic-gate /*
10977c478bd9Sstevel@tonic-gate * Adjust the IP header's payload length to reflect the removal
10987c478bd9Sstevel@tonic-gate * of the ICV.
10997c478bd9Sstevel@tonic-gate */
1100bd670b35SErik Nordmark if (!(ira->ira_flags & IRAF_IS_IPV4)) {
11017c478bd9Sstevel@tonic-gate ip6_t *ip6h = (ip6_t *)data_mp->b_rptr;
11027c478bd9Sstevel@tonic-gate ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) -
11037c478bd9Sstevel@tonic-gate ipsa->ipsa_mac_len);
11047c478bd9Sstevel@tonic-gate } else {
11057c478bd9Sstevel@tonic-gate ipha_t *ipha = (ipha_t *)data_mp->b_rptr;
11067c478bd9Sstevel@tonic-gate ipha->ipha_length = htons(ntohs(ipha->ipha_length) -
11077c478bd9Sstevel@tonic-gate ipsa->ipsa_mac_len);
11087c478bd9Sstevel@tonic-gate }
11097c478bd9Sstevel@tonic-gate
11107c478bd9Sstevel@tonic-gate /* submit the request to the crypto framework */
1111bd670b35SErik Nordmark return (esp_submit_req_inbound(data_mp, ira, ipsa,
11127c478bd9Sstevel@tonic-gate (uint8_t *)esph - data_mp->b_rptr));
11137c478bd9Sstevel@tonic-gate }
11147c478bd9Sstevel@tonic-gate
11155d3b8cb7SBill Sommerfeld /* XXX refactor me */
11167c478bd9Sstevel@tonic-gate /*
11177c478bd9Sstevel@tonic-gate * Handle the SADB_GETSPI message. Create a larval SA.
11187c478bd9Sstevel@tonic-gate */
11197c478bd9Sstevel@tonic-gate static void
esp_getspi(mblk_t * mp,keysock_in_t * ksi,ipsecesp_stack_t * espstack)1120f4b3ec61Sdh esp_getspi(mblk_t *mp, keysock_in_t *ksi, ipsecesp_stack_t *espstack)
11217c478bd9Sstevel@tonic-gate {
11227c478bd9Sstevel@tonic-gate ipsa_t *newbie, *target;
11237c478bd9Sstevel@tonic-gate isaf_t *outbound, *inbound;
11247c478bd9Sstevel@tonic-gate int rc, diagnostic;
11257c478bd9Sstevel@tonic-gate sadb_sa_t *assoc;
11267c478bd9Sstevel@tonic-gate keysock_out_t *kso;
11277c478bd9Sstevel@tonic-gate uint32_t newspi;
11287c478bd9Sstevel@tonic-gate
11297c478bd9Sstevel@tonic-gate /*
11307c478bd9Sstevel@tonic-gate * Randomly generate a proposed SPI value
11317c478bd9Sstevel@tonic-gate */
11329c2c14abSThejaswini Singarajipura if (cl_inet_getspi != NULL) {
11338e4b770fSLu Huafeng cl_inet_getspi(espstack->ipsecesp_netstack->netstack_stackid,
11348e4b770fSLu Huafeng IPPROTO_ESP, (uint8_t *)&newspi, sizeof (uint32_t), NULL);
11359c2c14abSThejaswini Singarajipura } else {
11369c2c14abSThejaswini Singarajipura (void) random_get_pseudo_bytes((uint8_t *)&newspi,
11379c2c14abSThejaswini Singarajipura sizeof (uint32_t));
11389c2c14abSThejaswini Singarajipura }
1139f4b3ec61Sdh newbie = sadb_getspi(ksi, newspi, &diagnostic,
11409c2c14abSThejaswini Singarajipura espstack->ipsecesp_netstack, IPPROTO_ESP);
11417c478bd9Sstevel@tonic-gate
11427c478bd9Sstevel@tonic-gate if (newbie == NULL) {
1143f4b3ec61Sdh sadb_pfkey_error(espstack->esp_pfkey_q, mp, ENOMEM, diagnostic,
11447c478bd9Sstevel@tonic-gate ksi->ks_in_serial);
11457c478bd9Sstevel@tonic-gate return;
11467c478bd9Sstevel@tonic-gate } else if (newbie == (ipsa_t *)-1) {
1147f4b3ec61Sdh sadb_pfkey_error(espstack->esp_pfkey_q, mp, EINVAL, diagnostic,
11487c478bd9Sstevel@tonic-gate ksi->ks_in_serial);
11497c478bd9Sstevel@tonic-gate return;
11507c478bd9Sstevel@tonic-gate }
11517c478bd9Sstevel@tonic-gate
11527c478bd9Sstevel@tonic-gate /*
11537c478bd9Sstevel@tonic-gate * XXX - We may randomly collide. We really should recover from this.
11547c478bd9Sstevel@tonic-gate * Unfortunately, that could require spending way-too-much-time
11557c478bd9Sstevel@tonic-gate * in here. For now, let the user retry.
11567c478bd9Sstevel@tonic-gate */
11577c478bd9Sstevel@tonic-gate
11587c478bd9Sstevel@tonic-gate if (newbie->ipsa_addrfam == AF_INET6) {
1159f4b3ec61Sdh outbound = OUTBOUND_BUCKET_V6(&espstack->esp_sadb.s_v6,
1160fb87b5d2Ssommerfe *(uint32_t *)(newbie->ipsa_dstaddr));
1161f4b3ec61Sdh inbound = INBOUND_BUCKET(&espstack->esp_sadb.s_v6,
1162f4b3ec61Sdh newbie->ipsa_spi);
11637c478bd9Sstevel@tonic-gate } else {
11647c478bd9Sstevel@tonic-gate ASSERT(newbie->ipsa_addrfam == AF_INET);
1165f4b3ec61Sdh outbound = OUTBOUND_BUCKET_V4(&espstack->esp_sadb.s_v4,
1166fb87b5d2Ssommerfe *(uint32_t *)(newbie->ipsa_dstaddr));
1167f4b3ec61Sdh inbound = INBOUND_BUCKET(&espstack->esp_sadb.s_v4,
1168f4b3ec61Sdh newbie->ipsa_spi);
11697c478bd9Sstevel@tonic-gate }
11707c478bd9Sstevel@tonic-gate
11717c478bd9Sstevel@tonic-gate mutex_enter(&outbound->isaf_lock);
11727c478bd9Sstevel@tonic-gate mutex_enter(&inbound->isaf_lock);
11737c478bd9Sstevel@tonic-gate
11747c478bd9Sstevel@tonic-gate /*
11757c478bd9Sstevel@tonic-gate * Check for collisions (i.e. did sadb_getspi() return with something
11767c478bd9Sstevel@tonic-gate * that already exists?).
11777c478bd9Sstevel@tonic-gate *
11787c478bd9Sstevel@tonic-gate * Try outbound first. Even though SADB_GETSPI is traditionally
11797c478bd9Sstevel@tonic-gate * for inbound SAs, you never know what a user might do.
11807c478bd9Sstevel@tonic-gate */
11817c478bd9Sstevel@tonic-gate target = ipsec_getassocbyspi(outbound, newbie->ipsa_spi,
11827c478bd9Sstevel@tonic-gate newbie->ipsa_srcaddr, newbie->ipsa_dstaddr, newbie->ipsa_addrfam);
11837c478bd9Sstevel@tonic-gate if (target == NULL) {
11847c478bd9Sstevel@tonic-gate target = ipsec_getassocbyspi(inbound, newbie->ipsa_spi,
11857c478bd9Sstevel@tonic-gate newbie->ipsa_srcaddr, newbie->ipsa_dstaddr,
11867c478bd9Sstevel@tonic-gate newbie->ipsa_addrfam);
11877c478bd9Sstevel@tonic-gate }
11887c478bd9Sstevel@tonic-gate
11897c478bd9Sstevel@tonic-gate /*
11907c478bd9Sstevel@tonic-gate * I don't have collisions elsewhere!
11917c478bd9Sstevel@tonic-gate * (Nor will I because I'm still holding inbound/outbound locks.)
11927c478bd9Sstevel@tonic-gate */
11937c478bd9Sstevel@tonic-gate
11947c478bd9Sstevel@tonic-gate if (target != NULL) {
11957c478bd9Sstevel@tonic-gate rc = EEXIST;
11967c478bd9Sstevel@tonic-gate IPSA_REFRELE(target);
11977c478bd9Sstevel@tonic-gate } else {
11987c478bd9Sstevel@tonic-gate /*
11997c478bd9Sstevel@tonic-gate * sadb_insertassoc() also checks for collisions, so
12007c478bd9Sstevel@tonic-gate * if there's a colliding entry, rc will be set
12017c478bd9Sstevel@tonic-gate * to EEXIST.
12027c478bd9Sstevel@tonic-gate */
12037c478bd9Sstevel@tonic-gate rc = sadb_insertassoc(newbie, inbound);
1204437220cdSdanmcd newbie->ipsa_hardexpiretime = gethrestime_sec();
1205f4b3ec61Sdh newbie->ipsa_hardexpiretime +=
1206f4b3ec61Sdh espstack->ipsecesp_larval_timeout;
12077c478bd9Sstevel@tonic-gate }
12087c478bd9Sstevel@tonic-gate
12097c478bd9Sstevel@tonic-gate /*
12107c478bd9Sstevel@tonic-gate * Can exit outbound mutex. Hold inbound until we're done
12117c478bd9Sstevel@tonic-gate * with newbie.
12127c478bd9Sstevel@tonic-gate */
12137c478bd9Sstevel@tonic-gate mutex_exit(&outbound->isaf_lock);
12147c478bd9Sstevel@tonic-gate
12157c478bd9Sstevel@tonic-gate if (rc != 0) {
12167c478bd9Sstevel@tonic-gate mutex_exit(&inbound->isaf_lock);
12177c478bd9Sstevel@tonic-gate IPSA_REFRELE(newbie);
1218f4b3ec61Sdh sadb_pfkey_error(espstack->esp_pfkey_q, mp, rc,
1219f4b3ec61Sdh SADB_X_DIAGNOSTIC_NONE, ksi->ks_in_serial);
12207c478bd9Sstevel@tonic-gate return;
12217c478bd9Sstevel@tonic-gate }
12227c478bd9Sstevel@tonic-gate
12237c478bd9Sstevel@tonic-gate
12247c478bd9Sstevel@tonic-gate /* Can write here because I'm still holding the bucket lock. */
12257c478bd9Sstevel@tonic-gate newbie->ipsa_type = SADB_SATYPE_ESP;
12267c478bd9Sstevel@tonic-gate
12277c478bd9Sstevel@tonic-gate /*
122838d95a78Smarkfen * Construct successful return message. We have one thing going
12297c478bd9Sstevel@tonic-gate * for us in PF_KEY v2. That's the fact that
12307c478bd9Sstevel@tonic-gate * sizeof (sadb_spirange_t) == sizeof (sadb_sa_t)
12317c478bd9Sstevel@tonic-gate */
12327c478bd9Sstevel@tonic-gate assoc = (sadb_sa_t *)ksi->ks_in_extv[SADB_EXT_SPIRANGE];
12337c478bd9Sstevel@tonic-gate assoc->sadb_sa_exttype = SADB_EXT_SA;
12347c478bd9Sstevel@tonic-gate assoc->sadb_sa_spi = newbie->ipsa_spi;
12357c478bd9Sstevel@tonic-gate *((uint64_t *)(&assoc->sadb_sa_replay)) = 0;
12367c478bd9Sstevel@tonic-gate mutex_exit(&inbound->isaf_lock);
12377c478bd9Sstevel@tonic-gate
12387c478bd9Sstevel@tonic-gate /* Convert KEYSOCK_IN to KEYSOCK_OUT. */
12397c478bd9Sstevel@tonic-gate kso = (keysock_out_t *)ksi;
12407c478bd9Sstevel@tonic-gate kso->ks_out_len = sizeof (*kso);
12417c478bd9Sstevel@tonic-gate kso->ks_out_serial = ksi->ks_in_serial;
12427c478bd9Sstevel@tonic-gate kso->ks_out_type = KEYSOCK_OUT;
12437c478bd9Sstevel@tonic-gate
12447c478bd9Sstevel@tonic-gate /*
12457c478bd9Sstevel@tonic-gate * Can safely putnext() to esp_pfkey_q, because this is a turnaround
12467c478bd9Sstevel@tonic-gate * from the esp_pfkey_q.
12477c478bd9Sstevel@tonic-gate */
1248f4b3ec61Sdh putnext(espstack->esp_pfkey_q, mp);
12497c478bd9Sstevel@tonic-gate }
12507c478bd9Sstevel@tonic-gate
12517c478bd9Sstevel@tonic-gate /*
12527c478bd9Sstevel@tonic-gate * Insert the ESP header into a packet. Duplicate an mblk, and insert a newly
12537c478bd9Sstevel@tonic-gate * allocated mblk with the ESP header in between the two.
12547c478bd9Sstevel@tonic-gate */
12557c478bd9Sstevel@tonic-gate static boolean_t
esp_insert_esp(mblk_t * mp,mblk_t * esp_mp,uint_t divpoint,ipsecesp_stack_t * espstack)1256f4b3ec61Sdh esp_insert_esp(mblk_t *mp, mblk_t *esp_mp, uint_t divpoint,
1257f4b3ec61Sdh ipsecesp_stack_t *espstack)
12587c478bd9Sstevel@tonic-gate {
12597c478bd9Sstevel@tonic-gate mblk_t *split_mp = mp;
12607c478bd9Sstevel@tonic-gate uint_t wheretodiv = divpoint;
12617c478bd9Sstevel@tonic-gate
12627c478bd9Sstevel@tonic-gate while ((split_mp->b_wptr - split_mp->b_rptr) < wheretodiv) {
12637c478bd9Sstevel@tonic-gate wheretodiv -= (split_mp->b_wptr - split_mp->b_rptr);
12647c478bd9Sstevel@tonic-gate split_mp = split_mp->b_cont;
12657c478bd9Sstevel@tonic-gate ASSERT(split_mp != NULL);
12667c478bd9Sstevel@tonic-gate }
12677c478bd9Sstevel@tonic-gate
12687c478bd9Sstevel@tonic-gate if (split_mp->b_wptr - split_mp->b_rptr != wheretodiv) {
12697c478bd9Sstevel@tonic-gate mblk_t *scratch;
12707c478bd9Sstevel@tonic-gate
12717c478bd9Sstevel@tonic-gate /* "scratch" is the 2nd half, split_mp is the first. */
12727c478bd9Sstevel@tonic-gate scratch = dupb(split_mp);
12737c478bd9Sstevel@tonic-gate if (scratch == NULL) {
1274f4b3ec61Sdh esp1dbg(espstack,
1275f4b3ec61Sdh ("esp_insert_esp: can't allocate scratch.\n"));
12767c478bd9Sstevel@tonic-gate return (B_FALSE);
12777c478bd9Sstevel@tonic-gate }
12787c478bd9Sstevel@tonic-gate /* NOTE: dupb() doesn't set b_cont appropriately. */
12797c478bd9Sstevel@tonic-gate scratch->b_cont = split_mp->b_cont;
12807c478bd9Sstevel@tonic-gate scratch->b_rptr += wheretodiv;
12817c478bd9Sstevel@tonic-gate split_mp->b_wptr = split_mp->b_rptr + wheretodiv;
12827c478bd9Sstevel@tonic-gate split_mp->b_cont = scratch;
12837c478bd9Sstevel@tonic-gate }
12847c478bd9Sstevel@tonic-gate /*
12857c478bd9Sstevel@tonic-gate * At this point, split_mp is exactly "wheretodiv" bytes long, and
12867c478bd9Sstevel@tonic-gate * holds the end of the pre-ESP part of the datagram.
12877c478bd9Sstevel@tonic-gate */
12887c478bd9Sstevel@tonic-gate esp_mp->b_cont = split_mp->b_cont;
12897c478bd9Sstevel@tonic-gate split_mp->b_cont = esp_mp;
12907c478bd9Sstevel@tonic-gate
12917c478bd9Sstevel@tonic-gate return (B_TRUE);
12927c478bd9Sstevel@tonic-gate }
12937c478bd9Sstevel@tonic-gate
12944a179720Sdanmcd /*
12954a179720Sdanmcd * Section 7 of RFC 3947 says:
12964a179720Sdanmcd *
12974a179720Sdanmcd * 7. Recovering from the Expiring NAT Mappings
12984a179720Sdanmcd *
12994a179720Sdanmcd * There are cases where NAT box decides to remove mappings that are still
13004a179720Sdanmcd * alive (for example, when the keepalive interval is too long, or when the
13014a179720Sdanmcd * NAT box is rebooted). To recover from this, ends that are NOT behind
13024a179720Sdanmcd * NAT SHOULD use the last valid UDP encapsulated IKE or IPsec packet from
13034a179720Sdanmcd * the other end to determine which IP and port addresses should be used.
13044a179720Sdanmcd * The host behind dynamic NAT MUST NOT do this, as otherwise it opens a
13054a179720Sdanmcd * DoS attack possibility because the IP address or port of the other host
13064a179720Sdanmcd * will not change (it is not behind NAT).
13074a179720Sdanmcd *
13084a179720Sdanmcd * Keepalives cannot be used for these purposes, as they are not
13094a179720Sdanmcd * authenticated, but any IKE authenticated IKE packet or ESP packet can be
13104a179720Sdanmcd * used to detect whether the IP address or the port has changed.
13114a179720Sdanmcd *
13124a179720Sdanmcd * The following function will check an SA and its explicitly-set pair to see
13134a179720Sdanmcd * if the NAT-T remote port matches the received packet (which must have
13144a179720Sdanmcd * passed ESP authentication, see esp_in_done() for the caller context). If
13154a179720Sdanmcd * there is a mismatch, the SAs are updated. It is not important if we race
13164a179720Sdanmcd * with a transmitting thread, as if there is a transmitting thread, it will
13174a179720Sdanmcd * merely emit a packet that will most-likely be dropped.
13184a179720Sdanmcd *
13194a179720Sdanmcd * "ports" are ordered src,dst, and assoc is an inbound SA, where src should
13204a179720Sdanmcd * match ipsa_remote_nat_port and dst should match ipsa_local_nat_port.
13214a179720Sdanmcd */
13224a179720Sdanmcd #ifdef _LITTLE_ENDIAN
13234a179720Sdanmcd #define FIRST_16(x) ((x) & 0xFFFF)
13244a179720Sdanmcd #define NEXT_16(x) (((x) >> 16) & 0xFFFF)
13254a179720Sdanmcd #else
13264a179720Sdanmcd #define FIRST_16(x) (((x) >> 16) & 0xFFFF)
13274a179720Sdanmcd #define NEXT_16(x) ((x) & 0xFFFF)
13284a179720Sdanmcd #endif
13294a179720Sdanmcd static void
esp_port_freshness(uint32_t ports,ipsa_t * assoc)13304a179720Sdanmcd esp_port_freshness(uint32_t ports, ipsa_t *assoc)
13314a179720Sdanmcd {
13324a179720Sdanmcd uint16_t remote = FIRST_16(ports);
13334a179720Sdanmcd uint16_t local = NEXT_16(ports);
13344a179720Sdanmcd ipsa_t *outbound_peer;
13354a179720Sdanmcd isaf_t *bucket;
13364a179720Sdanmcd ipsecesp_stack_t *espstack = assoc->ipsa_netstack->netstack_ipsecesp;
13374a179720Sdanmcd
13384a179720Sdanmcd /* We found a conn_t, therefore local != 0. */
13394a179720Sdanmcd ASSERT(local != 0);
13404a179720Sdanmcd /* Assume an IPv4 SA. */
13414a179720Sdanmcd ASSERT(assoc->ipsa_addrfam == AF_INET);
13424a179720Sdanmcd
13434a179720Sdanmcd /*
13444a179720Sdanmcd * On-the-wire rport == 0 means something's very wrong.
13454a179720Sdanmcd * An unpaired SA is also useless to us.
13464a179720Sdanmcd * If we are behind the NAT, don't bother.
13474a179720Sdanmcd * A zero local NAT port defaults to 4500, so check that too.
13484a179720Sdanmcd * And, of course, if the ports already match, we don't need to
13494a179720Sdanmcd * bother.
13504a179720Sdanmcd */
13514a179720Sdanmcd if (remote == 0 || assoc->ipsa_otherspi == 0 ||
13524a179720Sdanmcd (assoc->ipsa_flags & IPSA_F_BEHIND_NAT) ||
13534a179720Sdanmcd (assoc->ipsa_remote_nat_port == 0 &&
13544a179720Sdanmcd remote == htons(IPPORT_IKE_NATT)) ||
13554a179720Sdanmcd remote == assoc->ipsa_remote_nat_port)
13564a179720Sdanmcd return;
13574a179720Sdanmcd
13584a179720Sdanmcd /* Try and snag the peer. NOTE: Assume IPv4 for now. */
13594a179720Sdanmcd bucket = OUTBOUND_BUCKET_V4(&(espstack->esp_sadb.s_v4),
13604a179720Sdanmcd assoc->ipsa_srcaddr[0]);
13614a179720Sdanmcd mutex_enter(&bucket->isaf_lock);
13624a179720Sdanmcd outbound_peer = ipsec_getassocbyspi(bucket, assoc->ipsa_otherspi,
13634a179720Sdanmcd assoc->ipsa_dstaddr, assoc->ipsa_srcaddr, AF_INET);
13644a179720Sdanmcd mutex_exit(&bucket->isaf_lock);
13654a179720Sdanmcd
13664a179720Sdanmcd /* We probably lost a race to a deleting or expiring thread. */
13674a179720Sdanmcd if (outbound_peer == NULL)
13684a179720Sdanmcd return;
13694a179720Sdanmcd
13704a179720Sdanmcd /*
13714a179720Sdanmcd * Hold the mutexes for both SAs so we don't race another inbound
13724a179720Sdanmcd * thread. A lock-entry order shouldn't matter, since all other
13734a179720Sdanmcd * per-ipsa locks are individually held-then-released.
13744a179720Sdanmcd *
13754a179720Sdanmcd * Luckily, this has nothing to do with the remote-NAT address,
13764a179720Sdanmcd * so we don't have to re-scribble the cached-checksum differential.
13774a179720Sdanmcd */
13784a179720Sdanmcd mutex_enter(&outbound_peer->ipsa_lock);
13794a179720Sdanmcd mutex_enter(&assoc->ipsa_lock);
13804a179720Sdanmcd outbound_peer->ipsa_remote_nat_port = assoc->ipsa_remote_nat_port =
13814a179720Sdanmcd remote;
13824a179720Sdanmcd mutex_exit(&assoc->ipsa_lock);
13834a179720Sdanmcd mutex_exit(&outbound_peer->ipsa_lock);
13844a179720Sdanmcd IPSA_REFRELE(outbound_peer);
13854a179720Sdanmcd ESP_BUMP_STAT(espstack, sa_port_renumbers);
13864a179720Sdanmcd }
13877c478bd9Sstevel@tonic-gate /*
13887c478bd9Sstevel@tonic-gate * Finish processing of an inbound ESP packet after processing by the
13897c478bd9Sstevel@tonic-gate * crypto framework.
13907c478bd9Sstevel@tonic-gate * - Remove the ESP header.
13917c478bd9Sstevel@tonic-gate * - Send packet back to IP.
13927c478bd9Sstevel@tonic-gate * If authentication was performed on the packet, this function is called
13937c478bd9Sstevel@tonic-gate * only if the authentication succeeded.
13947c478bd9Sstevel@tonic-gate * On success returns B_TRUE, on failure returns B_FALSE and frees the
1395bd670b35SErik Nordmark * mblk chain data_mp.
13967c478bd9Sstevel@tonic-gate */
1397bd670b35SErik Nordmark static mblk_t *
esp_in_done(mblk_t * data_mp,ip_recv_attr_t * ira,ipsec_crypto_t * ic)1398bd670b35SErik Nordmark esp_in_done(mblk_t *data_mp, ip_recv_attr_t *ira, ipsec_crypto_t *ic)
13997c478bd9Sstevel@tonic-gate {
14007c478bd9Sstevel@tonic-gate ipsa_t *assoc;
14017c478bd9Sstevel@tonic-gate uint_t espstart;
14027c478bd9Sstevel@tonic-gate uint32_t ivlen = 0;
14037c478bd9Sstevel@tonic-gate uint_t processed_len;
14047c478bd9Sstevel@tonic-gate esph_t *esph;
14057c478bd9Sstevel@tonic-gate kstat_named_t *counter;
14067c478bd9Sstevel@tonic-gate boolean_t is_natt;
1407bd670b35SErik Nordmark netstack_t *ns = ira->ira_ill->ill_ipst->ips_netstack;
1408f4b3ec61Sdh ipsecesp_stack_t *espstack = ns->netstack_ipsecesp;
1409f4b3ec61Sdh ipsec_stack_t *ipss = ns->netstack_ipsec;
14107c478bd9Sstevel@tonic-gate
1411bd670b35SErik Nordmark assoc = ira->ira_ipsec_esp_sa;
14127c478bd9Sstevel@tonic-gate ASSERT(assoc != NULL);
14137c478bd9Sstevel@tonic-gate
14147c478bd9Sstevel@tonic-gate is_natt = ((assoc->ipsa_flags & IPSA_F_NATT) != 0);
14157c478bd9Sstevel@tonic-gate
14167c478bd9Sstevel@tonic-gate /* get the pointer to the ESP header */
14177c478bd9Sstevel@tonic-gate if (assoc->ipsa_encr_alg == SADB_EALG_NULL) {
14187c478bd9Sstevel@tonic-gate /* authentication-only ESP */
1419bd670b35SErik Nordmark espstart = ic->ic_crypto_data.cd_offset;
1420bd670b35SErik Nordmark processed_len = ic->ic_crypto_data.cd_length;
14217c478bd9Sstevel@tonic-gate } else {
14227c478bd9Sstevel@tonic-gate /* encryption present */
14237c478bd9Sstevel@tonic-gate ivlen = assoc->ipsa_iv_len;
14247c478bd9Sstevel@tonic-gate if (assoc->ipsa_auth_alg == SADB_AALG_NONE) {
14257c478bd9Sstevel@tonic-gate /* encryption-only ESP */
1426bd670b35SErik Nordmark espstart = ic->ic_crypto_data.cd_offset -
1427437220cdSdanmcd sizeof (esph_t) - assoc->ipsa_iv_len;
1428bd670b35SErik Nordmark processed_len = ic->ic_crypto_data.cd_length +
1429437220cdSdanmcd ivlen;
14307c478bd9Sstevel@tonic-gate } else {
14317c478bd9Sstevel@tonic-gate /* encryption with authentication */
1432bd670b35SErik Nordmark espstart = ic->ic_crypto_dual_data.dd_offset1;
1433bd670b35SErik Nordmark processed_len = ic->ic_crypto_dual_data.dd_len2 +
14347c478bd9Sstevel@tonic-gate ivlen;
14357c478bd9Sstevel@tonic-gate }
14367c478bd9Sstevel@tonic-gate }
14377c478bd9Sstevel@tonic-gate
14387c478bd9Sstevel@tonic-gate esph = (esph_t *)(data_mp->b_rptr + espstart);
14397c478bd9Sstevel@tonic-gate
1440628b0c67SMark Fenwick if (assoc->ipsa_auth_alg != IPSA_AALG_NONE ||
1441628b0c67SMark Fenwick (assoc->ipsa_flags & IPSA_F_COMBINED)) {
1442628b0c67SMark Fenwick /*
1443628b0c67SMark Fenwick * Authentication passed if we reach this point.
1444628b0c67SMark Fenwick * Packets with authentication will have the ICV
1445628b0c67SMark Fenwick * after the crypto data. Adjust b_wptr before
1446628b0c67SMark Fenwick * making padlen checks.
1447628b0c67SMark Fenwick */
1448f4b3ec61Sdh ESP_BUMP_STAT(espstack, good_auth);
14497c478bd9Sstevel@tonic-gate data_mp->b_wptr -= assoc->ipsa_mac_len;
14507c478bd9Sstevel@tonic-gate
14517c478bd9Sstevel@tonic-gate /*
14527c478bd9Sstevel@tonic-gate * Check replay window here!
14537c478bd9Sstevel@tonic-gate * For right now, assume keysock will set the replay window
14547c478bd9Sstevel@tonic-gate * size to zero for SAs that have an unspecified sender.
14557c478bd9Sstevel@tonic-gate * This may change...
14567c478bd9Sstevel@tonic-gate */
14577c478bd9Sstevel@tonic-gate
14587c478bd9Sstevel@tonic-gate if (!sadb_replay_check(assoc, esph->esph_replay)) {
14597c478bd9Sstevel@tonic-gate /*
14607c478bd9Sstevel@tonic-gate * Log the event. As of now we print out an event.
14617c478bd9Sstevel@tonic-gate * Do not print the replay failure number, or else
14627c478bd9Sstevel@tonic-gate * syslog cannot collate the error messages. Printing
14637c478bd9Sstevel@tonic-gate * the replay number that failed opens a denial-of-
14647c478bd9Sstevel@tonic-gate * service attack.
14657c478bd9Sstevel@tonic-gate */
14667c478bd9Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0,
14677c478bd9Sstevel@tonic-gate SL_ERROR | SL_WARN,
14687c478bd9Sstevel@tonic-gate "Replay failed for ESP spi 0x%x, dst %s.\n",
14697c478bd9Sstevel@tonic-gate assoc->ipsa_spi, assoc->ipsa_dstaddr,
1470f4b3ec61Sdh assoc->ipsa_addrfam, espstack->ipsecesp_netstack);
1471f4b3ec61Sdh ESP_BUMP_STAT(espstack, replay_failures);
1472f4b3ec61Sdh counter = DROPPER(ipss, ipds_esp_replay);
14737c478bd9Sstevel@tonic-gate goto drop_and_bail;
14747c478bd9Sstevel@tonic-gate }
14754a179720Sdanmcd
1476bd670b35SErik Nordmark if (is_natt) {
1477bd670b35SErik Nordmark ASSERT(ira->ira_flags & IRAF_ESP_UDP_PORTS);
1478bd670b35SErik Nordmark ASSERT(ira->ira_esp_udp_ports != 0);
1479bd670b35SErik Nordmark esp_port_freshness(ira->ira_esp_udp_ports, assoc);
1480bd670b35SErik Nordmark }
14817c478bd9Sstevel@tonic-gate }
14827c478bd9Sstevel@tonic-gate
1483437220cdSdanmcd esp_set_usetime(assoc, B_TRUE);
1484437220cdSdanmcd
14857c478bd9Sstevel@tonic-gate if (!esp_age_bytes(assoc, processed_len, B_TRUE)) {
14867c478bd9Sstevel@tonic-gate /* The ipsa has hit hard expiration, LOG and AUDIT. */
14877c478bd9Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0,
14887c478bd9Sstevel@tonic-gate SL_ERROR | SL_WARN,
14897c478bd9Sstevel@tonic-gate "ESP association 0x%x, dst %s had bytes expire.\n",
1490f4b3ec61Sdh assoc->ipsa_spi, assoc->ipsa_dstaddr, assoc->ipsa_addrfam,
1491f4b3ec61Sdh espstack->ipsecesp_netstack);
1492f4b3ec61Sdh ESP_BUMP_STAT(espstack, bytes_expired);
1493f4b3ec61Sdh counter = DROPPER(ipss, ipds_esp_bytes_expire);
14947c478bd9Sstevel@tonic-gate goto drop_and_bail;
14957c478bd9Sstevel@tonic-gate }
14967c478bd9Sstevel@tonic-gate
14977c478bd9Sstevel@tonic-gate /*
14987c478bd9Sstevel@tonic-gate * Remove ESP header and padding from packet. I hope the compiler
14997c478bd9Sstevel@tonic-gate * spews "branch, predict taken" code for this.
15007c478bd9Sstevel@tonic-gate */
15017c478bd9Sstevel@tonic-gate
1502bd670b35SErik Nordmark if (esp_strip_header(data_mp, (ira->ira_flags & IRAF_IS_IPV4),
1503bd670b35SErik Nordmark ivlen, &counter, espstack)) {
15045d3b8cb7SBill Sommerfeld
1505bd670b35SErik Nordmark if (is_system_labeled() && assoc->ipsa_tsl != NULL) {
1506bd670b35SErik Nordmark if (!ip_recv_attr_replace_label(ira, assoc->ipsa_tsl)) {
1507bd670b35SErik Nordmark ip_drop_packet(data_mp, B_TRUE, ira->ira_ill,
1508bd670b35SErik Nordmark DROPPER(ipss, ipds_ah_nomem),
1509bd670b35SErik Nordmark &espstack->esp_dropper);
1510bd670b35SErik Nordmark BUMP_MIB(ira->ira_ill->ill_ip_mib,
1511bd670b35SErik Nordmark ipIfStatsInDiscards);
1512bd670b35SErik Nordmark return (NULL);
15135d3b8cb7SBill Sommerfeld }
15145d3b8cb7SBill Sommerfeld }
15157c478bd9Sstevel@tonic-gate if (is_natt)
15167c478bd9Sstevel@tonic-gate return (esp_fix_natt_checksums(data_mp, assoc));
1517d74f5ecaSDan McDonald
1518d74f5ecaSDan McDonald if (assoc->ipsa_state == IPSA_STATE_IDLE) {
1519d74f5ecaSDan McDonald /*
1520d74f5ecaSDan McDonald * Cluster buffering case. Tell caller that we're
1521d74f5ecaSDan McDonald * handling the packet.
1522d74f5ecaSDan McDonald */
1523bd670b35SErik Nordmark sadb_buf_pkt(assoc, data_mp, ira);
1524bd670b35SErik Nordmark return (NULL);
1525d74f5ecaSDan McDonald }
1526d74f5ecaSDan McDonald
1527bd670b35SErik Nordmark return (data_mp);
15287c478bd9Sstevel@tonic-gate }
15297c478bd9Sstevel@tonic-gate
1530f4b3ec61Sdh esp1dbg(espstack, ("esp_in_done: esp_strip_header() failed\n"));
15317c478bd9Sstevel@tonic-gate drop_and_bail:
1532f4b3ec61Sdh IP_ESP_BUMP_STAT(ipss, in_discards);
1533bd670b35SErik Nordmark ip_drop_packet(data_mp, B_TRUE, ira->ira_ill, counter,
1534f4b3ec61Sdh &espstack->esp_dropper);
1535bd670b35SErik Nordmark BUMP_MIB(ira->ira_ill->ill_ip_mib, ipIfStatsInDiscards);
1536bd670b35SErik Nordmark return (NULL);
15377c478bd9Sstevel@tonic-gate }
15387c478bd9Sstevel@tonic-gate
15397c478bd9Sstevel@tonic-gate /*
15407c478bd9Sstevel@tonic-gate * Called upon failing the inbound ICV check. The message passed as
15417c478bd9Sstevel@tonic-gate * argument is freed.
15427c478bd9Sstevel@tonic-gate */
15437c478bd9Sstevel@tonic-gate static void
esp_log_bad_auth(mblk_t * mp,ip_recv_attr_t * ira)1544bd670b35SErik Nordmark esp_log_bad_auth(mblk_t *mp, ip_recv_attr_t *ira)
15457c478bd9Sstevel@tonic-gate {
1546bd670b35SErik Nordmark ipsa_t *assoc = ira->ira_ipsec_esp_sa;
1547bd670b35SErik Nordmark netstack_t *ns = ira->ira_ill->ill_ipst->ips_netstack;
1548f4b3ec61Sdh ipsecesp_stack_t *espstack = ns->netstack_ipsecesp;
1549f4b3ec61Sdh ipsec_stack_t *ipss = ns->netstack_ipsec;
15507c478bd9Sstevel@tonic-gate
15517c478bd9Sstevel@tonic-gate /*
15527c478bd9Sstevel@tonic-gate * Log the event. Don't print to the console, block
15537c478bd9Sstevel@tonic-gate * potential denial-of-service attack.
15547c478bd9Sstevel@tonic-gate */
1555f4b3ec61Sdh ESP_BUMP_STAT(espstack, bad_auth);
15567c478bd9Sstevel@tonic-gate
15577c478bd9Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0, SL_ERROR | SL_WARN,
15587c478bd9Sstevel@tonic-gate "ESP Authentication failed for spi 0x%x, dst %s.\n",
1559f4b3ec61Sdh assoc->ipsa_spi, assoc->ipsa_dstaddr, assoc->ipsa_addrfam,
1560f4b3ec61Sdh espstack->ipsecesp_netstack);
15617c478bd9Sstevel@tonic-gate
1562f4b3ec61Sdh IP_ESP_BUMP_STAT(ipss, in_discards);
1563bd670b35SErik Nordmark ip_drop_packet(mp, B_TRUE, ira->ira_ill,
1564f4b3ec61Sdh DROPPER(ipss, ipds_esp_bad_auth),
1565f4b3ec61Sdh &espstack->esp_dropper);
15667c478bd9Sstevel@tonic-gate }
15677c478bd9Sstevel@tonic-gate
15687c478bd9Sstevel@tonic-gate
15697c478bd9Sstevel@tonic-gate /*
15707c478bd9Sstevel@tonic-gate * Invoked for outbound packets after ESP processing. If the packet
15717c478bd9Sstevel@tonic-gate * also requires AH, performs the AH SA selection and AH processing.
1572bd670b35SErik Nordmark *
1573b7daf799SDan McDonald * Returns data_mp (possibly with AH added) unless data_mp was consumed
1574b7daf799SDan McDonald * due to an error, or queued due to async. crypto or an ACQUIRE trigger.
15757c478bd9Sstevel@tonic-gate */
1576bd670b35SErik Nordmark static mblk_t *
esp_do_outbound_ah(mblk_t * data_mp,ip_xmit_attr_t * ixa)1577bd670b35SErik Nordmark esp_do_outbound_ah(mblk_t *data_mp, ip_xmit_attr_t *ixa)
15787c478bd9Sstevel@tonic-gate {
15797c478bd9Sstevel@tonic-gate ipsec_action_t *ap;
15807c478bd9Sstevel@tonic-gate
1581bd670b35SErik Nordmark ap = ixa->ixa_ipsec_action;
15827c478bd9Sstevel@tonic-gate if (ap == NULL) {
1583bd670b35SErik Nordmark ipsec_policy_t *pp = ixa->ixa_ipsec_policy;
15847c478bd9Sstevel@tonic-gate ap = pp->ipsp_act;
15857c478bd9Sstevel@tonic-gate }
15867c478bd9Sstevel@tonic-gate
15877c478bd9Sstevel@tonic-gate if (!ap->ipa_want_ah)
1588bd670b35SErik Nordmark return (data_mp);
15897c478bd9Sstevel@tonic-gate
1590bd670b35SErik Nordmark /*
1591bd670b35SErik Nordmark * Normally the AH SA would have already been put in place
1592bd670b35SErik Nordmark * but it could have been flushed so we need to look for it.
1593bd670b35SErik Nordmark */
1594bd670b35SErik Nordmark if (ixa->ixa_ipsec_ah_sa == NULL) {
1595bd670b35SErik Nordmark if (!ipsec_outbound_sa(data_mp, ixa, IPPROTO_AH)) {
1596bd670b35SErik Nordmark sadb_acquire(data_mp, ixa, B_TRUE, B_FALSE);
1597bd670b35SErik Nordmark return (NULL);
15987c478bd9Sstevel@tonic-gate }
15997c478bd9Sstevel@tonic-gate }
1600bd670b35SErik Nordmark ASSERT(ixa->ixa_ipsec_ah_sa != NULL);
16017c478bd9Sstevel@tonic-gate
1602bd670b35SErik Nordmark data_mp = ixa->ixa_ipsec_ah_sa->ipsa_output_func(data_mp, ixa);
1603bd670b35SErik Nordmark return (data_mp);
16047c478bd9Sstevel@tonic-gate }
16057c478bd9Sstevel@tonic-gate
16067c478bd9Sstevel@tonic-gate
16077c478bd9Sstevel@tonic-gate /*
16087c478bd9Sstevel@tonic-gate * Kernel crypto framework callback invoked after completion of async
1609bd670b35SErik Nordmark * crypto requests for outbound packets.
16107c478bd9Sstevel@tonic-gate */
16117c478bd9Sstevel@tonic-gate static void
esp_kcf_callback_outbound(void * arg,int status)1612bd670b35SErik Nordmark esp_kcf_callback_outbound(void *arg, int status)
16137c478bd9Sstevel@tonic-gate {
1614bd670b35SErik Nordmark mblk_t *mp = (mblk_t *)arg;
1615bd670b35SErik Nordmark mblk_t *async_mp;
1616bd670b35SErik Nordmark netstack_t *ns;
1617f4b3ec61Sdh ipsec_stack_t *ipss;
1618bd670b35SErik Nordmark ipsecesp_stack_t *espstack;
1619bd670b35SErik Nordmark mblk_t *data_mp;
1620bd670b35SErik Nordmark ip_xmit_attr_t ixas;
1621bd670b35SErik Nordmark ipsec_crypto_t *ic;
1622bd670b35SErik Nordmark ill_t *ill;
16237c478bd9Sstevel@tonic-gate
1624bd670b35SErik Nordmark /*
1625bd670b35SErik Nordmark * First remove the ipsec_crypto_t mblk
1626bd670b35SErik Nordmark * Note that we need to ipsec_free_crypto_data(mp) once done with ic.
1627bd670b35SErik Nordmark */
1628bd670b35SErik Nordmark async_mp = ipsec_remove_crypto_data(mp, &ic);
1629bd670b35SErik Nordmark ASSERT(async_mp != NULL);
16307c478bd9Sstevel@tonic-gate
1631bd670b35SErik Nordmark /*
1632bd670b35SErik Nordmark * Extract the ip_xmit_attr_t from the first mblk.
1633bd670b35SErik Nordmark * Verifies that the netstack and ill is still around; could
1634bd670b35SErik Nordmark * have vanished while kEf was doing its work.
1635bd670b35SErik Nordmark * On succesful return we have a nce_t and the ill/ipst can't
1636bd670b35SErik Nordmark * disappear until we do the nce_refrele in ixa_cleanup.
1637bd670b35SErik Nordmark */
1638bd670b35SErik Nordmark data_mp = async_mp->b_cont;
1639bd670b35SErik Nordmark async_mp->b_cont = NULL;
1640bd670b35SErik Nordmark if (!ip_xmit_attr_from_mblk(async_mp, &ixas)) {
1641bd670b35SErik Nordmark /* Disappeared on us - no ill/ipst for MIB */
1642bd670b35SErik Nordmark /* We have nowhere to do stats since ixa_ipst could be NULL */
1643bd670b35SErik Nordmark if (ixas.ixa_nce != NULL) {
1644bd670b35SErik Nordmark ill = ixas.ixa_nce->nce_ill;
1645bd670b35SErik Nordmark BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
1646bd670b35SErik Nordmark ip_drop_output("ipIfStatsOutDiscards", data_mp, ill);
1647bd670b35SErik Nordmark }
1648bd670b35SErik Nordmark freemsg(data_mp);
1649bd670b35SErik Nordmark goto done;
1650bd670b35SErik Nordmark }
1651bd670b35SErik Nordmark ns = ixas.ixa_ipst->ips_netstack;
1652bd670b35SErik Nordmark espstack = ns->netstack_ipsecesp;
1653bd670b35SErik Nordmark ipss = ns->netstack_ipsec;
1654bd670b35SErik Nordmark ill = ixas.ixa_nce->nce_ill;
1655bd670b35SErik Nordmark
1656bd670b35SErik Nordmark if (status == CRYPTO_SUCCESS) {
1657bd670b35SErik Nordmark /*
1658bd670b35SErik Nordmark * If a ICV was computed, it was stored by the
1659bd670b35SErik Nordmark * crypto framework at the end of the packet.
1660bd670b35SErik Nordmark */
1661bd670b35SErik Nordmark ipha_t *ipha = (ipha_t *)data_mp->b_rptr;
1662bd670b35SErik Nordmark
1663bd670b35SErik Nordmark esp_set_usetime(ixas.ixa_ipsec_esp_sa, B_FALSE);
1664bd670b35SErik Nordmark /* NAT-T packet. */
1665bd670b35SErik Nordmark if (IPH_HDR_VERSION(ipha) == IP_VERSION &&
1666bd670b35SErik Nordmark ipha->ipha_protocol == IPPROTO_UDP)
1667bd670b35SErik Nordmark esp_prepare_udp(ns, data_mp, ipha);
1668bd670b35SErik Nordmark
1669bd670b35SErik Nordmark /* do AH processing if needed */
1670bd670b35SErik Nordmark data_mp = esp_do_outbound_ah(data_mp, &ixas);
1671bd670b35SErik Nordmark if (data_mp == NULL)
1672bd670b35SErik Nordmark goto done;
1673bd670b35SErik Nordmark
1674bd670b35SErik Nordmark (void) ip_output_post_ipsec(data_mp, &ixas);
1675f4b3ec61Sdh } else {
1676bd670b35SErik Nordmark /* Outbound shouldn't see invalid MAC */
1677bd670b35SErik Nordmark ASSERT(status != CRYPTO_INVALID_MAC);
1678bd670b35SErik Nordmark
1679bd670b35SErik Nordmark esp1dbg(espstack,
1680bd670b35SErik Nordmark ("esp_kcf_callback_outbound: crypto failed with 0x%x\n",
1681bd670b35SErik Nordmark status));
1682bd670b35SErik Nordmark ESP_BUMP_STAT(espstack, crypto_failures);
1683bd670b35SErik Nordmark ESP_BUMP_STAT(espstack, out_discards);
1684bd670b35SErik Nordmark ip_drop_packet(data_mp, B_FALSE, ill,
1685bd670b35SErik Nordmark DROPPER(ipss, ipds_esp_crypto_failed),
1686bd670b35SErik Nordmark &espstack->esp_dropper);
1687bd670b35SErik Nordmark BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
1688f4b3ec61Sdh }
1689bd670b35SErik Nordmark done:
1690bd670b35SErik Nordmark ixa_cleanup(&ixas);
1691bd670b35SErik Nordmark (void) ipsec_free_crypto_data(mp);
1692bd670b35SErik Nordmark }
1693bd670b35SErik Nordmark
1694bd670b35SErik Nordmark /*
1695bd670b35SErik Nordmark * Kernel crypto framework callback invoked after completion of async
1696bd670b35SErik Nordmark * crypto requests for inbound packets.
1697bd670b35SErik Nordmark */
1698bd670b35SErik Nordmark static void
esp_kcf_callback_inbound(void * arg,int status)1699bd670b35SErik Nordmark esp_kcf_callback_inbound(void *arg, int status)
1700bd670b35SErik Nordmark {
1701bd670b35SErik Nordmark mblk_t *mp = (mblk_t *)arg;
1702bd670b35SErik Nordmark mblk_t *async_mp;
1703bd670b35SErik Nordmark netstack_t *ns;
1704bd670b35SErik Nordmark ipsecesp_stack_t *espstack;
1705bd670b35SErik Nordmark ipsec_stack_t *ipss;
1706bd670b35SErik Nordmark mblk_t *data_mp;
1707bd670b35SErik Nordmark ip_recv_attr_t iras;
1708bd670b35SErik Nordmark ipsec_crypto_t *ic;
1709f4b3ec61Sdh
1710f4b3ec61Sdh /*
1711bd670b35SErik Nordmark * First remove the ipsec_crypto_t mblk
1712bd670b35SErik Nordmark * Note that we need to ipsec_free_crypto_data(mp) once done with ic.
1713f4b3ec61Sdh */
1714bd670b35SErik Nordmark async_mp = ipsec_remove_crypto_data(mp, &ic);
1715bd670b35SErik Nordmark ASSERT(async_mp != NULL);
1716bd670b35SErik Nordmark
1717bd670b35SErik Nordmark /*
1718bd670b35SErik Nordmark * Extract the ip_recv_attr_t from the first mblk.
1719bd670b35SErik Nordmark * Verifies that the netstack and ill is still around; could
1720bd670b35SErik Nordmark * have vanished while kEf was doing its work.
1721bd670b35SErik Nordmark */
1722bd670b35SErik Nordmark data_mp = async_mp->b_cont;
1723bd670b35SErik Nordmark async_mp->b_cont = NULL;
1724bd670b35SErik Nordmark if (!ip_recv_attr_from_mblk(async_mp, &iras)) {
1725bd670b35SErik Nordmark /* The ill or ip_stack_t disappeared on us */
1726bd670b35SErik Nordmark ip_drop_input("ip_recv_attr_from_mblk", data_mp, NULL);
1727bd670b35SErik Nordmark freemsg(data_mp);
1728bd670b35SErik Nordmark goto done;
1729f4b3ec61Sdh }
1730f4b3ec61Sdh
1731bd670b35SErik Nordmark ns = iras.ira_ill->ill_ipst->ips_netstack;
1732f4b3ec61Sdh espstack = ns->netstack_ipsecesp;
1733f4b3ec61Sdh ipss = ns->netstack_ipsec;
1734f4b3ec61Sdh
17357c478bd9Sstevel@tonic-gate if (status == CRYPTO_SUCCESS) {
1736bd670b35SErik Nordmark data_mp = esp_in_done(data_mp, &iras, ic);
1737bd670b35SErik Nordmark if (data_mp == NULL)
1738bd670b35SErik Nordmark goto done;
17397c478bd9Sstevel@tonic-gate
1740bd670b35SErik Nordmark /* finish IPsec processing */
1741bd670b35SErik Nordmark ip_input_post_ipsec(data_mp, &iras);
17427c478bd9Sstevel@tonic-gate } else if (status == CRYPTO_INVALID_MAC) {
1743bd670b35SErik Nordmark esp_log_bad_auth(data_mp, &iras);
17447c478bd9Sstevel@tonic-gate } else {
1745f4b3ec61Sdh esp1dbg(espstack,
1746f4b3ec61Sdh ("esp_kcf_callback: crypto failed with 0x%x\n",
17477c478bd9Sstevel@tonic-gate status));
1748f4b3ec61Sdh ESP_BUMP_STAT(espstack, crypto_failures);
1749bd670b35SErik Nordmark IP_ESP_BUMP_STAT(ipss, in_discards);
1750bd670b35SErik Nordmark ip_drop_packet(data_mp, B_TRUE, iras.ira_ill,
1751f4b3ec61Sdh DROPPER(ipss, ipds_esp_crypto_failed),
1752f4b3ec61Sdh &espstack->esp_dropper);
1753bd670b35SErik Nordmark BUMP_MIB(iras.ira_ill->ill_ip_mib, ipIfStatsInDiscards);
17547c478bd9Sstevel@tonic-gate }
1755bd670b35SErik Nordmark done:
1756bd670b35SErik Nordmark ira_cleanup(&iras, B_TRUE);
1757bd670b35SErik Nordmark (void) ipsec_free_crypto_data(mp);
17587c478bd9Sstevel@tonic-gate }
17597c478bd9Sstevel@tonic-gate
17607c478bd9Sstevel@tonic-gate /*
17617c478bd9Sstevel@tonic-gate * Invoked on crypto framework failure during inbound and outbound processing.
17627c478bd9Sstevel@tonic-gate */
17637c478bd9Sstevel@tonic-gate static void
esp_crypto_failed(mblk_t * data_mp,boolean_t is_inbound,int kef_rc,ill_t * ill,ipsecesp_stack_t * espstack)1764bd670b35SErik Nordmark esp_crypto_failed(mblk_t *data_mp, boolean_t is_inbound, int kef_rc,
1765bd670b35SErik Nordmark ill_t *ill, ipsecesp_stack_t *espstack)
17667c478bd9Sstevel@tonic-gate {
1767f4b3ec61Sdh ipsec_stack_t *ipss = espstack->ipsecesp_netstack->netstack_ipsec;
1768f4b3ec61Sdh
1769f4b3ec61Sdh esp1dbg(espstack, ("crypto failed for %s ESP with 0x%x\n",
17707c478bd9Sstevel@tonic-gate is_inbound ? "inbound" : "outbound", kef_rc));
1771bd670b35SErik Nordmark ip_drop_packet(data_mp, is_inbound, ill,
1772f4b3ec61Sdh DROPPER(ipss, ipds_esp_crypto_failed),
1773f4b3ec61Sdh &espstack->esp_dropper);
1774f4b3ec61Sdh ESP_BUMP_STAT(espstack, crypto_failures);
17757c478bd9Sstevel@tonic-gate if (is_inbound)
1776f4b3ec61Sdh IP_ESP_BUMP_STAT(ipss, in_discards);
17777c478bd9Sstevel@tonic-gate else
1778f4b3ec61Sdh ESP_BUMP_STAT(espstack, out_discards);
17797c478bd9Sstevel@tonic-gate }
17807c478bd9Sstevel@tonic-gate
1781bd670b35SErik Nordmark /*
1782bd670b35SErik Nordmark * A statement-equivalent macro, _cr MUST point to a modifiable
1783bd670b35SErik Nordmark * crypto_call_req_t.
1784bd670b35SErik Nordmark */
1785bd670b35SErik Nordmark #define ESP_INIT_CALLREQ(_cr, _mp, _callback) \
1786bd670b35SErik Nordmark (_cr)->cr_flag = CRYPTO_SKIP_REQID|CRYPTO_ALWAYS_QUEUE; \
1787bd670b35SErik Nordmark (_cr)->cr_callback_arg = (_mp); \
1788bd670b35SErik Nordmark (_cr)->cr_callback_func = (_callback)
17897c478bd9Sstevel@tonic-gate
17907c478bd9Sstevel@tonic-gate #define ESP_INIT_CRYPTO_MAC(mac, icvlen, icvbuf) { \
17917c478bd9Sstevel@tonic-gate (mac)->cd_format = CRYPTO_DATA_RAW; \
17927c478bd9Sstevel@tonic-gate (mac)->cd_offset = 0; \
17937c478bd9Sstevel@tonic-gate (mac)->cd_length = icvlen; \
17947c478bd9Sstevel@tonic-gate (mac)->cd_raw.iov_base = (char *)icvbuf; \
17957c478bd9Sstevel@tonic-gate (mac)->cd_raw.iov_len = icvlen; \
17967c478bd9Sstevel@tonic-gate }
17977c478bd9Sstevel@tonic-gate
17987c478bd9Sstevel@tonic-gate #define ESP_INIT_CRYPTO_DATA(data, mp, off, len) { \
17997c478bd9Sstevel@tonic-gate if (MBLKL(mp) >= (len) + (off)) { \
18007c478bd9Sstevel@tonic-gate (data)->cd_format = CRYPTO_DATA_RAW; \
18017c478bd9Sstevel@tonic-gate (data)->cd_raw.iov_base = (char *)(mp)->b_rptr; \
18027c478bd9Sstevel@tonic-gate (data)->cd_raw.iov_len = MBLKL(mp); \
18037c478bd9Sstevel@tonic-gate (data)->cd_offset = off; \
18047c478bd9Sstevel@tonic-gate } else { \
18057c478bd9Sstevel@tonic-gate (data)->cd_format = CRYPTO_DATA_MBLK; \
18069c451ec7SToomas Soome (data)->cd_mp = mp; \
18077c478bd9Sstevel@tonic-gate (data)->cd_offset = off; \
18087c478bd9Sstevel@tonic-gate } \
18097c478bd9Sstevel@tonic-gate (data)->cd_length = len; \
18107c478bd9Sstevel@tonic-gate }
18117c478bd9Sstevel@tonic-gate
18127c478bd9Sstevel@tonic-gate #define ESP_INIT_CRYPTO_DUAL_DATA(data, mp, off1, len1, off2, len2) { \
18137c478bd9Sstevel@tonic-gate (data)->dd_format = CRYPTO_DATA_MBLK; \
18147c478bd9Sstevel@tonic-gate (data)->dd_mp = mp; \
18157c478bd9Sstevel@tonic-gate (data)->dd_len1 = len1; \
18167c478bd9Sstevel@tonic-gate (data)->dd_offset1 = off1; \
18177c478bd9Sstevel@tonic-gate (data)->dd_len2 = len2; \
18187c478bd9Sstevel@tonic-gate (data)->dd_offset2 = off2; \
18197c478bd9Sstevel@tonic-gate }
18207c478bd9Sstevel@tonic-gate
1821bd670b35SErik Nordmark /*
1822bd670b35SErik Nordmark * Returns data_mp if successfully completed the request. Returns
1823bd670b35SErik Nordmark * NULL if it failed (and increments InDiscards) or if it is pending.
1824bd670b35SErik Nordmark */
1825bd670b35SErik Nordmark static mblk_t *
esp_submit_req_inbound(mblk_t * esp_mp,ip_recv_attr_t * ira,ipsa_t * assoc,uint_t esph_offset)1826bd670b35SErik Nordmark esp_submit_req_inbound(mblk_t *esp_mp, ip_recv_attr_t *ira,
1827bd670b35SErik Nordmark ipsa_t *assoc, uint_t esph_offset)
18287c478bd9Sstevel@tonic-gate {
18297c478bd9Sstevel@tonic-gate uint_t auth_offset, msg_len, auth_len;
1830bd670b35SErik Nordmark crypto_call_req_t call_req, *callrp;
1831bd670b35SErik Nordmark mblk_t *mp;
1832628b0c67SMark Fenwick esph_t *esph_ptr;
1833bd670b35SErik Nordmark int kef_rc;
18347c478bd9Sstevel@tonic-gate uint_t icv_len = assoc->ipsa_mac_len;
18357c478bd9Sstevel@tonic-gate crypto_ctx_template_t auth_ctx_tmpl;
1836bd670b35SErik Nordmark boolean_t do_auth, do_encr, force;
18377c478bd9Sstevel@tonic-gate uint_t encr_offset, encr_len;
18387c478bd9Sstevel@tonic-gate uint_t iv_len = assoc->ipsa_iv_len;
18397c478bd9Sstevel@tonic-gate crypto_ctx_template_t encr_ctx_tmpl;
1840bd670b35SErik Nordmark ipsec_crypto_t *ic, icstack;
1841628b0c67SMark Fenwick uchar_t *iv_ptr;
1842bd670b35SErik Nordmark netstack_t *ns = ira->ira_ill->ill_ipst->ips_netstack;
1843bd670b35SErik Nordmark ipsec_stack_t *ipss = ns->netstack_ipsec;
1844bd670b35SErik Nordmark ipsecesp_stack_t *espstack = ns->netstack_ipsecesp;
1845f4b3ec61Sdh
1846*4132743aSToomas Soome mp = NULL;
18477c478bd9Sstevel@tonic-gate do_auth = assoc->ipsa_auth_alg != SADB_AALG_NONE;
18487c478bd9Sstevel@tonic-gate do_encr = assoc->ipsa_encr_alg != SADB_EALG_NULL;
1849bd670b35SErik Nordmark force = (assoc->ipsa_flags & IPSA_F_ASYNC);
1850bd670b35SErik Nordmark
1851bd670b35SErik Nordmark #ifdef IPSEC_LATENCY_TEST
1852bd670b35SErik Nordmark kef_rc = CRYPTO_SUCCESS;
1853bd670b35SErik Nordmark #else
1854bd670b35SErik Nordmark kef_rc = CRYPTO_FAILED;
1855bd670b35SErik Nordmark #endif
18567c478bd9Sstevel@tonic-gate
18577c478bd9Sstevel@tonic-gate /*
18587c478bd9Sstevel@tonic-gate * An inbound packet is of the form:
1859bd670b35SErik Nordmark * [IP,options,ESP,IV,data,ICV,pad]
18607c478bd9Sstevel@tonic-gate */
1861628b0c67SMark Fenwick esph_ptr = (esph_t *)(esp_mp->b_rptr + esph_offset);
1862628b0c67SMark Fenwick iv_ptr = (uchar_t *)(esph_ptr + 1);
1863628b0c67SMark Fenwick /* Packet length starting at IP header ending after ESP ICV. */
18647c478bd9Sstevel@tonic-gate msg_len = MBLKL(esp_mp);
18657c478bd9Sstevel@tonic-gate
1866628b0c67SMark Fenwick encr_offset = esph_offset + sizeof (esph_t) + iv_len;
1867628b0c67SMark Fenwick encr_len = msg_len - encr_offset;
1868628b0c67SMark Fenwick
1869628b0c67SMark Fenwick /*
1870628b0c67SMark Fenwick * Counter mode algs need a nonce. This is setup in sadb_common_add().
1871628b0c67SMark Fenwick * If for some reason we are using a SA which does not have a nonce
1872628b0c67SMark Fenwick * then we must fail here.
1873628b0c67SMark Fenwick */
1874628b0c67SMark Fenwick if ((assoc->ipsa_flags & IPSA_F_COUNTERMODE) &&
1875628b0c67SMark Fenwick (assoc->ipsa_nonce == NULL)) {
1876bd670b35SErik Nordmark ip_drop_packet(esp_mp, B_TRUE, ira->ira_ill,
1877628b0c67SMark Fenwick DROPPER(ipss, ipds_esp_nomem), &espstack->esp_dropper);
1878bd670b35SErik Nordmark return (NULL);
1879628b0c67SMark Fenwick }
1880628b0c67SMark Fenwick
1881bd670b35SErik Nordmark if (force) {
1882bd670b35SErik Nordmark /* We are doing asynch; allocate mblks to hold state */
1883bd670b35SErik Nordmark if ((mp = ip_recv_attr_to_mblk(ira)) == NULL ||
1884bd670b35SErik Nordmark (mp = ipsec_add_crypto_data(mp, &ic)) == NULL) {
1885bd670b35SErik Nordmark BUMP_MIB(ira->ira_ill->ill_ip_mib, ipIfStatsInDiscards);
1886bd670b35SErik Nordmark ip_drop_input("ipIfStatsInDiscards", esp_mp,
1887bd670b35SErik Nordmark ira->ira_ill);
1888bd670b35SErik Nordmark return (NULL);
1889bd670b35SErik Nordmark }
1890bd670b35SErik Nordmark linkb(mp, esp_mp);
1891bd670b35SErik Nordmark callrp = &call_req;
1892bd670b35SErik Nordmark ESP_INIT_CALLREQ(callrp, mp, esp_kcf_callback_inbound);
1893bd670b35SErik Nordmark } else {
1894bd670b35SErik Nordmark /*
1895bd670b35SErik Nordmark * If we know we are going to do sync then ipsec_crypto_t
1896bd670b35SErik Nordmark * should be on the stack.
1897bd670b35SErik Nordmark */
1898bd670b35SErik Nordmark ic = &icstack;
1899bd670b35SErik Nordmark bzero(ic, sizeof (*ic));
1900bd670b35SErik Nordmark callrp = NULL;
1901bd670b35SErik Nordmark }
19027c478bd9Sstevel@tonic-gate
1903bd670b35SErik Nordmark if (do_auth) {
19047c478bd9Sstevel@tonic-gate /* authentication context template */
19057c478bd9Sstevel@tonic-gate IPSEC_CTX_TMPL(assoc, ipsa_authtmpl, IPSEC_ALG_AUTH,
19067c478bd9Sstevel@tonic-gate auth_ctx_tmpl);
19077c478bd9Sstevel@tonic-gate
19087c478bd9Sstevel@tonic-gate /* ICV to be verified */
1909bd670b35SErik Nordmark ESP_INIT_CRYPTO_MAC(&ic->ic_crypto_mac,
19107c478bd9Sstevel@tonic-gate icv_len, esp_mp->b_wptr - icv_len);
19117c478bd9Sstevel@tonic-gate
19127c478bd9Sstevel@tonic-gate /* authentication starts at the ESP header */
19137c478bd9Sstevel@tonic-gate auth_offset = esph_offset;
19147c478bd9Sstevel@tonic-gate auth_len = msg_len - auth_offset - icv_len;
19157c478bd9Sstevel@tonic-gate if (!do_encr) {
19167c478bd9Sstevel@tonic-gate /* authentication only */
19177c478bd9Sstevel@tonic-gate /* initialize input data argument */
1918bd670b35SErik Nordmark ESP_INIT_CRYPTO_DATA(&ic->ic_crypto_data,
19197c478bd9Sstevel@tonic-gate esp_mp, auth_offset, auth_len);
19207c478bd9Sstevel@tonic-gate
19217c478bd9Sstevel@tonic-gate /* call the crypto framework */
19227c478bd9Sstevel@tonic-gate kef_rc = crypto_mac_verify(&assoc->ipsa_amech,
1923bd670b35SErik Nordmark &ic->ic_crypto_data,
19247c478bd9Sstevel@tonic-gate &assoc->ipsa_kcfauthkey, auth_ctx_tmpl,
1925bd670b35SErik Nordmark &ic->ic_crypto_mac, callrp);
19267c478bd9Sstevel@tonic-gate }
19277c478bd9Sstevel@tonic-gate }
19287c478bd9Sstevel@tonic-gate
19297c478bd9Sstevel@tonic-gate if (do_encr) {
19307c478bd9Sstevel@tonic-gate /* encryption template */
19317c478bd9Sstevel@tonic-gate IPSEC_CTX_TMPL(assoc, ipsa_encrtmpl, IPSEC_ALG_ENCR,
19327c478bd9Sstevel@tonic-gate encr_ctx_tmpl);
19337c478bd9Sstevel@tonic-gate
1934628b0c67SMark Fenwick /* Call the nonce update function. Also passes in IV */
1935628b0c67SMark Fenwick (assoc->ipsa_noncefunc)(assoc, (uchar_t *)esph_ptr, encr_len,
1936bd670b35SErik Nordmark iv_ptr, &ic->ic_cmm, &ic->ic_crypto_data);
19377c478bd9Sstevel@tonic-gate
19387c478bd9Sstevel@tonic-gate if (!do_auth) {
19397c478bd9Sstevel@tonic-gate /* decryption only */
19407c478bd9Sstevel@tonic-gate /* initialize input data argument */
1941bd670b35SErik Nordmark ESP_INIT_CRYPTO_DATA(&ic->ic_crypto_data,
19427c478bd9Sstevel@tonic-gate esp_mp, encr_offset, encr_len);
19437c478bd9Sstevel@tonic-gate
19447c478bd9Sstevel@tonic-gate /* call the crypto framework */
1945628b0c67SMark Fenwick kef_rc = crypto_decrypt((crypto_mechanism_t *)
1946bd670b35SErik Nordmark &ic->ic_cmm, &ic->ic_crypto_data,
19477c478bd9Sstevel@tonic-gate &assoc->ipsa_kcfencrkey, encr_ctx_tmpl,
1948bd670b35SErik Nordmark NULL, callrp);
19497c478bd9Sstevel@tonic-gate }
19507c478bd9Sstevel@tonic-gate }
19517c478bd9Sstevel@tonic-gate
19527c478bd9Sstevel@tonic-gate if (do_auth && do_encr) {
19537c478bd9Sstevel@tonic-gate /* dual operation */
19547c478bd9Sstevel@tonic-gate /* initialize input data argument */
1955bd670b35SErik Nordmark ESP_INIT_CRYPTO_DUAL_DATA(&ic->ic_crypto_dual_data,
19567c478bd9Sstevel@tonic-gate esp_mp, auth_offset, auth_len,
19577c478bd9Sstevel@tonic-gate encr_offset, encr_len - icv_len);
19587c478bd9Sstevel@tonic-gate
19597c478bd9Sstevel@tonic-gate /* specify IV */
1960bd670b35SErik Nordmark ic->ic_crypto_dual_data.dd_miscdata = (char *)iv_ptr;
19617c478bd9Sstevel@tonic-gate
19627c478bd9Sstevel@tonic-gate /* call the framework */
19637c478bd9Sstevel@tonic-gate kef_rc = crypto_mac_verify_decrypt(&assoc->ipsa_amech,
1964bd670b35SErik Nordmark &assoc->ipsa_emech, &ic->ic_crypto_dual_data,
19657c478bd9Sstevel@tonic-gate &assoc->ipsa_kcfauthkey, &assoc->ipsa_kcfencrkey,
1966bd670b35SErik Nordmark auth_ctx_tmpl, encr_ctx_tmpl, &ic->ic_crypto_mac,
1967bd670b35SErik Nordmark NULL, callrp);
19687c478bd9Sstevel@tonic-gate }
19697c478bd9Sstevel@tonic-gate
19707c478bd9Sstevel@tonic-gate switch (kef_rc) {
19717c478bd9Sstevel@tonic-gate case CRYPTO_SUCCESS:
1972f4b3ec61Sdh ESP_BUMP_STAT(espstack, crypto_sync);
1973bd670b35SErik Nordmark esp_mp = esp_in_done(esp_mp, ira, ic);
1974bd670b35SErik Nordmark if (force) {
1975bd670b35SErik Nordmark /* Free mp after we are done with ic */
1976bd670b35SErik Nordmark mp = ipsec_free_crypto_data(mp);
1977bd670b35SErik Nordmark (void) ip_recv_attr_free_mblk(mp);
1978bd670b35SErik Nordmark }
1979bd670b35SErik Nordmark return (esp_mp);
19807c478bd9Sstevel@tonic-gate case CRYPTO_QUEUED:
1981bd670b35SErik Nordmark /* esp_kcf_callback_inbound() will be invoked on completion */
1982f4b3ec61Sdh ESP_BUMP_STAT(espstack, crypto_async);
1983bd670b35SErik Nordmark return (NULL);
19847c478bd9Sstevel@tonic-gate case CRYPTO_INVALID_MAC:
1985bd670b35SErik Nordmark if (force) {
1986bd670b35SErik Nordmark mp = ipsec_free_crypto_data(mp);
1987bd670b35SErik Nordmark esp_mp = ip_recv_attr_free_mblk(mp);
1988bd670b35SErik Nordmark }
1989f4b3ec61Sdh ESP_BUMP_STAT(espstack, crypto_sync);
1990bd670b35SErik Nordmark BUMP_MIB(ira->ira_ill->ill_ip_mib, ipIfStatsInDiscards);
1991bd670b35SErik Nordmark esp_log_bad_auth(esp_mp, ira);
1992bd670b35SErik Nordmark /* esp_mp was passed to ip_drop_packet */
1993bd670b35SErik Nordmark return (NULL);
19947c478bd9Sstevel@tonic-gate }
19957c478bd9Sstevel@tonic-gate
1996bd984e57SDan McDonald if (force) {
1997bd984e57SDan McDonald mp = ipsec_free_crypto_data(mp);
1998bd984e57SDan McDonald esp_mp = ip_recv_attr_free_mblk(mp);
1999bd984e57SDan McDonald }
2000bd670b35SErik Nordmark BUMP_MIB(ira->ira_ill->ill_ip_mib, ipIfStatsInDiscards);
2001bd670b35SErik Nordmark esp_crypto_failed(esp_mp, B_TRUE, kef_rc, ira->ira_ill, espstack);
2002bd670b35SErik Nordmark /* esp_mp was passed to ip_drop_packet */
2003bd670b35SErik Nordmark return (NULL);
20047c478bd9Sstevel@tonic-gate }
20057c478bd9Sstevel@tonic-gate
2006437220cdSdanmcd /*
2007437220cdSdanmcd * Compute the IP and UDP checksums -- common code for both keepalives and
2008437220cdSdanmcd * actual ESP-in-UDP packets. Be flexible with multiple mblks because ESP
2009437220cdSdanmcd * uses mblk-insertion to insert the UDP header.
2010437220cdSdanmcd * TODO - If there is an easy way to prep a packet for HW checksums, make
2011437220cdSdanmcd * it happen here.
2012bd670b35SErik Nordmark * Note that this is used before both before calling ip_output_simple and
2013bd670b35SErik Nordmark * in the esp datapath. The former could use IXAF_SET_ULP_CKSUM but not the
2014bd670b35SErik Nordmark * latter.
2015437220cdSdanmcd */
2016437220cdSdanmcd static void
esp_prepare_udp(netstack_t * ns,mblk_t * mp,ipha_t * ipha)2017437220cdSdanmcd esp_prepare_udp(netstack_t *ns, mblk_t *mp, ipha_t *ipha)
2018437220cdSdanmcd {
2019437220cdSdanmcd int offset;
2020437220cdSdanmcd uint32_t cksum;
2021437220cdSdanmcd uint16_t *arr;
2022437220cdSdanmcd mblk_t *udpmp = mp;
202387e10ffaSwy uint_t hlen = IPH_HDR_LENGTH(ipha);
2024437220cdSdanmcd
2025437220cdSdanmcd ASSERT(MBLKL(mp) >= sizeof (ipha_t));
2026437220cdSdanmcd
2027437220cdSdanmcd ipha->ipha_hdr_checksum = 0;
2028437220cdSdanmcd ipha->ipha_hdr_checksum = ip_csum_hdr(ipha);
2029437220cdSdanmcd
2030437220cdSdanmcd if (ns->netstack_udp->us_do_checksum) {
2031437220cdSdanmcd ASSERT(MBLKL(udpmp) >= sizeof (udpha_t));
2032437220cdSdanmcd /* arr points to the IP header. */
2033437220cdSdanmcd arr = (uint16_t *)ipha;
2034437220cdSdanmcd IP_STAT(ns->netstack_ip, ip_out_sw_cksum);
2035bd670b35SErik Nordmark IP_STAT_UPDATE(ns->netstack_ip, ip_out_sw_cksum_bytes,
203687e10ffaSwy ntohs(htons(ipha->ipha_length) - hlen));
2037437220cdSdanmcd /* arr[6-9] are the IP addresses. */
2038437220cdSdanmcd cksum = IP_UDP_CSUM_COMP + arr[6] + arr[7] + arr[8] + arr[9] +
203987e10ffaSwy ntohs(htons(ipha->ipha_length) - hlen);
204087e10ffaSwy cksum = IP_CSUM(mp, hlen, cksum);
204187e10ffaSwy offset = hlen + UDP_CHECKSUM_OFFSET;
2042437220cdSdanmcd while (offset >= MBLKL(udpmp)) {
2043437220cdSdanmcd offset -= MBLKL(udpmp);
2044437220cdSdanmcd udpmp = udpmp->b_cont;
2045437220cdSdanmcd }
2046437220cdSdanmcd /* arr points to the UDP header's checksum field. */
2047437220cdSdanmcd arr = (uint16_t *)(udpmp->b_rptr + offset);
2048437220cdSdanmcd *arr = cksum;
2049437220cdSdanmcd }
2050437220cdSdanmcd }
2051437220cdSdanmcd
205273184bc7SDan McDonald /*
205373184bc7SDan McDonald * taskq handler so we can send the NAT-T keepalive on a separate thread.
205473184bc7SDan McDonald */
205573184bc7SDan McDonald static void
actually_send_keepalive(void * arg)205673184bc7SDan McDonald actually_send_keepalive(void *arg)
205773184bc7SDan McDonald {
2058bd670b35SErik Nordmark mblk_t *mp = (mblk_t *)arg;
2059bd670b35SErik Nordmark ip_xmit_attr_t ixas;
2060bd670b35SErik Nordmark netstack_t *ns;
2061bd670b35SErik Nordmark netstackid_t stackid;
2062bd670b35SErik Nordmark
2063bd670b35SErik Nordmark stackid = (netstackid_t)(uintptr_t)mp->b_prev;
2064bd670b35SErik Nordmark mp->b_prev = NULL;
2065bd670b35SErik Nordmark ns = netstack_find_by_stackid(stackid);
2066bd670b35SErik Nordmark if (ns == NULL) {
2067bd670b35SErik Nordmark /* Disappeared */
2068bd670b35SErik Nordmark ip_drop_output("ipIfStatsOutDiscards", mp, NULL);
2069bd670b35SErik Nordmark freemsg(mp);
207073184bc7SDan McDonald return;
207173184bc7SDan McDonald }
207273184bc7SDan McDonald
2073bd670b35SErik Nordmark bzero(&ixas, sizeof (ixas));
2074bd670b35SErik Nordmark ixas.ixa_zoneid = ALL_ZONES;
2075bd670b35SErik Nordmark ixas.ixa_cred = kcred;
2076bd670b35SErik Nordmark ixas.ixa_cpid = NOPID;
2077bd670b35SErik Nordmark ixas.ixa_tsl = NULL;
2078bd670b35SErik Nordmark ixas.ixa_ipst = ns->netstack_ip;
2079bd670b35SErik Nordmark /* No ULP checksum; done by esp_prepare_udp */
208044b099c4SSowmini Varadhan ixas.ixa_flags = (IXAF_IS_IPV4 | IXAF_NO_IPSEC | IXAF_VERIFY_SOURCE);
2081bd670b35SErik Nordmark
2082bd670b35SErik Nordmark (void) ip_output_simple(mp, &ixas);
2083bd670b35SErik Nordmark ixa_cleanup(&ixas);
208473184bc7SDan McDonald netstack_rele(ns);
208573184bc7SDan McDonald }
208673184bc7SDan McDonald
2087437220cdSdanmcd /*
2088bd670b35SErik Nordmark * Send a one-byte UDP NAT-T keepalive.
2089437220cdSdanmcd */
2090437220cdSdanmcd void
ipsecesp_send_keepalive(ipsa_t * assoc)2091437220cdSdanmcd ipsecesp_send_keepalive(ipsa_t *assoc)
2092437220cdSdanmcd {
2093bd670b35SErik Nordmark mblk_t *mp;
2094bd670b35SErik Nordmark ipha_t *ipha;
2095bd670b35SErik Nordmark udpha_t *udpha;
2096bd670b35SErik Nordmark netstack_t *ns = assoc->ipsa_netstack;
2097437220cdSdanmcd
20980c0328cdSBill Sommerfeld ASSERT(MUTEX_NOT_HELD(&assoc->ipsa_lock));
2099437220cdSdanmcd
2100437220cdSdanmcd mp = allocb(sizeof (ipha_t) + sizeof (udpha_t) + 1, BPRI_HI);
2101437220cdSdanmcd if (mp == NULL)
2102437220cdSdanmcd return;
2103437220cdSdanmcd ipha = (ipha_t *)mp->b_rptr;
2104437220cdSdanmcd ipha->ipha_version_and_hdr_length = IP_SIMPLE_HDR_VERSION;
2105437220cdSdanmcd ipha->ipha_type_of_service = 0;
2106437220cdSdanmcd ipha->ipha_length = htons(sizeof (ipha_t) + sizeof (udpha_t) + 1);
2107437220cdSdanmcd /* Use the low-16 of the SPI so we have some clue where it came from. */
2108437220cdSdanmcd ipha->ipha_ident = *(((uint16_t *)(&assoc->ipsa_spi)) + 1);
2109437220cdSdanmcd ipha->ipha_fragment_offset_and_flags = 0; /* Too small to fragment! */
2110437220cdSdanmcd ipha->ipha_ttl = 0xFF;
2111437220cdSdanmcd ipha->ipha_protocol = IPPROTO_UDP;
2112437220cdSdanmcd ipha->ipha_hdr_checksum = 0;
2113437220cdSdanmcd ipha->ipha_src = assoc->ipsa_srcaddr[0];
2114437220cdSdanmcd ipha->ipha_dst = assoc->ipsa_dstaddr[0];
2115437220cdSdanmcd udpha = (udpha_t *)(ipha + 1);
2116437220cdSdanmcd udpha->uha_src_port = (assoc->ipsa_local_nat_port != 0) ?
2117437220cdSdanmcd assoc->ipsa_local_nat_port : htons(IPPORT_IKE_NATT);
2118437220cdSdanmcd udpha->uha_dst_port = (assoc->ipsa_remote_nat_port != 0) ?
2119437220cdSdanmcd assoc->ipsa_remote_nat_port : htons(IPPORT_IKE_NATT);
2120437220cdSdanmcd udpha->uha_length = htons(sizeof (udpha_t) + 1);
2121437220cdSdanmcd udpha->uha_checksum = 0;
2122437220cdSdanmcd mp->b_wptr = (uint8_t *)(udpha + 1);
2123437220cdSdanmcd *(mp->b_wptr++) = 0xFF;
2124437220cdSdanmcd
2125bd670b35SErik Nordmark esp_prepare_udp(ns, mp, ipha);
2126437220cdSdanmcd
212773184bc7SDan McDonald /*
212873184bc7SDan McDonald * We're holding an isaf_t bucket lock, so pawn off the actual
212973184bc7SDan McDonald * packet transmission to another thread. Just in case syncq
213073184bc7SDan McDonald * processing causes a same-bucket packet to be processed.
213173184bc7SDan McDonald */
2132bd670b35SErik Nordmark mp->b_prev = (mblk_t *)(uintptr_t)ns->netstack_stackid;
2133bd670b35SErik Nordmark
2134bd670b35SErik Nordmark if (taskq_dispatch(esp_taskq, actually_send_keepalive, mp,
2135fc8ae2ecSToomas Soome TQ_NOSLEEP) == TASKQID_INVALID) {
213673184bc7SDan McDonald /* Assume no memory if taskq_dispatch() fails. */
2137bd670b35SErik Nordmark mp->b_prev = NULL;
2138bd670b35SErik Nordmark ip_drop_packet(mp, B_FALSE, NULL,
2139bd670b35SErik Nordmark DROPPER(ns->netstack_ipsec, ipds_esp_nomem),
2140bd670b35SErik Nordmark &ns->netstack_ipsecesp->esp_dropper);
214173184bc7SDan McDonald }
2142437220cdSdanmcd }
2143437220cdSdanmcd
2144bd670b35SErik Nordmark /*
2145bd670b35SErik Nordmark * Returns mp if successfully completed the request. Returns
2146bd670b35SErik Nordmark * NULL if it failed (and increments InDiscards) or if it is pending.
2147bd670b35SErik Nordmark */
2148bd670b35SErik Nordmark static mblk_t *
esp_submit_req_outbound(mblk_t * data_mp,ip_xmit_attr_t * ixa,ipsa_t * assoc,uchar_t * icv_buf,uint_t payload_len)2149bd670b35SErik Nordmark esp_submit_req_outbound(mblk_t *data_mp, ip_xmit_attr_t *ixa, ipsa_t *assoc,
2150bd670b35SErik Nordmark uchar_t *icv_buf, uint_t payload_len)
21517c478bd9Sstevel@tonic-gate {
21527c478bd9Sstevel@tonic-gate uint_t auth_len;
2153bd670b35SErik Nordmark crypto_call_req_t call_req, *callrp;
2154bd670b35SErik Nordmark mblk_t *esp_mp;
2155628b0c67SMark Fenwick esph_t *esph_ptr;
2156bd670b35SErik Nordmark mblk_t *mp;
21577c478bd9Sstevel@tonic-gate int kef_rc = CRYPTO_FAILED;
21587c478bd9Sstevel@tonic-gate uint_t icv_len = assoc->ipsa_mac_len;
21597c478bd9Sstevel@tonic-gate crypto_ctx_template_t auth_ctx_tmpl;
2160bd670b35SErik Nordmark boolean_t do_auth, do_encr, force;
21617c478bd9Sstevel@tonic-gate uint_t iv_len = assoc->ipsa_iv_len;
21627c478bd9Sstevel@tonic-gate crypto_ctx_template_t encr_ctx_tmpl;
21637c478bd9Sstevel@tonic-gate boolean_t is_natt = ((assoc->ipsa_flags & IPSA_F_NATT) != 0);
21647c478bd9Sstevel@tonic-gate size_t esph_offset = (is_natt ? UDPH_SIZE : 0);
2165bd670b35SErik Nordmark netstack_t *ns = ixa->ixa_ipst->ips_netstack;
2166f4b3ec61Sdh ipsecesp_stack_t *espstack = ns->netstack_ipsecesp;
2167bd670b35SErik Nordmark ipsec_crypto_t *ic, icstack;
2168bd670b35SErik Nordmark uchar_t *iv_ptr;
2169bd670b35SErik Nordmark crypto_data_t *cd_ptr = NULL;
2170bd670b35SErik Nordmark ill_t *ill = ixa->ixa_nce->nce_ill;
2171f4b3ec61Sdh ipsec_stack_t *ipss = ns->netstack_ipsec;
21727c478bd9Sstevel@tonic-gate
2173f4b3ec61Sdh esp3dbg(espstack, ("esp_submit_req_outbound:%s",
2174437220cdSdanmcd is_natt ? "natt" : "not natt"));
21757c478bd9Sstevel@tonic-gate
2176*4132743aSToomas Soome mp = NULL;
21777c478bd9Sstevel@tonic-gate do_encr = assoc->ipsa_encr_alg != SADB_EALG_NULL;
21787c478bd9Sstevel@tonic-gate do_auth = assoc->ipsa_auth_alg != SADB_AALG_NONE;
2179bd670b35SErik Nordmark force = (assoc->ipsa_flags & IPSA_F_ASYNC);
2180bd670b35SErik Nordmark
2181bd670b35SErik Nordmark #ifdef IPSEC_LATENCY_TEST
2182bd670b35SErik Nordmark kef_rc = CRYPTO_SUCCESS;
2183bd670b35SErik Nordmark #else
2184bd670b35SErik Nordmark kef_rc = CRYPTO_FAILED;
2185bd670b35SErik Nordmark #endif
21867c478bd9Sstevel@tonic-gate
21877c478bd9Sstevel@tonic-gate /*
21887c478bd9Sstevel@tonic-gate * Outbound IPsec packets are of the form:
2189bd670b35SErik Nordmark * [IP,options] -> [ESP,IV] -> [data] -> [pad,ICV]
21907c478bd9Sstevel@tonic-gate * unless it's NATT, then it's
2191bd670b35SErik Nordmark * [IP,options] -> [udp][ESP,IV] -> [data] -> [pad,ICV]
21927c478bd9Sstevel@tonic-gate * Get a pointer to the mblk containing the ESP header.
21937c478bd9Sstevel@tonic-gate */
2194bd670b35SErik Nordmark ASSERT(data_mp->b_cont != NULL);
2195bd670b35SErik Nordmark esp_mp = data_mp->b_cont;
2196628b0c67SMark Fenwick esph_ptr = (esph_t *)(esp_mp->b_rptr + esph_offset);
2197628b0c67SMark Fenwick iv_ptr = (uchar_t *)(esph_ptr + 1);
2198628b0c67SMark Fenwick
2199628b0c67SMark Fenwick /*
2200628b0c67SMark Fenwick * Combined mode algs need a nonce. This is setup in sadb_common_add().
2201628b0c67SMark Fenwick * If for some reason we are using a SA which does not have a nonce
2202628b0c67SMark Fenwick * then we must fail here.
2203628b0c67SMark Fenwick */
2204628b0c67SMark Fenwick if ((assoc->ipsa_flags & IPSA_F_COUNTERMODE) &&
2205628b0c67SMark Fenwick (assoc->ipsa_nonce == NULL)) {
2206bd670b35SErik Nordmark ip_drop_packet(data_mp, B_FALSE, NULL,
2207628b0c67SMark Fenwick DROPPER(ipss, ipds_esp_nomem), &espstack->esp_dropper);
2208bd670b35SErik Nordmark return (NULL);
2209628b0c67SMark Fenwick }
22107c478bd9Sstevel@tonic-gate
2211bd670b35SErik Nordmark if (force) {
2212bd670b35SErik Nordmark /* We are doing asynch; allocate mblks to hold state */
2213bd670b35SErik Nordmark if ((mp = ip_xmit_attr_to_mblk(ixa)) == NULL ||
2214bd670b35SErik Nordmark (mp = ipsec_add_crypto_data(mp, &ic)) == NULL) {
2215bd670b35SErik Nordmark BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
2216bd670b35SErik Nordmark ip_drop_output("ipIfStatsOutDiscards", data_mp, ill);
2217bd670b35SErik Nordmark freemsg(data_mp);
2218bd670b35SErik Nordmark return (NULL);
2219bd670b35SErik Nordmark }
2220bd670b35SErik Nordmark
2221bd670b35SErik Nordmark linkb(mp, data_mp);
2222bd670b35SErik Nordmark callrp = &call_req;
2223bd670b35SErik Nordmark ESP_INIT_CALLREQ(callrp, mp, esp_kcf_callback_outbound);
2224bd670b35SErik Nordmark } else {
2225bd670b35SErik Nordmark /*
2226bd670b35SErik Nordmark * If we know we are going to do sync then ipsec_crypto_t
2227bd670b35SErik Nordmark * should be on the stack.
2228bd670b35SErik Nordmark */
2229bd670b35SErik Nordmark ic = &icstack;
2230bd670b35SErik Nordmark bzero(ic, sizeof (*ic));
2231bd670b35SErik Nordmark callrp = NULL;
2232bd670b35SErik Nordmark }
22337c478bd9Sstevel@tonic-gate
22347c478bd9Sstevel@tonic-gate
2235bd670b35SErik Nordmark if (do_auth) {
22367c478bd9Sstevel@tonic-gate /* authentication context template */
22377c478bd9Sstevel@tonic-gate IPSEC_CTX_TMPL(assoc, ipsa_authtmpl, IPSEC_ALG_AUTH,
22387c478bd9Sstevel@tonic-gate auth_ctx_tmpl);
22397c478bd9Sstevel@tonic-gate
22407c478bd9Sstevel@tonic-gate /* where to store the computed mac */
2241bd670b35SErik Nordmark ESP_INIT_CRYPTO_MAC(&ic->ic_crypto_mac,
22427c478bd9Sstevel@tonic-gate icv_len, icv_buf);
22437c478bd9Sstevel@tonic-gate
22447c478bd9Sstevel@tonic-gate /* authentication starts at the ESP header */
2245a86080f9Sdanmcd auth_len = payload_len + iv_len + sizeof (esph_t);
22467c478bd9Sstevel@tonic-gate if (!do_encr) {
22477c478bd9Sstevel@tonic-gate /* authentication only */
22487c478bd9Sstevel@tonic-gate /* initialize input data argument */
2249bd670b35SErik Nordmark ESP_INIT_CRYPTO_DATA(&ic->ic_crypto_data,
22507c478bd9Sstevel@tonic-gate esp_mp, esph_offset, auth_len);
22517c478bd9Sstevel@tonic-gate
22527c478bd9Sstevel@tonic-gate /* call the crypto framework */
22537c478bd9Sstevel@tonic-gate kef_rc = crypto_mac(&assoc->ipsa_amech,
2254bd670b35SErik Nordmark &ic->ic_crypto_data,
22557c478bd9Sstevel@tonic-gate &assoc->ipsa_kcfauthkey, auth_ctx_tmpl,
2256bd670b35SErik Nordmark &ic->ic_crypto_mac, callrp);
22577c478bd9Sstevel@tonic-gate }
22587c478bd9Sstevel@tonic-gate }
22597c478bd9Sstevel@tonic-gate
22607c478bd9Sstevel@tonic-gate if (do_encr) {
22617c478bd9Sstevel@tonic-gate /* encryption context template */
22627c478bd9Sstevel@tonic-gate IPSEC_CTX_TMPL(assoc, ipsa_encrtmpl, IPSEC_ALG_ENCR,
22637c478bd9Sstevel@tonic-gate encr_ctx_tmpl);
2264628b0c67SMark Fenwick /* Call the nonce update function. */
2265628b0c67SMark Fenwick (assoc->ipsa_noncefunc)(assoc, (uchar_t *)esph_ptr, payload_len,
2266bd670b35SErik Nordmark iv_ptr, &ic->ic_cmm, &ic->ic_crypto_data);
22677c478bd9Sstevel@tonic-gate
22687c478bd9Sstevel@tonic-gate if (!do_auth) {
22697c478bd9Sstevel@tonic-gate /* encryption only, skip mblk that contains ESP hdr */
22707c478bd9Sstevel@tonic-gate /* initialize input data argument */
2271bd670b35SErik Nordmark ESP_INIT_CRYPTO_DATA(&ic->ic_crypto_data,
2272bd670b35SErik Nordmark esp_mp->b_cont, 0, payload_len);
22737c478bd9Sstevel@tonic-gate
2274628b0c67SMark Fenwick /*
2275628b0c67SMark Fenwick * For combined mode ciphers, the ciphertext is the same
2276628b0c67SMark Fenwick * size as the clear text, the ICV should follow the
2277628b0c67SMark Fenwick * ciphertext. To convince the kcf to allow in-line
2278628b0c67SMark Fenwick * encryption, with an ICV, use ipsec_out_crypto_mac
2279628b0c67SMark Fenwick * to point to the same buffer as the data. The calling
2280628b0c67SMark Fenwick * function need to ensure the buffer is large enough to
2281628b0c67SMark Fenwick * include the ICV.
2282628b0c67SMark Fenwick *
2283628b0c67SMark Fenwick * The IV is already written to the packet buffer, the
2284628b0c67SMark Fenwick * nonce setup function copied it to the params struct
2285628b0c67SMark Fenwick * for the cipher to use.
2286628b0c67SMark Fenwick */
2287628b0c67SMark Fenwick if (assoc->ipsa_flags & IPSA_F_COMBINED) {
2288bd670b35SErik Nordmark bcopy(&ic->ic_crypto_data,
2289bd670b35SErik Nordmark &ic->ic_crypto_mac,
2290628b0c67SMark Fenwick sizeof (crypto_data_t));
2291bd670b35SErik Nordmark ic->ic_crypto_mac.cd_length =
2292628b0c67SMark Fenwick payload_len + icv_len;
2293bd670b35SErik Nordmark cd_ptr = &ic->ic_crypto_mac;
2294628b0c67SMark Fenwick }
22957c478bd9Sstevel@tonic-gate
22967c478bd9Sstevel@tonic-gate /* call the crypto framework */
2297628b0c67SMark Fenwick kef_rc = crypto_encrypt((crypto_mechanism_t *)
2298bd670b35SErik Nordmark &ic->ic_cmm, &ic->ic_crypto_data,
22997c478bd9Sstevel@tonic-gate &assoc->ipsa_kcfencrkey, encr_ctx_tmpl,
2300bd670b35SErik Nordmark cd_ptr, callrp);
2301628b0c67SMark Fenwick
23027c478bd9Sstevel@tonic-gate }
23037c478bd9Sstevel@tonic-gate }
23047c478bd9Sstevel@tonic-gate
23057c478bd9Sstevel@tonic-gate if (do_auth && do_encr) {
23067c478bd9Sstevel@tonic-gate /*
23077c478bd9Sstevel@tonic-gate * Encryption and authentication:
23087c478bd9Sstevel@tonic-gate * Pass the pointer to the mblk chain starting at the ESP
23097c478bd9Sstevel@tonic-gate * header to the framework. Skip the ESP header mblk
23107c478bd9Sstevel@tonic-gate * for encryption, which is reflected by an encryption
23117c478bd9Sstevel@tonic-gate * offset equal to the length of that mblk. Start
23127c478bd9Sstevel@tonic-gate * the authentication at the ESP header, i.e. use an
23137c478bd9Sstevel@tonic-gate * authentication offset of zero.
23147c478bd9Sstevel@tonic-gate */
2315bd670b35SErik Nordmark ESP_INIT_CRYPTO_DUAL_DATA(&ic->ic_crypto_dual_data,
23167c478bd9Sstevel@tonic-gate esp_mp, MBLKL(esp_mp), payload_len, esph_offset, auth_len);
23177c478bd9Sstevel@tonic-gate
23187c478bd9Sstevel@tonic-gate /* specify IV */
2319bd670b35SErik Nordmark ic->ic_crypto_dual_data.dd_miscdata = (char *)iv_ptr;
23207c478bd9Sstevel@tonic-gate
23217c478bd9Sstevel@tonic-gate /* call the framework */
23227c478bd9Sstevel@tonic-gate kef_rc = crypto_encrypt_mac(&assoc->ipsa_emech,
23237c478bd9Sstevel@tonic-gate &assoc->ipsa_amech, NULL,
23247c478bd9Sstevel@tonic-gate &assoc->ipsa_kcfencrkey, &assoc->ipsa_kcfauthkey,
23257c478bd9Sstevel@tonic-gate encr_ctx_tmpl, auth_ctx_tmpl,
2326bd670b35SErik Nordmark &ic->ic_crypto_dual_data,
2327bd670b35SErik Nordmark &ic->ic_crypto_mac, callrp);
23287c478bd9Sstevel@tonic-gate }
23297c478bd9Sstevel@tonic-gate
23307c478bd9Sstevel@tonic-gate switch (kef_rc) {
23317c478bd9Sstevel@tonic-gate case CRYPTO_SUCCESS:
2332f4b3ec61Sdh ESP_BUMP_STAT(espstack, crypto_sync);
2333437220cdSdanmcd esp_set_usetime(assoc, B_FALSE);
2334bd670b35SErik Nordmark if (force) {
2335bd670b35SErik Nordmark mp = ipsec_free_crypto_data(mp);
2336bd670b35SErik Nordmark data_mp = ip_xmit_attr_free_mblk(mp);
2337bd670b35SErik Nordmark }
2338437220cdSdanmcd if (is_natt)
2339bd670b35SErik Nordmark esp_prepare_udp(ns, data_mp, (ipha_t *)data_mp->b_rptr);
2340bd670b35SErik Nordmark return (data_mp);
23417c478bd9Sstevel@tonic-gate case CRYPTO_QUEUED:
2342bd670b35SErik Nordmark /* esp_kcf_callback_outbound() will be invoked on completion */
2343f4b3ec61Sdh ESP_BUMP_STAT(espstack, crypto_async);
2344bd670b35SErik Nordmark return (NULL);
23457c478bd9Sstevel@tonic-gate }
23467c478bd9Sstevel@tonic-gate
2347bd670b35SErik Nordmark if (force) {
2348bd670b35SErik Nordmark mp = ipsec_free_crypto_data(mp);
2349bd670b35SErik Nordmark data_mp = ip_xmit_attr_free_mblk(mp);
2350bd670b35SErik Nordmark }
2351bd670b35SErik Nordmark BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
2352bd670b35SErik Nordmark esp_crypto_failed(data_mp, B_FALSE, kef_rc, NULL, espstack);
2353bd670b35SErik Nordmark /* data_mp was passed to ip_drop_packet */
2354bd670b35SErik Nordmark return (NULL);
23557c478bd9Sstevel@tonic-gate }
23567c478bd9Sstevel@tonic-gate
23577c478bd9Sstevel@tonic-gate /*
23587c478bd9Sstevel@tonic-gate * Handle outbound IPsec processing for IPv4 and IPv6
2359bd670b35SErik Nordmark *
2360bd670b35SErik Nordmark * Returns data_mp if successfully completed the request. Returns
2361bd670b35SErik Nordmark * NULL if it failed (and increments InDiscards) or if it is pending.
23627c478bd9Sstevel@tonic-gate */
2363bd670b35SErik Nordmark static mblk_t *
esp_outbound(mblk_t * data_mp,ip_xmit_attr_t * ixa)2364bd670b35SErik Nordmark esp_outbound(mblk_t *data_mp, ip_xmit_attr_t *ixa)
23657c478bd9Sstevel@tonic-gate {
2366bd670b35SErik Nordmark mblk_t *espmp, *tailmp;
23677c478bd9Sstevel@tonic-gate ipha_t *ipha;
23687c478bd9Sstevel@tonic-gate ip6_t *ip6h;
2369628b0c67SMark Fenwick esph_t *esph_ptr, *iv_ptr;
23707c478bd9Sstevel@tonic-gate uint_t af;
23717c478bd9Sstevel@tonic-gate uint8_t *nhp;
23727c478bd9Sstevel@tonic-gate uintptr_t divpoint, datalen, adj, padlen, i, alloclen;
23737c478bd9Sstevel@tonic-gate uintptr_t esplen = sizeof (esph_t);
23747c478bd9Sstevel@tonic-gate uint8_t protocol;
23757c478bd9Sstevel@tonic-gate ipsa_t *assoc;
2376628b0c67SMark Fenwick uint_t iv_len, block_size, mac_len = 0;
23777c478bd9Sstevel@tonic-gate uchar_t *icv_buf;
23787c478bd9Sstevel@tonic-gate udpha_t *udpha;
23797c478bd9Sstevel@tonic-gate boolean_t is_natt = B_FALSE;
2380bd670b35SErik Nordmark netstack_t *ns = ixa->ixa_ipst->ips_netstack;
2381bd670b35SErik Nordmark ipsecesp_stack_t *espstack = ns->netstack_ipsecesp;
2382bd670b35SErik Nordmark ipsec_stack_t *ipss = ns->netstack_ipsec;
2383bd670b35SErik Nordmark ill_t *ill = ixa->ixa_nce->nce_ill;
2384bd670b35SErik Nordmark boolean_t need_refrele = B_FALSE;
2385f4b3ec61Sdh
2386f4b3ec61Sdh ESP_BUMP_STAT(espstack, out_requests);
2387f4b3ec61Sdh
23887c478bd9Sstevel@tonic-gate /*
23897c478bd9Sstevel@tonic-gate * <sigh> We have to copy the message here, because TCP (for example)
23907c478bd9Sstevel@tonic-gate * keeps a dupb() of the message lying around for retransmission.
23917c478bd9Sstevel@tonic-gate * Since ESP changes the whole of the datagram, we have to create our
23927c478bd9Sstevel@tonic-gate * own copy lest we clobber TCP's data. Since we have to copy anyway,
23937c478bd9Sstevel@tonic-gate * we might as well make use of msgpullup() and get the mblk into one
23947c478bd9Sstevel@tonic-gate * contiguous piece!
23957c478bd9Sstevel@tonic-gate */
2396bd670b35SErik Nordmark tailmp = msgpullup(data_mp, -1);
2397bd670b35SErik Nordmark if (tailmp == NULL) {
23987c478bd9Sstevel@tonic-gate esp0dbg(("esp_outbound: msgpullup() failed, "
23997c478bd9Sstevel@tonic-gate "dropping packet.\n"));
2400bd670b35SErik Nordmark ip_drop_packet(data_mp, B_FALSE, ill,
2401f4b3ec61Sdh DROPPER(ipss, ipds_esp_nomem),
2402f4b3ec61Sdh &espstack->esp_dropper);
2403bd670b35SErik Nordmark BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
2404bd670b35SErik Nordmark return (NULL);
24057c478bd9Sstevel@tonic-gate }
2406bd670b35SErik Nordmark freemsg(data_mp);
2407bd670b35SErik Nordmark data_mp = tailmp;
24087c478bd9Sstevel@tonic-gate
2409bd670b35SErik Nordmark assoc = ixa->ixa_ipsec_esp_sa;
24105d3b8cb7SBill Sommerfeld ASSERT(assoc != NULL);
24115d3b8cb7SBill Sommerfeld
24127c478bd9Sstevel@tonic-gate /*
24135d3b8cb7SBill Sommerfeld * Get the outer IP header in shape to escape this system..
24147c478bd9Sstevel@tonic-gate */
2415bd670b35SErik Nordmark if (is_system_labeled() && (assoc->ipsa_otsl != NULL)) {
2416bd670b35SErik Nordmark /*
2417bd670b35SErik Nordmark * Need to update packet with any CIPSO option and update
2418bd670b35SErik Nordmark * ixa_tsl to capture the new label.
2419bd670b35SErik Nordmark * We allocate a separate ixa for that purpose.
2420bd670b35SErik Nordmark */
2421bd670b35SErik Nordmark ixa = ip_xmit_attr_duplicate(ixa);
2422bd670b35SErik Nordmark if (ixa == NULL) {
2423bd670b35SErik Nordmark ip_drop_packet(data_mp, B_FALSE, ill,
2424bd670b35SErik Nordmark DROPPER(ipss, ipds_esp_nomem),
24255d3b8cb7SBill Sommerfeld &espstack->esp_dropper);
2426bd670b35SErik Nordmark return (NULL);
24275d3b8cb7SBill Sommerfeld }
2428bd670b35SErik Nordmark need_refrele = B_TRUE;
24295d3b8cb7SBill Sommerfeld
2430bd670b35SErik Nordmark label_hold(assoc->ipsa_otsl);
2431bd670b35SErik Nordmark ip_xmit_attr_replace_tsl(ixa, assoc->ipsa_otsl);
2432bd670b35SErik Nordmark
2433bd670b35SErik Nordmark data_mp = sadb_whack_label(data_mp, assoc, ixa,
2434bd670b35SErik Nordmark DROPPER(ipss, ipds_esp_nomem), &espstack->esp_dropper);
2435bd670b35SErik Nordmark if (data_mp == NULL) {
2436bd670b35SErik Nordmark /* Packet dropped by sadb_whack_label */
2437bd670b35SErik Nordmark ixa_refrele(ixa);
2438bd670b35SErik Nordmark return (NULL);
2439bd670b35SErik Nordmark }
2440bd670b35SErik Nordmark }
24415d3b8cb7SBill Sommerfeld
24425d3b8cb7SBill Sommerfeld /*
24435d3b8cb7SBill Sommerfeld * Reality check....
24445d3b8cb7SBill Sommerfeld */
24457c478bd9Sstevel@tonic-gate ipha = (ipha_t *)data_mp->b_rptr; /* So we can call esp_acquire(). */
2446*4132743aSToomas Soome ip6h = (ip6_t *)ipha;
24477c478bd9Sstevel@tonic-gate
2448bd670b35SErik Nordmark if (ixa->ixa_flags & IXAF_IS_IPV4) {
2449bd670b35SErik Nordmark ASSERT(IPH_HDR_VERSION(ipha) == IPV4_VERSION);
2450bd670b35SErik Nordmark
24517c478bd9Sstevel@tonic-gate af = AF_INET;
24527c478bd9Sstevel@tonic-gate divpoint = IPH_HDR_LENGTH(ipha);
24537c478bd9Sstevel@tonic-gate datalen = ntohs(ipha->ipha_length) - divpoint;
24547c478bd9Sstevel@tonic-gate nhp = (uint8_t *)&ipha->ipha_protocol;
24557c478bd9Sstevel@tonic-gate } else {
2456bd670b35SErik Nordmark ip_pkt_t ipp;
2457bd670b35SErik Nordmark
2458bd670b35SErik Nordmark ASSERT(IPH_HDR_VERSION(ipha) == IPV6_VERSION);
24597c478bd9Sstevel@tonic-gate
24607c478bd9Sstevel@tonic-gate af = AF_INET6;
24617c478bd9Sstevel@tonic-gate bzero(&ipp, sizeof (ipp));
2462bd670b35SErik Nordmark divpoint = ip_find_hdr_v6(data_mp, ip6h, B_FALSE, &ipp, NULL);
24637c478bd9Sstevel@tonic-gate if (ipp.ipp_dstopts != NULL &&
24647c478bd9Sstevel@tonic-gate ipp.ipp_dstopts->ip6d_nxt != IPPROTO_ROUTING) {
24657c478bd9Sstevel@tonic-gate /*
24667c478bd9Sstevel@tonic-gate * Destination options are tricky. If we get in here,
24677c478bd9Sstevel@tonic-gate * then we have a terminal header following the
24687c478bd9Sstevel@tonic-gate * destination options. We need to adjust backwards
24697c478bd9Sstevel@tonic-gate * so we insert ESP BEFORE the destination options
24707c478bd9Sstevel@tonic-gate * bag. (So that the dstopts get encrypted!)
24717c478bd9Sstevel@tonic-gate *
24727c478bd9Sstevel@tonic-gate * Since this is for outbound packets only, we know
24737c478bd9Sstevel@tonic-gate * that non-terminal destination options only precede
24747c478bd9Sstevel@tonic-gate * routing headers.
24757c478bd9Sstevel@tonic-gate */
24767c478bd9Sstevel@tonic-gate divpoint -= ipp.ipp_dstoptslen;
24777c478bd9Sstevel@tonic-gate }
24787c478bd9Sstevel@tonic-gate datalen = ntohs(ip6h->ip6_plen) + sizeof (ip6_t) - divpoint;
24797c478bd9Sstevel@tonic-gate
24807c478bd9Sstevel@tonic-gate if (ipp.ipp_rthdr != NULL) {
24817c478bd9Sstevel@tonic-gate nhp = &ipp.ipp_rthdr->ip6r_nxt;
24827c478bd9Sstevel@tonic-gate } else if (ipp.ipp_hopopts != NULL) {
24837c478bd9Sstevel@tonic-gate nhp = &ipp.ipp_hopopts->ip6h_nxt;
24847c478bd9Sstevel@tonic-gate } else {
24857c478bd9Sstevel@tonic-gate ASSERT(divpoint == sizeof (ip6_t));
24867c478bd9Sstevel@tonic-gate /* It's probably IP + ESP. */
24877c478bd9Sstevel@tonic-gate nhp = &ip6h->ip6_nxt;
24887c478bd9Sstevel@tonic-gate }
24897c478bd9Sstevel@tonic-gate }
24907c478bd9Sstevel@tonic-gate
2491628b0c67SMark Fenwick mac_len = assoc->ipsa_mac_len;
24927c478bd9Sstevel@tonic-gate
24937c478bd9Sstevel@tonic-gate if (assoc->ipsa_flags & IPSA_F_NATT) {
24945d3b8cb7SBill Sommerfeld /* wedge in UDP header */
24957c478bd9Sstevel@tonic-gate is_natt = B_TRUE;
24967c478bd9Sstevel@tonic-gate esplen += UDPH_SIZE;
24977c478bd9Sstevel@tonic-gate }
24987c478bd9Sstevel@tonic-gate
24997c478bd9Sstevel@tonic-gate /*
25007c478bd9Sstevel@tonic-gate * Set up ESP header and encryption padding for ENCR PI request.
25017c478bd9Sstevel@tonic-gate */
25027c478bd9Sstevel@tonic-gate
250332350c00Sdanmcd /* Determine the padding length. Pad to 4-bytes for no-encryption. */
25047c478bd9Sstevel@tonic-gate if (assoc->ipsa_encr_alg != SADB_EALG_NULL) {
250532350c00Sdanmcd iv_len = assoc->ipsa_iv_len;
2506628b0c67SMark Fenwick block_size = assoc->ipsa_datalen;
250732350c00Sdanmcd
250832350c00Sdanmcd /*
2509628b0c67SMark Fenwick * Pad the data to the length of the cipher block size.
251032350c00Sdanmcd * Include the two additional bytes (hence the - 2) for the
251132350c00Sdanmcd * padding length and the next header. Take this into account
251232350c00Sdanmcd * when calculating the actual length of the padding.
251332350c00Sdanmcd */
251432350c00Sdanmcd ASSERT(ISP2(iv_len));
2515628b0c67SMark Fenwick padlen = ((unsigned)(block_size - datalen - 2)) &
2516628b0c67SMark Fenwick (block_size - 1);
25177c478bd9Sstevel@tonic-gate } else {
251832350c00Sdanmcd iv_len = 0;
251932350c00Sdanmcd padlen = ((unsigned)(sizeof (uint32_t) - datalen - 2)) &
252032350c00Sdanmcd (sizeof (uint32_t) - 1);
25217c478bd9Sstevel@tonic-gate }
25227c478bd9Sstevel@tonic-gate
25237c478bd9Sstevel@tonic-gate /* Allocate ESP header and IV. */
25247c478bd9Sstevel@tonic-gate esplen += iv_len;
25257c478bd9Sstevel@tonic-gate
25267c478bd9Sstevel@tonic-gate /*
25277c478bd9Sstevel@tonic-gate * Update association byte-count lifetimes. Don't forget to take
25287c478bd9Sstevel@tonic-gate * into account the padding length and next-header (hence the + 2).
2529a86080f9Sdanmcd *
25307c478bd9Sstevel@tonic-gate * Use the amount of data fed into the "encryption algorithm". This
25317c478bd9Sstevel@tonic-gate * is the IV, the data length, the padding length, and the final two
25327c478bd9Sstevel@tonic-gate * bytes (padlen, and next-header).
25337c478bd9Sstevel@tonic-gate *
25347c478bd9Sstevel@tonic-gate */
25357c478bd9Sstevel@tonic-gate
2536a86080f9Sdanmcd if (!esp_age_bytes(assoc, datalen + padlen + iv_len + 2, B_FALSE)) {
2537bd670b35SErik Nordmark ip_drop_packet(data_mp, B_FALSE, ill,
2538f4b3ec61Sdh DROPPER(ipss, ipds_esp_bytes_expire),
2539f4b3ec61Sdh &espstack->esp_dropper);
2540bd670b35SErik Nordmark BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
2541bd670b35SErik Nordmark if (need_refrele)
2542bd670b35SErik Nordmark ixa_refrele(ixa);
2543bd670b35SErik Nordmark return (NULL);
25447c478bd9Sstevel@tonic-gate }
25457c478bd9Sstevel@tonic-gate
25467c478bd9Sstevel@tonic-gate espmp = allocb(esplen, BPRI_HI);
25477c478bd9Sstevel@tonic-gate if (espmp == NULL) {
2548f4b3ec61Sdh ESP_BUMP_STAT(espstack, out_discards);
2549f4b3ec61Sdh esp1dbg(espstack, ("esp_outbound: can't allocate espmp.\n"));
2550bd670b35SErik Nordmark ip_drop_packet(data_mp, B_FALSE, ill,
2551f4b3ec61Sdh DROPPER(ipss, ipds_esp_nomem),
2552f4b3ec61Sdh &espstack->esp_dropper);
2553bd670b35SErik Nordmark BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
2554bd670b35SErik Nordmark if (need_refrele)
2555bd670b35SErik Nordmark ixa_refrele(ixa);
2556bd670b35SErik Nordmark return (NULL);
25577c478bd9Sstevel@tonic-gate }
25587c478bd9Sstevel@tonic-gate espmp->b_wptr += esplen;
2559628b0c67SMark Fenwick esph_ptr = (esph_t *)espmp->b_rptr;
25607c478bd9Sstevel@tonic-gate
25617c478bd9Sstevel@tonic-gate if (is_natt) {
2562f4b3ec61Sdh esp3dbg(espstack, ("esp_outbound: NATT"));
25637c478bd9Sstevel@tonic-gate
25647c478bd9Sstevel@tonic-gate udpha = (udpha_t *)espmp->b_rptr;
2565437220cdSdanmcd udpha->uha_src_port = (assoc->ipsa_local_nat_port != 0) ?
2566437220cdSdanmcd assoc->ipsa_local_nat_port : htons(IPPORT_IKE_NATT);
2567437220cdSdanmcd udpha->uha_dst_port = (assoc->ipsa_remote_nat_port != 0) ?
2568437220cdSdanmcd assoc->ipsa_remote_nat_port : htons(IPPORT_IKE_NATT);
25697c478bd9Sstevel@tonic-gate /*
2570437220cdSdanmcd * Set the checksum to 0, so that the esp_prepare_udp() call
25717c478bd9Sstevel@tonic-gate * can do the right thing.
25727c478bd9Sstevel@tonic-gate */
25737c478bd9Sstevel@tonic-gate udpha->uha_checksum = 0;
2574628b0c67SMark Fenwick esph_ptr = (esph_t *)(udpha + 1);
25757c478bd9Sstevel@tonic-gate }
25767c478bd9Sstevel@tonic-gate
2577628b0c67SMark Fenwick esph_ptr->esph_spi = assoc->ipsa_spi;
25787c478bd9Sstevel@tonic-gate
25791a5e258fSJosef 'Jeff' Sipek esph_ptr->esph_replay = htonl(atomic_inc_32_nv(&assoc->ipsa_replay));
2580628b0c67SMark Fenwick if (esph_ptr->esph_replay == 0 && assoc->ipsa_replay_wsize != 0) {
25817c478bd9Sstevel@tonic-gate /*
25827c478bd9Sstevel@tonic-gate * XXX We have replay counter wrapping.
25837c478bd9Sstevel@tonic-gate * We probably want to nuke this SA (and its peer).
25847c478bd9Sstevel@tonic-gate */
25857c478bd9Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, 0,
25867c478bd9Sstevel@tonic-gate SL_ERROR | SL_CONSOLE | SL_WARN,
25877c478bd9Sstevel@tonic-gate "Outbound ESP SA (0x%x, %s) has wrapped sequence.\n",
2588628b0c67SMark Fenwick esph_ptr->esph_spi, assoc->ipsa_dstaddr, af,
2589f4b3ec61Sdh espstack->ipsecesp_netstack);
25907c478bd9Sstevel@tonic-gate
2591f4b3ec61Sdh ESP_BUMP_STAT(espstack, out_discards);
25927c478bd9Sstevel@tonic-gate sadb_replay_delete(assoc);
2593bd670b35SErik Nordmark ip_drop_packet(data_mp, B_FALSE, ill,
2594f4b3ec61Sdh DROPPER(ipss, ipds_esp_replay),
2595f4b3ec61Sdh &espstack->esp_dropper);
2596bd670b35SErik Nordmark BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
2597bd670b35SErik Nordmark if (need_refrele)
2598bd670b35SErik Nordmark ixa_refrele(ixa);
2599bd670b35SErik Nordmark return (NULL);
26007c478bd9Sstevel@tonic-gate }
26017c478bd9Sstevel@tonic-gate
2602628b0c67SMark Fenwick iv_ptr = (esph_ptr + 1);
26037c478bd9Sstevel@tonic-gate /*
2604628b0c67SMark Fenwick * iv_ptr points to the mblk which will contain the IV once we have
2605628b0c67SMark Fenwick * written it there. This mblk will be part of a mblk chain that
2606628b0c67SMark Fenwick * will make up the packet.
2607628b0c67SMark Fenwick *
2608628b0c67SMark Fenwick * For counter mode algorithms, the IV is a 64 bit quantity, it
2609628b0c67SMark Fenwick * must NEVER repeat in the lifetime of the SA, otherwise an
2610628b0c67SMark Fenwick * attacker who had recorded enough packets might be able to
2611628b0c67SMark Fenwick * determine some clear text.
2612628b0c67SMark Fenwick *
2613628b0c67SMark Fenwick * To ensure this does not happen, the IV is stored in the SA and
2614628b0c67SMark Fenwick * incremented for each packet, the IV is then copied into the
2615628b0c67SMark Fenwick * "packet" for transmission to the receiving system. The IV will
2616628b0c67SMark Fenwick * also be copied into the nonce, when the packet is encrypted.
2617628b0c67SMark Fenwick *
2618628b0c67SMark Fenwick * CBC mode algorithms use a random IV for each packet. We do not
2619628b0c67SMark Fenwick * require the highest quality random bits, but for best security
2620628b0c67SMark Fenwick * with CBC mode ciphers, the value must be unlikely to repeat and
2621628b0c67SMark Fenwick * must not be known in advance to an adversary capable of influencing
2622628b0c67SMark Fenwick * the clear text.
26237c478bd9Sstevel@tonic-gate */
2624628b0c67SMark Fenwick if (!update_iv((uint8_t *)iv_ptr, espstack->esp_pfkey_q, assoc,
2625628b0c67SMark Fenwick espstack)) {
2626bd670b35SErik Nordmark ip_drop_packet(data_mp, B_FALSE, ill,
2627628b0c67SMark Fenwick DROPPER(ipss, ipds_esp_iv_wrap), &espstack->esp_dropper);
2628bd670b35SErik Nordmark if (need_refrele)
2629bd670b35SErik Nordmark ixa_refrele(ixa);
2630bd670b35SErik Nordmark return (NULL);
2631628b0c67SMark Fenwick }
26327c478bd9Sstevel@tonic-gate
26337c478bd9Sstevel@tonic-gate /* Fix the IP header. */
26347c478bd9Sstevel@tonic-gate alloclen = padlen + 2 + mac_len;
26357c478bd9Sstevel@tonic-gate adj = alloclen + (espmp->b_wptr - espmp->b_rptr);
26367c478bd9Sstevel@tonic-gate
26377c478bd9Sstevel@tonic-gate protocol = *nhp;
26387c478bd9Sstevel@tonic-gate
2639bd670b35SErik Nordmark if (ixa->ixa_flags & IXAF_IS_IPV4) {
26407c478bd9Sstevel@tonic-gate ipha->ipha_length = htons(ntohs(ipha->ipha_length) + adj);
26417c478bd9Sstevel@tonic-gate if (is_natt) {
26427c478bd9Sstevel@tonic-gate *nhp = IPPROTO_UDP;
26437c478bd9Sstevel@tonic-gate udpha->uha_length = htons(ntohs(ipha->ipha_length) -
26447c478bd9Sstevel@tonic-gate IPH_HDR_LENGTH(ipha));
26457c478bd9Sstevel@tonic-gate } else {
26467c478bd9Sstevel@tonic-gate *nhp = IPPROTO_ESP;
26477c478bd9Sstevel@tonic-gate }
26487c478bd9Sstevel@tonic-gate ipha->ipha_hdr_checksum = 0;
26497c478bd9Sstevel@tonic-gate ipha->ipha_hdr_checksum = (uint16_t)ip_csum_hdr(ipha);
26507c478bd9Sstevel@tonic-gate } else {
26517c478bd9Sstevel@tonic-gate ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) + adj);
26527c478bd9Sstevel@tonic-gate *nhp = IPPROTO_ESP;
26537c478bd9Sstevel@tonic-gate }
26547c478bd9Sstevel@tonic-gate
26557c478bd9Sstevel@tonic-gate /* I've got the two ESP mblks, now insert them. */
26567c478bd9Sstevel@tonic-gate
2657f4b3ec61Sdh esp2dbg(espstack, ("data_mp before outbound ESP adjustment:\n"));
2658f4b3ec61Sdh esp2dbg(espstack, (dump_msg(data_mp)));
26597c478bd9Sstevel@tonic-gate
2660f4b3ec61Sdh if (!esp_insert_esp(data_mp, espmp, divpoint, espstack)) {
2661f4b3ec61Sdh ESP_BUMP_STAT(espstack, out_discards);
26627c478bd9Sstevel@tonic-gate /* NOTE: esp_insert_esp() only fails if there's no memory. */
2663bd670b35SErik Nordmark ip_drop_packet(data_mp, B_FALSE, ill,
2664f4b3ec61Sdh DROPPER(ipss, ipds_esp_nomem),
2665f4b3ec61Sdh &espstack->esp_dropper);
26667c478bd9Sstevel@tonic-gate freeb(espmp);
2667bd670b35SErik Nordmark BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
2668bd670b35SErik Nordmark if (need_refrele)
2669bd670b35SErik Nordmark ixa_refrele(ixa);
2670bd670b35SErik Nordmark return (NULL);
26717c478bd9Sstevel@tonic-gate }
26727c478bd9Sstevel@tonic-gate
26737c478bd9Sstevel@tonic-gate /* Append padding (and leave room for ICV). */
26747c478bd9Sstevel@tonic-gate for (tailmp = data_mp; tailmp->b_cont != NULL; tailmp = tailmp->b_cont)
26757c478bd9Sstevel@tonic-gate ;
26767c478bd9Sstevel@tonic-gate if (tailmp->b_wptr + alloclen > tailmp->b_datap->db_lim) {
26777c478bd9Sstevel@tonic-gate tailmp->b_cont = allocb(alloclen, BPRI_HI);
26787c478bd9Sstevel@tonic-gate if (tailmp->b_cont == NULL) {
2679f4b3ec61Sdh ESP_BUMP_STAT(espstack, out_discards);
26807c478bd9Sstevel@tonic-gate esp0dbg(("esp_outbound: Can't allocate tailmp.\n"));
2681bd670b35SErik Nordmark ip_drop_packet(data_mp, B_FALSE, ill,
2682f4b3ec61Sdh DROPPER(ipss, ipds_esp_nomem),
2683f4b3ec61Sdh &espstack->esp_dropper);
2684bd670b35SErik Nordmark BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
2685bd670b35SErik Nordmark if (need_refrele)
2686bd670b35SErik Nordmark ixa_refrele(ixa);
2687bd670b35SErik Nordmark return (NULL);
26887c478bd9Sstevel@tonic-gate }
26897c478bd9Sstevel@tonic-gate tailmp = tailmp->b_cont;
26907c478bd9Sstevel@tonic-gate }
26917c478bd9Sstevel@tonic-gate
26927c478bd9Sstevel@tonic-gate /*
26937c478bd9Sstevel@tonic-gate * If there's padding, N bytes of padding must be of the form 0x1,
26947c478bd9Sstevel@tonic-gate * 0x2, 0x3... 0xN.
26957c478bd9Sstevel@tonic-gate */
26967c478bd9Sstevel@tonic-gate for (i = 0; i < padlen; ) {
26977c478bd9Sstevel@tonic-gate i++;
26987c478bd9Sstevel@tonic-gate *tailmp->b_wptr++ = i;
26997c478bd9Sstevel@tonic-gate }
27007c478bd9Sstevel@tonic-gate *tailmp->b_wptr++ = i;
27017c478bd9Sstevel@tonic-gate *tailmp->b_wptr++ = protocol;
27027c478bd9Sstevel@tonic-gate
2703f4b3ec61Sdh esp2dbg(espstack, ("data_Mp before encryption:\n"));
2704f4b3ec61Sdh esp2dbg(espstack, (dump_msg(data_mp)));
27057c478bd9Sstevel@tonic-gate
27067c478bd9Sstevel@tonic-gate /*
27077c478bd9Sstevel@tonic-gate * Okay. I've set up the pre-encryption ESP. Let's do it!
27087c478bd9Sstevel@tonic-gate */
27097c478bd9Sstevel@tonic-gate
27107c478bd9Sstevel@tonic-gate if (mac_len > 0) {
27117c478bd9Sstevel@tonic-gate ASSERT(tailmp->b_wptr + mac_len <= tailmp->b_datap->db_lim);
27127c478bd9Sstevel@tonic-gate icv_buf = tailmp->b_wptr;
27137c478bd9Sstevel@tonic-gate tailmp->b_wptr += mac_len;
27147c478bd9Sstevel@tonic-gate } else {
27157c478bd9Sstevel@tonic-gate icv_buf = NULL;
27167c478bd9Sstevel@tonic-gate }
27177c478bd9Sstevel@tonic-gate
2718bd670b35SErik Nordmark data_mp = esp_submit_req_outbound(data_mp, ixa, assoc, icv_buf,
2719bd670b35SErik Nordmark datalen + padlen + 2);
2720bd670b35SErik Nordmark if (need_refrele)
2721bd670b35SErik Nordmark ixa_refrele(ixa);
2722bd670b35SErik Nordmark return (data_mp);
27237c478bd9Sstevel@tonic-gate }
27247c478bd9Sstevel@tonic-gate
27257c478bd9Sstevel@tonic-gate /*
27267c478bd9Sstevel@tonic-gate * IP calls this to validate the ICMP errors that
27277c478bd9Sstevel@tonic-gate * we got from the network.
27287c478bd9Sstevel@tonic-gate */
2729bd670b35SErik Nordmark mblk_t *
ipsecesp_icmp_error(mblk_t * data_mp,ip_recv_attr_t * ira)2730bd670b35SErik Nordmark ipsecesp_icmp_error(mblk_t *data_mp, ip_recv_attr_t *ira)
27317c478bd9Sstevel@tonic-gate {
2732bd670b35SErik Nordmark netstack_t *ns = ira->ira_ill->ill_ipst->ips_netstack;
2733bd670b35SErik Nordmark ipsecesp_stack_t *espstack = ns->netstack_ipsecesp;
2734bd670b35SErik Nordmark ipsec_stack_t *ipss = ns->netstack_ipsec;
2735f4b3ec61Sdh
27367c478bd9Sstevel@tonic-gate /*
27377c478bd9Sstevel@tonic-gate * Unless we get an entire packet back, this function is useless.
27387c478bd9Sstevel@tonic-gate * Why?
27397c478bd9Sstevel@tonic-gate *
27407c478bd9Sstevel@tonic-gate * 1.) Partial packets are useless, because the "next header"
27417c478bd9Sstevel@tonic-gate * is at the end of the decrypted ESP packet. Without the
27427c478bd9Sstevel@tonic-gate * whole packet, this is useless.
27437c478bd9Sstevel@tonic-gate *
27447c478bd9Sstevel@tonic-gate * 2.) If we every use a stateful cipher, such as a stream or a
27457c478bd9Sstevel@tonic-gate * one-time pad, we can't do anything.
27467c478bd9Sstevel@tonic-gate *
27477c478bd9Sstevel@tonic-gate * Since the chances of us getting an entire packet back are very
27487c478bd9Sstevel@tonic-gate * very small, we discard here.
27497c478bd9Sstevel@tonic-gate */
2750f4b3ec61Sdh IP_ESP_BUMP_STAT(ipss, in_discards);
2751bd670b35SErik Nordmark ip_drop_packet(data_mp, B_TRUE, ira->ira_ill,
2752f4b3ec61Sdh DROPPER(ipss, ipds_esp_icmp),
2753f4b3ec61Sdh &espstack->esp_dropper);
2754bd670b35SErik Nordmark return (NULL);
27557c478bd9Sstevel@tonic-gate }
27567c478bd9Sstevel@tonic-gate
27577c478bd9Sstevel@tonic-gate /*
27587c478bd9Sstevel@tonic-gate * Construct an SADB_REGISTER message with the current algorithms.
2759628b0c67SMark Fenwick * This function gets called when 'ipsecalgs -s' is run or when
2760628b0c67SMark Fenwick * in.iked (or other KMD) starts.
27617c478bd9Sstevel@tonic-gate */
27627c478bd9Sstevel@tonic-gate static boolean_t
esp_register_out(uint32_t sequence,uint32_t pid,uint_t serial,ipsecesp_stack_t * espstack,cred_t * cr)2763f4b3ec61Sdh esp_register_out(uint32_t sequence, uint32_t pid, uint_t serial,
2764bd670b35SErik Nordmark ipsecesp_stack_t *espstack, cred_t *cr)
27657c478bd9Sstevel@tonic-gate {
27667c478bd9Sstevel@tonic-gate mblk_t *pfkey_msg_mp, *keysock_out_mp;
27677c478bd9Sstevel@tonic-gate sadb_msg_t *samsg;
27687c478bd9Sstevel@tonic-gate sadb_supported_t *sasupp_auth = NULL;
27697c478bd9Sstevel@tonic-gate sadb_supported_t *sasupp_encr = NULL;
27707c478bd9Sstevel@tonic-gate sadb_alg_t *saalg;
27717c478bd9Sstevel@tonic-gate uint_t allocsize = sizeof (*samsg);
27727c478bd9Sstevel@tonic-gate uint_t i, numalgs_snap;
27737c478bd9Sstevel@tonic-gate int current_aalgs;
27747c478bd9Sstevel@tonic-gate ipsec_alginfo_t **authalgs;
27757c478bd9Sstevel@tonic-gate uint_t num_aalgs;
27767c478bd9Sstevel@tonic-gate int current_ealgs;
27777c478bd9Sstevel@tonic-gate ipsec_alginfo_t **encralgs;
27787c478bd9Sstevel@tonic-gate uint_t num_ealgs;
2779f4b3ec61Sdh ipsec_stack_t *ipss = espstack->ipsecesp_netstack->netstack_ipsec;
27805d3b8cb7SBill Sommerfeld sadb_sens_t *sens;
27815d3b8cb7SBill Sommerfeld size_t sens_len = 0;
27825d3b8cb7SBill Sommerfeld sadb_ext_t *nextext;
2783bd670b35SErik Nordmark ts_label_t *sens_tsl = NULL;
27847c478bd9Sstevel@tonic-gate
27857c478bd9Sstevel@tonic-gate /* Allocate the KEYSOCK_OUT. */
27867c478bd9Sstevel@tonic-gate keysock_out_mp = sadb_keysock_out(serial);
27877c478bd9Sstevel@tonic-gate if (keysock_out_mp == NULL) {
27887c478bd9Sstevel@tonic-gate esp0dbg(("esp_register_out: couldn't allocate mblk.\n"));
27897c478bd9Sstevel@tonic-gate return (B_FALSE);
27907c478bd9Sstevel@tonic-gate }
27917c478bd9Sstevel@tonic-gate
2792bd670b35SErik Nordmark if (is_system_labeled() && (cr != NULL)) {
2793bd670b35SErik Nordmark sens_tsl = crgetlabel(cr);
2794bd670b35SErik Nordmark if (sens_tsl != NULL) {
2795bd670b35SErik Nordmark sens_len = sadb_sens_len_from_label(sens_tsl);
27965d3b8cb7SBill Sommerfeld allocsize += sens_len;
27975d3b8cb7SBill Sommerfeld }
27985d3b8cb7SBill Sommerfeld }
27995d3b8cb7SBill Sommerfeld
28007c478bd9Sstevel@tonic-gate /*
28017c478bd9Sstevel@tonic-gate * Allocate the PF_KEY message that follows KEYSOCK_OUT.
28027c478bd9Sstevel@tonic-gate */
28037c478bd9Sstevel@tonic-gate
280469e71331SBayard Bell rw_enter(&ipss->ipsec_alg_lock, RW_READER);
28057c478bd9Sstevel@tonic-gate /*
28067c478bd9Sstevel@tonic-gate * Fill SADB_REGISTER message's algorithm descriptors. Hold
28077c478bd9Sstevel@tonic-gate * down the lock while filling it.
28087c478bd9Sstevel@tonic-gate *
28097c478bd9Sstevel@tonic-gate * Return only valid algorithms, so the number of algorithms
28107c478bd9Sstevel@tonic-gate * to send up may be less than the number of algorithm entries
28117c478bd9Sstevel@tonic-gate * in the table.
28127c478bd9Sstevel@tonic-gate */
2813f4b3ec61Sdh authalgs = ipss->ipsec_alglists[IPSEC_ALG_AUTH];
28147c478bd9Sstevel@tonic-gate for (num_aalgs = 0, i = 0; i < IPSEC_MAX_ALGS; i++)
28157c478bd9Sstevel@tonic-gate if (authalgs[i] != NULL && ALG_VALID(authalgs[i]))
28167c478bd9Sstevel@tonic-gate num_aalgs++;
28177c478bd9Sstevel@tonic-gate
28187c478bd9Sstevel@tonic-gate if (num_aalgs != 0) {
28197c478bd9Sstevel@tonic-gate allocsize += (num_aalgs * sizeof (*saalg));
28207c478bd9Sstevel@tonic-gate allocsize += sizeof (*sasupp_auth);
28217c478bd9Sstevel@tonic-gate }
2822f4b3ec61Sdh encralgs = ipss->ipsec_alglists[IPSEC_ALG_ENCR];
28237c478bd9Sstevel@tonic-gate for (num_ealgs = 0, i = 0; i < IPSEC_MAX_ALGS; i++)
28247c478bd9Sstevel@tonic-gate if (encralgs[i] != NULL && ALG_VALID(encralgs[i]))
28257c478bd9Sstevel@tonic-gate num_ealgs++;
28267c478bd9Sstevel@tonic-gate
28277c478bd9Sstevel@tonic-gate if (num_ealgs != 0) {
28287c478bd9Sstevel@tonic-gate allocsize += (num_ealgs * sizeof (*saalg));
28297c478bd9Sstevel@tonic-gate allocsize += sizeof (*sasupp_encr);
28307c478bd9Sstevel@tonic-gate }
28317c478bd9Sstevel@tonic-gate keysock_out_mp->b_cont = allocb(allocsize, BPRI_HI);
28327c478bd9Sstevel@tonic-gate if (keysock_out_mp->b_cont == NULL) {
283369e71331SBayard Bell rw_exit(&ipss->ipsec_alg_lock);
28347c478bd9Sstevel@tonic-gate freemsg(keysock_out_mp);
28357c478bd9Sstevel@tonic-gate return (B_FALSE);
28367c478bd9Sstevel@tonic-gate }
28377c478bd9Sstevel@tonic-gate pfkey_msg_mp = keysock_out_mp->b_cont;
28387c478bd9Sstevel@tonic-gate pfkey_msg_mp->b_wptr += allocsize;
28395d3b8cb7SBill Sommerfeld
28405d3b8cb7SBill Sommerfeld nextext = (sadb_ext_t *)(pfkey_msg_mp->b_rptr + sizeof (*samsg));
28415d3b8cb7SBill Sommerfeld
28427c478bd9Sstevel@tonic-gate if (num_aalgs != 0) {
28435d3b8cb7SBill Sommerfeld sasupp_auth = (sadb_supported_t *)nextext;
28447c478bd9Sstevel@tonic-gate saalg = (sadb_alg_t *)(sasupp_auth + 1);
28457c478bd9Sstevel@tonic-gate
28467c478bd9Sstevel@tonic-gate ASSERT(((ulong_t)saalg & 0x7) == 0);
28477c478bd9Sstevel@tonic-gate
28487c478bd9Sstevel@tonic-gate numalgs_snap = 0;
28497c478bd9Sstevel@tonic-gate for (i = 0;
2850f4b3ec61Sdh ((i < IPSEC_MAX_ALGS) && (numalgs_snap < num_aalgs));
2851f4b3ec61Sdh i++) {
28527c478bd9Sstevel@tonic-gate if (authalgs[i] == NULL || !ALG_VALID(authalgs[i]))
28537c478bd9Sstevel@tonic-gate continue;
28547c478bd9Sstevel@tonic-gate
28557c478bd9Sstevel@tonic-gate saalg->sadb_alg_id = authalgs[i]->alg_id;
28567c478bd9Sstevel@tonic-gate saalg->sadb_alg_ivlen = 0;
28577c478bd9Sstevel@tonic-gate saalg->sadb_alg_minbits = authalgs[i]->alg_ef_minbits;
28587c478bd9Sstevel@tonic-gate saalg->sadb_alg_maxbits = authalgs[i]->alg_ef_maxbits;
28597c478bd9Sstevel@tonic-gate saalg->sadb_x_alg_increment =
28607c478bd9Sstevel@tonic-gate authalgs[i]->alg_increment;
2861628b0c67SMark Fenwick saalg->sadb_x_alg_saltbits = SADB_8TO1(
2862628b0c67SMark Fenwick authalgs[i]->alg_saltlen);
28637c478bd9Sstevel@tonic-gate numalgs_snap++;
28647c478bd9Sstevel@tonic-gate saalg++;
28657c478bd9Sstevel@tonic-gate }
28667c478bd9Sstevel@tonic-gate ASSERT(numalgs_snap == num_aalgs);
28677c478bd9Sstevel@tonic-gate #ifdef DEBUG
28687c478bd9Sstevel@tonic-gate /*
28697c478bd9Sstevel@tonic-gate * Reality check to make sure I snagged all of the
28707c478bd9Sstevel@tonic-gate * algorithms.
28717c478bd9Sstevel@tonic-gate */
28727c478bd9Sstevel@tonic-gate for (; i < IPSEC_MAX_ALGS; i++) {
28737c478bd9Sstevel@tonic-gate if (authalgs[i] != NULL && ALG_VALID(authalgs[i])) {
28747c478bd9Sstevel@tonic-gate cmn_err(CE_PANIC, "esp_register_out()! "
28757c478bd9Sstevel@tonic-gate "Missed aalg #%d.\n", i);
28767c478bd9Sstevel@tonic-gate }
28777c478bd9Sstevel@tonic-gate }
28787c478bd9Sstevel@tonic-gate #endif /* DEBUG */
28795d3b8cb7SBill Sommerfeld nextext = (sadb_ext_t *)saalg;
28807c478bd9Sstevel@tonic-gate }
28817c478bd9Sstevel@tonic-gate
28827c478bd9Sstevel@tonic-gate if (num_ealgs != 0) {
28835d3b8cb7SBill Sommerfeld sasupp_encr = (sadb_supported_t *)nextext;
28847c478bd9Sstevel@tonic-gate saalg = (sadb_alg_t *)(sasupp_encr + 1);
28857c478bd9Sstevel@tonic-gate
28867c478bd9Sstevel@tonic-gate numalgs_snap = 0;
28877c478bd9Sstevel@tonic-gate for (i = 0;
28887c478bd9Sstevel@tonic-gate ((i < IPSEC_MAX_ALGS) && (numalgs_snap < num_ealgs)); i++) {
28897c478bd9Sstevel@tonic-gate if (encralgs[i] == NULL || !ALG_VALID(encralgs[i]))
28907c478bd9Sstevel@tonic-gate continue;
28917c478bd9Sstevel@tonic-gate saalg->sadb_alg_id = encralgs[i]->alg_id;
2892628b0c67SMark Fenwick saalg->sadb_alg_ivlen = encralgs[i]->alg_ivlen;
28937c478bd9Sstevel@tonic-gate saalg->sadb_alg_minbits = encralgs[i]->alg_ef_minbits;
28947c478bd9Sstevel@tonic-gate saalg->sadb_alg_maxbits = encralgs[i]->alg_ef_maxbits;
2895628b0c67SMark Fenwick /*
2896628b0c67SMark Fenwick * We could advertise the ICV length, except there
2897628b0c67SMark Fenwick * is not a value in sadb_x_algb to do this.
2898628b0c67SMark Fenwick * saalg->sadb_alg_maclen = encralgs[i]->alg_maclen;
2899628b0c67SMark Fenwick */
29007c478bd9Sstevel@tonic-gate saalg->sadb_x_alg_increment =
29017c478bd9Sstevel@tonic-gate encralgs[i]->alg_increment;
2902628b0c67SMark Fenwick saalg->sadb_x_alg_saltbits =
2903628b0c67SMark Fenwick SADB_8TO1(encralgs[i]->alg_saltlen);
2904628b0c67SMark Fenwick
29057c478bd9Sstevel@tonic-gate numalgs_snap++;
29067c478bd9Sstevel@tonic-gate saalg++;
29077c478bd9Sstevel@tonic-gate }
29087c478bd9Sstevel@tonic-gate ASSERT(numalgs_snap == num_ealgs);
29097c478bd9Sstevel@tonic-gate #ifdef DEBUG
29107c478bd9Sstevel@tonic-gate /*
29117c478bd9Sstevel@tonic-gate * Reality check to make sure I snagged all of the
29127c478bd9Sstevel@tonic-gate * algorithms.
29137c478bd9Sstevel@tonic-gate */
29147c478bd9Sstevel@tonic-gate for (; i < IPSEC_MAX_ALGS; i++) {
29157c478bd9Sstevel@tonic-gate if (encralgs[i] != NULL && ALG_VALID(encralgs[i])) {
29167c478bd9Sstevel@tonic-gate cmn_err(CE_PANIC, "esp_register_out()! "
29177c478bd9Sstevel@tonic-gate "Missed ealg #%d.\n", i);
29187c478bd9Sstevel@tonic-gate }
29197c478bd9Sstevel@tonic-gate }
29207c478bd9Sstevel@tonic-gate #endif /* DEBUG */
29215d3b8cb7SBill Sommerfeld nextext = (sadb_ext_t *)saalg;
29227c478bd9Sstevel@tonic-gate }
29237c478bd9Sstevel@tonic-gate
29247c478bd9Sstevel@tonic-gate current_aalgs = num_aalgs;
29257c478bd9Sstevel@tonic-gate current_ealgs = num_ealgs;
29267c478bd9Sstevel@tonic-gate
292769e71331SBayard Bell rw_exit(&ipss->ipsec_alg_lock);
29287c478bd9Sstevel@tonic-gate
2929bd670b35SErik Nordmark if (sens_tsl != NULL) {
29305d3b8cb7SBill Sommerfeld sens = (sadb_sens_t *)nextext;
2931bd670b35SErik Nordmark sadb_sens_from_label(sens, SADB_EXT_SENSITIVITY,
2932bd670b35SErik Nordmark sens_tsl, sens_len);
29335d3b8cb7SBill Sommerfeld
29345d3b8cb7SBill Sommerfeld nextext = (sadb_ext_t *)(((uint8_t *)sens) + sens_len);
29355d3b8cb7SBill Sommerfeld }
29365d3b8cb7SBill Sommerfeld
29377c478bd9Sstevel@tonic-gate /* Now fill the rest of the SADB_REGISTER message. */
29387c478bd9Sstevel@tonic-gate
29397c478bd9Sstevel@tonic-gate samsg = (sadb_msg_t *)pfkey_msg_mp->b_rptr;
29407c478bd9Sstevel@tonic-gate samsg->sadb_msg_version = PF_KEY_V2;
29417c478bd9Sstevel@tonic-gate samsg->sadb_msg_type = SADB_REGISTER;
29427c478bd9Sstevel@tonic-gate samsg->sadb_msg_errno = 0;
29437c478bd9Sstevel@tonic-gate samsg->sadb_msg_satype = SADB_SATYPE_ESP;
29447c478bd9Sstevel@tonic-gate samsg->sadb_msg_len = SADB_8TO64(allocsize);
29457c478bd9Sstevel@tonic-gate samsg->sadb_msg_reserved = 0;
29467c478bd9Sstevel@tonic-gate /*
29477c478bd9Sstevel@tonic-gate * Assume caller has sufficient sequence/pid number info. If it's one
29487c478bd9Sstevel@tonic-gate * from me over a new alg., I could give two hoots about sequence.
29497c478bd9Sstevel@tonic-gate */
29507c478bd9Sstevel@tonic-gate samsg->sadb_msg_seq = sequence;
29517c478bd9Sstevel@tonic-gate samsg->sadb_msg_pid = pid;
29527c478bd9Sstevel@tonic-gate
29537c478bd9Sstevel@tonic-gate if (sasupp_auth != NULL) {
2954437220cdSdanmcd sasupp_auth->sadb_supported_len = SADB_8TO64(
2955437220cdSdanmcd sizeof (*sasupp_auth) + sizeof (*saalg) * current_aalgs);
29567c478bd9Sstevel@tonic-gate sasupp_auth->sadb_supported_exttype = SADB_EXT_SUPPORTED_AUTH;
29577c478bd9Sstevel@tonic-gate sasupp_auth->sadb_supported_reserved = 0;
29587c478bd9Sstevel@tonic-gate }
29597c478bd9Sstevel@tonic-gate
29607c478bd9Sstevel@tonic-gate if (sasupp_encr != NULL) {
2961437220cdSdanmcd sasupp_encr->sadb_supported_len = SADB_8TO64(
2962437220cdSdanmcd sizeof (*sasupp_encr) + sizeof (*saalg) * current_ealgs);
29637c478bd9Sstevel@tonic-gate sasupp_encr->sadb_supported_exttype =
29647c478bd9Sstevel@tonic-gate SADB_EXT_SUPPORTED_ENCRYPT;
29657c478bd9Sstevel@tonic-gate sasupp_encr->sadb_supported_reserved = 0;
29667c478bd9Sstevel@tonic-gate }
29677c478bd9Sstevel@tonic-gate
2968f4b3ec61Sdh if (espstack->esp_pfkey_q != NULL)
2969f4b3ec61Sdh putnext(espstack->esp_pfkey_q, keysock_out_mp);
29707c478bd9Sstevel@tonic-gate else {
29717c478bd9Sstevel@tonic-gate freemsg(keysock_out_mp);
29727c478bd9Sstevel@tonic-gate return (B_FALSE);
29737c478bd9Sstevel@tonic-gate }
29747c478bd9Sstevel@tonic-gate
29757c478bd9Sstevel@tonic-gate return (B_TRUE);
29767c478bd9Sstevel@tonic-gate }
29777c478bd9Sstevel@tonic-gate
29787c478bd9Sstevel@tonic-gate /*
29797c478bd9Sstevel@tonic-gate * Invoked when the algorithm table changes. Causes SADB_REGISTER
29807c478bd9Sstevel@tonic-gate * messages continaining the current list of algorithms to be
29817c478bd9Sstevel@tonic-gate * sent up to the ESP listeners.
29827c478bd9Sstevel@tonic-gate */
29837c478bd9Sstevel@tonic-gate void
ipsecesp_algs_changed(netstack_t * ns)2984f4b3ec61Sdh ipsecesp_algs_changed(netstack_t *ns)
29857c478bd9Sstevel@tonic-gate {
2986f4b3ec61Sdh ipsecesp_stack_t *espstack = ns->netstack_ipsecesp;
2987f4b3ec61Sdh
29887c478bd9Sstevel@tonic-gate /*
29897c478bd9Sstevel@tonic-gate * Time to send a PF_KEY SADB_REGISTER message to ESP listeners
29907c478bd9Sstevel@tonic-gate * everywhere. (The function itself checks for NULL esp_pfkey_q.)
29917c478bd9Sstevel@tonic-gate */
29925d3b8cb7SBill Sommerfeld (void) esp_register_out(0, 0, 0, espstack, NULL);
29937c478bd9Sstevel@tonic-gate }
29947c478bd9Sstevel@tonic-gate
29957c478bd9Sstevel@tonic-gate /*
299673184bc7SDan McDonald * Stub function that taskq_dispatch() invokes to take the mblk (in arg)
2997bd670b35SErik Nordmark * and send() it into ESP and IP again.
29987c478bd9Sstevel@tonic-gate */
29997c478bd9Sstevel@tonic-gate static void
inbound_task(void * arg)30007c478bd9Sstevel@tonic-gate inbound_task(void *arg)
30017c478bd9Sstevel@tonic-gate {
3002bd670b35SErik Nordmark mblk_t *mp = (mblk_t *)arg;
3003bd670b35SErik Nordmark mblk_t *async_mp;
3004bd670b35SErik Nordmark ip_recv_attr_t iras;
3005bd670b35SErik Nordmark
3006bd670b35SErik Nordmark async_mp = mp;
3007bd670b35SErik Nordmark mp = async_mp->b_cont;
3008bd670b35SErik Nordmark async_mp->b_cont = NULL;
3009bd670b35SErik Nordmark if (!ip_recv_attr_from_mblk(async_mp, &iras)) {
3010bd670b35SErik Nordmark /* The ill or ip_stack_t disappeared on us */
3011bd670b35SErik Nordmark ip_drop_input("ip_recv_attr_from_mblk", mp, NULL);
301273184bc7SDan McDonald freemsg(mp);
3013bd670b35SErik Nordmark goto done;
301473184bc7SDan McDonald }
301573184bc7SDan McDonald
3016bd670b35SErik Nordmark esp_inbound_restart(mp, &iras);
3017bd670b35SErik Nordmark done:
3018bd670b35SErik Nordmark ira_cleanup(&iras, B_TRUE);
3019bd670b35SErik Nordmark }
3020bd670b35SErik Nordmark
3021bd670b35SErik Nordmark /*
3022bd670b35SErik Nordmark * Restart ESP after the SA has been added.
3023bd670b35SErik Nordmark */
3024bd670b35SErik Nordmark static void
esp_inbound_restart(mblk_t * mp,ip_recv_attr_t * ira)3025bd670b35SErik Nordmark esp_inbound_restart(mblk_t *mp, ip_recv_attr_t *ira)
3026bd670b35SErik Nordmark {
3027bd670b35SErik Nordmark esph_t *esph;
3028bd670b35SErik Nordmark netstack_t *ns = ira->ira_ill->ill_ipst->ips_netstack;
3029bd670b35SErik Nordmark ipsecesp_stack_t *espstack = ns->netstack_ipsecesp;
303073184bc7SDan McDonald
3031f4b3ec61Sdh esp2dbg(espstack, ("in ESP inbound_task"));
3032f4b3ec61Sdh ASSERT(espstack != NULL);
30337c478bd9Sstevel@tonic-gate
3034bd670b35SErik Nordmark mp = ipsec_inbound_esp_sa(mp, ira, &esph);
3035bd670b35SErik Nordmark if (mp == NULL)
3036bd670b35SErik Nordmark return;
3037bd670b35SErik Nordmark
3038bd670b35SErik Nordmark ASSERT(esph != NULL);
3039bd670b35SErik Nordmark ASSERT(ira->ira_flags & IRAF_IPSEC_SECURE);
3040bd670b35SErik Nordmark ASSERT(ira->ira_ipsec_esp_sa != NULL);
3041bd670b35SErik Nordmark
3042bd670b35SErik Nordmark mp = ira->ira_ipsec_esp_sa->ipsa_input_func(mp, esph, ira);
3043bd670b35SErik Nordmark if (mp == NULL) {
3044bd670b35SErik Nordmark /*
3045bd670b35SErik Nordmark * Either it failed or is pending. In the former case
3046bd670b35SErik Nordmark * ipIfStatsInDiscards was increased.
3047bd670b35SErik Nordmark */
3048bd670b35SErik Nordmark return;
304973184bc7SDan McDonald }
3050bd670b35SErik Nordmark
3051bd670b35SErik Nordmark ip_input_post_ipsec(mp, ira);
30527c478bd9Sstevel@tonic-gate }
30537c478bd9Sstevel@tonic-gate
30547c478bd9Sstevel@tonic-gate /*
30557c478bd9Sstevel@tonic-gate * Now that weak-key passed, actually ADD the security association, and
30567c478bd9Sstevel@tonic-gate * send back a reply ADD message.
30577c478bd9Sstevel@tonic-gate */
30587c478bd9Sstevel@tonic-gate static int
esp_add_sa_finish(mblk_t * mp,sadb_msg_t * samsg,keysock_in_t * ksi,int * diagnostic,ipsecesp_stack_t * espstack)30598810c16bSdanmcd esp_add_sa_finish(mblk_t *mp, sadb_msg_t *samsg, keysock_in_t *ksi,
3060f4b3ec61Sdh int *diagnostic, ipsecesp_stack_t *espstack)
30617c478bd9Sstevel@tonic-gate {
30625d3b8cb7SBill Sommerfeld isaf_t *primary = NULL, *secondary;
30635d3b8cb7SBill Sommerfeld boolean_t clone = B_FALSE, is_inbound = B_FALSE;
30647c478bd9Sstevel@tonic-gate ipsa_t *larval = NULL;
30657c478bd9Sstevel@tonic-gate ipsacq_t *acqrec;
30667c478bd9Sstevel@tonic-gate iacqf_t *acq_bucket;
30677c478bd9Sstevel@tonic-gate mblk_t *acq_msgs = NULL;
30687c478bd9Sstevel@tonic-gate int rc;
30697c478bd9Sstevel@tonic-gate mblk_t *lpkt;
30705d3b8cb7SBill Sommerfeld int error;
30715d3b8cb7SBill Sommerfeld ipsa_query_t sq;
3072f4b3ec61Sdh ipsec_stack_t *ipss = espstack->ipsecesp_netstack->netstack_ipsec;
30737c478bd9Sstevel@tonic-gate
30747c478bd9Sstevel@tonic-gate /*
30757c478bd9Sstevel@tonic-gate * Locate the appropriate table(s).
30767c478bd9Sstevel@tonic-gate */
30775d3b8cb7SBill Sommerfeld sq.spp = &espstack->esp_sadb; /* XXX */
30785d3b8cb7SBill Sommerfeld error = sadb_form_query(ksi, IPSA_Q_SA|IPSA_Q_DST,
30795d3b8cb7SBill Sommerfeld IPSA_Q_SA|IPSA_Q_DST|IPSA_Q_INBOUND|IPSA_Q_OUTBOUND,
30805d3b8cb7SBill Sommerfeld &sq, diagnostic);
30815d3b8cb7SBill Sommerfeld if (error)
30825d3b8cb7SBill Sommerfeld return (error);
308307b56925Ssommerfe
308438d95a78Smarkfen /*
308538d95a78Smarkfen * Use the direction flags provided by the KMD to determine
308638d95a78Smarkfen * if the inbound or outbound table should be the primary
308738d95a78Smarkfen * for this SA. If these flags were absent then make this
308838d95a78Smarkfen * decision based on the addresses.
308938d95a78Smarkfen */
30905d3b8cb7SBill Sommerfeld if (sq.assoc->sadb_sa_flags & IPSA_F_INBOUND) {
30915d3b8cb7SBill Sommerfeld primary = sq.inbound;
30925d3b8cb7SBill Sommerfeld secondary = sq.outbound;
309338d95a78Smarkfen is_inbound = B_TRUE;
30945d3b8cb7SBill Sommerfeld if (sq.assoc->sadb_sa_flags & IPSA_F_OUTBOUND)
309538d95a78Smarkfen clone = B_TRUE;
30965d3b8cb7SBill Sommerfeld } else if (sq.assoc->sadb_sa_flags & IPSA_F_OUTBOUND) {
30975d3b8cb7SBill Sommerfeld primary = sq.outbound;
30985d3b8cb7SBill Sommerfeld secondary = sq.inbound;
309938d95a78Smarkfen }
310038d95a78Smarkfen
310138d95a78Smarkfen if (primary == NULL) {
310238d95a78Smarkfen /*
310338d95a78Smarkfen * The KMD did not set a direction flag, determine which
310438d95a78Smarkfen * table to insert the SA into based on addresses.
310538d95a78Smarkfen */
310638d95a78Smarkfen switch (ksi->ks_in_dsttype) {
310738d95a78Smarkfen case KS_IN_ADDR_MBCAST:
310838d95a78Smarkfen clone = B_TRUE; /* All mcast SAs can be bidirectional */
31095d3b8cb7SBill Sommerfeld sq.assoc->sadb_sa_flags |= IPSA_F_OUTBOUND;
311038d95a78Smarkfen /* FALLTHRU */
31117c478bd9Sstevel@tonic-gate /*
31127c478bd9Sstevel@tonic-gate * If the source address is either one of mine, or unspecified
31137c478bd9Sstevel@tonic-gate * (which is best summed up by saying "not 'not mine'"),
31147c478bd9Sstevel@tonic-gate * then the association is potentially bi-directional,
31157c478bd9Sstevel@tonic-gate * in that it can be used for inbound traffic and outbound
31167c478bd9Sstevel@tonic-gate * traffic. The best example of such an SA is a multicast
31177c478bd9Sstevel@tonic-gate * SA (which allows me to receive the outbound traffic).
31187c478bd9Sstevel@tonic-gate */
311938d95a78Smarkfen case KS_IN_ADDR_ME:
31205d3b8cb7SBill Sommerfeld sq.assoc->sadb_sa_flags |= IPSA_F_INBOUND;
31215d3b8cb7SBill Sommerfeld primary = sq.inbound;
31225d3b8cb7SBill Sommerfeld secondary = sq.outbound;
312338d95a78Smarkfen if (ksi->ks_in_srctype != KS_IN_ADDR_NOTME)
312438d95a78Smarkfen clone = B_TRUE;
312538d95a78Smarkfen is_inbound = B_TRUE;
312638d95a78Smarkfen break;
31277c478bd9Sstevel@tonic-gate /*
31287c478bd9Sstevel@tonic-gate * If the source address literally not mine (either
31297c478bd9Sstevel@tonic-gate * unspecified or not mine), then this SA may have an
31307c478bd9Sstevel@tonic-gate * address that WILL be mine after some configuration.
31317c478bd9Sstevel@tonic-gate * We pay the price for this by making it a bi-directional
31327c478bd9Sstevel@tonic-gate * SA.
31337c478bd9Sstevel@tonic-gate */
313438d95a78Smarkfen case KS_IN_ADDR_NOTME:
31355d3b8cb7SBill Sommerfeld sq.assoc->sadb_sa_flags |= IPSA_F_OUTBOUND;
31365d3b8cb7SBill Sommerfeld primary = sq.outbound;
31375d3b8cb7SBill Sommerfeld secondary = sq.inbound;
313838d95a78Smarkfen if (ksi->ks_in_srctype != KS_IN_ADDR_ME) {
31395d3b8cb7SBill Sommerfeld sq.assoc->sadb_sa_flags |= IPSA_F_INBOUND;
314038d95a78Smarkfen clone = B_TRUE;
314138d95a78Smarkfen }
314238d95a78Smarkfen break;
314338d95a78Smarkfen default:
314438d95a78Smarkfen *diagnostic = SADB_X_DIAGNOSTIC_BAD_DST;
314538d95a78Smarkfen return (EINVAL);
314638d95a78Smarkfen }
31477c478bd9Sstevel@tonic-gate }
31487c478bd9Sstevel@tonic-gate
31497c478bd9Sstevel@tonic-gate /*
31507c478bd9Sstevel@tonic-gate * Find a ACQUIRE list entry if possible. If we've added an SA that
31517c478bd9Sstevel@tonic-gate * suits the needs of an ACQUIRE list entry, we can eliminate the
31527c478bd9Sstevel@tonic-gate * ACQUIRE list entry and transmit the enqueued packets. Use the
31537c478bd9Sstevel@tonic-gate * high-bit of the sequence number to queue it. Key off destination
31547c478bd9Sstevel@tonic-gate * addr, and change acqrec's state.
31557c478bd9Sstevel@tonic-gate */
31567c478bd9Sstevel@tonic-gate
31577c478bd9Sstevel@tonic-gate if (samsg->sadb_msg_seq & IACQF_LOWEST_SEQ) {
31585d3b8cb7SBill Sommerfeld acq_bucket = &(sq.sp->sdb_acq[sq.outhash]);
31597c478bd9Sstevel@tonic-gate mutex_enter(&acq_bucket->iacqf_lock);
31607c478bd9Sstevel@tonic-gate for (acqrec = acq_bucket->iacqf_ipsacq; acqrec != NULL;
31617c478bd9Sstevel@tonic-gate acqrec = acqrec->ipsacq_next) {
31627c478bd9Sstevel@tonic-gate mutex_enter(&acqrec->ipsacq_lock);
31637c478bd9Sstevel@tonic-gate /*
31647c478bd9Sstevel@tonic-gate * Q: I only check sequence. Should I check dst?
31657c478bd9Sstevel@tonic-gate * A: Yes, check dest because those are the packets
31667c478bd9Sstevel@tonic-gate * that are queued up.
31677c478bd9Sstevel@tonic-gate */
31687c478bd9Sstevel@tonic-gate if (acqrec->ipsacq_seq == samsg->sadb_msg_seq &&
31695d3b8cb7SBill Sommerfeld IPSA_ARE_ADDR_EQUAL(sq.dstaddr,
3170437220cdSdanmcd acqrec->ipsacq_dstaddr, acqrec->ipsacq_addrfam))
31717c478bd9Sstevel@tonic-gate break;
31727c478bd9Sstevel@tonic-gate mutex_exit(&acqrec->ipsacq_lock);
31737c478bd9Sstevel@tonic-gate }
31747c478bd9Sstevel@tonic-gate if (acqrec != NULL) {
31757c478bd9Sstevel@tonic-gate /*
31767c478bd9Sstevel@tonic-gate * AHA! I found an ACQUIRE record for this SA.
31777c478bd9Sstevel@tonic-gate * Grab the msg list, and free the acquire record.
31787c478bd9Sstevel@tonic-gate * I already am holding the lock for this record,
31797c478bd9Sstevel@tonic-gate * so all I have to do is free it.
31807c478bd9Sstevel@tonic-gate */
31817c478bd9Sstevel@tonic-gate acq_msgs = acqrec->ipsacq_mp;
31827c478bd9Sstevel@tonic-gate acqrec->ipsacq_mp = NULL;
31837c478bd9Sstevel@tonic-gate mutex_exit(&acqrec->ipsacq_lock);
3184f4b3ec61Sdh sadb_destroy_acquire(acqrec,
3185f4b3ec61Sdh espstack->ipsecesp_netstack);
31867c478bd9Sstevel@tonic-gate }
31877c478bd9Sstevel@tonic-gate mutex_exit(&acq_bucket->iacqf_lock);
31887c478bd9Sstevel@tonic-gate }
31897c478bd9Sstevel@tonic-gate
31907c478bd9Sstevel@tonic-gate /*
31917c478bd9Sstevel@tonic-gate * Find PF_KEY message, and see if I'm an update. If so, find entry
31927c478bd9Sstevel@tonic-gate * in larval list (if there).
31937c478bd9Sstevel@tonic-gate */
31947c478bd9Sstevel@tonic-gate if (samsg->sadb_msg_type == SADB_UPDATE) {
31955d3b8cb7SBill Sommerfeld mutex_enter(&sq.inbound->isaf_lock);
31965d3b8cb7SBill Sommerfeld larval = ipsec_getassocbyspi(sq.inbound, sq.assoc->sadb_sa_spi,
31975d3b8cb7SBill Sommerfeld ALL_ZEROES_PTR, sq.dstaddr, sq.dst->sin_family);
31985d3b8cb7SBill Sommerfeld mutex_exit(&sq.inbound->isaf_lock);
31997c478bd9Sstevel@tonic-gate
320072bd9b6bSdanmcd if ((larval == NULL) ||
320172bd9b6bSdanmcd (larval->ipsa_state != IPSA_STATE_LARVAL)) {
320238d95a78Smarkfen *diagnostic = SADB_X_DIAGNOSTIC_SA_NOTFOUND;
320372bd9b6bSdanmcd if (larval != NULL) {
320472bd9b6bSdanmcd IPSA_REFRELE(larval);
320572bd9b6bSdanmcd }
32067c478bd9Sstevel@tonic-gate esp0dbg(("Larval update, but larval disappeared.\n"));
32077c478bd9Sstevel@tonic-gate return (ESRCH);
32087c478bd9Sstevel@tonic-gate } /* Else sadb_common_add unlinks it for me! */
32097c478bd9Sstevel@tonic-gate }
32107c478bd9Sstevel@tonic-gate
3211930af642SDan McDonald if (larval != NULL) {
3212930af642SDan McDonald /*
3213930af642SDan McDonald * Hold again, because sadb_common_add() consumes a reference,
3214930af642SDan McDonald * and we don't want to clear_lpkt() without a reference.
3215930af642SDan McDonald */
3216930af642SDan McDonald IPSA_REFHOLD(larval);
3217930af642SDan McDonald }
32187c478bd9Sstevel@tonic-gate
3219bd670b35SErik Nordmark rc = sadb_common_add(espstack->esp_pfkey_q,
3220f4b3ec61Sdh mp, samsg, ksi, primary, secondary, larval, clone, is_inbound,
322138d95a78Smarkfen diagnostic, espstack->ipsecesp_netstack, &espstack->esp_sadb);
32227c478bd9Sstevel@tonic-gate
3223930af642SDan McDonald if (larval != NULL) {
3224bd670b35SErik Nordmark if (rc == 0) {
3225930af642SDan McDonald lpkt = sadb_clear_lpkt(larval);
3226930af642SDan McDonald if (lpkt != NULL) {
3227fc8ae2ecSToomas Soome rc = taskq_dispatch(esp_taskq, inbound_task,
3228fc8ae2ecSToomas Soome lpkt, TQ_NOSLEEP) == TASKQID_INVALID;
3229930af642SDan McDonald }
3230bd670b35SErik Nordmark }
3231930af642SDan McDonald IPSA_REFRELE(larval);
32327c478bd9Sstevel@tonic-gate }
32337c478bd9Sstevel@tonic-gate
32347c478bd9Sstevel@tonic-gate /*
32357c478bd9Sstevel@tonic-gate * How much more stack will I create with all of these
32367c478bd9Sstevel@tonic-gate * esp_outbound() calls?
32377c478bd9Sstevel@tonic-gate */
32387c478bd9Sstevel@tonic-gate
3239bd670b35SErik Nordmark /* Handle the packets queued waiting for the SA */
32407c478bd9Sstevel@tonic-gate while (acq_msgs != NULL) {
3241bd670b35SErik Nordmark mblk_t *asyncmp;
3242bd670b35SErik Nordmark mblk_t *data_mp;
3243bd670b35SErik Nordmark ip_xmit_attr_t ixas;
3244bd670b35SErik Nordmark ill_t *ill;
32457c478bd9Sstevel@tonic-gate
3246bd670b35SErik Nordmark asyncmp = acq_msgs;
32477c478bd9Sstevel@tonic-gate acq_msgs = acq_msgs->b_next;
3248bd670b35SErik Nordmark asyncmp->b_next = NULL;
3249bd670b35SErik Nordmark
3250bd670b35SErik Nordmark /*
3251bd670b35SErik Nordmark * Extract the ip_xmit_attr_t from the first mblk.
3252bd670b35SErik Nordmark * Verifies that the netstack and ill is still around; could
3253bd670b35SErik Nordmark * have vanished while iked was doing its work.
3254bd670b35SErik Nordmark * On succesful return we have a nce_t and the ill/ipst can't
3255bd670b35SErik Nordmark * disappear until we do the nce_refrele in ixa_cleanup.
3256bd670b35SErik Nordmark */
3257bd670b35SErik Nordmark data_mp = asyncmp->b_cont;
3258bd670b35SErik Nordmark asyncmp->b_cont = NULL;
3259bd670b35SErik Nordmark if (!ip_xmit_attr_from_mblk(asyncmp, &ixas)) {
3260bd670b35SErik Nordmark ESP_BUMP_STAT(espstack, out_discards);
3261bd670b35SErik Nordmark ip_drop_packet(data_mp, B_FALSE, NULL,
3262bd670b35SErik Nordmark DROPPER(ipss, ipds_sadb_acquire_timeout),
3263bd670b35SErik Nordmark &espstack->esp_dropper);
3264bd670b35SErik Nordmark } else if (rc != 0) {
3265bd670b35SErik Nordmark ill = ixas.ixa_nce->nce_ill;
3266bd670b35SErik Nordmark ESP_BUMP_STAT(espstack, out_discards);
3267bd670b35SErik Nordmark ip_drop_packet(data_mp, B_FALSE, ill,
3268bd670b35SErik Nordmark DROPPER(ipss, ipds_sadb_acquire_timeout),
3269bd670b35SErik Nordmark &espstack->esp_dropper);
3270bd670b35SErik Nordmark BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
3271bd670b35SErik Nordmark } else {
3272bd670b35SErik Nordmark esp_outbound_finish(data_mp, &ixas);
32737c478bd9Sstevel@tonic-gate }
3274bd670b35SErik Nordmark ixa_cleanup(&ixas);
3275bd670b35SErik Nordmark }
3276bd670b35SErik Nordmark
3277bd670b35SErik Nordmark return (rc);
3278bd670b35SErik Nordmark }
3279bd670b35SErik Nordmark
3280bd670b35SErik Nordmark /*
3281bd670b35SErik Nordmark * Process one of the queued messages (from ipsacq_mp) once the SA
3282bd670b35SErik Nordmark * has been added.
3283bd670b35SErik Nordmark */
3284bd670b35SErik Nordmark static void
esp_outbound_finish(mblk_t * data_mp,ip_xmit_attr_t * ixa)3285bd670b35SErik Nordmark esp_outbound_finish(mblk_t *data_mp, ip_xmit_attr_t *ixa)
3286bd670b35SErik Nordmark {
3287bd670b35SErik Nordmark netstack_t *ns = ixa->ixa_ipst->ips_netstack;
3288bd670b35SErik Nordmark ipsecesp_stack_t *espstack = ns->netstack_ipsecesp;
3289bd670b35SErik Nordmark ipsec_stack_t *ipss = ns->netstack_ipsec;
3290bd670b35SErik Nordmark ill_t *ill = ixa->ixa_nce->nce_ill;
3291bd670b35SErik Nordmark
3292bd670b35SErik Nordmark if (!ipsec_outbound_sa(data_mp, ixa, IPPROTO_ESP)) {
3293f4b3ec61Sdh ESP_BUMP_STAT(espstack, out_discards);
3294bd670b35SErik Nordmark ip_drop_packet(data_mp, B_FALSE, ill,
3295f4b3ec61Sdh DROPPER(ipss, ipds_sadb_acquire_timeout),
3296f4b3ec61Sdh &espstack->esp_dropper);
3297bd670b35SErik Nordmark BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
3298bd670b35SErik Nordmark return;
32997c478bd9Sstevel@tonic-gate }
33007c478bd9Sstevel@tonic-gate
3301bd670b35SErik Nordmark data_mp = esp_outbound(data_mp, ixa);
3302bd670b35SErik Nordmark if (data_mp == NULL)
3303bd670b35SErik Nordmark return;
3304bd670b35SErik Nordmark
3305bd670b35SErik Nordmark /* do AH processing if needed */
3306bd670b35SErik Nordmark data_mp = esp_do_outbound_ah(data_mp, ixa);
3307bd670b35SErik Nordmark if (data_mp == NULL)
3308bd670b35SErik Nordmark return;
3309bd670b35SErik Nordmark
3310bd670b35SErik Nordmark (void) ip_output_post_ipsec(data_mp, ixa);
33117c478bd9Sstevel@tonic-gate }
33127c478bd9Sstevel@tonic-gate
33137c478bd9Sstevel@tonic-gate /*
33147c478bd9Sstevel@tonic-gate * Add new ESP security association. This may become a generic AH/ESP
33157c478bd9Sstevel@tonic-gate * routine eventually.
33167c478bd9Sstevel@tonic-gate */
33177c478bd9Sstevel@tonic-gate static int
esp_add_sa(mblk_t * mp,keysock_in_t * ksi,int * diagnostic,netstack_t * ns)3318f4b3ec61Sdh esp_add_sa(mblk_t *mp, keysock_in_t *ksi, int *diagnostic, netstack_t *ns)
33197c478bd9Sstevel@tonic-gate {
33207c478bd9Sstevel@tonic-gate sadb_sa_t *assoc = (sadb_sa_t *)ksi->ks_in_extv[SADB_EXT_SA];
33217c478bd9Sstevel@tonic-gate sadb_address_t *srcext =
33227c478bd9Sstevel@tonic-gate (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_SRC];
33237c478bd9Sstevel@tonic-gate sadb_address_t *dstext =
33247c478bd9Sstevel@tonic-gate (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_DST];
33258810c16bSdanmcd sadb_address_t *isrcext =
33268810c16bSdanmcd (sadb_address_t *)ksi->ks_in_extv[SADB_X_EXT_ADDRESS_INNER_SRC];
33278810c16bSdanmcd sadb_address_t *idstext =
33288810c16bSdanmcd (sadb_address_t *)ksi->ks_in_extv[SADB_X_EXT_ADDRESS_INNER_DST];
33297c478bd9Sstevel@tonic-gate sadb_address_t *nttext_loc =
33307c478bd9Sstevel@tonic-gate (sadb_address_t *)ksi->ks_in_extv[SADB_X_EXT_ADDRESS_NATT_LOC];
33317c478bd9Sstevel@tonic-gate sadb_address_t *nttext_rem =
33327c478bd9Sstevel@tonic-gate (sadb_address_t *)ksi->ks_in_extv[SADB_X_EXT_ADDRESS_NATT_REM];
33337c478bd9Sstevel@tonic-gate sadb_key_t *akey = (sadb_key_t *)ksi->ks_in_extv[SADB_EXT_KEY_AUTH];
33347c478bd9Sstevel@tonic-gate sadb_key_t *ekey = (sadb_key_t *)ksi->ks_in_extv[SADB_EXT_KEY_ENCRYPT];
33357c478bd9Sstevel@tonic-gate struct sockaddr_in *src, *dst;
33367c478bd9Sstevel@tonic-gate struct sockaddr_in *natt_loc, *natt_rem;
33377c478bd9Sstevel@tonic-gate struct sockaddr_in6 *natt_loc6, *natt_rem6;
33387c478bd9Sstevel@tonic-gate sadb_lifetime_t *soft =
33397c478bd9Sstevel@tonic-gate (sadb_lifetime_t *)ksi->ks_in_extv[SADB_EXT_LIFETIME_SOFT];
33407c478bd9Sstevel@tonic-gate sadb_lifetime_t *hard =
33417c478bd9Sstevel@tonic-gate (sadb_lifetime_t *)ksi->ks_in_extv[SADB_EXT_LIFETIME_HARD];
33429c2c14abSThejaswini Singarajipura sadb_lifetime_t *idle =
33439c2c14abSThejaswini Singarajipura (sadb_lifetime_t *)ksi->ks_in_extv[SADB_X_EXT_LIFETIME_IDLE];
3344f4b3ec61Sdh ipsecesp_stack_t *espstack = ns->netstack_ipsecesp;
3345f4b3ec61Sdh ipsec_stack_t *ipss = ns->netstack_ipsec;
33467c478bd9Sstevel@tonic-gate
33475d3b8cb7SBill Sommerfeld
33485d3b8cb7SBill Sommerfeld
33497c478bd9Sstevel@tonic-gate /* I need certain extensions present for an ADD message. */
33507c478bd9Sstevel@tonic-gate if (srcext == NULL) {
33517c478bd9Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_MISSING_SRC;
33527c478bd9Sstevel@tonic-gate return (EINVAL);
33537c478bd9Sstevel@tonic-gate }
33547c478bd9Sstevel@tonic-gate if (dstext == NULL) {
33557c478bd9Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_MISSING_DST;
33567c478bd9Sstevel@tonic-gate return (EINVAL);
33577c478bd9Sstevel@tonic-gate }
33588810c16bSdanmcd if (isrcext == NULL && idstext != NULL) {
33598810c16bSdanmcd *diagnostic = SADB_X_DIAGNOSTIC_MISSING_INNER_SRC;
33608810c16bSdanmcd return (EINVAL);
33618810c16bSdanmcd }
33628810c16bSdanmcd if (isrcext != NULL && idstext == NULL) {
33638810c16bSdanmcd *diagnostic = SADB_X_DIAGNOSTIC_MISSING_INNER_DST;
33648810c16bSdanmcd return (EINVAL);
33658810c16bSdanmcd }
33667c478bd9Sstevel@tonic-gate if (assoc == NULL) {
33677c478bd9Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_MISSING_SA;
33687c478bd9Sstevel@tonic-gate return (EINVAL);
33697c478bd9Sstevel@tonic-gate }
33707c478bd9Sstevel@tonic-gate if (ekey == NULL && assoc->sadb_sa_encrypt != SADB_EALG_NULL) {
33717c478bd9Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_MISSING_EKEY;
33727c478bd9Sstevel@tonic-gate return (EINVAL);
33737c478bd9Sstevel@tonic-gate }
33747c478bd9Sstevel@tonic-gate
33757c478bd9Sstevel@tonic-gate src = (struct sockaddr_in *)(srcext + 1);
33767c478bd9Sstevel@tonic-gate dst = (struct sockaddr_in *)(dstext + 1);
33777c478bd9Sstevel@tonic-gate natt_loc = (struct sockaddr_in *)(nttext_loc + 1);
33787c478bd9Sstevel@tonic-gate natt_loc6 = (struct sockaddr_in6 *)(nttext_loc + 1);
33797c478bd9Sstevel@tonic-gate natt_rem = (struct sockaddr_in *)(nttext_rem + 1);
33807c478bd9Sstevel@tonic-gate natt_rem6 = (struct sockaddr_in6 *)(nttext_rem + 1);
33817c478bd9Sstevel@tonic-gate
33827c478bd9Sstevel@tonic-gate /* Sundry ADD-specific reality checks. */
33837c478bd9Sstevel@tonic-gate /* XXX STATS : Logging/stats here? */
33849c2c14abSThejaswini Singarajipura
33859c2c14abSThejaswini Singarajipura if ((assoc->sadb_sa_state != SADB_SASTATE_MATURE) &&
33869c2c14abSThejaswini Singarajipura (assoc->sadb_sa_state != SADB_X_SASTATE_ACTIVE_ELSEWHERE)) {
33877c478bd9Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_BAD_SASTATE;
33887c478bd9Sstevel@tonic-gate return (EINVAL);
33897c478bd9Sstevel@tonic-gate }
33907c478bd9Sstevel@tonic-gate if (assoc->sadb_sa_encrypt == SADB_EALG_NONE) {
33917c478bd9Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_BAD_EALG;
33927c478bd9Sstevel@tonic-gate return (EINVAL);
33937c478bd9Sstevel@tonic-gate }
33947c478bd9Sstevel@tonic-gate
3395bd670b35SErik Nordmark #ifndef IPSEC_LATENCY_TEST
33967c478bd9Sstevel@tonic-gate if (assoc->sadb_sa_encrypt == SADB_EALG_NULL &&
33977c478bd9Sstevel@tonic-gate assoc->sadb_sa_auth == SADB_AALG_NONE) {
33987c478bd9Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_BAD_AALG;
33997c478bd9Sstevel@tonic-gate return (EINVAL);
34007c478bd9Sstevel@tonic-gate }
3401bd670b35SErik Nordmark #endif
34027c478bd9Sstevel@tonic-gate
340372bd9b6bSdanmcd if (assoc->sadb_sa_flags & ~espstack->esp_sadb.s_addflags) {
34047c478bd9Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_BAD_SAFLAGS;
34057c478bd9Sstevel@tonic-gate return (EINVAL);
34067c478bd9Sstevel@tonic-gate }
34077c478bd9Sstevel@tonic-gate
34089c2c14abSThejaswini Singarajipura if ((*diagnostic = sadb_hardsoftchk(hard, soft, idle)) != 0) {
34097c478bd9Sstevel@tonic-gate return (EINVAL);
34107c478bd9Sstevel@tonic-gate }
34118810c16bSdanmcd ASSERT(src->sin_family == dst->sin_family);
34127c478bd9Sstevel@tonic-gate
34137c478bd9Sstevel@tonic-gate if (assoc->sadb_sa_flags & SADB_X_SAFLAGS_NATT_LOC) {
34147c478bd9Sstevel@tonic-gate if (nttext_loc == NULL) {
34157c478bd9Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_MISSING_NATT_LOC;
34167c478bd9Sstevel@tonic-gate return (EINVAL);
34177c478bd9Sstevel@tonic-gate }
34187c478bd9Sstevel@tonic-gate
34197c478bd9Sstevel@tonic-gate if (natt_loc->sin_family == AF_INET6 &&
34207c478bd9Sstevel@tonic-gate !IN6_IS_ADDR_V4MAPPED(&natt_loc6->sin6_addr)) {
34217c478bd9Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_MALFORMED_NATT_LOC;
34227c478bd9Sstevel@tonic-gate return (EINVAL);
34237c478bd9Sstevel@tonic-gate }
34247c478bd9Sstevel@tonic-gate }
34257c478bd9Sstevel@tonic-gate
34267c478bd9Sstevel@tonic-gate if (assoc->sadb_sa_flags & SADB_X_SAFLAGS_NATT_REM) {
34277c478bd9Sstevel@tonic-gate if (nttext_rem == NULL) {
34287c478bd9Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_MISSING_NATT_REM;
34297c478bd9Sstevel@tonic-gate return (EINVAL);
34307c478bd9Sstevel@tonic-gate }
34317c478bd9Sstevel@tonic-gate if (natt_rem->sin_family == AF_INET6 &&
34327c478bd9Sstevel@tonic-gate !IN6_IS_ADDR_V4MAPPED(&natt_rem6->sin6_addr)) {
34337c478bd9Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_MALFORMED_NATT_REM;
34347c478bd9Sstevel@tonic-gate return (EINVAL);
34357c478bd9Sstevel@tonic-gate }
34367c478bd9Sstevel@tonic-gate }
34377c478bd9Sstevel@tonic-gate
34387c478bd9Sstevel@tonic-gate
34397c478bd9Sstevel@tonic-gate /* Stuff I don't support, for now. XXX Diagnostic? */
34405d3b8cb7SBill Sommerfeld if (ksi->ks_in_extv[SADB_EXT_LIFETIME_CURRENT] != NULL)
34417c478bd9Sstevel@tonic-gate return (EOPNOTSUPP);
34427c478bd9Sstevel@tonic-gate
34435d3b8cb7SBill Sommerfeld if ((*diagnostic = sadb_labelchk(ksi)) != 0)
34445d3b8cb7SBill Sommerfeld return (EINVAL);
34455d3b8cb7SBill Sommerfeld
34467c478bd9Sstevel@tonic-gate /*
34475d3b8cb7SBill Sommerfeld * XXX Policy : I'm not checking identities at this time,
34485d3b8cb7SBill Sommerfeld * but if I did, I'd do them here, before I sent
34497c478bd9Sstevel@tonic-gate * the weak key check up to the algorithm.
34507c478bd9Sstevel@tonic-gate */
34517c478bd9Sstevel@tonic-gate
345269e71331SBayard Bell rw_enter(&ipss->ipsec_alg_lock, RW_READER);
34537c478bd9Sstevel@tonic-gate
34547c478bd9Sstevel@tonic-gate /*
34557c478bd9Sstevel@tonic-gate * First locate the authentication algorithm.
34567c478bd9Sstevel@tonic-gate */
3457bd670b35SErik Nordmark #ifdef IPSEC_LATENCY_TEST
3458bd670b35SErik Nordmark if (akey != NULL && assoc->sadb_sa_auth != SADB_AALG_NONE) {
3459bd670b35SErik Nordmark #else
34607c478bd9Sstevel@tonic-gate if (akey != NULL) {
3461bd670b35SErik Nordmark #endif
34627c478bd9Sstevel@tonic-gate ipsec_alginfo_t *aalg;
34637c478bd9Sstevel@tonic-gate
3464f4b3ec61Sdh aalg = ipss->ipsec_alglists[IPSEC_ALG_AUTH]
3465f4b3ec61Sdh [assoc->sadb_sa_auth];
34667c478bd9Sstevel@tonic-gate if (aalg == NULL || !ALG_VALID(aalg)) {
346769e71331SBayard Bell rw_exit(&ipss->ipsec_alg_lock);
3468f4b3ec61Sdh esp1dbg(espstack, ("Couldn't find auth alg #%d.\n",
34697c478bd9Sstevel@tonic-gate assoc->sadb_sa_auth));
34707c478bd9Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_BAD_AALG;
34717c478bd9Sstevel@tonic-gate return (EINVAL);
34727c478bd9Sstevel@tonic-gate }
34737c478bd9Sstevel@tonic-gate
347446c444baSmarkfen /*
347546c444baSmarkfen * Sanity check key sizes.
347646c444baSmarkfen * Note: It's not possible to use SADB_AALG_NONE because
347746c444baSmarkfen * this auth_alg is not defined with ALG_FLAG_VALID. If this
347846c444baSmarkfen * ever changes, the same check for SADB_AALG_NONE and
347946c444baSmarkfen * a auth_key != NULL should be made here ( see below).
348046c444baSmarkfen */
34817c478bd9Sstevel@tonic-gate if (!ipsec_valid_key_size(akey->sadb_key_bits, aalg)) {
348269e71331SBayard Bell rw_exit(&ipss->ipsec_alg_lock);
34837c478bd9Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_BAD_AKEYBITS;
34847c478bd9Sstevel@tonic-gate return (EINVAL);
34857c478bd9Sstevel@tonic-gate }
348646c444baSmarkfen ASSERT(aalg->alg_mech_type != CRYPTO_MECHANISM_INVALID);
34877c478bd9Sstevel@tonic-gate
34887c478bd9Sstevel@tonic-gate /* check key and fix parity if needed */
34897c478bd9Sstevel@tonic-gate if (ipsec_check_key(aalg->alg_mech_type, akey, B_TRUE,
34907c478bd9Sstevel@tonic-gate diagnostic) != 0) {
349169e71331SBayard Bell rw_exit(&ipss->ipsec_alg_lock);
34927c478bd9Sstevel@tonic-gate return (EINVAL);
34937c478bd9Sstevel@tonic-gate }
34947c478bd9Sstevel@tonic-gate }
34957c478bd9Sstevel@tonic-gate
34967c478bd9Sstevel@tonic-gate /*
34977c478bd9Sstevel@tonic-gate * Then locate the encryption algorithm.
34987c478bd9Sstevel@tonic-gate */
34997c478bd9Sstevel@tonic-gate if (ekey != NULL) {
3500628b0c67SMark Fenwick uint_t keybits;
35017c478bd9Sstevel@tonic-gate ipsec_alginfo_t *ealg;
35027c478bd9Sstevel@tonic-gate
3503f4b3ec61Sdh ealg = ipss->ipsec_alglists[IPSEC_ALG_ENCR]
3504f4b3ec61Sdh [assoc->sadb_sa_encrypt];
35057c478bd9Sstevel@tonic-gate if (ealg == NULL || !ALG_VALID(ealg)) {
350669e71331SBayard Bell rw_exit(&ipss->ipsec_alg_lock);
3507f4b3ec61Sdh esp1dbg(espstack, ("Couldn't find encr alg #%d.\n",
35087c478bd9Sstevel@tonic-gate assoc->sadb_sa_encrypt));
35097c478bd9Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_BAD_EALG;
35107c478bd9Sstevel@tonic-gate return (EINVAL);
35117c478bd9Sstevel@tonic-gate }
35127c478bd9Sstevel@tonic-gate
351346c444baSmarkfen /*
351446c444baSmarkfen * Sanity check key sizes. If the encryption algorithm is
351546c444baSmarkfen * SADB_EALG_NULL but the encryption key is NOT
351646c444baSmarkfen * NULL then complain.
3517628b0c67SMark Fenwick *
3518628b0c67SMark Fenwick * The keying material includes salt bits if required by
3519628b0c67SMark Fenwick * algorithm and optionally the Initial IV, check the
3520628b0c67SMark Fenwick * length of whats left.
352146c444baSmarkfen */
3522628b0c67SMark Fenwick keybits = ekey->sadb_key_bits;
3523628b0c67SMark Fenwick keybits -= ekey->sadb_key_reserved;
3524628b0c67SMark Fenwick keybits -= SADB_8TO1(ealg->alg_saltlen);
352546c444baSmarkfen if ((assoc->sadb_sa_encrypt == SADB_EALG_NULL) ||
3526628b0c67SMark Fenwick (!ipsec_valid_key_size(keybits, ealg))) {
352769e71331SBayard Bell rw_exit(&ipss->ipsec_alg_lock);
35287c478bd9Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_BAD_EKEYBITS;
35297c478bd9Sstevel@tonic-gate return (EINVAL);
35307c478bd9Sstevel@tonic-gate }
353146c444baSmarkfen ASSERT(ealg->alg_mech_type != CRYPTO_MECHANISM_INVALID);
35327c478bd9Sstevel@tonic-gate
35337c478bd9Sstevel@tonic-gate /* check key */
35347c478bd9Sstevel@tonic-gate if (ipsec_check_key(ealg->alg_mech_type, ekey, B_FALSE,
35357c478bd9Sstevel@tonic-gate diagnostic) != 0) {
353669e71331SBayard Bell rw_exit(&ipss->ipsec_alg_lock);
35377c478bd9Sstevel@tonic-gate return (EINVAL);
35387c478bd9Sstevel@tonic-gate }
35397c478bd9Sstevel@tonic-gate }
354069e71331SBayard Bell rw_exit(&ipss->ipsec_alg_lock);
35417c478bd9Sstevel@tonic-gate
35428810c16bSdanmcd return (esp_add_sa_finish(mp, (sadb_msg_t *)mp->b_cont->b_rptr, ksi,
3543437220cdSdanmcd diagnostic, espstack));
35447c478bd9Sstevel@tonic-gate }
35457c478bd9Sstevel@tonic-gate
35467c478bd9Sstevel@tonic-gate /*
35477c478bd9Sstevel@tonic-gate * Update a security association. Updates come in two varieties. The first
35487c478bd9Sstevel@tonic-gate * is an update of lifetimes on a non-larval SA. The second is an update of
35497c478bd9Sstevel@tonic-gate * a larval SA, which ends up looking a lot more like an add.
35507c478bd9Sstevel@tonic-gate */
35517c478bd9Sstevel@tonic-gate static int
3552f4b3ec61Sdh esp_update_sa(mblk_t *mp, keysock_in_t *ksi, int *diagnostic,
355338d95a78Smarkfen ipsecesp_stack_t *espstack, uint8_t sadb_msg_type)
35547c478bd9Sstevel@tonic-gate {
35559c2c14abSThejaswini Singarajipura sadb_sa_t *assoc = (sadb_sa_t *)ksi->ks_in_extv[SADB_EXT_SA];
35569c2c14abSThejaswini Singarajipura mblk_t *buf_pkt;
35579c2c14abSThejaswini Singarajipura int rcode;
35589c2c14abSThejaswini Singarajipura
35597c478bd9Sstevel@tonic-gate sadb_address_t *dstext =
35607c478bd9Sstevel@tonic-gate (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_DST];
35617c478bd9Sstevel@tonic-gate
35627c478bd9Sstevel@tonic-gate if (dstext == NULL) {
35637c478bd9Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_MISSING_DST;
35647c478bd9Sstevel@tonic-gate return (EINVAL);
35657c478bd9Sstevel@tonic-gate }
35667c478bd9Sstevel@tonic-gate
35679c2c14abSThejaswini Singarajipura rcode = sadb_update_sa(mp, ksi, &buf_pkt, &espstack->esp_sadb,
35689c2c14abSThejaswini Singarajipura diagnostic, espstack->esp_pfkey_q, esp_add_sa,
35699c2c14abSThejaswini Singarajipura espstack->ipsecesp_netstack, sadb_msg_type);
35709c2c14abSThejaswini Singarajipura
35719c2c14abSThejaswini Singarajipura if ((assoc->sadb_sa_state != SADB_X_SASTATE_ACTIVE) ||
35729c2c14abSThejaswini Singarajipura (rcode != 0)) {
35739c2c14abSThejaswini Singarajipura return (rcode);
35749c2c14abSThejaswini Singarajipura }
35759c2c14abSThejaswini Singarajipura
357673184bc7SDan McDonald HANDLE_BUF_PKT(esp_taskq, espstack->ipsecesp_netstack->netstack_ipsec,
35779c2c14abSThejaswini Singarajipura espstack->esp_dropper, buf_pkt);
35789c2c14abSThejaswini Singarajipura
35799c2c14abSThejaswini Singarajipura return (rcode);
35807c478bd9Sstevel@tonic-gate }
35817c478bd9Sstevel@tonic-gate
35825d3b8cb7SBill Sommerfeld /* XXX refactor me */
35837c478bd9Sstevel@tonic-gate /*
35847c478bd9Sstevel@tonic-gate * Delete a security association. This is REALLY likely to be code common to
35857c478bd9Sstevel@tonic-gate * both AH and ESP. Find the association, then unlink it.
35867c478bd9Sstevel@tonic-gate */
35877c478bd9Sstevel@tonic-gate static int
3588f4b3ec61Sdh esp_del_sa(mblk_t *mp, keysock_in_t *ksi, int *diagnostic,
358938d95a78Smarkfen ipsecesp_stack_t *espstack, uint8_t sadb_msg_type)
35907c478bd9Sstevel@tonic-gate {
35917c478bd9Sstevel@tonic-gate sadb_sa_t *assoc = (sadb_sa_t *)ksi->ks_in_extv[SADB_EXT_SA];
35927c478bd9Sstevel@tonic-gate sadb_address_t *dstext =
35937c478bd9Sstevel@tonic-gate (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_DST];
35947c478bd9Sstevel@tonic-gate sadb_address_t *srcext =
35957c478bd9Sstevel@tonic-gate (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_SRC];
35967c478bd9Sstevel@tonic-gate struct sockaddr_in *sin;
35977c478bd9Sstevel@tonic-gate
35987c478bd9Sstevel@tonic-gate if (assoc == NULL) {
35997c478bd9Sstevel@tonic-gate if (dstext != NULL) {
36007c478bd9Sstevel@tonic-gate sin = (struct sockaddr_in *)(dstext + 1);
36017c478bd9Sstevel@tonic-gate } else if (srcext != NULL) {
36027c478bd9Sstevel@tonic-gate sin = (struct sockaddr_in *)(srcext + 1);
36037c478bd9Sstevel@tonic-gate } else {
36047c478bd9Sstevel@tonic-gate *diagnostic = SADB_X_DIAGNOSTIC_MISSING_SA;
36057c478bd9Sstevel@tonic-gate return (EINVAL);
36067c478bd9Sstevel@tonic-gate }
36078810c16bSdanmcd return (sadb_purge_sa(mp, ksi,
3608f4b3ec61Sdh (sin->sin_family == AF_INET6) ? &espstack->esp_sadb.s_v6 :
36095d3b8cb7SBill Sommerfeld &espstack->esp_sadb.s_v4, diagnostic,
3610bd670b35SErik Nordmark espstack->esp_pfkey_q));
36117c478bd9Sstevel@tonic-gate }
36127c478bd9Sstevel@tonic-gate
361338d95a78Smarkfen return (sadb_delget_sa(mp, ksi, &espstack->esp_sadb, diagnostic,
361438d95a78Smarkfen espstack->esp_pfkey_q, sadb_msg_type));
36157c478bd9Sstevel@tonic-gate }
36167c478bd9Sstevel@tonic-gate
36175d3b8cb7SBill Sommerfeld /* XXX refactor me */
36187c478bd9Sstevel@tonic-gate /*
36197c478bd9Sstevel@tonic-gate * Convert the entire contents of all of ESP's SA tables into PF_KEY SADB_DUMP
36207c478bd9Sstevel@tonic-gate * messages.
36217c478bd9Sstevel@tonic-gate */
36227c478bd9Sstevel@tonic-gate static void
3623f4b3ec61Sdh esp_dump(mblk_t *mp, keysock_in_t *ksi, ipsecesp_stack_t *espstack)
36247c478bd9Sstevel@tonic-gate {
36257c478bd9Sstevel@tonic-gate int error;
36267c478bd9Sstevel@tonic-gate sadb_msg_t *samsg;
36277c478bd9Sstevel@tonic-gate
36287c478bd9Sstevel@tonic-gate /*
36297c478bd9Sstevel@tonic-gate * Dump each fanout, bailing if error is non-zero.
36307c478bd9Sstevel@tonic-gate */
36317c478bd9Sstevel@tonic-gate
36329c2c14abSThejaswini Singarajipura error = sadb_dump(espstack->esp_pfkey_q, mp, ksi,
3633f4b3ec61Sdh &espstack->esp_sadb.s_v4);
36347c478bd9Sstevel@tonic-gate if (error != 0)
36357c478bd9Sstevel@tonic-gate goto bail;
36367c478bd9Sstevel@tonic-gate
36379c2c14abSThejaswini Singarajipura error = sadb_dump(espstack->esp_pfkey_q, mp, ksi,
3638f4b3ec61Sdh &espstack->esp_sadb.s_v6);
36397c478bd9Sstevel@tonic-gate bail:
36407c478bd9Sstevel@tonic-gate ASSERT(mp->b_cont != NULL);
36417c478bd9Sstevel@tonic-gate samsg = (sadb_msg_t *)mp->b_cont->b_rptr;
36427c478bd9Sstevel@tonic-gate samsg->sadb_msg_errno = (uint8_t)error;
3643f4b3ec61Sdh sadb_pfkey_echo(espstack->esp_pfkey_q, mp,
3644f4b3ec61Sdh (sadb_msg_t *)mp->b_cont->b_rptr, ksi, NULL);
36457c478bd9Sstevel@tonic-gate }
36467c478bd9Sstevel@tonic-gate
36478810c16bSdanmcd /*
36488810c16bSdanmcd * First-cut reality check for an inbound PF_KEY message.
36498810c16bSdanmcd */
36508810c16bSdanmcd static boolean_t
3651f4b3ec61Sdh esp_pfkey_reality_failures(mblk_t *mp, keysock_in_t *ksi,
3652f4b3ec61Sdh ipsecesp_stack_t *espstack)
36538810c16bSdanmcd {
36548810c16bSdanmcd int diagnostic;
36558810c16bSdanmcd
36568810c16bSdanmcd if (ksi->ks_in_extv[SADB_EXT_PROPOSAL] != NULL) {
36578810c16bSdanmcd diagnostic = SADB_X_DIAGNOSTIC_PROP_PRESENT;
36588810c16bSdanmcd goto badmsg;
36598810c16bSdanmcd }
36608810c16bSdanmcd if (ksi->ks_in_extv[SADB_EXT_SUPPORTED_AUTH] != NULL ||
36618810c16bSdanmcd ksi->ks_in_extv[SADB_EXT_SUPPORTED_ENCRYPT] != NULL) {
36628810c16bSdanmcd diagnostic = SADB_X_DIAGNOSTIC_SUPP_PRESENT;
36638810c16bSdanmcd goto badmsg;
36648810c16bSdanmcd }
36658810c16bSdanmcd return (B_FALSE); /* False ==> no failures */
36668810c16bSdanmcd
36678810c16bSdanmcd badmsg:
3668f4b3ec61Sdh sadb_pfkey_error(espstack->esp_pfkey_q, mp, EINVAL, diagnostic,
36698810c16bSdanmcd ksi->ks_in_serial);
36708810c16bSdanmcd return (B_TRUE); /* True ==> failures */
36718810c16bSdanmcd }
36728810c16bSdanmcd
36737c478bd9Sstevel@tonic-gate /*
36747c478bd9Sstevel@tonic-gate * ESP parsing of PF_KEY messages. Keysock did most of the really silly
36757c478bd9Sstevel@tonic-gate * error cases. What I receive is a fully-formed, syntactically legal
36767c478bd9Sstevel@tonic-gate * PF_KEY message. I then need to check semantics...
36777c478bd9Sstevel@tonic-gate *
36787c478bd9Sstevel@tonic-gate * This code may become common to AH and ESP. Stay tuned.
36797c478bd9Sstevel@tonic-gate *
36807c478bd9Sstevel@tonic-gate * I also make the assumption that db_ref's are cool. If this assumption
36817c478bd9Sstevel@tonic-gate * is wrong, this means that someone other than keysock or me has been
36827c478bd9Sstevel@tonic-gate * mucking with PF_KEY messages.
36837c478bd9Sstevel@tonic-gate */
36847c478bd9Sstevel@tonic-gate static void
3685f4b3ec61Sdh esp_parse_pfkey(mblk_t *mp, ipsecesp_stack_t *espstack)
36867c478bd9Sstevel@tonic-gate {
36877c478bd9Sstevel@tonic-gate mblk_t *msg = mp->b_cont;
36887c478bd9Sstevel@tonic-gate sadb_msg_t *samsg;
36897c478bd9Sstevel@tonic-gate keysock_in_t *ksi;
36907c478bd9Sstevel@tonic-gate int error;
36917c478bd9Sstevel@tonic-gate int diagnostic = SADB_X_DIAGNOSTIC_NONE;
36927c478bd9Sstevel@tonic-gate
36937c478bd9Sstevel@tonic-gate ASSERT(msg != NULL);
3694f4b3ec61Sdh
36957c478bd9Sstevel@tonic-gate samsg = (sadb_msg_t *)msg->b_rptr;
36967c478bd9Sstevel@tonic-gate ksi = (keysock_in_t *)mp->b_rptr;
36977c478bd9Sstevel@tonic-gate
36987c478bd9Sstevel@tonic-gate /*
36997c478bd9Sstevel@tonic-gate * If applicable, convert unspecified AF_INET6 to unspecified
37008810c16bSdanmcd * AF_INET. And do other address reality checks.
37017c478bd9Sstevel@tonic-gate */
3702f4b3ec61Sdh if (!sadb_addrfix(ksi, espstack->esp_pfkey_q, mp,
3703f4b3ec61Sdh espstack->ipsecesp_netstack) ||
3704f4b3ec61Sdh esp_pfkey_reality_failures(mp, ksi, espstack)) {
37058810c16bSdanmcd return;
37068810c16bSdanmcd }
37077c478bd9Sstevel@tonic-gate
37087c478bd9Sstevel@tonic-gate switch (samsg->sadb_msg_type) {
37097c478bd9Sstevel@tonic-gate case SADB_ADD:
3710f4b3ec61Sdh error = esp_add_sa(mp, ksi, &diagnostic,
3711f4b3ec61Sdh espstack->ipsecesp_netstack);
37127c478bd9Sstevel@tonic-gate if (error != 0) {
3713f4b3ec61Sdh sadb_pfkey_error(espstack->esp_pfkey_q, mp, error,
3714f4b3ec61Sdh diagnostic, ksi->ks_in_serial);
37157c478bd9Sstevel@tonic-gate }
37167c478bd9Sstevel@tonic-gate /* else esp_add_sa() took care of things. */
37177c478bd9Sstevel@tonic-gate break;
37187c478bd9Sstevel@tonic-gate case SADB_DELETE:
371938d95a78Smarkfen case SADB_X_DELPAIR:
37209c2c14abSThejaswini Singarajipura case SADB_X_DELPAIR_STATE:
372138d95a78Smarkfen error = esp_del_sa(mp, ksi, &diagnostic, espstack,
372238d95a78Smarkfen samsg->sadb_msg_type);
37237c478bd9Sstevel@tonic-gate if (error != 0) {
3724f4b3ec61Sdh sadb_pfkey_error(espstack->esp_pfkey_q, mp, error,
3725f4b3ec61Sdh diagnostic, ksi->ks_in_serial);
37267c478bd9Sstevel@tonic-gate }
37277c478bd9Sstevel@tonic-gate /* Else esp_del_sa() took care of things. */
37287c478bd9Sstevel@tonic-gate break;
37297c478bd9Sstevel@tonic-gate case SADB_GET:
373038d95a78Smarkfen error = sadb_delget_sa(mp, ksi, &espstack->esp_sadb,
373138d95a78Smarkfen &diagnostic, espstack->esp_pfkey_q, samsg->sadb_msg_type);
37327c478bd9Sstevel@tonic-gate if (error != 0) {
3733f4b3ec61Sdh sadb_pfkey_error(espstack->esp_pfkey_q, mp, error,
3734f4b3ec61Sdh diagnostic, ksi->ks_in_serial);
37357c478bd9Sstevel@tonic-gate }
37367c478bd9Sstevel@tonic-gate /* Else sadb_get_sa() took care of things. */
37377c478bd9Sstevel@tonic-gate break;
37387c478bd9Sstevel@tonic-gate case SADB_FLUSH:
3739f4b3ec61Sdh sadbp_flush(&espstack->esp_sadb, espstack->ipsecesp_netstack);
3740f4b3ec61Sdh sadb_pfkey_echo(espstack->esp_pfkey_q, mp, samsg, ksi, NULL);
37417c478bd9Sstevel@tonic-gate break;
37427c478bd9Sstevel@tonic-gate case SADB_REGISTER:
37437c478bd9Sstevel@tonic-gate /*
37447c478bd9Sstevel@tonic-gate * Hmmm, let's do it! Check for extensions (there should
37457c478bd9Sstevel@tonic-gate * be none), extract the fields, call esp_register_out(),
37467c478bd9Sstevel@tonic-gate * then either free or report an error.
37477c478bd9Sstevel@tonic-gate *
37487c478bd9Sstevel@tonic-gate * Keysock takes care of the PF_KEY bookkeeping for this.
37497c478bd9Sstevel@tonic-gate */
37507c478bd9Sstevel@tonic-gate if (esp_register_out(samsg->sadb_msg_seq, samsg->sadb_msg_pid,
3751bd670b35SErik Nordmark ksi->ks_in_serial, espstack, msg_getcred(mp, NULL))) {
37527c478bd9Sstevel@tonic-gate freemsg(mp);
37537c478bd9Sstevel@tonic-gate } else {
37547c478bd9Sstevel@tonic-gate /*
37557c478bd9Sstevel@tonic-gate * Only way this path hits is if there is a memory
37567c478bd9Sstevel@tonic-gate * failure. It will not return B_FALSE because of
37577c478bd9Sstevel@tonic-gate * lack of esp_pfkey_q if I am in wput().
37587c478bd9Sstevel@tonic-gate */
3759f4b3ec61Sdh sadb_pfkey_error(espstack->esp_pfkey_q, mp, ENOMEM,
3760f4b3ec61Sdh diagnostic, ksi->ks_in_serial);
37617c478bd9Sstevel@tonic-gate }
37627c478bd9Sstevel@tonic-gate break;
37637c478bd9Sstevel@tonic-gate case SADB_UPDATE:
376438d95a78Smarkfen case SADB_X_UPDATEPAIR:
37657c478bd9Sstevel@tonic-gate /*
37667c478bd9Sstevel@tonic-gate * Find a larval, if not there, find a full one and get
37677c478bd9Sstevel@tonic-gate * strict.
37687c478bd9Sstevel@tonic-gate */
376938d95a78Smarkfen error = esp_update_sa(mp, ksi, &diagnostic, espstack,
377038d95a78Smarkfen samsg->sadb_msg_type);
37717c478bd9Sstevel@tonic-gate if (error != 0) {
3772f4b3ec61Sdh sadb_pfkey_error(espstack->esp_pfkey_q, mp, error,
3773f4b3ec61Sdh diagnostic, ksi->ks_in_serial);
37747c478bd9Sstevel@tonic-gate }
37757c478bd9Sstevel@tonic-gate /* else esp_update_sa() took care of things. */
37767c478bd9Sstevel@tonic-gate break;
37777c478bd9Sstevel@tonic-gate case SADB_GETSPI:
37787c478bd9Sstevel@tonic-gate /*
37797c478bd9Sstevel@tonic-gate * Reserve a new larval entry.
37807c478bd9Sstevel@tonic-gate */
3781f4b3ec61Sdh esp_getspi(mp, ksi, espstack);
37827c478bd9Sstevel@tonic-gate break;
37837c478bd9Sstevel@tonic-gate case SADB_ACQUIRE:
37847c478bd9Sstevel@tonic-gate /*
37857c478bd9Sstevel@tonic-gate * Find larval and/or ACQUIRE record and kill it (them), I'm
37867c478bd9Sstevel@tonic-gate * most likely an error. Inbound ACQUIRE messages should only
37877c478bd9Sstevel@tonic-gate * have the base header.
37887c478bd9Sstevel@tonic-gate */
3789f4b3ec61Sdh sadb_in_acquire(samsg, &espstack->esp_sadb,
3790f4b3ec61Sdh espstack->esp_pfkey_q, espstack->ipsecesp_netstack);
37917c478bd9Sstevel@tonic-gate freemsg(mp);
37927c478bd9Sstevel@tonic-gate break;
37937c478bd9Sstevel@tonic-gate case SADB_DUMP:
37947c478bd9Sstevel@tonic-gate /*
37957c478bd9Sstevel@tonic-gate * Dump all entries.
37967c478bd9Sstevel@tonic-gate */
3797f4b3ec61Sdh esp_dump(mp, ksi, espstack);
37987c478bd9Sstevel@tonic-gate /* esp_dump will take care of the return message, etc. */
37997c478bd9Sstevel@tonic-gate break;
38007c478bd9Sstevel@tonic-gate case SADB_EXPIRE:
38017c478bd9Sstevel@tonic-gate /* Should never reach me. */
3802f4b3ec61Sdh sadb_pfkey_error(espstack->esp_pfkey_q, mp, EOPNOTSUPP,
3803f4b3ec61Sdh diagnostic, ksi->ks_in_serial);
38047c478bd9Sstevel@tonic-gate break;
38057c478bd9Sstevel@tonic-gate default:
3806f4b3ec61Sdh sadb_pfkey_error(espstack->esp_pfkey_q, mp, EINVAL,
38077c478bd9Sstevel@tonic-gate SADB_X_DIAGNOSTIC_UNKNOWN_MSG, ksi->ks_in_serial);
38087c478bd9Sstevel@tonic-gate break;
38097c478bd9Sstevel@tonic-gate }
38107c478bd9Sstevel@tonic-gate }
38117c478bd9Sstevel@tonic-gate
38127c478bd9Sstevel@tonic-gate /*
38137c478bd9Sstevel@tonic-gate * Handle case where PF_KEY says it can't find a keysock for one of my
38147c478bd9Sstevel@tonic-gate * ACQUIRE messages.
38157c478bd9Sstevel@tonic-gate */
38167c478bd9Sstevel@tonic-gate static void
3817f4b3ec61Sdh esp_keysock_no_socket(mblk_t *mp, ipsecesp_stack_t *espstack)
38187c478bd9Sstevel@tonic-gate {
38197c478bd9Sstevel@tonic-gate sadb_msg_t *samsg;
38207c478bd9Sstevel@tonic-gate keysock_out_err_t *kse = (keysock_out_err_t *)mp->b_rptr;
38217c478bd9Sstevel@tonic-gate
38227c478bd9Sstevel@tonic-gate if (mp->b_cont == NULL) {
38237c478bd9Sstevel@tonic-gate freemsg(mp);
38247c478bd9Sstevel@tonic-gate return;
38257c478bd9Sstevel@tonic-gate }
38267c478bd9Sstevel@tonic-gate samsg = (sadb_msg_t *)mp->b_cont->b_rptr;
38277c478bd9Sstevel@tonic-gate
38287c478bd9Sstevel@tonic-gate /*
38297c478bd9Sstevel@tonic-gate * If keysock can't find any registered, delete the acquire record
38307c478bd9Sstevel@tonic-gate * immediately, and handle errors.
38317c478bd9Sstevel@tonic-gate */
38327c478bd9Sstevel@tonic-gate if (samsg->sadb_msg_type == SADB_ACQUIRE) {
38337c478bd9Sstevel@tonic-gate samsg->sadb_msg_errno = kse->ks_err_errno;
38347c478bd9Sstevel@tonic-gate samsg->sadb_msg_len = SADB_8TO64(sizeof (*samsg));
38357c478bd9Sstevel@tonic-gate /*
3836bd670b35SErik Nordmark * Use the write-side of the esp_pfkey_q
38377c478bd9Sstevel@tonic-gate */
3838f4b3ec61Sdh sadb_in_acquire(samsg, &espstack->esp_sadb,
3839f4b3ec61Sdh WR(espstack->esp_pfkey_q), espstack->ipsecesp_netstack);
38407c478bd9Sstevel@tonic-gate }
38417c478bd9Sstevel@tonic-gate
38427c478bd9Sstevel@tonic-gate freemsg(mp);
38437c478bd9Sstevel@tonic-gate }
38447c478bd9Sstevel@tonic-gate
38459c451ec7SToomas Soome /*
38469c451ec7SToomas Soome * ESP module read put routine.
38479c451ec7SToomas Soome */
38489c451ec7SToomas Soome static int
38499c451ec7SToomas Soome ipsecesp_rput(queue_t *q, mblk_t *mp)
38509c451ec7SToomas Soome {
38519c451ec7SToomas Soome putnext(q, mp);
38529c451ec7SToomas Soome return (0);
38539c451ec7SToomas Soome }
38549c451ec7SToomas Soome
38557c478bd9Sstevel@tonic-gate /*
38567c478bd9Sstevel@tonic-gate * ESP module write put routine.
38577c478bd9Sstevel@tonic-gate */
38589c451ec7SToomas Soome static int
38597c478bd9Sstevel@tonic-gate ipsecesp_wput(queue_t *q, mblk_t *mp)
38607c478bd9Sstevel@tonic-gate {
38617c478bd9Sstevel@tonic-gate ipsec_info_t *ii;
38627c478bd9Sstevel@tonic-gate struct iocblk *iocp;
3863f4b3ec61Sdh ipsecesp_stack_t *espstack = (ipsecesp_stack_t *)q->q_ptr;
38647c478bd9Sstevel@tonic-gate
3865f4b3ec61Sdh esp3dbg(espstack, ("In esp_wput().\n"));
38667c478bd9Sstevel@tonic-gate
38677c478bd9Sstevel@tonic-gate /* NOTE: Each case must take care of freeing or passing mp. */
38687c478bd9Sstevel@tonic-gate switch (mp->b_datap->db_type) {
38697c478bd9Sstevel@tonic-gate case M_CTL:
38707c478bd9Sstevel@tonic-gate if ((mp->b_wptr - mp->b_rptr) < sizeof (ipsec_info_t)) {
38717c478bd9Sstevel@tonic-gate /* Not big enough message. */
38727c478bd9Sstevel@tonic-gate freemsg(mp);
38737c478bd9Sstevel@tonic-gate break;
38747c478bd9Sstevel@tonic-gate }
38757c478bd9Sstevel@tonic-gate ii = (ipsec_info_t *)mp->b_rptr;
38767c478bd9Sstevel@tonic-gate
38777c478bd9Sstevel@tonic-gate switch (ii->ipsec_info_type) {
38787c478bd9Sstevel@tonic-gate case KEYSOCK_OUT_ERR:
3879f4b3ec61Sdh esp1dbg(espstack, ("Got KEYSOCK_OUT_ERR message.\n"));
3880f4b3ec61Sdh esp_keysock_no_socket(mp, espstack);
38817c478bd9Sstevel@tonic-gate break;
38827c478bd9Sstevel@tonic-gate case KEYSOCK_IN:
3883f4b3ec61Sdh ESP_BUMP_STAT(espstack, keysock_in);
3884f4b3ec61Sdh esp3dbg(espstack, ("Got KEYSOCK_IN message.\n"));
38857c478bd9Sstevel@tonic-gate
38868810c16bSdanmcd /* Parse the message. */
3887f4b3ec61Sdh esp_parse_pfkey(mp, espstack);
38887c478bd9Sstevel@tonic-gate break;
38897c478bd9Sstevel@tonic-gate case KEYSOCK_HELLO:
3890f4b3ec61Sdh sadb_keysock_hello(&espstack->esp_pfkey_q, q, mp,
3891f4b3ec61Sdh esp_ager, (void *)espstack, &espstack->esp_event,
3892f4b3ec61Sdh SADB_SATYPE_ESP);
38937c478bd9Sstevel@tonic-gate break;
38947c478bd9Sstevel@tonic-gate default:
3895f4b3ec61Sdh esp2dbg(espstack, ("Got M_CTL from above of 0x%x.\n",
38967c478bd9Sstevel@tonic-gate ii->ipsec_info_type));
38977c478bd9Sstevel@tonic-gate freemsg(mp);
38987c478bd9Sstevel@tonic-gate break;
38997c478bd9Sstevel@tonic-gate }
39007c478bd9Sstevel@tonic-gate break;
39017c478bd9Sstevel@tonic-gate case M_IOCTL:
39027c478bd9Sstevel@tonic-gate iocp = (struct iocblk *)mp->b_rptr;
39037c478bd9Sstevel@tonic-gate switch (iocp->ioc_cmd) {
39047c478bd9Sstevel@tonic-gate case ND_SET:
39057c478bd9Sstevel@tonic-gate case ND_GET:
3906f4b3ec61Sdh if (nd_getset(q, espstack->ipsecesp_g_nd, mp)) {
39077c478bd9Sstevel@tonic-gate qreply(q, mp);
39089c451ec7SToomas Soome return (0);
39097c478bd9Sstevel@tonic-gate } else {
39107c478bd9Sstevel@tonic-gate iocp->ioc_error = ENOENT;
39117c478bd9Sstevel@tonic-gate }
39127c478bd9Sstevel@tonic-gate /* FALLTHRU */
39137c478bd9Sstevel@tonic-gate default:
39147c478bd9Sstevel@tonic-gate /* We really don't support any other ioctls, do we? */
39157c478bd9Sstevel@tonic-gate
39167c478bd9Sstevel@tonic-gate /* Return EINVAL */
39177c478bd9Sstevel@tonic-gate if (iocp->ioc_error != ENOENT)
39187c478bd9Sstevel@tonic-gate iocp->ioc_error = EINVAL;
39197c478bd9Sstevel@tonic-gate iocp->ioc_count = 0;
39207c478bd9Sstevel@tonic-gate mp->b_datap->db_type = M_IOCACK;
39217c478bd9Sstevel@tonic-gate qreply(q, mp);
39229c451ec7SToomas Soome return (0);
39237c478bd9Sstevel@tonic-gate }
39247c478bd9Sstevel@tonic-gate default:
3925f4b3ec61Sdh esp3dbg(espstack,
3926f4b3ec61Sdh ("Got default message, type %d, passing to IP.\n",
39277c478bd9Sstevel@tonic-gate mp->b_datap->db_type));
39287c478bd9Sstevel@tonic-gate putnext(q, mp);
39297c478bd9Sstevel@tonic-gate }
39309c451ec7SToomas Soome return (0);
39317c478bd9Sstevel@tonic-gate }
39327c478bd9Sstevel@tonic-gate
39337c478bd9Sstevel@tonic-gate /*
39347c478bd9Sstevel@tonic-gate * Wrapper to allow IP to trigger an ESP association failure message
39357c478bd9Sstevel@tonic-gate * during inbound SA selection.
39367c478bd9Sstevel@tonic-gate */
39377c478bd9Sstevel@tonic-gate void
39387c478bd9Sstevel@tonic-gate ipsecesp_in_assocfailure(mblk_t *mp, char level, ushort_t sl, char *fmt,
3939bd670b35SErik Nordmark uint32_t spi, void *addr, int af, ip_recv_attr_t *ira)
39407c478bd9Sstevel@tonic-gate {
3941bd670b35SErik Nordmark netstack_t *ns = ira->ira_ill->ill_ipst->ips_netstack;
3942bd670b35SErik Nordmark ipsecesp_stack_t *espstack = ns->netstack_ipsecesp;
3943bd670b35SErik Nordmark ipsec_stack_t *ipss = ns->netstack_ipsec;
3944f4b3ec61Sdh
3945f4b3ec61Sdh if (espstack->ipsecesp_log_unknown_spi) {
39467c478bd9Sstevel@tonic-gate ipsec_assocfailure(info.mi_idnum, 0, level, sl, fmt, spi,
3947f4b3ec61Sdh addr, af, espstack->ipsecesp_netstack);
39487c478bd9Sstevel@tonic-gate }
39497c478bd9Sstevel@tonic-gate
3950bd670b35SErik Nordmark ip_drop_packet(mp, B_TRUE, ira->ira_ill,
3951f4b3ec61Sdh DROPPER(ipss, ipds_esp_no_sa),
3952f4b3ec61Sdh &espstack->esp_dropper);
39537c478bd9Sstevel@tonic-gate }
39547c478bd9Sstevel@tonic-gate
39557c478bd9Sstevel@tonic-gate /*
39567c478bd9Sstevel@tonic-gate * Initialize the ESP input and output processing functions.
39577c478bd9Sstevel@tonic-gate */
39587c478bd9Sstevel@tonic-gate void
39597c478bd9Sstevel@tonic-gate ipsecesp_init_funcs(ipsa_t *sa)
39607c478bd9Sstevel@tonic-gate {
39617c478bd9Sstevel@tonic-gate if (sa->ipsa_output_func == NULL)
39627c478bd9Sstevel@tonic-gate sa->ipsa_output_func = esp_outbound;
39637c478bd9Sstevel@tonic-gate if (sa->ipsa_input_func == NULL)
39647c478bd9Sstevel@tonic-gate sa->ipsa_input_func = esp_inbound;
39657c478bd9Sstevel@tonic-gate }
3966