xref: /illumos-gate/usr/src/uts/common/inet/ip/sadb.c (revision cd434274)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5bffb04cfSmarkfen  * Common Development and Distribution License (the "License").
6bffb04cfSmarkfen  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22930af642SDan McDonald  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
2469e71331SBayard Bell  * Copyright (c) 2012 Nexenta Systems, Inc. All rights reserved.
25351128adSJason King  * Copyright (c) 2018 Joyent, Inc.
263580e26cSDan McDonald  * Copyright 2022 MNX Cloud, Inc.
277c478bd9Sstevel@tonic-gate  */
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate #include <sys/types.h>
307c478bd9Sstevel@tonic-gate #include <sys/stream.h>
317c478bd9Sstevel@tonic-gate #include <sys/stropts.h>
32de8c4a14SErik Nordmark #include <sys/strsubr.h>
3338d95a78Smarkfen #include <sys/errno.h>
347c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
357c478bd9Sstevel@tonic-gate #include <sys/debug.h>
367c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
377c478bd9Sstevel@tonic-gate #include <sys/stream.h>
387c478bd9Sstevel@tonic-gate #include <sys/strlog.h>
397c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
407c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
417c478bd9Sstevel@tonic-gate #include <sys/tihdr.h>
427c478bd9Sstevel@tonic-gate #include <sys/atomic.h>
437c478bd9Sstevel@tonic-gate #include <sys/socket.h>
447c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
457c478bd9Sstevel@tonic-gate #include <sys/crypto/common.h>
467c478bd9Sstevel@tonic-gate #include <sys/crypto/api.h>
477c478bd9Sstevel@tonic-gate #include <sys/zone.h>
487c478bd9Sstevel@tonic-gate #include <netinet/in.h>
497c478bd9Sstevel@tonic-gate #include <net/if.h>
507c478bd9Sstevel@tonic-gate #include <net/pfkeyv2.h>
51628b0c67SMark Fenwick #include <net/pfpolicy.h>
527c478bd9Sstevel@tonic-gate #include <inet/common.h>
537c478bd9Sstevel@tonic-gate #include <netinet/ip6.h>
547c478bd9Sstevel@tonic-gate #include <inet/ip.h>
558810c16bSdanmcd #include <inet/ip_ire.h>
567c478bd9Sstevel@tonic-gate #include <inet/ip6.h>
577c478bd9Sstevel@tonic-gate #include <inet/ipsec_info.h>
587c478bd9Sstevel@tonic-gate #include <inet/tcp.h>
597c478bd9Sstevel@tonic-gate #include <inet/sadb.h>
60f4b3ec61Sdh #include <inet/ipsec_impl.h>
617c478bd9Sstevel@tonic-gate #include <inet/ipsecah.h>
627c478bd9Sstevel@tonic-gate #include <inet/ipsecesp.h>
637c478bd9Sstevel@tonic-gate #include <sys/random.h>
647c478bd9Sstevel@tonic-gate #include <sys/dlpi.h>
655d3b8cb7SBill Sommerfeld #include <sys/strsun.h>
665d3b8cb7SBill Sommerfeld #include <sys/strsubr.h>
677c478bd9Sstevel@tonic-gate #include <inet/ip_if.h>
687c478bd9Sstevel@tonic-gate #include <inet/ipdrop.h>
697c478bd9Sstevel@tonic-gate #include <inet/ipclassifier.h>
707c478bd9Sstevel@tonic-gate #include <inet/sctp_ip.h>
715d3b8cb7SBill Sommerfeld #include <sys/tsol/tnet.h>
727c478bd9Sstevel@tonic-gate 
737c478bd9Sstevel@tonic-gate /*
747c478bd9Sstevel@tonic-gate  * This source file contains Security Association Database (SADB) common
757c478bd9Sstevel@tonic-gate  * routines.  They are linked in with the AH module.  Since AH has no chance
767c478bd9Sstevel@tonic-gate  * of falling under export control, it was safe to link it in there.
777c478bd9Sstevel@tonic-gate  */
787c478bd9Sstevel@tonic-gate 
79b7daf799SDan McDonald static uint8_t *sadb_action_to_ecomb(uint8_t *, uint8_t *, ipsec_action_t *,
805d3b8cb7SBill Sommerfeld     netstack_t *);
81bd670b35SErik Nordmark static ipsa_t *sadb_torch_assoc(isaf_t *, ipsa_t *);
82f4b3ec61Sdh static void sadb_destroy_acqlist(iacqf_t **, uint_t, boolean_t,
83f4b3ec61Sdh 			    netstack_t *);
84f4b3ec61Sdh static void sadb_destroy(sadb_t *, netstack_t *);
858810c16bSdanmcd static mblk_t *sadb_sa2msg(ipsa_t *, sadb_msg_t *);
86bd670b35SErik Nordmark static ts_label_t *sadb_label_from_sens(sadb_sens_t *, uint64_t *);
877c478bd9Sstevel@tonic-gate 
888810c16bSdanmcd static time_t sadb_add_time(time_t, uint64_t);
8938d95a78Smarkfen static void lifetime_fuzz(ipsa_t *);
9038d95a78Smarkfen static void age_pair_peer_list(templist_t *, sadb_t *, boolean_t);
915d3b8cb7SBill Sommerfeld static int get_ipsa_pair(ipsa_query_t *, ipsap_t *, int *);
925d3b8cb7SBill Sommerfeld static void init_ipsa_pair(ipsap_t *);
935d3b8cb7SBill Sommerfeld static void destroy_ipsa_pair(ipsap_t *);
945d3b8cb7SBill Sommerfeld static int update_pairing(ipsap_t *, ipsa_query_t *, keysock_in_t *, int *);
959c2c14abSThejaswini Singarajipura static void ipsa_set_replay(ipsa_t *ipsa, uint32_t offset);
969c2c14abSThejaswini Singarajipura 
97bffb04cfSmarkfen /*
98bffb04cfSmarkfen  * ipsacq_maxpackets is defined here to make it tunable
99bffb04cfSmarkfen  * from /etc/system.
100bffb04cfSmarkfen  */
101bffb04cfSmarkfen extern uint64_t ipsacq_maxpackets;
102bffb04cfSmarkfen 
1037c478bd9Sstevel@tonic-gate #define	SET_EXPIRE(sa, delta, exp) {				\
1047c478bd9Sstevel@tonic-gate 	if (((sa)->ipsa_ ## delta) != 0) {				\
1057c478bd9Sstevel@tonic-gate 		(sa)->ipsa_ ## exp = sadb_add_time((sa)->ipsa_addtime,	\
1067c478bd9Sstevel@tonic-gate 			(sa)->ipsa_ ## delta);				\
1077c478bd9Sstevel@tonic-gate 	}								\
1087c478bd9Sstevel@tonic-gate }
1097c478bd9Sstevel@tonic-gate 
1107c478bd9Sstevel@tonic-gate #define	UPDATE_EXPIRE(sa, delta, exp) {					\
1117c478bd9Sstevel@tonic-gate 	if (((sa)->ipsa_ ## delta) != 0) {				\
1127c478bd9Sstevel@tonic-gate 		time_t tmp = sadb_add_time((sa)->ipsa_usetime,		\
1137c478bd9Sstevel@tonic-gate 			(sa)->ipsa_ ## delta);				\
1147c478bd9Sstevel@tonic-gate 		if (((sa)->ipsa_ ## exp) == 0)				\
1157c478bd9Sstevel@tonic-gate 			(sa)->ipsa_ ## exp = tmp;			\
1167c478bd9Sstevel@tonic-gate 		else							\
117a23b3b1bSToomas Soome 			(sa)->ipsa_ ## exp =				\
118a23b3b1bSToomas Soome 			    MIN((sa)->ipsa_ ## exp, tmp);		\
1197c478bd9Sstevel@tonic-gate 	}								\
1207c478bd9Sstevel@tonic-gate }
1217c478bd9Sstevel@tonic-gate 
1227c478bd9Sstevel@tonic-gate 
1237c478bd9Sstevel@tonic-gate /* wrap the macro so we can pass it as a function pointer */
1247c478bd9Sstevel@tonic-gate void
sadb_sa_refrele(void * target)1257c478bd9Sstevel@tonic-gate sadb_sa_refrele(void *target)
1267c478bd9Sstevel@tonic-gate {
1277c478bd9Sstevel@tonic-gate 	IPSA_REFRELE(((ipsa_t *)target));
1287c478bd9Sstevel@tonic-gate }
1297c478bd9Sstevel@tonic-gate 
1307c478bd9Sstevel@tonic-gate /*
1317c478bd9Sstevel@tonic-gate  * We presume that sizeof (long) == sizeof (time_t) and that time_t is
1327c478bd9Sstevel@tonic-gate  * a signed type.
1337c478bd9Sstevel@tonic-gate  */
1347c478bd9Sstevel@tonic-gate #define	TIME_MAX LONG_MAX
1357c478bd9Sstevel@tonic-gate 
1367c478bd9Sstevel@tonic-gate /*
1377c478bd9Sstevel@tonic-gate  * PF_KEY gives us lifetimes in uint64_t seconds.  We presume that
1387c478bd9Sstevel@tonic-gate  * time_t is defined to be a signed type with the same range as
1397c478bd9Sstevel@tonic-gate  * "long".  On ILP32 systems, we thus run the risk of wrapping around
1407c478bd9Sstevel@tonic-gate  * at end of time, as well as "overwrapping" the clock back around
1417c478bd9Sstevel@tonic-gate  * into a seemingly valid but incorrect future date earlier than the
1427c478bd9Sstevel@tonic-gate  * desired expiration.
1437c478bd9Sstevel@tonic-gate  *
1447c478bd9Sstevel@tonic-gate  * In order to avoid odd behavior (either negative lifetimes or loss
1457c478bd9Sstevel@tonic-gate  * of high order bits) when someone asks for bizarrely long SA
1467c478bd9Sstevel@tonic-gate  * lifetimes, we do a saturating add for expire times.
1477c478bd9Sstevel@tonic-gate  *
1487c478bd9Sstevel@tonic-gate  * We presume that ILP32 systems will be past end of support life when
1497c478bd9Sstevel@tonic-gate  * the 32-bit time_t overflows (a dangerous assumption, mind you..).
1507c478bd9Sstevel@tonic-gate  *
1517c478bd9Sstevel@tonic-gate  * On LP64, 2^64 seconds are about 5.8e11 years, at which point we
1527c478bd9Sstevel@tonic-gate  * will hopefully have figured out clever ways to avoid the use of
1537c478bd9Sstevel@tonic-gate  * fixed-sized integers in computation.
1547c478bd9Sstevel@tonic-gate  */
1557c478bd9Sstevel@tonic-gate static time_t
sadb_add_time(time_t base,uint64_t delta)1567c478bd9Sstevel@tonic-gate sadb_add_time(time_t base, uint64_t delta)
1577c478bd9Sstevel@tonic-gate {
1587c478bd9Sstevel@tonic-gate 	/*
1597c478bd9Sstevel@tonic-gate 	 * Clip delta to the maximum possible time_t value to
1607c478bd9Sstevel@tonic-gate 	 * prevent "overwrapping" back into a shorter-than-desired
1617c478bd9Sstevel@tonic-gate 	 * future time.
1627c478bd9Sstevel@tonic-gate 	 */
1637c478bd9Sstevel@tonic-gate 	if (delta > TIME_MAX)
1647c478bd9Sstevel@tonic-gate 		delta = TIME_MAX;
1657c478bd9Sstevel@tonic-gate 
166a23b3b1bSToomas Soome 	if (base > 0) {
167a23b3b1bSToomas Soome 		if (TIME_MAX - base < delta)
168a23b3b1bSToomas Soome 			return (TIME_MAX);	/* Overflow */
169a23b3b1bSToomas Soome 	}
170a23b3b1bSToomas Soome 	return (base + delta);
1717c478bd9Sstevel@tonic-gate }
1727c478bd9Sstevel@tonic-gate 
1737c478bd9Sstevel@tonic-gate /*
1747c478bd9Sstevel@tonic-gate  * Callers of this function have already created a working security
1757c478bd9Sstevel@tonic-gate  * association, and have found the appropriate table & hash chain.  All this
1767c478bd9Sstevel@tonic-gate  * function does is check duplicates, and insert the SA.  The caller needs to
1777c478bd9Sstevel@tonic-gate  * hold the hash bucket lock and increment the refcnt before insertion.
1787c478bd9Sstevel@tonic-gate  *
1797c478bd9Sstevel@tonic-gate  * Return 0 if success, EEXIST if collision.
1807c478bd9Sstevel@tonic-gate  */
181437220cdSdanmcd #define	SA_UNIQUE_MATCH(sa1, sa2) \
182437220cdSdanmcd 	(((sa1)->ipsa_unique_id & (sa1)->ipsa_unique_mask) == \
183437220cdSdanmcd 	((sa2)->ipsa_unique_id & (sa2)->ipsa_unique_mask))
184437220cdSdanmcd 
1857c478bd9Sstevel@tonic-gate int
sadb_insertassoc(ipsa_t * ipsa,isaf_t * bucket)1867c478bd9Sstevel@tonic-gate sadb_insertassoc(ipsa_t *ipsa, isaf_t *bucket)
1877c478bd9Sstevel@tonic-gate {
1887c478bd9Sstevel@tonic-gate 	ipsa_t **ptpn = NULL;
1897c478bd9Sstevel@tonic-gate 	ipsa_t *walker;
1907c478bd9Sstevel@tonic-gate 	boolean_t unspecsrc;
1917c478bd9Sstevel@tonic-gate 
1927c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&bucket->isaf_lock));
1937c478bd9Sstevel@tonic-gate 
1947c478bd9Sstevel@tonic-gate 	unspecsrc = IPSA_IS_ADDR_UNSPEC(ipsa->ipsa_srcaddr, ipsa->ipsa_addrfam);
1957c478bd9Sstevel@tonic-gate 
1967c478bd9Sstevel@tonic-gate 	walker = bucket->isaf_ipsa;
1977c478bd9Sstevel@tonic-gate 	ASSERT(walker == NULL || ipsa->ipsa_addrfam == walker->ipsa_addrfam);
1987c478bd9Sstevel@tonic-gate 
1997c478bd9Sstevel@tonic-gate 	/*
2007c478bd9Sstevel@tonic-gate 	 * Find insertion point (pointed to with **ptpn).  Insert at the head
2017c478bd9Sstevel@tonic-gate 	 * of the list unless there's an unspecified source address, then
2027c478bd9Sstevel@tonic-gate 	 * insert it after the last SA with a specified source address.
2037c478bd9Sstevel@tonic-gate 	 *
2047c478bd9Sstevel@tonic-gate 	 * BTW, you'll have to walk the whole chain, matching on {DST, SPI}
2057c478bd9Sstevel@tonic-gate 	 * checking for collisions.
2067c478bd9Sstevel@tonic-gate 	 */
2077c478bd9Sstevel@tonic-gate 
2087c478bd9Sstevel@tonic-gate 	while (walker != NULL) {
2097c478bd9Sstevel@tonic-gate 		if (IPSA_ARE_ADDR_EQUAL(walker->ipsa_dstaddr,
2107c478bd9Sstevel@tonic-gate 		    ipsa->ipsa_dstaddr, ipsa->ipsa_addrfam)) {
2117c478bd9Sstevel@tonic-gate 			if (walker->ipsa_spi == ipsa->ipsa_spi)
2127c478bd9Sstevel@tonic-gate 				return (EEXIST);
2137c478bd9Sstevel@tonic-gate 
2147c478bd9Sstevel@tonic-gate 			mutex_enter(&walker->ipsa_lock);
2157c478bd9Sstevel@tonic-gate 			if (ipsa->ipsa_state == IPSA_STATE_MATURE &&
2167c478bd9Sstevel@tonic-gate 			    (walker->ipsa_flags & IPSA_F_USED) &&
217437220cdSdanmcd 			    SA_UNIQUE_MATCH(walker, ipsa)) {
2187c478bd9Sstevel@tonic-gate 				walker->ipsa_flags |= IPSA_F_CINVALID;
2197c478bd9Sstevel@tonic-gate 			}
2207c478bd9Sstevel@tonic-gate 			mutex_exit(&walker->ipsa_lock);
2217c478bd9Sstevel@tonic-gate 		}
2227c478bd9Sstevel@tonic-gate 
2237c478bd9Sstevel@tonic-gate 		if (ptpn == NULL && unspecsrc) {
2247c478bd9Sstevel@tonic-gate 			if (IPSA_IS_ADDR_UNSPEC(walker->ipsa_srcaddr,
2257c478bd9Sstevel@tonic-gate 			    walker->ipsa_addrfam))
2267c478bd9Sstevel@tonic-gate 				ptpn = walker->ipsa_ptpn;
2277c478bd9Sstevel@tonic-gate 			else if (walker->ipsa_next == NULL)
2287c478bd9Sstevel@tonic-gate 				ptpn = &walker->ipsa_next;
2297c478bd9Sstevel@tonic-gate 		}
2307c478bd9Sstevel@tonic-gate 
2317c478bd9Sstevel@tonic-gate 		walker = walker->ipsa_next;
2327c478bd9Sstevel@tonic-gate 	}
2337c478bd9Sstevel@tonic-gate 
2347c478bd9Sstevel@tonic-gate 	if (ptpn == NULL)
2357c478bd9Sstevel@tonic-gate 		ptpn = &bucket->isaf_ipsa;
2367c478bd9Sstevel@tonic-gate 	ipsa->ipsa_next = *ptpn;
2377c478bd9Sstevel@tonic-gate 	ipsa->ipsa_ptpn = ptpn;
2387c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_next != NULL)
2397c478bd9Sstevel@tonic-gate 		ipsa->ipsa_next->ipsa_ptpn = &ipsa->ipsa_next;
2407c478bd9Sstevel@tonic-gate 	*ptpn = ipsa;
2417c478bd9Sstevel@tonic-gate 	ipsa->ipsa_linklock = &bucket->isaf_lock;
2427c478bd9Sstevel@tonic-gate 
2437c478bd9Sstevel@tonic-gate 	return (0);
2447c478bd9Sstevel@tonic-gate }
245437220cdSdanmcd #undef SA_UNIQUE_MATCH
2467c478bd9Sstevel@tonic-gate 
2477c478bd9Sstevel@tonic-gate /*
2487c478bd9Sstevel@tonic-gate  * Free a security association.  Its reference count is 0, which means
2497c478bd9Sstevel@tonic-gate  * I must free it.  The SA must be unlocked and must not be linked into
2507c478bd9Sstevel@tonic-gate  * any fanout list.
2517c478bd9Sstevel@tonic-gate  */
2527c478bd9Sstevel@tonic-gate static void
sadb_freeassoc(ipsa_t * ipsa)2537c478bd9Sstevel@tonic-gate sadb_freeassoc(ipsa_t *ipsa)
2547c478bd9Sstevel@tonic-gate {
255f4b3ec61Sdh 	ipsec_stack_t	*ipss = ipsa->ipsa_netstack->netstack_ipsec;
256bd670b35SErik Nordmark 	mblk_t		*asyncmp, *mp;
257f4b3ec61Sdh 
258f4b3ec61Sdh 	ASSERT(ipss != NULL);
2590c0328cdSBill Sommerfeld 	ASSERT(MUTEX_NOT_HELD(&ipsa->ipsa_lock));
2607c478bd9Sstevel@tonic-gate 	ASSERT(ipsa->ipsa_refcnt == 0);
2617c478bd9Sstevel@tonic-gate 	ASSERT(ipsa->ipsa_next == NULL);
2627c478bd9Sstevel@tonic-gate 	ASSERT(ipsa->ipsa_ptpn == NULL);
2637c478bd9Sstevel@tonic-gate 
264bd670b35SErik Nordmark 
265bd670b35SErik Nordmark 	asyncmp = sadb_clear_lpkt(ipsa);
266bd670b35SErik Nordmark 	if (asyncmp != NULL) {
267bd670b35SErik Nordmark 		mp = ip_recv_attr_free_mblk(asyncmp);
268bd670b35SErik Nordmark 		ip_drop_packet(mp, B_TRUE, NULL,
269bd670b35SErik Nordmark 		    DROPPER(ipss, ipds_sadb_inlarval_timeout),
270bd670b35SErik Nordmark 		    &ipss->ipsec_sadb_dropper);
271bd670b35SErik Nordmark 	}
272a14de6c8SDan McDonald 	mutex_enter(&ipsa->ipsa_lock);
2735d3b8cb7SBill Sommerfeld 
274bd670b35SErik Nordmark 	if (ipsa->ipsa_tsl != NULL) {
275bd670b35SErik Nordmark 		label_rele(ipsa->ipsa_tsl);
276bd670b35SErik Nordmark 		ipsa->ipsa_tsl = NULL;
2775d3b8cb7SBill Sommerfeld 	}
2785d3b8cb7SBill Sommerfeld 
279bd670b35SErik Nordmark 	if (ipsa->ipsa_otsl != NULL) {
280bd670b35SErik Nordmark 		label_rele(ipsa->ipsa_otsl);
281bd670b35SErik Nordmark 		ipsa->ipsa_otsl = NULL;
2825d3b8cb7SBill Sommerfeld 	}
2835d3b8cb7SBill Sommerfeld 
2847c478bd9Sstevel@tonic-gate 	ipsec_destroy_ctx_tmpl(ipsa, IPSEC_ALG_AUTH);
2857c478bd9Sstevel@tonic-gate 	ipsec_destroy_ctx_tmpl(ipsa, IPSEC_ALG_ENCR);
2867c478bd9Sstevel@tonic-gate 	mutex_exit(&ipsa->ipsa_lock);
2877c478bd9Sstevel@tonic-gate 
2887c478bd9Sstevel@tonic-gate 	/* bzero() these fields for paranoia's sake. */
2897c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_authkey != NULL) {
2907c478bd9Sstevel@tonic-gate 		bzero(ipsa->ipsa_authkey, ipsa->ipsa_authkeylen);
2917c478bd9Sstevel@tonic-gate 		kmem_free(ipsa->ipsa_authkey, ipsa->ipsa_authkeylen);
2927c478bd9Sstevel@tonic-gate 	}
2937c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_encrkey != NULL) {
2947c478bd9Sstevel@tonic-gate 		bzero(ipsa->ipsa_encrkey, ipsa->ipsa_encrkeylen);
2957c478bd9Sstevel@tonic-gate 		kmem_free(ipsa->ipsa_encrkey, ipsa->ipsa_encrkeylen);
2967c478bd9Sstevel@tonic-gate 	}
297628b0c67SMark Fenwick 	if (ipsa->ipsa_nonce_buf != NULL) {
298628b0c67SMark Fenwick 		bzero(ipsa->ipsa_nonce_buf, sizeof (ipsec_nonce_t));
299628b0c67SMark Fenwick 		kmem_free(ipsa->ipsa_nonce_buf, sizeof (ipsec_nonce_t));
300628b0c67SMark Fenwick 	}
3017c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_src_cid != NULL) {
3027c478bd9Sstevel@tonic-gate 		IPSID_REFRELE(ipsa->ipsa_src_cid);
3037c478bd9Sstevel@tonic-gate 	}
3047c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_dst_cid != NULL) {
3057c478bd9Sstevel@tonic-gate 		IPSID_REFRELE(ipsa->ipsa_dst_cid);
3067c478bd9Sstevel@tonic-gate 	}
307628b0c67SMark Fenwick 	if (ipsa->ipsa_emech.cm_param != NULL)
308628b0c67SMark Fenwick 		kmem_free(ipsa->ipsa_emech.cm_param,
309628b0c67SMark Fenwick 		    ipsa->ipsa_emech.cm_param_len);
3107c478bd9Sstevel@tonic-gate 
3117c478bd9Sstevel@tonic-gate 	mutex_destroy(&ipsa->ipsa_lock);
3127c478bd9Sstevel@tonic-gate 	kmem_free(ipsa, sizeof (*ipsa));
3137c478bd9Sstevel@tonic-gate }
3147c478bd9Sstevel@tonic-gate 
3157c478bd9Sstevel@tonic-gate /*
3167c478bd9Sstevel@tonic-gate  * Unlink a security association from a hash bucket.  Assume the hash bucket
3177c478bd9Sstevel@tonic-gate  * lock is held, but the association's lock is not.
3187c478bd9Sstevel@tonic-gate  *
3197c478bd9Sstevel@tonic-gate  * Note that we do not bump the bucket's generation number here because
3207c478bd9Sstevel@tonic-gate  * we might not be making a visible change to the set of visible SA's.
3217c478bd9Sstevel@tonic-gate  * All callers MUST bump the bucket's generation number before they unlock
3227c478bd9Sstevel@tonic-gate  * the bucket if they use sadb_unlinkassoc to permanetly remove an SA which
3237c478bd9Sstevel@tonic-gate  * was present in the bucket at the time it was locked.
3247c478bd9Sstevel@tonic-gate  */
3257c478bd9Sstevel@tonic-gate void
sadb_unlinkassoc(ipsa_t * ipsa)3267c478bd9Sstevel@tonic-gate sadb_unlinkassoc(ipsa_t *ipsa)
3277c478bd9Sstevel@tonic-gate {
3287c478bd9Sstevel@tonic-gate 	ASSERT(ipsa->ipsa_linklock != NULL);
3297c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(ipsa->ipsa_linklock));
3307c478bd9Sstevel@tonic-gate 
3313580e26cSDan McDonald 	/* Sometimes someone beats us here with the same SA. Check now. */
3323580e26cSDan McDonald 	if (ipsa->ipsa_ptpn == NULL)
3333580e26cSDan McDonald 		return;
3343580e26cSDan McDonald 
3357c478bd9Sstevel@tonic-gate 	/* These fields are protected by the link lock. */
3367c478bd9Sstevel@tonic-gate 	*(ipsa->ipsa_ptpn) = ipsa->ipsa_next;
3377c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_next != NULL) {
3387c478bd9Sstevel@tonic-gate 		ipsa->ipsa_next->ipsa_ptpn = ipsa->ipsa_ptpn;
3397c478bd9Sstevel@tonic-gate 		ipsa->ipsa_next = NULL;
3407c478bd9Sstevel@tonic-gate 	}
3417c478bd9Sstevel@tonic-gate 	ipsa->ipsa_ptpn = NULL;
3427c478bd9Sstevel@tonic-gate 
3437c478bd9Sstevel@tonic-gate 	/* This may destroy the SA. */
3447c478bd9Sstevel@tonic-gate 	IPSA_REFRELE(ipsa);
3457c478bd9Sstevel@tonic-gate }
3467c478bd9Sstevel@tonic-gate 
3479c2c14abSThejaswini Singarajipura void
sadb_delete_cluster(ipsa_t * assoc)3489c2c14abSThejaswini Singarajipura sadb_delete_cluster(ipsa_t *assoc)
3499c2c14abSThejaswini Singarajipura {
3509c2c14abSThejaswini Singarajipura 	uint8_t protocol;
3519c2c14abSThejaswini Singarajipura 
3529c2c14abSThejaswini Singarajipura 	if (cl_inet_deletespi &&
3539c2c14abSThejaswini Singarajipura 	    ((assoc->ipsa_state == IPSA_STATE_LARVAL) ||
3549c2c14abSThejaswini Singarajipura 	    (assoc->ipsa_state == IPSA_STATE_MATURE))) {
3559c2c14abSThejaswini Singarajipura 		protocol = (assoc->ipsa_type == SADB_SATYPE_AH) ?
3569c2c14abSThejaswini Singarajipura 		    IPPROTO_AH : IPPROTO_ESP;
3578e4b770fSLu Huafeng 		cl_inet_deletespi(assoc->ipsa_netstack->netstack_stackid,
3588e4b770fSLu Huafeng 		    protocol, assoc->ipsa_spi, NULL);
3599c2c14abSThejaswini Singarajipura 	}
3609c2c14abSThejaswini Singarajipura }
3619c2c14abSThejaswini Singarajipura 
3627c478bd9Sstevel@tonic-gate /*
3637c478bd9Sstevel@tonic-gate  * Create a larval security association with the specified SPI.	 All other
3647c478bd9Sstevel@tonic-gate  * fields are zeroed.
3657c478bd9Sstevel@tonic-gate  */
3667c478bd9Sstevel@tonic-gate static ipsa_t *
sadb_makelarvalassoc(uint32_t spi,uint32_t * src,uint32_t * dst,int addrfam,netstack_t * ns)367f4b3ec61Sdh sadb_makelarvalassoc(uint32_t spi, uint32_t *src, uint32_t *dst, int addrfam,
368f4b3ec61Sdh     netstack_t *ns)
3697c478bd9Sstevel@tonic-gate {
3707c478bd9Sstevel@tonic-gate 	ipsa_t *newbie;
3717c478bd9Sstevel@tonic-gate 
3727c478bd9Sstevel@tonic-gate 	/*
3737c478bd9Sstevel@tonic-gate 	 * Allocate...
3747c478bd9Sstevel@tonic-gate 	 */
3757c478bd9Sstevel@tonic-gate 
3767c478bd9Sstevel@tonic-gate 	newbie = (ipsa_t *)kmem_zalloc(sizeof (ipsa_t), KM_NOSLEEP);
3777c478bd9Sstevel@tonic-gate 	if (newbie == NULL) {
3787c478bd9Sstevel@tonic-gate 		/* Can't make new larval SA. */
3797c478bd9Sstevel@tonic-gate 		return (NULL);
3807c478bd9Sstevel@tonic-gate 	}
3817c478bd9Sstevel@tonic-gate 
3827c478bd9Sstevel@tonic-gate 	/* Assigned requested SPI, assume caller does SPI allocation magic. */
3837c478bd9Sstevel@tonic-gate 	newbie->ipsa_spi = spi;
384f4b3ec61Sdh 	newbie->ipsa_netstack = ns;	/* No netstack_hold */
3857c478bd9Sstevel@tonic-gate 
3867c478bd9Sstevel@tonic-gate 	/*
3877c478bd9Sstevel@tonic-gate 	 * Copy addresses...
3887c478bd9Sstevel@tonic-gate 	 */
3897c478bd9Sstevel@tonic-gate 
3907c478bd9Sstevel@tonic-gate 	IPSA_COPY_ADDR(newbie->ipsa_srcaddr, src, addrfam);
3917c478bd9Sstevel@tonic-gate 	IPSA_COPY_ADDR(newbie->ipsa_dstaddr, dst, addrfam);
3927c478bd9Sstevel@tonic-gate 
3937c478bd9Sstevel@tonic-gate 	newbie->ipsa_addrfam = addrfam;
3947c478bd9Sstevel@tonic-gate 
3957c478bd9Sstevel@tonic-gate 	/*
3967c478bd9Sstevel@tonic-gate 	 * Set common initialization values, including refcnt.
3977c478bd9Sstevel@tonic-gate 	 */
3987c478bd9Sstevel@tonic-gate 	mutex_init(&newbie->ipsa_lock, NULL, MUTEX_DEFAULT, NULL);
3997c478bd9Sstevel@tonic-gate 	newbie->ipsa_state = IPSA_STATE_LARVAL;
4007c478bd9Sstevel@tonic-gate 	newbie->ipsa_refcnt = 1;
4017c478bd9Sstevel@tonic-gate 	newbie->ipsa_freefunc = sadb_freeassoc;
4027c478bd9Sstevel@tonic-gate 
4037c478bd9Sstevel@tonic-gate 	/*
4047c478bd9Sstevel@tonic-gate 	 * There aren't a lot of other common initialization values, as
4057c478bd9Sstevel@tonic-gate 	 * they are copied in from the PF_KEY message.
4067c478bd9Sstevel@tonic-gate 	 */
4077c478bd9Sstevel@tonic-gate 
4087c478bd9Sstevel@tonic-gate 	return (newbie);
4097c478bd9Sstevel@tonic-gate }
4107c478bd9Sstevel@tonic-gate 
4117c478bd9Sstevel@tonic-gate /*
4127c478bd9Sstevel@tonic-gate  * Call me to initialize a security association fanout.
4137c478bd9Sstevel@tonic-gate  */
414fb87b5d2Ssommerfe static int
sadb_init_fanout(isaf_t ** tablep,uint_t size,int kmflag)415fb87b5d2Ssommerfe sadb_init_fanout(isaf_t **tablep, uint_t size, int kmflag)
4167c478bd9Sstevel@tonic-gate {
4177c478bd9Sstevel@tonic-gate 	isaf_t *table;
4187c478bd9Sstevel@tonic-gate 	int i;
4197c478bd9Sstevel@tonic-gate 
420fb87b5d2Ssommerfe 	table = (isaf_t *)kmem_alloc(size * sizeof (*table), kmflag);
4217c478bd9Sstevel@tonic-gate 	*tablep = table;
4227c478bd9Sstevel@tonic-gate 
423fb87b5d2Ssommerfe 	if (table == NULL)
424fb87b5d2Ssommerfe 		return (ENOMEM);
425fb87b5d2Ssommerfe 
426fb87b5d2Ssommerfe 	for (i = 0; i < size; i++) {
4277c478bd9Sstevel@tonic-gate 		mutex_init(&(table[i].isaf_lock), NULL, MUTEX_DEFAULT, NULL);
4287c478bd9Sstevel@tonic-gate 		table[i].isaf_ipsa = NULL;
4297c478bd9Sstevel@tonic-gate 		table[i].isaf_gen = 0;
4307c478bd9Sstevel@tonic-gate 	}
431fb87b5d2Ssommerfe 
432fb87b5d2Ssommerfe 	return (0);
4337c478bd9Sstevel@tonic-gate }
4347c478bd9Sstevel@tonic-gate 
435fb87b5d2Ssommerfe /*
436fb87b5d2Ssommerfe  * Call me to initialize an acquire fanout
437fb87b5d2Ssommerfe  */
438fb87b5d2Ssommerfe static int
sadb_init_acfanout(iacqf_t ** tablep,uint_t size,int kmflag)439fb87b5d2Ssommerfe sadb_init_acfanout(iacqf_t **tablep, uint_t size, int kmflag)
4407c478bd9Sstevel@tonic-gate {
4417c478bd9Sstevel@tonic-gate 	iacqf_t *table;
4427c478bd9Sstevel@tonic-gate 	int i;
4437c478bd9Sstevel@tonic-gate 
444fb87b5d2Ssommerfe 	table = (iacqf_t *)kmem_alloc(size * sizeof (*table), kmflag);
4457c478bd9Sstevel@tonic-gate 	*tablep = table;
4467c478bd9Sstevel@tonic-gate 
447fb87b5d2Ssommerfe 	if (table == NULL)
448fb87b5d2Ssommerfe 		return (ENOMEM);
449fb87b5d2Ssommerfe 
450fb87b5d2Ssommerfe 	for (i = 0; i < size; i++) {
4517c478bd9Sstevel@tonic-gate 		mutex_init(&(table[i].iacqf_lock), NULL, MUTEX_DEFAULT, NULL);
4527c478bd9Sstevel@tonic-gate 		table[i].iacqf_ipsacq = NULL;
4537c478bd9Sstevel@tonic-gate 	}
454fb87b5d2Ssommerfe 
455fb87b5d2Ssommerfe 	return (0);
4567c478bd9Sstevel@tonic-gate }
4577c478bd9Sstevel@tonic-gate 
4587c478bd9Sstevel@tonic-gate /*
459fb87b5d2Ssommerfe  * Attempt to initialize an SADB instance.  On failure, return ENOMEM;
460fb87b5d2Ssommerfe  * caller must clean up partial allocations.
4617c478bd9Sstevel@tonic-gate  */
462fb87b5d2Ssommerfe static int
sadb_init_trial(sadb_t * sp,uint_t size,int kmflag)463fb87b5d2Ssommerfe sadb_init_trial(sadb_t *sp, uint_t size, int kmflag)
464fb87b5d2Ssommerfe {
465fb87b5d2Ssommerfe 	ASSERT(sp->sdb_of == NULL);
466fb87b5d2Ssommerfe 	ASSERT(sp->sdb_if == NULL);
467fb87b5d2Ssommerfe 	ASSERT(sp->sdb_acq == NULL);
468fb87b5d2Ssommerfe 
469fb87b5d2Ssommerfe 	sp->sdb_hashsize = size;
470fb87b5d2Ssommerfe 	if (sadb_init_fanout(&sp->sdb_of, size, kmflag) != 0)
471fb87b5d2Ssommerfe 		return (ENOMEM);
472fb87b5d2Ssommerfe 	if (sadb_init_fanout(&sp->sdb_if, size, kmflag) != 0)
473fb87b5d2Ssommerfe 		return (ENOMEM);
474fb87b5d2Ssommerfe 	if (sadb_init_acfanout(&sp->sdb_acq, size, kmflag) != 0)
475fb87b5d2Ssommerfe 		return (ENOMEM);
4767c478bd9Sstevel@tonic-gate 
477fb87b5d2Ssommerfe 	return (0);
478fb87b5d2Ssommerfe }
479fb87b5d2Ssommerfe 
480fb87b5d2Ssommerfe /*
481fb87b5d2Ssommerfe  * Call me to initialize an SADB instance; fall back to default size on failure.
482fb87b5d2Ssommerfe  */
4837c478bd9Sstevel@tonic-gate static void
sadb_init(const char * name,sadb_t * sp,uint_t size,uint_t ver,netstack_t * ns)484f4b3ec61Sdh sadb_init(const char *name, sadb_t *sp, uint_t size, uint_t ver,
485f4b3ec61Sdh     netstack_t *ns)
4867c478bd9Sstevel@tonic-gate {
487fb87b5d2Ssommerfe 	ASSERT(sp->sdb_of == NULL);
488fb87b5d2Ssommerfe 	ASSERT(sp->sdb_if == NULL);
489fb87b5d2Ssommerfe 	ASSERT(sp->sdb_acq == NULL);
490fb87b5d2Ssommerfe 
491fb87b5d2Ssommerfe 	if (size < IPSEC_DEFAULT_HASH_SIZE)
492fb87b5d2Ssommerfe 		size = IPSEC_DEFAULT_HASH_SIZE;
493fb87b5d2Ssommerfe 
494fb87b5d2Ssommerfe 	if (sadb_init_trial(sp, size, KM_NOSLEEP) != 0) {
495fb87b5d2Ssommerfe 
496fb87b5d2Ssommerfe 		cmn_err(CE_WARN,
497fb87b5d2Ssommerfe 		    "Unable to allocate %u entry IPv%u %s SADB hash table",
498fb87b5d2Ssommerfe 		    size, ver, name);
499fb87b5d2Ssommerfe 
500f4b3ec61Sdh 		sadb_destroy(sp, ns);
501fb87b5d2Ssommerfe 		size = IPSEC_DEFAULT_HASH_SIZE;
502fb87b5d2Ssommerfe 		cmn_err(CE_WARN, "Falling back to %d entries", size);
503fb87b5d2Ssommerfe 		(void) sadb_init_trial(sp, size, KM_SLEEP);
504fb87b5d2Ssommerfe 	}
5057c478bd9Sstevel@tonic-gate }
5067c478bd9Sstevel@tonic-gate 
507fb87b5d2Ssommerfe 
5087c478bd9Sstevel@tonic-gate /*
5097c478bd9Sstevel@tonic-gate  * Initialize an SADB-pair.
5107c478bd9Sstevel@tonic-gate  */
5117c478bd9Sstevel@tonic-gate void
sadbp_init(const char * name,sadbp_t * sp,int type,int size,netstack_t * ns)512f4b3ec61Sdh sadbp_init(const char *name, sadbp_t *sp, int type, int size, netstack_t *ns)
5137c478bd9Sstevel@tonic-gate {
514f4b3ec61Sdh 	sadb_init(name, &sp->s_v4, size, 4, ns);
515f4b3ec61Sdh 	sadb_init(name, &sp->s_v6, size, 6, ns);
5167c478bd9Sstevel@tonic-gate 
5177c478bd9Sstevel@tonic-gate 	sp->s_satype = type;
5187c478bd9Sstevel@tonic-gate 
5197c478bd9Sstevel@tonic-gate 	ASSERT((type == SADB_SATYPE_AH) || (type == SADB_SATYPE_ESP));
520f4b3ec61Sdh 	if (type == SADB_SATYPE_AH) {
521f4b3ec61Sdh 		ipsec_stack_t	*ipss = ns->netstack_ipsec;
522f4b3ec61Sdh 
523f4b3ec61Sdh 		ip_drop_register(&ipss->ipsec_sadb_dropper, "IPsec SADB");
52472bd9b6bSdanmcd 		sp->s_addflags = AH_ADD_SETTABLE_FLAGS;
52572bd9b6bSdanmcd 		sp->s_updateflags = AH_UPDATE_SETTABLE_FLAGS;
52672bd9b6bSdanmcd 	} else {
52772bd9b6bSdanmcd 		sp->s_addflags = ESP_ADD_SETTABLE_FLAGS;
52872bd9b6bSdanmcd 		sp->s_updateflags = ESP_UPDATE_SETTABLE_FLAGS;
529f4b3ec61Sdh 	}
5307c478bd9Sstevel@tonic-gate }
5317c478bd9Sstevel@tonic-gate 
5327c478bd9Sstevel@tonic-gate /*
5337c478bd9Sstevel@tonic-gate  * Deliver a single SADB_DUMP message representing a single SA.  This is
5347c478bd9Sstevel@tonic-gate  * called many times by sadb_dump().
5357c478bd9Sstevel@tonic-gate  *
5367c478bd9Sstevel@tonic-gate  * If the return value of this is ENOBUFS (not the same as ENOMEM), then
5377c478bd9Sstevel@tonic-gate  * the caller should take that as a hint that dupb() on the "original answer"
5387c478bd9Sstevel@tonic-gate  * failed, and that perhaps the caller should try again with a copyb()ed
5397c478bd9Sstevel@tonic-gate  * "original answer".
5407c478bd9Sstevel@tonic-gate  */
5417c478bd9Sstevel@tonic-gate static int
sadb_dump_deliver(queue_t * pfkey_q,mblk_t * original_answer,ipsa_t * ipsa,sadb_msg_t * samsg)5427c478bd9Sstevel@tonic-gate sadb_dump_deliver(queue_t *pfkey_q, mblk_t *original_answer, ipsa_t *ipsa,
5437c478bd9Sstevel@tonic-gate     sadb_msg_t *samsg)
5447c478bd9Sstevel@tonic-gate {
5457c478bd9Sstevel@tonic-gate 	mblk_t *answer;
5467c478bd9Sstevel@tonic-gate 
5477c478bd9Sstevel@tonic-gate 	answer = dupb(original_answer);
5487c478bd9Sstevel@tonic-gate 	if (answer == NULL)
5497c478bd9Sstevel@tonic-gate 		return (ENOBUFS);
5507c478bd9Sstevel@tonic-gate 	answer->b_cont = sadb_sa2msg(ipsa, samsg);
5517c478bd9Sstevel@tonic-gate 	if (answer->b_cont == NULL) {
5527c478bd9Sstevel@tonic-gate 		freeb(answer);
5537c478bd9Sstevel@tonic-gate 		return (ENOMEM);
5547c478bd9Sstevel@tonic-gate 	}
5557c478bd9Sstevel@tonic-gate 
5567c478bd9Sstevel@tonic-gate 	/* Just do a putnext, and let keysock deal with flow control. */
5577c478bd9Sstevel@tonic-gate 	putnext(pfkey_q, answer);
5587c478bd9Sstevel@tonic-gate 	return (0);
5597c478bd9Sstevel@tonic-gate }
5607c478bd9Sstevel@tonic-gate 
5617c478bd9Sstevel@tonic-gate /*
5627c478bd9Sstevel@tonic-gate  * Common function to allocate and prepare a keysock_out_t M_CTL message.
5637c478bd9Sstevel@tonic-gate  */
5647c478bd9Sstevel@tonic-gate mblk_t *
sadb_keysock_out(minor_t serial)5657c478bd9Sstevel@tonic-gate sadb_keysock_out(minor_t serial)
5667c478bd9Sstevel@tonic-gate {
5677c478bd9Sstevel@tonic-gate 	mblk_t *mp;
5687c478bd9Sstevel@tonic-gate 	keysock_out_t *kso;
5697c478bd9Sstevel@tonic-gate 
5707c478bd9Sstevel@tonic-gate 	mp = allocb(sizeof (ipsec_info_t), BPRI_HI);
5717c478bd9Sstevel@tonic-gate 	if (mp != NULL) {
5727c478bd9Sstevel@tonic-gate 		mp->b_datap->db_type = M_CTL;
5737c478bd9Sstevel@tonic-gate 		mp->b_wptr += sizeof (ipsec_info_t);
5747c478bd9Sstevel@tonic-gate 		kso = (keysock_out_t *)mp->b_rptr;
5757c478bd9Sstevel@tonic-gate 		kso->ks_out_type = KEYSOCK_OUT;
5767c478bd9Sstevel@tonic-gate 		kso->ks_out_len = sizeof (*kso);
5777c478bd9Sstevel@tonic-gate 		kso->ks_out_serial = serial;
5787c478bd9Sstevel@tonic-gate 	}
5797c478bd9Sstevel@tonic-gate 
5807c478bd9Sstevel@tonic-gate 	return (mp);
5817c478bd9Sstevel@tonic-gate }
5827c478bd9Sstevel@tonic-gate 
5837c478bd9Sstevel@tonic-gate /*
5847c478bd9Sstevel@tonic-gate  * Perform an SADB_DUMP, spewing out every SA in an array of SA fanouts
5857c478bd9Sstevel@tonic-gate  * to keysock.
5867c478bd9Sstevel@tonic-gate  */
5877c478bd9Sstevel@tonic-gate static int
sadb_dump_fanout(queue_t * pfkey_q,mblk_t * mp,minor_t serial,isaf_t * fanout,int num_entries,boolean_t do_peers,time_t active_time)5887c478bd9Sstevel@tonic-gate sadb_dump_fanout(queue_t *pfkey_q, mblk_t *mp, minor_t serial, isaf_t *fanout,
5899c2c14abSThejaswini Singarajipura     int num_entries, boolean_t do_peers, time_t active_time)
5907c478bd9Sstevel@tonic-gate {
5917c478bd9Sstevel@tonic-gate 	int i, error = 0;
5927c478bd9Sstevel@tonic-gate 	mblk_t *original_answer;
5937c478bd9Sstevel@tonic-gate 	ipsa_t *walker;
5947c478bd9Sstevel@tonic-gate 	sadb_msg_t *samsg;
5959c2c14abSThejaswini Singarajipura 	time_t	current;
5967c478bd9Sstevel@tonic-gate 
5977c478bd9Sstevel@tonic-gate 	/*
5987c478bd9Sstevel@tonic-gate 	 * For each IPSA hash bucket do:
5997c478bd9Sstevel@tonic-gate 	 *	- Hold the mutex
6007c478bd9Sstevel@tonic-gate 	 *	- Walk each entry, doing an sadb_dump_deliver() on it.
6017c478bd9Sstevel@tonic-gate 	 */
6027c478bd9Sstevel@tonic-gate 	ASSERT(mp->b_cont != NULL);
6037c478bd9Sstevel@tonic-gate 	samsg = (sadb_msg_t *)mp->b_cont->b_rptr;
6047c478bd9Sstevel@tonic-gate 
6057c478bd9Sstevel@tonic-gate 	original_answer = sadb_keysock_out(serial);
6067c478bd9Sstevel@tonic-gate 	if (original_answer == NULL)
6077c478bd9Sstevel@tonic-gate 		return (ENOMEM);
6087c478bd9Sstevel@tonic-gate 
6099c2c14abSThejaswini Singarajipura 	current = gethrestime_sec();
6107c478bd9Sstevel@tonic-gate 	for (i = 0; i < num_entries; i++) {
6117c478bd9Sstevel@tonic-gate 		mutex_enter(&fanout[i].isaf_lock);
6127c478bd9Sstevel@tonic-gate 		for (walker = fanout[i].isaf_ipsa; walker != NULL;
6137c478bd9Sstevel@tonic-gate 		    walker = walker->ipsa_next) {
6147c478bd9Sstevel@tonic-gate 			if (!do_peers && walker->ipsa_haspeer)
6157c478bd9Sstevel@tonic-gate 				continue;
6169c2c14abSThejaswini Singarajipura 			if ((active_time != 0) &&
6179c2c14abSThejaswini Singarajipura 			    ((current - walker->ipsa_lastuse) > active_time))
6189c2c14abSThejaswini Singarajipura 				continue;
6197c478bd9Sstevel@tonic-gate 			error = sadb_dump_deliver(pfkey_q, original_answer,
6207c478bd9Sstevel@tonic-gate 			    walker, samsg);
6217c478bd9Sstevel@tonic-gate 			if (error == ENOBUFS) {
6227c478bd9Sstevel@tonic-gate 				mblk_t *new_original_answer;
6237c478bd9Sstevel@tonic-gate 
6247c478bd9Sstevel@tonic-gate 				/* Ran out of dupb's.  Try a copyb. */
6257c478bd9Sstevel@tonic-gate 				new_original_answer = copyb(original_answer);
6267c478bd9Sstevel@tonic-gate 				if (new_original_answer == NULL) {
6277c478bd9Sstevel@tonic-gate 					error = ENOMEM;
6287c478bd9Sstevel@tonic-gate 				} else {
6297c478bd9Sstevel@tonic-gate 					freeb(original_answer);
6307c478bd9Sstevel@tonic-gate 					original_answer = new_original_answer;
6317c478bd9Sstevel@tonic-gate 					error = sadb_dump_deliver(pfkey_q,
6327c478bd9Sstevel@tonic-gate 					    original_answer, walker, samsg);
6337c478bd9Sstevel@tonic-gate 				}
6347c478bd9Sstevel@tonic-gate 			}
6357c478bd9Sstevel@tonic-gate 			if (error != 0)
6367c478bd9Sstevel@tonic-gate 				break;	/* out of for loop. */
6377c478bd9Sstevel@tonic-gate 		}
6387c478bd9Sstevel@tonic-gate 		mutex_exit(&fanout[i].isaf_lock);
6397c478bd9Sstevel@tonic-gate 		if (error != 0)
6407c478bd9Sstevel@tonic-gate 			break;	/* out of for loop. */
6417c478bd9Sstevel@tonic-gate 	}
6427c478bd9Sstevel@tonic-gate 
6437c478bd9Sstevel@tonic-gate 	freeb(original_answer);
6447c478bd9Sstevel@tonic-gate 	return (error);
6457c478bd9Sstevel@tonic-gate }
6467c478bd9Sstevel@tonic-gate 
6477c478bd9Sstevel@tonic-gate /*
6487c478bd9Sstevel@tonic-gate  * Dump an entire SADB; outbound first, then inbound.
6497c478bd9Sstevel@tonic-gate  */
6507c478bd9Sstevel@tonic-gate 
6517c478bd9Sstevel@tonic-gate int
sadb_dump(queue_t * pfkey_q,mblk_t * mp,keysock_in_t * ksi,sadb_t * sp)6529c2c14abSThejaswini Singarajipura sadb_dump(queue_t *pfkey_q, mblk_t *mp, keysock_in_t *ksi, sadb_t *sp)
6537c478bd9Sstevel@tonic-gate {
6547c478bd9Sstevel@tonic-gate 	int error;
6559c2c14abSThejaswini Singarajipura 	time_t	active_time = 0;
6569c2c14abSThejaswini Singarajipura 	sadb_x_edump_t	*edump =
6579c2c14abSThejaswini Singarajipura 	    (sadb_x_edump_t *)ksi->ks_in_extv[SADB_X_EXT_EDUMP];
6589c2c14abSThejaswini Singarajipura 
6599c2c14abSThejaswini Singarajipura 	if (edump != NULL) {
6609c2c14abSThejaswini Singarajipura 		active_time = edump->sadb_x_edump_timeout;
6619c2c14abSThejaswini Singarajipura 	}
6627c478bd9Sstevel@tonic-gate 
6637c478bd9Sstevel@tonic-gate 	/* Dump outbound */
6649c2c14abSThejaswini Singarajipura 	error = sadb_dump_fanout(pfkey_q, mp, ksi->ks_in_serial, sp->sdb_of,
6659c2c14abSThejaswini Singarajipura 	    sp->sdb_hashsize, B_TRUE, active_time);
6667c478bd9Sstevel@tonic-gate 	if (error)
6677c478bd9Sstevel@tonic-gate 		return (error);
6687c478bd9Sstevel@tonic-gate 
6697c478bd9Sstevel@tonic-gate 	/* Dump inbound */
6709c2c14abSThejaswini Singarajipura 	return sadb_dump_fanout(pfkey_q, mp, ksi->ks_in_serial, sp->sdb_if,
6719c2c14abSThejaswini Singarajipura 	    sp->sdb_hashsize, B_FALSE, active_time);
6727c478bd9Sstevel@tonic-gate }
6737c478bd9Sstevel@tonic-gate 
6747c478bd9Sstevel@tonic-gate /*
6757c478bd9Sstevel@tonic-gate  * Generic sadb table walker.
6767c478bd9Sstevel@tonic-gate  *
6777c478bd9Sstevel@tonic-gate  * Call "walkfn" for each SA in each bucket in "table"; pass the
6787c478bd9Sstevel@tonic-gate  * bucket, the entry and "cookie" to the callback function.
6797c478bd9Sstevel@tonic-gate  * Take care to ensure that walkfn can delete the SA without screwing
6807c478bd9Sstevel@tonic-gate  * up our traverse.
6817c478bd9Sstevel@tonic-gate  *
6827c478bd9Sstevel@tonic-gate  * The bucket is locked for the duration of the callback, both so that the
6837c478bd9Sstevel@tonic-gate  * callback can just call sadb_unlinkassoc() when it wants to delete something,
6847c478bd9Sstevel@tonic-gate  * and so that no new entries are added while we're walking the list.
6857c478bd9Sstevel@tonic-gate  */
6867c478bd9Sstevel@tonic-gate static void
sadb_walker(isaf_t * table,uint_t numentries,void (* walkfn)(isaf_t * head,ipsa_t * entry,void * cookie),void * cookie)6877c478bd9Sstevel@tonic-gate sadb_walker(isaf_t *table, uint_t numentries,
6887c478bd9Sstevel@tonic-gate     void (*walkfn)(isaf_t *head, ipsa_t *entry, void *cookie),
6897c478bd9Sstevel@tonic-gate     void *cookie)
6907c478bd9Sstevel@tonic-gate {
6917c478bd9Sstevel@tonic-gate 	int i;
6927c478bd9Sstevel@tonic-gate 	for (i = 0; i < numentries; i++) {
6937c478bd9Sstevel@tonic-gate 		ipsa_t *entry, *next;
6947c478bd9Sstevel@tonic-gate 
6957c478bd9Sstevel@tonic-gate 		mutex_enter(&table[i].isaf_lock);
6967c478bd9Sstevel@tonic-gate 
6977c478bd9Sstevel@tonic-gate 		for (entry = table[i].isaf_ipsa; entry != NULL;
6987c478bd9Sstevel@tonic-gate 		    entry = next) {
6997c478bd9Sstevel@tonic-gate 			next = entry->ipsa_next;
7007c478bd9Sstevel@tonic-gate 			(*walkfn)(&table[i], entry, cookie);
7017c478bd9Sstevel@tonic-gate 		}
7027c478bd9Sstevel@tonic-gate 		mutex_exit(&table[i].isaf_lock);
7037c478bd9Sstevel@tonic-gate 	}
7047c478bd9Sstevel@tonic-gate }
7057c478bd9Sstevel@tonic-gate 
7067c478bd9Sstevel@tonic-gate /*
7077c478bd9Sstevel@tonic-gate  * Call me to free up a security association fanout.  Use the forever
7087c478bd9Sstevel@tonic-gate  * variable to indicate freeing up the SAs (forever == B_FALSE, e.g.
7097c478bd9Sstevel@tonic-gate  * an SADB_FLUSH message), or destroying everything (forever == B_TRUE,
7107c478bd9Sstevel@tonic-gate  * when a module is unloaded).
7117c478bd9Sstevel@tonic-gate  */
7127c478bd9Sstevel@tonic-gate static void
sadb_destroyer(isaf_t ** tablep,uint_t numentries,boolean_t forever,boolean_t inbound)7139c2c14abSThejaswini Singarajipura sadb_destroyer(isaf_t **tablep, uint_t numentries, boolean_t forever,
7149c2c14abSThejaswini Singarajipura     boolean_t inbound)
7157c478bd9Sstevel@tonic-gate {
7167c478bd9Sstevel@tonic-gate 	int i;
717fb87b5d2Ssommerfe 	isaf_t *table = *tablep;
7189c2c14abSThejaswini Singarajipura 	uint8_t protocol;
7198e4b770fSLu Huafeng 	ipsa_t *sa;
7208e4b770fSLu Huafeng 	netstackid_t sid;
721fb87b5d2Ssommerfe 
722fb87b5d2Ssommerfe 	if (table == NULL)
723fb87b5d2Ssommerfe 		return;
7247c478bd9Sstevel@tonic-gate 
7257c478bd9Sstevel@tonic-gate 	for (i = 0; i < numentries; i++) {
7267c478bd9Sstevel@tonic-gate 		mutex_enter(&table[i].isaf_lock);
7278e4b770fSLu Huafeng 		while ((sa = table[i].isaf_ipsa) != NULL) {
7289c2c14abSThejaswini Singarajipura 			if (inbound && cl_inet_deletespi &&
7298e4b770fSLu Huafeng 			    (sa->ipsa_state != IPSA_STATE_ACTIVE_ELSEWHERE) &&
7308e4b770fSLu Huafeng 			    (sa->ipsa_state != IPSA_STATE_IDLE)) {
7318e4b770fSLu Huafeng 				protocol = (sa->ipsa_type == SADB_SATYPE_AH) ?
7328e4b770fSLu Huafeng 				    IPPROTO_AH : IPPROTO_ESP;
7338e4b770fSLu Huafeng 				sid = sa->ipsa_netstack->netstack_stackid;
7348e4b770fSLu Huafeng 				cl_inet_deletespi(sid, protocol, sa->ipsa_spi,
7358e4b770fSLu Huafeng 				    NULL);
7369c2c14abSThejaswini Singarajipura 			}
7378e4b770fSLu Huafeng 			sadb_unlinkassoc(sa);
7389c2c14abSThejaswini Singarajipura 		}
7397c478bd9Sstevel@tonic-gate 		table[i].isaf_gen++;
7407c478bd9Sstevel@tonic-gate 		mutex_exit(&table[i].isaf_lock);
7417c478bd9Sstevel@tonic-gate 		if (forever)
7427c478bd9Sstevel@tonic-gate 			mutex_destroy(&(table[i].isaf_lock));
7437c478bd9Sstevel@tonic-gate 	}
7447c478bd9Sstevel@tonic-gate 
745fb87b5d2Ssommerfe 	if (forever) {
746fb87b5d2Ssommerfe 		*tablep = NULL;
7477c478bd9Sstevel@tonic-gate 		kmem_free(table, numentries * sizeof (*table));
748fb87b5d2Ssommerfe 	}
7497c478bd9Sstevel@tonic-gate }
7507c478bd9Sstevel@tonic-gate 
7517c478bd9Sstevel@tonic-gate /*
7527c478bd9Sstevel@tonic-gate  * Entry points to sadb_destroyer().
7537c478bd9Sstevel@tonic-gate  */
7547c478bd9Sstevel@tonic-gate static void
sadb_flush(sadb_t * sp,netstack_t * ns)755f4b3ec61Sdh sadb_flush(sadb_t *sp, netstack_t *ns)
7567c478bd9Sstevel@tonic-gate {
7577c478bd9Sstevel@tonic-gate 	/*
7587c478bd9Sstevel@tonic-gate 	 * Flush out each bucket, one at a time.  Were it not for keysock's
7597c478bd9Sstevel@tonic-gate 	 * enforcement, there would be a subtlety where I could add on the
7607c478bd9Sstevel@tonic-gate 	 * heels of a flush.  With keysock's enforcement, however, this
7617c478bd9Sstevel@tonic-gate 	 * makes ESP's job easy.
7627c478bd9Sstevel@tonic-gate 	 */
7639c2c14abSThejaswini Singarajipura 	sadb_destroyer(&sp->sdb_of, sp->sdb_hashsize, B_FALSE, B_FALSE);
7649c2c14abSThejaswini Singarajipura 	sadb_destroyer(&sp->sdb_if, sp->sdb_hashsize, B_FALSE, B_TRUE);
7657c478bd9Sstevel@tonic-gate 
7667c478bd9Sstevel@tonic-gate 	/* For each acquire, destroy it; leave the bucket mutex alone. */
767f4b3ec61Sdh 	sadb_destroy_acqlist(&sp->sdb_acq, sp->sdb_hashsize, B_FALSE, ns);
7687c478bd9Sstevel@tonic-gate }
7697c478bd9Sstevel@tonic-gate 
7707c478bd9Sstevel@tonic-gate static void
sadb_destroy(sadb_t * sp,netstack_t * ns)771f4b3ec61Sdh sadb_destroy(sadb_t *sp, netstack_t *ns)
7727c478bd9Sstevel@tonic-gate {
7739c2c14abSThejaswini Singarajipura 	sadb_destroyer(&sp->sdb_of, sp->sdb_hashsize, B_TRUE, B_FALSE);
7749c2c14abSThejaswini Singarajipura 	sadb_destroyer(&sp->sdb_if, sp->sdb_hashsize, B_TRUE, B_TRUE);
7757c478bd9Sstevel@tonic-gate 
7767c478bd9Sstevel@tonic-gate 	/* For each acquire, destroy it, including the bucket mutex. */
777f4b3ec61Sdh 	sadb_destroy_acqlist(&sp->sdb_acq, sp->sdb_hashsize, B_TRUE, ns);
778fb87b5d2Ssommerfe 
779fb87b5d2Ssommerfe 	ASSERT(sp->sdb_of == NULL);
780fb87b5d2Ssommerfe 	ASSERT(sp->sdb_if == NULL);
781fb87b5d2Ssommerfe 	ASSERT(sp->sdb_acq == NULL);
7827c478bd9Sstevel@tonic-gate }
7837c478bd9Sstevel@tonic-gate 
7847c478bd9Sstevel@tonic-gate void
sadbp_flush(sadbp_t * spp,netstack_t * ns)785f4b3ec61Sdh sadbp_flush(sadbp_t *spp, netstack_t *ns)
7867c478bd9Sstevel@tonic-gate {
787f4b3ec61Sdh 	sadb_flush(&spp->s_v4, ns);
788f4b3ec61Sdh 	sadb_flush(&spp->s_v6, ns);
7897c478bd9Sstevel@tonic-gate }
7907c478bd9Sstevel@tonic-gate 
7917c478bd9Sstevel@tonic-gate void
sadbp_destroy(sadbp_t * spp,netstack_t * ns)792f4b3ec61Sdh sadbp_destroy(sadbp_t *spp, netstack_t *ns)
7937c478bd9Sstevel@tonic-gate {
794f4b3ec61Sdh 	sadb_destroy(&spp->s_v4, ns);
795f4b3ec61Sdh 	sadb_destroy(&spp->s_v6, ns);
7967c478bd9Sstevel@tonic-gate 
797f4b3ec61Sdh 	if (spp->s_satype == SADB_SATYPE_AH) {
798f4b3ec61Sdh 		ipsec_stack_t	*ipss = ns->netstack_ipsec;
799f4b3ec61Sdh 
800f4b3ec61Sdh 		ip_drop_unregister(&ipss->ipsec_sadb_dropper);
801f4b3ec61Sdh 	}
8027c478bd9Sstevel@tonic-gate }
8037c478bd9Sstevel@tonic-gate 
8047c478bd9Sstevel@tonic-gate 
8057c478bd9Sstevel@tonic-gate /*
8067c478bd9Sstevel@tonic-gate  * Check hard vs. soft lifetimes.  If there's a reality mismatch (e.g.
8077c478bd9Sstevel@tonic-gate  * soft lifetimes > hard lifetimes) return an appropriate diagnostic for
8087c478bd9Sstevel@tonic-gate  * EINVAL.
8097c478bd9Sstevel@tonic-gate  */
8107c478bd9Sstevel@tonic-gate int
sadb_hardsoftchk(sadb_lifetime_t * hard,sadb_lifetime_t * soft,sadb_lifetime_t * idle)8119c2c14abSThejaswini Singarajipura sadb_hardsoftchk(sadb_lifetime_t *hard, sadb_lifetime_t *soft,
8129c2c14abSThejaswini Singarajipura     sadb_lifetime_t *idle)
8137c478bd9Sstevel@tonic-gate {
8147c478bd9Sstevel@tonic-gate 	if (hard == NULL || soft == NULL)
8157c478bd9Sstevel@tonic-gate 		return (0);
8167c478bd9Sstevel@tonic-gate 
8177c478bd9Sstevel@tonic-gate 	if (hard->sadb_lifetime_allocations != 0 &&
8187c478bd9Sstevel@tonic-gate 	    soft->sadb_lifetime_allocations != 0 &&
8197c478bd9Sstevel@tonic-gate 	    hard->sadb_lifetime_allocations < soft->sadb_lifetime_allocations)
8207c478bd9Sstevel@tonic-gate 		return (SADB_X_DIAGNOSTIC_ALLOC_HSERR);
8217c478bd9Sstevel@tonic-gate 
8227c478bd9Sstevel@tonic-gate 	if (hard->sadb_lifetime_bytes != 0 &&
8237c478bd9Sstevel@tonic-gate 	    soft->sadb_lifetime_bytes != 0 &&
8247c478bd9Sstevel@tonic-gate 	    hard->sadb_lifetime_bytes < soft->sadb_lifetime_bytes)
8257c478bd9Sstevel@tonic-gate 		return (SADB_X_DIAGNOSTIC_BYTES_HSERR);
8267c478bd9Sstevel@tonic-gate 
8277c478bd9Sstevel@tonic-gate 	if (hard->sadb_lifetime_addtime != 0 &&
8287c478bd9Sstevel@tonic-gate 	    soft->sadb_lifetime_addtime != 0 &&
8297c478bd9Sstevel@tonic-gate 	    hard->sadb_lifetime_addtime < soft->sadb_lifetime_addtime)
8307c478bd9Sstevel@tonic-gate 		return (SADB_X_DIAGNOSTIC_ADDTIME_HSERR);
8317c478bd9Sstevel@tonic-gate 
8327c478bd9Sstevel@tonic-gate 	if (hard->sadb_lifetime_usetime != 0 &&
8337c478bd9Sstevel@tonic-gate 	    soft->sadb_lifetime_usetime != 0 &&
8347c478bd9Sstevel@tonic-gate 	    hard->sadb_lifetime_usetime < soft->sadb_lifetime_usetime)
8357c478bd9Sstevel@tonic-gate 		return (SADB_X_DIAGNOSTIC_USETIME_HSERR);
8367c478bd9Sstevel@tonic-gate 
8379c2c14abSThejaswini Singarajipura 	if (idle != NULL) {
8389c2c14abSThejaswini Singarajipura 		if (hard->sadb_lifetime_addtime != 0 &&
8399c2c14abSThejaswini Singarajipura 		    idle->sadb_lifetime_addtime != 0 &&
8409c2c14abSThejaswini Singarajipura 		    hard->sadb_lifetime_addtime < idle->sadb_lifetime_addtime)
8419c2c14abSThejaswini Singarajipura 			return (SADB_X_DIAGNOSTIC_ADDTIME_HSERR);
8429c2c14abSThejaswini Singarajipura 
8439c2c14abSThejaswini Singarajipura 		if (soft->sadb_lifetime_addtime != 0 &&
8449c2c14abSThejaswini Singarajipura 		    idle->sadb_lifetime_addtime != 0 &&
8459c2c14abSThejaswini Singarajipura 		    soft->sadb_lifetime_addtime < idle->sadb_lifetime_addtime)
8469c2c14abSThejaswini Singarajipura 			return (SADB_X_DIAGNOSTIC_ADDTIME_HSERR);
8479c2c14abSThejaswini Singarajipura 
8489c2c14abSThejaswini Singarajipura 		if (hard->sadb_lifetime_usetime != 0 &&
8499c2c14abSThejaswini Singarajipura 		    idle->sadb_lifetime_usetime != 0 &&
8509c2c14abSThejaswini Singarajipura 		    hard->sadb_lifetime_usetime < idle->sadb_lifetime_usetime)
8519c2c14abSThejaswini Singarajipura 			return (SADB_X_DIAGNOSTIC_USETIME_HSERR);
8529c2c14abSThejaswini Singarajipura 
8539c2c14abSThejaswini Singarajipura 		if (soft->sadb_lifetime_usetime != 0 &&
8549c2c14abSThejaswini Singarajipura 		    idle->sadb_lifetime_usetime != 0 &&
8559c2c14abSThejaswini Singarajipura 		    soft->sadb_lifetime_usetime < idle->sadb_lifetime_usetime)
8569c2c14abSThejaswini Singarajipura 			return (SADB_X_DIAGNOSTIC_USETIME_HSERR);
8579c2c14abSThejaswini Singarajipura 	}
8589c2c14abSThejaswini Singarajipura 
8597c478bd9Sstevel@tonic-gate 	return (0);
8607c478bd9Sstevel@tonic-gate }
8617c478bd9Sstevel@tonic-gate 
8625d3b8cb7SBill Sommerfeld /*
8635d3b8cb7SBill Sommerfeld  * Sanity check sensitivity labels.
8645d3b8cb7SBill Sommerfeld  *
8655d3b8cb7SBill Sommerfeld  * For now, just reject labels on unlabeled systems.
8665d3b8cb7SBill Sommerfeld  */
8675d3b8cb7SBill Sommerfeld int
sadb_labelchk(keysock_in_t * ksi)8685d3b8cb7SBill Sommerfeld sadb_labelchk(keysock_in_t *ksi)
8695d3b8cb7SBill Sommerfeld {
8705d3b8cb7SBill Sommerfeld 	if (!is_system_labeled()) {
8715d3b8cb7SBill Sommerfeld 		if (ksi->ks_in_extv[SADB_EXT_SENSITIVITY] != NULL)
8725d3b8cb7SBill Sommerfeld 			return (SADB_X_DIAGNOSTIC_BAD_LABEL);
8735d3b8cb7SBill Sommerfeld 
8745d3b8cb7SBill Sommerfeld 		if (ksi->ks_in_extv[SADB_X_EXT_OUTER_SENS] != NULL)
8755d3b8cb7SBill Sommerfeld 			return (SADB_X_DIAGNOSTIC_BAD_LABEL);
8765d3b8cb7SBill Sommerfeld 	}
8775d3b8cb7SBill Sommerfeld 
8785d3b8cb7SBill Sommerfeld 	return (0);
8795d3b8cb7SBill Sommerfeld }
8805d3b8cb7SBill Sommerfeld 
8817c478bd9Sstevel@tonic-gate /*
8827c478bd9Sstevel@tonic-gate  * Clone a security association for the purposes of inserting a single SA
88338d95a78Smarkfen  * into inbound and outbound tables respectively. This function should only
88438d95a78Smarkfen  * be called from sadb_common_add().
8857c478bd9Sstevel@tonic-gate  */
8867c478bd9Sstevel@tonic-gate static ipsa_t *
sadb_cloneassoc(ipsa_t * ipsa)8877c478bd9Sstevel@tonic-gate sadb_cloneassoc(ipsa_t *ipsa)
8887c478bd9Sstevel@tonic-gate {
8897c478bd9Sstevel@tonic-gate 	ipsa_t *newbie;
8907c478bd9Sstevel@tonic-gate 	boolean_t error = B_FALSE;
8917c478bd9Sstevel@tonic-gate 
8920c0328cdSBill Sommerfeld 	ASSERT(MUTEX_NOT_HELD(&(ipsa->ipsa_lock)));
8937c478bd9Sstevel@tonic-gate 
8947c478bd9Sstevel@tonic-gate 	newbie = kmem_alloc(sizeof (ipsa_t), KM_NOSLEEP);
8957c478bd9Sstevel@tonic-gate 	if (newbie == NULL)
8967c478bd9Sstevel@tonic-gate 		return (NULL);
8977c478bd9Sstevel@tonic-gate 
8987c478bd9Sstevel@tonic-gate 	/* Copy over what we can. */
8997c478bd9Sstevel@tonic-gate 	*newbie = *ipsa;
9007c478bd9Sstevel@tonic-gate 
9017c478bd9Sstevel@tonic-gate 	/* bzero and initialize locks, in case *_init() allocates... */
9027c478bd9Sstevel@tonic-gate 	mutex_init(&newbie->ipsa_lock, NULL, MUTEX_DEFAULT, NULL);
9037c478bd9Sstevel@tonic-gate 
904bd670b35SErik Nordmark 	if (newbie->ipsa_tsl != NULL)
905bd670b35SErik Nordmark 		label_hold(newbie->ipsa_tsl);
9065d3b8cb7SBill Sommerfeld 
907bd670b35SErik Nordmark 	if (newbie->ipsa_otsl != NULL)
908bd670b35SErik Nordmark 		label_hold(newbie->ipsa_otsl);
9095d3b8cb7SBill Sommerfeld 
9107c478bd9Sstevel@tonic-gate 	/*
9117c478bd9Sstevel@tonic-gate 	 * While somewhat dain-bramaged, the most graceful way to
9127c478bd9Sstevel@tonic-gate 	 * recover from errors is to keep plowing through the
9137c478bd9Sstevel@tonic-gate 	 * allocations, and getting what I can.  It's easier to call
9147c478bd9Sstevel@tonic-gate 	 * sadb_freeassoc() on the stillborn clone when all the
9157c478bd9Sstevel@tonic-gate 	 * pointers aren't pointing to the parent's data.
9167c478bd9Sstevel@tonic-gate 	 */
9177c478bd9Sstevel@tonic-gate 
9187c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_authkey != NULL) {
9197c478bd9Sstevel@tonic-gate 		newbie->ipsa_authkey = kmem_alloc(newbie->ipsa_authkeylen,
9207c478bd9Sstevel@tonic-gate 		    KM_NOSLEEP);
9217c478bd9Sstevel@tonic-gate 		if (newbie->ipsa_authkey == NULL) {
9227c478bd9Sstevel@tonic-gate 			error = B_TRUE;
9237c478bd9Sstevel@tonic-gate 		} else {
9247c478bd9Sstevel@tonic-gate 			bcopy(ipsa->ipsa_authkey, newbie->ipsa_authkey,
9257c478bd9Sstevel@tonic-gate 			    newbie->ipsa_authkeylen);
9267c478bd9Sstevel@tonic-gate 
9277c478bd9Sstevel@tonic-gate 			newbie->ipsa_kcfauthkey.ck_data =
9287c478bd9Sstevel@tonic-gate 			    newbie->ipsa_authkey;
9297c478bd9Sstevel@tonic-gate 		}
9307c478bd9Sstevel@tonic-gate 
9317c478bd9Sstevel@tonic-gate 		if (newbie->ipsa_amech.cm_param != NULL) {
9327c478bd9Sstevel@tonic-gate 			newbie->ipsa_amech.cm_param =
9337c478bd9Sstevel@tonic-gate 			    (char *)&newbie->ipsa_mac_len;
9347c478bd9Sstevel@tonic-gate 		}
9357c478bd9Sstevel@tonic-gate 	}
9367c478bd9Sstevel@tonic-gate 
9377c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_encrkey != NULL) {
9387c478bd9Sstevel@tonic-gate 		newbie->ipsa_encrkey = kmem_alloc(newbie->ipsa_encrkeylen,
9397c478bd9Sstevel@tonic-gate 		    KM_NOSLEEP);
9407c478bd9Sstevel@tonic-gate 		if (newbie->ipsa_encrkey == NULL) {
9417c478bd9Sstevel@tonic-gate 			error = B_TRUE;
9427c478bd9Sstevel@tonic-gate 		} else {
9437c478bd9Sstevel@tonic-gate 			bcopy(ipsa->ipsa_encrkey, newbie->ipsa_encrkey,
9447c478bd9Sstevel@tonic-gate 			    newbie->ipsa_encrkeylen);
9457c478bd9Sstevel@tonic-gate 
9467c478bd9Sstevel@tonic-gate 			newbie->ipsa_kcfencrkey.ck_data =
9477c478bd9Sstevel@tonic-gate 			    newbie->ipsa_encrkey;
9487c478bd9Sstevel@tonic-gate 		}
9497c478bd9Sstevel@tonic-gate 	}
9507c478bd9Sstevel@tonic-gate 
9517c478bd9Sstevel@tonic-gate 	newbie->ipsa_authtmpl = NULL;
9527c478bd9Sstevel@tonic-gate 	newbie->ipsa_encrtmpl = NULL;
95338d95a78Smarkfen 	newbie->ipsa_haspeer = B_TRUE;
9547c478bd9Sstevel@tonic-gate 
9557c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_src_cid != NULL) {
9567c478bd9Sstevel@tonic-gate 		newbie->ipsa_src_cid = ipsa->ipsa_src_cid;
9577c478bd9Sstevel@tonic-gate 		IPSID_REFHOLD(ipsa->ipsa_src_cid);
9587c478bd9Sstevel@tonic-gate 	}
9597c478bd9Sstevel@tonic-gate 
9607c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_dst_cid != NULL) {
9617c478bd9Sstevel@tonic-gate 		newbie->ipsa_dst_cid = ipsa->ipsa_dst_cid;
9627c478bd9Sstevel@tonic-gate 		IPSID_REFHOLD(ipsa->ipsa_dst_cid);
9637c478bd9Sstevel@tonic-gate 	}
9647c478bd9Sstevel@tonic-gate 
9657c478bd9Sstevel@tonic-gate 	if (error) {
9667c478bd9Sstevel@tonic-gate 		sadb_freeassoc(newbie);
9677c478bd9Sstevel@tonic-gate 		return (NULL);
9687c478bd9Sstevel@tonic-gate 	}
9697c478bd9Sstevel@tonic-gate 
9707c478bd9Sstevel@tonic-gate 	return (newbie);
9717c478bd9Sstevel@tonic-gate }
9727c478bd9Sstevel@tonic-gate 
9737c478bd9Sstevel@tonic-gate /*
9747c478bd9Sstevel@tonic-gate  * Initialize a SADB address extension at the address specified by addrext.
9757c478bd9Sstevel@tonic-gate  * Return a pointer to the end of the new address extension.
9767c478bd9Sstevel@tonic-gate  */
9777c478bd9Sstevel@tonic-gate static uint8_t *
sadb_make_addr_ext(uint8_t * start,uint8_t * end,uint16_t exttype,sa_family_t af,uint32_t * addr,uint16_t port,uint8_t proto,int prefix)9787c478bd9Sstevel@tonic-gate sadb_make_addr_ext(uint8_t *start, uint8_t *end, uint16_t exttype,
9798810c16bSdanmcd     sa_family_t af, uint32_t *addr, uint16_t port, uint8_t proto, int prefix)
9807c478bd9Sstevel@tonic-gate {
9817c478bd9Sstevel@tonic-gate 	struct sockaddr_in *sin;
9827c478bd9Sstevel@tonic-gate 	struct sockaddr_in6 *sin6;
9837c478bd9Sstevel@tonic-gate 	uint8_t *cur = start;
9847c478bd9Sstevel@tonic-gate 	int addrext_len;
9857c478bd9Sstevel@tonic-gate 	int sin_len;
9867c478bd9Sstevel@tonic-gate 	sadb_address_t *addrext	= (sadb_address_t *)cur;
9877c478bd9Sstevel@tonic-gate 
9887c478bd9Sstevel@tonic-gate 	if (cur == NULL)
9897c478bd9Sstevel@tonic-gate 		return (NULL);
9907c478bd9Sstevel@tonic-gate 
9917c478bd9Sstevel@tonic-gate 	cur += sizeof (*addrext);
9927c478bd9Sstevel@tonic-gate 	if (cur > end)
9937c478bd9Sstevel@tonic-gate 		return (NULL);
9947c478bd9Sstevel@tonic-gate 
9957c478bd9Sstevel@tonic-gate 	addrext->sadb_address_proto = proto;
9968810c16bSdanmcd 	addrext->sadb_address_prefixlen = prefix;
9977c478bd9Sstevel@tonic-gate 	addrext->sadb_address_reserved = 0;
9987c478bd9Sstevel@tonic-gate 	addrext->sadb_address_exttype = exttype;
9997c478bd9Sstevel@tonic-gate 
10007c478bd9Sstevel@tonic-gate 	switch (af) {
10017c478bd9Sstevel@tonic-gate 	case AF_INET:
10027c478bd9Sstevel@tonic-gate 		sin = (struct sockaddr_in *)cur;
10037c478bd9Sstevel@tonic-gate 		sin_len = sizeof (*sin);
10047c478bd9Sstevel@tonic-gate 		cur += sin_len;
10057c478bd9Sstevel@tonic-gate 		if (cur > end)
10067c478bd9Sstevel@tonic-gate 			return (NULL);
10077c478bd9Sstevel@tonic-gate 
10087c478bd9Sstevel@tonic-gate 		sin->sin_family = af;
10097c478bd9Sstevel@tonic-gate 		bzero(sin->sin_zero, sizeof (sin->sin_zero));
10107c478bd9Sstevel@tonic-gate 		sin->sin_port = port;
10117c478bd9Sstevel@tonic-gate 		IPSA_COPY_ADDR(&sin->sin_addr, addr, af);
10127c478bd9Sstevel@tonic-gate 		break;
10137c478bd9Sstevel@tonic-gate 	case AF_INET6:
10147c478bd9Sstevel@tonic-gate 		sin6 = (struct sockaddr_in6 *)cur;
10157c478bd9Sstevel@tonic-gate 		sin_len = sizeof (*sin6);
10167c478bd9Sstevel@tonic-gate 		cur += sin_len;
10177c478bd9Sstevel@tonic-gate 		if (cur > end)
10187c478bd9Sstevel@tonic-gate 			return (NULL);
10197c478bd9Sstevel@tonic-gate 
10207c478bd9Sstevel@tonic-gate 		bzero(sin6, sizeof (*sin6));
10217c478bd9Sstevel@tonic-gate 		sin6->sin6_family = af;
10227c478bd9Sstevel@tonic-gate 		sin6->sin6_port = port;
10237c478bd9Sstevel@tonic-gate 		IPSA_COPY_ADDR(&sin6->sin6_addr, addr, af);
10247c478bd9Sstevel@tonic-gate 		break;
10257c478bd9Sstevel@tonic-gate 	}
10267c478bd9Sstevel@tonic-gate 
10277c478bd9Sstevel@tonic-gate 	addrext_len = roundup(cur - start, sizeof (uint64_t));
10287c478bd9Sstevel@tonic-gate 	addrext->sadb_address_len = SADB_8TO64(addrext_len);
10297c478bd9Sstevel@tonic-gate 
10307c478bd9Sstevel@tonic-gate 	cur = start + addrext_len;
10317c478bd9Sstevel@tonic-gate 	if (cur > end)
10327c478bd9Sstevel@tonic-gate 		cur = NULL;
10337c478bd9Sstevel@tonic-gate 
10347c478bd9Sstevel@tonic-gate 	return (cur);
10357c478bd9Sstevel@tonic-gate }
10367c478bd9Sstevel@tonic-gate 
10377c478bd9Sstevel@tonic-gate /*
10387c478bd9Sstevel@tonic-gate  * Construct a key management cookie extension.
10397c478bd9Sstevel@tonic-gate  */
10407c478bd9Sstevel@tonic-gate 
10417c478bd9Sstevel@tonic-gate static uint8_t *
sadb_make_kmc_ext(uint8_t * cur,uint8_t * end,uint32_t kmp,uint64_t kmc)1042f4a6f97eSDan McDonald sadb_make_kmc_ext(uint8_t *cur, uint8_t *end, uint32_t kmp, uint64_t kmc)
10437c478bd9Sstevel@tonic-gate {
10447c478bd9Sstevel@tonic-gate 	sadb_x_kmc_t *kmcext = (sadb_x_kmc_t *)cur;
10457c478bd9Sstevel@tonic-gate 
10467c478bd9Sstevel@tonic-gate 	if (cur == NULL)
10477c478bd9Sstevel@tonic-gate 		return (NULL);
10487c478bd9Sstevel@tonic-gate 
10497c478bd9Sstevel@tonic-gate 	cur += sizeof (*kmcext);
10507c478bd9Sstevel@tonic-gate 
10517c478bd9Sstevel@tonic-gate 	if (cur > end)
10527c478bd9Sstevel@tonic-gate 		return (NULL);
10537c478bd9Sstevel@tonic-gate 
10547c478bd9Sstevel@tonic-gate 	kmcext->sadb_x_kmc_len = SADB_8TO64(sizeof (*kmcext));
10557c478bd9Sstevel@tonic-gate 	kmcext->sadb_x_kmc_exttype = SADB_X_EXT_KM_COOKIE;
10567c478bd9Sstevel@tonic-gate 	kmcext->sadb_x_kmc_proto = kmp;
1057f4a6f97eSDan McDonald 	kmcext->sadb_x_kmc_cookie64 = kmc;
10587c478bd9Sstevel@tonic-gate 
10597c478bd9Sstevel@tonic-gate 	return (cur);
10607c478bd9Sstevel@tonic-gate }
10617c478bd9Sstevel@tonic-gate 
10627c478bd9Sstevel@tonic-gate /*
10637c478bd9Sstevel@tonic-gate  * Given an original message header with sufficient space following it, and an
10647c478bd9Sstevel@tonic-gate  * SA, construct a full PF_KEY message with all of the relevant extensions.
10657c478bd9Sstevel@tonic-gate  * This is mostly used for SADB_GET, and SADB_DUMP.
10667c478bd9Sstevel@tonic-gate  */
10678810c16bSdanmcd static mblk_t *
sadb_sa2msg(ipsa_t * ipsa,sadb_msg_t * samsg)10687c478bd9Sstevel@tonic-gate sadb_sa2msg(ipsa_t *ipsa, sadb_msg_t *samsg)
10697c478bd9Sstevel@tonic-gate {
10707c478bd9Sstevel@tonic-gate 	int alloclen, addrsize, paddrsize, authsize, encrsize;
10715d3b8cb7SBill Sommerfeld 	int srcidsize, dstidsize, senslen, osenslen;
10727c478bd9Sstevel@tonic-gate 	sa_family_t fam, pfam;	/* Address family for SADB_EXT_ADDRESS */
10737c478bd9Sstevel@tonic-gate 				/* src/dst and proxy sockaddrs. */
10744132743aSToomas Soome 
10754132743aSToomas Soome 	authsize = 0;
10764132743aSToomas Soome 	encrsize = 0;
10774132743aSToomas Soome 	pfam = 0;
10784132743aSToomas Soome 	srcidsize = 0;
10794132743aSToomas Soome 	dstidsize = 0;
10804132743aSToomas Soome 	paddrsize = 0;
10814132743aSToomas Soome 	senslen = 0;
10824132743aSToomas Soome 	osenslen = 0;
10837c478bd9Sstevel@tonic-gate 	/*
10847c478bd9Sstevel@tonic-gate 	 * The following are pointers into the PF_KEY message this PF_KEY
10857c478bd9Sstevel@tonic-gate 	 * message creates.
10867c478bd9Sstevel@tonic-gate 	 */
10877c478bd9Sstevel@tonic-gate 	sadb_msg_t *newsamsg;
10887c478bd9Sstevel@tonic-gate 	sadb_sa_t *assoc;
10897c478bd9Sstevel@tonic-gate 	sadb_lifetime_t *lt;
10907c478bd9Sstevel@tonic-gate 	sadb_key_t *key;
10917c478bd9Sstevel@tonic-gate 	sadb_ident_t *ident;
10927c478bd9Sstevel@tonic-gate 	sadb_sens_t *sens;
10937c478bd9Sstevel@tonic-gate 	sadb_ext_t *walker;	/* For when we need a generic ext. pointer. */
10949c2c14abSThejaswini Singarajipura 	sadb_x_replay_ctr_t *repl_ctr;
109538d95a78Smarkfen 	sadb_x_pair_t *pair_ext;
109638d95a78Smarkfen 
10977c478bd9Sstevel@tonic-gate 	mblk_t *mp;
10987c478bd9Sstevel@tonic-gate 	uint8_t *cur, *end;
10997c478bd9Sstevel@tonic-gate 	/* These indicate the presence of the above extension fields. */
11005d3b8cb7SBill Sommerfeld 	boolean_t soft = B_FALSE, hard = B_FALSE;
11015d3b8cb7SBill Sommerfeld 	boolean_t isrc = B_FALSE, idst = B_FALSE;
11025d3b8cb7SBill Sommerfeld 	boolean_t auth = B_FALSE, encr = B_FALSE;
11035d3b8cb7SBill Sommerfeld 	boolean_t sensinteg = B_FALSE, osensinteg = B_FALSE;
11045d3b8cb7SBill Sommerfeld 	boolean_t srcid = B_FALSE, dstid = B_FALSE;
11059c2c14abSThejaswini Singarajipura 	boolean_t idle;
110638d95a78Smarkfen 	boolean_t paired;
110738d95a78Smarkfen 	uint32_t otherspi;
11087c478bd9Sstevel@tonic-gate 
11097c478bd9Sstevel@tonic-gate 	/* First off, figure out the allocation length for this message. */
11107c478bd9Sstevel@tonic-gate 	/*
11117c478bd9Sstevel@tonic-gate 	 * Constant stuff.  This includes base, SA, address (src, dst),
11127c478bd9Sstevel@tonic-gate 	 * and lifetime (current).
11137c478bd9Sstevel@tonic-gate 	 */
11147c478bd9Sstevel@tonic-gate 	alloclen = sizeof (sadb_msg_t) + sizeof (sadb_sa_t) +
11157c478bd9Sstevel@tonic-gate 	    sizeof (sadb_lifetime_t);
11164132743aSToomas Soome 	otherspi = 0;
11177c478bd9Sstevel@tonic-gate 
11187c478bd9Sstevel@tonic-gate 	fam = ipsa->ipsa_addrfam;
11197c478bd9Sstevel@tonic-gate 	switch (fam) {
11207c478bd9Sstevel@tonic-gate 	case AF_INET:
11217c478bd9Sstevel@tonic-gate 		addrsize = roundup(sizeof (struct sockaddr_in) +
11227c478bd9Sstevel@tonic-gate 		    sizeof (sadb_address_t), sizeof (uint64_t));
11237c478bd9Sstevel@tonic-gate 		break;
11247c478bd9Sstevel@tonic-gate 	case AF_INET6:
11257c478bd9Sstevel@tonic-gate 		addrsize = roundup(sizeof (struct sockaddr_in6) +
11267c478bd9Sstevel@tonic-gate 		    sizeof (sadb_address_t), sizeof (uint64_t));
11277c478bd9Sstevel@tonic-gate 		break;
11287c478bd9Sstevel@tonic-gate 	default:
11297c478bd9Sstevel@tonic-gate 		return (NULL);
11307c478bd9Sstevel@tonic-gate 	}
11317c478bd9Sstevel@tonic-gate 	/*
11327c478bd9Sstevel@tonic-gate 	 * Allocate TWO address extensions, for source and destination.
11337c478bd9Sstevel@tonic-gate 	 * (Thus, the * 2.)
11347c478bd9Sstevel@tonic-gate 	 */
11357c478bd9Sstevel@tonic-gate 	alloclen += addrsize * 2;
11367c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_flags & IPSA_F_NATT_REM)
1137437220cdSdanmcd 		alloclen += addrsize;
11387c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_flags & IPSA_F_NATT_LOC)
1139437220cdSdanmcd 		alloclen += addrsize;
11407c478bd9Sstevel@tonic-gate 
114138d95a78Smarkfen 	if (ipsa->ipsa_flags & IPSA_F_PAIRED) {
114238d95a78Smarkfen 		paired = B_TRUE;
114338d95a78Smarkfen 		alloclen += sizeof (sadb_x_pair_t);
114438d95a78Smarkfen 		otherspi = ipsa->ipsa_otherspi;
114538d95a78Smarkfen 	} else {
114638d95a78Smarkfen 		paired = B_FALSE;
114738d95a78Smarkfen 	}
11487c478bd9Sstevel@tonic-gate 
11497c478bd9Sstevel@tonic-gate 	/* How 'bout other lifetimes? */
11507c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_softaddlt != 0 || ipsa->ipsa_softuselt != 0 ||
11517c478bd9Sstevel@tonic-gate 	    ipsa->ipsa_softbyteslt != 0 || ipsa->ipsa_softalloc != 0) {
11527c478bd9Sstevel@tonic-gate 		alloclen += sizeof (sadb_lifetime_t);
11537c478bd9Sstevel@tonic-gate 		soft = B_TRUE;
11547c478bd9Sstevel@tonic-gate 	}
11557c478bd9Sstevel@tonic-gate 
11567c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_hardaddlt != 0 || ipsa->ipsa_harduselt != 0 ||
11577c478bd9Sstevel@tonic-gate 	    ipsa->ipsa_hardbyteslt != 0 || ipsa->ipsa_hardalloc != 0) {
11587c478bd9Sstevel@tonic-gate 		alloclen += sizeof (sadb_lifetime_t);
11597c478bd9Sstevel@tonic-gate 		hard = B_TRUE;
11607c478bd9Sstevel@tonic-gate 	}
11617c478bd9Sstevel@tonic-gate 
11629c2c14abSThejaswini Singarajipura 	if (ipsa->ipsa_idleaddlt != 0 || ipsa->ipsa_idleuselt != 0) {
11639c2c14abSThejaswini Singarajipura 		alloclen += sizeof (sadb_lifetime_t);
11649c2c14abSThejaswini Singarajipura 		idle = B_TRUE;
11659c2c14abSThejaswini Singarajipura 	} else {
11669c2c14abSThejaswini Singarajipura 		idle = B_FALSE;
11679c2c14abSThejaswini Singarajipura 	}
11689c2c14abSThejaswini Singarajipura 
11698810c16bSdanmcd 	/* Inner addresses. */
11705d3b8cb7SBill Sommerfeld 	if (ipsa->ipsa_innerfam != 0) {
11718810c16bSdanmcd 		pfam = ipsa->ipsa_innerfam;
11727c478bd9Sstevel@tonic-gate 		switch (pfam) {
11737c478bd9Sstevel@tonic-gate 		case AF_INET6:
11747c478bd9Sstevel@tonic-gate 			paddrsize = roundup(sizeof (struct sockaddr_in6) +
11757c478bd9Sstevel@tonic-gate 			    sizeof (sadb_address_t), sizeof (uint64_t));
11767c478bd9Sstevel@tonic-gate 			break;
11777c478bd9Sstevel@tonic-gate 		case AF_INET:
11787c478bd9Sstevel@tonic-gate 			paddrsize = roundup(sizeof (struct sockaddr_in) +
11797c478bd9Sstevel@tonic-gate 			    sizeof (sadb_address_t), sizeof (uint64_t));
11807c478bd9Sstevel@tonic-gate 			break;
11817c478bd9Sstevel@tonic-gate 		default:
11827c478bd9Sstevel@tonic-gate 			cmn_err(CE_PANIC,
11837c478bd9Sstevel@tonic-gate 			    "IPsec SADB: Proxy length failure.\n");
11847c478bd9Sstevel@tonic-gate 			break;
11857c478bd9Sstevel@tonic-gate 		}
11868810c16bSdanmcd 		isrc = B_TRUE;
11878810c16bSdanmcd 		idst = B_TRUE;
11888810c16bSdanmcd 		alloclen += 2 * paddrsize;
11897c478bd9Sstevel@tonic-gate 	}
11907c478bd9Sstevel@tonic-gate 
11917c478bd9Sstevel@tonic-gate 	/* For the following fields, assume that length != 0 ==> stuff */
11927c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_authkeylen != 0) {
11937c478bd9Sstevel@tonic-gate 		authsize = roundup(sizeof (sadb_key_t) + ipsa->ipsa_authkeylen,
11947c478bd9Sstevel@tonic-gate 		    sizeof (uint64_t));
11957c478bd9Sstevel@tonic-gate 		alloclen += authsize;
11967c478bd9Sstevel@tonic-gate 		auth = B_TRUE;
11977c478bd9Sstevel@tonic-gate 	}
11987c478bd9Sstevel@tonic-gate 
11997c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_encrkeylen != 0) {
1200628b0c67SMark Fenwick 		encrsize = roundup(sizeof (sadb_key_t) + ipsa->ipsa_encrkeylen +
1201628b0c67SMark Fenwick 		    ipsa->ipsa_nonce_len, sizeof (uint64_t));
12027c478bd9Sstevel@tonic-gate 		alloclen += encrsize;
12037c478bd9Sstevel@tonic-gate 		encr = B_TRUE;
12047c478bd9Sstevel@tonic-gate 	} else {
12057c478bd9Sstevel@tonic-gate 		encr = B_FALSE;
12067c478bd9Sstevel@tonic-gate 	}
12077c478bd9Sstevel@tonic-gate 
1208bd670b35SErik Nordmark 	if (ipsa->ipsa_tsl != NULL) {
1209bd670b35SErik Nordmark 		senslen = sadb_sens_len_from_label(ipsa->ipsa_tsl);
12105d3b8cb7SBill Sommerfeld 		alloclen += senslen;
12117c478bd9Sstevel@tonic-gate 		sensinteg = B_TRUE;
12125d3b8cb7SBill Sommerfeld 	}
12135d3b8cb7SBill Sommerfeld 
1214bd670b35SErik Nordmark 	if (ipsa->ipsa_otsl != NULL) {
1215bd670b35SErik Nordmark 		osenslen = sadb_sens_len_from_label(ipsa->ipsa_otsl);
12165d3b8cb7SBill Sommerfeld 		alloclen += osenslen;
12175d3b8cb7SBill Sommerfeld 		osensinteg = B_TRUE;
12187c478bd9Sstevel@tonic-gate 	}
12197c478bd9Sstevel@tonic-gate 
12207c478bd9Sstevel@tonic-gate 	/*
12217c478bd9Sstevel@tonic-gate 	 * Must use strlen() here for lengths.	Identities use NULL
12227c478bd9Sstevel@tonic-gate 	 * pointers to indicate their nonexistence.
12237c478bd9Sstevel@tonic-gate 	 */
12247c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_src_cid != NULL) {
12257c478bd9Sstevel@tonic-gate 		srcidsize = roundup(sizeof (sadb_ident_t) +
12267c478bd9Sstevel@tonic-gate 		    strlen(ipsa->ipsa_src_cid->ipsid_cid) + 1,
12277c478bd9Sstevel@tonic-gate 		    sizeof (uint64_t));
12287c478bd9Sstevel@tonic-gate 		alloclen += srcidsize;
12297c478bd9Sstevel@tonic-gate 		srcid = B_TRUE;
12307c478bd9Sstevel@tonic-gate 	}
12317c478bd9Sstevel@tonic-gate 
12327c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_dst_cid != NULL) {
12337c478bd9Sstevel@tonic-gate 		dstidsize = roundup(sizeof (sadb_ident_t) +
12347c478bd9Sstevel@tonic-gate 		    strlen(ipsa->ipsa_dst_cid->ipsid_cid) + 1,
12357c478bd9Sstevel@tonic-gate 		    sizeof (uint64_t));
12367c478bd9Sstevel@tonic-gate 		alloclen += dstidsize;
12377c478bd9Sstevel@tonic-gate 		dstid = B_TRUE;
12387c478bd9Sstevel@tonic-gate 	}
12397c478bd9Sstevel@tonic-gate 
12407c478bd9Sstevel@tonic-gate 	if ((ipsa->ipsa_kmp != 0) || (ipsa->ipsa_kmc != 0))
12417c478bd9Sstevel@tonic-gate 		alloclen += sizeof (sadb_x_kmc_t);
12427c478bd9Sstevel@tonic-gate 
12439c2c14abSThejaswini Singarajipura 	if (ipsa->ipsa_replay != 0) {
12449c2c14abSThejaswini Singarajipura 		alloclen += sizeof (sadb_x_replay_ctr_t);
12459c2c14abSThejaswini Singarajipura 	}
12469c2c14abSThejaswini Singarajipura 
12477c478bd9Sstevel@tonic-gate 	/* Make sure the allocation length is a multiple of 8 bytes. */
12487c478bd9Sstevel@tonic-gate 	ASSERT((alloclen & 0x7) == 0);
12497c478bd9Sstevel@tonic-gate 
12507c478bd9Sstevel@tonic-gate 	/* XXX Possibly make it esballoc, with a bzero-ing free_ftn. */
12517c478bd9Sstevel@tonic-gate 	mp = allocb(alloclen, BPRI_HI);
12527c478bd9Sstevel@tonic-gate 	if (mp == NULL)
12537c478bd9Sstevel@tonic-gate 		return (NULL);
12545d3b8cb7SBill Sommerfeld 	bzero(mp->b_rptr, alloclen);
12557c478bd9Sstevel@tonic-gate 
12567c478bd9Sstevel@tonic-gate 	mp->b_wptr += alloclen;
12577c478bd9Sstevel@tonic-gate 	end = mp->b_wptr;
12587c478bd9Sstevel@tonic-gate 	newsamsg = (sadb_msg_t *)mp->b_rptr;
12597c478bd9Sstevel@tonic-gate 	*newsamsg = *samsg;
12607c478bd9Sstevel@tonic-gate 	newsamsg->sadb_msg_len = (uint16_t)SADB_8TO64(alloclen);
12617c478bd9Sstevel@tonic-gate 
12627c478bd9Sstevel@tonic-gate 	mutex_enter(&ipsa->ipsa_lock);	/* Since I'm grabbing SA fields... */
12637c478bd9Sstevel@tonic-gate 
12647c478bd9Sstevel@tonic-gate 	newsamsg->sadb_msg_satype = ipsa->ipsa_type;
12657c478bd9Sstevel@tonic-gate 
12667c478bd9Sstevel@tonic-gate 	assoc = (sadb_sa_t *)(newsamsg + 1);
12677c478bd9Sstevel@tonic-gate 	assoc->sadb_sa_len = SADB_8TO64(sizeof (*assoc));
12687c478bd9Sstevel@tonic-gate 	assoc->sadb_sa_exttype = SADB_EXT_SA;
12697c478bd9Sstevel@tonic-gate 	assoc->sadb_sa_spi = ipsa->ipsa_spi;
12707c478bd9Sstevel@tonic-gate 	assoc->sadb_sa_replay = ipsa->ipsa_replay_wsize;
12717c478bd9Sstevel@tonic-gate 	assoc->sadb_sa_state = ipsa->ipsa_state;
12727c478bd9Sstevel@tonic-gate 	assoc->sadb_sa_auth = ipsa->ipsa_auth_alg;
12737c478bd9Sstevel@tonic-gate 	assoc->sadb_sa_encrypt = ipsa->ipsa_encr_alg;
12747c478bd9Sstevel@tonic-gate 	assoc->sadb_sa_flags = ipsa->ipsa_flags;
12757c478bd9Sstevel@tonic-gate 
12767c478bd9Sstevel@tonic-gate 	lt = (sadb_lifetime_t *)(assoc + 1);
12777c478bd9Sstevel@tonic-gate 	lt->sadb_lifetime_len = SADB_8TO64(sizeof (*lt));
12787c478bd9Sstevel@tonic-gate 	lt->sadb_lifetime_exttype = SADB_EXT_LIFETIME_CURRENT;
1279437220cdSdanmcd 	/* We do not support the concept. */
1280437220cdSdanmcd 	lt->sadb_lifetime_allocations = 0;
12817c478bd9Sstevel@tonic-gate 	lt->sadb_lifetime_bytes = ipsa->ipsa_bytes;
12827c478bd9Sstevel@tonic-gate 	lt->sadb_lifetime_addtime = ipsa->ipsa_addtime;
12837c478bd9Sstevel@tonic-gate 	lt->sadb_lifetime_usetime = ipsa->ipsa_usetime;
12847c478bd9Sstevel@tonic-gate 
12857c478bd9Sstevel@tonic-gate 	if (hard) {
12867c478bd9Sstevel@tonic-gate 		lt++;
12877c478bd9Sstevel@tonic-gate 		lt->sadb_lifetime_len = SADB_8TO64(sizeof (*lt));
12887c478bd9Sstevel@tonic-gate 		lt->sadb_lifetime_exttype = SADB_EXT_LIFETIME_HARD;
12897c478bd9Sstevel@tonic-gate 		lt->sadb_lifetime_allocations = ipsa->ipsa_hardalloc;
12907c478bd9Sstevel@tonic-gate 		lt->sadb_lifetime_bytes = ipsa->ipsa_hardbyteslt;
12917c478bd9Sstevel@tonic-gate 		lt->sadb_lifetime_addtime = ipsa->ipsa_hardaddlt;
12927c478bd9Sstevel@tonic-gate 		lt->sadb_lifetime_usetime = ipsa->ipsa_harduselt;
12937c478bd9Sstevel@tonic-gate 	}
12947c478bd9Sstevel@tonic-gate 
12957c478bd9Sstevel@tonic-gate 	if (soft) {
12967c478bd9Sstevel@tonic-gate 		lt++;
12977c478bd9Sstevel@tonic-gate 		lt->sadb_lifetime_len = SADB_8TO64(sizeof (*lt));
12987c478bd9Sstevel@tonic-gate 		lt->sadb_lifetime_exttype = SADB_EXT_LIFETIME_SOFT;
12997c478bd9Sstevel@tonic-gate 		lt->sadb_lifetime_allocations = ipsa->ipsa_softalloc;
13007c478bd9Sstevel@tonic-gate 		lt->sadb_lifetime_bytes = ipsa->ipsa_softbyteslt;
13017c478bd9Sstevel@tonic-gate 		lt->sadb_lifetime_addtime = ipsa->ipsa_softaddlt;
13027c478bd9Sstevel@tonic-gate 		lt->sadb_lifetime_usetime = ipsa->ipsa_softuselt;
13037c478bd9Sstevel@tonic-gate 	}
13047c478bd9Sstevel@tonic-gate 
13059c2c14abSThejaswini Singarajipura 	if (idle) {
13069c2c14abSThejaswini Singarajipura 		lt++;
13079c2c14abSThejaswini Singarajipura 		lt->sadb_lifetime_len = SADB_8TO64(sizeof (*lt));
13089c2c14abSThejaswini Singarajipura 		lt->sadb_lifetime_exttype = SADB_X_EXT_LIFETIME_IDLE;
13099c2c14abSThejaswini Singarajipura 		lt->sadb_lifetime_addtime = ipsa->ipsa_idleaddlt;
13109c2c14abSThejaswini Singarajipura 		lt->sadb_lifetime_usetime = ipsa->ipsa_idleuselt;
13119c2c14abSThejaswini Singarajipura 	}
13129c2c14abSThejaswini Singarajipura 
13137c478bd9Sstevel@tonic-gate 	cur = (uint8_t *)(lt + 1);
13147c478bd9Sstevel@tonic-gate 
13158810c16bSdanmcd 	/* NOTE:  Don't fill in ports here if we are a tunnel-mode SA. */
13167c478bd9Sstevel@tonic-gate 	cur = sadb_make_addr_ext(cur, end, SADB_EXT_ADDRESS_SRC, fam,
13178810c16bSdanmcd 	    ipsa->ipsa_srcaddr, (!isrc && !idst) ? SA_SRCPORT(ipsa) : 0,
13188810c16bSdanmcd 	    SA_PROTO(ipsa), 0);
13197c478bd9Sstevel@tonic-gate 	if (cur == NULL) {
13207c478bd9Sstevel@tonic-gate 		freemsg(mp);
13217c478bd9Sstevel@tonic-gate 		mp = NULL;
13227c478bd9Sstevel@tonic-gate 		goto bail;
13237c478bd9Sstevel@tonic-gate 	}
13247c478bd9Sstevel@tonic-gate 
13257c478bd9Sstevel@tonic-gate 	cur = sadb_make_addr_ext(cur, end, SADB_EXT_ADDRESS_DST, fam,
13268810c16bSdanmcd 	    ipsa->ipsa_dstaddr, (!isrc && !idst) ? SA_DSTPORT(ipsa) : 0,
13278810c16bSdanmcd 	    SA_PROTO(ipsa), 0);
13287c478bd9Sstevel@tonic-gate 	if (cur == NULL) {
13297c478bd9Sstevel@tonic-gate 		freemsg(mp);
13307c478bd9Sstevel@tonic-gate 		mp = NULL;
13317c478bd9Sstevel@tonic-gate 		goto bail;
13327c478bd9Sstevel@tonic-gate 	}
13337c478bd9Sstevel@tonic-gate 
13347c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_flags & IPSA_F_NATT_LOC) {
13357c478bd9Sstevel@tonic-gate 		cur = sadb_make_addr_ext(cur, end, SADB_X_EXT_ADDRESS_NATT_LOC,
1336437220cdSdanmcd 		    fam, &ipsa->ipsa_natt_addr_loc, ipsa->ipsa_local_nat_port,
1337437220cdSdanmcd 		    IPPROTO_UDP, 0);
13387c478bd9Sstevel@tonic-gate 		if (cur == NULL) {
13397c478bd9Sstevel@tonic-gate 			freemsg(mp);
13407c478bd9Sstevel@tonic-gate 			mp = NULL;
13417c478bd9Sstevel@tonic-gate 			goto bail;
13427c478bd9Sstevel@tonic-gate 		}
13437c478bd9Sstevel@tonic-gate 	}
13447c478bd9Sstevel@tonic-gate 
13457c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_flags & IPSA_F_NATT_REM) {
13467c478bd9Sstevel@tonic-gate 		cur = sadb_make_addr_ext(cur, end, SADB_X_EXT_ADDRESS_NATT_REM,
1347437220cdSdanmcd 		    fam, &ipsa->ipsa_natt_addr_rem, ipsa->ipsa_remote_nat_port,
13488810c16bSdanmcd 		    IPPROTO_UDP, 0);
13497c478bd9Sstevel@tonic-gate 		if (cur == NULL) {
13507c478bd9Sstevel@tonic-gate 			freemsg(mp);
13517c478bd9Sstevel@tonic-gate 			mp = NULL;
13527c478bd9Sstevel@tonic-gate 			goto bail;
13537c478bd9Sstevel@tonic-gate 		}
13547c478bd9Sstevel@tonic-gate 	}
13557c478bd9Sstevel@tonic-gate 
13568810c16bSdanmcd 	/* If we are a tunnel-mode SA, fill in the inner-selectors. */
13578810c16bSdanmcd 	if (isrc) {
13588810c16bSdanmcd 		cur = sadb_make_addr_ext(cur, end, SADB_X_EXT_ADDRESS_INNER_SRC,
13598810c16bSdanmcd 		    pfam, ipsa->ipsa_innersrc, SA_SRCPORT(ipsa),
13608810c16bSdanmcd 		    SA_IPROTO(ipsa), ipsa->ipsa_innersrcpfx);
13618810c16bSdanmcd 		if (cur == NULL) {
13628810c16bSdanmcd 			freemsg(mp);
13638810c16bSdanmcd 			mp = NULL;
13648810c16bSdanmcd 			goto bail;
13658810c16bSdanmcd 		}
13668810c16bSdanmcd 	}
13678810c16bSdanmcd 
13688810c16bSdanmcd 	if (idst) {
13698810c16bSdanmcd 		cur = sadb_make_addr_ext(cur, end, SADB_X_EXT_ADDRESS_INNER_DST,
13708810c16bSdanmcd 		    pfam, ipsa->ipsa_innerdst, SA_DSTPORT(ipsa),
13718810c16bSdanmcd 		    SA_IPROTO(ipsa), ipsa->ipsa_innerdstpfx);
13727c478bd9Sstevel@tonic-gate 		if (cur == NULL) {
13737c478bd9Sstevel@tonic-gate 			freemsg(mp);
13747c478bd9Sstevel@tonic-gate 			mp = NULL;
13757c478bd9Sstevel@tonic-gate 			goto bail;
13767c478bd9Sstevel@tonic-gate 		}
13777c478bd9Sstevel@tonic-gate 	}
13787c478bd9Sstevel@tonic-gate 
13797c478bd9Sstevel@tonic-gate 	if ((ipsa->ipsa_kmp != 0) || (ipsa->ipsa_kmc != 0)) {
13807c478bd9Sstevel@tonic-gate 		cur = sadb_make_kmc_ext(cur, end,
13817c478bd9Sstevel@tonic-gate 		    ipsa->ipsa_kmp, ipsa->ipsa_kmc);
13827c478bd9Sstevel@tonic-gate 		if (cur == NULL) {
13837c478bd9Sstevel@tonic-gate 			freemsg(mp);
13847c478bd9Sstevel@tonic-gate 			mp = NULL;
13857c478bd9Sstevel@tonic-gate 			goto bail;
13867c478bd9Sstevel@tonic-gate 		}
13877c478bd9Sstevel@tonic-gate 	}
13887c478bd9Sstevel@tonic-gate 
13897c478bd9Sstevel@tonic-gate 	walker = (sadb_ext_t *)cur;
13907c478bd9Sstevel@tonic-gate 	if (auth) {
13917c478bd9Sstevel@tonic-gate 		key = (sadb_key_t *)walker;
13927c478bd9Sstevel@tonic-gate 		key->sadb_key_len = SADB_8TO64(authsize);
13937c478bd9Sstevel@tonic-gate 		key->sadb_key_exttype = SADB_EXT_KEY_AUTH;
13947c478bd9Sstevel@tonic-gate 		key->sadb_key_bits = ipsa->ipsa_authkeybits;
13957c478bd9Sstevel@tonic-gate 		key->sadb_key_reserved = 0;
13967c478bd9Sstevel@tonic-gate 		bcopy(ipsa->ipsa_authkey, key + 1, ipsa->ipsa_authkeylen);
13977c478bd9Sstevel@tonic-gate 		walker = (sadb_ext_t *)((uint64_t *)walker +
13987c478bd9Sstevel@tonic-gate 		    walker->sadb_ext_len);
13997c478bd9Sstevel@tonic-gate 	}
14007c478bd9Sstevel@tonic-gate 
14017c478bd9Sstevel@tonic-gate 	if (encr) {
1402628b0c67SMark Fenwick 		uint8_t *buf_ptr;
14037c478bd9Sstevel@tonic-gate 		key = (sadb_key_t *)walker;
14047c478bd9Sstevel@tonic-gate 		key->sadb_key_len = SADB_8TO64(encrsize);
14057c478bd9Sstevel@tonic-gate 		key->sadb_key_exttype = SADB_EXT_KEY_ENCRYPT;
14067c478bd9Sstevel@tonic-gate 		key->sadb_key_bits = ipsa->ipsa_encrkeybits;
1407628b0c67SMark Fenwick 		key->sadb_key_reserved = ipsa->ipsa_saltbits;
1408628b0c67SMark Fenwick 		buf_ptr = (uint8_t *)(key + 1);
1409628b0c67SMark Fenwick 		bcopy(ipsa->ipsa_encrkey, buf_ptr, ipsa->ipsa_encrkeylen);
1410628b0c67SMark Fenwick 		if (ipsa->ipsa_salt != NULL) {
1411628b0c67SMark Fenwick 			buf_ptr += ipsa->ipsa_encrkeylen;
1412628b0c67SMark Fenwick 			bcopy(ipsa->ipsa_salt, buf_ptr, ipsa->ipsa_saltlen);
1413628b0c67SMark Fenwick 		}
14147c478bd9Sstevel@tonic-gate 		walker = (sadb_ext_t *)((uint64_t *)walker +
14157c478bd9Sstevel@tonic-gate 		    walker->sadb_ext_len);
14167c478bd9Sstevel@tonic-gate 	}
14177c478bd9Sstevel@tonic-gate 
14187c478bd9Sstevel@tonic-gate 	if (srcid) {
14197c478bd9Sstevel@tonic-gate 		ident = (sadb_ident_t *)walker;
14207c478bd9Sstevel@tonic-gate 		ident->sadb_ident_len = SADB_8TO64(srcidsize);
14217c478bd9Sstevel@tonic-gate 		ident->sadb_ident_exttype = SADB_EXT_IDENTITY_SRC;
14227c478bd9Sstevel@tonic-gate 		ident->sadb_ident_type = ipsa->ipsa_src_cid->ipsid_type;
14237c478bd9Sstevel@tonic-gate 		ident->sadb_ident_id = 0;
14247c478bd9Sstevel@tonic-gate 		ident->sadb_ident_reserved = 0;
14257c478bd9Sstevel@tonic-gate 		(void) strcpy((char *)(ident + 1),
14267c478bd9Sstevel@tonic-gate 		    ipsa->ipsa_src_cid->ipsid_cid);
14277c478bd9Sstevel@tonic-gate 		walker = (sadb_ext_t *)((uint64_t *)walker +
14287c478bd9Sstevel@tonic-gate 		    walker->sadb_ext_len);
14297c478bd9Sstevel@tonic-gate 	}
14307c478bd9Sstevel@tonic-gate 
14317c478bd9Sstevel@tonic-gate 	if (dstid) {
14327c478bd9Sstevel@tonic-gate 		ident = (sadb_ident_t *)walker;
14337c478bd9Sstevel@tonic-gate 		ident->sadb_ident_len = SADB_8TO64(dstidsize);
14347c478bd9Sstevel@tonic-gate 		ident->sadb_ident_exttype = SADB_EXT_IDENTITY_DST;
14357c478bd9Sstevel@tonic-gate 		ident->sadb_ident_type = ipsa->ipsa_dst_cid->ipsid_type;
14367c478bd9Sstevel@tonic-gate 		ident->sadb_ident_id = 0;
14377c478bd9Sstevel@tonic-gate 		ident->sadb_ident_reserved = 0;
14387c478bd9Sstevel@tonic-gate 		(void) strcpy((char *)(ident + 1),
14397c478bd9Sstevel@tonic-gate 		    ipsa->ipsa_dst_cid->ipsid_cid);
14407c478bd9Sstevel@tonic-gate 		walker = (sadb_ext_t *)((uint64_t *)walker +
14417c478bd9Sstevel@tonic-gate 		    walker->sadb_ext_len);
14427c478bd9Sstevel@tonic-gate 	}
14437c478bd9Sstevel@tonic-gate 
14447c478bd9Sstevel@tonic-gate 	if (sensinteg) {
14457c478bd9Sstevel@tonic-gate 		sens = (sadb_sens_t *)walker;
1446bd670b35SErik Nordmark 		sadb_sens_from_label(sens, SADB_EXT_SENSITIVITY,
1447bd670b35SErik Nordmark 		    ipsa->ipsa_tsl, senslen);
14485d3b8cb7SBill Sommerfeld 
14495d3b8cb7SBill Sommerfeld 		walker = (sadb_ext_t *)((uint64_t *)walker +
14505d3b8cb7SBill Sommerfeld 		    walker->sadb_ext_len);
14515d3b8cb7SBill Sommerfeld 	}
14525d3b8cb7SBill Sommerfeld 
14535d3b8cb7SBill Sommerfeld 	if (osensinteg) {
14545d3b8cb7SBill Sommerfeld 		sens = (sadb_sens_t *)walker;
14555d3b8cb7SBill Sommerfeld 
1456bd670b35SErik Nordmark 		sadb_sens_from_label(sens, SADB_X_EXT_OUTER_SENS,
1457bd670b35SErik Nordmark 		    ipsa->ipsa_otsl, osenslen);
14585d3b8cb7SBill Sommerfeld 		if (ipsa->ipsa_mac_exempt)
14595d3b8cb7SBill Sommerfeld 			sens->sadb_x_sens_flags = SADB_X_SENS_IMPLICIT;
14605d3b8cb7SBill Sommerfeld 
14617c478bd9Sstevel@tonic-gate 		walker = (sadb_ext_t *)((uint64_t *)walker +
14627c478bd9Sstevel@tonic-gate 		    walker->sadb_ext_len);
14637c478bd9Sstevel@tonic-gate 	}
14647c478bd9Sstevel@tonic-gate 
146538d95a78Smarkfen 	if (paired) {
146638d95a78Smarkfen 		pair_ext = (sadb_x_pair_t *)walker;
146738d95a78Smarkfen 
146838d95a78Smarkfen 		pair_ext->sadb_x_pair_len = SADB_8TO64(sizeof (sadb_x_pair_t));
146938d95a78Smarkfen 		pair_ext->sadb_x_pair_exttype = SADB_X_EXT_PAIR;
147038d95a78Smarkfen 		pair_ext->sadb_x_pair_spi = otherspi;
147138d95a78Smarkfen 
147238d95a78Smarkfen 		walker = (sadb_ext_t *)((uint64_t *)walker +
147338d95a78Smarkfen 		    walker->sadb_ext_len);
147438d95a78Smarkfen 	}
147538d95a78Smarkfen 
14769c2c14abSThejaswini Singarajipura 	if (ipsa->ipsa_replay != 0) {
14779c2c14abSThejaswini Singarajipura 		repl_ctr = (sadb_x_replay_ctr_t *)walker;
14789c2c14abSThejaswini Singarajipura 		repl_ctr->sadb_x_rc_len = SADB_8TO64(sizeof (*repl_ctr));
14799c2c14abSThejaswini Singarajipura 		repl_ctr->sadb_x_rc_exttype = SADB_X_EXT_REPLAY_VALUE;
14809c2c14abSThejaswini Singarajipura 		repl_ctr->sadb_x_rc_replay32 = ipsa->ipsa_replay;
14819c2c14abSThejaswini Singarajipura 		repl_ctr->sadb_x_rc_replay64 = 0;
14829c2c14abSThejaswini Singarajipura 		walker = (sadb_ext_t *)(repl_ctr + 1);
14839c2c14abSThejaswini Singarajipura 	}
14849c2c14abSThejaswini Singarajipura 
14857c478bd9Sstevel@tonic-gate bail:
14867c478bd9Sstevel@tonic-gate 	/* Pardon any delays... */
14877c478bd9Sstevel@tonic-gate 	mutex_exit(&ipsa->ipsa_lock);
14887c478bd9Sstevel@tonic-gate 
14897c478bd9Sstevel@tonic-gate 	return (mp);
14907c478bd9Sstevel@tonic-gate }
14917c478bd9Sstevel@tonic-gate 
14927c478bd9Sstevel@tonic-gate /*
14937c478bd9Sstevel@tonic-gate  * Strip out key headers or unmarked headers (SADB_EXT_KEY_*, SADB_EXT_UNKNOWN)
14947c478bd9Sstevel@tonic-gate  * and adjust base message accordingly.
14957c478bd9Sstevel@tonic-gate  *
14967c478bd9Sstevel@tonic-gate  * Assume message is pulled up in one piece of contiguous memory.
14977c478bd9Sstevel@tonic-gate  *
14987c478bd9Sstevel@tonic-gate  * Say if we start off with:
14997c478bd9Sstevel@tonic-gate  *
15007c478bd9Sstevel@tonic-gate  * +------+----+-------------+-----------+---------------+---------------+
15017c478bd9Sstevel@tonic-gate  * | base | SA | source addr | dest addr | rsrvd. or key | soft lifetime |
15027c478bd9Sstevel@tonic-gate  * +------+----+-------------+-----------+---------------+---------------+
15037c478bd9Sstevel@tonic-gate  *
15047c478bd9Sstevel@tonic-gate  * we will end up with
15057c478bd9Sstevel@tonic-gate  *
15067c478bd9Sstevel@tonic-gate  * +------+----+-------------+-----------+---------------+
15077c478bd9Sstevel@tonic-gate  * | base | SA | source addr | dest addr | soft lifetime |
15087c478bd9Sstevel@tonic-gate  * +------+----+-------------+-----------+---------------+
15097c478bd9Sstevel@tonic-gate  */
15107c478bd9Sstevel@tonic-gate static void
sadb_strip(sadb_msg_t * samsg)15117c478bd9Sstevel@tonic-gate sadb_strip(sadb_msg_t *samsg)
15127c478bd9Sstevel@tonic-gate {
15137c478bd9Sstevel@tonic-gate 	sadb_ext_t *ext;
15147c478bd9Sstevel@tonic-gate 	uint8_t *target = NULL;
15157c478bd9Sstevel@tonic-gate 	uint8_t *msgend;
15167c478bd9Sstevel@tonic-gate 	int sofar = SADB_8TO64(sizeof (*samsg));
15177c478bd9Sstevel@tonic-gate 	int copylen;
15187c478bd9Sstevel@tonic-gate 
15197c478bd9Sstevel@tonic-gate 	ext = (sadb_ext_t *)(samsg + 1);
15207c478bd9Sstevel@tonic-gate 	msgend = (uint8_t *)samsg;
15217c478bd9Sstevel@tonic-gate 	msgend += SADB_64TO8(samsg->sadb_msg_len);
15227c478bd9Sstevel@tonic-gate 	while ((uint8_t *)ext < msgend) {
15237c478bd9Sstevel@tonic-gate 		if (ext->sadb_ext_type == SADB_EXT_RESERVED ||
15247c478bd9Sstevel@tonic-gate 		    ext->sadb_ext_type == SADB_EXT_KEY_AUTH ||
15259c2c14abSThejaswini Singarajipura 		    ext->sadb_ext_type == SADB_X_EXT_EDUMP ||
15267c478bd9Sstevel@tonic-gate 		    ext->sadb_ext_type == SADB_EXT_KEY_ENCRYPT) {
15277c478bd9Sstevel@tonic-gate 			/*
15287c478bd9Sstevel@tonic-gate 			 * Aha!	 I found a header to be erased.
15297c478bd9Sstevel@tonic-gate 			 */
15307c478bd9Sstevel@tonic-gate 
15317c478bd9Sstevel@tonic-gate 			if (target != NULL) {
15327c478bd9Sstevel@tonic-gate 				/*
15337c478bd9Sstevel@tonic-gate 				 * If I had a previous header to be erased,
15347c478bd9Sstevel@tonic-gate 				 * copy over it.  I can get away with just
15357c478bd9Sstevel@tonic-gate 				 * copying backwards because the target will
15367c478bd9Sstevel@tonic-gate 				 * always be 8 bytes behind the source.
15377c478bd9Sstevel@tonic-gate 				 */
15387c478bd9Sstevel@tonic-gate 				copylen = ((uint8_t *)ext) - (target +
15397c478bd9Sstevel@tonic-gate 				    SADB_64TO8(
1540437220cdSdanmcd 				    ((sadb_ext_t *)target)->sadb_ext_len));
15417c478bd9Sstevel@tonic-gate 				ovbcopy(((uint8_t *)ext - copylen), target,
15427c478bd9Sstevel@tonic-gate 				    copylen);
15437c478bd9Sstevel@tonic-gate 				target += copylen;
15447c478bd9Sstevel@tonic-gate 				((sadb_ext_t *)target)->sadb_ext_len =
15457c478bd9Sstevel@tonic-gate 				    SADB_8TO64(((uint8_t *)ext) - target +
1546437220cdSdanmcd 				    SADB_64TO8(ext->sadb_ext_len));
15477c478bd9Sstevel@tonic-gate 			} else {
15487c478bd9Sstevel@tonic-gate 				target = (uint8_t *)ext;
15497c478bd9Sstevel@tonic-gate 			}
15507c478bd9Sstevel@tonic-gate 		} else {
15517c478bd9Sstevel@tonic-gate 			sofar += ext->sadb_ext_len;
15527c478bd9Sstevel@tonic-gate 		}
15537c478bd9Sstevel@tonic-gate 
15547c478bd9Sstevel@tonic-gate 		ext = (sadb_ext_t *)(((uint64_t *)ext) + ext->sadb_ext_len);
15557c478bd9Sstevel@tonic-gate 	}
15567c478bd9Sstevel@tonic-gate 
15577c478bd9Sstevel@tonic-gate 	ASSERT((uint8_t *)ext == msgend);
15587c478bd9Sstevel@tonic-gate 
15597c478bd9Sstevel@tonic-gate 	if (target != NULL) {
15607c478bd9Sstevel@tonic-gate 		copylen = ((uint8_t *)ext) - (target +
15617c478bd9Sstevel@tonic-gate 		    SADB_64TO8(((sadb_ext_t *)target)->sadb_ext_len));
15627c478bd9Sstevel@tonic-gate 		if (copylen != 0)
15637c478bd9Sstevel@tonic-gate 			ovbcopy(((uint8_t *)ext - copylen), target, copylen);
15647c478bd9Sstevel@tonic-gate 	}
15657c478bd9Sstevel@tonic-gate 
15667c478bd9Sstevel@tonic-gate 	/* Adjust samsg. */
15677c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_len = (uint16_t)sofar;
15687c478bd9Sstevel@tonic-gate 
15697c478bd9Sstevel@tonic-gate 	/* Assume all of the rest is cleared by caller in sadb_pfkey_echo(). */
15707c478bd9Sstevel@tonic-gate }
15717c478bd9Sstevel@tonic-gate 
15727c478bd9Sstevel@tonic-gate /*
15737c478bd9Sstevel@tonic-gate  * AH needs to send an error to PF_KEY.	 Assume mp points to an M_CTL
15747c478bd9Sstevel@tonic-gate  * followed by an M_DATA with a PF_KEY message in it.  The serial of
15757c478bd9Sstevel@tonic-gate  * the sending keysock instance is included.
15767c478bd9Sstevel@tonic-gate  */
15777c478bd9Sstevel@tonic-gate void
sadb_pfkey_error(queue_t * pfkey_q,mblk_t * mp,int error,int diagnostic,uint_t serial)15787c478bd9Sstevel@tonic-gate sadb_pfkey_error(queue_t *pfkey_q, mblk_t *mp, int error, int diagnostic,
15797c478bd9Sstevel@tonic-gate     uint_t serial)
15807c478bd9Sstevel@tonic-gate {
15817c478bd9Sstevel@tonic-gate 	mblk_t *msg = mp->b_cont;
15827c478bd9Sstevel@tonic-gate 	sadb_msg_t *samsg;
15837c478bd9Sstevel@tonic-gate 	keysock_out_t *kso;
15847c478bd9Sstevel@tonic-gate 
15857c478bd9Sstevel@tonic-gate 	/*
15867c478bd9Sstevel@tonic-gate 	 * Enough functions call this to merit a NULL queue check.
15877c478bd9Sstevel@tonic-gate 	 */
15887c478bd9Sstevel@tonic-gate 	if (pfkey_q == NULL) {
15897c478bd9Sstevel@tonic-gate 		freemsg(mp);
15907c478bd9Sstevel@tonic-gate 		return;
15917c478bd9Sstevel@tonic-gate 	}
15927c478bd9Sstevel@tonic-gate 
15937c478bd9Sstevel@tonic-gate 	ASSERT(msg != NULL);
15947c478bd9Sstevel@tonic-gate 	ASSERT((mp->b_wptr - mp->b_rptr) == sizeof (ipsec_info_t));
15957c478bd9Sstevel@tonic-gate 	ASSERT((msg->b_wptr - msg->b_rptr) >= sizeof (sadb_msg_t));
15967c478bd9Sstevel@tonic-gate 	samsg = (sadb_msg_t *)msg->b_rptr;
15977c478bd9Sstevel@tonic-gate 	kso = (keysock_out_t *)mp->b_rptr;
15987c478bd9Sstevel@tonic-gate 
15997c478bd9Sstevel@tonic-gate 	kso->ks_out_type = KEYSOCK_OUT;
16007c478bd9Sstevel@tonic-gate 	kso->ks_out_len = sizeof (*kso);
16017c478bd9Sstevel@tonic-gate 	kso->ks_out_serial = serial;
16027c478bd9Sstevel@tonic-gate 
16037c478bd9Sstevel@tonic-gate 	/*
16047c478bd9Sstevel@tonic-gate 	 * Only send the base message up in the event of an error.
16057c478bd9Sstevel@tonic-gate 	 * Don't worry about bzero()-ing, because it was probably bogus
16067c478bd9Sstevel@tonic-gate 	 * anyway.
16077c478bd9Sstevel@tonic-gate 	 */
16087c478bd9Sstevel@tonic-gate 	msg->b_wptr = msg->b_rptr + sizeof (*samsg);
16097c478bd9Sstevel@tonic-gate 	samsg = (sadb_msg_t *)msg->b_rptr;
16107c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_len = SADB_8TO64(sizeof (*samsg));
16117c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_errno = (uint8_t)error;
16127c478bd9Sstevel@tonic-gate 	if (diagnostic != SADB_X_DIAGNOSTIC_PRESET)
16137c478bd9Sstevel@tonic-gate 		samsg->sadb_x_msg_diagnostic = (uint16_t)diagnostic;
16147c478bd9Sstevel@tonic-gate 
16157c478bd9Sstevel@tonic-gate 	putnext(pfkey_q, mp);
16167c478bd9Sstevel@tonic-gate }
16177c478bd9Sstevel@tonic-gate 
16187c478bd9Sstevel@tonic-gate /*
16197c478bd9Sstevel@tonic-gate  * Send a successful return packet back to keysock via the queue in pfkey_q.
16207c478bd9Sstevel@tonic-gate  *
16217c478bd9Sstevel@tonic-gate  * Often, an SA is associated with the reply message, it's passed in if needed,
16227c478bd9Sstevel@tonic-gate  * and NULL if not.  BTW, that ipsa will have its refcnt appropriately held,
16237c478bd9Sstevel@tonic-gate  * and the caller will release said refcnt.
16247c478bd9Sstevel@tonic-gate  */
16257c478bd9Sstevel@tonic-gate void
sadb_pfkey_echo(queue_t * pfkey_q,mblk_t * mp,sadb_msg_t * samsg,keysock_in_t * ksi,ipsa_t * ipsa)16267c478bd9Sstevel@tonic-gate sadb_pfkey_echo(queue_t *pfkey_q, mblk_t *mp, sadb_msg_t *samsg,
16277c478bd9Sstevel@tonic-gate     keysock_in_t *ksi, ipsa_t *ipsa)
16287c478bd9Sstevel@tonic-gate {
16297c478bd9Sstevel@tonic-gate 	keysock_out_t *kso;
16307c478bd9Sstevel@tonic-gate 	mblk_t *mp1;
16317c478bd9Sstevel@tonic-gate 	sadb_msg_t *newsamsg;
16327c478bd9Sstevel@tonic-gate 	uint8_t *oldend;
16337c478bd9Sstevel@tonic-gate 
16347c478bd9Sstevel@tonic-gate 	ASSERT((mp->b_cont != NULL) &&
16357c478bd9Sstevel@tonic-gate 	    ((void *)samsg == (void *)mp->b_cont->b_rptr) &&
16367c478bd9Sstevel@tonic-gate 	    ((void *)mp->b_rptr == (void *)ksi));
16377c478bd9Sstevel@tonic-gate 
16387c478bd9Sstevel@tonic-gate 	switch (samsg->sadb_msg_type) {
16397c478bd9Sstevel@tonic-gate 	case SADB_ADD:
16407c478bd9Sstevel@tonic-gate 	case SADB_UPDATE:
164138d95a78Smarkfen 	case SADB_X_UPDATEPAIR:
16429c2c14abSThejaswini Singarajipura 	case SADB_X_DELPAIR_STATE:
16437c478bd9Sstevel@tonic-gate 	case SADB_FLUSH:
16447c478bd9Sstevel@tonic-gate 	case SADB_DUMP:
16457c478bd9Sstevel@tonic-gate 		/*
16467c478bd9Sstevel@tonic-gate 		 * I have all of the message already.  I just need to strip
16477c478bd9Sstevel@tonic-gate 		 * out the keying material and echo the message back.
16487c478bd9Sstevel@tonic-gate 		 *
16497c478bd9Sstevel@tonic-gate 		 * NOTE: for SADB_DUMP, the function sadb_dump() did the
16507c478bd9Sstevel@tonic-gate 		 * work.  When DUMP reaches here, it should only be a base
16517c478bd9Sstevel@tonic-gate 		 * message.
16527c478bd9Sstevel@tonic-gate 		 */
16537c478bd9Sstevel@tonic-gate 	justecho:
16547c478bd9Sstevel@tonic-gate 		if (ksi->ks_in_extv[SADB_EXT_KEY_AUTH] != NULL ||
16559c2c14abSThejaswini Singarajipura 		    ksi->ks_in_extv[SADB_EXT_KEY_ENCRYPT] != NULL ||
16569c2c14abSThejaswini Singarajipura 		    ksi->ks_in_extv[SADB_X_EXT_EDUMP] != NULL) {
16577c478bd9Sstevel@tonic-gate 			sadb_strip(samsg);
16587c478bd9Sstevel@tonic-gate 			/* Assume PF_KEY message is contiguous. */
16597c478bd9Sstevel@tonic-gate 			ASSERT(mp->b_cont->b_cont == NULL);
16607c478bd9Sstevel@tonic-gate 			oldend = mp->b_cont->b_wptr;
16617c478bd9Sstevel@tonic-gate 			mp->b_cont->b_wptr = mp->b_cont->b_rptr +
16627c478bd9Sstevel@tonic-gate 			    SADB_64TO8(samsg->sadb_msg_len);
16637c478bd9Sstevel@tonic-gate 			bzero(mp->b_cont->b_wptr, oldend - mp->b_cont->b_wptr);
16647c478bd9Sstevel@tonic-gate 		}
16657c478bd9Sstevel@tonic-gate 		break;
16667c478bd9Sstevel@tonic-gate 	case SADB_GET:
16677c478bd9Sstevel@tonic-gate 		/*
16687c478bd9Sstevel@tonic-gate 		 * Do a lot of work here, because of the ipsa I just found.
16698810c16bSdanmcd 		 * First construct the new PF_KEY message, then abandon
16708810c16bSdanmcd 		 * the old one.
16717c478bd9Sstevel@tonic-gate 		 */
16727c478bd9Sstevel@tonic-gate 		mp1 = sadb_sa2msg(ipsa, samsg);
16737c478bd9Sstevel@tonic-gate 		if (mp1 == NULL) {
16747c478bd9Sstevel@tonic-gate 			sadb_pfkey_error(pfkey_q, mp, ENOMEM,
16757c478bd9Sstevel@tonic-gate 			    SADB_X_DIAGNOSTIC_NONE, ksi->ks_in_serial);
16767c478bd9Sstevel@tonic-gate 			return;
16777c478bd9Sstevel@tonic-gate 		}
16787c478bd9Sstevel@tonic-gate 		freemsg(mp->b_cont);
16797c478bd9Sstevel@tonic-gate 		mp->b_cont = mp1;
16807c478bd9Sstevel@tonic-gate 		break;
16817c478bd9Sstevel@tonic-gate 	case SADB_DELETE:
168238d95a78Smarkfen 	case SADB_X_DELPAIR:
16837c478bd9Sstevel@tonic-gate 		if (ipsa == NULL)
16847c478bd9Sstevel@tonic-gate 			goto justecho;
16857c478bd9Sstevel@tonic-gate 		/*
16867c478bd9Sstevel@tonic-gate 		 * Because listening KMds may require more info, treat
16877c478bd9Sstevel@tonic-gate 		 * DELETE like a special case of GET.
16887c478bd9Sstevel@tonic-gate 		 */
16897c478bd9Sstevel@tonic-gate 		mp1 = sadb_sa2msg(ipsa, samsg);
16907c478bd9Sstevel@tonic-gate 		if (mp1 == NULL) {
16917c478bd9Sstevel@tonic-gate 			sadb_pfkey_error(pfkey_q, mp, ENOMEM,
16927c478bd9Sstevel@tonic-gate 			    SADB_X_DIAGNOSTIC_NONE, ksi->ks_in_serial);
16937c478bd9Sstevel@tonic-gate 			return;
16947c478bd9Sstevel@tonic-gate 		}
16957c478bd9Sstevel@tonic-gate 		newsamsg = (sadb_msg_t *)mp1->b_rptr;
16967c478bd9Sstevel@tonic-gate 		sadb_strip(newsamsg);
16977c478bd9Sstevel@tonic-gate 		oldend = mp1->b_wptr;
16987c478bd9Sstevel@tonic-gate 		mp1->b_wptr = mp1->b_rptr + SADB_64TO8(newsamsg->sadb_msg_len);
16997c478bd9Sstevel@tonic-gate 		bzero(mp1->b_wptr, oldend - mp1->b_wptr);
17007c478bd9Sstevel@tonic-gate 		freemsg(mp->b_cont);
17017c478bd9Sstevel@tonic-gate 		mp->b_cont = mp1;
17027c478bd9Sstevel@tonic-gate 		break;
17037c478bd9Sstevel@tonic-gate 	default:
1704a23b3b1bSToomas Soome 		freemsg(mp);
17057c478bd9Sstevel@tonic-gate 		return;
17067c478bd9Sstevel@tonic-gate 	}
17077c478bd9Sstevel@tonic-gate 
17087c478bd9Sstevel@tonic-gate 	/* ksi is now null and void. */
17097c478bd9Sstevel@tonic-gate 	kso = (keysock_out_t *)ksi;
17107c478bd9Sstevel@tonic-gate 	kso->ks_out_type = KEYSOCK_OUT;
17117c478bd9Sstevel@tonic-gate 	kso->ks_out_len = sizeof (*kso);
17127c478bd9Sstevel@tonic-gate 	kso->ks_out_serial = ksi->ks_in_serial;
17137c478bd9Sstevel@tonic-gate 	/* We're ready to send... */
17147c478bd9Sstevel@tonic-gate 	putnext(pfkey_q, mp);
17157c478bd9Sstevel@tonic-gate }
17167c478bd9Sstevel@tonic-gate 
17177c478bd9Sstevel@tonic-gate /*
17187c478bd9Sstevel@tonic-gate  * Set up a global pfkey_q instance for AH, ESP, or some other consumer.
17197c478bd9Sstevel@tonic-gate  */
17207c478bd9Sstevel@tonic-gate void
sadb_keysock_hello(queue_t ** pfkey_qp,queue_t * q,mblk_t * mp,void (* ager)(void *),void * agerarg,timeout_id_t * top,int satype)17217c478bd9Sstevel@tonic-gate sadb_keysock_hello(queue_t **pfkey_qp, queue_t *q, mblk_t *mp,
1722f4b3ec61Sdh     void (*ager)(void *), void *agerarg, timeout_id_t *top, int satype)
17237c478bd9Sstevel@tonic-gate {
17247c478bd9Sstevel@tonic-gate 	keysock_hello_ack_t *kha;
17257c478bd9Sstevel@tonic-gate 	queue_t *oldq;
17267c478bd9Sstevel@tonic-gate 
17277c478bd9Sstevel@tonic-gate 	ASSERT(OTHERQ(q) != NULL);
17287c478bd9Sstevel@tonic-gate 
17297c478bd9Sstevel@tonic-gate 	/*
17307c478bd9Sstevel@tonic-gate 	 * First, check atomically that I'm the first and only keysock
17317c478bd9Sstevel@tonic-gate 	 * instance.
17327c478bd9Sstevel@tonic-gate 	 *
17337c478bd9Sstevel@tonic-gate 	 * Use OTHERQ(q), because qreply(q, mp) == putnext(OTHERQ(q), mp),
17347c478bd9Sstevel@tonic-gate 	 * and I want this module to say putnext(*_pfkey_q, mp) for PF_KEY
17357c478bd9Sstevel@tonic-gate 	 * messages.
17367c478bd9Sstevel@tonic-gate 	 */
17377c478bd9Sstevel@tonic-gate 
173875d94465SJosef 'Jeff' Sipek 	oldq = atomic_cas_ptr((void **)pfkey_qp, NULL, OTHERQ(q));
17397c478bd9Sstevel@tonic-gate 	if (oldq != NULL) {
17407c478bd9Sstevel@tonic-gate 		ASSERT(oldq != q);
17417c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "Danger!  Multiple keysocks on top of %s.\n",
17427c478bd9Sstevel@tonic-gate 		    (satype == SADB_SATYPE_ESP)? "ESP" : "AH or other");
17437c478bd9Sstevel@tonic-gate 		freemsg(mp);
17447c478bd9Sstevel@tonic-gate 		return;
17457c478bd9Sstevel@tonic-gate 	}
17467c478bd9Sstevel@tonic-gate 
17477c478bd9Sstevel@tonic-gate 	kha = (keysock_hello_ack_t *)mp->b_rptr;
17487c478bd9Sstevel@tonic-gate 	kha->ks_hello_len = sizeof (keysock_hello_ack_t);
17497c478bd9Sstevel@tonic-gate 	kha->ks_hello_type = KEYSOCK_HELLO_ACK;
17507c478bd9Sstevel@tonic-gate 	kha->ks_hello_satype = (uint8_t)satype;
17517c478bd9Sstevel@tonic-gate 
17527c478bd9Sstevel@tonic-gate 	/*
175375d94465SJosef 'Jeff' Sipek 	 * If we made it past the atomic_cas_ptr, then we have "exclusive"
175475d94465SJosef 'Jeff' Sipek 	 * access to the timeout handle.  Fire it off after the default ager
17550e9b5742SDan McDonald 	 * interval.
17567c478bd9Sstevel@tonic-gate 	 */
17570e9b5742SDan McDonald 	*top = qtimeout(*pfkey_qp, ager, agerarg,
17580e9b5742SDan McDonald 	    drv_usectohz(SADB_AGE_INTERVAL_DEFAULT * 1000));
17597c478bd9Sstevel@tonic-gate 
17607c478bd9Sstevel@tonic-gate 	putnext(*pfkey_qp, mp);
17617c478bd9Sstevel@tonic-gate }
17627c478bd9Sstevel@tonic-gate 
17637c478bd9Sstevel@tonic-gate /*
17648810c16bSdanmcd  * Normalize IPv4-mapped IPv6 addresses (and prefixes) as appropriate.
17657c478bd9Sstevel@tonic-gate  *
17668810c16bSdanmcd  * Check addresses themselves for wildcard or multicast.
17678810c16bSdanmcd  * Check ire table for local/non-local/broadcast.
17687c478bd9Sstevel@tonic-gate  */
17697c478bd9Sstevel@tonic-gate int
sadb_addrcheck(queue_t * pfkey_q,mblk_t * mp,sadb_ext_t * ext,uint_t serial,netstack_t * ns)1770f4b3ec61Sdh sadb_addrcheck(queue_t *pfkey_q, mblk_t *mp, sadb_ext_t *ext, uint_t serial,
1771f4b3ec61Sdh     netstack_t *ns)
17727c478bd9Sstevel@tonic-gate {
17737c478bd9Sstevel@tonic-gate 	sadb_address_t *addr = (sadb_address_t *)ext;
17747c478bd9Sstevel@tonic-gate 	struct sockaddr_in *sin;
17757c478bd9Sstevel@tonic-gate 	struct sockaddr_in6 *sin6;
17768810c16bSdanmcd 	int diagnostic, type;
17778810c16bSdanmcd 	boolean_t normalized = B_FALSE;
17787c478bd9Sstevel@tonic-gate 
17797c478bd9Sstevel@tonic-gate 	ASSERT(ext != NULL);
17807c478bd9Sstevel@tonic-gate 	ASSERT((ext->sadb_ext_type == SADB_EXT_ADDRESS_SRC) ||
17817c478bd9Sstevel@tonic-gate 	    (ext->sadb_ext_type == SADB_EXT_ADDRESS_DST) ||
17828810c16bSdanmcd 	    (ext->sadb_ext_type == SADB_X_EXT_ADDRESS_INNER_SRC) ||
17838810c16bSdanmcd 	    (ext->sadb_ext_type == SADB_X_EXT_ADDRESS_INNER_DST) ||
17848810c16bSdanmcd 	    (ext->sadb_ext_type == SADB_X_EXT_ADDRESS_NATT_LOC) ||
17858810c16bSdanmcd 	    (ext->sadb_ext_type == SADB_X_EXT_ADDRESS_NATT_REM));
17867c478bd9Sstevel@tonic-gate 
17874132743aSToomas Soome 	diagnostic = 0;
17884132743aSToomas Soome 
17897c478bd9Sstevel@tonic-gate 	/* Assign both sockaddrs, the compiler will do the right thing. */
17907c478bd9Sstevel@tonic-gate 	sin = (struct sockaddr_in *)(addr + 1);
17917c478bd9Sstevel@tonic-gate 	sin6 = (struct sockaddr_in6 *)(addr + 1);
17927c478bd9Sstevel@tonic-gate 
17938810c16bSdanmcd 	if (sin6->sin6_family == AF_INET6) {
17948810c16bSdanmcd 		if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
17958810c16bSdanmcd 			/*
17968810c16bSdanmcd 			 * Convert to an AF_INET sockaddr.  This means the
17978810c16bSdanmcd 			 * return messages will have the extra space, but have
17988810c16bSdanmcd 			 * AF_INET sockaddrs instead of AF_INET6.
17998810c16bSdanmcd 			 *
18008810c16bSdanmcd 			 * Yes, RFC 2367 isn't clear on what to do here w.r.t.
18018810c16bSdanmcd 			 * mapped addresses, but since AF_INET6 ::ffff:<v4> is
18028810c16bSdanmcd 			 * equal to AF_INET <v4>, it shouldnt be a huge
18038810c16bSdanmcd 			 * problem.
18048810c16bSdanmcd 			 */
18058810c16bSdanmcd 			sin->sin_family = AF_INET;
18068810c16bSdanmcd 			IN6_V4MAPPED_TO_INADDR(&sin6->sin6_addr,
18078810c16bSdanmcd 			    &sin->sin_addr);
18088810c16bSdanmcd 			bzero(&sin->sin_zero, sizeof (sin->sin_zero));
18098810c16bSdanmcd 			normalized = B_TRUE;
18107c478bd9Sstevel@tonic-gate 		}
18118810c16bSdanmcd 	} else if (sin->sin_family != AF_INET) {
18127c478bd9Sstevel@tonic-gate 		switch (ext->sadb_ext_type) {
18137c478bd9Sstevel@tonic-gate 		case SADB_EXT_ADDRESS_SRC:
18147c478bd9Sstevel@tonic-gate 			diagnostic = SADB_X_DIAGNOSTIC_BAD_SRC_AF;
18157c478bd9Sstevel@tonic-gate 			break;
18167c478bd9Sstevel@tonic-gate 		case SADB_EXT_ADDRESS_DST:
18177c478bd9Sstevel@tonic-gate 			diagnostic = SADB_X_DIAGNOSTIC_BAD_DST_AF;
18187c478bd9Sstevel@tonic-gate 			break;
18198810c16bSdanmcd 		case SADB_X_EXT_ADDRESS_INNER_SRC:
18207c478bd9Sstevel@tonic-gate 			diagnostic = SADB_X_DIAGNOSTIC_BAD_PROXY_AF;
18217c478bd9Sstevel@tonic-gate 			break;
18228810c16bSdanmcd 		case SADB_X_EXT_ADDRESS_INNER_DST:
18238810c16bSdanmcd 			diagnostic = SADB_X_DIAGNOSTIC_BAD_INNER_DST_AF;
18248810c16bSdanmcd 			break;
18258810c16bSdanmcd 		case SADB_X_EXT_ADDRESS_NATT_LOC:
18268810c16bSdanmcd 			diagnostic = SADB_X_DIAGNOSTIC_BAD_NATT_LOC_AF;
18278810c16bSdanmcd 			break;
18288810c16bSdanmcd 		case SADB_X_EXT_ADDRESS_NATT_REM:
18298810c16bSdanmcd 			diagnostic = SADB_X_DIAGNOSTIC_BAD_NATT_REM_AF;
18308810c16bSdanmcd 			break;
18317c478bd9Sstevel@tonic-gate 			/* There is no default, see above ASSERT. */
18327c478bd9Sstevel@tonic-gate 		}
18338810c16bSdanmcd bail:
18348810c16bSdanmcd 		if (pfkey_q != NULL) {
18358810c16bSdanmcd 			sadb_pfkey_error(pfkey_q, mp, EINVAL, diagnostic,
18368810c16bSdanmcd 			    serial);
18378810c16bSdanmcd 		} else {
18388810c16bSdanmcd 			/*
18398810c16bSdanmcd 			 * Scribble in sadb_msg that we got passed in.
18408810c16bSdanmcd 			 * Overload "mp" to be an sadb_msg pointer.
18418810c16bSdanmcd 			 */
18428810c16bSdanmcd 			sadb_msg_t *samsg = (sadb_msg_t *)mp;
18437c478bd9Sstevel@tonic-gate 
18448810c16bSdanmcd 			samsg->sadb_msg_errno = EINVAL;
18458810c16bSdanmcd 			samsg->sadb_x_msg_diagnostic = diagnostic;
18468810c16bSdanmcd 		}
18477c478bd9Sstevel@tonic-gate 		return (KS_IN_ADDR_UNKNOWN);
18487c478bd9Sstevel@tonic-gate 	}
18497c478bd9Sstevel@tonic-gate 
18508810c16bSdanmcd 	if (ext->sadb_ext_type == SADB_X_EXT_ADDRESS_INNER_SRC ||
18518810c16bSdanmcd 	    ext->sadb_ext_type == SADB_X_EXT_ADDRESS_INNER_DST) {
18528810c16bSdanmcd 		/*
18538810c16bSdanmcd 		 * We need only check for prefix issues.
18548810c16bSdanmcd 		 */
18558810c16bSdanmcd 
18568810c16bSdanmcd 		/* Set diagnostic now, in case we need it later. */
18578810c16bSdanmcd 		diagnostic =
18588810c16bSdanmcd 		    (ext->sadb_ext_type == SADB_X_EXT_ADDRESS_INNER_SRC) ?
18598810c16bSdanmcd 		    SADB_X_DIAGNOSTIC_PREFIX_INNER_SRC :
18608810c16bSdanmcd 		    SADB_X_DIAGNOSTIC_PREFIX_INNER_DST;
18618810c16bSdanmcd 
18628810c16bSdanmcd 		if (normalized)
18638810c16bSdanmcd 			addr->sadb_address_prefixlen -= 96;
18648810c16bSdanmcd 
18658810c16bSdanmcd 		/*
18668810c16bSdanmcd 		 * Verify and mask out inner-addresses based on prefix length.
18678810c16bSdanmcd 		 */
18688810c16bSdanmcd 		if (sin->sin_family == AF_INET) {
18698810c16bSdanmcd 			if (addr->sadb_address_prefixlen > 32)
18708810c16bSdanmcd 				goto bail;
18718810c16bSdanmcd 			sin->sin_addr.s_addr &=
18728810c16bSdanmcd 			    ip_plen_to_mask(addr->sadb_address_prefixlen);
18738810c16bSdanmcd 		} else {
18748810c16bSdanmcd 			in6_addr_t mask;
18758810c16bSdanmcd 
18768810c16bSdanmcd 			ASSERT(sin->sin_family == AF_INET6);
18778810c16bSdanmcd 			/*
18788810c16bSdanmcd 			 * ip_plen_to_mask_v6() returns NULL if the value in
18798810c16bSdanmcd 			 * question is out of range.
18808810c16bSdanmcd 			 */
18818810c16bSdanmcd 			if (ip_plen_to_mask_v6(addr->sadb_address_prefixlen,
1882437220cdSdanmcd 			    &mask) == NULL)
18838810c16bSdanmcd 				goto bail;
18848810c16bSdanmcd 			sin6->sin6_addr.s6_addr32[0] &= mask.s6_addr32[0];
18858810c16bSdanmcd 			sin6->sin6_addr.s6_addr32[1] &= mask.s6_addr32[1];
18868810c16bSdanmcd 			sin6->sin6_addr.s6_addr32[2] &= mask.s6_addr32[2];
18878810c16bSdanmcd 			sin6->sin6_addr.s6_addr32[3] &= mask.s6_addr32[3];
18888810c16bSdanmcd 		}
18898810c16bSdanmcd 
18908810c16bSdanmcd 		/* We don't care in these cases. */
18918810c16bSdanmcd 		return (KS_IN_ADDR_DONTCARE);
18928810c16bSdanmcd 	}
18938810c16bSdanmcd 
18948810c16bSdanmcd 	if (sin->sin_family == AF_INET6) {
18958810c16bSdanmcd 		/* Check the easy ones now. */
18968810c16bSdanmcd 		if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
18978810c16bSdanmcd 			return (KS_IN_ADDR_MBCAST);
18988810c16bSdanmcd 		if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr))
18998810c16bSdanmcd 			return (KS_IN_ADDR_UNSPEC);
19008810c16bSdanmcd 		/*
19018810c16bSdanmcd 		 * At this point, we're a unicast IPv6 address.
19028810c16bSdanmcd 		 *
19038810c16bSdanmcd 		 * XXX Zones alert -> me/notme decision needs to be tempered
19048810c16bSdanmcd 		 * by what zone we're in when we go to zone-aware IPsec.
19058810c16bSdanmcd 		 */
1906bd670b35SErik Nordmark 		if (ip_type_v6(&sin6->sin6_addr, ns->netstack_ip) ==
1907bd670b35SErik Nordmark 		    IRE_LOCAL) {
19088810c16bSdanmcd 			/* Hey hey, it's local. */
19098810c16bSdanmcd 			return (KS_IN_ADDR_ME);
19108810c16bSdanmcd 		}
19118810c16bSdanmcd 	} else {
19128810c16bSdanmcd 		ASSERT(sin->sin_family == AF_INET);
19138810c16bSdanmcd 		if (sin->sin_addr.s_addr == INADDR_ANY)
19148810c16bSdanmcd 			return (KS_IN_ADDR_UNSPEC);
19158810c16bSdanmcd 		if (CLASSD(sin->sin_addr.s_addr))
19168810c16bSdanmcd 			return (KS_IN_ADDR_MBCAST);
19178810c16bSdanmcd 		/*
19188810c16bSdanmcd 		 * At this point we're a unicast or broadcast IPv4 address.
19198810c16bSdanmcd 		 *
1920bd670b35SErik Nordmark 		 * Check if the address is IRE_BROADCAST or IRE_LOCAL.
19218810c16bSdanmcd 		 *
19228810c16bSdanmcd 		 * XXX Zones alert -> me/notme decision needs to be tempered
19238810c16bSdanmcd 		 * by what zone we're in when we go to zone-aware IPsec.
19248810c16bSdanmcd 		 */
1925bd670b35SErik Nordmark 		type = ip_type_v4(sin->sin_addr.s_addr, ns->netstack_ip);
1926bd670b35SErik Nordmark 		switch (type) {
1927bd670b35SErik Nordmark 		case IRE_LOCAL:
1928bd670b35SErik Nordmark 			return (KS_IN_ADDR_ME);
1929bd670b35SErik Nordmark 		case IRE_BROADCAST:
1930bd670b35SErik Nordmark 			return (KS_IN_ADDR_MBCAST);
19318810c16bSdanmcd 		}
19328810c16bSdanmcd 	}
19338810c16bSdanmcd 
19348810c16bSdanmcd 	return (KS_IN_ADDR_NOTME);
19357c478bd9Sstevel@tonic-gate }
19367c478bd9Sstevel@tonic-gate 
19377c478bd9Sstevel@tonic-gate /*
19388810c16bSdanmcd  * Address normalizations and reality checks for inbound PF_KEY messages.
19398810c16bSdanmcd  *
19407c478bd9Sstevel@tonic-gate  * For the case of src == unspecified AF_INET6, and dst == AF_INET, convert
19418810c16bSdanmcd  * the source to AF_INET.  Do the same for the inner sources.
19427c478bd9Sstevel@tonic-gate  */
19438810c16bSdanmcd boolean_t
sadb_addrfix(keysock_in_t * ksi,queue_t * pfkey_q,mblk_t * mp,netstack_t * ns)1944f4b3ec61Sdh sadb_addrfix(keysock_in_t *ksi, queue_t *pfkey_q, mblk_t *mp, netstack_t *ns)
19457c478bd9Sstevel@tonic-gate {
19468810c16bSdanmcd 	struct sockaddr_in *src, *isrc;
19478810c16bSdanmcd 	struct sockaddr_in6 *dst, *idst;
19487c478bd9Sstevel@tonic-gate 	sadb_address_t *srcext, *dstext;
194907b56925Ssommerfe 	uint16_t sport;
19508810c16bSdanmcd 	sadb_ext_t **extv = ksi->ks_in_extv;
19518810c16bSdanmcd 	int rc;
19527c478bd9Sstevel@tonic-gate 
19538810c16bSdanmcd 	if (extv[SADB_EXT_ADDRESS_SRC] != NULL) {
19548810c16bSdanmcd 		rc = sadb_addrcheck(pfkey_q, mp, extv[SADB_EXT_ADDRESS_SRC],
1955f4b3ec61Sdh 		    ksi->ks_in_serial, ns);
19568810c16bSdanmcd 		if (rc == KS_IN_ADDR_UNKNOWN)
19578810c16bSdanmcd 			return (B_FALSE);
19588810c16bSdanmcd 		if (rc == KS_IN_ADDR_MBCAST) {
19598810c16bSdanmcd 			sadb_pfkey_error(pfkey_q, mp, EINVAL,
19608810c16bSdanmcd 			    SADB_X_DIAGNOSTIC_BAD_SRC, ksi->ks_in_serial);
19618810c16bSdanmcd 			return (B_FALSE);
19628810c16bSdanmcd 		}
19638810c16bSdanmcd 		ksi->ks_in_srctype = rc;
19648810c16bSdanmcd 	}
19658810c16bSdanmcd 
19668810c16bSdanmcd 	if (extv[SADB_EXT_ADDRESS_DST] != NULL) {
19678810c16bSdanmcd 		rc = sadb_addrcheck(pfkey_q, mp, extv[SADB_EXT_ADDRESS_DST],
1968f4b3ec61Sdh 		    ksi->ks_in_serial, ns);
19698810c16bSdanmcd 		if (rc == KS_IN_ADDR_UNKNOWN)
19708810c16bSdanmcd 			return (B_FALSE);
19718810c16bSdanmcd 		if (rc == KS_IN_ADDR_UNSPEC) {
19728810c16bSdanmcd 			sadb_pfkey_error(pfkey_q, mp, EINVAL,
19738810c16bSdanmcd 			    SADB_X_DIAGNOSTIC_BAD_DST, ksi->ks_in_serial);
19748810c16bSdanmcd 			return (B_FALSE);
19758810c16bSdanmcd 		}
19768810c16bSdanmcd 		ksi->ks_in_dsttype = rc;
19778810c16bSdanmcd 	}
19788810c16bSdanmcd 
19798810c16bSdanmcd 	/*
19808810c16bSdanmcd 	 * NAT-Traversal addrs are simple enough to not require all of
19818810c16bSdanmcd 	 * the checks in sadb_addrcheck().  Just normalize or reject if not
19828810c16bSdanmcd 	 * AF_INET.
19838810c16bSdanmcd 	 */
19848810c16bSdanmcd 	if (extv[SADB_X_EXT_ADDRESS_NATT_LOC] != NULL) {
19858810c16bSdanmcd 		rc = sadb_addrcheck(pfkey_q, mp,
1986f4b3ec61Sdh 		    extv[SADB_X_EXT_ADDRESS_NATT_LOC], ksi->ks_in_serial, ns);
19878810c16bSdanmcd 
19888810c16bSdanmcd 		/*
1989437220cdSdanmcd 		 * Local NAT-T addresses never use an IRE_LOCAL, so it should
1990437220cdSdanmcd 		 * always be NOTME, or UNSPEC (to handle both tunnel mode
1991437220cdSdanmcd 		 * AND local-port flexibility).
19928810c16bSdanmcd 		 */
1993437220cdSdanmcd 		if (rc != KS_IN_ADDR_NOTME && rc != KS_IN_ADDR_UNSPEC) {
1994437220cdSdanmcd 			sadb_pfkey_error(pfkey_q, mp, EINVAL,
1995437220cdSdanmcd 			    SADB_X_DIAGNOSTIC_MALFORMED_NATT_LOC,
1996437220cdSdanmcd 			    ksi->ks_in_serial);
19978810c16bSdanmcd 			return (B_FALSE);
19988810c16bSdanmcd 		}
19998810c16bSdanmcd 		src = (struct sockaddr_in *)
20008810c16bSdanmcd 		    (((sadb_address_t *)extv[SADB_X_EXT_ADDRESS_NATT_LOC]) + 1);
20018810c16bSdanmcd 		if (src->sin_family != AF_INET) {
20028810c16bSdanmcd 			sadb_pfkey_error(pfkey_q, mp, EINVAL,
20038810c16bSdanmcd 			    SADB_X_DIAGNOSTIC_BAD_NATT_LOC_AF,
20048810c16bSdanmcd 			    ksi->ks_in_serial);
20058810c16bSdanmcd 			return (B_FALSE);
20068810c16bSdanmcd 		}
20078810c16bSdanmcd 	}
20088810c16bSdanmcd 
20098810c16bSdanmcd 	if (extv[SADB_X_EXT_ADDRESS_NATT_REM] != NULL) {
20108810c16bSdanmcd 		rc = sadb_addrcheck(pfkey_q, mp,
2011f4b3ec61Sdh 		    extv[SADB_X_EXT_ADDRESS_NATT_REM], ksi->ks_in_serial, ns);
20128810c16bSdanmcd 
20138810c16bSdanmcd 		/*
2014437220cdSdanmcd 		 * Remote NAT-T addresses never use an IRE_LOCAL, so it should
20158810c16bSdanmcd 		 * always be NOTME, or UNSPEC if it's a tunnel-mode SA.
20168810c16bSdanmcd 		 */
20178810c16bSdanmcd 		if (rc != KS_IN_ADDR_NOTME &&
20188810c16bSdanmcd 		    !(extv[SADB_X_EXT_ADDRESS_INNER_SRC] != NULL &&
2019437220cdSdanmcd 		    rc == KS_IN_ADDR_UNSPEC)) {
2020437220cdSdanmcd 			sadb_pfkey_error(pfkey_q, mp, EINVAL,
2021437220cdSdanmcd 			    SADB_X_DIAGNOSTIC_MALFORMED_NATT_REM,
2022437220cdSdanmcd 			    ksi->ks_in_serial);
20238810c16bSdanmcd 			return (B_FALSE);
20248810c16bSdanmcd 		}
20258810c16bSdanmcd 		src = (struct sockaddr_in *)
20268810c16bSdanmcd 		    (((sadb_address_t *)extv[SADB_X_EXT_ADDRESS_NATT_REM]) + 1);
20278810c16bSdanmcd 		if (src->sin_family != AF_INET) {
20288810c16bSdanmcd 			sadb_pfkey_error(pfkey_q, mp, EINVAL,
20298810c16bSdanmcd 			    SADB_X_DIAGNOSTIC_BAD_NATT_REM_AF,
20308810c16bSdanmcd 			    ksi->ks_in_serial);
20318810c16bSdanmcd 			return (B_FALSE);
20328810c16bSdanmcd 		}
20338810c16bSdanmcd 	}
20348810c16bSdanmcd 
20358810c16bSdanmcd 	if (extv[SADB_X_EXT_ADDRESS_INNER_SRC] != NULL) {
20368810c16bSdanmcd 		if (extv[SADB_X_EXT_ADDRESS_INNER_DST] == NULL) {
20378810c16bSdanmcd 			sadb_pfkey_error(pfkey_q, mp, EINVAL,
20388810c16bSdanmcd 			    SADB_X_DIAGNOSTIC_MISSING_INNER_DST,
20398810c16bSdanmcd 			    ksi->ks_in_serial);
20408810c16bSdanmcd 			return (B_FALSE);
20418810c16bSdanmcd 		}
20428810c16bSdanmcd 
20438810c16bSdanmcd 		if (sadb_addrcheck(pfkey_q, mp,
2044f4b3ec61Sdh 		    extv[SADB_X_EXT_ADDRESS_INNER_DST], ksi->ks_in_serial, ns)
20458810c16bSdanmcd 		    == KS_IN_ADDR_UNKNOWN ||
20468810c16bSdanmcd 		    sadb_addrcheck(pfkey_q, mp,
2047f4b3ec61Sdh 		    extv[SADB_X_EXT_ADDRESS_INNER_SRC], ksi->ks_in_serial, ns)
20488810c16bSdanmcd 		    == KS_IN_ADDR_UNKNOWN)
20498810c16bSdanmcd 			return (B_FALSE);
20508810c16bSdanmcd 
20518810c16bSdanmcd 		isrc = (struct sockaddr_in *)
20528810c16bSdanmcd 		    (((sadb_address_t *)extv[SADB_X_EXT_ADDRESS_INNER_SRC]) +
2053437220cdSdanmcd 		    1);
20548810c16bSdanmcd 		idst = (struct sockaddr_in6 *)
20558810c16bSdanmcd 		    (((sadb_address_t *)extv[SADB_X_EXT_ADDRESS_INNER_DST]) +
2056437220cdSdanmcd 		    1);
20578810c16bSdanmcd 		if (isrc->sin_family != idst->sin6_family) {
20588810c16bSdanmcd 			sadb_pfkey_error(pfkey_q, mp, EINVAL,
20598810c16bSdanmcd 			    SADB_X_DIAGNOSTIC_INNER_AF_MISMATCH,
20608810c16bSdanmcd 			    ksi->ks_in_serial);
20618810c16bSdanmcd 			return (B_FALSE);
20628810c16bSdanmcd 		}
20638810c16bSdanmcd 	} else if (extv[SADB_X_EXT_ADDRESS_INNER_DST] != NULL) {
20648810c16bSdanmcd 			sadb_pfkey_error(pfkey_q, mp, EINVAL,
20658810c16bSdanmcd 			    SADB_X_DIAGNOSTIC_MISSING_INNER_SRC,
20668810c16bSdanmcd 			    ksi->ks_in_serial);
20678810c16bSdanmcd 			return (B_FALSE);
20688810c16bSdanmcd 	} else {
20698810c16bSdanmcd 		isrc = NULL;	/* For inner/outer port check below. */
20708810c16bSdanmcd 	}
20718810c16bSdanmcd 
20728810c16bSdanmcd 	dstext = (sadb_address_t *)extv[SADB_EXT_ADDRESS_DST];
20738810c16bSdanmcd 	srcext = (sadb_address_t *)extv[SADB_EXT_ADDRESS_SRC];
20748810c16bSdanmcd 
20758810c16bSdanmcd 	if (dstext == NULL || srcext == NULL)
20768810c16bSdanmcd 		return (B_TRUE);
20777c478bd9Sstevel@tonic-gate 
20787c478bd9Sstevel@tonic-gate 	dst = (struct sockaddr_in6 *)(dstext + 1);
20797c478bd9Sstevel@tonic-gate 	src = (struct sockaddr_in *)(srcext + 1);
20807c478bd9Sstevel@tonic-gate 
20818810c16bSdanmcd 	if (isrc != NULL &&
20828810c16bSdanmcd 	    (isrc->sin_port != 0 || idst->sin6_port != 0) &&
20838810c16bSdanmcd 	    (src->sin_port != 0 || dst->sin6_port != 0)) {
20848810c16bSdanmcd 		/* Can't set inner and outer ports in one SA. */
20858810c16bSdanmcd 		sadb_pfkey_error(pfkey_q, mp, EINVAL,
20868810c16bSdanmcd 		    SADB_X_DIAGNOSTIC_DUAL_PORT_SETS,
20878810c16bSdanmcd 		    ksi->ks_in_serial);
20888810c16bSdanmcd 		return (B_FALSE);
20898810c16bSdanmcd 	}
20908810c16bSdanmcd 
20918810c16bSdanmcd 	if (dst->sin6_family == src->sin_family)
20928810c16bSdanmcd 		return (B_TRUE);
20938810c16bSdanmcd 
20948810c16bSdanmcd 	if (srcext->sadb_address_proto != dstext->sadb_address_proto) {
20958810c16bSdanmcd 		if (srcext->sadb_address_proto == 0) {
20968810c16bSdanmcd 			srcext->sadb_address_proto = dstext->sadb_address_proto;
20978810c16bSdanmcd 		} else if (dstext->sadb_address_proto == 0) {
20988810c16bSdanmcd 			dstext->sadb_address_proto = srcext->sadb_address_proto;
20998810c16bSdanmcd 		} else {
21008810c16bSdanmcd 			/* Inequal protocols, neither were 0.  Report error. */
21018810c16bSdanmcd 			sadb_pfkey_error(pfkey_q, mp, EINVAL,
21028810c16bSdanmcd 			    SADB_X_DIAGNOSTIC_PROTO_MISMATCH,
21038810c16bSdanmcd 			    ksi->ks_in_serial);
21048810c16bSdanmcd 			return (B_FALSE);
21058810c16bSdanmcd 		}
21068810c16bSdanmcd 	}
21078810c16bSdanmcd 
21087c478bd9Sstevel@tonic-gate 	/*
21098810c16bSdanmcd 	 * With the exception of an unspec IPv6 source and an IPv4
21108810c16bSdanmcd 	 * destination, address families MUST me matched.
21117c478bd9Sstevel@tonic-gate 	 */
21128810c16bSdanmcd 	if (src->sin_family == AF_INET ||
21138810c16bSdanmcd 	    ksi->ks_in_srctype != KS_IN_ADDR_UNSPEC) {
21148810c16bSdanmcd 		sadb_pfkey_error(pfkey_q, mp, EINVAL,
21158810c16bSdanmcd 		    SADB_X_DIAGNOSTIC_AF_MISMATCH, ksi->ks_in_serial);
21168810c16bSdanmcd 		return (B_FALSE);
21178810c16bSdanmcd 	}
21187c478bd9Sstevel@tonic-gate 
211907b56925Ssommerfe 	/*
212007b56925Ssommerfe 	 * Convert "src" to AF_INET INADDR_ANY.  We rely on sin_port being
212107b56925Ssommerfe 	 * in the same place for sockaddr_in and sockaddr_in6.
212207b56925Ssommerfe 	 */
212307b56925Ssommerfe 	sport = src->sin_port;
21247c478bd9Sstevel@tonic-gate 	bzero(src, sizeof (*src));
21257c478bd9Sstevel@tonic-gate 	src->sin_family = AF_INET;
212607b56925Ssommerfe 	src->sin_port = sport;
21278810c16bSdanmcd 
21288810c16bSdanmcd 	return (B_TRUE);
21297c478bd9Sstevel@tonic-gate }
21307c478bd9Sstevel@tonic-gate 
21317c478bd9Sstevel@tonic-gate /*
21327c478bd9Sstevel@tonic-gate  * Set the results in "addrtype", given an IRE as requested by
21337c478bd9Sstevel@tonic-gate  * sadb_addrcheck().
21347c478bd9Sstevel@tonic-gate  */
21357c478bd9Sstevel@tonic-gate int
sadb_addrset(ire_t * ire)21367c478bd9Sstevel@tonic-gate sadb_addrset(ire_t *ire)
21377c478bd9Sstevel@tonic-gate {
21387c478bd9Sstevel@tonic-gate 	if ((ire->ire_type & IRE_BROADCAST) ||
21397c478bd9Sstevel@tonic-gate 	    (ire->ire_ipversion == IPV4_VERSION && CLASSD(ire->ire_addr)) ||
21407c478bd9Sstevel@tonic-gate 	    (ire->ire_ipversion == IPV6_VERSION &&
2141437220cdSdanmcd 	    IN6_IS_ADDR_MULTICAST(&(ire->ire_addr_v6))))
21427c478bd9Sstevel@tonic-gate 		return (KS_IN_ADDR_MBCAST);
21437c478bd9Sstevel@tonic-gate 	if (ire->ire_type & (IRE_LOCAL | IRE_LOOPBACK))
21447c478bd9Sstevel@tonic-gate 		return (KS_IN_ADDR_ME);
21457c478bd9Sstevel@tonic-gate 	return (KS_IN_ADDR_NOTME);
21467c478bd9Sstevel@tonic-gate }
21477c478bd9Sstevel@tonic-gate 
21485d3b8cb7SBill Sommerfeld /*
21495d3b8cb7SBill Sommerfeld  * Match primitives..
21505d3b8cb7SBill Sommerfeld  * !!! TODO: short term: inner selectors
21515d3b8cb7SBill Sommerfeld  *		ipv6 scope id (ifindex)
21525d3b8cb7SBill Sommerfeld  * longer term:  zone id.  sensitivity label. uid.
21535d3b8cb7SBill Sommerfeld  */
21545d3b8cb7SBill Sommerfeld boolean_t
sadb_match_spi(ipsa_query_t * sq,ipsa_t * sa)21555d3b8cb7SBill Sommerfeld sadb_match_spi(ipsa_query_t *sq, ipsa_t *sa)
21565d3b8cb7SBill Sommerfeld {
21575d3b8cb7SBill Sommerfeld 	return (sq->spi == sa->ipsa_spi);
21585d3b8cb7SBill Sommerfeld }
21595d3b8cb7SBill Sommerfeld 
21605d3b8cb7SBill Sommerfeld boolean_t
sadb_match_dst_v6(ipsa_query_t * sq,ipsa_t * sa)21615d3b8cb7SBill Sommerfeld sadb_match_dst_v6(ipsa_query_t *sq, ipsa_t *sa)
21625d3b8cb7SBill Sommerfeld {
21635d3b8cb7SBill Sommerfeld 	return (IPSA_ARE_ADDR_EQUAL(sa->ipsa_dstaddr, sq->dstaddr, AF_INET6));
21645d3b8cb7SBill Sommerfeld }
21655d3b8cb7SBill Sommerfeld 
21665d3b8cb7SBill Sommerfeld boolean_t
sadb_match_src_v6(ipsa_query_t * sq,ipsa_t * sa)21675d3b8cb7SBill Sommerfeld sadb_match_src_v6(ipsa_query_t *sq, ipsa_t *sa)
21685d3b8cb7SBill Sommerfeld {
21695d3b8cb7SBill Sommerfeld 	return (IPSA_ARE_ADDR_EQUAL(sa->ipsa_srcaddr, sq->srcaddr, AF_INET6));
21705d3b8cb7SBill Sommerfeld }
21715d3b8cb7SBill Sommerfeld 
21725d3b8cb7SBill Sommerfeld boolean_t
sadb_match_dst_v4(ipsa_query_t * sq,ipsa_t * sa)21735d3b8cb7SBill Sommerfeld sadb_match_dst_v4(ipsa_query_t *sq, ipsa_t *sa)
21745d3b8cb7SBill Sommerfeld {
21755d3b8cb7SBill Sommerfeld 	return (sq->dstaddr[0] == sa->ipsa_dstaddr[0]);
21765d3b8cb7SBill Sommerfeld }
21775d3b8cb7SBill Sommerfeld 
21785d3b8cb7SBill Sommerfeld boolean_t
sadb_match_src_v4(ipsa_query_t * sq,ipsa_t * sa)21795d3b8cb7SBill Sommerfeld sadb_match_src_v4(ipsa_query_t *sq, ipsa_t *sa)
21805d3b8cb7SBill Sommerfeld {
21815d3b8cb7SBill Sommerfeld 	return (sq->srcaddr[0] == sa->ipsa_srcaddr[0]);
21825d3b8cb7SBill Sommerfeld }
21835d3b8cb7SBill Sommerfeld 
21845d3b8cb7SBill Sommerfeld boolean_t
sadb_match_dstid(ipsa_query_t * sq,ipsa_t * sa)21855d3b8cb7SBill Sommerfeld sadb_match_dstid(ipsa_query_t *sq, ipsa_t *sa)
21865d3b8cb7SBill Sommerfeld {
21875d3b8cb7SBill Sommerfeld 	return ((sa->ipsa_dst_cid != NULL) &&
21885d3b8cb7SBill Sommerfeld 	    (sq->didtype == sa->ipsa_dst_cid->ipsid_type) &&
21895d3b8cb7SBill Sommerfeld 	    (strcmp(sq->didstr, sa->ipsa_dst_cid->ipsid_cid) == 0));
21905d3b8cb7SBill Sommerfeld 
21915d3b8cb7SBill Sommerfeld }
21925d3b8cb7SBill Sommerfeld boolean_t
sadb_match_srcid(ipsa_query_t * sq,ipsa_t * sa)21935d3b8cb7SBill Sommerfeld sadb_match_srcid(ipsa_query_t *sq, ipsa_t *sa)
21945d3b8cb7SBill Sommerfeld {
21955d3b8cb7SBill Sommerfeld 	return ((sa->ipsa_src_cid != NULL) &&
21965d3b8cb7SBill Sommerfeld 	    (sq->sidtype == sa->ipsa_src_cid->ipsid_type) &&
21975d3b8cb7SBill Sommerfeld 	    (strcmp(sq->sidstr, sa->ipsa_src_cid->ipsid_cid) == 0));
21985d3b8cb7SBill Sommerfeld }
21995d3b8cb7SBill Sommerfeld 
22005d3b8cb7SBill Sommerfeld boolean_t
sadb_match_kmc(ipsa_query_t * sq,ipsa_t * sa)22015d3b8cb7SBill Sommerfeld sadb_match_kmc(ipsa_query_t *sq, ipsa_t *sa)
22025d3b8cb7SBill Sommerfeld {
22035d3b8cb7SBill Sommerfeld #define	M(a, b) (((a) == 0) || ((b) == 0) || ((a) == (b)))
22045d3b8cb7SBill Sommerfeld 
22055d3b8cb7SBill Sommerfeld 	return (M(sq->kmc, sa->ipsa_kmc) && M(sq->kmp, sa->ipsa_kmp));
22065d3b8cb7SBill Sommerfeld 
22075d3b8cb7SBill Sommerfeld #undef M
22085d3b8cb7SBill Sommerfeld }
22095d3b8cb7SBill Sommerfeld 
22105d3b8cb7SBill Sommerfeld /*
22115d3b8cb7SBill Sommerfeld  * Common function which extracts several PF_KEY extensions for ease of
22125d3b8cb7SBill Sommerfeld  * SADB matching.
22135d3b8cb7SBill Sommerfeld  *
22145d3b8cb7SBill Sommerfeld  * XXX TODO: weed out ipsa_query_t fields not used during matching
22155d3b8cb7SBill Sommerfeld  * or afterwards?
22165d3b8cb7SBill Sommerfeld  */
22175d3b8cb7SBill Sommerfeld int
sadb_form_query(keysock_in_t * ksi,uint32_t req,uint32_t match,ipsa_query_t * sq,int * diagnostic)22185d3b8cb7SBill Sommerfeld sadb_form_query(keysock_in_t *ksi, uint32_t req, uint32_t match,
22195d3b8cb7SBill Sommerfeld     ipsa_query_t *sq, int *diagnostic)
22205d3b8cb7SBill Sommerfeld {
22215d3b8cb7SBill Sommerfeld 	int i;
22225d3b8cb7SBill Sommerfeld 	ipsa_match_fn_t *mfpp = &(sq->matchers[0]);
22235d3b8cb7SBill Sommerfeld 
22245d3b8cb7SBill Sommerfeld 	for (i = 0; i < IPSA_NMATCH; i++)
22255d3b8cb7SBill Sommerfeld 		sq->matchers[i] = NULL;
22265d3b8cb7SBill Sommerfeld 
22275d3b8cb7SBill Sommerfeld 	ASSERT((req & ~match) == 0);
22285d3b8cb7SBill Sommerfeld 
22295d3b8cb7SBill Sommerfeld 	sq->req = req;
22305d3b8cb7SBill Sommerfeld 	sq->dstext = (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_DST];
22315d3b8cb7SBill Sommerfeld 	sq->srcext = (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_SRC];
22325d3b8cb7SBill Sommerfeld 	sq->assoc = (sadb_sa_t *)ksi->ks_in_extv[SADB_EXT_SA];
22335d3b8cb7SBill Sommerfeld 
22345d3b8cb7SBill Sommerfeld 	if ((req & IPSA_Q_DST) && (sq->dstext == NULL)) {
22355d3b8cb7SBill Sommerfeld 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_DST;
22365d3b8cb7SBill Sommerfeld 		return (EINVAL);
22375d3b8cb7SBill Sommerfeld 	}
22385d3b8cb7SBill Sommerfeld 	if ((req & IPSA_Q_SRC) && (sq->srcext == NULL)) {
22395d3b8cb7SBill Sommerfeld 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_SRC;
22405d3b8cb7SBill Sommerfeld 		return (EINVAL);
22415d3b8cb7SBill Sommerfeld 	}
22425d3b8cb7SBill Sommerfeld 	if ((req & IPSA_Q_SA) && (sq->assoc == NULL)) {
22435d3b8cb7SBill Sommerfeld 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_SA;
22445d3b8cb7SBill Sommerfeld 		return (EINVAL);
22455d3b8cb7SBill Sommerfeld 	}
22465d3b8cb7SBill Sommerfeld 
22475d3b8cb7SBill Sommerfeld 	if (match & IPSA_Q_SA) {
22485d3b8cb7SBill Sommerfeld 		*mfpp++ = sadb_match_spi;
22495d3b8cb7SBill Sommerfeld 		sq->spi = sq->assoc->sadb_sa_spi;
22505d3b8cb7SBill Sommerfeld 	}
22515d3b8cb7SBill Sommerfeld 
22525d3b8cb7SBill Sommerfeld 	if (sq->dstext != NULL)
22535d3b8cb7SBill Sommerfeld 		sq->dst = (struct sockaddr_in *)(sq->dstext + 1);
22545d3b8cb7SBill Sommerfeld 	else {
22555d3b8cb7SBill Sommerfeld 		sq->dst = NULL;
22565d3b8cb7SBill Sommerfeld 		sq->dst6 = NULL;
22575d3b8cb7SBill Sommerfeld 		sq->dstaddr = NULL;
22585d3b8cb7SBill Sommerfeld 	}
22595d3b8cb7SBill Sommerfeld 
22605d3b8cb7SBill Sommerfeld 	if (sq->srcext != NULL)
22615d3b8cb7SBill Sommerfeld 		sq->src = (struct sockaddr_in *)(sq->srcext + 1);
22625d3b8cb7SBill Sommerfeld 	else {
22635d3b8cb7SBill Sommerfeld 		sq->src = NULL;
22645d3b8cb7SBill Sommerfeld 		sq->src6 = NULL;
22655d3b8cb7SBill Sommerfeld 		sq->srcaddr = NULL;
22665d3b8cb7SBill Sommerfeld 	}
22675d3b8cb7SBill Sommerfeld 
22685d3b8cb7SBill Sommerfeld 	if (sq->dst != NULL)
22695d3b8cb7SBill Sommerfeld 		sq->af = sq->dst->sin_family;
22705d3b8cb7SBill Sommerfeld 	else if (sq->src != NULL)
22715d3b8cb7SBill Sommerfeld 		sq->af = sq->src->sin_family;
22725d3b8cb7SBill Sommerfeld 	else
22735d3b8cb7SBill Sommerfeld 		sq->af = AF_INET;
22745d3b8cb7SBill Sommerfeld 
22755d3b8cb7SBill Sommerfeld 	if (sq->af == AF_INET6) {
22765d3b8cb7SBill Sommerfeld 		if ((match & IPSA_Q_DST) && (sq->dstext != NULL)) {
22775d3b8cb7SBill Sommerfeld 			*mfpp++ = sadb_match_dst_v6;
22785d3b8cb7SBill Sommerfeld 			sq->dst6 = (struct sockaddr_in6 *)sq->dst;
22795d3b8cb7SBill Sommerfeld 			sq->dstaddr = (uint32_t *)&(sq->dst6->sin6_addr);
22805d3b8cb7SBill Sommerfeld 		} else {
22815d3b8cb7SBill Sommerfeld 			match &= ~IPSA_Q_DST;
22825d3b8cb7SBill Sommerfeld 			sq->dstaddr = ALL_ZEROES_PTR;
22835d3b8cb7SBill Sommerfeld 		}
22845d3b8cb7SBill Sommerfeld 
22855d3b8cb7SBill Sommerfeld 		if ((match & IPSA_Q_SRC) && (sq->srcext != NULL)) {
22865d3b8cb7SBill Sommerfeld 			sq->src6 = (struct sockaddr_in6 *)(sq->srcext + 1);
22875d3b8cb7SBill Sommerfeld 			sq->srcaddr = (uint32_t *)&sq->src6->sin6_addr;
22885d3b8cb7SBill Sommerfeld 			if (sq->src6->sin6_family != AF_INET6) {
22895d3b8cb7SBill Sommerfeld 				*diagnostic = SADB_X_DIAGNOSTIC_AF_MISMATCH;
22905d3b8cb7SBill Sommerfeld 				return (EINVAL);
22915d3b8cb7SBill Sommerfeld 			}
22925d3b8cb7SBill Sommerfeld 			*mfpp++ = sadb_match_src_v6;
22935d3b8cb7SBill Sommerfeld 		} else {
22945d3b8cb7SBill Sommerfeld 			match &= ~IPSA_Q_SRC;
22955d3b8cb7SBill Sommerfeld 			sq->srcaddr = ALL_ZEROES_PTR;
22965d3b8cb7SBill Sommerfeld 		}
22975d3b8cb7SBill Sommerfeld 	} else {
22985d3b8cb7SBill Sommerfeld 		sq->src6 = sq->dst6 = NULL;
22995d3b8cb7SBill Sommerfeld 		if ((match & IPSA_Q_DST) && (sq->dstext != NULL)) {
23005d3b8cb7SBill Sommerfeld 			*mfpp++ = sadb_match_dst_v4;
23015d3b8cb7SBill Sommerfeld 			sq->dstaddr = (uint32_t *)&sq->dst->sin_addr;
23025d3b8cb7SBill Sommerfeld 		} else {
23035d3b8cb7SBill Sommerfeld 			match &= ~IPSA_Q_DST;
23045d3b8cb7SBill Sommerfeld 			sq->dstaddr = ALL_ZEROES_PTR;
23055d3b8cb7SBill Sommerfeld 		}
23065d3b8cb7SBill Sommerfeld 		if ((match & IPSA_Q_SRC) && (sq->srcext != NULL)) {
23075d3b8cb7SBill Sommerfeld 			sq->srcaddr = (uint32_t *)&sq->src->sin_addr;
23085d3b8cb7SBill Sommerfeld 			if (sq->src->sin_family != AF_INET) {
23095d3b8cb7SBill Sommerfeld 				*diagnostic = SADB_X_DIAGNOSTIC_AF_MISMATCH;
23105d3b8cb7SBill Sommerfeld 				return (EINVAL);
23115d3b8cb7SBill Sommerfeld 			}
23125d3b8cb7SBill Sommerfeld 			*mfpp++ = sadb_match_src_v4;
23135d3b8cb7SBill Sommerfeld 		} else {
23145d3b8cb7SBill Sommerfeld 			match &= ~IPSA_Q_SRC;
23155d3b8cb7SBill Sommerfeld 			sq->srcaddr = ALL_ZEROES_PTR;
23165d3b8cb7SBill Sommerfeld 		}
23175d3b8cb7SBill Sommerfeld 	}
23185d3b8cb7SBill Sommerfeld 
23195d3b8cb7SBill Sommerfeld 	sq->dstid = (sadb_ident_t *)ksi->ks_in_extv[SADB_EXT_IDENTITY_DST];
23205d3b8cb7SBill Sommerfeld 	if ((match & IPSA_Q_DSTID) && (sq->dstid != NULL)) {
23215d3b8cb7SBill Sommerfeld 		sq->didstr = (char *)(sq->dstid + 1);
23225d3b8cb7SBill Sommerfeld 		sq->didtype = sq->dstid->sadb_ident_type;
23235d3b8cb7SBill Sommerfeld 		*mfpp++ = sadb_match_dstid;
23245d3b8cb7SBill Sommerfeld 	}
23255d3b8cb7SBill Sommerfeld 
23265d3b8cb7SBill Sommerfeld 	sq->srcid = (sadb_ident_t *)ksi->ks_in_extv[SADB_EXT_IDENTITY_SRC];
23275d3b8cb7SBill Sommerfeld 
23285d3b8cb7SBill Sommerfeld 	if ((match & IPSA_Q_SRCID) && (sq->srcid != NULL)) {
23295d3b8cb7SBill Sommerfeld 		sq->sidstr = (char *)(sq->srcid + 1);
23305d3b8cb7SBill Sommerfeld 		sq->sidtype = sq->srcid->sadb_ident_type;
23315d3b8cb7SBill Sommerfeld 		*mfpp++ = sadb_match_srcid;
23325d3b8cb7SBill Sommerfeld 	}
23335d3b8cb7SBill Sommerfeld 
23345d3b8cb7SBill Sommerfeld 	sq->kmcext = (sadb_x_kmc_t *)ksi->ks_in_extv[SADB_X_EXT_KM_COOKIE];
23355d3b8cb7SBill Sommerfeld 	sq->kmc = 0;
23365d3b8cb7SBill Sommerfeld 	sq->kmp = 0;
23375d3b8cb7SBill Sommerfeld 
23385d3b8cb7SBill Sommerfeld 	if ((match & IPSA_Q_KMC) && (sq->kmcext)) {
23395d3b8cb7SBill Sommerfeld 		sq->kmp = sq->kmcext->sadb_x_kmc_proto;
2340cbc1abb4SDan McDonald 		/*
2341cbc1abb4SDan McDonald 		 * Be liberal in what we receive.  Special-case the IKEv1
2342cbc1abb4SDan McDonald 		 * cookie, which closed-source in.iked assumes is 32 bits.
2343cbc1abb4SDan McDonald 		 * Now that we store all 64 bits, we should pre-zero the
2344cbc1abb4SDan McDonald 		 * reserved field on behalf of closed-source in.iked.
2345cbc1abb4SDan McDonald 		 */
2346f4a6f97eSDan McDonald 		if (sq->kmp == SADB_X_KMP_IKE) {
2347f4a6f97eSDan McDonald 			/* Just in case in.iked is misbehaving... */
2348f4a6f97eSDan McDonald 			sq->kmcext->sadb_x_kmc_reserved = 0;
2349f4a6f97eSDan McDonald 		}
2350f4a6f97eSDan McDonald 		sq->kmc = sq->kmcext->sadb_x_kmc_cookie64;
23515d3b8cb7SBill Sommerfeld 		*mfpp++ = sadb_match_kmc;
23525d3b8cb7SBill Sommerfeld 	}
23535d3b8cb7SBill Sommerfeld 
23545d3b8cb7SBill Sommerfeld 	if (match & (IPSA_Q_INBOUND|IPSA_Q_OUTBOUND)) {
23555d3b8cb7SBill Sommerfeld 		if (sq->af == AF_INET6)
23565d3b8cb7SBill Sommerfeld 			sq->sp = &sq->spp->s_v6;
23575d3b8cb7SBill Sommerfeld 		else
23585d3b8cb7SBill Sommerfeld 			sq->sp = &sq->spp->s_v4;
23595d3b8cb7SBill Sommerfeld 	} else {
23605d3b8cb7SBill Sommerfeld 		sq->sp = NULL;
23615d3b8cb7SBill Sommerfeld 	}
23625d3b8cb7SBill Sommerfeld 
23635d3b8cb7SBill Sommerfeld 	if (match & IPSA_Q_INBOUND) {
23645d3b8cb7SBill Sommerfeld 		sq->inhash = INBOUND_HASH(sq->sp, sq->assoc->sadb_sa_spi);
23655d3b8cb7SBill Sommerfeld 		sq->inbound = &sq->sp->sdb_if[sq->inhash];
23665d3b8cb7SBill Sommerfeld 	} else {
23675d3b8cb7SBill Sommerfeld 		sq->inhash = 0;
23685d3b8cb7SBill Sommerfeld 		sq->inbound = NULL;
23695d3b8cb7SBill Sommerfeld 	}
23705d3b8cb7SBill Sommerfeld 
23715d3b8cb7SBill Sommerfeld 	if (match & IPSA_Q_OUTBOUND) {
23725d3b8cb7SBill Sommerfeld 		if (sq->af == AF_INET6) {
23735d3b8cb7SBill Sommerfeld 			sq->outhash = OUTBOUND_HASH_V6(sq->sp, *(sq->dstaddr));
23745d3b8cb7SBill Sommerfeld 		} else {
23755d3b8cb7SBill Sommerfeld 			sq->outhash = OUTBOUND_HASH_V4(sq->sp, *(sq->dstaddr));
23765d3b8cb7SBill Sommerfeld 		}
23775d3b8cb7SBill Sommerfeld 		sq->outbound = &sq->sp->sdb_of[sq->outhash];
23785d3b8cb7SBill Sommerfeld 	} else {
23795d3b8cb7SBill Sommerfeld 		sq->outhash = 0;
23805d3b8cb7SBill Sommerfeld 		sq->outbound = NULL;
23815d3b8cb7SBill Sommerfeld 	}
23825d3b8cb7SBill Sommerfeld 	sq->match = match;
23835d3b8cb7SBill Sommerfeld 	return (0);
23845d3b8cb7SBill Sommerfeld }
23855d3b8cb7SBill Sommerfeld 
23865d3b8cb7SBill Sommerfeld /*
23875d3b8cb7SBill Sommerfeld  * Match an initialized query structure with a security association;
23885d3b8cb7SBill Sommerfeld  * return B_TRUE on a match, B_FALSE on a miss.
23895d3b8cb7SBill Sommerfeld  * Applies match functions set up by sadb_form_query() until one returns false.
23905d3b8cb7SBill Sommerfeld  */
23915d3b8cb7SBill Sommerfeld boolean_t
sadb_match_query(ipsa_query_t * sq,ipsa_t * sa)23925d3b8cb7SBill Sommerfeld sadb_match_query(ipsa_query_t *sq, ipsa_t *sa)
23935d3b8cb7SBill Sommerfeld {
23945d3b8cb7SBill Sommerfeld 	ipsa_match_fn_t *mfpp = &(sq->matchers[0]);
23955d3b8cb7SBill Sommerfeld 	ipsa_match_fn_t mfp;
23965d3b8cb7SBill Sommerfeld 
23975d3b8cb7SBill Sommerfeld 	for (mfp = *mfpp++; mfp != NULL; mfp = *mfpp++) {
23985d3b8cb7SBill Sommerfeld 		if (!mfp(sq, sa))
23995d3b8cb7SBill Sommerfeld 			return (B_FALSE);
24005d3b8cb7SBill Sommerfeld 	}
24015d3b8cb7SBill Sommerfeld 	return (B_TRUE);
24025d3b8cb7SBill Sommerfeld }
24037c478bd9Sstevel@tonic-gate 
24047c478bd9Sstevel@tonic-gate /*
24057c478bd9Sstevel@tonic-gate  * Walker callback function to delete sa's based on src/dst address.
24067c478bd9Sstevel@tonic-gate  * Assumes that we're called with *head locked, no other locks held;
24077c478bd9Sstevel@tonic-gate  * Conveniently, and not coincidentally, this is both what sadb_walker
24087c478bd9Sstevel@tonic-gate  * gives us and also what sadb_unlinkassoc expects.
24097c478bd9Sstevel@tonic-gate  */
24107c478bd9Sstevel@tonic-gate struct sadb_purge_state
24117c478bd9Sstevel@tonic-gate {
24125d3b8cb7SBill Sommerfeld 	ipsa_query_t sq;
24137c478bd9Sstevel@tonic-gate 	boolean_t inbnd;
24149c2c14abSThejaswini Singarajipura 	uint8_t sadb_sa_state;
24157c478bd9Sstevel@tonic-gate };
24167c478bd9Sstevel@tonic-gate 
24177c478bd9Sstevel@tonic-gate static void
sadb_purge_cb(isaf_t * head,ipsa_t * entry,void * cookie)24187c478bd9Sstevel@tonic-gate sadb_purge_cb(isaf_t *head, ipsa_t *entry, void *cookie)
24197c478bd9Sstevel@tonic-gate {
24207c478bd9Sstevel@tonic-gate 	struct sadb_purge_state *ps = (struct sadb_purge_state *)cookie;
24217c478bd9Sstevel@tonic-gate 
24227c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&head->isaf_lock));
24237c478bd9Sstevel@tonic-gate 
24247c478bd9Sstevel@tonic-gate 	mutex_enter(&entry->ipsa_lock);
24257c478bd9Sstevel@tonic-gate 
24265d3b8cb7SBill Sommerfeld 	if (entry->ipsa_state == IPSA_STATE_LARVAL ||
24275d3b8cb7SBill Sommerfeld 	    !sadb_match_query(&ps->sq, entry)) {
24287c478bd9Sstevel@tonic-gate 		mutex_exit(&entry->ipsa_lock);
24297c478bd9Sstevel@tonic-gate 		return;
24307c478bd9Sstevel@tonic-gate 	}
24317c478bd9Sstevel@tonic-gate 
24329c2c14abSThejaswini Singarajipura 	if (ps->inbnd) {
24339c2c14abSThejaswini Singarajipura 		sadb_delete_cluster(entry);
24349c2c14abSThejaswini Singarajipura 	}
24357c478bd9Sstevel@tonic-gate 	entry->ipsa_state = IPSA_STATE_DEAD;
2436bd670b35SErik Nordmark 	(void) sadb_torch_assoc(head, entry);
24377c478bd9Sstevel@tonic-gate }
24387c478bd9Sstevel@tonic-gate 
24397c478bd9Sstevel@tonic-gate /*
24407c478bd9Sstevel@tonic-gate  * Common code to purge an SA with a matching src or dst address.
24417c478bd9Sstevel@tonic-gate  * Don't kill larval SA's in such a purge.
24427c478bd9Sstevel@tonic-gate  */
24437c478bd9Sstevel@tonic-gate int
sadb_purge_sa(mblk_t * mp,keysock_in_t * ksi,sadb_t * sp,int * diagnostic,queue_t * pfkey_q)24445d3b8cb7SBill Sommerfeld sadb_purge_sa(mblk_t *mp, keysock_in_t *ksi, sadb_t *sp,
244569e71331SBayard Bell     int *diagnostic, queue_t *pfkey_q)
24467c478bd9Sstevel@tonic-gate {
24477c478bd9Sstevel@tonic-gate 	struct sadb_purge_state ps;
24485d3b8cb7SBill Sommerfeld 	int error = sadb_form_query(ksi, 0,
24495d3b8cb7SBill Sommerfeld 	    IPSA_Q_SRC|IPSA_Q_DST|IPSA_Q_SRCID|IPSA_Q_DSTID|IPSA_Q_KMC,
24505d3b8cb7SBill Sommerfeld 	    &ps.sq, diagnostic);
24517c478bd9Sstevel@tonic-gate 
24525d3b8cb7SBill Sommerfeld 	if (error != 0)
24535d3b8cb7SBill Sommerfeld 		return (error);
24547c478bd9Sstevel@tonic-gate 
24557c478bd9Sstevel@tonic-gate 	/*
24567c478bd9Sstevel@tonic-gate 	 * This is simple, crude, and effective.
24577c478bd9Sstevel@tonic-gate 	 * Unimplemented optimizations (TBD):
24587c478bd9Sstevel@tonic-gate 	 * - we can limit how many places we search based on where we
24597c478bd9Sstevel@tonic-gate 	 * think the SA is filed.
24607c478bd9Sstevel@tonic-gate 	 * - if we get a dst address, we can hash based on dst addr to find
24617c478bd9Sstevel@tonic-gate 	 * the correct bucket in the outbound table.
24627c478bd9Sstevel@tonic-gate 	 */
24637c478bd9Sstevel@tonic-gate 	ps.inbnd = B_TRUE;
2464fb87b5d2Ssommerfe 	sadb_walker(sp->sdb_if, sp->sdb_hashsize, sadb_purge_cb, &ps);
24657c478bd9Sstevel@tonic-gate 	ps.inbnd = B_FALSE;
2466fb87b5d2Ssommerfe 	sadb_walker(sp->sdb_of, sp->sdb_hashsize, sadb_purge_cb, &ps);
24677c478bd9Sstevel@tonic-gate 
24687c478bd9Sstevel@tonic-gate 	ASSERT(mp->b_cont != NULL);
24697c478bd9Sstevel@tonic-gate 	sadb_pfkey_echo(pfkey_q, mp, (sadb_msg_t *)mp->b_cont->b_rptr, ksi,
24707c478bd9Sstevel@tonic-gate 	    NULL);
24717c478bd9Sstevel@tonic-gate 	return (0);
24727c478bd9Sstevel@tonic-gate }
24737c478bd9Sstevel@tonic-gate 
24749c2c14abSThejaswini Singarajipura static void
sadb_delpair_state_one(isaf_t * head,ipsa_t * entry,void * cookie)24755d3b8cb7SBill Sommerfeld sadb_delpair_state_one(isaf_t *head, ipsa_t *entry, void *cookie)
24769c2c14abSThejaswini Singarajipura {
24779c2c14abSThejaswini Singarajipura 	struct sadb_purge_state *ps = (struct sadb_purge_state *)cookie;
24789c2c14abSThejaswini Singarajipura 	isaf_t  *inbound_bucket;
24799c2c14abSThejaswini Singarajipura 	ipsa_t *peer_assoc;
24805d3b8cb7SBill Sommerfeld 	ipsa_query_t *sq = &ps->sq;
24819c2c14abSThejaswini Singarajipura 
24829c2c14abSThejaswini Singarajipura 	ASSERT(MUTEX_HELD(&head->isaf_lock));
24839c2c14abSThejaswini Singarajipura 
24849c2c14abSThejaswini Singarajipura 	mutex_enter(&entry->ipsa_lock);
24859c2c14abSThejaswini Singarajipura 
24869c2c14abSThejaswini Singarajipura 	if ((entry->ipsa_state != ps->sadb_sa_state) ||
24875d3b8cb7SBill Sommerfeld 	    ((sq->srcaddr != NULL) &&
24885d3b8cb7SBill Sommerfeld 	    !IPSA_ARE_ADDR_EQUAL(entry->ipsa_srcaddr, sq->srcaddr, sq->af))) {
24899c2c14abSThejaswini Singarajipura 		mutex_exit(&entry->ipsa_lock);
24909c2c14abSThejaswini Singarajipura 		return;
24919c2c14abSThejaswini Singarajipura 	}
24929c2c14abSThejaswini Singarajipura 
24939c2c14abSThejaswini Singarajipura 	/*
24949c2c14abSThejaswini Singarajipura 	 * The isaf_t *, which is passed in , is always an outbound bucket,
24959c2c14abSThejaswini Singarajipura 	 * and we are preserving the outbound-then-inbound hash-bucket lock
24969c2c14abSThejaswini Singarajipura 	 * ordering. The sadb_walker() which triggers this function is called
24979c2c14abSThejaswini Singarajipura 	 * only on the outbound fanout, and the corresponding inbound bucket
24989c2c14abSThejaswini Singarajipura 	 * lock is safe to acquire here.
24999c2c14abSThejaswini Singarajipura 	 */
25009c2c14abSThejaswini Singarajipura 
25019c2c14abSThejaswini Singarajipura 	if (entry->ipsa_haspeer) {
25025d3b8cb7SBill Sommerfeld 		inbound_bucket = INBOUND_BUCKET(sq->sp, entry->ipsa_spi);
25039c2c14abSThejaswini Singarajipura 		mutex_enter(&inbound_bucket->isaf_lock);
25049c2c14abSThejaswini Singarajipura 		peer_assoc = ipsec_getassocbyspi(inbound_bucket,
25059c2c14abSThejaswini Singarajipura 		    entry->ipsa_spi, entry->ipsa_srcaddr,
25069c2c14abSThejaswini Singarajipura 		    entry->ipsa_dstaddr, entry->ipsa_addrfam);
25079c2c14abSThejaswini Singarajipura 	} else {
25085d3b8cb7SBill Sommerfeld 		inbound_bucket = INBOUND_BUCKET(sq->sp, entry->ipsa_otherspi);
25099c2c14abSThejaswini Singarajipura 		mutex_enter(&inbound_bucket->isaf_lock);
25109c2c14abSThejaswini Singarajipura 		peer_assoc = ipsec_getassocbyspi(inbound_bucket,
25119c2c14abSThejaswini Singarajipura 		    entry->ipsa_otherspi, entry->ipsa_dstaddr,
25129c2c14abSThejaswini Singarajipura 		    entry->ipsa_srcaddr, entry->ipsa_addrfam);
25139c2c14abSThejaswini Singarajipura 	}
25149c2c14abSThejaswini Singarajipura 
25159c2c14abSThejaswini Singarajipura 	entry->ipsa_state = IPSA_STATE_DEAD;
2516bd670b35SErik Nordmark 	(void) sadb_torch_assoc(head, entry);
25179c2c14abSThejaswini Singarajipura 	if (peer_assoc != NULL) {
25189c2c14abSThejaswini Singarajipura 		mutex_enter(&peer_assoc->ipsa_lock);
25199c2c14abSThejaswini Singarajipura 		peer_assoc->ipsa_state = IPSA_STATE_DEAD;
2520bd670b35SErik Nordmark 		(void) sadb_torch_assoc(inbound_bucket, peer_assoc);
25219c2c14abSThejaswini Singarajipura 	}
25229c2c14abSThejaswini Singarajipura 	mutex_exit(&inbound_bucket->isaf_lock);
25239c2c14abSThejaswini Singarajipura }
25249c2c14abSThejaswini Singarajipura 
25255d3b8cb7SBill Sommerfeld static int
sadb_delpair_state(mblk_t * mp,keysock_in_t * ksi,sadbp_t * spp,int * diagnostic,queue_t * pfkey_q)25265d3b8cb7SBill Sommerfeld sadb_delpair_state(mblk_t *mp, keysock_in_t *ksi, sadbp_t *spp,
25275d3b8cb7SBill Sommerfeld     int *diagnostic, queue_t *pfkey_q)
25285d3b8cb7SBill Sommerfeld {
25295d3b8cb7SBill Sommerfeld 	sadb_sa_t *assoc = (sadb_sa_t *)ksi->ks_in_extv[SADB_EXT_SA];
25305d3b8cb7SBill Sommerfeld 	struct sadb_purge_state ps;
25315d3b8cb7SBill Sommerfeld 	int error;
25325d3b8cb7SBill Sommerfeld 
25335d3b8cb7SBill Sommerfeld 	ps.sq.spp = spp;		/* XXX param */
25345d3b8cb7SBill Sommerfeld 
25355d3b8cb7SBill Sommerfeld 	error = sadb_form_query(ksi, IPSA_Q_DST|IPSA_Q_SRC,
25365d3b8cb7SBill Sommerfeld 	    IPSA_Q_SRC|IPSA_Q_DST|IPSA_Q_SRCID|IPSA_Q_DSTID|IPSA_Q_KMC,
25375d3b8cb7SBill Sommerfeld 	    &ps.sq, diagnostic);
25385d3b8cb7SBill Sommerfeld 	if (error != 0)
25395d3b8cb7SBill Sommerfeld 		return (error);
25405d3b8cb7SBill Sommerfeld 
25415d3b8cb7SBill Sommerfeld 	ps.inbnd = B_FALSE;
25425d3b8cb7SBill Sommerfeld 	ps.sadb_sa_state = assoc->sadb_sa_state;
25435d3b8cb7SBill Sommerfeld 	sadb_walker(ps.sq.sp->sdb_of, ps.sq.sp->sdb_hashsize,
25445d3b8cb7SBill Sommerfeld 	    sadb_delpair_state_one, &ps);
25455d3b8cb7SBill Sommerfeld 
25465d3b8cb7SBill Sommerfeld 	ASSERT(mp->b_cont != NULL);
25475d3b8cb7SBill Sommerfeld 	sadb_pfkey_echo(pfkey_q, mp, (sadb_msg_t *)mp->b_cont->b_rptr,
25485d3b8cb7SBill Sommerfeld 	    ksi, NULL);
25495d3b8cb7SBill Sommerfeld 	return (0);
25505d3b8cb7SBill Sommerfeld }
25515d3b8cb7SBill Sommerfeld 
25527c478bd9Sstevel@tonic-gate /*
25537c478bd9Sstevel@tonic-gate  * Common code to delete/get an SA.
25547c478bd9Sstevel@tonic-gate  */
25557c478bd9Sstevel@tonic-gate int
sadb_delget_sa(mblk_t * mp,keysock_in_t * ksi,sadbp_t * spp,int * diagnostic,queue_t * pfkey_q,uint8_t sadb_msg_type)25567c478bd9Sstevel@tonic-gate sadb_delget_sa(mblk_t *mp, keysock_in_t *ksi, sadbp_t *spp,
255738d95a78Smarkfen     int *diagnostic, queue_t *pfkey_q, uint8_t sadb_msg_type)
25587c478bd9Sstevel@tonic-gate {
25595d3b8cb7SBill Sommerfeld 	ipsa_query_t sq;
256038d95a78Smarkfen 	ipsa_t *echo_target = NULL;
25615d3b8cb7SBill Sommerfeld 	ipsap_t ipsapp;
256238d95a78Smarkfen 	uint_t	error = 0;
25637c478bd9Sstevel@tonic-gate 
25645d3b8cb7SBill Sommerfeld 	if (sadb_msg_type == SADB_X_DELPAIR_STATE)
25655d3b8cb7SBill Sommerfeld 		return (sadb_delpair_state(mp, ksi, spp, diagnostic, pfkey_q));
25669c2c14abSThejaswini Singarajipura 
25675d3b8cb7SBill Sommerfeld 	sq.spp = spp;		/* XXX param */
25685d3b8cb7SBill Sommerfeld 	error = sadb_form_query(ksi, IPSA_Q_DST|IPSA_Q_SA,
25695d3b8cb7SBill Sommerfeld 	    IPSA_Q_SRC|IPSA_Q_DST|IPSA_Q_SA|IPSA_Q_INBOUND|IPSA_Q_OUTBOUND,
25705d3b8cb7SBill Sommerfeld 	    &sq, diagnostic);
25715d3b8cb7SBill Sommerfeld 	if (error != 0)
25725d3b8cb7SBill Sommerfeld 		return (error);
25739c2c14abSThejaswini Singarajipura 
25745d3b8cb7SBill Sommerfeld 	error = get_ipsa_pair(&sq, &ipsapp, diagnostic);
25755d3b8cb7SBill Sommerfeld 	if (error != 0) {
25765d3b8cb7SBill Sommerfeld 		return (error);
257738d95a78Smarkfen 	}
257838d95a78Smarkfen 
25795d3b8cb7SBill Sommerfeld 	echo_target = ipsapp.ipsap_sa_ptr;
258038d95a78Smarkfen 	if (echo_target == NULL)
25815d3b8cb7SBill Sommerfeld 		echo_target = ipsapp.ipsap_psa_ptr;
258238d95a78Smarkfen 
258338d95a78Smarkfen 	if (sadb_msg_type == SADB_DELETE || sadb_msg_type == SADB_X_DELPAIR) {
258438d95a78Smarkfen 		/*
258538d95a78Smarkfen 		 * Bucket locks will be required if SA is actually unlinked.
258638d95a78Smarkfen 		 * get_ipsa_pair() returns valid hash bucket pointers even
2587a1ba8781SMark Fenwick 		 * if it can't find a pair SA pointer. To prevent a potential
2588a1ba8781SMark Fenwick 		 * deadlock, always lock the outbound bucket before the inbound.
258938d95a78Smarkfen 		 */
25905d3b8cb7SBill Sommerfeld 		if (ipsapp.in_inbound_table) {
25915d3b8cb7SBill Sommerfeld 			mutex_enter(&ipsapp.ipsap_pbucket->isaf_lock);
25925d3b8cb7SBill Sommerfeld 			mutex_enter(&ipsapp.ipsap_bucket->isaf_lock);
2593a1ba8781SMark Fenwick 		} else {
25945d3b8cb7SBill Sommerfeld 			mutex_enter(&ipsapp.ipsap_bucket->isaf_lock);
25955d3b8cb7SBill Sommerfeld 			mutex_enter(&ipsapp.ipsap_pbucket->isaf_lock);
2596a1ba8781SMark Fenwick 		}
259738d95a78Smarkfen 
25985d3b8cb7SBill Sommerfeld 		if (ipsapp.ipsap_sa_ptr != NULL) {
25995d3b8cb7SBill Sommerfeld 			mutex_enter(&ipsapp.ipsap_sa_ptr->ipsa_lock);
26005d3b8cb7SBill Sommerfeld 			if (ipsapp.ipsap_sa_ptr->ipsa_flags & IPSA_F_INBOUND) {
26015d3b8cb7SBill Sommerfeld 				sadb_delete_cluster(ipsapp.ipsap_sa_ptr);
26029c2c14abSThejaswini Singarajipura 			}
26035d3b8cb7SBill Sommerfeld 			ipsapp.ipsap_sa_ptr->ipsa_state = IPSA_STATE_DEAD;
26045d3b8cb7SBill Sommerfeld 			(void) sadb_torch_assoc(ipsapp.ipsap_bucket,
2605bd670b35SErik Nordmark 			    ipsapp.ipsap_sa_ptr);
260638d95a78Smarkfen 			/*
260738d95a78Smarkfen 			 * sadb_torch_assoc() releases the ipsa_lock
260838d95a78Smarkfen 			 * and calls sadb_unlinkassoc() which does a
260938d95a78Smarkfen 			 * IPSA_REFRELE.
261038d95a78Smarkfen 			 */
261138d95a78Smarkfen 		}
26125d3b8cb7SBill Sommerfeld 		if (ipsapp.ipsap_psa_ptr != NULL) {
26135d3b8cb7SBill Sommerfeld 			mutex_enter(&ipsapp.ipsap_psa_ptr->ipsa_lock);
2614fc809ee9SPaul Wernau 			if (sadb_msg_type == SADB_X_DELPAIR ||
26155d3b8cb7SBill Sommerfeld 			    ipsapp.ipsap_psa_ptr->ipsa_haspeer) {
26165d3b8cb7SBill Sommerfeld 				if (ipsapp.ipsap_psa_ptr->ipsa_flags &
26179c2c14abSThejaswini Singarajipura 				    IPSA_F_INBOUND) {
26185d3b8cb7SBill Sommerfeld 					sadb_delete_cluster
26195d3b8cb7SBill Sommerfeld 					    (ipsapp.ipsap_psa_ptr);
26209c2c14abSThejaswini Singarajipura 				}
26215d3b8cb7SBill Sommerfeld 				ipsapp.ipsap_psa_ptr->ipsa_state =
262238d95a78Smarkfen 				    IPSA_STATE_DEAD;
26235d3b8cb7SBill Sommerfeld 				(void) sadb_torch_assoc(ipsapp.ipsap_pbucket,
2624bd670b35SErik Nordmark 				    ipsapp.ipsap_psa_ptr);
262538d95a78Smarkfen 			} else {
262638d95a78Smarkfen 				/*
262738d95a78Smarkfen 				 * Only half of the "pair" has been deleted.
262838d95a78Smarkfen 				 * Update the remaining SA and remove references
262938d95a78Smarkfen 				 * to its pair SA, which is now gone.
263038d95a78Smarkfen 				 */
26315d3b8cb7SBill Sommerfeld 				ipsapp.ipsap_psa_ptr->ipsa_otherspi = 0;
26325d3b8cb7SBill Sommerfeld 				ipsapp.ipsap_psa_ptr->ipsa_flags &=
263338d95a78Smarkfen 				    ~IPSA_F_PAIRED;
26345d3b8cb7SBill Sommerfeld 				mutex_exit(&ipsapp.ipsap_psa_ptr->ipsa_lock);
263538d95a78Smarkfen 			}
263638d95a78Smarkfen 		} else if (sadb_msg_type == SADB_X_DELPAIR) {
263738d95a78Smarkfen 			*diagnostic = SADB_X_DIAGNOSTIC_PAIR_SA_NOTFOUND;
263838d95a78Smarkfen 			error = ESRCH;
263938d95a78Smarkfen 		}
26405d3b8cb7SBill Sommerfeld 		mutex_exit(&ipsapp.ipsap_bucket->isaf_lock);
26415d3b8cb7SBill Sommerfeld 		mutex_exit(&ipsapp.ipsap_pbucket->isaf_lock);
264238d95a78Smarkfen 	}
264338d95a78Smarkfen 
264438d95a78Smarkfen 	ASSERT(mp->b_cont != NULL);
264538d95a78Smarkfen 
264638d95a78Smarkfen 	if (error == 0)
264738d95a78Smarkfen 		sadb_pfkey_echo(pfkey_q, mp, (sadb_msg_t *)
264838d95a78Smarkfen 		    mp->b_cont->b_rptr, ksi, echo_target);
264938d95a78Smarkfen 
26505d3b8cb7SBill Sommerfeld 	destroy_ipsa_pair(&ipsapp);
265138d95a78Smarkfen 
265238d95a78Smarkfen 	return (error);
265338d95a78Smarkfen }
265438d95a78Smarkfen 
265538d95a78Smarkfen /*
265638d95a78Smarkfen  * This function takes a sadb_sa_t and finds the ipsa_t structure
265738d95a78Smarkfen  * and the isaf_t (hash bucket) that its stored under. If the security
265838d95a78Smarkfen  * association has a peer, the ipsa_t structure and bucket for that security
265938d95a78Smarkfen  * association are also searched for. The "pair" of ipsa_t's and isaf_t's
266038d95a78Smarkfen  * are returned as a ipsap_t.
266138d95a78Smarkfen  *
2662a1ba8781SMark Fenwick  * The hash buckets are returned for convenience, if the calling function
2663a1ba8781SMark Fenwick  * needs to use the hash bucket locks, say to remove the SA's, it should
2664a1ba8781SMark Fenwick  * take care to observe the convention of locking outbound bucket then
2665a1ba8781SMark Fenwick  * inbound bucket. The flag in_inbound_table provides direction.
2666a1ba8781SMark Fenwick  *
266738d95a78Smarkfen  * Note that a "pair" is defined as one (but not both) of the following:
266838d95a78Smarkfen  *
266938d95a78Smarkfen  * A security association which has a soft reference to another security
267038d95a78Smarkfen  * association via its SPI.
267138d95a78Smarkfen  *
267238d95a78Smarkfen  * A security association that is not obviously "inbound" or "outbound" so
267338d95a78Smarkfen  * it appears in both hash tables, the "peer" being the same security
267438d95a78Smarkfen  * association in the other hash table.
267538d95a78Smarkfen  *
267638d95a78Smarkfen  * This function will return NULL if the ipsa_t can't be found in the
267738d95a78Smarkfen  * inbound or outbound  hash tables (not found). If only one ipsa_t is
267838d95a78Smarkfen  * found, the pair ipsa_t will be NULL. Both isaf_t values are valid
267938d95a78Smarkfen  * provided at least one ipsa_t is found.
268038d95a78Smarkfen  */
26815d3b8cb7SBill Sommerfeld static int
get_ipsa_pair(ipsa_query_t * sq,ipsap_t * ipsapp,int * diagnostic)26825d3b8cb7SBill Sommerfeld get_ipsa_pair(ipsa_query_t *sq, ipsap_t *ipsapp, int *diagnostic)
268338d95a78Smarkfen {
268438d95a78Smarkfen 	uint32_t pair_srcaddr[IPSA_MAX_ADDRLEN];
268538d95a78Smarkfen 	uint32_t pair_dstaddr[IPSA_MAX_ADDRLEN];
268638d95a78Smarkfen 	uint32_t pair_spi;
268738d95a78Smarkfen 
26885d3b8cb7SBill Sommerfeld 	init_ipsa_pair(ipsapp);
268938d95a78Smarkfen 
2690a1ba8781SMark Fenwick 	ipsapp->in_inbound_table = B_FALSE;
2691a1ba8781SMark Fenwick 
26927c478bd9Sstevel@tonic-gate 	/* Lock down both buckets. */
26935d3b8cb7SBill Sommerfeld 	mutex_enter(&sq->outbound->isaf_lock);
26945d3b8cb7SBill Sommerfeld 	mutex_enter(&sq->inbound->isaf_lock);
26957c478bd9Sstevel@tonic-gate 
26965d3b8cb7SBill Sommerfeld 	if (sq->assoc->sadb_sa_flags & IPSA_F_INBOUND) {
26975d3b8cb7SBill Sommerfeld 		ipsapp->ipsap_sa_ptr = ipsec_getassocbyspi(sq->inbound,
26985d3b8cb7SBill Sommerfeld 		    sq->assoc->sadb_sa_spi, sq->srcaddr, sq->dstaddr, sq->af);
269938d95a78Smarkfen 		if (ipsapp->ipsap_sa_ptr != NULL) {
27005d3b8cb7SBill Sommerfeld 			ipsapp->ipsap_bucket = sq->inbound;
27015d3b8cb7SBill Sommerfeld 			ipsapp->ipsap_pbucket = sq->outbound;
2702a1ba8781SMark Fenwick 			ipsapp->in_inbound_table = B_TRUE;
270338d95a78Smarkfen 		} else {
27045d3b8cb7SBill Sommerfeld 			ipsapp->ipsap_sa_ptr = ipsec_getassocbyspi(sq->outbound,
27055d3b8cb7SBill Sommerfeld 			    sq->assoc->sadb_sa_spi, sq->srcaddr, sq->dstaddr,
27065d3b8cb7SBill Sommerfeld 			    sq->af);
27075d3b8cb7SBill Sommerfeld 			ipsapp->ipsap_bucket = sq->outbound;
27085d3b8cb7SBill Sommerfeld 			ipsapp->ipsap_pbucket = sq->inbound;
270938d95a78Smarkfen 		}
27107c478bd9Sstevel@tonic-gate 	} else {
271138d95a78Smarkfen 		/* IPSA_F_OUTBOUND is set *or* no directions flags set. */
271238d95a78Smarkfen 		ipsapp->ipsap_sa_ptr =
27135d3b8cb7SBill Sommerfeld 		    ipsec_getassocbyspi(sq->outbound,
27145d3b8cb7SBill Sommerfeld 		    sq->assoc->sadb_sa_spi, sq->srcaddr, sq->dstaddr, sq->af);
271538d95a78Smarkfen 		if (ipsapp->ipsap_sa_ptr != NULL) {
27165d3b8cb7SBill Sommerfeld 			ipsapp->ipsap_bucket = sq->outbound;
27175d3b8cb7SBill Sommerfeld 			ipsapp->ipsap_pbucket = sq->inbound;
271838d95a78Smarkfen 		} else {
27195d3b8cb7SBill Sommerfeld 			ipsapp->ipsap_sa_ptr = ipsec_getassocbyspi(sq->inbound,
27205d3b8cb7SBill Sommerfeld 			    sq->assoc->sadb_sa_spi, sq->srcaddr, sq->dstaddr,
27215d3b8cb7SBill Sommerfeld 			    sq->af);
27225d3b8cb7SBill Sommerfeld 			ipsapp->ipsap_bucket = sq->inbound;
27235d3b8cb7SBill Sommerfeld 			ipsapp->ipsap_pbucket = sq->outbound;
272438d95a78Smarkfen 			if (ipsapp->ipsap_sa_ptr != NULL)
2725a1ba8781SMark Fenwick 				ipsapp->in_inbound_table = B_TRUE;
272638d95a78Smarkfen 		}
27277c478bd9Sstevel@tonic-gate 	}
27287c478bd9Sstevel@tonic-gate 
272938d95a78Smarkfen 	if (ipsapp->ipsap_sa_ptr == NULL) {
27305d3b8cb7SBill Sommerfeld 		mutex_exit(&sq->outbound->isaf_lock);
27315d3b8cb7SBill Sommerfeld 		mutex_exit(&sq->inbound->isaf_lock);
27325d3b8cb7SBill Sommerfeld 		*diagnostic = SADB_X_DIAGNOSTIC_SA_NOTFOUND;
27335d3b8cb7SBill Sommerfeld 		return (ESRCH);
27347c478bd9Sstevel@tonic-gate 	}
27357c478bd9Sstevel@tonic-gate 
273638d95a78Smarkfen 	if ((ipsapp->ipsap_sa_ptr->ipsa_state == IPSA_STATE_LARVAL) &&
2737a1ba8781SMark Fenwick 	    ipsapp->in_inbound_table) {
27385d3b8cb7SBill Sommerfeld 		mutex_exit(&sq->outbound->isaf_lock);
27395d3b8cb7SBill Sommerfeld 		mutex_exit(&sq->inbound->isaf_lock);
27405d3b8cb7SBill Sommerfeld 		return (0);
27417c478bd9Sstevel@tonic-gate 	}
27427c478bd9Sstevel@tonic-gate 
274338d95a78Smarkfen 	mutex_enter(&ipsapp->ipsap_sa_ptr->ipsa_lock);
274438d95a78Smarkfen 	if (ipsapp->ipsap_sa_ptr->ipsa_haspeer) {
274538d95a78Smarkfen 		/*
274638d95a78Smarkfen 		 * haspeer implies no sa_pairing, look for same spi
274738d95a78Smarkfen 		 * in other hashtable.
274838d95a78Smarkfen 		 */
274938d95a78Smarkfen 		ipsapp->ipsap_psa_ptr =
275038d95a78Smarkfen 		    ipsec_getassocbyspi(ipsapp->ipsap_pbucket,
27515d3b8cb7SBill Sommerfeld 		    sq->assoc->sadb_sa_spi, sq->srcaddr, sq->dstaddr, sq->af);
275238d95a78Smarkfen 		mutex_exit(&ipsapp->ipsap_sa_ptr->ipsa_lock);
27535d3b8cb7SBill Sommerfeld 		mutex_exit(&sq->outbound->isaf_lock);
27545d3b8cb7SBill Sommerfeld 		mutex_exit(&sq->inbound->isaf_lock);
27555d3b8cb7SBill Sommerfeld 		return (0);
275638d95a78Smarkfen 	}
275738d95a78Smarkfen 	pair_spi = ipsapp->ipsap_sa_ptr->ipsa_otherspi;
275838d95a78Smarkfen 	IPSA_COPY_ADDR(&pair_srcaddr,
27595d3b8cb7SBill Sommerfeld 	    ipsapp->ipsap_sa_ptr->ipsa_srcaddr, sq->af);
276038d95a78Smarkfen 	IPSA_COPY_ADDR(&pair_dstaddr,
27615d3b8cb7SBill Sommerfeld 	    ipsapp->ipsap_sa_ptr->ipsa_dstaddr, sq->af);
276238d95a78Smarkfen 	mutex_exit(&ipsapp->ipsap_sa_ptr->ipsa_lock);
27635d3b8cb7SBill Sommerfeld 	mutex_exit(&sq->inbound->isaf_lock);
27645d3b8cb7SBill Sommerfeld 	mutex_exit(&sq->outbound->isaf_lock);
276538d95a78Smarkfen 
276638d95a78Smarkfen 	if (pair_spi == 0) {
276738d95a78Smarkfen 		ASSERT(ipsapp->ipsap_bucket != NULL);
276838d95a78Smarkfen 		ASSERT(ipsapp->ipsap_pbucket != NULL);
27695d3b8cb7SBill Sommerfeld 		return (0);
277038d95a78Smarkfen 	}
277138d95a78Smarkfen 
277238d95a78Smarkfen 	/* found sa in outbound sadb, peer should be inbound */
277338d95a78Smarkfen 
2774a1ba8781SMark Fenwick 	if (ipsapp->in_inbound_table) {
277538d95a78Smarkfen 		/* Found SA in inbound table, pair will be in outbound. */
27765d3b8cb7SBill Sommerfeld 		if (sq->af == AF_INET6) {
27775d3b8cb7SBill Sommerfeld 			ipsapp->ipsap_pbucket = OUTBOUND_BUCKET_V6(sq->sp,
277838d95a78Smarkfen 			    *(uint32_t *)pair_srcaddr);
277938d95a78Smarkfen 		} else {
27805d3b8cb7SBill Sommerfeld 			ipsapp->ipsap_pbucket = OUTBOUND_BUCKET_V4(sq->sp,
278138d95a78Smarkfen 			    *(uint32_t *)pair_srcaddr);
278238d95a78Smarkfen 		}
278338d95a78Smarkfen 	} else {
27845d3b8cb7SBill Sommerfeld 		ipsapp->ipsap_pbucket = INBOUND_BUCKET(sq->sp, pair_spi);
27857c478bd9Sstevel@tonic-gate 	}
278638d95a78Smarkfen 	mutex_enter(&ipsapp->ipsap_pbucket->isaf_lock);
278738d95a78Smarkfen 	ipsapp->ipsap_psa_ptr = ipsec_getassocbyspi(ipsapp->ipsap_pbucket,
27885d3b8cb7SBill Sommerfeld 	    pair_spi, pair_dstaddr, pair_srcaddr, sq->af);
278938d95a78Smarkfen 	mutex_exit(&ipsapp->ipsap_pbucket->isaf_lock);
279038d95a78Smarkfen 	ASSERT(ipsapp->ipsap_bucket != NULL);
279138d95a78Smarkfen 	ASSERT(ipsapp->ipsap_pbucket != NULL);
27925d3b8cb7SBill Sommerfeld 	return (0);
27937c478bd9Sstevel@tonic-gate }
27947c478bd9Sstevel@tonic-gate 
27958810c16bSdanmcd /*
27968810c16bSdanmcd  * Perform NAT-traversal cached checksum offset calculations here.
27978810c16bSdanmcd  */
27988810c16bSdanmcd static void
sadb_nat_calculations(ipsa_t * newbie,sadb_address_t * natt_loc_ext,sadb_address_t * natt_rem_ext,uint32_t * src_addr_ptr,uint32_t * dst_addr_ptr)27998810c16bSdanmcd sadb_nat_calculations(ipsa_t *newbie, sadb_address_t *natt_loc_ext,
28008810c16bSdanmcd     sadb_address_t *natt_rem_ext, uint32_t *src_addr_ptr,
28018810c16bSdanmcd     uint32_t *dst_addr_ptr)
28028810c16bSdanmcd {
28038810c16bSdanmcd 	struct sockaddr_in *natt_loc, *natt_rem;
28048810c16bSdanmcd 	uint32_t *natt_loc_ptr = NULL, *natt_rem_ptr = NULL;
28058810c16bSdanmcd 	uint32_t running_sum = 0;
28068810c16bSdanmcd 
28078810c16bSdanmcd #define	DOWN_SUM(x) (x) = ((x) & 0xFFFF) +	 ((x) >> 16)
28088810c16bSdanmcd 
28098810c16bSdanmcd 	if (natt_rem_ext != NULL) {
28108810c16bSdanmcd 		uint32_t l_src;
28118810c16bSdanmcd 		uint32_t l_rem;
28128810c16bSdanmcd 
28138810c16bSdanmcd 		natt_rem = (struct sockaddr_in *)(natt_rem_ext + 1);
28148810c16bSdanmcd 
28158810c16bSdanmcd 		/* Ensured by sadb_addrfix(). */
28168810c16bSdanmcd 		ASSERT(natt_rem->sin_family == AF_INET);
28178810c16bSdanmcd 
28188810c16bSdanmcd 		natt_rem_ptr = (uint32_t *)(&natt_rem->sin_addr);
2819437220cdSdanmcd 		newbie->ipsa_remote_nat_port = natt_rem->sin_port;
28208810c16bSdanmcd 		l_src = *src_addr_ptr;
28218810c16bSdanmcd 		l_rem = *natt_rem_ptr;
28228810c16bSdanmcd 
28238810c16bSdanmcd 		/* Instead of IPSA_COPY_ADDR(), just copy first 32 bits. */
2824437220cdSdanmcd 		newbie->ipsa_natt_addr_rem = *natt_rem_ptr;
28258810c16bSdanmcd 
28268810c16bSdanmcd 		l_src = ntohl(l_src);
28278810c16bSdanmcd 		DOWN_SUM(l_src);
28288810c16bSdanmcd 		DOWN_SUM(l_src);
28298810c16bSdanmcd 		l_rem = ntohl(l_rem);
28308810c16bSdanmcd 		DOWN_SUM(l_rem);
28318810c16bSdanmcd 		DOWN_SUM(l_rem);
28328810c16bSdanmcd 
28338810c16bSdanmcd 		/*
28348810c16bSdanmcd 		 * We're 1's complement for checksums, so check for wraparound
28358810c16bSdanmcd 		 * here.
28368810c16bSdanmcd 		 */
28378810c16bSdanmcd 		if (l_rem > l_src)
28388810c16bSdanmcd 			l_src--;
28398810c16bSdanmcd 
28408810c16bSdanmcd 		running_sum += l_src - l_rem;
28418810c16bSdanmcd 
28428810c16bSdanmcd 		DOWN_SUM(running_sum);
28438810c16bSdanmcd 		DOWN_SUM(running_sum);
28448810c16bSdanmcd 	}
28458810c16bSdanmcd 
28468810c16bSdanmcd 	if (natt_loc_ext != NULL) {
28478810c16bSdanmcd 		natt_loc = (struct sockaddr_in *)(natt_loc_ext + 1);
28488810c16bSdanmcd 
28498810c16bSdanmcd 		/* Ensured by sadb_addrfix(). */
28508810c16bSdanmcd 		ASSERT(natt_loc->sin_family == AF_INET);
28518810c16bSdanmcd 
2852437220cdSdanmcd 		natt_loc_ptr = (uint32_t *)(&natt_loc->sin_addr);
2853437220cdSdanmcd 		newbie->ipsa_local_nat_port = natt_loc->sin_port;
28548810c16bSdanmcd 
28558810c16bSdanmcd 		/* Instead of IPSA_COPY_ADDR(), just copy first 32 bits. */
2856437220cdSdanmcd 		newbie->ipsa_natt_addr_loc = *natt_loc_ptr;
28578810c16bSdanmcd 
28588810c16bSdanmcd 		/*
2859437220cdSdanmcd 		 * NAT-T port agility means we may have natt_loc_ext, but
2860437220cdSdanmcd 		 * only for a local-port change.
28618810c16bSdanmcd 		 */
2862437220cdSdanmcd 		if (natt_loc->sin_addr.s_addr != INADDR_ANY) {
2863437220cdSdanmcd 			uint32_t l_dst = ntohl(*dst_addr_ptr);
2864437220cdSdanmcd 			uint32_t l_loc = ntohl(*natt_loc_ptr);
28658810c16bSdanmcd 
2866437220cdSdanmcd 			DOWN_SUM(l_loc);
2867437220cdSdanmcd 			DOWN_SUM(l_loc);
2868437220cdSdanmcd 			DOWN_SUM(l_dst);
2869437220cdSdanmcd 			DOWN_SUM(l_dst);
2870437220cdSdanmcd 
2871437220cdSdanmcd 			/*
2872437220cdSdanmcd 			 * We're 1's complement for checksums, so check for
2873437220cdSdanmcd 			 * wraparound here.
2874437220cdSdanmcd 			 */
2875437220cdSdanmcd 			if (l_loc > l_dst)
2876437220cdSdanmcd 				l_dst--;
2877437220cdSdanmcd 
2878437220cdSdanmcd 			running_sum += l_dst - l_loc;
2879437220cdSdanmcd 			DOWN_SUM(running_sum);
2880437220cdSdanmcd 			DOWN_SUM(running_sum);
2881437220cdSdanmcd 		}
28828810c16bSdanmcd 	}
28838810c16bSdanmcd 
28848810c16bSdanmcd 	newbie->ipsa_inbound_cksum = running_sum;
28858810c16bSdanmcd #undef DOWN_SUM
28868810c16bSdanmcd }
28877c478bd9Sstevel@tonic-gate 
28887c478bd9Sstevel@tonic-gate /*
28897c478bd9Sstevel@tonic-gate  * This function is called from consumers that need to insert a fully-grown
28907c478bd9Sstevel@tonic-gate  * security association into its tables.  This function takes into account that
28917c478bd9Sstevel@tonic-gate  * SAs can be "inbound", "outbound", or "both".	 The "primary" and "secondary"
28927c478bd9Sstevel@tonic-gate  * hash bucket parameters are set in order of what the SA will be most of the
28937c478bd9Sstevel@tonic-gate  * time.  (For example, an SA with an unspecified source, and a multicast
28947c478bd9Sstevel@tonic-gate  * destination will primarily be an outbound SA.  OTOH, if that destination
28957c478bd9Sstevel@tonic-gate  * is unicast for this node, then the SA will primarily be inbound.)
28967c478bd9Sstevel@tonic-gate  *
28977c478bd9Sstevel@tonic-gate  * It takes a lot of parameters because even if clone is B_FALSE, this needs
28987c478bd9Sstevel@tonic-gate  * to check both buckets for purposes of collision.
28997c478bd9Sstevel@tonic-gate  *
29007c478bd9Sstevel@tonic-gate  * Return 0 upon success.  Return various errnos (ENOMEM, EEXIST) for
29018810c16bSdanmcd  * various error conditions.  We may need to set samsg->sadb_x_msg_diagnostic
29028810c16bSdanmcd  * with additional diagnostic information because there is at least one EINVAL
29038810c16bSdanmcd  * case here.
29047c478bd9Sstevel@tonic-gate  */
29057c478bd9Sstevel@tonic-gate int
sadb_common_add(queue_t * pfkey_q,mblk_t * mp,sadb_msg_t * samsg,keysock_in_t * ksi,isaf_t * primary,isaf_t * secondary,ipsa_t * newbie,boolean_t clone,boolean_t is_inbound,int * diagnostic,netstack_t * ns,sadbp_t * spp)2906bd670b35SErik Nordmark sadb_common_add(queue_t *pfkey_q, mblk_t *mp, sadb_msg_t *samsg,
29077c478bd9Sstevel@tonic-gate     keysock_in_t *ksi, isaf_t *primary, isaf_t *secondary,
2908f4b3ec61Sdh     ipsa_t *newbie, boolean_t clone, boolean_t is_inbound, int *diagnostic,
290938d95a78Smarkfen     netstack_t *ns, sadbp_t *spp)
29107c478bd9Sstevel@tonic-gate {
29117c478bd9Sstevel@tonic-gate 	ipsa_t *newbie_clone = NULL, *scratch;
29125d3b8cb7SBill Sommerfeld 	ipsap_t ipsapp;
29137c478bd9Sstevel@tonic-gate 	sadb_sa_t *assoc = (sadb_sa_t *)ksi->ks_in_extv[SADB_EXT_SA];
29147c478bd9Sstevel@tonic-gate 	sadb_address_t *srcext =
29157c478bd9Sstevel@tonic-gate 	    (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_SRC];
29167c478bd9Sstevel@tonic-gate 	sadb_address_t *dstext =
29177c478bd9Sstevel@tonic-gate 	    (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_DST];
29188810c16bSdanmcd 	sadb_address_t *isrcext =
29198810c16bSdanmcd 	    (sadb_address_t *)ksi->ks_in_extv[SADB_X_EXT_ADDRESS_INNER_SRC];
29208810c16bSdanmcd 	sadb_address_t *idstext =
29218810c16bSdanmcd 	    (sadb_address_t *)ksi->ks_in_extv[SADB_X_EXT_ADDRESS_INNER_DST];
29227c478bd9Sstevel@tonic-gate 	sadb_x_kmc_t *kmcext =
29237c478bd9Sstevel@tonic-gate 	    (sadb_x_kmc_t *)ksi->ks_in_extv[SADB_X_EXT_KM_COOKIE];
29247c478bd9Sstevel@tonic-gate 	sadb_key_t *akey = (sadb_key_t *)ksi->ks_in_extv[SADB_EXT_KEY_AUTH];
29257c478bd9Sstevel@tonic-gate 	sadb_key_t *ekey = (sadb_key_t *)ksi->ks_in_extv[SADB_EXT_KEY_ENCRYPT];
29265d3b8cb7SBill Sommerfeld 	sadb_sens_t *sens =
29275d3b8cb7SBill Sommerfeld 	    (sadb_sens_t *)ksi->ks_in_extv[SADB_EXT_SENSITIVITY];
29285d3b8cb7SBill Sommerfeld 	sadb_sens_t *osens =
29295d3b8cb7SBill Sommerfeld 	    (sadb_sens_t *)ksi->ks_in_extv[SADB_X_EXT_OUTER_SENS];
293038d95a78Smarkfen 	sadb_x_pair_t *pair_ext =
293138d95a78Smarkfen 	    (sadb_x_pair_t *)ksi->ks_in_extv[SADB_X_EXT_PAIR];
29329c2c14abSThejaswini Singarajipura 	sadb_x_replay_ctr_t *replayext =
29339c2c14abSThejaswini Singarajipura 	    (sadb_x_replay_ctr_t *)ksi->ks_in_extv[SADB_X_EXT_REPLAY_VALUE];
29349c2c14abSThejaswini Singarajipura 	uint8_t protocol =
29359c2c14abSThejaswini Singarajipura 	    (samsg->sadb_msg_satype == SADB_SATYPE_AH) ? IPPROTO_AH:IPPROTO_ESP;
2936628b0c67SMark Fenwick 	int salt_offset;
2937628b0c67SMark Fenwick 	uint8_t *buf_ptr;
29388810c16bSdanmcd 	struct sockaddr_in *src, *dst, *isrc, *idst;
29398810c16bSdanmcd 	struct sockaddr_in6 *src6, *dst6, *isrc6, *idst6;
29407c478bd9Sstevel@tonic-gate 	sadb_lifetime_t *soft =
29417c478bd9Sstevel@tonic-gate 	    (sadb_lifetime_t *)ksi->ks_in_extv[SADB_EXT_LIFETIME_SOFT];
29427c478bd9Sstevel@tonic-gate 	sadb_lifetime_t *hard =
29437c478bd9Sstevel@tonic-gate 	    (sadb_lifetime_t *)ksi->ks_in_extv[SADB_EXT_LIFETIME_HARD];
29449c2c14abSThejaswini Singarajipura 	sadb_lifetime_t	*idle =
29459c2c14abSThejaswini Singarajipura 	    (sadb_lifetime_t *)ksi->ks_in_extv[SADB_X_EXT_LIFETIME_IDLE];
29467c478bd9Sstevel@tonic-gate 	sa_family_t af;
29477c478bd9Sstevel@tonic-gate 	int error = 0;
29487c478bd9Sstevel@tonic-gate 	boolean_t isupdate = (newbie != NULL);
29498810c16bSdanmcd 	uint32_t *src_addr_ptr, *dst_addr_ptr, *isrc_addr_ptr, *idst_addr_ptr;
2950f4b3ec61Sdh 	ipsec_stack_t	*ipss = ns->netstack_ipsec;
2951a23b3b1bSToomas Soome 	ip_stack_t	*ipst = ns->netstack_ip;
2952628b0c67SMark Fenwick 	ipsec_alginfo_t *alg;
29539c2c14abSThejaswini Singarajipura 	int		rcode;
2954bd670b35SErik Nordmark 	boolean_t	async = B_FALSE;
29557c478bd9Sstevel@tonic-gate 
29565d3b8cb7SBill Sommerfeld 	init_ipsa_pair(&ipsapp);
29575d3b8cb7SBill Sommerfeld 
295838d95a78Smarkfen 	if (srcext == NULL) {
295938d95a78Smarkfen 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_SRC;
296038d95a78Smarkfen 		return (EINVAL);
296138d95a78Smarkfen 	}
296238d95a78Smarkfen 	if (dstext == NULL) {
296338d95a78Smarkfen 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_DST;
296438d95a78Smarkfen 		return (EINVAL);
296538d95a78Smarkfen 	}
296638d95a78Smarkfen 	if (assoc == NULL) {
296738d95a78Smarkfen 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_SA;
296838d95a78Smarkfen 		return (EINVAL);
296938d95a78Smarkfen 	}
297038d95a78Smarkfen 
29717c478bd9Sstevel@tonic-gate 	src = (struct sockaddr_in *)(srcext + 1);
29727c478bd9Sstevel@tonic-gate 	src6 = (struct sockaddr_in6 *)(srcext + 1);
29737c478bd9Sstevel@tonic-gate 	dst = (struct sockaddr_in *)(dstext + 1);
29747c478bd9Sstevel@tonic-gate 	dst6 = (struct sockaddr_in6 *)(dstext + 1);
29758810c16bSdanmcd 	if (isrcext != NULL) {
29768810c16bSdanmcd 		isrc = (struct sockaddr_in *)(isrcext + 1);
29778810c16bSdanmcd 		isrc6 = (struct sockaddr_in6 *)(isrcext + 1);
29788810c16bSdanmcd 		ASSERT(idstext != NULL);
29798810c16bSdanmcd 		idst = (struct sockaddr_in *)(idstext + 1);
29808810c16bSdanmcd 		idst6 = (struct sockaddr_in6 *)(idstext + 1);
29817c478bd9Sstevel@tonic-gate 	} else {
29828810c16bSdanmcd 		isrc = NULL;
29838810c16bSdanmcd 		isrc6 = NULL;
29847c478bd9Sstevel@tonic-gate 	}
29857c478bd9Sstevel@tonic-gate 
29867c478bd9Sstevel@tonic-gate 	af = src->sin_family;
29877c478bd9Sstevel@tonic-gate 
29887c478bd9Sstevel@tonic-gate 	if (af == AF_INET) {
29897c478bd9Sstevel@tonic-gate 		src_addr_ptr = (uint32_t *)&src->sin_addr;
29907c478bd9Sstevel@tonic-gate 		dst_addr_ptr = (uint32_t *)&dst->sin_addr;
29917c478bd9Sstevel@tonic-gate 	} else {
29927c478bd9Sstevel@tonic-gate 		ASSERT(af == AF_INET6);
29937c478bd9Sstevel@tonic-gate 		src_addr_ptr = (uint32_t *)&src6->sin6_addr;
29947c478bd9Sstevel@tonic-gate 		dst_addr_ptr = (uint32_t *)&dst6->sin6_addr;
29957c478bd9Sstevel@tonic-gate 	}
29967c478bd9Sstevel@tonic-gate 
29979c2c14abSThejaswini Singarajipura 	if (!isupdate && (clone == B_TRUE || is_inbound == B_TRUE) &&
29989c2c14abSThejaswini Singarajipura 	    cl_inet_checkspi &&
29999c2c14abSThejaswini Singarajipura 	    (assoc->sadb_sa_state != SADB_X_SASTATE_ACTIVE_ELSEWHERE)) {
30008e4b770fSLu Huafeng 		rcode = cl_inet_checkspi(ns->netstack_stackid, protocol,
30018e4b770fSLu Huafeng 		    assoc->sadb_sa_spi, NULL);
30029c2c14abSThejaswini Singarajipura 		if (rcode == -1) {
30039c2c14abSThejaswini Singarajipura 			return (EEXIST);
30049c2c14abSThejaswini Singarajipura 		}
30059c2c14abSThejaswini Singarajipura 	}
30069c2c14abSThejaswini Singarajipura 
300738d95a78Smarkfen 	/*
300838d95a78Smarkfen 	 * Check to see if the new SA will be cloned AND paired. The
300938d95a78Smarkfen 	 * reason a SA will be cloned is the source or destination addresses
301038d95a78Smarkfen 	 * are not specific enough to determine if the SA goes in the outbound
301138d95a78Smarkfen 	 * or the inbound hash table, so its cloned and put in both. If
301238d95a78Smarkfen 	 * the SA is paired, it's soft linked to another SA for the other
301338d95a78Smarkfen 	 * direction. Keeping track and looking up SA's that are direction
301438d95a78Smarkfen 	 * unspecific and linked is too hard.
301538d95a78Smarkfen 	 */
301638d95a78Smarkfen 	if (clone && (pair_ext != NULL)) {
301738d95a78Smarkfen 		*diagnostic = SADB_X_DIAGNOSTIC_PAIR_INAPPROPRIATE;
301838d95a78Smarkfen 		return (EINVAL);
301938d95a78Smarkfen 	}
302038d95a78Smarkfen 
30217c478bd9Sstevel@tonic-gate 	if (!isupdate) {
30227c478bd9Sstevel@tonic-gate 		newbie = sadb_makelarvalassoc(assoc->sadb_sa_spi,
3023f4b3ec61Sdh 		    src_addr_ptr, dst_addr_ptr, af, ns);
30247c478bd9Sstevel@tonic-gate 		if (newbie == NULL)
30257c478bd9Sstevel@tonic-gate 			return (ENOMEM);
30267c478bd9Sstevel@tonic-gate 	}
30277c478bd9Sstevel@tonic-gate 
30288810c16bSdanmcd 	mutex_enter(&newbie->ipsa_lock);
30297c478bd9Sstevel@tonic-gate 
30308810c16bSdanmcd 	if (isrc != NULL) {
30318810c16bSdanmcd 		if (isrc->sin_family == AF_INET) {
30328810c16bSdanmcd 			if (srcext->sadb_address_proto != IPPROTO_ENCAP) {
30338810c16bSdanmcd 				if (srcext->sadb_address_proto != 0) {
30348810c16bSdanmcd 					/*
30358810c16bSdanmcd 					 * Mismatched outer-packet protocol
30368810c16bSdanmcd 					 * and inner-packet address family.
30378810c16bSdanmcd 					 */
30388810c16bSdanmcd 					mutex_exit(&newbie->ipsa_lock);
30398810c16bSdanmcd 					error = EPROTOTYPE;
3040a1ba8781SMark Fenwick 					*diagnostic =
3041a1ba8781SMark Fenwick 					    SADB_X_DIAGNOSTIC_INNER_AF_MISMATCH;
30428810c16bSdanmcd 					goto error;
30438810c16bSdanmcd 				} else {
30448810c16bSdanmcd 					/* Fill in with explicit protocol. */
30458810c16bSdanmcd 					srcext->sadb_address_proto =
30468810c16bSdanmcd 					    IPPROTO_ENCAP;
30478810c16bSdanmcd 					dstext->sadb_address_proto =
30488810c16bSdanmcd 					    IPPROTO_ENCAP;
30498810c16bSdanmcd 				}
30508810c16bSdanmcd 			}
30518810c16bSdanmcd 			isrc_addr_ptr = (uint32_t *)&isrc->sin_addr;
30528810c16bSdanmcd 			idst_addr_ptr = (uint32_t *)&idst->sin_addr;
30537c478bd9Sstevel@tonic-gate 		} else {
30548810c16bSdanmcd 			ASSERT(isrc->sin_family == AF_INET6);
30558810c16bSdanmcd 			if (srcext->sadb_address_proto != IPPROTO_IPV6) {
30568810c16bSdanmcd 				if (srcext->sadb_address_proto != 0) {
30578810c16bSdanmcd 					/*
30588810c16bSdanmcd 					 * Mismatched outer-packet protocol
30598810c16bSdanmcd 					 * and inner-packet address family.
30608810c16bSdanmcd 					 */
30618810c16bSdanmcd 					mutex_exit(&newbie->ipsa_lock);
30628810c16bSdanmcd 					error = EPROTOTYPE;
3063a1ba8781SMark Fenwick 					*diagnostic =
3064a1ba8781SMark Fenwick 					    SADB_X_DIAGNOSTIC_INNER_AF_MISMATCH;
30658810c16bSdanmcd 					goto error;
30668810c16bSdanmcd 				} else {
30678810c16bSdanmcd 					/* Fill in with explicit protocol. */
30688810c16bSdanmcd 					srcext->sadb_address_proto =
30698810c16bSdanmcd 					    IPPROTO_IPV6;
30708810c16bSdanmcd 					dstext->sadb_address_proto =
30718810c16bSdanmcd 					    IPPROTO_IPV6;
30728810c16bSdanmcd 				}
30737c478bd9Sstevel@tonic-gate 			}
30748810c16bSdanmcd 			isrc_addr_ptr = (uint32_t *)&isrc6->sin6_addr;
30758810c16bSdanmcd 			idst_addr_ptr = (uint32_t *)&idst6->sin6_addr;
30767c478bd9Sstevel@tonic-gate 		}
30778810c16bSdanmcd 		newbie->ipsa_innerfam = isrc->sin_family;
30788810c16bSdanmcd 
30798810c16bSdanmcd 		IPSA_COPY_ADDR(newbie->ipsa_innersrc, isrc_addr_ptr,
30808810c16bSdanmcd 		    newbie->ipsa_innerfam);
30818810c16bSdanmcd 		IPSA_COPY_ADDR(newbie->ipsa_innerdst, idst_addr_ptr,
30828810c16bSdanmcd 		    newbie->ipsa_innerfam);
30838810c16bSdanmcd 		newbie->ipsa_innersrcpfx = isrcext->sadb_address_prefixlen;
30848810c16bSdanmcd 		newbie->ipsa_innerdstpfx = idstext->sadb_address_prefixlen;
30858810c16bSdanmcd 
30868810c16bSdanmcd 		/* Unique value uses inner-ports for Tunnel Mode... */
30878810c16bSdanmcd 		newbie->ipsa_unique_id = SA_UNIQUE_ID(isrc->sin_port,
30888810c16bSdanmcd 		    idst->sin_port, dstext->sadb_address_proto,
30898810c16bSdanmcd 		    idstext->sadb_address_proto);
30908810c16bSdanmcd 		newbie->ipsa_unique_mask = SA_UNIQUE_MASK(isrc->sin_port,
30918810c16bSdanmcd 		    idst->sin_port, dstext->sadb_address_proto,
30928810c16bSdanmcd 		    idstext->sadb_address_proto);
30938810c16bSdanmcd 	} else {
30948810c16bSdanmcd 		/* ... and outer-ports for Transport Mode. */
30958810c16bSdanmcd 		newbie->ipsa_unique_id = SA_UNIQUE_ID(src->sin_port,
30968810c16bSdanmcd 		    dst->sin_port, dstext->sadb_address_proto, 0);
30978810c16bSdanmcd 		newbie->ipsa_unique_mask = SA_UNIQUE_MASK(src->sin_port,
30988810c16bSdanmcd 		    dst->sin_port, dstext->sadb_address_proto, 0);
30997c478bd9Sstevel@tonic-gate 	}
31008810c16bSdanmcd 	if (newbie->ipsa_unique_mask != (uint64_t)0)
31018810c16bSdanmcd 		newbie->ipsa_flags |= IPSA_F_UNIQUE;
31027c478bd9Sstevel@tonic-gate 
31038810c16bSdanmcd 	sadb_nat_calculations(newbie,
31048810c16bSdanmcd 	    (sadb_address_t *)ksi->ks_in_extv[SADB_X_EXT_ADDRESS_NATT_LOC],
31058810c16bSdanmcd 	    (sadb_address_t *)ksi->ks_in_extv[SADB_X_EXT_ADDRESS_NATT_REM],
31068810c16bSdanmcd 	    src_addr_ptr, dst_addr_ptr);
31077c478bd9Sstevel@tonic-gate 
31087c478bd9Sstevel@tonic-gate 	newbie->ipsa_type = samsg->sadb_msg_satype;
3109a1ba8781SMark Fenwick 
31109c2c14abSThejaswini Singarajipura 	ASSERT((assoc->sadb_sa_state == SADB_SASTATE_MATURE) ||
31119c2c14abSThejaswini Singarajipura 	    (assoc->sadb_sa_state == SADB_X_SASTATE_ACTIVE_ELSEWHERE));
31127c478bd9Sstevel@tonic-gate 	newbie->ipsa_auth_alg = assoc->sadb_sa_auth;
31137c478bd9Sstevel@tonic-gate 	newbie->ipsa_encr_alg = assoc->sadb_sa_encrypt;
311438d95a78Smarkfen 
31158810c16bSdanmcd 	newbie->ipsa_flags |= assoc->sadb_sa_flags;
3116a1ba8781SMark Fenwick 	if (newbie->ipsa_flags & SADB_X_SAFLAGS_NATT_LOC &&
3117a1ba8781SMark Fenwick 	    ksi->ks_in_extv[SADB_X_EXT_ADDRESS_NATT_LOC] == NULL) {
31188810c16bSdanmcd 		mutex_exit(&newbie->ipsa_lock);
3119a1ba8781SMark Fenwick 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_NATT_LOC;
3120a1ba8781SMark Fenwick 		error = EINVAL;
3121a1ba8781SMark Fenwick 		goto error;
3122a1ba8781SMark Fenwick 	}
3123a1ba8781SMark Fenwick 	if (newbie->ipsa_flags & SADB_X_SAFLAGS_NATT_REM &&
3124a1ba8781SMark Fenwick 	    ksi->ks_in_extv[SADB_X_EXT_ADDRESS_NATT_REM] == NULL) {
3125a1ba8781SMark Fenwick 		mutex_exit(&newbie->ipsa_lock);
3126a1ba8781SMark Fenwick 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_NATT_REM;
3127a1ba8781SMark Fenwick 		error = EINVAL;
3128a1ba8781SMark Fenwick 		goto error;
3129a1ba8781SMark Fenwick 	}
3130a1ba8781SMark Fenwick 	if (newbie->ipsa_flags & SADB_X_SAFLAGS_TUNNEL &&
3131a1ba8781SMark Fenwick 	    ksi->ks_in_extv[SADB_X_EXT_ADDRESS_INNER_SRC] == NULL) {
3132a1ba8781SMark Fenwick 		mutex_exit(&newbie->ipsa_lock);
3133a1ba8781SMark Fenwick 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_INNER_SRC;
31348810c16bSdanmcd 		error = EINVAL;
31358810c16bSdanmcd 		goto error;
31368810c16bSdanmcd 	}
31377c478bd9Sstevel@tonic-gate 	/*
31387c478bd9Sstevel@tonic-gate 	 * If unspecified source address, force replay_wsize to 0.
31397c478bd9Sstevel@tonic-gate 	 * This is because an SA that has multiple sources of secure
31407c478bd9Sstevel@tonic-gate 	 * traffic cannot enforce a replay counter w/o synchronizing the
31417c478bd9Sstevel@tonic-gate 	 * senders.
31427c478bd9Sstevel@tonic-gate 	 */
31437c478bd9Sstevel@tonic-gate 	if (ksi->ks_in_srctype != KS_IN_ADDR_UNSPEC)
31447c478bd9Sstevel@tonic-gate 		newbie->ipsa_replay_wsize = assoc->sadb_sa_replay;
31457c478bd9Sstevel@tonic-gate 	else
31467c478bd9Sstevel@tonic-gate 		newbie->ipsa_replay_wsize = 0;
31477c478bd9Sstevel@tonic-gate 
3148437220cdSdanmcd 	newbie->ipsa_addtime = gethrestime_sec();
31497c478bd9Sstevel@tonic-gate 
31507c478bd9Sstevel@tonic-gate 	if (kmcext != NULL) {
31517c478bd9Sstevel@tonic-gate 		newbie->ipsa_kmp = kmcext->sadb_x_kmc_proto;
3152cbc1abb4SDan McDonald 		/*
3153cbc1abb4SDan McDonald 		 * Be liberal in what we receive.  Special-case the IKEv1
3154cbc1abb4SDan McDonald 		 * cookie, which closed-source in.iked assumes is 32 bits.
3155cbc1abb4SDan McDonald 		 * Now that we store all 64 bits, we should pre-zero the
3156cbc1abb4SDan McDonald 		 * reserved field on behalf of closed-source in.iked.
3157cbc1abb4SDan McDonald 		 */
3158f4a6f97eSDan McDonald 		if (newbie->ipsa_kmp == SADB_X_KMP_IKE) {
3159f4a6f97eSDan McDonald 			/* Just in case in.iked is misbehaving... */
3160f4a6f97eSDan McDonald 			kmcext->sadb_x_kmc_reserved = 0;
3161f4a6f97eSDan McDonald 		}
3162f4a6f97eSDan McDonald 		newbie->ipsa_kmc = kmcext->sadb_x_kmc_cookie64;
31637c478bd9Sstevel@tonic-gate 	}
31647c478bd9Sstevel@tonic-gate 
31657c478bd9Sstevel@tonic-gate 	/*
31667c478bd9Sstevel@tonic-gate 	 * XXX CURRENT lifetime checks MAY BE needed for an UPDATE.
31677c478bd9Sstevel@tonic-gate 	 * The spec says that one can update current lifetimes, but
31687c478bd9Sstevel@tonic-gate 	 * that seems impractical, especially in the larval-to-mature
31697c478bd9Sstevel@tonic-gate 	 * update that this function performs.
31707c478bd9Sstevel@tonic-gate 	 */
31717c478bd9Sstevel@tonic-gate 	if (soft != NULL) {
31727c478bd9Sstevel@tonic-gate 		newbie->ipsa_softaddlt = soft->sadb_lifetime_addtime;
31737c478bd9Sstevel@tonic-gate 		newbie->ipsa_softuselt = soft->sadb_lifetime_usetime;
31747c478bd9Sstevel@tonic-gate 		newbie->ipsa_softbyteslt = soft->sadb_lifetime_bytes;
31757c478bd9Sstevel@tonic-gate 		newbie->ipsa_softalloc = soft->sadb_lifetime_allocations;
31767c478bd9Sstevel@tonic-gate 		SET_EXPIRE(newbie, softaddlt, softexpiretime);
31777c478bd9Sstevel@tonic-gate 	}
31787c478bd9Sstevel@tonic-gate 	if (hard != NULL) {
31797c478bd9Sstevel@tonic-gate 		newbie->ipsa_hardaddlt = hard->sadb_lifetime_addtime;
31807c478bd9Sstevel@tonic-gate 		newbie->ipsa_harduselt = hard->sadb_lifetime_usetime;
31817c478bd9Sstevel@tonic-gate 		newbie->ipsa_hardbyteslt = hard->sadb_lifetime_bytes;
31827c478bd9Sstevel@tonic-gate 		newbie->ipsa_hardalloc = hard->sadb_lifetime_allocations;
31837c478bd9Sstevel@tonic-gate 		SET_EXPIRE(newbie, hardaddlt, hardexpiretime);
31847c478bd9Sstevel@tonic-gate 	}
31859c2c14abSThejaswini Singarajipura 	if (idle != NULL) {
31869c2c14abSThejaswini Singarajipura 		newbie->ipsa_idleaddlt = idle->sadb_lifetime_addtime;
31879c2c14abSThejaswini Singarajipura 		newbie->ipsa_idleuselt = idle->sadb_lifetime_usetime;
31889c2c14abSThejaswini Singarajipura 		newbie->ipsa_idleexpiretime = newbie->ipsa_addtime +
31899c2c14abSThejaswini Singarajipura 		    newbie->ipsa_idleaddlt;
31909c2c14abSThejaswini Singarajipura 		newbie->ipsa_idletime = newbie->ipsa_idleaddlt;
31919c2c14abSThejaswini Singarajipura 	}
31927c478bd9Sstevel@tonic-gate 
31937c478bd9Sstevel@tonic-gate 	newbie->ipsa_authtmpl = NULL;
31947c478bd9Sstevel@tonic-gate 	newbie->ipsa_encrtmpl = NULL;
31957c478bd9Sstevel@tonic-gate 
3196bd670b35SErik Nordmark #ifdef IPSEC_LATENCY_TEST
3197bd670b35SErik Nordmark 	if (akey != NULL && newbie->ipsa_auth_alg != SADB_AALG_NONE) {
3198bd670b35SErik Nordmark #else
31997c478bd9Sstevel@tonic-gate 	if (akey != NULL) {
3200bd670b35SErik Nordmark #endif
3201bd670b35SErik Nordmark 		async = (ipss->ipsec_algs_exec_mode[IPSEC_ALG_AUTH] ==
3202bd670b35SErik Nordmark 		    IPSEC_ALGS_EXEC_ASYNC);
3203bd670b35SErik Nordmark 
32047c478bd9Sstevel@tonic-gate 		newbie->ipsa_authkeybits = akey->sadb_key_bits;
32057c478bd9Sstevel@tonic-gate 		newbie->ipsa_authkeylen = SADB_1TO8(akey->sadb_key_bits);
32067c478bd9Sstevel@tonic-gate 		/* In case we have to round up to the next byte... */
32077c478bd9Sstevel@tonic-gate 		if ((akey->sadb_key_bits & 0x7) != 0)
32087c478bd9Sstevel@tonic-gate 			newbie->ipsa_authkeylen++;
32097c478bd9Sstevel@tonic-gate 		newbie->ipsa_authkey = kmem_alloc(newbie->ipsa_authkeylen,
32107c478bd9Sstevel@tonic-gate 		    KM_NOSLEEP);
32117c478bd9Sstevel@tonic-gate 		if (newbie->ipsa_authkey == NULL) {
32127c478bd9Sstevel@tonic-gate 			error = ENOMEM;
32137c478bd9Sstevel@tonic-gate 			mutex_exit(&newbie->ipsa_lock);
32147c478bd9Sstevel@tonic-gate 			goto error;
32157c478bd9Sstevel@tonic-gate 		}
32167c478bd9Sstevel@tonic-gate 		bcopy(akey + 1, newbie->ipsa_authkey, newbie->ipsa_authkeylen);
32177c478bd9Sstevel@tonic-gate 		bzero(akey + 1, newbie->ipsa_authkeylen);
32187c478bd9Sstevel@tonic-gate 
32197c478bd9Sstevel@tonic-gate 		/*
32207c478bd9Sstevel@tonic-gate 		 * Pre-initialize the kernel crypto framework key
32217c478bd9Sstevel@tonic-gate 		 * structure.
32227c478bd9Sstevel@tonic-gate 		 */
32237c478bd9Sstevel@tonic-gate 		newbie->ipsa_kcfauthkey.ck_format = CRYPTO_KEY_RAW;
32247c478bd9Sstevel@tonic-gate 		newbie->ipsa_kcfauthkey.ck_length = newbie->ipsa_authkeybits;
32257c478bd9Sstevel@tonic-gate 		newbie->ipsa_kcfauthkey.ck_data = newbie->ipsa_authkey;
32267c478bd9Sstevel@tonic-gate 
322769e71331SBayard Bell 		rw_enter(&ipss->ipsec_alg_lock, RW_READER);
3228628b0c67SMark Fenwick 		alg = ipss->ipsec_alglists[IPSEC_ALG_AUTH]
3229628b0c67SMark Fenwick 		    [newbie->ipsa_auth_alg];
3230628b0c67SMark Fenwick 		if (alg != NULL && ALG_VALID(alg)) {
3231628b0c67SMark Fenwick 			newbie->ipsa_amech.cm_type = alg->alg_mech_type;
3232628b0c67SMark Fenwick 			newbie->ipsa_amech.cm_param =
3233628b0c67SMark Fenwick 			    (char *)&newbie->ipsa_mac_len;
3234628b0c67SMark Fenwick 			newbie->ipsa_amech.cm_param_len = sizeof (size_t);
3235628b0c67SMark Fenwick 			newbie->ipsa_mac_len = (size_t)alg->alg_datalen;
3236628b0c67SMark Fenwick 		} else {
3237628b0c67SMark Fenwick 			newbie->ipsa_amech.cm_type = CRYPTO_MECHANISM_INVALID;
3238628b0c67SMark Fenwick 		}
32397c478bd9Sstevel@tonic-gate 		error = ipsec_create_ctx_tmpl(newbie, IPSEC_ALG_AUTH);
324069e71331SBayard Bell 		rw_exit(&ipss->ipsec_alg_lock);
32417c478bd9Sstevel@tonic-gate 		if (error != 0) {
32427c478bd9Sstevel@tonic-gate 			mutex_exit(&newbie->ipsa_lock);
3243a1ba8781SMark Fenwick 			/*
3244a1ba8781SMark Fenwick 			 * An error here indicates that alg is the wrong type
3245a1ba8781SMark Fenwick 			 * (IE: not authentication) or its not in the alg tables
3246bbf21555SRichard Lowe 			 * created by ipsecalgs(8), or Kcf does not like the
3247a1ba8781SMark Fenwick 			 * parameters passed in with this algorithm, which is
3248a1ba8781SMark Fenwick 			 * probably a coding error!
3249a1ba8781SMark Fenwick 			 */
3250a1ba8781SMark Fenwick 			*diagnostic = SADB_X_DIAGNOSTIC_BAD_CTX;
3251628b0c67SMark Fenwick 
32527c478bd9Sstevel@tonic-gate 			goto error;
32537c478bd9Sstevel@tonic-gate 		}
32547c478bd9Sstevel@tonic-gate 	}
32557c478bd9Sstevel@tonic-gate 
32567c478bd9Sstevel@tonic-gate 	if (ekey != NULL) {
325769e71331SBayard Bell 		rw_enter(&ipss->ipsec_alg_lock, RW_READER);
3258bd670b35SErik Nordmark 		async = async || (ipss->ipsec_algs_exec_mode[IPSEC_ALG_ENCR] ==
3259bd670b35SErik Nordmark 		    IPSEC_ALGS_EXEC_ASYNC);
3260628b0c67SMark Fenwick 		alg = ipss->ipsec_alglists[IPSEC_ALG_ENCR]
3261628b0c67SMark Fenwick 		    [newbie->ipsa_encr_alg];
3262628b0c67SMark Fenwick 
3263628b0c67SMark Fenwick 		if (alg != NULL && ALG_VALID(alg)) {
3264628b0c67SMark Fenwick 			newbie->ipsa_emech.cm_type = alg->alg_mech_type;
3265628b0c67SMark Fenwick 			newbie->ipsa_datalen = alg->alg_datalen;
3266628b0c67SMark Fenwick 			if (alg->alg_flags & ALG_FLAG_COUNTERMODE)
3267628b0c67SMark Fenwick 				newbie->ipsa_flags |= IPSA_F_COUNTERMODE;
3268628b0c67SMark Fenwick 
3269628b0c67SMark Fenwick 			if (alg->alg_flags & ALG_FLAG_COMBINED) {
3270628b0c67SMark Fenwick 				newbie->ipsa_flags |= IPSA_F_COMBINED;
3271628b0c67SMark Fenwick 				newbie->ipsa_mac_len =  alg->alg_icvlen;
3272628b0c67SMark Fenwick 			}
3273628b0c67SMark Fenwick 
3274628b0c67SMark Fenwick 			if (alg->alg_flags & ALG_FLAG_CCM)
3275628b0c67SMark Fenwick 				newbie->ipsa_noncefunc = ccm_params_init;
3276628b0c67SMark Fenwick 			else if (alg->alg_flags & ALG_FLAG_GCM)
3277628b0c67SMark Fenwick 				newbie->ipsa_noncefunc = gcm_params_init;
3278628b0c67SMark Fenwick 			else newbie->ipsa_noncefunc = cbc_params_init;
3279628b0c67SMark Fenwick 
3280628b0c67SMark Fenwick 			newbie->ipsa_saltlen = alg->alg_saltlen;
3281628b0c67SMark Fenwick 			newbie->ipsa_saltbits = SADB_8TO1(newbie->ipsa_saltlen);
3282628b0c67SMark Fenwick 			newbie->ipsa_iv_len = alg->alg_ivlen;
3283628b0c67SMark Fenwick 			newbie->ipsa_nonce_len = newbie->ipsa_saltlen +
3284628b0c67SMark Fenwick 			    newbie->ipsa_iv_len;
3285628b0c67SMark Fenwick 			newbie->ipsa_emech.cm_param = NULL;
3286628b0c67SMark Fenwick 			newbie->ipsa_emech.cm_param_len = 0;
3287628b0c67SMark Fenwick 		} else {
3288628b0c67SMark Fenwick 			newbie->ipsa_emech.cm_type = CRYPTO_MECHANISM_INVALID;
3289628b0c67SMark Fenwick 		}
329069e71331SBayard Bell 		rw_exit(&ipss->ipsec_alg_lock);
3291628b0c67SMark Fenwick 
3292628b0c67SMark Fenwick 		/*
3293628b0c67SMark Fenwick 		 * The byte stream following the sadb_key_t is made up of:
3294628b0c67SMark Fenwick 		 * key bytes, [salt bytes], [IV initial value]
3295628b0c67SMark Fenwick 		 * All of these have variable length. The IV is typically
3296628b0c67SMark Fenwick 		 * randomly generated by this function and not passed in.
3297628b0c67SMark Fenwick 		 * By supporting the injection of a known IV, the whole
3298628b0c67SMark Fenwick 		 * IPsec subsystem and the underlying crypto subsystem
3299628b0c67SMark Fenwick 		 * can be tested with known test vectors.
3300628b0c67SMark Fenwick 		 *
3301628b0c67SMark Fenwick 		 * The keying material has been checked by ext_check()
3302628b0c67SMark Fenwick 		 * and ipsec_valid_key_size(), after removing salt/IV
3303628b0c67SMark Fenwick 		 * bits, whats left is the encryption key. If this is too
3304628b0c67SMark Fenwick 		 * short, ipsec_create_ctx_tmpl() will fail and the SA
3305628b0c67SMark Fenwick 		 * won't get created.
3306628b0c67SMark Fenwick 		 *
3307628b0c67SMark Fenwick 		 * set ipsa_encrkeylen to length of key only.
3308628b0c67SMark Fenwick 		 */
33097c478bd9Sstevel@tonic-gate 		newbie->ipsa_encrkeybits = ekey->sadb_key_bits;
3310628b0c67SMark Fenwick 		newbie->ipsa_encrkeybits -= ekey->sadb_key_reserved;
3311628b0c67SMark Fenwick 		newbie->ipsa_encrkeybits -= newbie->ipsa_saltbits;
3312628b0c67SMark Fenwick 		newbie->ipsa_encrkeylen = SADB_1TO8(newbie->ipsa_encrkeybits);
3313628b0c67SMark Fenwick 
33147c478bd9Sstevel@tonic-gate 		/* In case we have to round up to the next byte... */
33157c478bd9Sstevel@tonic-gate 		if ((ekey->sadb_key_bits & 0x7) != 0)
33167c478bd9Sstevel@tonic-gate 			newbie->ipsa_encrkeylen++;
3317628b0c67SMark Fenwick 
33187c478bd9Sstevel@tonic-gate 		newbie->ipsa_encrkey = kmem_alloc(newbie->ipsa_encrkeylen,
33197c478bd9Sstevel@tonic-gate 		    KM_NOSLEEP);
33207c478bd9Sstevel@tonic-gate 		if (newbie->ipsa_encrkey == NULL) {
33217c478bd9Sstevel@tonic-gate 			error = ENOMEM;
33227c478bd9Sstevel@tonic-gate 			mutex_exit(&newbie->ipsa_lock);
33237c478bd9Sstevel@tonic-gate 			goto error;
33247c478bd9Sstevel@tonic-gate 		}
3325628b0c67SMark Fenwick 
3326628b0c67SMark Fenwick 		buf_ptr = (uint8_t *)(ekey + 1);
3327628b0c67SMark Fenwick 		bcopy(buf_ptr, newbie->ipsa_encrkey, newbie->ipsa_encrkeylen);
3328628b0c67SMark Fenwick 
3329628b0c67SMark Fenwick 		if (newbie->ipsa_flags & IPSA_F_COMBINED) {
3330628b0c67SMark Fenwick 			/*
3331628b0c67SMark Fenwick 			 * Combined mode algs need a nonce. Copy the salt and
3332628b0c67SMark Fenwick 			 * IV into a buffer. The ipsa_nonce is a pointer into
3333628b0c67SMark Fenwick 			 * this buffer, some bytes at the start of the buffer
3334628b0c67SMark Fenwick 			 * may be unused, depends on the salt length. The IV
3335628b0c67SMark Fenwick 			 * is 64 bit aligned so it can be incremented as a
3336628b0c67SMark Fenwick 			 * uint64_t. Zero out key in samsg_t before freeing.
3337628b0c67SMark Fenwick 			 */
3338628b0c67SMark Fenwick 
3339628b0c67SMark Fenwick 			newbie->ipsa_nonce_buf = kmem_alloc(
3340628b0c67SMark Fenwick 			    sizeof (ipsec_nonce_t), KM_NOSLEEP);
3341628b0c67SMark Fenwick 			if (newbie->ipsa_nonce_buf == NULL) {
3342628b0c67SMark Fenwick 				error = ENOMEM;
3343628b0c67SMark Fenwick 				mutex_exit(&newbie->ipsa_lock);
3344628b0c67SMark Fenwick 				goto error;
3345628b0c67SMark Fenwick 			}
3346628b0c67SMark Fenwick 			/*
3347628b0c67SMark Fenwick 			 * Initialize nonce and salt pointers to point
3348628b0c67SMark Fenwick 			 * to the nonce buffer. This is just in case we get
3349628b0c67SMark Fenwick 			 * bad data, the pointers will be valid, the data
3350628b0c67SMark Fenwick 			 * won't be.
3351628b0c67SMark Fenwick 			 *
3352628b0c67SMark Fenwick 			 * See sadb.h for layout of nonce.
3353628b0c67SMark Fenwick 			 */
3354628b0c67SMark Fenwick 			newbie->ipsa_iv = &newbie->ipsa_nonce_buf->iv;
3355628b0c67SMark Fenwick 			newbie->ipsa_salt = (uint8_t *)newbie->ipsa_nonce_buf;
3356628b0c67SMark Fenwick 			newbie->ipsa_nonce = newbie->ipsa_salt;
3357628b0c67SMark Fenwick 			if (newbie->ipsa_saltlen != 0) {
3358628b0c67SMark Fenwick 				salt_offset = MAXSALTSIZE -
3359628b0c67SMark Fenwick 				    newbie->ipsa_saltlen;
3360628b0c67SMark Fenwick 				newbie->ipsa_salt = (uint8_t *)
3361628b0c67SMark Fenwick 				    &newbie->ipsa_nonce_buf->salt[salt_offset];
3362628b0c67SMark Fenwick 				newbie->ipsa_nonce = newbie->ipsa_salt;
3363628b0c67SMark Fenwick 				buf_ptr += newbie->ipsa_encrkeylen;
3364628b0c67SMark Fenwick 				bcopy(buf_ptr, newbie->ipsa_salt,
3365628b0c67SMark Fenwick 				    newbie->ipsa_saltlen);
3366628b0c67SMark Fenwick 			}
3367628b0c67SMark Fenwick 			/*
3368628b0c67SMark Fenwick 			 * The IV for CCM/GCM mode increments, it should not
3369628b0c67SMark Fenwick 			 * repeat. Get a random value for the IV, make a
3370628b0c67SMark Fenwick 			 * copy, the SA will expire when/if the IV ever
3371628b0c67SMark Fenwick 			 * wraps back to the initial value. If an Initial IV
3372628b0c67SMark Fenwick 			 * is passed in via PF_KEY, save this in the SA.
3373628b0c67SMark Fenwick 			 * Initialising IV for inbound is pointless as its
3374628b0c67SMark Fenwick 			 * taken from the inbound packet.
3375628b0c67SMark Fenwick 			 */
3376628b0c67SMark Fenwick 			if (!is_inbound) {
3377628b0c67SMark Fenwick 				if (ekey->sadb_key_reserved != 0) {
3378628b0c67SMark Fenwick 					buf_ptr += newbie->ipsa_saltlen;
3379628b0c67SMark Fenwick 					bcopy(buf_ptr, (uint8_t *)newbie->
3380628b0c67SMark Fenwick 					    ipsa_iv, SADB_1TO8(ekey->
3381628b0c67SMark Fenwick 					    sadb_key_reserved));
3382628b0c67SMark Fenwick 				} else {
3383628b0c67SMark Fenwick 					(void) random_get_pseudo_bytes(
3384628b0c67SMark Fenwick 					    (uint8_t *)newbie->ipsa_iv,
3385628b0c67SMark Fenwick 					    newbie->ipsa_iv_len);
3386628b0c67SMark Fenwick 				}
3387628b0c67SMark Fenwick 				newbie->ipsa_iv_softexpire =
3388628b0c67SMark Fenwick 				    (*newbie->ipsa_iv) << 9;
3389628b0c67SMark Fenwick 				newbie->ipsa_iv_hardexpire = *newbie->ipsa_iv;
3390628b0c67SMark Fenwick 			}
3391628b0c67SMark Fenwick 		}
3392628b0c67SMark Fenwick 		bzero((ekey + 1), SADB_1TO8(ekey->sadb_key_bits));
33937c478bd9Sstevel@tonic-gate 
33947c478bd9Sstevel@tonic-gate 		/*
33957c478bd9Sstevel@tonic-gate 		 * Pre-initialize the kernel crypto framework key
33967c478bd9Sstevel@tonic-gate 		 * structure.
33977c478bd9Sstevel@tonic-gate 		 */
33987c478bd9Sstevel@tonic-gate 		newbie->ipsa_kcfencrkey.ck_format = CRYPTO_KEY_RAW;
33997c478bd9Sstevel@tonic-gate 		newbie->ipsa_kcfencrkey.ck_length = newbie->ipsa_encrkeybits;
34007c478bd9Sstevel@tonic-gate 		newbie->ipsa_kcfencrkey.ck_data = newbie->ipsa_encrkey;
34017c478bd9Sstevel@tonic-gate 
340269e71331SBayard Bell 		rw_enter(&ipss->ipsec_alg_lock, RW_READER);
34037c478bd9Sstevel@tonic-gate 		error = ipsec_create_ctx_tmpl(newbie, IPSEC_ALG_ENCR);
340469e71331SBayard Bell 		rw_exit(&ipss->ipsec_alg_lock);
34057c478bd9Sstevel@tonic-gate 		if (error != 0) {
34067c478bd9Sstevel@tonic-gate 			mutex_exit(&newbie->ipsa_lock);
3407a1ba8781SMark Fenwick 			/* See above for error explanation. */
3408a1ba8781SMark Fenwick 			*diagnostic = SADB_X_DIAGNOSTIC_BAD_CTX;
34097c478bd9Sstevel@tonic-gate 			goto error;
34107c478bd9Sstevel@tonic-gate 		}
34117c478bd9Sstevel@tonic-gate 	}
34127c478bd9Sstevel@tonic-gate 
3413bd670b35SErik Nordmark 	if (async)
3414bd670b35SErik Nordmark 		newbie->ipsa_flags |= IPSA_F_ASYNC;
3415bd670b35SErik Nordmark 
34167c478bd9Sstevel@tonic-gate 	/*
34177c478bd9Sstevel@tonic-gate 	 * Ptrs to processing functions.
34187c478bd9Sstevel@tonic-gate 	 */
34197c478bd9Sstevel@tonic-gate 	if (newbie->ipsa_type == SADB_SATYPE_ESP)
34207c478bd9Sstevel@tonic-gate 		ipsecesp_init_funcs(newbie);
34217c478bd9Sstevel@tonic-gate 	else
34227c478bd9Sstevel@tonic-gate 		ipsecah_init_funcs(newbie);
34237c478bd9Sstevel@tonic-gate 	ASSERT(newbie->ipsa_output_func != NULL &&
34247c478bd9Sstevel@tonic-gate 	    newbie->ipsa_input_func != NULL);
34257c478bd9Sstevel@tonic-gate 
34267c478bd9Sstevel@tonic-gate 	/*
34277c478bd9Sstevel@tonic-gate 	 * Certificate ID stuff.
34287c478bd9Sstevel@tonic-gate 	 */
34297c478bd9Sstevel@tonic-gate 	if (ksi->ks_in_extv[SADB_EXT_IDENTITY_SRC] != NULL) {
34307c478bd9Sstevel@tonic-gate 		sadb_ident_t *id =
34317c478bd9Sstevel@tonic-gate 		    (sadb_ident_t *)ksi->ks_in_extv[SADB_EXT_IDENTITY_SRC];
34327c478bd9Sstevel@tonic-gate 
34337c478bd9Sstevel@tonic-gate 		/*
34347c478bd9Sstevel@tonic-gate 		 * Can assume strlen() will return okay because ext_check() in
34357c478bd9Sstevel@tonic-gate 		 * keysock.c prepares the string for us.
34367c478bd9Sstevel@tonic-gate 		 */
34377c478bd9Sstevel@tonic-gate 		newbie->ipsa_src_cid = ipsid_lookup(id->sadb_ident_type,
3438f4b3ec61Sdh 		    (char *)(id+1), ns);
34397c478bd9Sstevel@tonic-gate 		if (newbie->ipsa_src_cid == NULL) {
34407c478bd9Sstevel@tonic-gate 			error = ENOMEM;
34417c478bd9Sstevel@tonic-gate 			mutex_exit(&newbie->ipsa_lock);
34427c478bd9Sstevel@tonic-gate 			goto error;
34437c478bd9Sstevel@tonic-gate 		}
34447c478bd9Sstevel@tonic-gate 	}
34457c478bd9Sstevel@tonic-gate 
34467c478bd9Sstevel@tonic-gate 	if (ksi->ks_in_extv[SADB_EXT_IDENTITY_DST] != NULL) {
34477c478bd9Sstevel@tonic-gate 		sadb_ident_t *id =
34487c478bd9Sstevel@tonic-gate 		    (sadb_ident_t *)ksi->ks_in_extv[SADB_EXT_IDENTITY_DST];
34497c478bd9Sstevel@tonic-gate 
34507c478bd9Sstevel@tonic-gate 		/*
34517c478bd9Sstevel@tonic-gate 		 * Can assume strlen() will return okay because ext_check() in
34527c478bd9Sstevel@tonic-gate 		 * keysock.c prepares the string for us.
34537c478bd9Sstevel@tonic-gate 		 */
34547c478bd9Sstevel@tonic-gate 		newbie->ipsa_dst_cid = ipsid_lookup(id->sadb_ident_type,
3455f4b3ec61Sdh 		    (char *)(id+1), ns);
34567c478bd9Sstevel@tonic-gate 		if (newbie->ipsa_dst_cid == NULL) {
34577c478bd9Sstevel@tonic-gate 			error = ENOMEM;
34587c478bd9Sstevel@tonic-gate 			mutex_exit(&newbie->ipsa_lock);
34597c478bd9Sstevel@tonic-gate 			goto error;
34607c478bd9Sstevel@tonic-gate 		}
34617c478bd9Sstevel@tonic-gate 	}
34627c478bd9Sstevel@tonic-gate 
34635d3b8cb7SBill Sommerfeld 	/*
34645d3b8cb7SBill Sommerfeld 	 * sensitivity label handling code:
34655d3b8cb7SBill Sommerfeld 	 * Convert sens + bitmap into cred_t, and associate it
34665d3b8cb7SBill Sommerfeld 	 * with the new SA.
34675d3b8cb7SBill Sommerfeld 	 */
34687c478bd9Sstevel@tonic-gate 	if (sens != NULL) {
34697c478bd9Sstevel@tonic-gate 		uint64_t *bitmap = (uint64_t *)(sens + 1);
34707c478bd9Sstevel@tonic-gate 
3471bd670b35SErik Nordmark 		newbie->ipsa_tsl = sadb_label_from_sens(sens, bitmap);
34725d3b8cb7SBill Sommerfeld 	}
34735d3b8cb7SBill Sommerfeld 
34745d3b8cb7SBill Sommerfeld 	/*
34755d3b8cb7SBill Sommerfeld 	 * Likewise for outer sensitivity.
34765d3b8cb7SBill Sommerfeld 	 */
34775d3b8cb7SBill Sommerfeld 	if (osens != NULL) {
34785d3b8cb7SBill Sommerfeld 		uint64_t *bitmap = (uint64_t *)(osens + 1);
3479bd670b35SErik Nordmark 		ts_label_t *tsl, *effective_tsl;
34805d3b8cb7SBill Sommerfeld 		uint32_t *peer_addr_ptr;
3481bd670b35SErik Nordmark 		zoneid_t zoneid = GLOBAL_ZONEID;
3482bd670b35SErik Nordmark 		zone_t *zone;
34835d3b8cb7SBill Sommerfeld 
34845d3b8cb7SBill Sommerfeld 		peer_addr_ptr = is_inbound ? src_addr_ptr : dst_addr_ptr;
34855d3b8cb7SBill Sommerfeld 
3486bd670b35SErik Nordmark 		tsl = sadb_label_from_sens(osens, bitmap);
34875d3b8cb7SBill Sommerfeld 		newbie->ipsa_mac_exempt = CONN_MAC_DEFAULT;
34885d3b8cb7SBill Sommerfeld 
34895d3b8cb7SBill Sommerfeld 		if (osens->sadb_x_sens_flags & SADB_X_SENS_IMPLICIT) {
34905d3b8cb7SBill Sommerfeld 			newbie->ipsa_mac_exempt = CONN_MAC_IMPLICIT;
34917c478bd9Sstevel@tonic-gate 		}
34925d3b8cb7SBill Sommerfeld 
3493bd670b35SErik Nordmark 		error = tsol_check_dest(tsl, peer_addr_ptr,
34945d3b8cb7SBill Sommerfeld 		    (af == AF_INET6)?IPV6_VERSION:IPV4_VERSION,
3495bd670b35SErik Nordmark 		    newbie->ipsa_mac_exempt, B_TRUE, &effective_tsl);
34965d3b8cb7SBill Sommerfeld 		if (error != 0) {
3497bd670b35SErik Nordmark 			label_rele(tsl);
34987c478bd9Sstevel@tonic-gate 			mutex_exit(&newbie->ipsa_lock);
34997c478bd9Sstevel@tonic-gate 			goto error;
35007c478bd9Sstevel@tonic-gate 		}
35015d3b8cb7SBill Sommerfeld 
3502bd670b35SErik Nordmark 		if (effective_tsl != NULL) {
3503bd670b35SErik Nordmark 			label_rele(tsl);
3504bd670b35SErik Nordmark 			tsl = effective_tsl;
35057c478bd9Sstevel@tonic-gate 		}
35065d3b8cb7SBill Sommerfeld 
3507bd670b35SErik Nordmark 		newbie->ipsa_otsl = tsl;
3508bd670b35SErik Nordmark 
3509bd670b35SErik Nordmark 		zone = zone_find_by_label(tsl);
3510bd670b35SErik Nordmark 		if (zone != NULL) {
3511bd670b35SErik Nordmark 			zoneid = zone->zone_id;
3512bd670b35SErik Nordmark 			zone_rele(zone);
3513bd670b35SErik Nordmark 		}
3514bd670b35SErik Nordmark 		/*
3515bd670b35SErik Nordmark 		 * For exclusive stacks we set the zoneid to zero to operate
3516bd670b35SErik Nordmark 		 * as if in the global zone for tsol_compute_label_v4/v6
3517bd670b35SErik Nordmark 		 */
3518bd670b35SErik Nordmark 		if (ipst->ips_netstack->netstack_stackid != GLOBAL_NETSTACKID)
3519bd670b35SErik Nordmark 			zoneid = GLOBAL_ZONEID;
35205d3b8cb7SBill Sommerfeld 
35215d3b8cb7SBill Sommerfeld 		if (af == AF_INET6) {
3522bd670b35SErik Nordmark 			error = tsol_compute_label_v6(tsl, zoneid,
3523eb035775SBill Sommerfeld 			    (in6_addr_t *)peer_addr_ptr,
35245d3b8cb7SBill Sommerfeld 			    newbie->ipsa_opt_storage, ipst);
35255d3b8cb7SBill Sommerfeld 		} else {
3526bd670b35SErik Nordmark 			error = tsol_compute_label_v4(tsl, zoneid,
3527bd670b35SErik Nordmark 			    *peer_addr_ptr, newbie->ipsa_opt_storage, ipst);
35287c478bd9Sstevel@tonic-gate 		}
3529eb035775SBill Sommerfeld 		if (error != 0) {
3530eb035775SBill Sommerfeld 			mutex_exit(&newbie->ipsa_lock);
3531eb035775SBill Sommerfeld 			goto error;
3532eb035775SBill Sommerfeld 		}
35337c478bd9Sstevel@tonic-gate 	}
35347c478bd9Sstevel@tonic-gate 
35357c478bd9Sstevel@tonic-gate 
35369c2c14abSThejaswini Singarajipura 	if (replayext != NULL) {
35379c2c14abSThejaswini Singarajipura 		if ((replayext->sadb_x_rc_replay32 == 0) &&
35389c2c14abSThejaswini Singarajipura 		    (replayext->sadb_x_rc_replay64 != 0)) {
35399c2c14abSThejaswini Singarajipura 			error = EOPNOTSUPP;
3540a1ba8781SMark Fenwick 			*diagnostic = SADB_X_DIAGNOSTIC_INVALID_REPLAY;
35419c2c14abSThejaswini Singarajipura 			mutex_exit(&newbie->ipsa_lock);
35429c2c14abSThejaswini Singarajipura 			goto error;
35439c2c14abSThejaswini Singarajipura 		}
35449c2c14abSThejaswini Singarajipura 		newbie->ipsa_replay = replayext->sadb_x_rc_replay32;
35459c2c14abSThejaswini Singarajipura 	}
35469c2c14abSThejaswini Singarajipura 
35477c478bd9Sstevel@tonic-gate 	/* now that the SA has been updated, set its new state */
35487c478bd9Sstevel@tonic-gate 	newbie->ipsa_state = assoc->sadb_sa_state;
35497c478bd9Sstevel@tonic-gate 
355038d95a78Smarkfen 	if (clone) {
355138d95a78Smarkfen 		newbie->ipsa_haspeer = B_TRUE;
355238d95a78Smarkfen 	} else {
355338d95a78Smarkfen 		if (!is_inbound) {
355438d95a78Smarkfen 			lifetime_fuzz(newbie);
355538d95a78Smarkfen 		}
355638d95a78Smarkfen 	}
35577c478bd9Sstevel@tonic-gate 	/*
35587c478bd9Sstevel@tonic-gate 	 * The less locks I hold when doing an insertion and possible cloning,
35597c478bd9Sstevel@tonic-gate 	 * the better!
35607c478bd9Sstevel@tonic-gate 	 */
35617c478bd9Sstevel@tonic-gate 	mutex_exit(&newbie->ipsa_lock);
35627c478bd9Sstevel@tonic-gate 
35637c478bd9Sstevel@tonic-gate 	if (clone) {
35647c478bd9Sstevel@tonic-gate 		newbie_clone = sadb_cloneassoc(newbie);
35657c478bd9Sstevel@tonic-gate 
35667c478bd9Sstevel@tonic-gate 		if (newbie_clone == NULL) {
35677c478bd9Sstevel@tonic-gate 			error = ENOMEM;
35687c478bd9Sstevel@tonic-gate 			goto error;
35697c478bd9Sstevel@tonic-gate 		}
35707c478bd9Sstevel@tonic-gate 	}
35717c478bd9Sstevel@tonic-gate 
35727c478bd9Sstevel@tonic-gate 	/*
35737c478bd9Sstevel@tonic-gate 	 * Enter the bucket locks.  The order of entry is outbound,
35747c478bd9Sstevel@tonic-gate 	 * inbound.  We map "primary" and "secondary" into outbound and inbound
35757c478bd9Sstevel@tonic-gate 	 * based on the destination address type.  If the destination address
35767c478bd9Sstevel@tonic-gate 	 * type is for a node that isn't mine (or potentially mine), the
35777c478bd9Sstevel@tonic-gate 	 * "primary" bucket is the outbound one.
35787c478bd9Sstevel@tonic-gate 	 */
357938d95a78Smarkfen 	if (!is_inbound) {
35807c478bd9Sstevel@tonic-gate 		/* primary == outbound */
35817c478bd9Sstevel@tonic-gate 		mutex_enter(&primary->isaf_lock);
35827c478bd9Sstevel@tonic-gate 		mutex_enter(&secondary->isaf_lock);
35837c478bd9Sstevel@tonic-gate 	} else {
35847c478bd9Sstevel@tonic-gate 		/* primary == inbound */
35857c478bd9Sstevel@tonic-gate 		mutex_enter(&secondary->isaf_lock);
35867c478bd9Sstevel@tonic-gate 		mutex_enter(&primary->isaf_lock);
35877c478bd9Sstevel@tonic-gate 	}
35887c478bd9Sstevel@tonic-gate 
35897c478bd9Sstevel@tonic-gate 	/*
35907c478bd9Sstevel@tonic-gate 	 * sadb_insertassoc() doesn't increment the reference
35917c478bd9Sstevel@tonic-gate 	 * count.  We therefore have to increment the
35927c478bd9Sstevel@tonic-gate 	 * reference count one more time to reflect the
35937c478bd9Sstevel@tonic-gate 	 * pointers of the table that reference this SA.
35947c478bd9Sstevel@tonic-gate 	 */
35957c478bd9Sstevel@tonic-gate 	IPSA_REFHOLD(newbie);
35967c478bd9Sstevel@tonic-gate 
35977c478bd9Sstevel@tonic-gate 	if (isupdate) {
35987c478bd9Sstevel@tonic-gate 		/*
35997c478bd9Sstevel@tonic-gate 		 * Unlink from larval holding cell in the "inbound" fanout.
36007c478bd9Sstevel@tonic-gate 		 */
36017c478bd9Sstevel@tonic-gate 		ASSERT(newbie->ipsa_linklock == &primary->isaf_lock ||
36027c478bd9Sstevel@tonic-gate 		    newbie->ipsa_linklock == &secondary->isaf_lock);
36037c478bd9Sstevel@tonic-gate 		sadb_unlinkassoc(newbie);
36047c478bd9Sstevel@tonic-gate 	}
36057c478bd9Sstevel@tonic-gate 
36067c478bd9Sstevel@tonic-gate 	mutex_enter(&newbie->ipsa_lock);
36077c478bd9Sstevel@tonic-gate 	error = sadb_insertassoc(newbie, primary);
36087c478bd9Sstevel@tonic-gate 	mutex_exit(&newbie->ipsa_lock);
36097c478bd9Sstevel@tonic-gate 
36107c478bd9Sstevel@tonic-gate 	if (error != 0) {
36117c478bd9Sstevel@tonic-gate 		/*
36127c478bd9Sstevel@tonic-gate 		 * Since sadb_insertassoc() failed, we must decrement the
36137c478bd9Sstevel@tonic-gate 		 * refcount again so the cleanup code will actually free
36147c478bd9Sstevel@tonic-gate 		 * the offending SA.
36157c478bd9Sstevel@tonic-gate 		 */
36167c478bd9Sstevel@tonic-gate 		IPSA_REFRELE(newbie);
36177c478bd9Sstevel@tonic-gate 		goto error_unlock;
36187c478bd9Sstevel@tonic-gate 	}
36197c478bd9Sstevel@tonic-gate 
36207c478bd9Sstevel@tonic-gate 	if (newbie_clone != NULL) {
36217c478bd9Sstevel@tonic-gate 		mutex_enter(&newbie_clone->ipsa_lock);
36227c478bd9Sstevel@tonic-gate 		error = sadb_insertassoc(newbie_clone, secondary);
36237c478bd9Sstevel@tonic-gate 		mutex_exit(&newbie_clone->ipsa_lock);
36247c478bd9Sstevel@tonic-gate 		if (error != 0) {
36257c478bd9Sstevel@tonic-gate 			/* Collision in secondary table. */
36267c478bd9Sstevel@tonic-gate 			sadb_unlinkassoc(newbie);  /* This does REFRELE. */
36277c478bd9Sstevel@tonic-gate 			goto error_unlock;
36287c478bd9Sstevel@tonic-gate 		}
36297c478bd9Sstevel@tonic-gate 		IPSA_REFHOLD(newbie_clone);
36307c478bd9Sstevel@tonic-gate 	} else {
36317c478bd9Sstevel@tonic-gate 		ASSERT(primary != secondary);
36327c478bd9Sstevel@tonic-gate 		scratch = ipsec_getassocbyspi(secondary, newbie->ipsa_spi,
36337c478bd9Sstevel@tonic-gate 		    ALL_ZEROES_PTR, newbie->ipsa_dstaddr, af);
36347c478bd9Sstevel@tonic-gate 		if (scratch != NULL) {
36357c478bd9Sstevel@tonic-gate 			/* Collision in secondary table. */
36367c478bd9Sstevel@tonic-gate 			sadb_unlinkassoc(newbie);  /* This does REFRELE. */
36377c478bd9Sstevel@tonic-gate 			/* Set the error, since ipsec_getassocbyspi() can't. */
36387c478bd9Sstevel@tonic-gate 			error = EEXIST;
36397c478bd9Sstevel@tonic-gate 			goto error_unlock;
36407c478bd9Sstevel@tonic-gate 		}
36417c478bd9Sstevel@tonic-gate 	}
36427c478bd9Sstevel@tonic-gate 
36437c478bd9Sstevel@tonic-gate 	/* OKAY!  So let's do some reality check assertions. */
36447c478bd9Sstevel@tonic-gate 
36450c0328cdSBill Sommerfeld 	ASSERT(MUTEX_NOT_HELD(&newbie->ipsa_lock));
36460c0328cdSBill Sommerfeld 	ASSERT(newbie_clone == NULL ||
36470c0328cdSBill Sommerfeld 	    (MUTEX_NOT_HELD(&newbie_clone->ipsa_lock)));
36487c478bd9Sstevel@tonic-gate 
36497c478bd9Sstevel@tonic-gate error_unlock:
36507c478bd9Sstevel@tonic-gate 
36517c478bd9Sstevel@tonic-gate 	/*
36527c478bd9Sstevel@tonic-gate 	 * We can exit the locks in any order.	Only entrance needs to
36537c478bd9Sstevel@tonic-gate 	 * follow any protocol.
36547c478bd9Sstevel@tonic-gate 	 */
36557c478bd9Sstevel@tonic-gate 	mutex_exit(&secondary->isaf_lock);
36567c478bd9Sstevel@tonic-gate 	mutex_exit(&primary->isaf_lock);
36577c478bd9Sstevel@tonic-gate 
365838d95a78Smarkfen 	if (pair_ext != NULL && error == 0) {
365938d95a78Smarkfen 		/* update pair_spi if it exists. */
36605d3b8cb7SBill Sommerfeld 		ipsa_query_t sq;
36615d3b8cb7SBill Sommerfeld 
36625d3b8cb7SBill Sommerfeld 		sq.spp = spp;		/* XXX param */
36635d3b8cb7SBill Sommerfeld 		error = sadb_form_query(ksi, IPSA_Q_DST, IPSA_Q_SRC|IPSA_Q_DST|
36645d3b8cb7SBill Sommerfeld 		    IPSA_Q_SA|IPSA_Q_INBOUND|IPSA_Q_OUTBOUND, &sq, diagnostic);
36655d3b8cb7SBill Sommerfeld 		if (error)
36665d3b8cb7SBill Sommerfeld 			return (error);
36675d3b8cb7SBill Sommerfeld 
36685d3b8cb7SBill Sommerfeld 		error = get_ipsa_pair(&sq, &ipsapp, diagnostic);
36695d3b8cb7SBill Sommerfeld 
36705d3b8cb7SBill Sommerfeld 		if (error != 0)
36715d3b8cb7SBill Sommerfeld 			goto error;
36725d3b8cb7SBill Sommerfeld 
36735d3b8cb7SBill Sommerfeld 		if (ipsapp.ipsap_psa_ptr != NULL) {
367438d95a78Smarkfen 			*diagnostic = SADB_X_DIAGNOSTIC_PAIR_ALREADY;
367538d95a78Smarkfen 			error = EINVAL;
367638d95a78Smarkfen 		} else {
367738d95a78Smarkfen 			/* update_pairing() sets diagnostic */
36785d3b8cb7SBill Sommerfeld 			error = update_pairing(&ipsapp, &sq, ksi, diagnostic);
367938d95a78Smarkfen 		}
368038d95a78Smarkfen 	}
36817c478bd9Sstevel@tonic-gate 	/* Common error point for this routine. */
36827c478bd9Sstevel@tonic-gate error:
36837c478bd9Sstevel@tonic-gate 	if (newbie != NULL) {
368438d95a78Smarkfen 		if (error != 0) {
368538d95a78Smarkfen 			/* This SA is broken, let the reaper clean up. */
368638d95a78Smarkfen 			mutex_enter(&newbie->ipsa_lock);
368738d95a78Smarkfen 			newbie->ipsa_state = IPSA_STATE_DEAD;
368838d95a78Smarkfen 			newbie->ipsa_hardexpiretime = 1;
368938d95a78Smarkfen 			mutex_exit(&newbie->ipsa_lock);
369038d95a78Smarkfen 		}
36917c478bd9Sstevel@tonic-gate 		IPSA_REFRELE(newbie);
36927c478bd9Sstevel@tonic-gate 	}
36937c478bd9Sstevel@tonic-gate 	if (newbie_clone != NULL) {
36947c478bd9Sstevel@tonic-gate 		IPSA_REFRELE(newbie_clone);
36957c478bd9Sstevel@tonic-gate 	}
36967c478bd9Sstevel@tonic-gate 
36977c478bd9Sstevel@tonic-gate 	if (error == 0) {
36987c478bd9Sstevel@tonic-gate 		/*
36997c478bd9Sstevel@tonic-gate 		 * Construct favorable PF_KEY return message and send to
370038d95a78Smarkfen 		 * keysock. Update the flags in the original keysock message
370138d95a78Smarkfen 		 * to reflect the actual flags in the new SA.
370238d95a78Smarkfen 		 *  (Q:  Do I need to pass "newbie"?  If I do,
37037c478bd9Sstevel@tonic-gate 		 * make sure to REFHOLD, call, then REFRELE.)
37047c478bd9Sstevel@tonic-gate 		 */
370538d95a78Smarkfen 		assoc->sadb_sa_flags = newbie->ipsa_flags;
37067c478bd9Sstevel@tonic-gate 		sadb_pfkey_echo(pfkey_q, mp, samsg, ksi, NULL);
37077c478bd9Sstevel@tonic-gate 	}
37087c478bd9Sstevel@tonic-gate 
37095d3b8cb7SBill Sommerfeld 	destroy_ipsa_pair(&ipsapp);
37107c478bd9Sstevel@tonic-gate 	return (error);
37117c478bd9Sstevel@tonic-gate }
37127c478bd9Sstevel@tonic-gate 
37137c478bd9Sstevel@tonic-gate /*
37147c478bd9Sstevel@tonic-gate  * Set the time of first use for a security association.  Update any
37157c478bd9Sstevel@tonic-gate  * expiration times as a result.
37167c478bd9Sstevel@tonic-gate  */
37177c478bd9Sstevel@tonic-gate void
37187c478bd9Sstevel@tonic-gate sadb_set_usetime(ipsa_t *assoc)
37197c478bd9Sstevel@tonic-gate {
3720437220cdSdanmcd 	time_t snapshot = gethrestime_sec();
3721437220cdSdanmcd 
37227c478bd9Sstevel@tonic-gate 	mutex_enter(&assoc->ipsa_lock);
3723437220cdSdanmcd 	assoc->ipsa_lastuse = snapshot;
37249c2c14abSThejaswini Singarajipura 	assoc->ipsa_idleexpiretime = snapshot + assoc->ipsa_idletime;
37259c2c14abSThejaswini Singarajipura 
37267c478bd9Sstevel@tonic-gate 	/*
37277c478bd9Sstevel@tonic-gate 	 * Caller does check usetime before calling me usually, and
37287c478bd9Sstevel@tonic-gate 	 * double-checking is better than a mutex_enter/exit hit.
37297c478bd9Sstevel@tonic-gate 	 */
37307c478bd9Sstevel@tonic-gate 	if (assoc->ipsa_usetime == 0) {
37317c478bd9Sstevel@tonic-gate 		/*
37327c478bd9Sstevel@tonic-gate 		 * This is redundant for outbound SA's, as
37337c478bd9Sstevel@tonic-gate 		 * ipsec_getassocbyconn() sets the IPSA_F_USED flag already.
37347c478bd9Sstevel@tonic-gate 		 * Inbound SAs, however, have no such protection.
37357c478bd9Sstevel@tonic-gate 		 */
37367c478bd9Sstevel@tonic-gate 		assoc->ipsa_flags |= IPSA_F_USED;
3737437220cdSdanmcd 		assoc->ipsa_usetime = snapshot;
37387c478bd9Sstevel@tonic-gate 
37397c478bd9Sstevel@tonic-gate 		/*
37407c478bd9Sstevel@tonic-gate 		 * After setting the use time, see if we have a use lifetime
37417c478bd9Sstevel@tonic-gate 		 * that would cause the actual SA expiration time to shorten.
37427c478bd9Sstevel@tonic-gate 		 */
37437c478bd9Sstevel@tonic-gate 		UPDATE_EXPIRE(assoc, softuselt, softexpiretime);
37447c478bd9Sstevel@tonic-gate 		UPDATE_EXPIRE(assoc, harduselt, hardexpiretime);
37457c478bd9Sstevel@tonic-gate 	}
37467c478bd9Sstevel@tonic-gate 	mutex_exit(&assoc->ipsa_lock);
37477c478bd9Sstevel@tonic-gate }
37487c478bd9Sstevel@tonic-gate 
37497c478bd9Sstevel@tonic-gate /*
37507c478bd9Sstevel@tonic-gate  * Send up a PF_KEY expire message for this association.
37517c478bd9Sstevel@tonic-gate  */
37527c478bd9Sstevel@tonic-gate static void
37537c478bd9Sstevel@tonic-gate sadb_expire_assoc(queue_t *pfkey_q, ipsa_t *assoc)
37547c478bd9Sstevel@tonic-gate {
37557c478bd9Sstevel@tonic-gate 	mblk_t *mp, *mp1;
37567c478bd9Sstevel@tonic-gate 	int alloclen, af;
37577c478bd9Sstevel@tonic-gate 	sadb_msg_t *samsg;
37587c478bd9Sstevel@tonic-gate 	sadb_lifetime_t *current, *expire;
37597c478bd9Sstevel@tonic-gate 	sadb_sa_t *saext;
37607c478bd9Sstevel@tonic-gate 	uint8_t *end;
37618810c16bSdanmcd 	boolean_t tunnel_mode;
37627c478bd9Sstevel@tonic-gate 
37637c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&assoc->ipsa_lock));
37647c478bd9Sstevel@tonic-gate 
37657c478bd9Sstevel@tonic-gate 	/* Don't bother sending if there's no queue. */
37667c478bd9Sstevel@tonic-gate 	if (pfkey_q == NULL)
37677c478bd9Sstevel@tonic-gate 		return;
37687c478bd9Sstevel@tonic-gate 
37697c478bd9Sstevel@tonic-gate 	mp = sadb_keysock_out(0);
37707c478bd9Sstevel@tonic-gate 	if (mp == NULL) {
37717c478bd9Sstevel@tonic-gate 		/* cmn_err(CE_WARN, */
37727c478bd9Sstevel@tonic-gate 		/*	"sadb_expire_assoc: Can't allocate KEYSOCK_OUT.\n"); */
37737c478bd9Sstevel@tonic-gate 		return;
37747c478bd9Sstevel@tonic-gate 	}
37757c478bd9Sstevel@tonic-gate 
37767c478bd9Sstevel@tonic-gate 	alloclen = sizeof (*samsg) + sizeof (*current) + sizeof (*expire) +
3777*cd434274SJason King 	    2 * sizeof (sadb_address_t) + sizeof (*saext) +
3778*cd434274SJason King 	    sizeof (sadb_x_kmc_t);
37797c478bd9Sstevel@tonic-gate 
37807c478bd9Sstevel@tonic-gate 	af = assoc->ipsa_addrfam;
37817c478bd9Sstevel@tonic-gate 	switch (af) {
37827c478bd9Sstevel@tonic-gate 	case AF_INET:
37837c478bd9Sstevel@tonic-gate 		alloclen += 2 * sizeof (struct sockaddr_in);
37847c478bd9Sstevel@tonic-gate 		break;
37857c478bd9Sstevel@tonic-gate 	case AF_INET6:
37867c478bd9Sstevel@tonic-gate 		alloclen += 2 * sizeof (struct sockaddr_in6);
37877c478bd9Sstevel@tonic-gate 		break;
37887c478bd9Sstevel@tonic-gate 	default:
37897c478bd9Sstevel@tonic-gate 		/* Won't happen unless there's a kernel bug. */
37907c478bd9Sstevel@tonic-gate 		freeb(mp);
37917c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN,
37927c478bd9Sstevel@tonic-gate 		    "sadb_expire_assoc: Unknown address length.\n");
37937c478bd9Sstevel@tonic-gate 		return;
37947c478bd9Sstevel@tonic-gate 	}
37957c478bd9Sstevel@tonic-gate 
37968810c16bSdanmcd 	tunnel_mode = (assoc->ipsa_flags & IPSA_F_TUNNEL);
37978810c16bSdanmcd 	if (tunnel_mode) {
37988810c16bSdanmcd 		alloclen += 2 * sizeof (sadb_address_t);
37998810c16bSdanmcd 		switch (assoc->ipsa_innerfam) {
38008810c16bSdanmcd 		case AF_INET:
38018810c16bSdanmcd 			alloclen += 2 * sizeof (struct sockaddr_in);
38028810c16bSdanmcd 			break;
38038810c16bSdanmcd 		case AF_INET6:
38048810c16bSdanmcd 			alloclen += 2 * sizeof (struct sockaddr_in6);
38058810c16bSdanmcd 			break;
38068810c16bSdanmcd 		default:
38078810c16bSdanmcd 			/* Won't happen unless there's a kernel bug. */
38088810c16bSdanmcd 			freeb(mp);
38098810c16bSdanmcd 			cmn_err(CE_WARN, "sadb_expire_assoc: "
38108810c16bSdanmcd 			    "Unknown inner address length.\n");
38118810c16bSdanmcd 			return;
38128810c16bSdanmcd 		}
38138810c16bSdanmcd 	}
38148810c16bSdanmcd 
38157c478bd9Sstevel@tonic-gate 	mp->b_cont = allocb(alloclen, BPRI_HI);
38167c478bd9Sstevel@tonic-gate 	if (mp->b_cont == NULL) {
38177c478bd9Sstevel@tonic-gate 		freeb(mp);
38187c478bd9Sstevel@tonic-gate 		/* cmn_err(CE_WARN, */
38197c478bd9Sstevel@tonic-gate 		/*	"sadb_expire_assoc: Can't allocate message.\n"); */
38207c478bd9Sstevel@tonic-gate 		return;
38217c478bd9Sstevel@tonic-gate 	}
38227c478bd9Sstevel@tonic-gate 
38237c478bd9Sstevel@tonic-gate 	mp1 = mp;
38247c478bd9Sstevel@tonic-gate 	mp = mp->b_cont;
38257c478bd9Sstevel@tonic-gate 	end = mp->b_wptr + alloclen;
38267c478bd9Sstevel@tonic-gate 
38277c478bd9Sstevel@tonic-gate 	samsg = (sadb_msg_t *)mp->b_wptr;
38287c478bd9Sstevel@tonic-gate 	mp->b_wptr += sizeof (*samsg);
38297c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_version = PF_KEY_V2;
38307c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_type = SADB_EXPIRE;
38317c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_errno = 0;
38327c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_satype = assoc->ipsa_type;
38337c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_len = SADB_8TO64(alloclen);
38347c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_reserved = 0;
38357c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_seq = 0;
38367c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_pid = 0;
38377c478bd9Sstevel@tonic-gate 
38387c478bd9Sstevel@tonic-gate 	saext = (sadb_sa_t *)mp->b_wptr;
38397c478bd9Sstevel@tonic-gate 	mp->b_wptr += sizeof (*saext);
38407c478bd9Sstevel@tonic-gate 	saext->sadb_sa_len = SADB_8TO64(sizeof (*saext));
38417c478bd9Sstevel@tonic-gate 	saext->sadb_sa_exttype = SADB_EXT_SA;
38427c478bd9Sstevel@tonic-gate 	saext->sadb_sa_spi = assoc->ipsa_spi;
38437c478bd9Sstevel@tonic-gate 	saext->sadb_sa_replay = assoc->ipsa_replay_wsize;
38447c478bd9Sstevel@tonic-gate 	saext->sadb_sa_state = assoc->ipsa_state;
38457c478bd9Sstevel@tonic-gate 	saext->sadb_sa_auth = assoc->ipsa_auth_alg;
38467c478bd9Sstevel@tonic-gate 	saext->sadb_sa_encrypt = assoc->ipsa_encr_alg;
38477c478bd9Sstevel@tonic-gate 	saext->sadb_sa_flags = assoc->ipsa_flags;
38487c478bd9Sstevel@tonic-gate 
38497c478bd9Sstevel@tonic-gate 	current = (sadb_lifetime_t *)mp->b_wptr;
38507c478bd9Sstevel@tonic-gate 	mp->b_wptr += sizeof (sadb_lifetime_t);
38517c478bd9Sstevel@tonic-gate 	current->sadb_lifetime_len = SADB_8TO64(sizeof (*current));
38527c478bd9Sstevel@tonic-gate 	current->sadb_lifetime_exttype = SADB_EXT_LIFETIME_CURRENT;
3853437220cdSdanmcd 	/* We do not support the concept. */
3854437220cdSdanmcd 	current->sadb_lifetime_allocations = 0;
38557c478bd9Sstevel@tonic-gate 	current->sadb_lifetime_bytes = assoc->ipsa_bytes;
38567c478bd9Sstevel@tonic-gate 	current->sadb_lifetime_addtime = assoc->ipsa_addtime;
38577c478bd9Sstevel@tonic-gate 	current->sadb_lifetime_usetime = assoc->ipsa_usetime;
38587c478bd9Sstevel@tonic-gate 
38597c478bd9Sstevel@tonic-gate 	expire = (sadb_lifetime_t *)mp->b_wptr;
38607c478bd9Sstevel@tonic-gate 	mp->b_wptr += sizeof (*expire);
38617c478bd9Sstevel@tonic-gate 	expire->sadb_lifetime_len = SADB_8TO64(sizeof (*expire));
38627c478bd9Sstevel@tonic-gate 
38637c478bd9Sstevel@tonic-gate 	if (assoc->ipsa_state == IPSA_STATE_DEAD) {
38647c478bd9Sstevel@tonic-gate 		expire->sadb_lifetime_exttype = SADB_EXT_LIFETIME_HARD;
38657c478bd9Sstevel@tonic-gate 		expire->sadb_lifetime_allocations = assoc->ipsa_hardalloc;
38667c478bd9Sstevel@tonic-gate 		expire->sadb_lifetime_bytes = assoc->ipsa_hardbyteslt;
38677c478bd9Sstevel@tonic-gate 		expire->sadb_lifetime_addtime = assoc->ipsa_hardaddlt;
38687c478bd9Sstevel@tonic-gate 		expire->sadb_lifetime_usetime = assoc->ipsa_harduselt;
38699c2c14abSThejaswini Singarajipura 	} else if (assoc->ipsa_state == IPSA_STATE_DYING) {
38707c478bd9Sstevel@tonic-gate 		expire->sadb_lifetime_exttype = SADB_EXT_LIFETIME_SOFT;
38717c478bd9Sstevel@tonic-gate 		expire->sadb_lifetime_allocations = assoc->ipsa_softalloc;
38727c478bd9Sstevel@tonic-gate 		expire->sadb_lifetime_bytes = assoc->ipsa_softbyteslt;
38737c478bd9Sstevel@tonic-gate 		expire->sadb_lifetime_addtime = assoc->ipsa_softaddlt;
38747c478bd9Sstevel@tonic-gate 		expire->sadb_lifetime_usetime = assoc->ipsa_softuselt;
38759c2c14abSThejaswini Singarajipura 	} else {
38769c2c14abSThejaswini Singarajipura 		ASSERT(assoc->ipsa_state == IPSA_STATE_MATURE);
38779c2c14abSThejaswini Singarajipura 		expire->sadb_lifetime_exttype = SADB_X_EXT_LIFETIME_IDLE;
38789c2c14abSThejaswini Singarajipura 		expire->sadb_lifetime_allocations = 0;
38799c2c14abSThejaswini Singarajipura 		expire->sadb_lifetime_bytes = 0;
38809c2c14abSThejaswini Singarajipura 		expire->sadb_lifetime_addtime = assoc->ipsa_idleaddlt;
38819c2c14abSThejaswini Singarajipura 		expire->sadb_lifetime_usetime = assoc->ipsa_idleuselt;
38827c478bd9Sstevel@tonic-gate 	}
38837c478bd9Sstevel@tonic-gate 
38847c478bd9Sstevel@tonic-gate 	mp->b_wptr = sadb_make_addr_ext(mp->b_wptr, end, SADB_EXT_ADDRESS_SRC,
38858810c16bSdanmcd 	    af, assoc->ipsa_srcaddr, tunnel_mode ? 0 : SA_SRCPORT(assoc),
38868810c16bSdanmcd 	    SA_PROTO(assoc), 0);
38877c478bd9Sstevel@tonic-gate 	ASSERT(mp->b_wptr != NULL);
38887c478bd9Sstevel@tonic-gate 
38897c478bd9Sstevel@tonic-gate 	mp->b_wptr = sadb_make_addr_ext(mp->b_wptr, end, SADB_EXT_ADDRESS_DST,
38908810c16bSdanmcd 	    af, assoc->ipsa_dstaddr, tunnel_mode ? 0 : SA_DSTPORT(assoc),
38918810c16bSdanmcd 	    SA_PROTO(assoc), 0);
38927c478bd9Sstevel@tonic-gate 	ASSERT(mp->b_wptr != NULL);
38937c478bd9Sstevel@tonic-gate 
38948810c16bSdanmcd 	if (tunnel_mode) {
38958810c16bSdanmcd 		mp->b_wptr = sadb_make_addr_ext(mp->b_wptr, end,
38968810c16bSdanmcd 		    SADB_X_EXT_ADDRESS_INNER_SRC, assoc->ipsa_innerfam,
38978810c16bSdanmcd 		    assoc->ipsa_innersrc, SA_SRCPORT(assoc), SA_IPROTO(assoc),
38988810c16bSdanmcd 		    assoc->ipsa_innersrcpfx);
38998810c16bSdanmcd 		ASSERT(mp->b_wptr != NULL);
39008810c16bSdanmcd 		mp->b_wptr = sadb_make_addr_ext(mp->b_wptr, end,
39018810c16bSdanmcd 		    SADB_X_EXT_ADDRESS_INNER_DST, assoc->ipsa_innerfam,
39028810c16bSdanmcd 		    assoc->ipsa_innerdst, SA_DSTPORT(assoc), SA_IPROTO(assoc),
39038810c16bSdanmcd 		    assoc->ipsa_innerdstpfx);
39048810c16bSdanmcd 		ASSERT(mp->b_wptr != NULL);
39058810c16bSdanmcd 	}
39068810c16bSdanmcd 
3907*cd434274SJason King 	mp->b_wptr = sadb_make_kmc_ext(mp->b_wptr, end, assoc->ipsa_kmp,
3908*cd434274SJason King 	    assoc->ipsa_kmc);
3909*cd434274SJason King 	ASSERT(mp->b_wptr != NULL);
3910*cd434274SJason King 
39117c478bd9Sstevel@tonic-gate 	/* Can just putnext, we're ready to go! */
39127c478bd9Sstevel@tonic-gate 	putnext(pfkey_q, mp1);
39137c478bd9Sstevel@tonic-gate }
39147c478bd9Sstevel@tonic-gate 
39157c478bd9Sstevel@tonic-gate /*
39167c478bd9Sstevel@tonic-gate  * "Age" the SA with the number of bytes that was used to protect traffic.
39177c478bd9Sstevel@tonic-gate  * Send an SADB_EXPIRE message if appropriate.	Return B_TRUE if there was
39187c478bd9Sstevel@tonic-gate  * enough "charge" left in the SA to protect the data.	Return B_FALSE
39197c478bd9Sstevel@tonic-gate  * otherwise.  (If B_FALSE is returned, the association either was, or became
39207c478bd9Sstevel@tonic-gate  * DEAD.)
39217c478bd9Sstevel@tonic-gate  */
39227c478bd9Sstevel@tonic-gate boolean_t
39237c478bd9Sstevel@tonic-gate sadb_age_bytes(queue_t *pfkey_q, ipsa_t *assoc, uint64_t bytes,
39247c478bd9Sstevel@tonic-gate     boolean_t sendmsg)
39257c478bd9Sstevel@tonic-gate {
39267c478bd9Sstevel@tonic-gate 	boolean_t rc = B_TRUE;
39277c478bd9Sstevel@tonic-gate 	uint64_t newtotal;
39287c478bd9Sstevel@tonic-gate 
39297c478bd9Sstevel@tonic-gate 	mutex_enter(&assoc->ipsa_lock);
39307c478bd9Sstevel@tonic-gate 	newtotal = assoc->ipsa_bytes + bytes;
39317c478bd9Sstevel@tonic-gate 	if (assoc->ipsa_hardbyteslt != 0 &&
39327c478bd9Sstevel@tonic-gate 	    newtotal >= assoc->ipsa_hardbyteslt) {
39339c2c14abSThejaswini Singarajipura 		if (assoc->ipsa_state != IPSA_STATE_DEAD) {
39349c2c14abSThejaswini Singarajipura 			sadb_delete_cluster(assoc);
39357c478bd9Sstevel@tonic-gate 			/*
39367c478bd9Sstevel@tonic-gate 			 * Send EXPIRE message to PF_KEY.  May wish to pawn
39377c478bd9Sstevel@tonic-gate 			 * this off on another non-interrupt thread.  Also
39387c478bd9Sstevel@tonic-gate 			 * unlink this SA immediately.
39397c478bd9Sstevel@tonic-gate 			 */
39407c478bd9Sstevel@tonic-gate 			assoc->ipsa_state = IPSA_STATE_DEAD;
39417c478bd9Sstevel@tonic-gate 			if (sendmsg)
39427c478bd9Sstevel@tonic-gate 				sadb_expire_assoc(pfkey_q, assoc);
39437c478bd9Sstevel@tonic-gate 			/*
39447c478bd9Sstevel@tonic-gate 			 * Set non-zero expiration time so sadb_age_assoc()
39457c478bd9Sstevel@tonic-gate 			 * will work when reaping.
39467c478bd9Sstevel@tonic-gate 			 */
39477c478bd9Sstevel@tonic-gate 			assoc->ipsa_hardexpiretime = (time_t)1;
39487c478bd9Sstevel@tonic-gate 		} /* Else someone beat me to it! */
39497c478bd9Sstevel@tonic-gate 		rc = B_FALSE;
39507c478bd9Sstevel@tonic-gate 	} else if (assoc->ipsa_softbyteslt != 0 &&
39517c478bd9Sstevel@tonic-gate 	    (newtotal >= assoc->ipsa_softbyteslt)) {
39527c478bd9Sstevel@tonic-gate 		if (assoc->ipsa_state < IPSA_STATE_DYING) {
39537c478bd9Sstevel@tonic-gate 			/*
39547c478bd9Sstevel@tonic-gate 			 * Send EXPIRE message to PF_KEY.  May wish to pawn
39557c478bd9Sstevel@tonic-gate 			 * this off on another non-interrupt thread.
39567c478bd9Sstevel@tonic-gate 			 */
39577c478bd9Sstevel@tonic-gate 			assoc->ipsa_state = IPSA_STATE_DYING;
395850283218Smarkfen 			assoc->ipsa_bytes = newtotal;
39597c478bd9Sstevel@tonic-gate 			if (sendmsg)
39607c478bd9Sstevel@tonic-gate 				sadb_expire_assoc(pfkey_q, assoc);
39617c478bd9Sstevel@tonic-gate 		} /* Else someone beat me to it! */
39627c478bd9Sstevel@tonic-gate 	}
39637c478bd9Sstevel@tonic-gate 	if (rc == B_TRUE)
39647c478bd9Sstevel@tonic-gate 		assoc->ipsa_bytes = newtotal;
39657c478bd9Sstevel@tonic-gate 	mutex_exit(&assoc->ipsa_lock);
39667c478bd9Sstevel@tonic-gate 	return (rc);
39677c478bd9Sstevel@tonic-gate }
39687c478bd9Sstevel@tonic-gate 
39697c478bd9Sstevel@tonic-gate /*
39707c478bd9Sstevel@tonic-gate  * "Torch" an individual SA.  Returns NULL, so it can be tail-called from
39717c478bd9Sstevel@tonic-gate  *     sadb_age_assoc().
39727c478bd9Sstevel@tonic-gate  */
39737c478bd9Sstevel@tonic-gate static ipsa_t *
3974bd670b35SErik Nordmark sadb_torch_assoc(isaf_t *head, ipsa_t *sa)
39757c478bd9Sstevel@tonic-gate {
39767c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&head->isaf_lock));
39777c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&sa->ipsa_lock));
39787c478bd9Sstevel@tonic-gate 	ASSERT(sa->ipsa_state == IPSA_STATE_DEAD);
39797c478bd9Sstevel@tonic-gate 
39807c478bd9Sstevel@tonic-gate 	/*
39817c478bd9Sstevel@tonic-gate 	 * Force cached SAs to be revalidated..
39827c478bd9Sstevel@tonic-gate 	 */
39837c478bd9Sstevel@tonic-gate 	head->isaf_gen++;
39847c478bd9Sstevel@tonic-gate 
39857c478bd9Sstevel@tonic-gate 	mutex_exit(&sa->ipsa_lock);
39867c478bd9Sstevel@tonic-gate 	sadb_unlinkassoc(sa);
39877c478bd9Sstevel@tonic-gate 
39887c478bd9Sstevel@tonic-gate 	return (NULL);
39897c478bd9Sstevel@tonic-gate }
39907c478bd9Sstevel@tonic-gate 
3991437220cdSdanmcd /*
3992437220cdSdanmcd  * Do various SA-is-idle activities depending on delta (the number of idle
3993437220cdSdanmcd  * seconds on the SA) and/or other properties of the SA.
3994437220cdSdanmcd  *
3995437220cdSdanmcd  * Return B_TRUE if I've sent a packet, because I have to drop the
3996437220cdSdanmcd  * association's mutex before sending a packet out the wire.
3997437220cdSdanmcd  */
3998437220cdSdanmcd /* ARGSUSED */
3999437220cdSdanmcd static boolean_t
4000437220cdSdanmcd sadb_idle_activities(ipsa_t *assoc, time_t delta, boolean_t inbound)
4001437220cdSdanmcd {
4002437220cdSdanmcd 	ipsecesp_stack_t *espstack = assoc->ipsa_netstack->netstack_ipsecesp;
4003437220cdSdanmcd 	int nat_t_interval = espstack->ipsecesp_nat_keepalive_interval;
4004437220cdSdanmcd 
4005437220cdSdanmcd 	ASSERT(MUTEX_HELD(&assoc->ipsa_lock));
4006437220cdSdanmcd 
4007437220cdSdanmcd 	if (!inbound && (assoc->ipsa_flags & IPSA_F_NATT_LOC) &&
4008437220cdSdanmcd 	    delta >= nat_t_interval &&
4009437220cdSdanmcd 	    gethrestime_sec() - assoc->ipsa_last_nat_t_ka >= nat_t_interval) {
4010437220cdSdanmcd 		ASSERT(assoc->ipsa_type == SADB_SATYPE_ESP);
4011437220cdSdanmcd 		assoc->ipsa_last_nat_t_ka = gethrestime_sec();
4012437220cdSdanmcd 		mutex_exit(&assoc->ipsa_lock);
4013437220cdSdanmcd 		ipsecesp_send_keepalive(assoc);
4014437220cdSdanmcd 		return (B_TRUE);
4015437220cdSdanmcd 	}
4016437220cdSdanmcd 	return (B_FALSE);
4017437220cdSdanmcd }
4018437220cdSdanmcd 
40197c478bd9Sstevel@tonic-gate /*
402038d95a78Smarkfen  * Return "assoc" if haspeer is true and I send an expire.  This allows
40217c478bd9Sstevel@tonic-gate  * the consumers' aging functions to tidy up an expired SA's peer.
40227c478bd9Sstevel@tonic-gate  */
40237c478bd9Sstevel@tonic-gate static ipsa_t *
40247c478bd9Sstevel@tonic-gate sadb_age_assoc(isaf_t *head, queue_t *pfkey_q, ipsa_t *assoc,
4025bd670b35SErik Nordmark     time_t current, int reap_delay, boolean_t inbound)
40267c478bd9Sstevel@tonic-gate {
40277c478bd9Sstevel@tonic-gate 	ipsa_t *retval = NULL;
4028437220cdSdanmcd 	boolean_t dropped_mutex = B_FALSE;
40297c478bd9Sstevel@tonic-gate 
40307c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&head->isaf_lock));
40317c478bd9Sstevel@tonic-gate 
40327c478bd9Sstevel@tonic-gate 	mutex_enter(&assoc->ipsa_lock);
40337c478bd9Sstevel@tonic-gate 
40349c2c14abSThejaswini Singarajipura 	if (((assoc->ipsa_state == IPSA_STATE_LARVAL) ||
40359c2c14abSThejaswini Singarajipura 	    ((assoc->ipsa_state == IPSA_STATE_IDLE) ||
40369c2c14abSThejaswini Singarajipura 	    (assoc->ipsa_state == IPSA_STATE_ACTIVE_ELSEWHERE) &&
40379c2c14abSThejaswini Singarajipura 	    (assoc->ipsa_hardexpiretime != 0))) &&
40387c478bd9Sstevel@tonic-gate 	    (assoc->ipsa_hardexpiretime <= current)) {
40397c478bd9Sstevel@tonic-gate 		assoc->ipsa_state = IPSA_STATE_DEAD;
4040bd670b35SErik Nordmark 		return (sadb_torch_assoc(head, assoc));
40417c478bd9Sstevel@tonic-gate 	}
40427c478bd9Sstevel@tonic-gate 
40437c478bd9Sstevel@tonic-gate 	/*
40447c478bd9Sstevel@tonic-gate 	 * Check lifetimes.  Fortunately, SA setup is done
40457c478bd9Sstevel@tonic-gate 	 * such that there are only two times to look at,
40467c478bd9Sstevel@tonic-gate 	 * softexpiretime, and hardexpiretime.
40477c478bd9Sstevel@tonic-gate 	 *
40487c478bd9Sstevel@tonic-gate 	 * Check hard first.
40497c478bd9Sstevel@tonic-gate 	 */
40507c478bd9Sstevel@tonic-gate 
40517c478bd9Sstevel@tonic-gate 	if (assoc->ipsa_hardexpiretime != 0 &&
40527c478bd9Sstevel@tonic-gate 	    assoc->ipsa_hardexpiretime <= current) {
40537c478bd9Sstevel@tonic-gate 		if (assoc->ipsa_state == IPSA_STATE_DEAD)
4054bd670b35SErik Nordmark 			return (sadb_torch_assoc(head, assoc));
40557c478bd9Sstevel@tonic-gate 
40569c2c14abSThejaswini Singarajipura 		if (inbound) {
40579c2c14abSThejaswini Singarajipura 			sadb_delete_cluster(assoc);
40589c2c14abSThejaswini Singarajipura 		}
40599c2c14abSThejaswini Singarajipura 
40607c478bd9Sstevel@tonic-gate 		/*
40617c478bd9Sstevel@tonic-gate 		 * Send SADB_EXPIRE with hard lifetime, delay for unlinking.
40627c478bd9Sstevel@tonic-gate 		 */
40637c478bd9Sstevel@tonic-gate 		assoc->ipsa_state = IPSA_STATE_DEAD;
406438d95a78Smarkfen 		if (assoc->ipsa_haspeer || assoc->ipsa_otherspi != 0) {
40657c478bd9Sstevel@tonic-gate 			/*
406638d95a78Smarkfen 			 * If the SA is paired or peered with another, put
406738d95a78Smarkfen 			 * a copy on a list which can be processed later, the
406838d95a78Smarkfen 			 * pair/peer SA needs to be updated so the both die
406938d95a78Smarkfen 			 * at the same time.
407038d95a78Smarkfen 			 *
407138d95a78Smarkfen 			 * If I return assoc, I have to bump up its reference
407238d95a78Smarkfen 			 * count to keep with the ipsa_t reference count
407338d95a78Smarkfen 			 * semantics.
40747c478bd9Sstevel@tonic-gate 			 */
40757c478bd9Sstevel@tonic-gate 			IPSA_REFHOLD(assoc);
40767c478bd9Sstevel@tonic-gate 			retval = assoc;
40777c478bd9Sstevel@tonic-gate 		}
40787c478bd9Sstevel@tonic-gate 		sadb_expire_assoc(pfkey_q, assoc);
40797c478bd9Sstevel@tonic-gate 		assoc->ipsa_hardexpiretime = current + reap_delay;
40807c478bd9Sstevel@tonic-gate 	} else if (assoc->ipsa_softexpiretime != 0 &&
40817c478bd9Sstevel@tonic-gate 	    assoc->ipsa_softexpiretime <= current &&
40827c478bd9Sstevel@tonic-gate 	    assoc->ipsa_state < IPSA_STATE_DYING) {
40837c478bd9Sstevel@tonic-gate 		/*
40847c478bd9Sstevel@tonic-gate 		 * Send EXPIRE message to PF_KEY.  May wish to pawn
40857c478bd9Sstevel@tonic-gate 		 * this off on another non-interrupt thread.
40867c478bd9Sstevel@tonic-gate 		 */
40877c478bd9Sstevel@tonic-gate 		assoc->ipsa_state = IPSA_STATE_DYING;
40887c478bd9Sstevel@tonic-gate 		if (assoc->ipsa_haspeer) {
40897c478bd9Sstevel@tonic-gate 			/*
409038d95a78Smarkfen 			 * If the SA has a peer, update the peer's state
409138d95a78Smarkfen 			 * on SOFT_EXPIRE, this is mostly to prevent two
409238d95a78Smarkfen 			 * expire messages from effectively the same SA.
409338d95a78Smarkfen 			 *
409438d95a78Smarkfen 			 * Don't care about paired SA's, then can (and should)
409538d95a78Smarkfen 			 * be able to soft expire at different times.
409638d95a78Smarkfen 			 *
40977c478bd9Sstevel@tonic-gate 			 * If I return assoc, I have to bump up its
40987c478bd9Sstevel@tonic-gate 			 * reference count to keep with the ipsa_t reference
40997c478bd9Sstevel@tonic-gate 			 * count semantics.
41007c478bd9Sstevel@tonic-gate 			 */
41017c478bd9Sstevel@tonic-gate 			IPSA_REFHOLD(assoc);
41027c478bd9Sstevel@tonic-gate 			retval = assoc;
41037c478bd9Sstevel@tonic-gate 		}
41047c478bd9Sstevel@tonic-gate 		sadb_expire_assoc(pfkey_q, assoc);
41059c2c14abSThejaswini Singarajipura 	} else if (assoc->ipsa_idletime != 0 &&
41069c2c14abSThejaswini Singarajipura 	    assoc->ipsa_idleexpiretime <= current) {
41079c2c14abSThejaswini Singarajipura 		if (assoc->ipsa_state == IPSA_STATE_ACTIVE_ELSEWHERE) {
41089c2c14abSThejaswini Singarajipura 			assoc->ipsa_state = IPSA_STATE_IDLE;
41099c2c14abSThejaswini Singarajipura 		}
41109c2c14abSThejaswini Singarajipura 
41119c2c14abSThejaswini Singarajipura 		/*
41129c2c14abSThejaswini Singarajipura 		 * Need to handle Mature case
41139c2c14abSThejaswini Singarajipura 		 */
4114a14de6c8SDan McDonald 		if (assoc->ipsa_state == IPSA_STATE_MATURE) {
41159c2c14abSThejaswini Singarajipura 			sadb_expire_assoc(pfkey_q, assoc);
41169c2c14abSThejaswini Singarajipura 		}
4117437220cdSdanmcd 	} else {
4118437220cdSdanmcd 		/* Check idle time activities. */
4119437220cdSdanmcd 		dropped_mutex = sadb_idle_activities(assoc,
4120437220cdSdanmcd 		    current - assoc->ipsa_lastuse, inbound);
41217c478bd9Sstevel@tonic-gate 	}
41227c478bd9Sstevel@tonic-gate 
4123437220cdSdanmcd 	if (!dropped_mutex)
4124437220cdSdanmcd 		mutex_exit(&assoc->ipsa_lock);
41257c478bd9Sstevel@tonic-gate 	return (retval);
41267c478bd9Sstevel@tonic-gate }
41277c478bd9Sstevel@tonic-gate 
41287c478bd9Sstevel@tonic-gate /*
41297c478bd9Sstevel@tonic-gate  * Called by a consumer protocol to do ther dirty work of reaping dead
41307c478bd9Sstevel@tonic-gate  * Security Associations.
413138d95a78Smarkfen  *
413238d95a78Smarkfen  * NOTE: sadb_age_assoc() marks expired SA's as DEAD but only removed
413338d95a78Smarkfen  * SA's that are already marked DEAD, so expired SA's are only reaped
413438d95a78Smarkfen  * the second time sadb_ager() runs.
41357c478bd9Sstevel@tonic-gate  */
41367c478bd9Sstevel@tonic-gate void
4137bd670b35SErik Nordmark sadb_ager(sadb_t *sp, queue_t *pfkey_q, int reap_delay, netstack_t *ns)
41387c478bd9Sstevel@tonic-gate {
41397c478bd9Sstevel@tonic-gate 	int i;
41407c478bd9Sstevel@tonic-gate 	isaf_t *bucket;
41417c478bd9Sstevel@tonic-gate 	ipsa_t *assoc, *spare;
41427c478bd9Sstevel@tonic-gate 	iacqf_t *acqlist;
41437c478bd9Sstevel@tonic-gate 	ipsacq_t *acqrec, *spareacq;
414438d95a78Smarkfen 	templist_t *haspeerlist, *newbie;
4145437220cdSdanmcd 	/* Snapshot current time now. */
4146437220cdSdanmcd 	time_t current = gethrestime_sec();
414738d95a78Smarkfen 	haspeerlist = NULL;
41487c478bd9Sstevel@tonic-gate 
41497c478bd9Sstevel@tonic-gate 	/*
41507c478bd9Sstevel@tonic-gate 	 * Do my dirty work.  This includes aging real entries, aging
41517c478bd9Sstevel@tonic-gate 	 * larvals, and aging outstanding ACQUIREs.
41527c478bd9Sstevel@tonic-gate 	 *
41537c478bd9Sstevel@tonic-gate 	 * I hope I don't tie up resources for too long.
41547c478bd9Sstevel@tonic-gate 	 */
41557c478bd9Sstevel@tonic-gate 
41567c478bd9Sstevel@tonic-gate 	/* Age acquires. */
41577c478bd9Sstevel@tonic-gate 
4158fb87b5d2Ssommerfe 	for (i = 0; i < sp->sdb_hashsize; i++) {
41597c478bd9Sstevel@tonic-gate 		acqlist = &sp->sdb_acq[i];
41607c478bd9Sstevel@tonic-gate 		mutex_enter(&acqlist->iacqf_lock);
41617c478bd9Sstevel@tonic-gate 		for (acqrec = acqlist->iacqf_ipsacq; acqrec != NULL;
41627c478bd9Sstevel@tonic-gate 		    acqrec = spareacq) {
41637c478bd9Sstevel@tonic-gate 			spareacq = acqrec->ipsacq_next;
41647c478bd9Sstevel@tonic-gate 			if (current > acqrec->ipsacq_expire)
4165f4b3ec61Sdh 				sadb_destroy_acquire(acqrec, ns);
41667c478bd9Sstevel@tonic-gate 		}
41677c478bd9Sstevel@tonic-gate 		mutex_exit(&acqlist->iacqf_lock);
41687c478bd9Sstevel@tonic-gate 	}
41697c478bd9Sstevel@tonic-gate 
41707c478bd9Sstevel@tonic-gate 	/* Age inbound associations. */
4171fb87b5d2Ssommerfe 	for (i = 0; i < sp->sdb_hashsize; i++) {
41727c478bd9Sstevel@tonic-gate 		bucket = &(sp->sdb_if[i]);
41737c478bd9Sstevel@tonic-gate 		mutex_enter(&bucket->isaf_lock);
41747c478bd9Sstevel@tonic-gate 		for (assoc = bucket->isaf_ipsa; assoc != NULL;
41757c478bd9Sstevel@tonic-gate 		    assoc = spare) {
41767c478bd9Sstevel@tonic-gate 			spare = assoc->ipsa_next;
41777c478bd9Sstevel@tonic-gate 			if (sadb_age_assoc(bucket, pfkey_q, assoc, current,
4178bd670b35SErik Nordmark 			    reap_delay, B_TRUE) != NULL) {
41797c478bd9Sstevel@tonic-gate 				/*
418038d95a78Smarkfen 				 * Put SA's which have a peer or SA's which
418138d95a78Smarkfen 				 * are paired on a list for processing after
418238d95a78Smarkfen 				 * all the hash tables have been walked.
418338d95a78Smarkfen 				 *
41847c478bd9Sstevel@tonic-gate 				 * sadb_age_assoc() increments the refcnt,
41857c478bd9Sstevel@tonic-gate 				 * effectively doing an IPSA_REFHOLD().
41867c478bd9Sstevel@tonic-gate 				 */
41877c478bd9Sstevel@tonic-gate 				newbie = kmem_alloc(sizeof (*newbie),
41887c478bd9Sstevel@tonic-gate 				    KM_NOSLEEP);
41897c478bd9Sstevel@tonic-gate 				if (newbie == NULL) {
41907c478bd9Sstevel@tonic-gate 					/*
41917c478bd9Sstevel@tonic-gate 					 * Don't forget to REFRELE().
41927c478bd9Sstevel@tonic-gate 					 */
41937c478bd9Sstevel@tonic-gate 					IPSA_REFRELE(assoc);
41947c478bd9Sstevel@tonic-gate 					continue;	/* for loop... */
41957c478bd9Sstevel@tonic-gate 				}
41967c478bd9Sstevel@tonic-gate 				newbie->next = haspeerlist;
41977c478bd9Sstevel@tonic-gate 				newbie->ipsa = assoc;
41987c478bd9Sstevel@tonic-gate 				haspeerlist = newbie;
41997c478bd9Sstevel@tonic-gate 			}
42007c478bd9Sstevel@tonic-gate 		}
42017c478bd9Sstevel@tonic-gate 		mutex_exit(&bucket->isaf_lock);
42027c478bd9Sstevel@tonic-gate 	}
42037c478bd9Sstevel@tonic-gate 
420438d95a78Smarkfen 	age_pair_peer_list(haspeerlist, sp, B_FALSE);
420538d95a78Smarkfen 	haspeerlist = NULL;
42067c478bd9Sstevel@tonic-gate 
42077c478bd9Sstevel@tonic-gate 	/* Age outbound associations. */
4208fb87b5d2Ssommerfe 	for (i = 0; i < sp->sdb_hashsize; i++) {
42097c478bd9Sstevel@tonic-gate 		bucket = &(sp->sdb_of[i]);
42107c478bd9Sstevel@tonic-gate 		mutex_enter(&bucket->isaf_lock);
42117c478bd9Sstevel@tonic-gate 		for (assoc = bucket->isaf_ipsa; assoc != NULL;
42127c478bd9Sstevel@tonic-gate 		    assoc = spare) {
42137c478bd9Sstevel@tonic-gate 			spare = assoc->ipsa_next;
42147c478bd9Sstevel@tonic-gate 			if (sadb_age_assoc(bucket, pfkey_q, assoc, current,
4215bd670b35SErik Nordmark 			    reap_delay, B_FALSE) != NULL) {
42167c478bd9Sstevel@tonic-gate 				/*
42177c478bd9Sstevel@tonic-gate 				 * sadb_age_assoc() increments the refcnt,
42187c478bd9Sstevel@tonic-gate 				 * effectively doing an IPSA_REFHOLD().
42197c478bd9Sstevel@tonic-gate 				 */
42207c478bd9Sstevel@tonic-gate 				newbie = kmem_alloc(sizeof (*newbie),
42217c478bd9Sstevel@tonic-gate 				    KM_NOSLEEP);
42227c478bd9Sstevel@tonic-gate 				if (newbie == NULL) {
42237c478bd9Sstevel@tonic-gate 					/*
42247c478bd9Sstevel@tonic-gate 					 * Don't forget to REFRELE().
42257c478bd9Sstevel@tonic-gate 					 */
42267c478bd9Sstevel@tonic-gate 					IPSA_REFRELE(assoc);
42277c478bd9Sstevel@tonic-gate 					continue;	/* for loop... */
42287c478bd9Sstevel@tonic-gate 				}
42297c478bd9Sstevel@tonic-gate 				newbie->next = haspeerlist;
42307c478bd9Sstevel@tonic-gate 				newbie->ipsa = assoc;
42317c478bd9Sstevel@tonic-gate 				haspeerlist = newbie;
42327c478bd9Sstevel@tonic-gate 			}
42337c478bd9Sstevel@tonic-gate 		}
42347c478bd9Sstevel@tonic-gate 		mutex_exit(&bucket->isaf_lock);
42357c478bd9Sstevel@tonic-gate 	}
423638d95a78Smarkfen 
423738d95a78Smarkfen 	age_pair_peer_list(haspeerlist, sp, B_TRUE);
423838d95a78Smarkfen 
42397c478bd9Sstevel@tonic-gate 	/*
42407c478bd9Sstevel@tonic-gate 	 * Run a GC pass to clean out dead identities.
42417c478bd9Sstevel@tonic-gate 	 */
4242f4b3ec61Sdh 	ipsid_gc(ns);
42437c478bd9Sstevel@tonic-gate }
42447c478bd9Sstevel@tonic-gate 
42457c478bd9Sstevel@tonic-gate /*
42467c478bd9Sstevel@tonic-gate  * Figure out when to reschedule the ager.
42477c478bd9Sstevel@tonic-gate  */
42487c478bd9Sstevel@tonic-gate timeout_id_t
42497c478bd9Sstevel@tonic-gate sadb_retimeout(hrtime_t begin, queue_t *pfkey_q, void (*ager)(void *),
4250f4b3ec61Sdh     void *agerarg, uint_t *intp, uint_t intmax, short mid)
42517c478bd9Sstevel@tonic-gate {
42527c478bd9Sstevel@tonic-gate 	hrtime_t end = gethrtime();
4253cf0ee00aSDan McDonald 	uint_t interval = *intp;	/* "interval" is in ms. */
42547c478bd9Sstevel@tonic-gate 
42557c478bd9Sstevel@tonic-gate 	/*
42567c478bd9Sstevel@tonic-gate 	 * See how long this took.  If it took too long, increase the
42577c478bd9Sstevel@tonic-gate 	 * aging interval.
42587c478bd9Sstevel@tonic-gate 	 */
4259cf0ee00aSDan McDonald 	if ((end - begin) > MSEC2NSEC(interval)) {
42607c478bd9Sstevel@tonic-gate 		if (interval >= intmax) {
42617c478bd9Sstevel@tonic-gate 			/* XXX Rate limit this?  Or recommend flush? */
42627c478bd9Sstevel@tonic-gate 			(void) strlog(mid, 0, 0, SL_ERROR | SL_WARN,
42637c478bd9Sstevel@tonic-gate 			    "Too many SA's to age out in %d msec.\n",
42647c478bd9Sstevel@tonic-gate 			    intmax);
42657c478bd9Sstevel@tonic-gate 		} else {
42667c478bd9Sstevel@tonic-gate 			/* Double by shifting by one bit. */
42677c478bd9Sstevel@tonic-gate 			interval <<= 1;
42687c478bd9Sstevel@tonic-gate 			interval = min(interval, intmax);
42697c478bd9Sstevel@tonic-gate 		}
4270cf0ee00aSDan McDonald 	} else if ((end - begin) <= (MSEC2NSEC(interval) / 2) &&
4271437220cdSdanmcd 	    interval > SADB_AGE_INTERVAL_DEFAULT) {
42727c478bd9Sstevel@tonic-gate 		/*
42737c478bd9Sstevel@tonic-gate 		 * If I took less than half of the interval, then I should
42747c478bd9Sstevel@tonic-gate 		 * ratchet the interval back down.  Never automatically
42757c478bd9Sstevel@tonic-gate 		 * shift below the default aging interval.
42767c478bd9Sstevel@tonic-gate 		 *
42777c478bd9Sstevel@tonic-gate 		 * NOTE:This even overrides manual setting of the age
42780e9b5742SDan McDonald 		 *	interval using NDD to lower the setting past the
42790e9b5742SDan McDonald 		 *	default.  In other words, if you set the interval
42800e9b5742SDan McDonald 		 *	lower than the default, and your SADB gets too big,
42810e9b5742SDan McDonald 		 *	the interval will only self-lower back to the default.
42827c478bd9Sstevel@tonic-gate 		 */
42837c478bd9Sstevel@tonic-gate 		/* Halve by shifting one bit. */
42847c478bd9Sstevel@tonic-gate 		interval >>= 1;
42857c478bd9Sstevel@tonic-gate 		interval = max(interval, SADB_AGE_INTERVAL_DEFAULT);
42867c478bd9Sstevel@tonic-gate 	}
42877c478bd9Sstevel@tonic-gate 	*intp = interval;
4288f4b3ec61Sdh 	return (qtimeout(pfkey_q, ager, agerarg,
4289cf0ee00aSDan McDonald 	    drv_usectohz(interval * (MICROSEC / MILLISEC))));
42907c478bd9Sstevel@tonic-gate }
42917c478bd9Sstevel@tonic-gate 
42927c478bd9Sstevel@tonic-gate 
42937c478bd9Sstevel@tonic-gate /*
42947c478bd9Sstevel@tonic-gate  * Update the lifetime values of an SA.	 This is the path an SADB_UPDATE
42957c478bd9Sstevel@tonic-gate  * message takes when updating a MATURE or DYING SA.
42967c478bd9Sstevel@tonic-gate  */
42977c478bd9Sstevel@tonic-gate static void
42987c478bd9Sstevel@tonic-gate sadb_update_lifetimes(ipsa_t *assoc, sadb_lifetime_t *hard,
42999c2c14abSThejaswini Singarajipura     sadb_lifetime_t *soft, sadb_lifetime_t *idle, boolean_t outbound)
43007c478bd9Sstevel@tonic-gate {
43017c478bd9Sstevel@tonic-gate 	mutex_enter(&assoc->ipsa_lock);
43027c478bd9Sstevel@tonic-gate 
43037c478bd9Sstevel@tonic-gate 	/*
43047c478bd9Sstevel@tonic-gate 	 * XXX RFC 2367 mentions how an SADB_EXT_LIFETIME_CURRENT can be
43057c478bd9Sstevel@tonic-gate 	 * passed in during an update message.	We currently don't handle
43067c478bd9Sstevel@tonic-gate 	 * these.
43077c478bd9Sstevel@tonic-gate 	 */
43087c478bd9Sstevel@tonic-gate 
43097c478bd9Sstevel@tonic-gate 	if (hard != NULL) {
43107c478bd9Sstevel@tonic-gate 		if (hard->sadb_lifetime_bytes != 0)
43117c478bd9Sstevel@tonic-gate 			assoc->ipsa_hardbyteslt = hard->sadb_lifetime_bytes;
43127c478bd9Sstevel@tonic-gate 		if (hard->sadb_lifetime_usetime != 0)
43137c478bd9Sstevel@tonic-gate 			assoc->ipsa_harduselt = hard->sadb_lifetime_usetime;
43147c478bd9Sstevel@tonic-gate 		if (hard->sadb_lifetime_addtime != 0)
43157c478bd9Sstevel@tonic-gate 			assoc->ipsa_hardaddlt = hard->sadb_lifetime_addtime;
43167c478bd9Sstevel@tonic-gate 		if (assoc->ipsa_hardaddlt != 0) {
43177c478bd9Sstevel@tonic-gate 			assoc->ipsa_hardexpiretime =
43187c478bd9Sstevel@tonic-gate 			    assoc->ipsa_addtime + assoc->ipsa_hardaddlt;
43197c478bd9Sstevel@tonic-gate 		}
432038d95a78Smarkfen 		if (assoc->ipsa_harduselt != 0 &&
432138d95a78Smarkfen 		    assoc->ipsa_flags & IPSA_F_USED) {
432238d95a78Smarkfen 			UPDATE_EXPIRE(assoc, harduselt, hardexpiretime);
43237c478bd9Sstevel@tonic-gate 		}
43247c478bd9Sstevel@tonic-gate 		if (hard->sadb_lifetime_allocations != 0)
43257c478bd9Sstevel@tonic-gate 			assoc->ipsa_hardalloc = hard->sadb_lifetime_allocations;
43267c478bd9Sstevel@tonic-gate 	}
43277c478bd9Sstevel@tonic-gate 
43287c478bd9Sstevel@tonic-gate 	if (soft != NULL) {
432938d95a78Smarkfen 		if (soft->sadb_lifetime_bytes != 0) {
433038d95a78Smarkfen 			if (soft->sadb_lifetime_bytes >
433138d95a78Smarkfen 			    assoc->ipsa_hardbyteslt) {
433238d95a78Smarkfen 				assoc->ipsa_softbyteslt =
433338d95a78Smarkfen 				    assoc->ipsa_hardbyteslt;
433438d95a78Smarkfen 			} else {
433538d95a78Smarkfen 				assoc->ipsa_softbyteslt =
433638d95a78Smarkfen 				    soft->sadb_lifetime_bytes;
433738d95a78Smarkfen 			}
43387c478bd9Sstevel@tonic-gate 		}
433938d95a78Smarkfen 		if (soft->sadb_lifetime_usetime != 0) {
434038d95a78Smarkfen 			if (soft->sadb_lifetime_usetime >
434138d95a78Smarkfen 			    assoc->ipsa_harduselt) {
434238d95a78Smarkfen 				assoc->ipsa_softuselt =
434338d95a78Smarkfen 				    assoc->ipsa_harduselt;
43447c478bd9Sstevel@tonic-gate 			} else {
434538d95a78Smarkfen 				assoc->ipsa_softuselt =
434638d95a78Smarkfen 				    soft->sadb_lifetime_usetime;
434738d95a78Smarkfen 			}
434838d95a78Smarkfen 		}
434938d95a78Smarkfen 		if (soft->sadb_lifetime_addtime != 0) {
435038d95a78Smarkfen 			if (soft->sadb_lifetime_addtime >
435138d95a78Smarkfen 			    assoc->ipsa_hardexpiretime) {
43527c478bd9Sstevel@tonic-gate 				assoc->ipsa_softexpiretime =
435338d95a78Smarkfen 				    assoc->ipsa_hardexpiretime;
435438d95a78Smarkfen 			} else {
435538d95a78Smarkfen 				assoc->ipsa_softaddlt =
435638d95a78Smarkfen 				    soft->sadb_lifetime_addtime;
43577c478bd9Sstevel@tonic-gate 			}
43587c478bd9Sstevel@tonic-gate 		}
435938d95a78Smarkfen 		if (assoc->ipsa_softaddlt != 0) {
436038d95a78Smarkfen 			assoc->ipsa_softexpiretime =
436138d95a78Smarkfen 			    assoc->ipsa_addtime + assoc->ipsa_softaddlt;
436238d95a78Smarkfen 		}
436338d95a78Smarkfen 		if (assoc->ipsa_softuselt != 0 &&
436438d95a78Smarkfen 		    assoc->ipsa_flags & IPSA_F_USED) {
436538d95a78Smarkfen 			UPDATE_EXPIRE(assoc, softuselt, softexpiretime);
436638d95a78Smarkfen 		}
436738d95a78Smarkfen 		if (outbound && assoc->ipsa_softexpiretime != 0) {
436838d95a78Smarkfen 			if (assoc->ipsa_state == IPSA_STATE_MATURE)
436938d95a78Smarkfen 				lifetime_fuzz(assoc);
437038d95a78Smarkfen 		}
43717c478bd9Sstevel@tonic-gate 
43727c478bd9Sstevel@tonic-gate 		if (soft->sadb_lifetime_allocations != 0)
43737c478bd9Sstevel@tonic-gate 			assoc->ipsa_softalloc = soft->sadb_lifetime_allocations;
43747c478bd9Sstevel@tonic-gate 	}
43759c2c14abSThejaswini Singarajipura 
43769c2c14abSThejaswini Singarajipura 	if (idle != NULL) {
43779c2c14abSThejaswini Singarajipura 		time_t current = gethrestime_sec();
43789c2c14abSThejaswini Singarajipura 		if ((assoc->ipsa_idleexpiretime <= current) &&
43799c2c14abSThejaswini Singarajipura 		    (assoc->ipsa_idleaddlt == idle->sadb_lifetime_addtime)) {
43809c2c14abSThejaswini Singarajipura 			assoc->ipsa_idleexpiretime =
43819c2c14abSThejaswini Singarajipura 			    current + assoc->ipsa_idleaddlt;
43829c2c14abSThejaswini Singarajipura 		}
43839c2c14abSThejaswini Singarajipura 		if (idle->sadb_lifetime_addtime != 0)
43849c2c14abSThejaswini Singarajipura 			assoc->ipsa_idleaddlt = idle->sadb_lifetime_addtime;
43859c2c14abSThejaswini Singarajipura 		if (idle->sadb_lifetime_usetime != 0)
43869c2c14abSThejaswini Singarajipura 			assoc->ipsa_idleuselt = idle->sadb_lifetime_usetime;
43879c2c14abSThejaswini Singarajipura 		if (assoc->ipsa_idleaddlt != 0) {
43889c2c14abSThejaswini Singarajipura 			assoc->ipsa_idleexpiretime =
43899c2c14abSThejaswini Singarajipura 			    current + idle->sadb_lifetime_addtime;
43909c2c14abSThejaswini Singarajipura 			assoc->ipsa_idletime = idle->sadb_lifetime_addtime;
43919c2c14abSThejaswini Singarajipura 		}
43929c2c14abSThejaswini Singarajipura 		if (assoc->ipsa_idleuselt != 0) {
43939c2c14abSThejaswini Singarajipura 			if (assoc->ipsa_idletime != 0) {
43949c2c14abSThejaswini Singarajipura 				assoc->ipsa_idletime = min(assoc->ipsa_idletime,
43959c2c14abSThejaswini Singarajipura 				    assoc->ipsa_idleuselt);
4396a23b3b1bSToomas Soome 				assoc->ipsa_idleexpiretime =
4397a23b3b1bSToomas Soome 				    current + assoc->ipsa_idletime;
43989c2c14abSThejaswini Singarajipura 			} else {
43999c2c14abSThejaswini Singarajipura 				assoc->ipsa_idleexpiretime =
44009c2c14abSThejaswini Singarajipura 				    current + assoc->ipsa_idleuselt;
44019c2c14abSThejaswini Singarajipura 				assoc->ipsa_idletime = assoc->ipsa_idleuselt;
44029c2c14abSThejaswini Singarajipura 			}
44039c2c14abSThejaswini Singarajipura 		}
44049c2c14abSThejaswini Singarajipura 	}
44057c478bd9Sstevel@tonic-gate 	mutex_exit(&assoc->ipsa_lock);
44067c478bd9Sstevel@tonic-gate }
44077c478bd9Sstevel@tonic-gate 
44089c2c14abSThejaswini Singarajipura static int
44099c2c14abSThejaswini Singarajipura sadb_update_state(ipsa_t *assoc, uint_t new_state, mblk_t **ipkt_lst)
44109c2c14abSThejaswini Singarajipura {
44119c2c14abSThejaswini Singarajipura 	int rcode = 0;
44129c2c14abSThejaswini Singarajipura 	time_t current = gethrestime_sec();
44139c2c14abSThejaswini Singarajipura 
44149c2c14abSThejaswini Singarajipura 	mutex_enter(&assoc->ipsa_lock);
44159c2c14abSThejaswini Singarajipura 
44169c2c14abSThejaswini Singarajipura 	switch (new_state) {
44179c2c14abSThejaswini Singarajipura 	case SADB_X_SASTATE_ACTIVE_ELSEWHERE:
44189c2c14abSThejaswini Singarajipura 		if (assoc->ipsa_state == SADB_X_SASTATE_IDLE) {
44199c2c14abSThejaswini Singarajipura 			assoc->ipsa_state = IPSA_STATE_ACTIVE_ELSEWHERE;
44209c2c14abSThejaswini Singarajipura 			assoc->ipsa_idleexpiretime =
44219c2c14abSThejaswini Singarajipura 			    current + assoc->ipsa_idletime;
44229c2c14abSThejaswini Singarajipura 		}
44239c2c14abSThejaswini Singarajipura 		break;
44249c2c14abSThejaswini Singarajipura 	case SADB_X_SASTATE_IDLE:
44259c2c14abSThejaswini Singarajipura 		if (assoc->ipsa_state == SADB_X_SASTATE_ACTIVE_ELSEWHERE) {
44269c2c14abSThejaswini Singarajipura 			assoc->ipsa_state = IPSA_STATE_IDLE;
44279c2c14abSThejaswini Singarajipura 			assoc->ipsa_idleexpiretime =
44289c2c14abSThejaswini Singarajipura 			    current + assoc->ipsa_idletime;
44299c2c14abSThejaswini Singarajipura 		} else {
44309c2c14abSThejaswini Singarajipura 			rcode = EINVAL;
44319c2c14abSThejaswini Singarajipura 		}
44329c2c14abSThejaswini Singarajipura 		break;
44339c2c14abSThejaswini Singarajipura 
44349c2c14abSThejaswini Singarajipura 	case SADB_X_SASTATE_ACTIVE:
44359c2c14abSThejaswini Singarajipura 		if (assoc->ipsa_state != SADB_X_SASTATE_IDLE) {
44369c2c14abSThejaswini Singarajipura 			rcode = EINVAL;
44379c2c14abSThejaswini Singarajipura 			break;
44389c2c14abSThejaswini Singarajipura 		}
44399c2c14abSThejaswini Singarajipura 		assoc->ipsa_state = IPSA_STATE_MATURE;
44409c2c14abSThejaswini Singarajipura 		assoc->ipsa_idleexpiretime = current + assoc->ipsa_idletime;
44419c2c14abSThejaswini Singarajipura 
44429c2c14abSThejaswini Singarajipura 		if (ipkt_lst == NULL) {
44439c2c14abSThejaswini Singarajipura 			break;
44449c2c14abSThejaswini Singarajipura 		}
44459c2c14abSThejaswini Singarajipura 
44469c2c14abSThejaswini Singarajipura 		if (assoc->ipsa_bpkt_head != NULL) {
44479c2c14abSThejaswini Singarajipura 			*ipkt_lst = assoc->ipsa_bpkt_head;
44489c2c14abSThejaswini Singarajipura 			assoc->ipsa_bpkt_head = assoc->ipsa_bpkt_tail = NULL;
44499c2c14abSThejaswini Singarajipura 			assoc->ipsa_mblkcnt = 0;
44509c2c14abSThejaswini Singarajipura 		} else {
44519c2c14abSThejaswini Singarajipura 			*ipkt_lst = NULL;
44529c2c14abSThejaswini Singarajipura 		}
44539c2c14abSThejaswini Singarajipura 		break;
44549c2c14abSThejaswini Singarajipura 	default:
44559c2c14abSThejaswini Singarajipura 		rcode = EINVAL;
44569c2c14abSThejaswini Singarajipura 		break;
44579c2c14abSThejaswini Singarajipura 	}
44589c2c14abSThejaswini Singarajipura 
44599c2c14abSThejaswini Singarajipura 	mutex_exit(&assoc->ipsa_lock);
44609c2c14abSThejaswini Singarajipura 	return (rcode);
44619c2c14abSThejaswini Singarajipura }
44629c2c14abSThejaswini Singarajipura 
44635d3b8cb7SBill Sommerfeld /*
44645d3b8cb7SBill Sommerfeld  * Check a proposed KMC update for sanity.
44655d3b8cb7SBill Sommerfeld  */
44665d3b8cb7SBill Sommerfeld static int
44675d3b8cb7SBill Sommerfeld sadb_check_kmc(ipsa_query_t *sq, ipsa_t *sa, int *diagnostic)
44685d3b8cb7SBill Sommerfeld {
44695d3b8cb7SBill Sommerfeld 	uint32_t kmp = sq->kmp;
4470f4a6f97eSDan McDonald 	uint64_t kmc = sq->kmc;
44715d3b8cb7SBill Sommerfeld 
44725d3b8cb7SBill Sommerfeld 	if (sa == NULL)
44735d3b8cb7SBill Sommerfeld 		return (0);
44745d3b8cb7SBill Sommerfeld 
44755d3b8cb7SBill Sommerfeld 	if (sa->ipsa_state == IPSA_STATE_DEAD)
44765d3b8cb7SBill Sommerfeld 		return (ESRCH);	/* DEAD == Not there, in this case. */
44775d3b8cb7SBill Sommerfeld 
4478f4a6f97eSDan McDonald 	if ((kmp != 0) && (sa->ipsa_kmp != 0) && (sa->ipsa_kmp != kmp)) {
44795d3b8cb7SBill Sommerfeld 		*diagnostic = SADB_X_DIAGNOSTIC_DUPLICATE_KMP;
44805d3b8cb7SBill Sommerfeld 		return (EINVAL);
44815d3b8cb7SBill Sommerfeld 	}
44825d3b8cb7SBill Sommerfeld 
44834c5582efSJason King 	/* Allow IKEv2 KMCs to update the kmc value for rekeying */
44844c5582efSJason King 	if ((kmp != SADB_X_KMP_IKEV2) && (kmc != 0) && (sa->ipsa_kmc != 0) &&
44854c5582efSJason King 	    (sa->ipsa_kmc != kmc)) {
44865d3b8cb7SBill Sommerfeld 		*diagnostic = SADB_X_DIAGNOSTIC_DUPLICATE_KMC;
44875d3b8cb7SBill Sommerfeld 		return (EINVAL);
44885d3b8cb7SBill Sommerfeld 	}
44895d3b8cb7SBill Sommerfeld 
44905d3b8cb7SBill Sommerfeld 	return (0);
44915d3b8cb7SBill Sommerfeld }
44925d3b8cb7SBill Sommerfeld 
44935d3b8cb7SBill Sommerfeld /*
44945d3b8cb7SBill Sommerfeld  * Actually update the KMC info.
44955d3b8cb7SBill Sommerfeld  */
44965d3b8cb7SBill Sommerfeld static void
44975d3b8cb7SBill Sommerfeld sadb_update_kmc(ipsa_query_t *sq, ipsa_t *sa)
44985d3b8cb7SBill Sommerfeld {
44995d3b8cb7SBill Sommerfeld 	uint32_t kmp = sq->kmp;
4500f4a6f97eSDan McDonald 	uint64_t kmc = sq->kmc;
45015d3b8cb7SBill Sommerfeld 
45025d3b8cb7SBill Sommerfeld 	if (kmp != 0)
45035d3b8cb7SBill Sommerfeld 		sa->ipsa_kmp = kmp;
45045d3b8cb7SBill Sommerfeld 	if (kmc != 0)
45055d3b8cb7SBill Sommerfeld 		sa->ipsa_kmc = kmc;
45065d3b8cb7SBill Sommerfeld }
45075d3b8cb7SBill Sommerfeld 
45087c478bd9Sstevel@tonic-gate /*
45097c478bd9Sstevel@tonic-gate  * Common code to update an SA.
45107c478bd9Sstevel@tonic-gate  */
45117c478bd9Sstevel@tonic-gate 
45127c478bd9Sstevel@tonic-gate int
45139c2c14abSThejaswini Singarajipura sadb_update_sa(mblk_t *mp, keysock_in_t *ksi, mblk_t **ipkt_lst,
451438d95a78Smarkfen     sadbp_t *spp, int *diagnostic, queue_t *pfkey_q,
4515f4b3ec61Sdh     int (*add_sa_func)(mblk_t *, keysock_in_t *, int *, netstack_t *),
451638d95a78Smarkfen     netstack_t *ns, uint8_t sadb_msg_type)
45177c478bd9Sstevel@tonic-gate {
45187c478bd9Sstevel@tonic-gate 	sadb_key_t *akey = (sadb_key_t *)ksi->ks_in_extv[SADB_EXT_KEY_AUTH];
45197c478bd9Sstevel@tonic-gate 	sadb_key_t *ekey = (sadb_key_t *)ksi->ks_in_extv[SADB_EXT_KEY_ENCRYPT];
45209c2c14abSThejaswini Singarajipura 	sadb_x_replay_ctr_t *replext =
45219c2c14abSThejaswini Singarajipura 	    (sadb_x_replay_ctr_t *)ksi->ks_in_extv[SADB_X_EXT_REPLAY_VALUE];
45227c478bd9Sstevel@tonic-gate 	sadb_lifetime_t *soft =
45237c478bd9Sstevel@tonic-gate 	    (sadb_lifetime_t *)ksi->ks_in_extv[SADB_EXT_LIFETIME_SOFT];
45247c478bd9Sstevel@tonic-gate 	sadb_lifetime_t *hard =
45257c478bd9Sstevel@tonic-gate 	    (sadb_lifetime_t *)ksi->ks_in_extv[SADB_EXT_LIFETIME_HARD];
45269c2c14abSThejaswini Singarajipura 	sadb_lifetime_t *idle =
45279c2c14abSThejaswini Singarajipura 	    (sadb_lifetime_t *)ksi->ks_in_extv[SADB_X_EXT_LIFETIME_IDLE];
452838d95a78Smarkfen 	sadb_x_pair_t *pair_ext =
452938d95a78Smarkfen 	    (sadb_x_pair_t *)ksi->ks_in_extv[SADB_X_EXT_PAIR];
453038d95a78Smarkfen 	ipsa_t *echo_target = NULL;
45315d3b8cb7SBill Sommerfeld 	ipsap_t ipsapp;
45325d3b8cb7SBill Sommerfeld 	ipsa_query_t sq;
45339c2c14abSThejaswini Singarajipura 	time_t current = gethrestime_sec();
45347c478bd9Sstevel@tonic-gate 
45355d3b8cb7SBill Sommerfeld 	sq.spp = spp;		/* XXX param */
45365d3b8cb7SBill Sommerfeld 	int error = sadb_form_query(ksi, IPSA_Q_SRC|IPSA_Q_DST|IPSA_Q_SA,
4537f4a6f97eSDan McDonald 	    IPSA_Q_SRC|IPSA_Q_DST|IPSA_Q_SA|IPSA_Q_INBOUND|IPSA_Q_OUTBOUND|
4538f4a6f97eSDan McDonald 	    IPSA_Q_KMC,
45395d3b8cb7SBill Sommerfeld 	    &sq, diagnostic);
454038d95a78Smarkfen 
45415d3b8cb7SBill Sommerfeld 	if (error != 0)
45425d3b8cb7SBill Sommerfeld 		return (error);
45437c478bd9Sstevel@tonic-gate 
45445d3b8cb7SBill Sommerfeld 	error = get_ipsa_pair(&sq, &ipsapp, diagnostic);
45455d3b8cb7SBill Sommerfeld 	if (error != 0)
45465d3b8cb7SBill Sommerfeld 		return (error);
45477c478bd9Sstevel@tonic-gate 
45485d3b8cb7SBill Sommerfeld 	if (ipsapp.ipsap_psa_ptr == NULL && ipsapp.ipsap_sa_ptr != NULL) {
45495d3b8cb7SBill Sommerfeld 		if (ipsapp.ipsap_sa_ptr->ipsa_state == IPSA_STATE_LARVAL) {
45507c478bd9Sstevel@tonic-gate 			/*
45517c478bd9Sstevel@tonic-gate 			 * REFRELE the target and let the add_sa_func()
45527c478bd9Sstevel@tonic-gate 			 * deal with updating a larval SA.
45537c478bd9Sstevel@tonic-gate 			 */
45545d3b8cb7SBill Sommerfeld 			destroy_ipsa_pair(&ipsapp);
4555f4b3ec61Sdh 			return (add_sa_func(mp, ksi, diagnostic, ns));
45567c478bd9Sstevel@tonic-gate 		}
45577c478bd9Sstevel@tonic-gate 	}
45587c478bd9Sstevel@tonic-gate 
4559a1ba8781SMark Fenwick 	/*
4560a1ba8781SMark Fenwick 	 * At this point we have an UPDATE to a MATURE SA. There should
4561a1ba8781SMark Fenwick 	 * not be any keying material present.
4562a1ba8781SMark Fenwick 	 */
4563a1ba8781SMark Fenwick 	if (akey != NULL) {
4564a1ba8781SMark Fenwick 		*diagnostic = SADB_X_DIAGNOSTIC_AKEY_PRESENT;
4565a1ba8781SMark Fenwick 		error = EINVAL;
4566a1ba8781SMark Fenwick 		goto bail;
4567a1ba8781SMark Fenwick 	}
4568a1ba8781SMark Fenwick 	if (ekey != NULL) {
4569a1ba8781SMark Fenwick 		*diagnostic = SADB_X_DIAGNOSTIC_EKEY_PRESENT;
4570a1ba8781SMark Fenwick 		error = EINVAL;
4571a1ba8781SMark Fenwick 		goto bail;
4572a1ba8781SMark Fenwick 	}
4573a1ba8781SMark Fenwick 
45745d3b8cb7SBill Sommerfeld 	if (sq.assoc->sadb_sa_state == SADB_X_SASTATE_ACTIVE_ELSEWHERE) {
45755d3b8cb7SBill Sommerfeld 		if (ipsapp.ipsap_sa_ptr != NULL &&
45765d3b8cb7SBill Sommerfeld 		    ipsapp.ipsap_sa_ptr->ipsa_state == IPSA_STATE_IDLE) {
45775d3b8cb7SBill Sommerfeld 			if ((error = sadb_update_state(ipsapp.ipsap_sa_ptr,
45785d3b8cb7SBill Sommerfeld 			    sq.assoc->sadb_sa_state, NULL)) != 0) {
45799c2c14abSThejaswini Singarajipura 				*diagnostic = SADB_X_DIAGNOSTIC_BAD_SASTATE;
45809c2c14abSThejaswini Singarajipura 				goto bail;
45819c2c14abSThejaswini Singarajipura 			}
45829c2c14abSThejaswini Singarajipura 		}
45835d3b8cb7SBill Sommerfeld 		if (ipsapp.ipsap_psa_ptr != NULL &&
45845d3b8cb7SBill Sommerfeld 		    ipsapp.ipsap_psa_ptr->ipsa_state == IPSA_STATE_IDLE) {
45855d3b8cb7SBill Sommerfeld 			if ((error = sadb_update_state(ipsapp.ipsap_psa_ptr,
45865d3b8cb7SBill Sommerfeld 			    sq.assoc->sadb_sa_state, NULL)) != 0) {
45879c2c14abSThejaswini Singarajipura 				*diagnostic = SADB_X_DIAGNOSTIC_BAD_SASTATE;
45889c2c14abSThejaswini Singarajipura 				goto bail;
45899c2c14abSThejaswini Singarajipura 			}
45909c2c14abSThejaswini Singarajipura 		}
45919c2c14abSThejaswini Singarajipura 	}
45925d3b8cb7SBill Sommerfeld 	if (sq.assoc->sadb_sa_state == SADB_X_SASTATE_ACTIVE) {
45935d3b8cb7SBill Sommerfeld 		if (ipsapp.ipsap_sa_ptr != NULL) {
45945d3b8cb7SBill Sommerfeld 			error = sadb_update_state(ipsapp.ipsap_sa_ptr,
45955d3b8cb7SBill Sommerfeld 			    sq.assoc->sadb_sa_state,
45965d3b8cb7SBill Sommerfeld 			    (ipsapp.ipsap_sa_ptr->ipsa_flags &
45979c2c14abSThejaswini Singarajipura 			    IPSA_F_INBOUND) ? ipkt_lst : NULL);
45989c2c14abSThejaswini Singarajipura 			if (error) {
45999c2c14abSThejaswini Singarajipura 				*diagnostic = SADB_X_DIAGNOSTIC_BAD_SASTATE;
46009c2c14abSThejaswini Singarajipura 				goto bail;
46019c2c14abSThejaswini Singarajipura 			}
46029c2c14abSThejaswini Singarajipura 		}
46035d3b8cb7SBill Sommerfeld 		if (ipsapp.ipsap_psa_ptr != NULL) {
46045d3b8cb7SBill Sommerfeld 			error = sadb_update_state(ipsapp.ipsap_psa_ptr,
46055d3b8cb7SBill Sommerfeld 			    sq.assoc->sadb_sa_state,
46065d3b8cb7SBill Sommerfeld 			    (ipsapp.ipsap_psa_ptr->ipsa_flags &
46079c2c14abSThejaswini Singarajipura 			    IPSA_F_INBOUND) ? ipkt_lst : NULL);
46089c2c14abSThejaswini Singarajipura 			if (error) {
46099c2c14abSThejaswini Singarajipura 				*diagnostic = SADB_X_DIAGNOSTIC_BAD_SASTATE;
46109c2c14abSThejaswini Singarajipura 				goto bail;
46119c2c14abSThejaswini Singarajipura 			}
46129c2c14abSThejaswini Singarajipura 		}
46139c2c14abSThejaswini Singarajipura 		sadb_pfkey_echo(pfkey_q, mp, (sadb_msg_t *)mp->b_cont->b_rptr,
46149c2c14abSThejaswini Singarajipura 		    ksi, echo_target);
46159c2c14abSThejaswini Singarajipura 		goto bail;
46169c2c14abSThejaswini Singarajipura 	}
46179c2c14abSThejaswini Singarajipura 
46187c478bd9Sstevel@tonic-gate 	/*
46197c478bd9Sstevel@tonic-gate 	 * Reality checks for updates of active associations.
46207c478bd9Sstevel@tonic-gate 	 * Sundry first-pass UPDATE-specific reality checks.
46217c478bd9Sstevel@tonic-gate 	 * Have to do the checks here, because it's after the add_sa code.
46227c478bd9Sstevel@tonic-gate 	 * XXX STATS : logging/stats here?
46237c478bd9Sstevel@tonic-gate 	 */
46247c478bd9Sstevel@tonic-gate 
46255d3b8cb7SBill Sommerfeld 	if (!((sq.assoc->sadb_sa_state == SADB_SASTATE_MATURE) ||
46265d3b8cb7SBill Sommerfeld 	    (sq.assoc->sadb_sa_state == SADB_X_SASTATE_ACTIVE_ELSEWHERE))) {
46277c478bd9Sstevel@tonic-gate 		*diagnostic = SADB_X_DIAGNOSTIC_BAD_SASTATE;
46287c478bd9Sstevel@tonic-gate 		error = EINVAL;
46297c478bd9Sstevel@tonic-gate 		goto bail;
46307c478bd9Sstevel@tonic-gate 	}
46315d3b8cb7SBill Sommerfeld 	if (sq.assoc->sadb_sa_flags & ~spp->s_updateflags) {
46327c478bd9Sstevel@tonic-gate 		*diagnostic = SADB_X_DIAGNOSTIC_BAD_SAFLAGS;
46337c478bd9Sstevel@tonic-gate 		error = EINVAL;
46347c478bd9Sstevel@tonic-gate 		goto bail;
46357c478bd9Sstevel@tonic-gate 	}
46367c478bd9Sstevel@tonic-gate 	if (ksi->ks_in_extv[SADB_EXT_LIFETIME_CURRENT] != NULL) {
4637a1ba8781SMark Fenwick 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_LIFETIME;
46387c478bd9Sstevel@tonic-gate 		error = EOPNOTSUPP;
46397c478bd9Sstevel@tonic-gate 		goto bail;
46407c478bd9Sstevel@tonic-gate 	}
46419c2c14abSThejaswini Singarajipura 
46429c2c14abSThejaswini Singarajipura 	if ((*diagnostic = sadb_hardsoftchk(hard, soft, idle)) != 0) {
46437c478bd9Sstevel@tonic-gate 		error = EINVAL;
46447c478bd9Sstevel@tonic-gate 		goto bail;
46457c478bd9Sstevel@tonic-gate 	}
46467c478bd9Sstevel@tonic-gate 
46475d3b8cb7SBill Sommerfeld 	if ((*diagnostic = sadb_labelchk(ksi)) != 0)
46485d3b8cb7SBill Sommerfeld 		return (EINVAL);
46495d3b8cb7SBill Sommerfeld 
46505d3b8cb7SBill Sommerfeld 	error = sadb_check_kmc(&sq, ipsapp.ipsap_sa_ptr, diagnostic);
46515d3b8cb7SBill Sommerfeld 	if (error != 0)
46525d3b8cb7SBill Sommerfeld 		goto bail;
46535d3b8cb7SBill Sommerfeld 
46545d3b8cb7SBill Sommerfeld 	error = sadb_check_kmc(&sq, ipsapp.ipsap_psa_ptr, diagnostic);
46555d3b8cb7SBill Sommerfeld 	if (error != 0)
46565d3b8cb7SBill Sommerfeld 		goto bail;
46575d3b8cb7SBill Sommerfeld 
46585d3b8cb7SBill Sommerfeld 
46595d3b8cb7SBill Sommerfeld 	if (ipsapp.ipsap_sa_ptr != NULL) {
46609c2c14abSThejaswini Singarajipura 		/*
46619c2c14abSThejaswini Singarajipura 		 * Do not allow replay value change for MATURE or LARVAL SA.
46629c2c14abSThejaswini Singarajipura 		 */
46639c2c14abSThejaswini Singarajipura 
46649c2c14abSThejaswini Singarajipura 		if ((replext != NULL) &&
46655d3b8cb7SBill Sommerfeld 		    ((ipsapp.ipsap_sa_ptr->ipsa_state == IPSA_STATE_LARVAL) ||
46665d3b8cb7SBill Sommerfeld 		    (ipsapp.ipsap_sa_ptr->ipsa_state == IPSA_STATE_MATURE))) {
46679c2c14abSThejaswini Singarajipura 			*diagnostic = SADB_X_DIAGNOSTIC_BAD_SASTATE;
46689c2c14abSThejaswini Singarajipura 			error = EINVAL;
46699c2c14abSThejaswini Singarajipura 			goto bail;
46709c2c14abSThejaswini Singarajipura 		}
46717c478bd9Sstevel@tonic-gate 	}
46727c478bd9Sstevel@tonic-gate 
46737c478bd9Sstevel@tonic-gate 
46745d3b8cb7SBill Sommerfeld 	if (ipsapp.ipsap_sa_ptr != NULL) {
46755d3b8cb7SBill Sommerfeld 		sadb_update_lifetimes(ipsapp.ipsap_sa_ptr, hard, soft,
46769c2c14abSThejaswini Singarajipura 		    idle, B_TRUE);
46775d3b8cb7SBill Sommerfeld 		sadb_update_kmc(&sq, ipsapp.ipsap_sa_ptr);
46789c2c14abSThejaswini Singarajipura 		if ((replext != NULL) &&
46795d3b8cb7SBill Sommerfeld 		    (ipsapp.ipsap_sa_ptr->ipsa_replay_wsize != 0)) {
46809c2c14abSThejaswini Singarajipura 			/*
46819c2c14abSThejaswini Singarajipura 			 * If an inbound SA, update the replay counter
46829c2c14abSThejaswini Singarajipura 			 * and check off all the other sequence number
46839c2c14abSThejaswini Singarajipura 			 */
46849c2c14abSThejaswini Singarajipura 			if (ksi->ks_in_dsttype == KS_IN_ADDR_ME) {
46855d3b8cb7SBill Sommerfeld 				if (!sadb_replay_check(ipsapp.ipsap_sa_ptr,
46869c2c14abSThejaswini Singarajipura 				    replext->sadb_x_rc_replay32)) {
4687a1ba8781SMark Fenwick 					*diagnostic =
4688a1ba8781SMark Fenwick 					    SADB_X_DIAGNOSTIC_INVALID_REPLAY;
46899c2c14abSThejaswini Singarajipura 					error = EINVAL;
46909c2c14abSThejaswini Singarajipura 					goto bail;
46919c2c14abSThejaswini Singarajipura 				}
46925d3b8cb7SBill Sommerfeld 				mutex_enter(&ipsapp.ipsap_sa_ptr->ipsa_lock);
46935d3b8cb7SBill Sommerfeld 				ipsapp.ipsap_sa_ptr->ipsa_idleexpiretime =
46949c2c14abSThejaswini Singarajipura 				    current +
46955d3b8cb7SBill Sommerfeld 				    ipsapp.ipsap_sa_ptr->ipsa_idletime;
46965d3b8cb7SBill Sommerfeld 				mutex_exit(&ipsapp.ipsap_sa_ptr->ipsa_lock);
46979c2c14abSThejaswini Singarajipura 			} else {
46985d3b8cb7SBill Sommerfeld 				mutex_enter(&ipsapp.ipsap_sa_ptr->ipsa_lock);
46995d3b8cb7SBill Sommerfeld 				ipsapp.ipsap_sa_ptr->ipsa_replay =
47009c2c14abSThejaswini Singarajipura 				    replext->sadb_x_rc_replay32;
47015d3b8cb7SBill Sommerfeld 				ipsapp.ipsap_sa_ptr->ipsa_idleexpiretime =
47029c2c14abSThejaswini Singarajipura 				    current +
47035d3b8cb7SBill Sommerfeld 				    ipsapp.ipsap_sa_ptr->ipsa_idletime;
47045d3b8cb7SBill Sommerfeld 				mutex_exit(&ipsapp.ipsap_sa_ptr->ipsa_lock);
47059c2c14abSThejaswini Singarajipura 			}
47069c2c14abSThejaswini Singarajipura 		}
47077c478bd9Sstevel@tonic-gate 	}
47087c478bd9Sstevel@tonic-gate 
470938d95a78Smarkfen 	if (sadb_msg_type == SADB_X_UPDATEPAIR) {
47105d3b8cb7SBill Sommerfeld 		if (ipsapp.ipsap_psa_ptr != NULL) {
47115d3b8cb7SBill Sommerfeld 			sadb_update_lifetimes(ipsapp.ipsap_psa_ptr, hard, soft,
47129c2c14abSThejaswini Singarajipura 			    idle, B_FALSE);
47135d3b8cb7SBill Sommerfeld 			sadb_update_kmc(&sq, ipsapp.ipsap_psa_ptr);
471438d95a78Smarkfen 		} else {
471538d95a78Smarkfen 			*diagnostic = SADB_X_DIAGNOSTIC_PAIR_SA_NOTFOUND;
471638d95a78Smarkfen 			error = ESRCH;
471738d95a78Smarkfen 			goto bail;
471838d95a78Smarkfen 		}
47197c478bd9Sstevel@tonic-gate 	}
47207c478bd9Sstevel@tonic-gate 
472138d95a78Smarkfen 	if (pair_ext != NULL)
47225d3b8cb7SBill Sommerfeld 		error = update_pairing(&ipsapp, &sq, ksi, diagnostic);
47237c478bd9Sstevel@tonic-gate 
472438d95a78Smarkfen 	if (error == 0)
472538d95a78Smarkfen 		sadb_pfkey_echo(pfkey_q, mp, (sadb_msg_t *)mp->b_cont->b_rptr,
472638d95a78Smarkfen 		    ksi, echo_target);
47277c478bd9Sstevel@tonic-gate bail:
472838d95a78Smarkfen 
47295d3b8cb7SBill Sommerfeld 	destroy_ipsa_pair(&ipsapp);
473038d95a78Smarkfen 
473138d95a78Smarkfen 	return (error);
473238d95a78Smarkfen }
473338d95a78Smarkfen 
473438d95a78Smarkfen 
47355d3b8cb7SBill Sommerfeld static int
47365d3b8cb7SBill Sommerfeld update_pairing(ipsap_t *ipsapp, ipsa_query_t *sq, keysock_in_t *ksi,
47375d3b8cb7SBill Sommerfeld     int *diagnostic)
473838d95a78Smarkfen {
473938d95a78Smarkfen 	sadb_sa_t *assoc = (sadb_sa_t *)ksi->ks_in_extv[SADB_EXT_SA];
474038d95a78Smarkfen 	sadb_x_pair_t *pair_ext =
474138d95a78Smarkfen 	    (sadb_x_pair_t *)ksi->ks_in_extv[SADB_X_EXT_PAIR];
474238d95a78Smarkfen 	int error = 0;
47435d3b8cb7SBill Sommerfeld 	ipsap_t oipsapp;
474438d95a78Smarkfen 	boolean_t undo_pair = B_FALSE;
474538d95a78Smarkfen 	uint32_t ipsa_flags;
474638d95a78Smarkfen 
474738d95a78Smarkfen 	if (pair_ext->sadb_x_pair_spi == 0 || pair_ext->sadb_x_pair_spi ==
474838d95a78Smarkfen 	    assoc->sadb_sa_spi) {
474938d95a78Smarkfen 		*diagnostic = SADB_X_DIAGNOSTIC_PAIR_INAPPROPRIATE;
475038d95a78Smarkfen 		return (EINVAL);
475138d95a78Smarkfen 	}
475238d95a78Smarkfen 
47537c478bd9Sstevel@tonic-gate 	/*
475438d95a78Smarkfen 	 * Assume for now that the spi value provided in the SADB_UPDATE
475538d95a78Smarkfen 	 * message was valid, update the SA with its pair spi value.
475638d95a78Smarkfen 	 * If the spi turns out to be bogus or the SA no longer exists
475738d95a78Smarkfen 	 * then this will be detected when the reverse update is made
475838d95a78Smarkfen 	 * below.
475938d95a78Smarkfen 	 */
476038d95a78Smarkfen 	mutex_enter(&ipsapp->ipsap_sa_ptr->ipsa_lock);
476138d95a78Smarkfen 	ipsapp->ipsap_sa_ptr->ipsa_flags |= IPSA_F_PAIRED;
476238d95a78Smarkfen 	ipsapp->ipsap_sa_ptr->ipsa_otherspi = pair_ext->sadb_x_pair_spi;
476338d95a78Smarkfen 	mutex_exit(&ipsapp->ipsap_sa_ptr->ipsa_lock);
476438d95a78Smarkfen 
476538d95a78Smarkfen 	/*
476638d95a78Smarkfen 	 * After updating the ipsa_otherspi element of the SA, get_ipsa_pair()
476738d95a78Smarkfen 	 * should now return pointers to the SA *AND* its pair, if this is not
476838d95a78Smarkfen 	 * the case, the "otherspi" either did not exist or was deleted. Also
476938d95a78Smarkfen 	 * check that "otherspi" is not already paired. If everything looks
477038d95a78Smarkfen 	 * good, complete the update. IPSA_REFRELE the first pair_pointer
477138d95a78Smarkfen 	 * after this update to ensure its not deleted until we are done.
47727c478bd9Sstevel@tonic-gate 	 */
47735d3b8cb7SBill Sommerfeld 	error = get_ipsa_pair(sq, &oipsapp, diagnostic);
47745d3b8cb7SBill Sommerfeld 	if (error != 0) {
477538d95a78Smarkfen 		/*
477638d95a78Smarkfen 		 * This should never happen, calling function still has
477738d95a78Smarkfen 		 * IPSA_REFHELD on the SA we just updated.
477838d95a78Smarkfen 		 */
47795d3b8cb7SBill Sommerfeld 		return (error);	/* XXX EINVAL instead of ESRCH? */
47807c478bd9Sstevel@tonic-gate 	}
478138d95a78Smarkfen 
47825d3b8cb7SBill Sommerfeld 	if (oipsapp.ipsap_psa_ptr == NULL) {
478338d95a78Smarkfen 		*diagnostic = SADB_X_DIAGNOSTIC_PAIR_INAPPROPRIATE;
4784a1ba8781SMark Fenwick 		error = EINVAL;
478538d95a78Smarkfen 		undo_pair = B_TRUE;
478638d95a78Smarkfen 	} else {
47875d3b8cb7SBill Sommerfeld 		ipsa_flags = oipsapp.ipsap_psa_ptr->ipsa_flags;
47885d3b8cb7SBill Sommerfeld 		if ((oipsapp.ipsap_psa_ptr->ipsa_state == IPSA_STATE_DEAD) ||
47895d3b8cb7SBill Sommerfeld 		    (oipsapp.ipsap_psa_ptr->ipsa_state == IPSA_STATE_DYING)) {
479038d95a78Smarkfen 			/* Its dead Jim! */
479138d95a78Smarkfen 			*diagnostic = SADB_X_DIAGNOSTIC_PAIR_INAPPROPRIATE;
479238d95a78Smarkfen 			undo_pair = B_TRUE;
479338d95a78Smarkfen 		} else if ((ipsa_flags & (IPSA_F_OUTBOUND | IPSA_F_INBOUND)) ==
479438d95a78Smarkfen 		    (IPSA_F_OUTBOUND | IPSA_F_INBOUND)) {
479538d95a78Smarkfen 			/* This SA is in both hashtables. */
479638d95a78Smarkfen 			*diagnostic = SADB_X_DIAGNOSTIC_PAIR_INAPPROPRIATE;
479738d95a78Smarkfen 			undo_pair = B_TRUE;
479838d95a78Smarkfen 		} else if (ipsa_flags & IPSA_F_PAIRED) {
479938d95a78Smarkfen 			/* This SA is already paired with another. */
480038d95a78Smarkfen 			*diagnostic = SADB_X_DIAGNOSTIC_PAIR_ALREADY;
480138d95a78Smarkfen 			undo_pair = B_TRUE;
480238d95a78Smarkfen 		}
480338d95a78Smarkfen 	}
480438d95a78Smarkfen 
480538d95a78Smarkfen 	if (undo_pair) {
480638d95a78Smarkfen 		/* The pair SA does not exist. */
480738d95a78Smarkfen 		mutex_enter(&ipsapp->ipsap_sa_ptr->ipsa_lock);
480838d95a78Smarkfen 		ipsapp->ipsap_sa_ptr->ipsa_flags &= ~IPSA_F_PAIRED;
480938d95a78Smarkfen 		ipsapp->ipsap_sa_ptr->ipsa_otherspi = 0;
481038d95a78Smarkfen 		mutex_exit(&ipsapp->ipsap_sa_ptr->ipsa_lock);
481138d95a78Smarkfen 	} else {
48125d3b8cb7SBill Sommerfeld 		mutex_enter(&oipsapp.ipsap_psa_ptr->ipsa_lock);
48135d3b8cb7SBill Sommerfeld 		oipsapp.ipsap_psa_ptr->ipsa_otherspi = assoc->sadb_sa_spi;
48145d3b8cb7SBill Sommerfeld 		oipsapp.ipsap_psa_ptr->ipsa_flags |= IPSA_F_PAIRED;
48155d3b8cb7SBill Sommerfeld 		mutex_exit(&oipsapp.ipsap_psa_ptr->ipsa_lock);
48167c478bd9Sstevel@tonic-gate 	}
48177c478bd9Sstevel@tonic-gate 
48185d3b8cb7SBill Sommerfeld 	destroy_ipsa_pair(&oipsapp);
48197c478bd9Sstevel@tonic-gate 	return (error);
48207c478bd9Sstevel@tonic-gate }
48217c478bd9Sstevel@tonic-gate 
48227c478bd9Sstevel@tonic-gate /*
48237c478bd9Sstevel@tonic-gate  * The following functions deal with ACQUIRE LISTS.  An ACQUIRE list is
48247c478bd9Sstevel@tonic-gate  * a list of outstanding SADB_ACQUIRE messages.	 If ipsec_getassocbyconn() fails
48257c478bd9Sstevel@tonic-gate  * for an outbound datagram, that datagram is queued up on an ACQUIRE record,
48267c478bd9Sstevel@tonic-gate  * and an SADB_ACQUIRE message is sent up.  Presumably, a user-space key
48277c478bd9Sstevel@tonic-gate  * management daemon will process the ACQUIRE, use a SADB_GETSPI to reserve
48287c478bd9Sstevel@tonic-gate  * an SPI value and a larval SA, then SADB_UPDATE the larval SA, and ADD the
48297c478bd9Sstevel@tonic-gate  * other direction's SA.
48307c478bd9Sstevel@tonic-gate  */
48317c478bd9Sstevel@tonic-gate 
48327c478bd9Sstevel@tonic-gate /*
48337c478bd9Sstevel@tonic-gate  * Check the ACQUIRE lists.  If there's an existing ACQUIRE record,
48347c478bd9Sstevel@tonic-gate  * grab it, lock it, and return it.  Otherwise return NULL.
48355d3b8cb7SBill Sommerfeld  *
48365d3b8cb7SBill Sommerfeld  * XXX MLS number of arguments getting unwieldy here
48377c478bd9Sstevel@tonic-gate  */
48387c478bd9Sstevel@tonic-gate static ipsacq_t *
48397c478bd9Sstevel@tonic-gate sadb_checkacquire(iacqf_t *bucket, ipsec_action_t *ap, ipsec_policy_t *pp,
48408810c16bSdanmcd     uint32_t *src, uint32_t *dst, uint32_t *isrc, uint32_t *idst,
4841bd670b35SErik Nordmark     uint64_t unique_id, ts_label_t *tsl)
48427c478bd9Sstevel@tonic-gate {
48437c478bd9Sstevel@tonic-gate 	ipsacq_t *walker;
48447c478bd9Sstevel@tonic-gate 	sa_family_t fam;
48458810c16bSdanmcd 	uint32_t blank_address[4] = {0, 0, 0, 0};
48468810c16bSdanmcd 
48478810c16bSdanmcd 	if (isrc == NULL) {
48488810c16bSdanmcd 		ASSERT(idst == NULL);
48498810c16bSdanmcd 		isrc = idst = blank_address;
48508810c16bSdanmcd 	}
48517c478bd9Sstevel@tonic-gate 
48527c478bd9Sstevel@tonic-gate 	/*
48537c478bd9Sstevel@tonic-gate 	 * Scan list for duplicates.  Check for UNIQUE, src/dest, policy.
48547c478bd9Sstevel@tonic-gate 	 *
48557c478bd9Sstevel@tonic-gate 	 * XXX May need search for duplicates based on other things too!
48567c478bd9Sstevel@tonic-gate 	 */
48577c478bd9Sstevel@tonic-gate 	for (walker = bucket->iacqf_ipsacq; walker != NULL;
48587c478bd9Sstevel@tonic-gate 	    walker = walker->ipsacq_next) {
48597c478bd9Sstevel@tonic-gate 		mutex_enter(&walker->ipsacq_lock);
48607c478bd9Sstevel@tonic-gate 		fam = walker->ipsacq_addrfam;
48617c478bd9Sstevel@tonic-gate 		if (IPSA_ARE_ADDR_EQUAL(dst, walker->ipsacq_dstaddr, fam) &&
48627c478bd9Sstevel@tonic-gate 		    IPSA_ARE_ADDR_EQUAL(src, walker->ipsacq_srcaddr, fam) &&
48638810c16bSdanmcd 		    ip_addr_match((uint8_t *)isrc, walker->ipsacq_innersrcpfx,
4864437220cdSdanmcd 		    (in6_addr_t *)walker->ipsacq_innersrc) &&
48658810c16bSdanmcd 		    ip_addr_match((uint8_t *)idst, walker->ipsacq_innerdstpfx,
4866437220cdSdanmcd 		    (in6_addr_t *)walker->ipsacq_innerdst) &&
48677c478bd9Sstevel@tonic-gate 		    (ap == walker->ipsacq_act) &&
48687c478bd9Sstevel@tonic-gate 		    (pp == walker->ipsacq_policy) &&
48697c478bd9Sstevel@tonic-gate 		    /* XXX do deep compares of ap/pp? */
48705d3b8cb7SBill Sommerfeld 		    (unique_id == walker->ipsacq_unique_id) &&
4871bd670b35SErik Nordmark 		    (ipsec_label_match(tsl, walker->ipsacq_tsl)))
48727c478bd9Sstevel@tonic-gate 			break;			/* everything matched */
48737c478bd9Sstevel@tonic-gate 		mutex_exit(&walker->ipsacq_lock);
48747c478bd9Sstevel@tonic-gate 	}
48757c478bd9Sstevel@tonic-gate 
48767c478bd9Sstevel@tonic-gate 	return (walker);
48777c478bd9Sstevel@tonic-gate }
48787c478bd9Sstevel@tonic-gate 
48797c478bd9Sstevel@tonic-gate /*
4880b7daf799SDan McDonald  * Generate an SADB_ACQUIRE base message mblk, including KEYSOCK_OUT metadata.
4881b7daf799SDan McDonald  * In other words, this will return, upon success, a two-mblk chain.
48827c478bd9Sstevel@tonic-gate  */
4883b7daf799SDan McDonald static inline mblk_t *
4884b7daf799SDan McDonald sadb_acquire_msg_base(minor_t serial, uint8_t satype, uint32_t seq, pid_t pid)
48857c478bd9Sstevel@tonic-gate {
4886b7daf799SDan McDonald 	mblk_t *mp;
4887b7daf799SDan McDonald 	sadb_msg_t *samsg;
48887c478bd9Sstevel@tonic-gate 
4889b7daf799SDan McDonald 	mp = sadb_keysock_out(serial);
4890b7daf799SDan McDonald 	if (mp == NULL)
4891b7daf799SDan McDonald 		return (NULL);
4892b7daf799SDan McDonald 	mp->b_cont = allocb(sizeof (sadb_msg_t), BPRI_HI);
4893b7daf799SDan McDonald 	if (mp->b_cont == NULL) {
4894b7daf799SDan McDonald 		freeb(mp);
4895b7daf799SDan McDonald 		return (NULL);
4896b7daf799SDan McDonald 	}
48977c478bd9Sstevel@tonic-gate 
4898b7daf799SDan McDonald 	samsg = (sadb_msg_t *)mp->b_cont->b_rptr;
4899b7daf799SDan McDonald 	mp->b_cont->b_wptr += sizeof (*samsg);
4900b7daf799SDan McDonald 	samsg->sadb_msg_version = PF_KEY_V2;
4901b7daf799SDan McDonald 	samsg->sadb_msg_type = SADB_ACQUIRE;
4902b7daf799SDan McDonald 	samsg->sadb_msg_errno = 0;
4903b7daf799SDan McDonald 	samsg->sadb_msg_reserved = 0;
4904b7daf799SDan McDonald 	samsg->sadb_msg_satype = satype;
4905b7daf799SDan McDonald 	samsg->sadb_msg_seq = seq;
4906b7daf799SDan McDonald 	samsg->sadb_msg_pid = pid;
49075d3b8cb7SBill Sommerfeld 
4908b7daf799SDan McDonald 	return (mp);
4909b7daf799SDan McDonald }
4910f4b3ec61Sdh 
4911b7daf799SDan McDonald /*
4912b7daf799SDan McDonald  * Generate address and TX/MLS sensitivity label PF_KEY extensions that are
4913b7daf799SDan McDonald  * common to both regular and extended ACQUIREs.
4914b7daf799SDan McDonald  */
4915b7daf799SDan McDonald static mblk_t *
4916b7daf799SDan McDonald sadb_acquire_msg_common(ipsec_selector_t *sel, ipsec_policy_t *pp,
4917b7daf799SDan McDonald     ipsec_action_t *ap, boolean_t tunnel_mode, ts_label_t *tsl,
4918b7daf799SDan McDonald     sadb_sens_t *sens)
4919b7daf799SDan McDonald {
4920b7daf799SDan McDonald 	size_t len;
4921b7daf799SDan McDonald 	mblk_t *mp;
4922b7daf799SDan McDonald 	uint8_t *start, *cur, *end;
4923b7daf799SDan McDonald 	uint32_t *saddrptr, *daddrptr;
4924b7daf799SDan McDonald 	sa_family_t af;
4925b7daf799SDan McDonald 	ipsec_action_t *oldap;
4926b7daf799SDan McDonald 	ipsec_selkey_t *ipsl;
4927b7daf799SDan McDonald 	uint8_t proto, pfxlen;
4928b7daf799SDan McDonald 	uint16_t lport, rport;
4929b7daf799SDan McDonald 	int senslen = 0;
4930f4b3ec61Sdh 
4931b7daf799SDan McDonald 	/*
4932b7daf799SDan McDonald 	 * Get action pointer set if it isn't already.
4933b7daf799SDan McDonald 	 */
4934b7daf799SDan McDonald 	oldap = ap;
4935b7daf799SDan McDonald 	if (pp != NULL) {
4936b7daf799SDan McDonald 		ap = pp->ipsp_act;
4937b7daf799SDan McDonald 		if (ap == NULL)
4938b7daf799SDan McDonald 			ap = oldap;
4939f4b3ec61Sdh 	}
49405d3b8cb7SBill Sommerfeld 
4941b7daf799SDan McDonald 	/*
4942b7daf799SDan McDonald 	 * Biggest-case scenario:
4943b7daf799SDan McDonald 	 * 4x (sadb_address_t + struct sockaddr_in6)
4944b7daf799SDan McDonald 	 *	(src, dst, isrc, idst)
4945b7daf799SDan McDonald 	 *	(COMING SOON, 6x, because of triggering-packet contents.)
4946b7daf799SDan McDonald 	 * sadb_x_kmc_t
4947b7daf799SDan McDonald 	 * sadb_sens_t
4948b7daf799SDan McDonald 	 * And wiggle room for label bitvectors.  Luckily there are
4949b7daf799SDan McDonald 	 * programmatic ways to find it.
4950b7daf799SDan McDonald 	 */
4951b7daf799SDan McDonald 	len = 4 * (sizeof (sadb_address_t) + sizeof (struct sockaddr_in6));
49525d3b8cb7SBill Sommerfeld 
4953b7daf799SDan McDonald 	/* Figure out full and proper length of sensitivity labels. */
4954b7daf799SDan McDonald 	if (sens != NULL) {
4955b7daf799SDan McDonald 		ASSERT(tsl == NULL);
4956b7daf799SDan McDonald 		senslen = SADB_64TO8(sens->sadb_sens_len);
4957b7daf799SDan McDonald 	} else if (tsl != NULL) {
4958b7daf799SDan McDonald 		senslen = sadb_sens_len_from_label(tsl);
4959b7daf799SDan McDonald 	}
4960b7daf799SDan McDonald #ifdef DEBUG
4961b7daf799SDan McDonald 	else {
4962b7daf799SDan McDonald 		ASSERT(senslen == 0);
4963b7daf799SDan McDonald 	}
4964b7daf799SDan McDonald #endif /* DEBUG */
4965b7daf799SDan McDonald 	len += senslen;
49667c478bd9Sstevel@tonic-gate 
4967b7daf799SDan McDonald 	mp = allocb(len, BPRI_HI);
4968b7daf799SDan McDonald 	if (mp == NULL)
4969b7daf799SDan McDonald 		return (NULL);
49707c478bd9Sstevel@tonic-gate 
4971b7daf799SDan McDonald 	start = mp->b_rptr;
4972b7daf799SDan McDonald 	end = start + len;
4973b7daf799SDan McDonald 	cur = start;
49747c478bd9Sstevel@tonic-gate 
49757c478bd9Sstevel@tonic-gate 	/*
4976b7daf799SDan McDonald 	 * Address extensions first, from most-recently-defined to least.
4977b7daf799SDan McDonald 	 * (This should immediately trigger surprise or verify robustness on
4978b7daf799SDan McDonald 	 * older apps, like in.iked.)
49797c478bd9Sstevel@tonic-gate 	 */
4980b7daf799SDan McDonald 	if (tunnel_mode) {
4981b7daf799SDan McDonald 		/*
4982b7daf799SDan McDonald 		 * Form inner address extensions based NOT on the inner
4983b7daf799SDan McDonald 		 * selectors (i.e. the packet data), but on the policy's
4984b7daf799SDan McDonald 		 * selector key (i.e. the policy's selector information).
4985b7daf799SDan McDonald 		 *
4986b7daf799SDan McDonald 		 * NOTE:  The position of IPv4 and IPv6 addresses is the
4987b7daf799SDan McDonald 		 * same in ipsec_selkey_t (unless the compiler does very
4988b7daf799SDan McDonald 		 * strange things with unions, consult your local C language
4989b7daf799SDan McDonald 		 * lawyer for details).
4990b7daf799SDan McDonald 		 */
4991b7daf799SDan McDonald 		ASSERT(pp != NULL);
49927c478bd9Sstevel@tonic-gate 
4993b7daf799SDan McDonald 		ipsl = &(pp->ipsp_sel->ipsl_key);
4994b7daf799SDan McDonald 		if (ipsl->ipsl_valid & IPSL_IPV4) {
4995b7daf799SDan McDonald 			af = AF_INET;
4996b7daf799SDan McDonald 			ASSERT(sel->ips_protocol == IPPROTO_ENCAP);
4997b7daf799SDan McDonald 			ASSERT(!(ipsl->ipsl_valid & IPSL_IPV6));
4998b7daf799SDan McDonald 		} else {
4999b7daf799SDan McDonald 			af = AF_INET6;
5000b7daf799SDan McDonald 			ASSERT(sel->ips_protocol == IPPROTO_IPV6);
5001b7daf799SDan McDonald 			ASSERT(ipsl->ipsl_valid & IPSL_IPV6);
5002b7daf799SDan McDonald 		}
50037c478bd9Sstevel@tonic-gate 
5004b7daf799SDan McDonald 		if (ipsl->ipsl_valid & IPSL_LOCAL_ADDR) {
5005b7daf799SDan McDonald 			saddrptr = (uint32_t *)(&ipsl->ipsl_local);
5006b7daf799SDan McDonald 			pfxlen = ipsl->ipsl_local_pfxlen;
5007b7daf799SDan McDonald 		} else {
5008b7daf799SDan McDonald 			saddrptr = (uint32_t *)(&ipv6_all_zeros);
5009b7daf799SDan McDonald 			pfxlen = 0;
5010b7daf799SDan McDonald 		}
5011b7daf799SDan McDonald 		/* XXX What about ICMP type/code? */
5012b7daf799SDan McDonald 		lport = (ipsl->ipsl_valid & IPSL_LOCAL_PORT) ?
5013b7daf799SDan McDonald 		    ipsl->ipsl_lport : 0;
5014b7daf799SDan McDonald 		proto = (ipsl->ipsl_valid & IPSL_PROTOCOL) ?
5015b7daf799SDan McDonald 		    ipsl->ipsl_proto : 0;
50167c478bd9Sstevel@tonic-gate 
5017b7daf799SDan McDonald 		cur = sadb_make_addr_ext(cur, end, SADB_X_EXT_ADDRESS_INNER_SRC,
5018b7daf799SDan McDonald 		    af, saddrptr, lport, proto, pfxlen);
5019b7daf799SDan McDonald 		if (cur == NULL) {
5020b7daf799SDan McDonald 			freeb(mp);
5021b7daf799SDan McDonald 			return (NULL);
50227807385bSDan McDonald 		}
50238810c16bSdanmcd 
5024b7daf799SDan McDonald 		if (ipsl->ipsl_valid & IPSL_REMOTE_ADDR) {
5025b7daf799SDan McDonald 			daddrptr = (uint32_t *)(&ipsl->ipsl_remote);
5026b7daf799SDan McDonald 			pfxlen = ipsl->ipsl_remote_pfxlen;
5027b7daf799SDan McDonald 		} else {
5028b7daf799SDan McDonald 			daddrptr = (uint32_t *)(&ipv6_all_zeros);
5029b7daf799SDan McDonald 			pfxlen = 0;
5030b7daf799SDan McDonald 		}
5031b7daf799SDan McDonald 		/* XXX What about ICMP type/code? */
5032b7daf799SDan McDonald 		rport = (ipsl->ipsl_valid & IPSL_REMOTE_PORT) ?
5033b7daf799SDan McDonald 		    ipsl->ipsl_rport : 0;
50347c478bd9Sstevel@tonic-gate 
5035b7daf799SDan McDonald 		cur = sadb_make_addr_ext(cur, end, SADB_X_EXT_ADDRESS_INNER_DST,
5036b7daf799SDan McDonald 		    af, daddrptr, rport, proto, pfxlen);
5037b7daf799SDan McDonald 		if (cur == NULL) {
5038b7daf799SDan McDonald 			freeb(mp);
5039b7daf799SDan McDonald 			return (NULL);
5040b7daf799SDan McDonald 		}
50417c478bd9Sstevel@tonic-gate 		/*
5042b7daf799SDan McDonald 		 * TODO  - if we go to 3884's dream of transport mode IP-in-IP
5043b7daf799SDan McDonald 		 * _with_ inner-packet address selectors, we'll need to further
5044b7daf799SDan McDonald 		 * distinguish tunnel mode here.  For now, having inner
5045b7daf799SDan McDonald 		 * addresses and/or ports is sufficient.
5046b7daf799SDan McDonald 		 *
5047b7daf799SDan McDonald 		 * Meanwhile, whack proto/ports to reflect IP-in-IP for the
5048b7daf799SDan McDonald 		 * outer addresses.
50497c478bd9Sstevel@tonic-gate 		 */
5050b7daf799SDan McDonald 		proto = sel->ips_protocol;	/* Either _ENCAP or _IPV6 */
5051b7daf799SDan McDonald 		lport = rport = 0;
5052b7daf799SDan McDonald 	} else if ((ap != NULL) && (!ap->ipa_want_unique)) {
5053b7daf799SDan McDonald 		/*
5054b7daf799SDan McDonald 		 * For cases when the policy calls out specific ports (or not).
5055b7daf799SDan McDonald 		 */
5056b7daf799SDan McDonald 		proto = 0;
5057b7daf799SDan McDonald 		lport = 0;
5058b7daf799SDan McDonald 		rport = 0;
50597c478bd9Sstevel@tonic-gate 		if (pp != NULL) {
5060b7daf799SDan McDonald 			ipsl = &(pp->ipsp_sel->ipsl_key);
5061b7daf799SDan McDonald 			if (ipsl->ipsl_valid & IPSL_PROTOCOL)
5062b7daf799SDan McDonald 				proto = ipsl->ipsl_proto;
5063b7daf799SDan McDonald 			if (ipsl->ipsl_valid & IPSL_REMOTE_PORT)
5064b7daf799SDan McDonald 				rport = ipsl->ipsl_rport;
5065b7daf799SDan McDonald 			if (ipsl->ipsl_valid & IPSL_LOCAL_PORT)
5066b7daf799SDan McDonald 				lport = ipsl->ipsl_lport;
50677c478bd9Sstevel@tonic-gate 		}
5068b7daf799SDan McDonald 	} else {
5069b7daf799SDan McDonald 		/*
5070b7daf799SDan McDonald 		 * For require-unique-SA policies.
5071b7daf799SDan McDonald 		 */
5072b7daf799SDan McDonald 		proto = sel->ips_protocol;
5073b7daf799SDan McDonald 		lport = sel->ips_local_port;
5074b7daf799SDan McDonald 		rport = sel->ips_remote_port;
50757c478bd9Sstevel@tonic-gate 	}
50767c478bd9Sstevel@tonic-gate 
50775d3b8cb7SBill Sommerfeld 	/*
5078b7daf799SDan McDonald 	 * Regular addresses.  These are outer-packet ones for tunnel mode.
5079b7daf799SDan McDonald 	 * Or for transport mode, the regulard address & port information.
50805d3b8cb7SBill Sommerfeld 	 */
5081b7daf799SDan McDonald 	af = sel->ips_isv4 ? AF_INET : AF_INET6;
50827c478bd9Sstevel@tonic-gate 
50837c478bd9Sstevel@tonic-gate 	/*
5084b7daf799SDan McDonald 	 * NOTE:  The position of IPv4 and IPv6 addresses is the same in
5085b7daf799SDan McDonald 	 * ipsec_selector_t.
50867c478bd9Sstevel@tonic-gate 	 */
5087b7daf799SDan McDonald 	cur = sadb_make_addr_ext(cur, end, SADB_EXT_ADDRESS_SRC, af,
5088b7daf799SDan McDonald 	    (uint32_t *)(&sel->ips_local_addr_v6), lport, proto, 0);
5089b7daf799SDan McDonald 	if (cur == NULL) {
5090b7daf799SDan McDonald 		freeb(mp);
5091b7daf799SDan McDonald 		return (NULL);
5092b7daf799SDan McDonald 	}
50937c478bd9Sstevel@tonic-gate 
5094b7daf799SDan McDonald 	cur = sadb_make_addr_ext(cur, end, SADB_EXT_ADDRESS_DST, af,
5095b7daf799SDan McDonald 	    (uint32_t *)(&sel->ips_remote_addr_v6), rport, proto, 0);
5096b7daf799SDan McDonald 	if (cur == NULL) {
5097b7daf799SDan McDonald 		freeb(mp);
5098b7daf799SDan McDonald 		return (NULL);
5099b7daf799SDan McDonald 	}
5100b7daf799SDan McDonald 
5101b7daf799SDan McDonald 	/*
5102b7daf799SDan McDonald 	 * If present, generate a sensitivity label.
5103bd670b35SErik Nordmark 	 */
5104b7daf799SDan McDonald 	if (cur + senslen > end) {
5105b7daf799SDan McDonald 		freeb(mp);
5106b7daf799SDan McDonald 		return (NULL);
5107b7daf799SDan McDonald 	}
5108b7daf799SDan McDonald 	if (sens != NULL) {
5109b7daf799SDan McDonald 		/* Explicit sadb_sens_t, usually from inverse-ACQUIRE. */
5110b7daf799SDan McDonald 		bcopy(sens, cur, senslen);
5111b7daf799SDan McDonald 	} else if (tsl != NULL) {
5112b7daf799SDan McDonald 		/* Generate sadb_sens_t from ACQUIRE source. */
5113b7daf799SDan McDonald 		sadb_sens_from_label((sadb_sens_t *)cur, SADB_EXT_SENSITIVITY,
5114b7daf799SDan McDonald 		    tsl, senslen);
5115b7daf799SDan McDonald 	}
5116b7daf799SDan McDonald #ifdef DEBUG
5117b7daf799SDan McDonald 	else {
5118b7daf799SDan McDonald 		ASSERT(senslen == 0);
5119b7daf799SDan McDonald 	}
5120b7daf799SDan McDonald #endif /* DEBUG */
5121b7daf799SDan McDonald 	cur += senslen;
5122b7daf799SDan McDonald 	mp->b_wptr = cur;
5123bd670b35SErik Nordmark 
5124b7daf799SDan McDonald 	return (mp);
5125b7daf799SDan McDonald }
5126bd670b35SErik Nordmark 
5127b7daf799SDan McDonald /*
5128b7daf799SDan McDonald  * Generate a regular ACQUIRE's proposal extension and KMC information..
5129b7daf799SDan McDonald  */
5130b7daf799SDan McDonald static mblk_t *
5131b7daf799SDan McDonald sadb_acquire_prop(ipsec_action_t *ap, netstack_t *ns, boolean_t do_esp)
5132b7daf799SDan McDonald {
5133b7daf799SDan McDonald 	ipsec_stack_t *ipss = ns->netstack_ipsec;
5134b7daf799SDan McDonald 	ipsecesp_stack_t *espstack = ns->netstack_ipsecesp;
5135b7daf799SDan McDonald 	ipsecah_stack_t *ahstack = ns->netstack_ipsecah;
5136b7daf799SDan McDonald 	mblk_t *mp = NULL;
5137b7daf799SDan McDonald 	sadb_prop_t *prop;
5138b7daf799SDan McDonald 	sadb_comb_t *comb;
5139b7daf799SDan McDonald 	ipsec_action_t *walker;
5140b7daf799SDan McDonald 	int ncombs, allocsize, ealgid, aalgid, aminbits, amaxbits, eminbits,
5141351128adSJason King 	    emaxbits, esaltlen, replay;
5142b7daf799SDan McDonald 	uint64_t softbytes, hardbytes, softaddtime, hardaddtime, softusetime,
5143b7daf799SDan McDonald 	    hardusetime;
5144f4a6f97eSDan McDonald 	uint64_t kmc = 0;
5145f4a6f97eSDan McDonald 	uint32_t kmp = 0;
5146b7daf799SDan McDonald 
5147b7daf799SDan McDonald 	/*
5148b7daf799SDan McDonald 	 * Since it's an rwlock read, AND writing to the IPsec algorithms is
5149b7daf799SDan McDonald 	 * rare, just acquire it once up top, and drop it upon return.
5150b7daf799SDan McDonald 	 */
5151b7daf799SDan McDonald 	rw_enter(&ipss->ipsec_alg_lock, RW_READER);
5152b7daf799SDan McDonald 	if (do_esp) {
5153b7daf799SDan McDonald 		uint64_t num_aalgs, num_ealgs;
5154b7daf799SDan McDonald 
5155b7daf799SDan McDonald 		if (espstack->esp_kstats == NULL)
5156b7daf799SDan McDonald 			goto bail;
5157b7daf799SDan McDonald 
5158b7daf799SDan McDonald 		num_aalgs = ipss->ipsec_nalgs[IPSEC_ALG_AUTH];
5159b7daf799SDan McDonald 		num_ealgs = ipss->ipsec_nalgs[IPSEC_ALG_ENCR];
5160b7daf799SDan McDonald 		if (num_ealgs == 0)
5161b7daf799SDan McDonald 			goto bail;	/* IPsec not loaded yet, apparently. */
5162b7daf799SDan McDonald 		num_aalgs++;	/* No-auth or self-auth-crypto ESP. */
5163b7daf799SDan McDonald 
5164b7daf799SDan McDonald 		/* Use netstack's maximum loaded algorithms... */
5165b7daf799SDan McDonald 		ncombs = num_ealgs * num_aalgs;
5166b7daf799SDan McDonald 		replay =  espstack->ipsecesp_replay_size;
5167b7daf799SDan McDonald 	} else {
5168b7daf799SDan McDonald 		if (ahstack->ah_kstats == NULL)
5169b7daf799SDan McDonald 			goto bail;
5170b7daf799SDan McDonald 
5171b7daf799SDan McDonald 		ncombs = ipss->ipsec_nalgs[IPSEC_ALG_AUTH];
5172b7daf799SDan McDonald 
5173b7daf799SDan McDonald 		if (ncombs == 0)
5174b7daf799SDan McDonald 			goto bail;	/* IPsec not loaded yet, apparently. */
5175b7daf799SDan McDonald 		replay =  ahstack->ipsecah_replay_size;
5176b7daf799SDan McDonald 	}
5177b7daf799SDan McDonald 
5178b7daf799SDan McDonald 	allocsize = sizeof (*prop) + ncombs * sizeof (*comb) +
5179b7daf799SDan McDonald 	    sizeof (sadb_x_kmc_t);
5180b7daf799SDan McDonald 	mp = allocb(allocsize, BPRI_HI);
5181b7daf799SDan McDonald 	if (mp == NULL)
5182b7daf799SDan McDonald 		goto bail;
5183b7daf799SDan McDonald 	prop = (sadb_prop_t *)mp->b_rptr;
5184b7daf799SDan McDonald 	mp->b_wptr += sizeof (*prop);
5185b7daf799SDan McDonald 	comb = (sadb_comb_t *)mp->b_wptr;
5186b7daf799SDan McDonald 	/* Decrement allocsize, if it goes to or below 0, stop. */
5187b7daf799SDan McDonald 	allocsize -= sizeof (*prop);
5188b7daf799SDan McDonald 	prop->sadb_prop_exttype = SADB_EXT_PROPOSAL;
5189b7daf799SDan McDonald 	prop->sadb_prop_len = SADB_8TO64(sizeof (*prop));
5190b7daf799SDan McDonald 	*(uint32_t *)(&prop->sadb_prop_replay) = 0;	/* Quick zero-out! */
5191b7daf799SDan McDonald 	prop->sadb_prop_replay = replay;
5192b7daf799SDan McDonald 
5193b7daf799SDan McDonald 	/*
5194b7daf799SDan McDonald 	 * Based upon algorithm properties, and what-not, prioritize a
5195b7daf799SDan McDonald 	 * proposal, based on the ordering of the ESP algorithms in the
5196b7daf799SDan McDonald 	 * alternatives in the policy rule or socket that was placed
5197b7daf799SDan McDonald 	 * in the acquire record.
5198b7daf799SDan McDonald 	 *
5199b7daf799SDan McDonald 	 * For each action in policy list
5200b7daf799SDan McDonald 	 *   Add combination.
5201b7daf799SDan McDonald 	 *   I should not hit it, but if I've hit limit, return.
5202b7daf799SDan McDonald 	 */
5203b7daf799SDan McDonald 
5204b7daf799SDan McDonald 	for (walker = ap; walker != NULL; walker = walker->ipa_next) {
5205b7daf799SDan McDonald 		ipsec_alginfo_t *ealg, *aalg;
5206b7daf799SDan McDonald 		ipsec_prot_t *prot;
5207b7daf799SDan McDonald 
5208b7daf799SDan McDonald 		if (walker->ipa_act.ipa_type != IPSEC_POLICY_APPLY)
5209b7daf799SDan McDonald 			continue;
5210b7daf799SDan McDonald 
5211b7daf799SDan McDonald 		prot = &walker->ipa_act.ipa_apply;
5212b7daf799SDan McDonald 		if (walker->ipa_act.ipa_apply.ipp_km_proto != 0)
5213b7daf799SDan McDonald 			kmp = walker->ipa_act.ipa_apply.ipp_km_proto;
5214b7daf799SDan McDonald 		if (walker->ipa_act.ipa_apply.ipp_km_cookie != 0)
5215b7daf799SDan McDonald 			kmc = walker->ipa_act.ipa_apply.ipp_km_cookie;
5216b7daf799SDan McDonald 		if (walker->ipa_act.ipa_apply.ipp_replay_depth) {
5217b7daf799SDan McDonald 			prop->sadb_prop_replay =
5218b7daf799SDan McDonald 			    walker->ipa_act.ipa_apply.ipp_replay_depth;
5219bd670b35SErik Nordmark 		}
52207c478bd9Sstevel@tonic-gate 
5221b7daf799SDan McDonald 		if (do_esp) {
5222b7daf799SDan McDonald 			if (!prot->ipp_use_esp)
5223b7daf799SDan McDonald 				continue;
5224b7daf799SDan McDonald 
5225b7daf799SDan McDonald 			if (prot->ipp_esp_auth_alg != 0) {
5226b7daf799SDan McDonald 				aalg = ipss->ipsec_alglists[IPSEC_ALG_AUTH]
5227b7daf799SDan McDonald 				    [prot->ipp_esp_auth_alg];
5228b7daf799SDan McDonald 				if (aalg == NULL || !ALG_VALID(aalg))
5229b7daf799SDan McDonald 					continue;
5230b7daf799SDan McDonald 			} else
5231b7daf799SDan McDonald 				aalg = NULL;
5232b7daf799SDan McDonald 
5233b7daf799SDan McDonald 			ASSERT(prot->ipp_encr_alg > 0);
5234b7daf799SDan McDonald 			ealg = ipss->ipsec_alglists[IPSEC_ALG_ENCR]
5235b7daf799SDan McDonald 			    [prot->ipp_encr_alg];
5236b7daf799SDan McDonald 			if (ealg == NULL || !ALG_VALID(ealg))
5237b7daf799SDan McDonald 				continue;
5238b7daf799SDan McDonald 
5239b7daf799SDan McDonald 			/*
5240b7daf799SDan McDonald 			 * These may want to come from policy rule..
5241b7daf799SDan McDonald 			 */
5242b7daf799SDan McDonald 			softbytes = espstack->ipsecesp_default_soft_bytes;
5243b7daf799SDan McDonald 			hardbytes = espstack->ipsecesp_default_hard_bytes;
5244b7daf799SDan McDonald 			softaddtime = espstack->ipsecesp_default_soft_addtime;
5245b7daf799SDan McDonald 			hardaddtime = espstack->ipsecesp_default_hard_addtime;
5246b7daf799SDan McDonald 			softusetime = espstack->ipsecesp_default_soft_usetime;
5247b7daf799SDan McDonald 			hardusetime = espstack->ipsecesp_default_hard_usetime;
52488810c16bSdanmcd 		} else {
5249b7daf799SDan McDonald 			if (!prot->ipp_use_ah)
5250b7daf799SDan McDonald 				continue;
5251b7daf799SDan McDonald 			ealg = NULL;
5252b7daf799SDan McDonald 			aalg = ipss->ipsec_alglists[IPSEC_ALG_AUTH]
5253b7daf799SDan McDonald 			    [prot->ipp_auth_alg];
5254b7daf799SDan McDonald 			if (aalg == NULL || !ALG_VALID(aalg))
5255b7daf799SDan McDonald 				continue;
52565d3b8cb7SBill Sommerfeld 
5257b7daf799SDan McDonald 			/*
5258b7daf799SDan McDonald 			 * These may want to come from policy rule..
5259b7daf799SDan McDonald 			 */
5260b7daf799SDan McDonald 			softbytes = ahstack->ipsecah_default_soft_bytes;
5261b7daf799SDan McDonald 			hardbytes = ahstack->ipsecah_default_hard_bytes;
5262b7daf799SDan McDonald 			softaddtime = ahstack->ipsecah_default_soft_addtime;
5263b7daf799SDan McDonald 			hardaddtime = ahstack->ipsecah_default_hard_addtime;
5264b7daf799SDan McDonald 			softusetime = ahstack->ipsecah_default_soft_usetime;
5265b7daf799SDan McDonald 			hardusetime = ahstack->ipsecah_default_hard_usetime;
52665d3b8cb7SBill Sommerfeld 		}
52677c478bd9Sstevel@tonic-gate 
5268b7daf799SDan McDonald 		if (ealg == NULL) {
5269351128adSJason King 			ealgid = eminbits = emaxbits = esaltlen = 0;
5270b7daf799SDan McDonald 		} else {
5271b7daf799SDan McDonald 			ealgid = ealg->alg_id;
5272b7daf799SDan McDonald 			eminbits =
5273b7daf799SDan McDonald 			    MAX(prot->ipp_espe_minbits, ealg->alg_ef_minbits);
5274b7daf799SDan McDonald 			emaxbits =
5275b7daf799SDan McDonald 			    MIN(prot->ipp_espe_maxbits, ealg->alg_ef_maxbits);
5276351128adSJason King 			esaltlen = ealg->alg_saltlen;
5277b7daf799SDan McDonald 		}
5278bd670b35SErik Nordmark 
5279b7daf799SDan McDonald 		if (aalg == NULL) {
5280b7daf799SDan McDonald 			aalgid = aminbits = amaxbits = 0;
5281bffb04cfSmarkfen 		} else {
5282b7daf799SDan McDonald 			aalgid = aalg->alg_id;
5283b7daf799SDan McDonald 			aminbits = MAX(prot->ipp_espa_minbits,
5284b7daf799SDan McDonald 			    aalg->alg_ef_minbits);
5285b7daf799SDan McDonald 			amaxbits = MIN(prot->ipp_espa_maxbits,
5286b7daf799SDan McDonald 			    aalg->alg_ef_maxbits);
5287b7daf799SDan McDonald 		}
5288b7daf799SDan McDonald 
5289b7daf799SDan McDonald 		comb->sadb_comb_flags = 0;
5290b7daf799SDan McDonald 		comb->sadb_comb_reserved = 0;
5291b7daf799SDan McDonald 		comb->sadb_comb_encrypt = ealgid;
5292b7daf799SDan McDonald 		comb->sadb_comb_encrypt_minbits = eminbits;
5293b7daf799SDan McDonald 		comb->sadb_comb_encrypt_maxbits = emaxbits;
5294351128adSJason King 		comb->sadb_x_comb_encrypt_saltbits = SADB_8TO1(esaltlen);
5295b7daf799SDan McDonald 		comb->sadb_comb_auth = aalgid;
5296b7daf799SDan McDonald 		comb->sadb_comb_auth_minbits = aminbits;
5297b7daf799SDan McDonald 		comb->sadb_comb_auth_maxbits = amaxbits;
5298b7daf799SDan McDonald 		comb->sadb_comb_soft_allocations = 0;
5299b7daf799SDan McDonald 		comb->sadb_comb_hard_allocations = 0;
5300b7daf799SDan McDonald 		comb->sadb_comb_soft_bytes = softbytes;
5301b7daf799SDan McDonald 		comb->sadb_comb_hard_bytes = hardbytes;
5302b7daf799SDan McDonald 		comb->sadb_comb_soft_addtime = softaddtime;
5303b7daf799SDan McDonald 		comb->sadb_comb_hard_addtime = hardaddtime;
5304b7daf799SDan McDonald 		comb->sadb_comb_soft_usetime = softusetime;
5305b7daf799SDan McDonald 		comb->sadb_comb_hard_usetime = hardusetime;
5306b7daf799SDan McDonald 
5307b7daf799SDan McDonald 		prop->sadb_prop_len += SADB_8TO64(sizeof (*comb));
5308b7daf799SDan McDonald 		mp->b_wptr += sizeof (*comb);
5309b7daf799SDan McDonald 		allocsize -= sizeof (*comb);
5310b7daf799SDan McDonald 		/* Should never dip BELOW sizeof (KM cookie extension). */
5311b7daf799SDan McDonald 		ASSERT3S(allocsize, >=, sizeof (sadb_x_kmc_t));
5312b7daf799SDan McDonald 		if (allocsize <= sizeof (sadb_x_kmc_t))
5313b7daf799SDan McDonald 			break;	/* out of space.. */
5314b7daf799SDan McDonald 		comb++;
5315b7daf799SDan McDonald 	}
5316b7daf799SDan McDonald 
5317b7daf799SDan McDonald 	/* Don't include KMC extension if there's no room. */
5318b7daf799SDan McDonald 	if (((kmp != 0) || (kmc != 0)) && allocsize >= sizeof (sadb_x_kmc_t)) {
5319b7daf799SDan McDonald 		if (sadb_make_kmc_ext(mp->b_wptr,
5320b7daf799SDan McDonald 		    mp->b_wptr + sizeof (sadb_x_kmc_t), kmp, kmc) == NULL) {
5321b7daf799SDan McDonald 			freeb(mp);
5322b7daf799SDan McDonald 			mp = NULL;
5323b7daf799SDan McDonald 			goto bail;
53247c478bd9Sstevel@tonic-gate 		}
5325b7daf799SDan McDonald 		mp->b_wptr += sizeof (sadb_x_kmc_t);
5326b7daf799SDan McDonald 		prop->sadb_prop_len += SADB_8TO64(sizeof (sadb_x_kmc_t));
53277c478bd9Sstevel@tonic-gate 	}
53287c478bd9Sstevel@tonic-gate 
5329b7daf799SDan McDonald bail:
5330b7daf799SDan McDonald 	rw_exit(&ipss->ipsec_alg_lock);
5331b7daf799SDan McDonald 	return (mp);
5332b7daf799SDan McDonald }
53337c478bd9Sstevel@tonic-gate 
5334b7daf799SDan McDonald /*
5335b7daf799SDan McDonald  * Generate an extended ACQUIRE's extended-proposal extension.
5336b7daf799SDan McDonald  */
5337b7daf799SDan McDonald static mblk_t *
5338b7daf799SDan McDonald sadb_acquire_extended_prop(ipsec_action_t *ap, netstack_t *ns)
5339b7daf799SDan McDonald {
5340b7daf799SDan McDonald 	sadb_prop_t *eprop;
5341b7daf799SDan McDonald 	uint8_t *cur, *end;
5342b7daf799SDan McDonald 	mblk_t *mp;
5343b7daf799SDan McDonald 	int allocsize, numecombs = 0, numalgdescs = 0;
5344f4a6f97eSDan McDonald 	uint32_t kmp = 0, replay = 0;
5345f4a6f97eSDan McDonald 	uint64_t kmc = 0;
5346b7daf799SDan McDonald 	ipsec_action_t *walker;
53477c478bd9Sstevel@tonic-gate 
5348b7daf799SDan McDonald 	allocsize = sizeof (*eprop);
53497c478bd9Sstevel@tonic-gate 
53505d3b8cb7SBill Sommerfeld 	/*
5351b7daf799SDan McDonald 	 * Going to walk through the action list twice.  Once for allocation
5352b7daf799SDan McDonald 	 * measurement, and once for actual construction.
53535d3b8cb7SBill Sommerfeld 	 */
5354b7daf799SDan McDonald 	for (walker = ap; walker != NULL; walker = walker->ipa_next) {
5355b7daf799SDan McDonald 		ipsec_prot_t *ipp;
53565d3b8cb7SBill Sommerfeld 
53577c478bd9Sstevel@tonic-gate 		/*
5358b7daf799SDan McDonald 		 * Skip non-IPsec policies
53597c478bd9Sstevel@tonic-gate 		 */
5360b7daf799SDan McDonald 		if (walker->ipa_act.ipa_type != IPSEC_ACT_APPLY)
5361b7daf799SDan McDonald 			continue;
5362b7daf799SDan McDonald 
5363b7daf799SDan McDonald 		ipp = &walker->ipa_act.ipa_apply;
53647c478bd9Sstevel@tonic-gate 
5365b7daf799SDan McDonald 		if (walker->ipa_act.ipa_apply.ipp_km_proto)
5366b7daf799SDan McDonald 			kmp = ipp->ipp_km_proto;
5367b7daf799SDan McDonald 		if (walker->ipa_act.ipa_apply.ipp_km_cookie)
5368b7daf799SDan McDonald 			kmc = ipp->ipp_km_cookie;
5369b7daf799SDan McDonald 		if (walker->ipa_act.ipa_apply.ipp_replay_depth)
5370b7daf799SDan McDonald 			replay = ipp->ipp_replay_depth;
5371b7daf799SDan McDonald 
5372b7daf799SDan McDonald 		if (ipp->ipp_use_ah)
5373b7daf799SDan McDonald 			numalgdescs++;
5374b7daf799SDan McDonald 		if (ipp->ipp_use_esp) {
5375b7daf799SDan McDonald 			numalgdescs++;
5376b7daf799SDan McDonald 			if (ipp->ipp_use_espa)
5377b7daf799SDan McDonald 				numalgdescs++;
53788810c16bSdanmcd 		}
5379b7daf799SDan McDonald 
5380b7daf799SDan McDonald 		numecombs++;
53815d3b8cb7SBill Sommerfeld 	}
5382b7daf799SDan McDonald 	ASSERT(numecombs > 0);
53838810c16bSdanmcd 
5384b7daf799SDan McDonald 	allocsize += numecombs * sizeof (sadb_x_ecomb_t) +
5385b7daf799SDan McDonald 	    numalgdescs * sizeof (sadb_x_algdesc_t) + sizeof (sadb_x_kmc_t);
5386b7daf799SDan McDonald 	mp = allocb(allocsize, BPRI_HI);
5387b7daf799SDan McDonald 	if (mp == NULL)
5388b7daf799SDan McDonald 		return (NULL);
5389b7daf799SDan McDonald 	eprop = (sadb_prop_t *)mp->b_rptr;
5390b7daf799SDan McDonald 	end = mp->b_rptr + allocsize;
5391b7daf799SDan McDonald 	cur = mp->b_rptr + sizeof (*eprop);
53925d3b8cb7SBill Sommerfeld 
5393b7daf799SDan McDonald 	eprop->sadb_prop_exttype = SADB_X_EXT_EPROP;
5394b7daf799SDan McDonald 	eprop->sadb_x_prop_ereserved = 0;
5395b7daf799SDan McDonald 	eprop->sadb_x_prop_numecombs = 0;
5396b7daf799SDan McDonald 	*(uint32_t *)(&eprop->sadb_prop_replay) = 0;	/* Quick zero-out! */
5397b7daf799SDan McDonald 	/* Pick ESP's replay default if need be. */
5398b7daf799SDan McDonald 	eprop->sadb_prop_replay = (replay == 0) ?
5399b7daf799SDan McDonald 	    ns->netstack_ipsecesp->ipsecesp_replay_size : replay;
54005d3b8cb7SBill Sommerfeld 
5401b7daf799SDan McDonald 	/* This time, walk through and actually allocate. */
5402b7daf799SDan McDonald 	for (walker = ap; walker != NULL; walker = walker->ipa_next) {
5403b7daf799SDan McDonald 		/*
5404b7daf799SDan McDonald 		 * Skip non-IPsec policies
5405b7daf799SDan McDonald 		 */
5406b7daf799SDan McDonald 		if (walker->ipa_act.ipa_type != IPSEC_ACT_APPLY)
5407b7daf799SDan McDonald 			continue;
5408b7daf799SDan McDonald 		cur = sadb_action_to_ecomb(cur, end, walker, ns);
5409b7daf799SDan McDonald 		if (cur == NULL) {
5410b7daf799SDan McDonald 			/* NOTE: inverse-ACQUIRE should note this as ENOMEM. */
5411b7daf799SDan McDonald 			freeb(mp);
5412b7daf799SDan McDonald 			return (NULL);
5413b7daf799SDan McDonald 		}
5414b7daf799SDan McDonald 		eprop->sadb_x_prop_numecombs++;
54155d3b8cb7SBill Sommerfeld 	}
54167c478bd9Sstevel@tonic-gate 
5417b7daf799SDan McDonald 	ASSERT(end - cur >= sizeof (sadb_x_kmc_t));
5418b7daf799SDan McDonald 	if ((kmp != 0) || (kmc != 0)) {
5419b7daf799SDan McDonald 		cur = sadb_make_kmc_ext(cur, end, kmp, kmc);
5420b7daf799SDan McDonald 		if (cur == NULL) {
5421b7daf799SDan McDonald 			freeb(mp);
5422b7daf799SDan McDonald 			return (NULL);
5423b7daf799SDan McDonald 		}
5424b7daf799SDan McDonald 	}
5425b7daf799SDan McDonald 	mp->b_wptr = cur;
5426b7daf799SDan McDonald 	eprop->sadb_prop_len = SADB_8TO64(cur - mp->b_rptr);
54275d3b8cb7SBill Sommerfeld 
5428b7daf799SDan McDonald 	return (mp);
54297c478bd9Sstevel@tonic-gate }
54307c478bd9Sstevel@tonic-gate 
54317c478bd9Sstevel@tonic-gate /*
5432b7daf799SDan McDonald  * For this mblk, insert a new acquire record.  Assume bucket contains addrs
5433b7daf799SDan McDonald  * of all of the same length.  Give up (and drop) if memory
5434b7daf799SDan McDonald  * cannot be allocated for a new one; otherwise, invoke callback to
5435b7daf799SDan McDonald  * send the acquire up..
5436b7daf799SDan McDonald  *
5437b7daf799SDan McDonald  * In cases where we need both AH and ESP, add the SA to the ESP ACQUIRE
5438b7daf799SDan McDonald  * list.  The ah_add_sa_finish() routines can look at the packet's attached
5439b7daf799SDan McDonald  * attributes and handle this case specially.
54407c478bd9Sstevel@tonic-gate  */
54417c478bd9Sstevel@tonic-gate void
5442b7daf799SDan McDonald sadb_acquire(mblk_t *datamp, ip_xmit_attr_t *ixa, boolean_t need_ah,
5443b7daf799SDan McDonald     boolean_t need_esp)
54447c478bd9Sstevel@tonic-gate {
5445b7daf799SDan McDonald 	mblk_t	*asyncmp, *regular, *extended, *common, *prop, *eprop;
5446b7daf799SDan McDonald 	sadbp_t *spp;
5447b7daf799SDan McDonald 	sadb_t *sp;
5448b7daf799SDan McDonald 	ipsacq_t *newbie;
5449b7daf799SDan McDonald 	iacqf_t *bucket;
5450b7daf799SDan McDonald 	ipha_t *ipha = (ipha_t *)datamp->b_rptr;
5451b7daf799SDan McDonald 	ip6_t *ip6h = (ip6_t *)datamp->b_rptr;
5452b7daf799SDan McDonald 	uint32_t *src, *dst, *isrc, *idst;
5453b7daf799SDan McDonald 	ipsec_policy_t *pp = ixa->ixa_ipsec_policy;
5454b7daf799SDan McDonald 	ipsec_action_t *ap = ixa->ixa_ipsec_action;
5455b7daf799SDan McDonald 	sa_family_t af;
5456b7daf799SDan McDonald 	int hashoffset;
5457b7daf799SDan McDonald 	uint32_t seq;
5458b7daf799SDan McDonald 	uint64_t unique_id = 0;
5459b7daf799SDan McDonald 	boolean_t tunnel_mode = (ixa->ixa_flags & IXAF_IPSEC_TUNNEL) != 0;
5460a23b3b1bSToomas Soome 	ts_label_t	*tsl;
5461b7daf799SDan McDonald 	netstack_t	*ns = ixa->ixa_ipst->ips_netstack;
5462f4b3ec61Sdh 	ipsec_stack_t	*ipss = ns->netstack_ipsec;
5463b7daf799SDan McDonald 	ipsecesp_stack_t *espstack = ns->netstack_ipsecesp;
5464b7daf799SDan McDonald 	ipsecah_stack_t	*ahstack = ns->netstack_ipsecah;
5465b7daf799SDan McDonald 	ipsec_selector_t sel;
5466b7daf799SDan McDonald 	queue_t *q;
54677c478bd9Sstevel@tonic-gate 
5468b7daf799SDan McDonald 	ASSERT((pp != NULL) || (ap != NULL));
54697c478bd9Sstevel@tonic-gate 
5470b7daf799SDan McDonald 	ASSERT(need_ah || need_esp);
54717c478bd9Sstevel@tonic-gate 
5472b7daf799SDan McDonald 	/* Assign sadb pointers */
5473b7daf799SDan McDonald 	if (need_esp) {
5474b7daf799SDan McDonald 		/*
5475b7daf799SDan McDonald 		 * ESP happens first if we need both AH and ESP.
5476b7daf799SDan McDonald 		 */
5477b7daf799SDan McDonald 		spp = &espstack->esp_sadb;
5478b7daf799SDan McDonald 	} else {
5479b7daf799SDan McDonald 		spp = &ahstack->ah_sadb;
54805d3b8cb7SBill Sommerfeld 	}
5481b7daf799SDan McDonald 	sp = (ixa->ixa_flags & IXAF_IS_IPV4) ? &spp->s_v4 : &spp->s_v6;
5482b7daf799SDan McDonald 
5483b7daf799SDan McDonald 	if (is_system_labeled())
5484b7daf799SDan McDonald 		tsl = ixa->ixa_tsl;
5485b7daf799SDan McDonald 	else
5486b7daf799SDan McDonald 		tsl = NULL;
5487b7daf799SDan McDonald 
5488b7daf799SDan McDonald 	if (ap == NULL)
5489b7daf799SDan McDonald 		ap = pp->ipsp_act;
5490b7daf799SDan McDonald 	ASSERT(ap != NULL);
5491b7daf799SDan McDonald 
5492b7daf799SDan McDonald 	if (ap->ipa_act.ipa_apply.ipp_use_unique || tunnel_mode)
5493b7daf799SDan McDonald 		unique_id = SA_FORM_UNIQUE_ID(ixa);
54945d3b8cb7SBill Sommerfeld 
54957c478bd9Sstevel@tonic-gate 	/*
5496b7daf799SDan McDonald 	 * Set up an ACQUIRE record.
54977c478bd9Sstevel@tonic-gate 	 *
5498b7daf799SDan McDonald 	 * Immediately, make sure the ACQUIRE sequence number doesn't slip
5499b7daf799SDan McDonald 	 * below the lowest point allowed in the kernel.  (In other words,
5500b7daf799SDan McDonald 	 * make sure the high bit on the sequence number is set.)
55017c478bd9Sstevel@tonic-gate 	 */
55027c478bd9Sstevel@tonic-gate 
5503b7daf799SDan McDonald 	seq = keysock_next_seq(ns) | IACQF_LOWEST_SEQ;
55047c478bd9Sstevel@tonic-gate 
5505b7daf799SDan McDonald 	if (IPH_HDR_VERSION(ipha) == IP_VERSION) {
5506b7daf799SDan McDonald 		src = (uint32_t *)&ipha->ipha_src;
5507b7daf799SDan McDonald 		dst = (uint32_t *)&ipha->ipha_dst;
5508b7daf799SDan McDonald 		af = AF_INET;
5509b7daf799SDan McDonald 		hashoffset = OUTBOUND_HASH_V4(sp, ipha->ipha_dst);
5510b7daf799SDan McDonald 		ASSERT(ixa->ixa_flags & IXAF_IS_IPV4);
5511b7daf799SDan McDonald 	} else {
5512b7daf799SDan McDonald 		ASSERT(IPH_HDR_VERSION(ipha) == IPV6_VERSION);
5513b7daf799SDan McDonald 		src = (uint32_t *)&ip6h->ip6_src;
5514b7daf799SDan McDonald 		dst = (uint32_t *)&ip6h->ip6_dst;
5515b7daf799SDan McDonald 		af = AF_INET6;
5516b7daf799SDan McDonald 		hashoffset = OUTBOUND_HASH_V6(sp, ip6h->ip6_dst);
5517b7daf799SDan McDonald 		ASSERT(!(ixa->ixa_flags & IXAF_IS_IPV4));
5518b7daf799SDan McDonald 	}
55197c478bd9Sstevel@tonic-gate 
5520b7daf799SDan McDonald 	if (tunnel_mode) {
5521b7daf799SDan McDonald 		if (pp == NULL) {
5522b7daf799SDan McDonald 			/*
5523b7daf799SDan McDonald 			 * Tunnel mode with no policy pointer means this is a
5524b7daf799SDan McDonald 			 * reflected ICMP (like a ECHO REQUEST) that came in
5525b7daf799SDan McDonald 			 * with self-encapsulated protection.  Until we better
5526b7daf799SDan McDonald 			 * support this, drop the packet.
5527b7daf799SDan McDonald 			 */
5528b7daf799SDan McDonald 			ip_drop_packet(datamp, B_FALSE, NULL,
5529b7daf799SDan McDonald 			    DROPPER(ipss, ipds_spd_got_selfencap),
5530b7daf799SDan McDonald 			    &ipss->ipsec_spd_dropper);
5531b7daf799SDan McDonald 			return;
5532b7daf799SDan McDonald 		}
5533b7daf799SDan McDonald 		/* Snag inner addresses. */
5534b7daf799SDan McDonald 		isrc = ixa->ixa_ipsec_insrc;
5535b7daf799SDan McDonald 		idst = ixa->ixa_ipsec_indst;
5536b7daf799SDan McDonald 	} else {
5537b7daf799SDan McDonald 		isrc = idst = NULL;
5538b7daf799SDan McDonald 	}
5539fb87b5d2Ssommerfe 
5540b7daf799SDan McDonald 	/*
5541b7daf799SDan McDonald 	 * Check buckets to see if there is an existing entry.  If so,
5542b7daf799SDan McDonald 	 * grab it.  sadb_checkacquire locks newbie if found.
5543b7daf799SDan McDonald 	 */
5544b7daf799SDan McDonald 	bucket = &(sp->sdb_acq[hashoffset]);
5545b7daf799SDan McDonald 	mutex_enter(&bucket->iacqf_lock);
5546b7daf799SDan McDonald 	newbie = sadb_checkacquire(bucket, ap, pp, src, dst, isrc, idst,
5547b7daf799SDan McDonald 	    unique_id, tsl);
55487c478bd9Sstevel@tonic-gate 
5549b7daf799SDan McDonald 	if (newbie == NULL) {
5550b7daf799SDan McDonald 		/*
5551b7daf799SDan McDonald 		 * Otherwise, allocate a new one.
5552b7daf799SDan McDonald 		 */
5553b7daf799SDan McDonald 		newbie = kmem_zalloc(sizeof (*newbie), KM_NOSLEEP);
5554b7daf799SDan McDonald 		if (newbie == NULL) {
5555b7daf799SDan McDonald 			mutex_exit(&bucket->iacqf_lock);
5556b7daf799SDan McDonald 			ip_drop_packet(datamp, B_FALSE, NULL,
5557b7daf799SDan McDonald 			    DROPPER(ipss, ipds_sadb_acquire_nomem),
5558b7daf799SDan McDonald 			    &ipss->ipsec_sadb_dropper);
5559b7daf799SDan McDonald 			return;
5560b7daf799SDan McDonald 		}
5561b7daf799SDan McDonald 		newbie->ipsacq_policy = pp;
5562b7daf799SDan McDonald 		if (pp != NULL) {
5563b7daf799SDan McDonald 			IPPOL_REFHOLD(pp);
5564b7daf799SDan McDonald 		}
5565b7daf799SDan McDonald 		IPACT_REFHOLD(ap);
5566b7daf799SDan McDonald 		newbie->ipsacq_act = ap;
5567b7daf799SDan McDonald 		newbie->ipsacq_linklock = &bucket->iacqf_lock;
5568b7daf799SDan McDonald 		newbie->ipsacq_next = bucket->iacqf_ipsacq;
5569b7daf799SDan McDonald 		newbie->ipsacq_ptpn = &bucket->iacqf_ipsacq;
5570b7daf799SDan McDonald 		if (newbie->ipsacq_next != NULL)
5571b7daf799SDan McDonald 			newbie->ipsacq_next->ipsacq_ptpn = &newbie->ipsacq_next;
55727c478bd9Sstevel@tonic-gate 
5573b7daf799SDan McDonald 		bucket->iacqf_ipsacq = newbie;
5574b7daf799SDan McDonald 		mutex_init(&newbie->ipsacq_lock, NULL, MUTEX_DEFAULT, NULL);
5575b7daf799SDan McDonald 		mutex_enter(&newbie->ipsacq_lock);
5576fb87b5d2Ssommerfe 	}
55777c478bd9Sstevel@tonic-gate 
5578b7daf799SDan McDonald 	/*
5579b7daf799SDan McDonald 	 * XXX MLS does it actually help us to drop the bucket lock here?
5580b7daf799SDan McDonald 	 * we have inserted a half-built, locked acquire record into the
5581b7daf799SDan McDonald 	 * bucket.  any competing thread will now be able to lock the bucket
5582b7daf799SDan McDonald 	 * to scan it, but will immediately pile up on the new acquire
5583b7daf799SDan McDonald 	 * record's lock; I don't think we gain anything here other than to
5584b7daf799SDan McDonald 	 * disperse blame for lock contention.
5585b7daf799SDan McDonald 	 *
5586b7daf799SDan McDonald 	 * we might be able to dispense with acquire record locks entirely..
5587b7daf799SDan McDonald 	 * just use the bucket locks..
5588b7daf799SDan McDonald 	 */
55897845d282Sdanmcd 
5590b7daf799SDan McDonald 	mutex_exit(&bucket->iacqf_lock);
55917c478bd9Sstevel@tonic-gate 
5592b7daf799SDan McDonald 	/*
5593b7daf799SDan McDonald 	 * This assert looks silly for now, but we may need to enter newbie's
5594b7daf799SDan McDonald 	 * mutex during a search.
5595b7daf799SDan McDonald 	 */
5596b7daf799SDan McDonald 	ASSERT(MUTEX_HELD(&newbie->ipsacq_lock));
55977c478bd9Sstevel@tonic-gate 
55987845d282Sdanmcd 	/*
5599b7daf799SDan McDonald 	 * Make the ip_xmit_attr_t into something we can queue.
5600b7daf799SDan McDonald 	 * If no memory it frees datamp.
56017845d282Sdanmcd 	 */
5602b7daf799SDan McDonald 	asyncmp = ip_xmit_attr_to_mblk(ixa);
5603b7daf799SDan McDonald 	if (asyncmp != NULL)
5604b7daf799SDan McDonald 		linkb(asyncmp, datamp);
56057845d282Sdanmcd 
5606b7daf799SDan McDonald 	/* Queue up packet.  Use b_next. */
5607628b0c67SMark Fenwick 
5608b7daf799SDan McDonald 	if (asyncmp == NULL) {
5609b7daf799SDan McDonald 		/* Statistics for allocation failure */
5610b7daf799SDan McDonald 		if (ixa->ixa_flags & IXAF_IS_IPV4) {
5611b7daf799SDan McDonald 			BUMP_MIB(&ixa->ixa_ipst->ips_ip_mib,
5612b7daf799SDan McDonald 			    ipIfStatsOutDiscards);
5613b7daf799SDan McDonald 		} else {
5614b7daf799SDan McDonald 			BUMP_MIB(&ixa->ixa_ipst->ips_ip6_mib,
5615b7daf799SDan McDonald 			    ipIfStatsOutDiscards);
5616b7daf799SDan McDonald 		}
5617b7daf799SDan McDonald 		ip_drop_output("No memory for asyncmp", datamp, NULL);
5618b7daf799SDan McDonald 		freemsg(datamp);
5619b7daf799SDan McDonald 		/*
5620b7daf799SDan McDonald 		 * The acquire record will be freed quickly if it's new
5621b7daf799SDan McDonald 		 * (ipsacq_expire == 0), and will proceed as if no packet
5622b7daf799SDan McDonald 		 * showed up if not.
5623b7daf799SDan McDonald 		 */
5624b7daf799SDan McDonald 		mutex_exit(&newbie->ipsacq_lock);
5625b7daf799SDan McDonald 		return;
5626b7daf799SDan McDonald 	} else if (newbie->ipsacq_numpackets == 0) {
5627b7daf799SDan McDonald 		/* First one. */
5628b7daf799SDan McDonald 		newbie->ipsacq_mp = asyncmp;
5629b7daf799SDan McDonald 		newbie->ipsacq_numpackets = 1;
5630b7daf799SDan McDonald 		newbie->ipsacq_expire = gethrestime_sec();
5631b7daf799SDan McDonald 		/*
5632b7daf799SDan McDonald 		 * Extended ACQUIRE with both AH+ESP will use ESP's timeout
5633b7daf799SDan McDonald 		 * value.
5634b7daf799SDan McDonald 		 */
5635b7daf799SDan McDonald 		newbie->ipsacq_expire += *spp->s_acquire_timeout;
5636b7daf799SDan McDonald 		newbie->ipsacq_seq = seq;
5637b7daf799SDan McDonald 		newbie->ipsacq_addrfam = af;
56387c478bd9Sstevel@tonic-gate 
5639b7daf799SDan McDonald 		newbie->ipsacq_srcport = ixa->ixa_ipsec_src_port;
5640b7daf799SDan McDonald 		newbie->ipsacq_dstport = ixa->ixa_ipsec_dst_port;
5641b7daf799SDan McDonald 		newbie->ipsacq_icmp_type = ixa->ixa_ipsec_icmp_type;
5642b7daf799SDan McDonald 		newbie->ipsacq_icmp_code = ixa->ixa_ipsec_icmp_code;
5643b7daf799SDan McDonald 		if (tunnel_mode) {
5644b7daf799SDan McDonald 			newbie->ipsacq_inneraddrfam = ixa->ixa_ipsec_inaf;
5645b7daf799SDan McDonald 			newbie->ipsacq_proto = ixa->ixa_ipsec_inaf == AF_INET6 ?
5646b7daf799SDan McDonald 			    IPPROTO_IPV6 : IPPROTO_ENCAP;
5647b7daf799SDan McDonald 			newbie->ipsacq_innersrcpfx = ixa->ixa_ipsec_insrcpfx;
5648b7daf799SDan McDonald 			newbie->ipsacq_innerdstpfx = ixa->ixa_ipsec_indstpfx;
5649b7daf799SDan McDonald 			IPSA_COPY_ADDR(newbie->ipsacq_innersrc,
5650b7daf799SDan McDonald 			    ixa->ixa_ipsec_insrc, ixa->ixa_ipsec_inaf);
5651b7daf799SDan McDonald 			IPSA_COPY_ADDR(newbie->ipsacq_innerdst,
5652b7daf799SDan McDonald 			    ixa->ixa_ipsec_indst, ixa->ixa_ipsec_inaf);
5653b7daf799SDan McDonald 		} else {
5654b7daf799SDan McDonald 			newbie->ipsacq_proto = ixa->ixa_ipsec_proto;
5655b7daf799SDan McDonald 		}
5656b7daf799SDan McDonald 		newbie->ipsacq_unique_id = unique_id;
56577c478bd9Sstevel@tonic-gate 
5658b7daf799SDan McDonald 		if (tsl != NULL) {
5659b7daf799SDan McDonald 			label_hold(tsl);
5660b7daf799SDan McDonald 			newbie->ipsacq_tsl = tsl;
5661b7daf799SDan McDonald 		}
5662b7daf799SDan McDonald 	} else {
5663b7daf799SDan McDonald 		/* Scan to the end of the list & insert. */
5664b7daf799SDan McDonald 		mblk_t *lastone = newbie->ipsacq_mp;
56657c478bd9Sstevel@tonic-gate 
5666b7daf799SDan McDonald 		while (lastone->b_next != NULL)
5667b7daf799SDan McDonald 			lastone = lastone->b_next;
5668b7daf799SDan McDonald 		lastone->b_next = asyncmp;
5669b7daf799SDan McDonald 		if (newbie->ipsacq_numpackets++ == ipsacq_maxpackets) {
5670b7daf799SDan McDonald 			newbie->ipsacq_numpackets = ipsacq_maxpackets;
5671b7daf799SDan McDonald 			lastone = newbie->ipsacq_mp;
5672b7daf799SDan McDonald 			newbie->ipsacq_mp = lastone->b_next;
5673b7daf799SDan McDonald 			lastone->b_next = NULL;
56747c478bd9Sstevel@tonic-gate 
5675b7daf799SDan McDonald 			/* Freeing the async message */
5676b7daf799SDan McDonald 			lastone = ip_xmit_attr_free_mblk(lastone);
5677b7daf799SDan McDonald 			ip_drop_packet(lastone, B_FALSE, NULL,
5678b7daf799SDan McDonald 			    DROPPER(ipss, ipds_sadb_acquire_toofull),
5679b7daf799SDan McDonald 			    &ipss->ipsec_sadb_dropper);
5680b7daf799SDan McDonald 		} else {
5681b7daf799SDan McDonald 			IP_ACQUIRE_STAT(ipss, qhiwater,
5682b7daf799SDan McDonald 			    newbie->ipsacq_numpackets);
5683b7daf799SDan McDonald 		}
5684b7daf799SDan McDonald 	}
56857c478bd9Sstevel@tonic-gate 
56867c478bd9Sstevel@tonic-gate 	/*
5687b7daf799SDan McDonald 	 * Reset addresses.  Set them to the most recently added mblk chain,
5688b7daf799SDan McDonald 	 * so that the address pointers in the acquire record will point
5689b7daf799SDan McDonald 	 * at an mblk still attached to the acquire list.
56907c478bd9Sstevel@tonic-gate 	 */
5691b7daf799SDan McDonald 
5692b7daf799SDan McDonald 	newbie->ipsacq_srcaddr = src;
5693b7daf799SDan McDonald 	newbie->ipsacq_dstaddr = dst;
56947c478bd9Sstevel@tonic-gate 
56957c478bd9Sstevel@tonic-gate 	/*
5696b7daf799SDan McDonald 	 * If the acquire record has more than one queued packet, we've
5697b7daf799SDan McDonald 	 * already sent an ACQUIRE, and don't need to repeat ourself.
56987c478bd9Sstevel@tonic-gate 	 */
5699b7daf799SDan McDonald 	if (newbie->ipsacq_seq != seq || newbie->ipsacq_numpackets > 1) {
5700b7daf799SDan McDonald 		/* I have an acquire outstanding already! */
5701b7daf799SDan McDonald 		mutex_exit(&newbie->ipsacq_lock);
5702b7daf799SDan McDonald 		return;
57037c478bd9Sstevel@tonic-gate 	}
57047c478bd9Sstevel@tonic-gate 
5705b7daf799SDan McDonald 	if (need_esp) {
5706b7daf799SDan McDonald 		ESP_BUMP_STAT(espstack, acquire_requests);
5707b7daf799SDan McDonald 		q = espstack->esp_pfkey_q;
5708b7daf799SDan McDonald 	} else {
5709b7daf799SDan McDonald 		/*
5710b7daf799SDan McDonald 		 * Two cases get us here:
5711b7daf799SDan McDonald 		 * 1.) AH-only policy.
5712b7daf799SDan McDonald 		 *
5713b7daf799SDan McDonald 		 * 2.) A continuation of an AH+ESP policy, and this is the
5714b7daf799SDan McDonald 		 * post-ESP, AH-needs-to-send-a-regular-ACQUIRE case.
5715b7daf799SDan McDonald 		 * (i.e. called from esp_do_outbound_ah().)
5716b7daf799SDan McDonald 		 */
5717b7daf799SDan McDonald 		AH_BUMP_STAT(ahstack, acquire_requests);
5718b7daf799SDan McDonald 		q = ahstack->ah_pfkey_q;
57197c478bd9Sstevel@tonic-gate 	}
57207c478bd9Sstevel@tonic-gate 
5721b7daf799SDan McDonald 	/*
5722b7daf799SDan McDonald 	 * Get selectors and other policy-expression bits needed for an
5723b7daf799SDan McDonald 	 * ACQUIRE.
5724b7daf799SDan McDonald 	 */
5725b7daf799SDan McDonald 	bzero(&sel, sizeof (sel));
5726b7daf799SDan McDonald 	sel.ips_isv4 = (ixa->ixa_flags & IXAF_IS_IPV4) != 0;
5727b7daf799SDan McDonald 	if (tunnel_mode) {
5728b7daf799SDan McDonald 		sel.ips_protocol = (ixa->ixa_ipsec_inaf == AF_INET) ?
5729b7daf799SDan McDonald 		    IPPROTO_ENCAP : IPPROTO_IPV6;
5730b7daf799SDan McDonald 	} else {
5731b7daf799SDan McDonald 		sel.ips_protocol = ixa->ixa_ipsec_proto;
5732b7daf799SDan McDonald 		sel.ips_local_port = ixa->ixa_ipsec_src_port;
5733b7daf799SDan McDonald 		sel.ips_remote_port = ixa->ixa_ipsec_dst_port;
5734b7daf799SDan McDonald 	}
5735b7daf799SDan McDonald 	sel.ips_icmp_type = ixa->ixa_ipsec_icmp_type;
5736b7daf799SDan McDonald 	sel.ips_icmp_code = ixa->ixa_ipsec_icmp_code;
5737b7daf799SDan McDonald 	sel.ips_is_icmp_inv_acq = 0;
5738b7daf799SDan McDonald 	if (af == AF_INET) {
5739b7daf799SDan McDonald 		sel.ips_local_addr_v4 = ipha->ipha_src;
5740b7daf799SDan McDonald 		sel.ips_remote_addr_v4 = ipha->ipha_dst;
5741b7daf799SDan McDonald 	} else {
5742b7daf799SDan McDonald 		sel.ips_local_addr_v6 = ip6h->ip6_src;
5743b7daf799SDan McDonald 		sel.ips_remote_addr_v6 = ip6h->ip6_dst;
5744b7daf799SDan McDonald 	}
57455d3b8cb7SBill Sommerfeld 
57465d3b8cb7SBill Sommerfeld 
5747b7daf799SDan McDonald 	/*
5748b7daf799SDan McDonald 	 * 1. Generate addresses, kmc, and sensitivity.  These are "common"
5749b7daf799SDan McDonald 	 * and should be an mblk pointed to by common. TBD -- eventually it
5750b7daf799SDan McDonald 	 * will include triggering packet contents as more address extensions.
5751b7daf799SDan McDonald 	 *
5752b7daf799SDan McDonald 	 * 2. Generate ACQUIRE & KEYSOCK_OUT and single-protocol proposal.
5753b7daf799SDan McDonald 	 * These are "regular" and "prop".  String regular->b_cont->b_cont =
5754b7daf799SDan McDonald 	 * common, common->b_cont = prop.
5755b7daf799SDan McDonald 	 *
5756b7daf799SDan McDonald 	 * 3. If extended register got turned on, generate EXT_ACQUIRE &
5757b7daf799SDan McDonald 	 * KEYSOCK_OUT and multi-protocol eprop. These are "extended" and
5758b7daf799SDan McDonald 	 * "eprop".  String extended->b_cont->b_cont = dupb(common) and
5759b7daf799SDan McDonald 	 * extended->b_cont->b_cont->b_cont = prop.
5760b7daf799SDan McDonald 	 *
5761b7daf799SDan McDonald 	 * 4. Deliver:  putnext(q, regular) and if there, putnext(q, extended).
5762b7daf799SDan McDonald 	 */
57635d3b8cb7SBill Sommerfeld 
5764b7daf799SDan McDonald 	regular = extended = prop = eprop = NULL;
57655d3b8cb7SBill Sommerfeld 
5766b7daf799SDan McDonald 	common = sadb_acquire_msg_common(&sel, pp, ap, tunnel_mode, tsl, NULL);
5767b7daf799SDan McDonald 	if (common == NULL)
5768b7daf799SDan McDonald 		goto bail;
57695d3b8cb7SBill Sommerfeld 
5770b7daf799SDan McDonald 	regular = sadb_acquire_msg_base(0, (need_esp ?
5771b7daf799SDan McDonald 	    SADB_SATYPE_ESP : SADB_SATYPE_AH), newbie->ipsacq_seq, 0);
5772b7daf799SDan McDonald 	if (regular == NULL)
5773b7daf799SDan McDonald 		goto bail;
57745d3b8cb7SBill Sommerfeld 
5775b7daf799SDan McDonald 	/*
5776b7daf799SDan McDonald 	 * Pardon the boolean cleverness. At least one of need_* must be true.
5777b7daf799SDan McDonald 	 * If they are equal, it's an AH & ESP policy and ESP needs to go
5778b7daf799SDan McDonald 	 * first.  If they aren't, just check the contents of need_esp.
5779b7daf799SDan McDonald 	 */
5780b7daf799SDan McDonald 	prop = sadb_acquire_prop(ap, ns, need_esp);
5781b7daf799SDan McDonald 	if (prop == NULL)
5782b7daf799SDan McDonald 		goto bail;
57835d3b8cb7SBill Sommerfeld 
5784b7daf799SDan McDonald 	/* Link the parts together. */
5785b7daf799SDan McDonald 	regular->b_cont->b_cont = common;
5786b7daf799SDan McDonald 	common->b_cont = prop;
5787b7daf799SDan McDonald 	/*
5788b7daf799SDan McDonald 	 * Prop is now linked, so don't freemsg() it if the extended
5789b7daf799SDan McDonald 	 * construction goes off the rails.
5790b7daf799SDan McDonald 	 */
5791b7daf799SDan McDonald 	prop = NULL;
57925d3b8cb7SBill Sommerfeld 
5793b7daf799SDan McDonald 	((sadb_msg_t *)(regular->b_cont->b_rptr))->sadb_msg_len =
5794b7daf799SDan McDonald 	    SADB_8TO64(msgsize(regular->b_cont));
57955d3b8cb7SBill Sommerfeld 
5796b7daf799SDan McDonald 	/*
5797b7daf799SDan McDonald 	 * If we need an extended ACQUIRE, build it here.
5798b7daf799SDan McDonald 	 */
5799b7daf799SDan McDonald 	if (keysock_extended_reg(ns)) {
5800b7daf799SDan McDonald 		/* NOTE: "common" still points to what we need. */
5801b7daf799SDan McDonald 		extended = sadb_acquire_msg_base(0, 0, newbie->ipsacq_seq, 0);
5802b7daf799SDan McDonald 		if (extended == NULL) {
5803b7daf799SDan McDonald 			common = NULL;
5804b7daf799SDan McDonald 			goto bail;
5805b7daf799SDan McDonald 		}
58065d3b8cb7SBill Sommerfeld 
5807b7daf799SDan McDonald 		extended->b_cont->b_cont = dupb(common);
5808b7daf799SDan McDonald 		common = NULL;
5809b7daf799SDan McDonald 		if (extended->b_cont->b_cont == NULL)
5810b7daf799SDan McDonald 			goto bail;
58115d3b8cb7SBill Sommerfeld 
5812b7daf799SDan McDonald 		eprop = sadb_acquire_extended_prop(ap, ns);
5813b7daf799SDan McDonald 		if (eprop == NULL)
5814b7daf799SDan McDonald 			goto bail;
5815b7daf799SDan McDonald 		extended->b_cont->b_cont->b_cont = eprop;
58165d3b8cb7SBill Sommerfeld 
5817b7daf799SDan McDonald 		((sadb_msg_t *)(extended->b_cont->b_rptr))->sadb_msg_len =
5818b7daf799SDan McDonald 		    SADB_8TO64(msgsize(extended->b_cont));
5819b7daf799SDan McDonald 	}
58205d3b8cb7SBill Sommerfeld 
5821b7daf799SDan McDonald 	/* So we don't hold a lock across putnext()... */
5822b7daf799SDan McDonald 	mutex_exit(&newbie->ipsacq_lock);
58235d3b8cb7SBill Sommerfeld 
5824b7daf799SDan McDonald 	if (extended != NULL)
5825b7daf799SDan McDonald 		putnext(q, extended);
5826b7daf799SDan McDonald 	ASSERT(regular != NULL);
5827b7daf799SDan McDonald 	putnext(q, regular);
5828b7daf799SDan McDonald 	return;
58295d3b8cb7SBill Sommerfeld 
5830b7daf799SDan McDonald bail:
5831b7daf799SDan McDonald 	/* Make this acquire record go away quickly... */
5832b7daf799SDan McDonald 	newbie->ipsacq_expire = 0;
5833b7daf799SDan McDonald 	/* Exploit freemsg(NULL) being legal for fun & profit. */
5834b7daf799SDan McDonald 	freemsg(common);
5835b7daf799SDan McDonald 	freemsg(prop);
5836b7daf799SDan McDonald 	freemsg(extended);
5837b7daf799SDan McDonald 	freemsg(regular);
5838b7daf799SDan McDonald 	mutex_exit(&newbie->ipsacq_lock);
58395d3b8cb7SBill Sommerfeld }
58405d3b8cb7SBill Sommerfeld 
58417c478bd9Sstevel@tonic-gate /*
5842b7daf799SDan McDonald  * Unlink and free an acquire record.
58437c478bd9Sstevel@tonic-gate  */
5844b7daf799SDan McDonald void
5845b7daf799SDan McDonald sadb_destroy_acquire(ipsacq_t *acqrec, netstack_t *ns)
58467c478bd9Sstevel@tonic-gate {
5847b7daf799SDan McDonald 	mblk_t		*mp;
5848b7daf799SDan McDonald 	ipsec_stack_t	*ipss = ns->netstack_ipsec;
58497c478bd9Sstevel@tonic-gate 
5850b7daf799SDan McDonald 	ASSERT(MUTEX_HELD(acqrec->ipsacq_linklock));
58517c478bd9Sstevel@tonic-gate 
5852b7daf799SDan McDonald 	if (acqrec->ipsacq_policy != NULL) {
5853b7daf799SDan McDonald 		IPPOL_REFRELE(acqrec->ipsacq_policy);
5854b7daf799SDan McDonald 	}
5855b7daf799SDan McDonald 	if (acqrec->ipsacq_act != NULL) {
5856b7daf799SDan McDonald 		IPACT_REFRELE(acqrec->ipsacq_act);
58577c478bd9Sstevel@tonic-gate 	}
58587c478bd9Sstevel@tonic-gate 
5859b7daf799SDan McDonald 	/* Unlink */
5860b7daf799SDan McDonald 	*(acqrec->ipsacq_ptpn) = acqrec->ipsacq_next;
5861b7daf799SDan McDonald 	if (acqrec->ipsacq_next != NULL)
5862b7daf799SDan McDonald 		acqrec->ipsacq_next->ipsacq_ptpn = acqrec->ipsacq_ptpn;
58637c478bd9Sstevel@tonic-gate 
5864b7daf799SDan McDonald 	if (acqrec->ipsacq_tsl != NULL) {
5865b7daf799SDan McDonald 		label_rele(acqrec->ipsacq_tsl);
5866b7daf799SDan McDonald 		acqrec->ipsacq_tsl = NULL;
5867b7daf799SDan McDonald 	}
58687c478bd9Sstevel@tonic-gate 
5869b7daf799SDan McDonald 	/*
5870b7daf799SDan McDonald 	 * Free hanging mp's.
5871b7daf799SDan McDonald 	 *
5872b7daf799SDan McDonald 	 * XXX Instead of freemsg(), perhaps use IPSEC_REQ_FAILED.
5873b7daf799SDan McDonald 	 */
58747807385bSDan McDonald 
5875b7daf799SDan McDonald 	mutex_enter(&acqrec->ipsacq_lock);
5876b7daf799SDan McDonald 	while (acqrec->ipsacq_mp != NULL) {
5877b7daf799SDan McDonald 		mp = acqrec->ipsacq_mp;
5878b7daf799SDan McDonald 		acqrec->ipsacq_mp = mp->b_next;
5879b7daf799SDan McDonald 		mp->b_next = NULL;
5880b7daf799SDan McDonald 		/* Freeing the async message */
5881b7daf799SDan McDonald 		mp = ip_xmit_attr_free_mblk(mp);
5882b7daf799SDan McDonald 		ip_drop_packet(mp, B_FALSE, NULL,
5883b7daf799SDan McDonald 		    DROPPER(ipss, ipds_sadb_acquire_timeout),
5884b7daf799SDan McDonald 		    &ipss->ipsec_sadb_dropper);
5885b7daf799SDan McDonald 	}
5886b7daf799SDan McDonald 	mutex_exit(&acqrec->ipsacq_lock);
58877c478bd9Sstevel@tonic-gate 
5888b7daf799SDan McDonald 	/* Free */
5889b7daf799SDan McDonald 	mutex_destroy(&acqrec->ipsacq_lock);
5890b7daf799SDan McDonald 	kmem_free(acqrec, sizeof (*acqrec));
5891b7daf799SDan McDonald }
58928810c16bSdanmcd 
5893b7daf799SDan McDonald /*
5894b7daf799SDan McDonald  * Destroy an acquire list fanout.
5895b7daf799SDan McDonald  */
5896b7daf799SDan McDonald static void
5897b7daf799SDan McDonald sadb_destroy_acqlist(iacqf_t **listp, uint_t numentries, boolean_t forever,
5898b7daf799SDan McDonald     netstack_t *ns)
5899b7daf799SDan McDonald {
5900b7daf799SDan McDonald 	int i;
5901b7daf799SDan McDonald 	iacqf_t *list = *listp;
59028810c16bSdanmcd 
5903b7daf799SDan McDonald 	if (list == NULL)
5904b7daf799SDan McDonald 		return;
59057c478bd9Sstevel@tonic-gate 
5906b7daf799SDan McDonald 	for (i = 0; i < numentries; i++) {
5907b7daf799SDan McDonald 		mutex_enter(&(list[i].iacqf_lock));
5908b7daf799SDan McDonald 		while (list[i].iacqf_ipsacq != NULL)
5909b7daf799SDan McDonald 			sadb_destroy_acquire(list[i].iacqf_ipsacq, ns);
5910b7daf799SDan McDonald 		mutex_exit(&(list[i].iacqf_lock));
5911b7daf799SDan McDonald 		if (forever)
5912b7daf799SDan McDonald 			mutex_destroy(&(list[i].iacqf_lock));
59137c478bd9Sstevel@tonic-gate 	}
59147c478bd9Sstevel@tonic-gate 
5915b7daf799SDan McDonald 	if (forever) {
5916b7daf799SDan McDonald 		*listp = NULL;
5917b7daf799SDan McDonald 		kmem_free(list, numentries * sizeof (*list));
59187c478bd9Sstevel@tonic-gate 	}
5919b7daf799SDan McDonald }
59207c478bd9Sstevel@tonic-gate 
5921b7daf799SDan McDonald /*
5922b7daf799SDan McDonald  * Create an algorithm descriptor for an extended ACQUIRE.  Filter crypto
5923b7daf799SDan McDonald  * framework's view of reality vs. IPsec's.  EF's wins, BTW.
5924b7daf799SDan McDonald  */
5925b7daf799SDan McDonald static uint8_t *
5926b7daf799SDan McDonald sadb_new_algdesc(uint8_t *start, uint8_t *limit,
5927b7daf799SDan McDonald     sadb_x_ecomb_t *ecomb, uint8_t satype, uint8_t algtype,
5928b7daf799SDan McDonald     uint8_t alg, uint16_t minbits, uint16_t maxbits, ipsec_stack_t *ipss)
5929b7daf799SDan McDonald {
5930b7daf799SDan McDonald 	uint8_t *cur = start;
5931b7daf799SDan McDonald 	ipsec_alginfo_t *algp;
5932b7daf799SDan McDonald 	sadb_x_algdesc_t *algdesc = (sadb_x_algdesc_t *)cur;
59337c478bd9Sstevel@tonic-gate 
5934b7daf799SDan McDonald 	cur += sizeof (*algdesc);
5935b7daf799SDan McDonald 	if (cur >= limit)
59367c478bd9Sstevel@tonic-gate 		return (NULL);
59375d3b8cb7SBill Sommerfeld 
5938b7daf799SDan McDonald 	ecomb->sadb_x_ecomb_numalgs++;
59395d3b8cb7SBill Sommerfeld 
59407c478bd9Sstevel@tonic-gate 	/*
5941b7daf799SDan McDonald 	 * Normalize vs. crypto framework's limits.  This way, you can specify
5942b7daf799SDan McDonald 	 * a stronger policy, and when the framework loads a stronger version,
5943b7daf799SDan McDonald 	 * you can just keep plowing w/o rewhacking your SPD.
59447c478bd9Sstevel@tonic-gate 	 */
5945b7daf799SDan McDonald 	rw_enter(&ipss->ipsec_alg_lock, RW_READER);
5946b7daf799SDan McDonald 	algp = ipss->ipsec_alglists[(algtype == SADB_X_ALGTYPE_AUTH) ?
5947b7daf799SDan McDonald 	    IPSEC_ALG_AUTH : IPSEC_ALG_ENCR][alg];
5948b7daf799SDan McDonald 	if (algp == NULL) {
5949b7daf799SDan McDonald 		rw_exit(&ipss->ipsec_alg_lock);
5950b7daf799SDan McDonald 		return (NULL);	/* Algorithm doesn't exist.  Fail gracefully. */
59517c478bd9Sstevel@tonic-gate 	}
5952b7daf799SDan McDonald 	if (minbits < algp->alg_ef_minbits)
5953b7daf799SDan McDonald 		minbits = algp->alg_ef_minbits;
5954b7daf799SDan McDonald 	if (maxbits > algp->alg_ef_maxbits)
5955b7daf799SDan McDonald 		maxbits = algp->alg_ef_maxbits;
5956b7daf799SDan McDonald 	rw_exit(&ipss->ipsec_alg_lock);
59577c478bd9Sstevel@tonic-gate 
5958351128adSJason King 	algdesc->sadb_x_algdesc_saltbits = SADB_8TO1(algp->alg_saltlen);
5959b7daf799SDan McDonald 	algdesc->sadb_x_algdesc_satype = satype;
5960b7daf799SDan McDonald 	algdesc->sadb_x_algdesc_algtype = algtype;
5961b7daf799SDan McDonald 	algdesc->sadb_x_algdesc_alg = alg;
5962b7daf799SDan McDonald 	algdesc->sadb_x_algdesc_minbits = minbits;
5963b7daf799SDan McDonald 	algdesc->sadb_x_algdesc_maxbits = maxbits;
5964b7daf799SDan McDonald 
5965b7daf799SDan McDonald 	return (cur);
5966b7daf799SDan McDonald }
5967b7daf799SDan McDonald 
5968b7daf799SDan McDonald /*
5969b7daf799SDan McDonald  * Convert the given ipsec_action_t into an ecomb starting at *ecomb
5970b7daf799SDan McDonald  * which must fit before *limit
5971b7daf799SDan McDonald  *
5972b7daf799SDan McDonald  * return NULL if we ran out of room or a pointer to the end of the ecomb.
5973b7daf799SDan McDonald  */
5974b7daf799SDan McDonald static uint8_t *
5975b7daf799SDan McDonald sadb_action_to_ecomb(uint8_t *start, uint8_t *limit, ipsec_action_t *act,
5976b7daf799SDan McDonald     netstack_t *ns)
5977b7daf799SDan McDonald {
5978b7daf799SDan McDonald 	uint8_t *cur = start;
5979b7daf799SDan McDonald 	sadb_x_ecomb_t *ecomb = (sadb_x_ecomb_t *)cur;
5980b7daf799SDan McDonald 	ipsec_prot_t *ipp;
5981b7daf799SDan McDonald 	ipsec_stack_t *ipss = ns->netstack_ipsec;
59827c478bd9Sstevel@tonic-gate 
5983b7daf799SDan McDonald 	cur += sizeof (*ecomb);
5984b7daf799SDan McDonald 	if (cur >= limit)
5985b7daf799SDan McDonald 		return (NULL);
59867c478bd9Sstevel@tonic-gate 
5987b7daf799SDan McDonald 	ASSERT(act->ipa_act.ipa_type == IPSEC_ACT_APPLY);
59887c478bd9Sstevel@tonic-gate 
5989b7daf799SDan McDonald 	ipp = &act->ipa_act.ipa_apply;
59907c478bd9Sstevel@tonic-gate 
5991b7daf799SDan McDonald 	ecomb->sadb_x_ecomb_numalgs = 0;
5992b7daf799SDan McDonald 	ecomb->sadb_x_ecomb_reserved = 0;
5993b7daf799SDan McDonald 	ecomb->sadb_x_ecomb_reserved2 = 0;
5994b7daf799SDan McDonald 	/*
5995b7daf799SDan McDonald 	 * No limits on allocations, since we really don't support that
5996b7daf799SDan McDonald 	 * concept currently.
5997b7daf799SDan McDonald 	 */
5998b7daf799SDan McDonald 	ecomb->sadb_x_ecomb_soft_allocations = 0;
5999b7daf799SDan McDonald 	ecomb->sadb_x_ecomb_hard_allocations = 0;
60007c478bd9Sstevel@tonic-gate 
6001b7daf799SDan McDonald 	/*
6002b7daf799SDan McDonald 	 * XXX TBD: Policy or global parameters will eventually be
6003b7daf799SDan McDonald 	 * able to fill in some of these.
6004b7daf799SDan McDonald 	 */
6005b7daf799SDan McDonald 	ecomb->sadb_x_ecomb_flags = 0;
6006b7daf799SDan McDonald 	ecomb->sadb_x_ecomb_soft_bytes = 0;
6007b7daf799SDan McDonald 	ecomb->sadb_x_ecomb_hard_bytes = 0;
6008b7daf799SDan McDonald 	ecomb->sadb_x_ecomb_soft_addtime = 0;
6009b7daf799SDan McDonald 	ecomb->sadb_x_ecomb_hard_addtime = 0;
6010b7daf799SDan McDonald 	ecomb->sadb_x_ecomb_soft_usetime = 0;
6011b7daf799SDan McDonald 	ecomb->sadb_x_ecomb_hard_usetime = 0;
6012b7daf799SDan McDonald 
6013b7daf799SDan McDonald 	if (ipp->ipp_use_ah) {
6014b7daf799SDan McDonald 		cur = sadb_new_algdesc(cur, limit, ecomb,
6015b7daf799SDan McDonald 		    SADB_SATYPE_AH, SADB_X_ALGTYPE_AUTH, ipp->ipp_auth_alg,
6016b7daf799SDan McDonald 		    ipp->ipp_ah_minbits, ipp->ipp_ah_maxbits, ipss);
6017b7daf799SDan McDonald 		if (cur == NULL)
60187c478bd9Sstevel@tonic-gate 			return (NULL);
6019b7daf799SDan McDonald 		ipsecah_fill_defs(ecomb, ns);
60207c478bd9Sstevel@tonic-gate 	}
60217c478bd9Sstevel@tonic-gate 
6022b7daf799SDan McDonald 	if (ipp->ipp_use_esp) {
6023b7daf799SDan McDonald 		if (ipp->ipp_use_espa) {
6024b7daf799SDan McDonald 			cur = sadb_new_algdesc(cur, limit, ecomb,
6025b7daf799SDan McDonald 			    SADB_SATYPE_ESP, SADB_X_ALGTYPE_AUTH,
6026b7daf799SDan McDonald 			    ipp->ipp_esp_auth_alg,
6027b7daf799SDan McDonald 			    ipp->ipp_espa_minbits,
6028b7daf799SDan McDonald 			    ipp->ipp_espa_maxbits, ipss);
6029b7daf799SDan McDonald 			if (cur == NULL)
6030b7daf799SDan McDonald 				return (NULL);
6031b7daf799SDan McDonald 		}
60327c478bd9Sstevel@tonic-gate 
6033b7daf799SDan McDonald 		cur = sadb_new_algdesc(cur, limit, ecomb,
6034b7daf799SDan McDonald 		    SADB_SATYPE_ESP, SADB_X_ALGTYPE_CRYPT,
6035b7daf799SDan McDonald 		    ipp->ipp_encr_alg,
6036b7daf799SDan McDonald 		    ipp->ipp_espe_minbits,
6037b7daf799SDan McDonald 		    ipp->ipp_espe_maxbits, ipss);
6038b7daf799SDan McDonald 		if (cur == NULL)
60397c478bd9Sstevel@tonic-gate 			return (NULL);
6040b7daf799SDan McDonald 		/* Fill in lifetimes if and only if AH didn't already... */
6041b7daf799SDan McDonald 		if (!ipp->ipp_use_ah)
6042b7daf799SDan McDonald 			ipsecesp_fill_defs(ecomb, ns);
60437c478bd9Sstevel@tonic-gate 	}
60447c478bd9Sstevel@tonic-gate 
6045b7daf799SDan McDonald 	return (cur);
60467c478bd9Sstevel@tonic-gate }
60477c478bd9Sstevel@tonic-gate 
6048b7daf799SDan McDonald #include <sys/tsol/label_macro.h> /* XXX should not need this */
6049b7daf799SDan McDonald 
60507c478bd9Sstevel@tonic-gate /*
6051b7daf799SDan McDonald  * From a cred_t, construct a sensitivity label extension
60528810c16bSdanmcd  *
6053b7daf799SDan McDonald  * We send up a fixed-size sensitivity label bitmap, and are perhaps
6054b7daf799SDan McDonald  * overly chummy with the underlying data structures here.
60557c478bd9Sstevel@tonic-gate  */
60567c478bd9Sstevel@tonic-gate 
6057b7daf799SDan McDonald /* ARGSUSED */
6058b7daf799SDan McDonald int
6059b7daf799SDan McDonald sadb_sens_len_from_label(ts_label_t *tsl)
6060b7daf799SDan McDonald {
6061b7daf799SDan McDonald 	int baselen = sizeof (sadb_sens_t) + _C_LEN * 4;
6062b7daf799SDan McDonald 	return (roundup(baselen, sizeof (uint64_t)));
6063b7daf799SDan McDonald }
60648810c16bSdanmcd 
6065b7daf799SDan McDonald void
6066b7daf799SDan McDonald sadb_sens_from_label(sadb_sens_t *sens, int exttype, ts_label_t *tsl,
6067b7daf799SDan McDonald     int senslen)
6068b7daf799SDan McDonald {
6069b7daf799SDan McDonald 	uint8_t *bitmap;
6070b7daf799SDan McDonald 	bslabel_t *sl;
60718810c16bSdanmcd 
6072b7daf799SDan McDonald 	/* LINTED */
6073b7daf799SDan McDonald 	ASSERT((_C_LEN & 1) == 0);
6074b7daf799SDan McDonald 	ASSERT((senslen & 7) == 0);
60758810c16bSdanmcd 
6076b7daf799SDan McDonald 	sl = label2bslabel(tsl);
60778810c16bSdanmcd 
6078b7daf799SDan McDonald 	sens->sadb_sens_exttype = exttype;
6079b7daf799SDan McDonald 	sens->sadb_sens_len = SADB_8TO64(senslen);
60808810c16bSdanmcd 
6081b7daf799SDan McDonald 	sens->sadb_sens_dpd = tsl->tsl_doi;
6082b7daf799SDan McDonald 	sens->sadb_sens_sens_level = LCLASS(sl);
6083b7daf799SDan McDonald 	sens->sadb_sens_integ_level = 0; /* TBD */
6084b7daf799SDan McDonald 	sens->sadb_sens_sens_len = _C_LEN >> 1;
6085b7daf799SDan McDonald 	sens->sadb_sens_integ_len = 0; /* TBD */
6086b7daf799SDan McDonald 	sens->sadb_x_sens_flags = 0;
60878810c16bSdanmcd 
6088b7daf799SDan McDonald 	bitmap = (uint8_t *)(sens + 1);
6089b7daf799SDan McDonald 	bcopy(&(((_bslabel_impl_t *)sl)->compartments), bitmap, _C_LEN * 4);
6090b7daf799SDan McDonald }
60918810c16bSdanmcd 
6092b7daf799SDan McDonald /*
6093b7daf799SDan McDonald  * Okay, how do we report errors/invalid labels from this?
6094b7daf799SDan McDonald  * With a special designated "not a label" cred_t ?
6095b7daf799SDan McDonald  */
6096b7daf799SDan McDonald /* ARGSUSED */
6097b7daf799SDan McDonald ts_label_t *
6098b7daf799SDan McDonald sadb_label_from_sens(sadb_sens_t *sens, uint64_t *bitmap)
6099b7daf799SDan McDonald {
6100b7daf799SDan McDonald 	int bitmap_len = SADB_64TO8(sens->sadb_sens_sens_len);
6101b7daf799SDan McDonald 	bslabel_t sl;
6102b7daf799SDan McDonald 	ts_label_t *tsl;
61037c478bd9Sstevel@tonic-gate 
6104b7daf799SDan McDonald 	if (sens->sadb_sens_integ_level != 0)
6105b7daf799SDan McDonald 		return (NULL);
6106b7daf799SDan McDonald 	if (sens->sadb_sens_integ_len != 0)
6107b7daf799SDan McDonald 		return (NULL);
6108b7daf799SDan McDonald 	if (bitmap_len > _C_LEN * 4)
61097c478bd9Sstevel@tonic-gate 		return (NULL);
61108810c16bSdanmcd 
6111b7daf799SDan McDonald 	bsllow(&sl);
6112a23b3b1bSToomas Soome 	LCLASS_SET((_bslabel_impl_t *)&sl,
6113a23b3b1bSToomas Soome 	    (uint16_t)sens->sadb_sens_sens_level);
6114b7daf799SDan McDonald 	bcopy(bitmap, &((_bslabel_impl_t *)&sl)->compartments,
6115b7daf799SDan McDonald 	    bitmap_len);
61167c478bd9Sstevel@tonic-gate 
6117b7daf799SDan McDonald 	tsl = labelalloc(&sl, sens->sadb_sens_dpd, KM_NOSLEEP);
6118b7daf799SDan McDonald 	if (tsl == NULL)
6119b7daf799SDan McDonald 		return (NULL);
61207c478bd9Sstevel@tonic-gate 
6121b7daf799SDan McDonald 	if (sens->sadb_x_sens_flags & SADB_X_SENS_UNLABELED)
6122b7daf799SDan McDonald 		tsl->tsl_flags |= TSLF_UNLABELED;
6123b7daf799SDan McDonald 	return (tsl);
61247c478bd9Sstevel@tonic-gate }
61257c478bd9Sstevel@tonic-gate 
6126b7daf799SDan McDonald /* End XXX label-library-leakage */
6127b7daf799SDan McDonald 
61287c478bd9Sstevel@tonic-gate /*
61297c478bd9Sstevel@tonic-gate  * Given an SADB_GETSPI message, find an appropriately ranged SA and
61307c478bd9Sstevel@tonic-gate  * allocate an SA.  If there are message improprieties, return (ipsa_t *)-1.
61317c478bd9Sstevel@tonic-gate  * If there was a memory allocation error, return NULL.	 (Assume NULL !=
61327c478bd9Sstevel@tonic-gate  * (ipsa_t *)-1).
61337c478bd9Sstevel@tonic-gate  *
61347c478bd9Sstevel@tonic-gate  * master_spi is passed in host order.
61357c478bd9Sstevel@tonic-gate  */
61367c478bd9Sstevel@tonic-gate ipsa_t *
6137f4b3ec61Sdh sadb_getspi(keysock_in_t *ksi, uint32_t master_spi, int *diagnostic,
61389c2c14abSThejaswini Singarajipura     netstack_t *ns, uint_t sa_type)
61397c478bd9Sstevel@tonic-gate {
61407c478bd9Sstevel@tonic-gate 	sadb_address_t *src =
61417c478bd9Sstevel@tonic-gate 	    (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_SRC],
61427c478bd9Sstevel@tonic-gate 	    *dst = (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_DST];
61437c478bd9Sstevel@tonic-gate 	sadb_spirange_t *range =
61447c478bd9Sstevel@tonic-gate 	    (sadb_spirange_t *)ksi->ks_in_extv[SADB_EXT_SPIRANGE];
61457c478bd9Sstevel@tonic-gate 	struct sockaddr_in *ssa, *dsa;
61467c478bd9Sstevel@tonic-gate 	struct sockaddr_in6 *ssa6, *dsa6;
61477c478bd9Sstevel@tonic-gate 	uint32_t *srcaddr, *dstaddr;
61487c478bd9Sstevel@tonic-gate 	sa_family_t af;
61497c478bd9Sstevel@tonic-gate 	uint32_t add, min, max;
61509c2c14abSThejaswini Singarajipura 	uint8_t protocol =
61519c2c14abSThejaswini Singarajipura 	    (sa_type == SADB_SATYPE_AH) ? IPPROTO_AH : IPPROTO_ESP;
61527c478bd9Sstevel@tonic-gate 
61537c478bd9Sstevel@tonic-gate 	if (src == NULL) {
61547c478bd9Sstevel@tonic-gate 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_SRC;
61557c478bd9Sstevel@tonic-gate 		return ((ipsa_t *)-1);
61567c478bd9Sstevel@tonic-gate 	}
61577c478bd9Sstevel@tonic-gate 	if (dst == NULL) {
61587c478bd9Sstevel@tonic-gate 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_DST;
61597c478bd9Sstevel@tonic-gate 		return ((ipsa_t *)-1);
61607c478bd9Sstevel@tonic-gate 	}
61617c478bd9Sstevel@tonic-gate 	if (range == NULL) {
61627c478bd9Sstevel@tonic-gate 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_RANGE;
61637c478bd9Sstevel@tonic-gate 		return ((ipsa_t *)-1);
61647c478bd9Sstevel@tonic-gate 	}
61657c478bd9Sstevel@tonic-gate 
61667c478bd9Sstevel@tonic-gate 	min = ntohl(range->sadb_spirange_min);
61677c478bd9Sstevel@tonic-gate 	max = ntohl(range->sadb_spirange_max);
61687c478bd9Sstevel@tonic-gate 	dsa = (struct sockaddr_in *)(dst + 1);
61697c478bd9Sstevel@tonic-gate 	dsa6 = (struct sockaddr_in6 *)dsa;
61707c478bd9Sstevel@tonic-gate 
61717c478bd9Sstevel@tonic-gate 	ssa = (struct sockaddr_in *)(src + 1);
61727c478bd9Sstevel@tonic-gate 	ssa6 = (struct sockaddr_in6 *)ssa;
61738810c16bSdanmcd 	ASSERT(dsa->sin_family == ssa->sin_family);
61747c478bd9Sstevel@tonic-gate 
61757c478bd9Sstevel@tonic-gate 	srcaddr = ALL_ZEROES_PTR;
61767c478bd9Sstevel@tonic-gate 	af = dsa->sin_family;
61777c478bd9Sstevel@tonic-gate 	switch (af) {
61787c478bd9Sstevel@tonic-gate 	case AF_INET:
61797c478bd9Sstevel@tonic-gate 		if (src != NULL)
61807c478bd9Sstevel@tonic-gate 			srcaddr = (uint32_t *)(&ssa->sin_addr);
61817c478bd9Sstevel@tonic-gate 		dstaddr = (uint32_t *)(&dsa->sin_addr);
61827c478bd9Sstevel@tonic-gate 		break;
61837c478bd9Sstevel@tonic-gate 	case AF_INET6:
61847c478bd9Sstevel@tonic-gate 		if (src != NULL)
61857c478bd9Sstevel@tonic-gate 			srcaddr = (uint32_t *)(&ssa6->sin6_addr);
61867c478bd9Sstevel@tonic-gate 		dstaddr = (uint32_t *)(&dsa6->sin6_addr);
61877c478bd9Sstevel@tonic-gate 		break;
61887c478bd9Sstevel@tonic-gate 	default:
61897c478bd9Sstevel@tonic-gate 		*diagnostic = SADB_X_DIAGNOSTIC_BAD_DST_AF;
61907c478bd9Sstevel@tonic-gate 		return ((ipsa_t *)-1);
61917c478bd9Sstevel@tonic-gate 	}
61927c478bd9Sstevel@tonic-gate 
61937c478bd9Sstevel@tonic-gate 	if (master_spi < min || master_spi > max) {
61947c478bd9Sstevel@tonic-gate 		/* Return a random value in the range. */
61959c2c14abSThejaswini Singarajipura 		if (cl_inet_getspi) {
61968e4b770fSLu Huafeng 			cl_inet_getspi(ns->netstack_stackid, protocol,
61978e4b770fSLu Huafeng 			    (uint8_t *)&add, sizeof (add), NULL);
61989c2c14abSThejaswini Singarajipura 		} else {
61999c2c14abSThejaswini Singarajipura 			(void) random_get_pseudo_bytes((uint8_t *)&add,
62009c2c14abSThejaswini Singarajipura 			    sizeof (add));
62019c2c14abSThejaswini Singarajipura 		}
62027c478bd9Sstevel@tonic-gate 		master_spi = min + (add % (max - min + 1));
62037c478bd9Sstevel@tonic-gate 	}
62047c478bd9Sstevel@tonic-gate 
62057c478bd9Sstevel@tonic-gate 	/*
62067c478bd9Sstevel@tonic-gate 	 * Since master_spi is passed in host order, we need to htonl() it
62077c478bd9Sstevel@tonic-gate 	 * for the purposes of creating a new SA.
62087c478bd9Sstevel@tonic-gate 	 */
6209f4b3ec61Sdh 	return (sadb_makelarvalassoc(htonl(master_spi), srcaddr, dstaddr, af,
6210437220cdSdanmcd 	    ns));
62117c478bd9Sstevel@tonic-gate }
62127c478bd9Sstevel@tonic-gate 
62137c478bd9Sstevel@tonic-gate /*
62147c478bd9Sstevel@tonic-gate  *
62157c478bd9Sstevel@tonic-gate  * Locate an ACQUIRE and nuke it.  If I have an samsg that's larger than the
62167c478bd9Sstevel@tonic-gate  * base header, just ignore it.	 Otherwise, lock down the whole ACQUIRE list
62177c478bd9Sstevel@tonic-gate  * and scan for the sequence number in question.  I may wish to accept an
62187c478bd9Sstevel@tonic-gate  * address pair with it, for easier searching.
62197c478bd9Sstevel@tonic-gate  *
62207c478bd9Sstevel@tonic-gate  * Caller frees the message, so we don't have to here.
62217c478bd9Sstevel@tonic-gate  *
6222bd670b35SErik Nordmark  * NOTE:	The pfkey_q parameter may be used in the future for ACQUIRE
62237c478bd9Sstevel@tonic-gate  *		failures.
62247c478bd9Sstevel@tonic-gate  */
62257c478bd9Sstevel@tonic-gate /* ARGSUSED */
62267c478bd9Sstevel@tonic-gate void
6227bd670b35SErik Nordmark sadb_in_acquire(sadb_msg_t *samsg, sadbp_t *sp, queue_t *pfkey_q,
6228bd670b35SErik Nordmark     netstack_t *ns)
62297c478bd9Sstevel@tonic-gate {
62307c478bd9Sstevel@tonic-gate 	int i;
62317c478bd9Sstevel@tonic-gate 	ipsacq_t *acqrec;
62327c478bd9Sstevel@tonic-gate 	iacqf_t *bucket;
62337c478bd9Sstevel@tonic-gate 
62347c478bd9Sstevel@tonic-gate 	/*
62357c478bd9Sstevel@tonic-gate 	 * I only accept the base header for this!
62367c478bd9Sstevel@tonic-gate 	 * Though to be honest, requiring the dst address would help
62377c478bd9Sstevel@tonic-gate 	 * immensely.
62387c478bd9Sstevel@tonic-gate 	 *
62397c478bd9Sstevel@tonic-gate 	 * XXX	There are already cases where I can get the dst address.
62407c478bd9Sstevel@tonic-gate 	 */
62417c478bd9Sstevel@tonic-gate 	if (samsg->sadb_msg_len > SADB_8TO64(sizeof (*samsg)))
62427c478bd9Sstevel@tonic-gate 		return;
62437c478bd9Sstevel@tonic-gate 
62447c478bd9Sstevel@tonic-gate 	/*
62457c478bd9Sstevel@tonic-gate 	 * Using the samsg->sadb_msg_seq, find the ACQUIRE record, delete it,
62467c478bd9Sstevel@tonic-gate 	 * (and in the future send a message to IP with the appropriate error
62477c478bd9Sstevel@tonic-gate 	 * number).
62487c478bd9Sstevel@tonic-gate 	 *
62497c478bd9Sstevel@tonic-gate 	 * Q: Do I want to reject if pid != 0?
62507c478bd9Sstevel@tonic-gate 	 */
62517c478bd9Sstevel@tonic-gate 
6252fb87b5d2Ssommerfe 	for (i = 0; i < sp->s_v4.sdb_hashsize; i++) {
62537c478bd9Sstevel@tonic-gate 		bucket = &sp->s_v4.sdb_acq[i];
62547c478bd9Sstevel@tonic-gate 		mutex_enter(&bucket->iacqf_lock);
62557c478bd9Sstevel@tonic-gate 		for (acqrec = bucket->iacqf_ipsacq; acqrec != NULL;
62567c478bd9Sstevel@tonic-gate 		    acqrec = acqrec->ipsacq_next) {
62577c478bd9Sstevel@tonic-gate 			if (samsg->sadb_msg_seq == acqrec->ipsacq_seq)
62587c478bd9Sstevel@tonic-gate 				break;	/* for acqrec... loop. */
62597c478bd9Sstevel@tonic-gate 		}
62607c478bd9Sstevel@tonic-gate 		if (acqrec != NULL)
62617c478bd9Sstevel@tonic-gate 			break;	/* for i = 0... loop. */
62627c478bd9Sstevel@tonic-gate 
62637c478bd9Sstevel@tonic-gate 		mutex_exit(&bucket->iacqf_lock);
6264fb87b5d2Ssommerfe 	}
62657c478bd9Sstevel@tonic-gate 
6266fb87b5d2Ssommerfe 	if (acqrec == NULL) {
6267fb87b5d2Ssommerfe 		for (i = 0; i < sp->s_v6.sdb_hashsize; i++) {
6268fb87b5d2Ssommerfe 			bucket = &sp->s_v6.sdb_acq[i];
6269fb87b5d2Ssommerfe 			mutex_enter(&bucket->iacqf_lock);
6270fb87b5d2Ssommerfe 			for (acqrec = bucket->iacqf_ipsacq; acqrec != NULL;
6271fb87b5d2Ssommerfe 			    acqrec = acqrec->ipsacq_next) {
6272fb87b5d2Ssommerfe 				if (samsg->sadb_msg_seq == acqrec->ipsacq_seq)
6273fb87b5d2Ssommerfe 					break;	/* for acqrec... loop. */
6274fb87b5d2Ssommerfe 			}
6275fb87b5d2Ssommerfe 			if (acqrec != NULL)
6276fb87b5d2Ssommerfe 				break;	/* for i = 0... loop. */
62777c478bd9Sstevel@tonic-gate 
6278fb87b5d2Ssommerfe 			mutex_exit(&bucket->iacqf_lock);
6279fb87b5d2Ssommerfe 		}
62807c478bd9Sstevel@tonic-gate 	}
62817c478bd9Sstevel@tonic-gate 
6282fb87b5d2Ssommerfe 
62837c478bd9Sstevel@tonic-gate 	if (acqrec == NULL)
62847c478bd9Sstevel@tonic-gate 		return;
62857c478bd9Sstevel@tonic-gate 
62867c478bd9Sstevel@tonic-gate 	/*
62877c478bd9Sstevel@tonic-gate 	 * What do I do with the errno and IP?	I may need mp's services a
62887c478bd9Sstevel@tonic-gate 	 * little more.	 See sadb_destroy_acquire() for future directions
62897c478bd9Sstevel@tonic-gate 	 * beyond free the mblk chain on the acquire record.
62907c478bd9Sstevel@tonic-gate 	 */
62917c478bd9Sstevel@tonic-gate 
62927c478bd9Sstevel@tonic-gate 	ASSERT(&bucket->iacqf_lock == acqrec->ipsacq_linklock);
6293f4b3ec61Sdh 	sadb_destroy_acquire(acqrec, ns);
62947c478bd9Sstevel@tonic-gate 	/* Have to exit mutex here, because of breaking out of for loop. */
62957c478bd9Sstevel@tonic-gate 	mutex_exit(&bucket->iacqf_lock);
62967c478bd9Sstevel@tonic-gate }
62977c478bd9Sstevel@tonic-gate 
62987c478bd9Sstevel@tonic-gate /*
62997c478bd9Sstevel@tonic-gate  * The following functions work with the replay windows of an SA.  They assume
63007c478bd9Sstevel@tonic-gate  * the ipsa->ipsa_replay_arr is an array of uint64_t, and that the bit vector
63017c478bd9Sstevel@tonic-gate  * represents the highest sequence number packet received, and back
63027c478bd9Sstevel@tonic-gate  * (ipsa->ipsa_replay_wsize) packets.
63037c478bd9Sstevel@tonic-gate  */
63047c478bd9Sstevel@tonic-gate 
63057c478bd9Sstevel@tonic-gate /*
63067c478bd9Sstevel@tonic-gate  * Is the replay bit set?
63077c478bd9Sstevel@tonic-gate  */
63087c478bd9Sstevel@tonic-gate static boolean_t
63097c478bd9Sstevel@tonic-gate ipsa_is_replay_set(ipsa_t *ipsa, uint32_t offset)
63107c478bd9Sstevel@tonic-gate {
63117c478bd9Sstevel@tonic-gate 	uint64_t bit = (uint64_t)1 << (uint64_t)(offset & 63);
63127c478bd9Sstevel@tonic-gate 
63137c478bd9Sstevel@tonic-gate 	return ((bit & ipsa->ipsa_replay_arr[offset >> 6]) ? B_TRUE : B_FALSE);
63147c478bd9Sstevel@tonic-gate }
63157c478bd9Sstevel@tonic-gate 
63167c478bd9Sstevel@tonic-gate /*
63177c478bd9Sstevel@tonic-gate  * Shift the bits of the replay window over.
63187c478bd9Sstevel@tonic-gate  */
63197c478bd9Sstevel@tonic-gate static void
63207c478bd9Sstevel@tonic-gate ipsa_shift_replay(ipsa_t *ipsa, uint32_t shift)
63217c478bd9Sstevel@tonic-gate {
63227c478bd9Sstevel@tonic-gate 	int i;
63237c478bd9Sstevel@tonic-gate 	int jump = ((shift - 1) >> 6) + 1;
63247c478bd9Sstevel@tonic-gate 
63257c478bd9Sstevel@tonic-gate 	if (shift == 0)
63267c478bd9Sstevel@tonic-gate 		return;
63277c478bd9Sstevel@tonic-gate 
63287c478bd9Sstevel@tonic-gate 	for (i = (ipsa->ipsa_replay_wsize - 1) >> 6; i >= 0; i--) {
63297c478bd9Sstevel@tonic-gate 		if (i + jump <= (ipsa->ipsa_replay_wsize - 1) >> 6) {
63307c478bd9Sstevel@tonic-gate 			ipsa->ipsa_replay_arr[i + jump] |=
63317c478bd9Sstevel@tonic-gate 			    ipsa->ipsa_replay_arr[i] >> (64 - (shift & 63));
63327c478bd9Sstevel@tonic-gate 		}
63337c478bd9Sstevel@tonic-gate 		ipsa->ipsa_replay_arr[i] <<= shift;
63347c478bd9Sstevel@tonic-gate 	}
63357c478bd9Sstevel@tonic-gate }
63367c478bd9Sstevel@tonic-gate 
63377c478bd9Sstevel@tonic-gate /*
63387c478bd9Sstevel@tonic-gate  * Set a bit in the bit vector.
63397c478bd9Sstevel@tonic-gate  */
63407c478bd9Sstevel@tonic-gate static void
63417c478bd9Sstevel@tonic-gate ipsa_set_replay(ipsa_t *ipsa, uint32_t offset)
63427c478bd9Sstevel@tonic-gate {
63437c478bd9Sstevel@tonic-gate 	uint64_t bit = (uint64_t)1 << (uint64_t)(offset & 63);
63447c478bd9Sstevel@tonic-gate 
63457c478bd9Sstevel@tonic-gate 	ipsa->ipsa_replay_arr[offset >> 6] |= bit;
63467c478bd9Sstevel@tonic-gate }
63477c478bd9Sstevel@tonic-gate 
63487c478bd9Sstevel@tonic-gate #define	SADB_MAX_REPLAY_VALUE 0xffffffff
63497c478bd9Sstevel@tonic-gate 
63507c478bd9Sstevel@tonic-gate /*
63517c478bd9Sstevel@tonic-gate  * Assume caller has NOT done ntohl() already on seq.  Check to see
63527c478bd9Sstevel@tonic-gate  * if replay sequence number "seq" has been seen already.
63537c478bd9Sstevel@tonic-gate  */
63547c478bd9Sstevel@tonic-gate boolean_t
63557c478bd9Sstevel@tonic-gate sadb_replay_check(ipsa_t *ipsa, uint32_t seq)
63567c478bd9Sstevel@tonic-gate {
63577c478bd9Sstevel@tonic-gate 	boolean_t rc;
63587c478bd9Sstevel@tonic-gate 	uint32_t diff;
63597c478bd9Sstevel@tonic-gate 
63607c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_replay_wsize == 0)
63617c478bd9Sstevel@tonic-gate 		return (B_TRUE);
63627c478bd9Sstevel@tonic-gate 
63637c478bd9Sstevel@tonic-gate 	/*
63647c478bd9Sstevel@tonic-gate 	 * NOTE:  I've already checked for 0 on the wire in sadb_replay_peek().
63657c478bd9Sstevel@tonic-gate 	 */
63667c478bd9Sstevel@tonic-gate 
63677c478bd9Sstevel@tonic-gate 	/* Convert sequence number into host order before holding the mutex. */
63687c478bd9Sstevel@tonic-gate 	seq = ntohl(seq);
63697c478bd9Sstevel@tonic-gate 
63707c478bd9Sstevel@tonic-gate 	mutex_enter(&ipsa->ipsa_lock);
63717c478bd9Sstevel@tonic-gate 
63727c478bd9Sstevel@tonic-gate 	/* Initialize inbound SA's ipsa_replay field to last one received. */
63737c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_replay == 0)
63747c478bd9Sstevel@tonic-gate 		ipsa->ipsa_replay = 1;
63757c478bd9Sstevel@tonic-gate 
63767c478bd9Sstevel@tonic-gate 	if (seq > ipsa->ipsa_replay) {
63777c478bd9Sstevel@tonic-gate 		/*
63787c478bd9Sstevel@tonic-gate 		 * I have received a new "highest value received".  Shift
63797c478bd9Sstevel@tonic-gate 		 * the replay window over.
63807c478bd9Sstevel@tonic-gate 		 */
63817c478bd9Sstevel@tonic-gate 		diff = seq - ipsa->ipsa_replay;
63827c478bd9Sstevel@tonic-gate 		if (diff < ipsa->ipsa_replay_wsize) {
63837c478bd9Sstevel@tonic-gate 			/* In replay window, shift bits over. */
63847c478bd9Sstevel@tonic-gate 			ipsa_shift_replay(ipsa, diff);
63857c478bd9Sstevel@tonic-gate 		} else {
63867c478bd9Sstevel@tonic-gate 			/* WAY FAR AHEAD, clear bits and start again. */
63877c478bd9Sstevel@tonic-gate 			bzero(ipsa->ipsa_replay_arr,
63887c478bd9Sstevel@tonic-gate 			    sizeof (ipsa->ipsa_replay_arr));
63897c478bd9Sstevel@tonic-gate 		}
63907c478bd9Sstevel@tonic-gate 		ipsa_set_replay(ipsa, 0);
63917c478bd9Sstevel@tonic-gate 		ipsa->ipsa_replay = seq;
63927c478bd9Sstevel@tonic-gate 		rc = B_TRUE;
63937c478bd9Sstevel@tonic-gate 		goto done;
63947c478bd9Sstevel@tonic-gate 	}
63957c478bd9Sstevel@tonic-gate 	diff = ipsa->ipsa_replay - seq;
63967c478bd9Sstevel@tonic-gate 	if (diff >= ipsa->ipsa_replay_wsize || ipsa_is_replay_set(ipsa, diff)) {
63977c478bd9Sstevel@tonic-gate 		rc = B_FALSE;
63987c478bd9Sstevel@tonic-gate 		goto done;
63997c478bd9Sstevel@tonic-gate 	}
64007c478bd9Sstevel@tonic-gate 	/* Set this packet as seen. */
64017c478bd9Sstevel@tonic-gate 	ipsa_set_replay(ipsa, diff);
64027c478bd9Sstevel@tonic-gate 
64037c478bd9Sstevel@tonic-gate 	rc = B_TRUE;
64047c478bd9Sstevel@tonic-gate done:
64057c478bd9Sstevel@tonic-gate 	mutex_exit(&ipsa->ipsa_lock);
64067c478bd9Sstevel@tonic-gate 	return (rc);
64077c478bd9Sstevel@tonic-gate }
64087c478bd9Sstevel@tonic-gate 
64097c478bd9Sstevel@tonic-gate /*
64107c478bd9Sstevel@tonic-gate  * "Peek" and see if we should even bother going through the effort of
64117c478bd9Sstevel@tonic-gate  * running an authentication check on the sequence number passed in.
64127c478bd9Sstevel@tonic-gate  * this takes into account packets that are below the replay window,
64137c478bd9Sstevel@tonic-gate  * and collisions with already replayed packets.  Return B_TRUE if it
64148810c16bSdanmcd  * is okay to proceed, B_FALSE if this packet should be dropped immediately.
64157c478bd9Sstevel@tonic-gate  * Assume same byte-ordering as sadb_replay_check.
64167c478bd9Sstevel@tonic-gate  */
64177c478bd9Sstevel@tonic-gate boolean_t
64187c478bd9Sstevel@tonic-gate sadb_replay_peek(ipsa_t *ipsa, uint32_t seq)
64197c478bd9Sstevel@tonic-gate {
64207c478bd9Sstevel@tonic-gate 	boolean_t rc = B_FALSE;
64217c478bd9Sstevel@tonic-gate 	uint32_t diff;
64227c478bd9Sstevel@tonic-gate 
64237c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_replay_wsize == 0)
64247c478bd9Sstevel@tonic-gate 		return (B_TRUE);
64257c478bd9Sstevel@tonic-gate 
64267c478bd9Sstevel@tonic-gate 	/*
64277c478bd9Sstevel@tonic-gate 	 * 0 is 0, regardless of byte order... :)
64287c478bd9Sstevel@tonic-gate 	 *
64297c478bd9Sstevel@tonic-gate 	 * If I get 0 on the wire (and there is a replay window) then the
64307c478bd9Sstevel@tonic-gate 	 * sender most likely wrapped.	This ipsa may need to be marked or
64317c478bd9Sstevel@tonic-gate 	 * something.
64327c478bd9Sstevel@tonic-gate 	 */
64337c478bd9Sstevel@tonic-gate 	if (seq == 0)
64347c478bd9Sstevel@tonic-gate 		return (B_FALSE);
64357c478bd9Sstevel@tonic-gate 
64367c478bd9Sstevel@tonic-gate 	seq = ntohl(seq);
64377c478bd9Sstevel@tonic-gate 	mutex_enter(&ipsa->ipsa_lock);
64387c478bd9Sstevel@tonic-gate 	if (seq < ipsa->ipsa_replay - ipsa->ipsa_replay_wsize &&
64397c478bd9Sstevel@tonic-gate 	    ipsa->ipsa_replay >= ipsa->ipsa_replay_wsize)
64407c478bd9Sstevel@tonic-gate 		goto done;
64417c478bd9Sstevel@tonic-gate 
64427c478bd9Sstevel@tonic-gate 	/*
64437c478bd9Sstevel@tonic-gate 	 * If I've hit 0xffffffff, then quite honestly, I don't need to
64447c478bd9Sstevel@tonic-gate 	 * bother with formalities.  I'm not accepting any more packets
64457c478bd9Sstevel@tonic-gate 	 * on this SA.
64467c478bd9Sstevel@tonic-gate 	 */
64477c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_replay == SADB_MAX_REPLAY_VALUE) {
64487c478bd9Sstevel@tonic-gate 		/*
64497c478bd9Sstevel@tonic-gate 		 * Since we're already holding the lock, update the
64507c478bd9Sstevel@tonic-gate 		 * expire time ala. sadb_replay_delete() and return.
64517c478bd9Sstevel@tonic-gate 		 */
64527c478bd9Sstevel@tonic-gate 		ipsa->ipsa_hardexpiretime = (time_t)1;
64537c478bd9Sstevel@tonic-gate 		goto done;
64547c478bd9Sstevel@tonic-gate 	}
64557c478bd9Sstevel@tonic-gate 
64567c478bd9Sstevel@tonic-gate 	if (seq <= ipsa->ipsa_replay) {
64577c478bd9Sstevel@tonic-gate 		/*
64587c478bd9Sstevel@tonic-gate 		 * This seq is in the replay window.  I'm not below it,
64597c478bd9Sstevel@tonic-gate 		 * because I already checked for that above!
64607c478bd9Sstevel@tonic-gate 		 */
64617c478bd9Sstevel@tonic-gate 		diff = ipsa->ipsa_replay - seq;
64627c478bd9Sstevel@tonic-gate 		if (ipsa_is_replay_set(ipsa, diff))
64637c478bd9Sstevel@tonic-gate 			goto done;
64647c478bd9Sstevel@tonic-gate 	}
64657c478bd9Sstevel@tonic-gate 	/* Else return B_TRUE, I'm going to advance the window. */
64667c478bd9Sstevel@tonic-gate 
64677c478bd9Sstevel@tonic-gate 	rc = B_TRUE;
64687c478bd9Sstevel@tonic-gate done:
64697c478bd9Sstevel@tonic-gate 	mutex_exit(&ipsa->ipsa_lock);
64707c478bd9Sstevel@tonic-gate 	return (rc);
64717c478bd9Sstevel@tonic-gate }
64727c478bd9Sstevel@tonic-gate 
64737c478bd9Sstevel@tonic-gate /*
64747c478bd9Sstevel@tonic-gate  * Delete a single SA.
64757c478bd9Sstevel@tonic-gate  *
64767c478bd9Sstevel@tonic-gate  * For now, use the quick-and-dirty trick of making the association's
64777c478bd9Sstevel@tonic-gate  * hard-expire lifetime (time_t)1, ensuring deletion by the *_ager().
64787c478bd9Sstevel@tonic-gate  */
64797c478bd9Sstevel@tonic-gate void
64807c478bd9Sstevel@tonic-gate sadb_replay_delete(ipsa_t *assoc)
64817c478bd9Sstevel@tonic-gate {
64827c478bd9Sstevel@tonic-gate 	mutex_enter(&assoc->ipsa_lock);
64837c478bd9Sstevel@tonic-gate 	assoc->ipsa_hardexpiretime = (time_t)1;
64847c478bd9Sstevel@tonic-gate 	mutex_exit(&assoc->ipsa_lock);
64857c478bd9Sstevel@tonic-gate }
64867c478bd9Sstevel@tonic-gate 
64877c478bd9Sstevel@tonic-gate /*
64887c478bd9Sstevel@tonic-gate  * Special front-end to ipsec_rl_strlog() dealing with SA failure.
64897c478bd9Sstevel@tonic-gate  * this is designed to take only a format string with "* %x * %s *", so
64907c478bd9Sstevel@tonic-gate  * that "spi" is printed first, then "addr" is converted using inet_pton().
64917c478bd9Sstevel@tonic-gate  *
64927c478bd9Sstevel@tonic-gate  * This is abstracted out to save the stack space for only when inet_pton()
64937c478bd9Sstevel@tonic-gate  * is called.  Make sure "spi" is in network order; it usually is when this
64947c478bd9Sstevel@tonic-gate  * would get called.
64957c478bd9Sstevel@tonic-gate  */
64967c478bd9Sstevel@tonic-gate void
64977c478bd9Sstevel@tonic-gate ipsec_assocfailure(short mid, short sid, char level, ushort_t sl, char *fmt,
6498f4b3ec61Sdh     uint32_t spi, void *addr, int af, netstack_t *ns)
64997c478bd9Sstevel@tonic-gate {
65007c478bd9Sstevel@tonic-gate 	char buf[INET6_ADDRSTRLEN];
65017c478bd9Sstevel@tonic-gate 
65027c478bd9Sstevel@tonic-gate 	ASSERT(af == AF_INET6 || af == AF_INET);
65037c478bd9Sstevel@tonic-gate 
6504f4b3ec61Sdh 	ipsec_rl_strlog(ns, mid, sid, level, sl, fmt, ntohl(spi),
65057c478bd9Sstevel@tonic-gate 	    inet_ntop(af, addr, buf, sizeof (buf)));
65067c478bd9Sstevel@tonic-gate }
65077c478bd9Sstevel@tonic-gate 
65087c478bd9Sstevel@tonic-gate /*
65097c478bd9Sstevel@tonic-gate  * Fills in a reference to the policy, if any, from the conn, in *ppp
65107c478bd9Sstevel@tonic-gate  */
65117c478bd9Sstevel@tonic-gate static void
65128810c16bSdanmcd ipsec_conn_pol(ipsec_selector_t *sel, conn_t *connp, ipsec_policy_t **ppp)
65137c478bd9Sstevel@tonic-gate {
65147c478bd9Sstevel@tonic-gate 	ipsec_policy_t	*pp;
65157c478bd9Sstevel@tonic-gate 	ipsec_latch_t	*ipl = connp->conn_latch;
65167c478bd9Sstevel@tonic-gate 
6517bd670b35SErik Nordmark 	if ((ipl != NULL) && (connp->conn_ixa->ixa_ipsec_policy != NULL)) {
6518bd670b35SErik Nordmark 		pp = connp->conn_ixa->ixa_ipsec_policy;
65197c478bd9Sstevel@tonic-gate 		IPPOL_REFHOLD(pp);
65207c478bd9Sstevel@tonic-gate 	} else {
6521bd670b35SErik Nordmark 		pp = ipsec_find_policy(IPSEC_TYPE_OUTBOUND, connp, sel,
6522f4b3ec61Sdh 		    connp->conn_netstack);
65237c478bd9Sstevel@tonic-gate 	}
65247c478bd9Sstevel@tonic-gate 	*ppp = pp;
65257c478bd9Sstevel@tonic-gate }
65267c478bd9Sstevel@tonic-gate 
65277c478bd9Sstevel@tonic-gate /*
65287c478bd9Sstevel@tonic-gate  * The following functions scan through active conn_t structures
65297c478bd9Sstevel@tonic-gate  * and return a reference to the best-matching policy it can find.
65307c478bd9Sstevel@tonic-gate  * Caller must release the reference.
65317c478bd9Sstevel@tonic-gate  */
65327c478bd9Sstevel@tonic-gate static void
6533f4b3ec61Sdh ipsec_udp_pol(ipsec_selector_t *sel, ipsec_policy_t **ppp, ip_stack_t *ipst)
65347c478bd9Sstevel@tonic-gate {
65357c478bd9Sstevel@tonic-gate 	connf_t *connfp;
65367c478bd9Sstevel@tonic-gate 	conn_t *connp = NULL;
65377c478bd9Sstevel@tonic-gate 	ipsec_selector_t portonly;
65387c478bd9Sstevel@tonic-gate 
65399c2c14abSThejaswini Singarajipura 	bzero((void *)&portonly, sizeof (portonly));
65407c478bd9Sstevel@tonic-gate 
65417c478bd9Sstevel@tonic-gate 	if (sel->ips_local_port == 0)
65427c478bd9Sstevel@tonic-gate 		return;
65437c478bd9Sstevel@tonic-gate 
6544f4b3ec61Sdh 	connfp = &ipst->ips_ipcl_udp_fanout[IPCL_UDP_HASH(sel->ips_local_port,
6545f4b3ec61Sdh 	    ipst)];
65467c478bd9Sstevel@tonic-gate 	mutex_enter(&connfp->connf_lock);
65477c478bd9Sstevel@tonic-gate 
65487c478bd9Sstevel@tonic-gate 	if (sel->ips_isv4) {
65497c478bd9Sstevel@tonic-gate 		connp = connfp->connf_head;
65507c478bd9Sstevel@tonic-gate 		while (connp != NULL) {
65517c478bd9Sstevel@tonic-gate 			if (IPCL_UDP_MATCH(connp, sel->ips_local_port,
65527c478bd9Sstevel@tonic-gate 			    sel->ips_local_addr_v4, sel->ips_remote_port,
65537c478bd9Sstevel@tonic-gate 			    sel->ips_remote_addr_v4))
65547c478bd9Sstevel@tonic-gate 				break;
65557c478bd9Sstevel@tonic-gate 			connp = connp->conn_next;
65567c478bd9Sstevel@tonic-gate 		}
65577c478bd9Sstevel@tonic-gate 
65587c478bd9Sstevel@tonic-gate 		if (connp == NULL) {
65597c478bd9Sstevel@tonic-gate 			/* Try port-only match in IPv6. */
65607c478bd9Sstevel@tonic-gate 			portonly.ips_local_port = sel->ips_local_port;
65617c478bd9Sstevel@tonic-gate 			sel = &portonly;
65627c478bd9Sstevel@tonic-gate 		}
65637c478bd9Sstevel@tonic-gate 	}
65647c478bd9Sstevel@tonic-gate 
65657c478bd9Sstevel@tonic-gate 	if (connp == NULL) {
65667c478bd9Sstevel@tonic-gate 		connp = connfp->connf_head;
65677c478bd9Sstevel@tonic-gate 		while (connp != NULL) {
65687c478bd9Sstevel@tonic-gate 			if (IPCL_UDP_MATCH_V6(connp, sel->ips_local_port,
65697c478bd9Sstevel@tonic-gate 			    sel->ips_local_addr_v6, sel->ips_remote_port,
65707c478bd9Sstevel@tonic-gate 			    sel->ips_remote_addr_v6))
65717c478bd9Sstevel@tonic-gate 				break;
65727c478bd9Sstevel@tonic-gate 			connp = connp->conn_next;
65737c478bd9Sstevel@tonic-gate 		}
65747c478bd9Sstevel@tonic-gate 
65757c478bd9Sstevel@tonic-gate 		if (connp == NULL) {
65767c478bd9Sstevel@tonic-gate 			mutex_exit(&connfp->connf_lock);
65777c478bd9Sstevel@tonic-gate 			return;
65787c478bd9Sstevel@tonic-gate 		}
65797c478bd9Sstevel@tonic-gate 	}
65807c478bd9Sstevel@tonic-gate 
65817c478bd9Sstevel@tonic-gate 	CONN_INC_REF(connp);
65827c478bd9Sstevel@tonic-gate 	mutex_exit(&connfp->connf_lock);
65837c478bd9Sstevel@tonic-gate 
65848810c16bSdanmcd 	ipsec_conn_pol(sel, connp, ppp);
6585bd670b35SErik Nordmark 	CONN_DEC_REF(connp);
65867c478bd9Sstevel@tonic-gate }
65877c478bd9Sstevel@tonic-gate 
65887c478bd9Sstevel@tonic-gate static conn_t *
6589f4b3ec61Sdh ipsec_find_listen_conn(uint16_t *pptr, ipsec_selector_t *sel, ip_stack_t *ipst)
65907c478bd9Sstevel@tonic-gate {
65917c478bd9Sstevel@tonic-gate 	connf_t *connfp;
65927c478bd9Sstevel@tonic-gate 	conn_t *connp = NULL;
65937c478bd9Sstevel@tonic-gate 	const in6_addr_t *v6addrmatch = &sel->ips_local_addr_v6;
65947c478bd9Sstevel@tonic-gate 
65957c478bd9Sstevel@tonic-gate 	if (sel->ips_local_port == 0)
65967c478bd9Sstevel@tonic-gate 		return (NULL);
65977c478bd9Sstevel@tonic-gate 
6598437220cdSdanmcd 	connfp = &ipst->ips_ipcl_bind_fanout[
6599437220cdSdanmcd 	    IPCL_BIND_HASH(sel->ips_local_port, ipst)];
66007c478bd9Sstevel@tonic-gate 	mutex_enter(&connfp->connf_lock);
66017c478bd9Sstevel@tonic-gate 
66027c478bd9Sstevel@tonic-gate 	if (sel->ips_isv4) {
66037c478bd9Sstevel@tonic-gate 		connp = connfp->connf_head;
66047c478bd9Sstevel@tonic-gate 		while (connp != NULL) {
66057c478bd9Sstevel@tonic-gate 			if (IPCL_BIND_MATCH(connp, IPPROTO_TCP,
66067c478bd9Sstevel@tonic-gate 			    sel->ips_local_addr_v4, pptr[1]))
66077c478bd9Sstevel@tonic-gate 				break;
66087c478bd9Sstevel@tonic-gate 			connp = connp->conn_next;
66097c478bd9Sstevel@tonic-gate 		}
66107c478bd9Sstevel@tonic-gate 
66117c478bd9Sstevel@tonic-gate 		if (connp == NULL) {
66127c478bd9Sstevel@tonic-gate 			/* Match to all-zeroes. */
66137c478bd9Sstevel@tonic-gate 			v6addrmatch = &ipv6_all_zeros;
66147c478bd9Sstevel@tonic-gate 		}
66157c478bd9Sstevel@tonic-gate 	}
66167c478bd9Sstevel@tonic-gate 
66177c478bd9Sstevel@tonic-gate 	if (connp == NULL) {
66187c478bd9Sstevel@tonic-gate 		connp = connfp->connf_head;
66197c478bd9Sstevel@tonic-gate 		while (connp != NULL) {
66207c478bd9Sstevel@tonic-gate 			if (IPCL_BIND_MATCH_V6(connp, IPPROTO_TCP,
66217c478bd9Sstevel@tonic-gate 			    *v6addrmatch, pptr[1]))
66227c478bd9Sstevel@tonic-gate 				break;
66237c478bd9Sstevel@tonic-gate 			connp = connp->conn_next;
66247c478bd9Sstevel@tonic-gate 		}
66257c478bd9Sstevel@tonic-gate 
66267c478bd9Sstevel@tonic-gate 		if (connp == NULL) {
66277c478bd9Sstevel@tonic-gate 			mutex_exit(&connfp->connf_lock);
66287c478bd9Sstevel@tonic-gate 			return (NULL);
66297c478bd9Sstevel@tonic-gate 		}
66307c478bd9Sstevel@tonic-gate 	}
66317c478bd9Sstevel@tonic-gate 
66327c478bd9Sstevel@tonic-gate 	CONN_INC_REF(connp);
66337c478bd9Sstevel@tonic-gate 	mutex_exit(&connfp->connf_lock);
66347c478bd9Sstevel@tonic-gate 	return (connp);
66357c478bd9Sstevel@tonic-gate }
66367c478bd9Sstevel@tonic-gate 
66377c478bd9Sstevel@tonic-gate static void
6638f4b3ec61Sdh ipsec_tcp_pol(ipsec_selector_t *sel, ipsec_policy_t **ppp, ip_stack_t *ipst)
66397c478bd9Sstevel@tonic-gate {
6640a23b3b1bSToomas Soome 	connf_t		*connfp;
66417c478bd9Sstevel@tonic-gate 	conn_t		*connp;
66427c478bd9Sstevel@tonic-gate 	uint32_t	ports;
66437c478bd9Sstevel@tonic-gate 	uint16_t	*pptr = (uint16_t *)&ports;
66447c478bd9Sstevel@tonic-gate 
66457c478bd9Sstevel@tonic-gate 	/*
66467c478bd9Sstevel@tonic-gate 	 * Find TCP state in the following order:
66477c478bd9Sstevel@tonic-gate 	 * 1.) Connected conns.
66487c478bd9Sstevel@tonic-gate 	 * 2.) Listeners.
66497c478bd9Sstevel@tonic-gate 	 *
66507c478bd9Sstevel@tonic-gate 	 * Even though #2 will be the common case for inbound traffic, only
66517c478bd9Sstevel@tonic-gate 	 * following this order insures correctness.
66527c478bd9Sstevel@tonic-gate 	 */
66537c478bd9Sstevel@tonic-gate 
66547c478bd9Sstevel@tonic-gate 	if (sel->ips_local_port == 0)
66557c478bd9Sstevel@tonic-gate 		return;
66567c478bd9Sstevel@tonic-gate 
66577c478bd9Sstevel@tonic-gate 	/*
66587c478bd9Sstevel@tonic-gate 	 * 0 should be fport, 1 should be lport.  SRC is the local one here.
66597c478bd9Sstevel@tonic-gate 	 * See ipsec_construct_inverse_acquire() for details.
66607c478bd9Sstevel@tonic-gate 	 */
66617c478bd9Sstevel@tonic-gate 	pptr[0] = sel->ips_remote_port;
66627c478bd9Sstevel@tonic-gate 	pptr[1] = sel->ips_local_port;
66637c478bd9Sstevel@tonic-gate 
6664f4b3ec61Sdh 	connfp = &ipst->ips_ipcl_conn_fanout[
6665f4b3ec61Sdh 	    IPCL_CONN_HASH(sel->ips_remote_addr_v4, ports, ipst)];
66667c478bd9Sstevel@tonic-gate 	mutex_enter(&connfp->connf_lock);
66677c478bd9Sstevel@tonic-gate 	connp = connfp->connf_head;
66687c478bd9Sstevel@tonic-gate 
66697c478bd9Sstevel@tonic-gate 	if (sel->ips_isv4) {
66707c478bd9Sstevel@tonic-gate 		while (connp != NULL) {
66717c478bd9Sstevel@tonic-gate 			if (IPCL_CONN_MATCH(connp, IPPROTO_TCP,
66727c478bd9Sstevel@tonic-gate 			    sel->ips_remote_addr_v4, sel->ips_local_addr_v4,
66737c478bd9Sstevel@tonic-gate 			    ports))
66747c478bd9Sstevel@tonic-gate 				break;
66757c478bd9Sstevel@tonic-gate 			connp = connp->conn_next;
66767c478bd9Sstevel@tonic-gate 		}
66777c478bd9Sstevel@tonic-gate 	} else {
66787c478bd9Sstevel@tonic-gate 		while (connp != NULL) {
66797c478bd9Sstevel@tonic-gate 			if (IPCL_CONN_MATCH_V6(connp, IPPROTO_TCP,
66807c478bd9Sstevel@tonic-gate 			    sel->ips_remote_addr_v6, sel->ips_local_addr_v6,
66817c478bd9Sstevel@tonic-gate 			    ports))
66827c478bd9Sstevel@tonic-gate 				break;
66837c478bd9Sstevel@tonic-gate 			connp = connp->conn_next;
66847c478bd9Sstevel@tonic-gate 		}
66857c478bd9Sstevel@tonic-gate 	}
66867c478bd9Sstevel@tonic-gate 
66877c478bd9Sstevel@tonic-gate 	if (connp != NULL) {
66887c478bd9Sstevel@tonic-gate 		CONN_INC_REF(connp);
66897c478bd9Sstevel@tonic-gate 		mutex_exit(&connfp->connf_lock);
66907c478bd9Sstevel@tonic-gate 	} else {
66917c478bd9Sstevel@tonic-gate 		mutex_exit(&connfp->connf_lock);
66927c478bd9Sstevel@tonic-gate 
66937c478bd9Sstevel@tonic-gate 		/* Try the listen hash. */
6694f4b3ec61Sdh 		if ((connp = ipsec_find_listen_conn(pptr, sel, ipst)) == NULL)
66957c478bd9Sstevel@tonic-gate 			return;
66967c478bd9Sstevel@tonic-gate 	}
66977c478bd9Sstevel@tonic-gate 
66988810c16bSdanmcd 	ipsec_conn_pol(sel, connp, ppp);
6699bd670b35SErik Nordmark 	CONN_DEC_REF(connp);
67007c478bd9Sstevel@tonic-gate }
67017c478bd9Sstevel@tonic-gate 
67027c478bd9Sstevel@tonic-gate static void
6703f4b3ec61Sdh ipsec_sctp_pol(ipsec_selector_t *sel, ipsec_policy_t **ppp,
6704f4b3ec61Sdh     ip_stack_t *ipst)
67057c478bd9Sstevel@tonic-gate {
67067c478bd9Sstevel@tonic-gate 	conn_t		*connp;
67077c478bd9Sstevel@tonic-gate 	uint32_t	ports;
67087c478bd9Sstevel@tonic-gate 	uint16_t	*pptr = (uint16_t *)&ports;
67097c478bd9Sstevel@tonic-gate 
67107c478bd9Sstevel@tonic-gate 	/*
67117c478bd9Sstevel@tonic-gate 	 * Find SCP state in the following order:
67127c478bd9Sstevel@tonic-gate 	 * 1.) Connected conns.
67137c478bd9Sstevel@tonic-gate 	 * 2.) Listeners.
67147c478bd9Sstevel@tonic-gate 	 *
67157c478bd9Sstevel@tonic-gate 	 * Even though #2 will be the common case for inbound traffic, only
67167c478bd9Sstevel@tonic-gate 	 * following this order insures correctness.
67177c478bd9Sstevel@tonic-gate 	 */
67187c478bd9Sstevel@tonic-gate 
67197c478bd9Sstevel@tonic-gate 	if (sel->ips_local_port == 0)
67207c478bd9Sstevel@tonic-gate 		return;
67217c478bd9Sstevel@tonic-gate 
67227c478bd9Sstevel@tonic-gate 	/*
67237c478bd9Sstevel@tonic-gate 	 * 0 should be fport, 1 should be lport.  SRC is the local one here.
67247c478bd9Sstevel@tonic-gate 	 * See ipsec_construct_inverse_acquire() for details.
67257c478bd9Sstevel@tonic-gate 	 */
67267c478bd9Sstevel@tonic-gate 	pptr[0] = sel->ips_remote_port;
67277c478bd9Sstevel@tonic-gate 	pptr[1] = sel->ips_local_port;
67287c478bd9Sstevel@tonic-gate 
6729bd670b35SErik Nordmark 	/*
6730bd670b35SErik Nordmark 	 * For labeled systems, there's no need to check the
6731bd670b35SErik Nordmark 	 * label here.  It's known to be good as we checked
6732bd670b35SErik Nordmark 	 * before allowing the connection to become bound.
6733bd670b35SErik Nordmark 	 */
67347c478bd9Sstevel@tonic-gate 	if (sel->ips_isv4) {
67357c478bd9Sstevel@tonic-gate 		in6_addr_t	src, dst;
67367c478bd9Sstevel@tonic-gate 
67377c478bd9Sstevel@tonic-gate 		IN6_IPADDR_TO_V4MAPPED(sel->ips_remote_addr_v4, &dst);
67387c478bd9Sstevel@tonic-gate 		IN6_IPADDR_TO_V4MAPPED(sel->ips_local_addr_v4, &src);
6739e35d2278Svi 		connp = sctp_find_conn(&dst, &src, ports, ALL_ZONES,
6740bd670b35SErik Nordmark 		    0, ipst->ips_netstack->netstack_sctp);
67417c478bd9Sstevel@tonic-gate 	} else {
67427c478bd9Sstevel@tonic-gate 		connp = sctp_find_conn(&sel->ips_remote_addr_v6,
6743e35d2278Svi 		    &sel->ips_local_addr_v6, ports, ALL_ZONES,
6744bd670b35SErik Nordmark 		    0, ipst->ips_netstack->netstack_sctp);
67457c478bd9Sstevel@tonic-gate 	}
67467c478bd9Sstevel@tonic-gate 	if (connp == NULL)
67477c478bd9Sstevel@tonic-gate 		return;
67488810c16bSdanmcd 	ipsec_conn_pol(sel, connp, ppp);
6749bd670b35SErik Nordmark 	CONN_DEC_REF(connp);
67508810c16bSdanmcd }
67518810c16bSdanmcd 
67528810c16bSdanmcd /*
67538810c16bSdanmcd  * Fill in a query for the SPD (in "sel") using two PF_KEY address extensions.
67548810c16bSdanmcd  * Returns 0 or errno, and always sets *diagnostic to something appropriate
67558810c16bSdanmcd  * to PF_KEY.
67568810c16bSdanmcd  *
67578810c16bSdanmcd  * NOTE:  For right now, this function (and ipsec_selector_t for that matter),
67588810c16bSdanmcd  * ignore prefix lengths in the address extension.  Since we match on first-
67598810c16bSdanmcd  * entered policies, this shouldn't matter.  Also, since we normalize prefix-
67608810c16bSdanmcd  * set addresses to mask out the lower bits, we should get a suitable search
67618810c16bSdanmcd  * key for the SPD anyway.  This is the function to change if the assumption
67628810c16bSdanmcd  * about suitable search keys is wrong.
67638810c16bSdanmcd  */
67648810c16bSdanmcd static int
67658810c16bSdanmcd ipsec_get_inverse_acquire_sel(ipsec_selector_t *sel, sadb_address_t *srcext,
67668810c16bSdanmcd     sadb_address_t *dstext, int *diagnostic)
67678810c16bSdanmcd {
67688810c16bSdanmcd 	struct sockaddr_in *src, *dst;
67698810c16bSdanmcd 	struct sockaddr_in6 *src6, *dst6;
67708810c16bSdanmcd 
67718810c16bSdanmcd 	*diagnostic = 0;
67728810c16bSdanmcd 
67738810c16bSdanmcd 	bzero(sel, sizeof (*sel));
67748810c16bSdanmcd 	sel->ips_protocol = srcext->sadb_address_proto;
67758810c16bSdanmcd 	dst = (struct sockaddr_in *)(dstext + 1);
67768810c16bSdanmcd 	if (dst->sin_family == AF_INET6) {
67778810c16bSdanmcd 		dst6 = (struct sockaddr_in6 *)dst;
67788810c16bSdanmcd 		src6 = (struct sockaddr_in6 *)(srcext + 1);
67798810c16bSdanmcd 		if (src6->sin6_family != AF_INET6) {
67808810c16bSdanmcd 			*diagnostic = SADB_X_DIAGNOSTIC_AF_MISMATCH;
67818810c16bSdanmcd 			return (EINVAL);
67828810c16bSdanmcd 		}
67838810c16bSdanmcd 		sel->ips_remote_addr_v6 = dst6->sin6_addr;
67848810c16bSdanmcd 		sel->ips_local_addr_v6 = src6->sin6_addr;
67858810c16bSdanmcd 		if (sel->ips_protocol == IPPROTO_ICMPV6) {
67868810c16bSdanmcd 			sel->ips_is_icmp_inv_acq = 1;
67878810c16bSdanmcd 		} else {
67888810c16bSdanmcd 			sel->ips_remote_port = dst6->sin6_port;
67898810c16bSdanmcd 			sel->ips_local_port = src6->sin6_port;
67908810c16bSdanmcd 		}
67918810c16bSdanmcd 		sel->ips_isv4 = B_FALSE;
67928810c16bSdanmcd 	} else {
67938810c16bSdanmcd 		src = (struct sockaddr_in *)(srcext + 1);
67948810c16bSdanmcd 		if (src->sin_family != AF_INET) {
67958810c16bSdanmcd 			*diagnostic = SADB_X_DIAGNOSTIC_AF_MISMATCH;
67968810c16bSdanmcd 			return (EINVAL);
67978810c16bSdanmcd 		}
67988810c16bSdanmcd 		sel->ips_remote_addr_v4 = dst->sin_addr.s_addr;
67998810c16bSdanmcd 		sel->ips_local_addr_v4 = src->sin_addr.s_addr;
68008810c16bSdanmcd 		if (sel->ips_protocol == IPPROTO_ICMP) {
68018810c16bSdanmcd 			sel->ips_is_icmp_inv_acq = 1;
68028810c16bSdanmcd 		} else {
68038810c16bSdanmcd 			sel->ips_remote_port = dst->sin_port;
68048810c16bSdanmcd 			sel->ips_local_port = src->sin_port;
68058810c16bSdanmcd 		}
68068810c16bSdanmcd 		sel->ips_isv4 = B_TRUE;
68078810c16bSdanmcd 	}
68088810c16bSdanmcd 	return (0);
68098810c16bSdanmcd }
68108810c16bSdanmcd 
68118810c16bSdanmcd /*
68128810c16bSdanmcd  * We have encapsulation.
68138810c16bSdanmcd  * - Lookup tun_t by address and look for an associated
68148810c16bSdanmcd  *   tunnel policy
68158810c16bSdanmcd  * - If there are inner selectors
68168810c16bSdanmcd  *   - check ITPF_P_TUNNEL and ITPF_P_ACTIVE
68178810c16bSdanmcd  *   - Look up tunnel policy based on selectors
68188810c16bSdanmcd  * - Else
68198810c16bSdanmcd  *   - Sanity check the negotation
68208810c16bSdanmcd  *   - If appropriate, fall through to global policy
68218810c16bSdanmcd  */
68228810c16bSdanmcd static int
68238810c16bSdanmcd ipsec_tun_pol(ipsec_selector_t *sel, ipsec_policy_t **ppp,
68248810c16bSdanmcd     sadb_address_t *innsrcext, sadb_address_t *inndstext, ipsec_tun_pol_t *itp,
6825bd670b35SErik Nordmark     int *diagnostic)
68268810c16bSdanmcd {
68278810c16bSdanmcd 	int err;
68288810c16bSdanmcd 	ipsec_policy_head_t *polhead;
68298810c16bSdanmcd 
6830051c7f8aSDan McDonald 	*diagnostic = 0;
6831051c7f8aSDan McDonald 
68328810c16bSdanmcd 	/* Check for inner selectors and act appropriately */
68338810c16bSdanmcd 
68348810c16bSdanmcd 	if (innsrcext != NULL) {
68358810c16bSdanmcd 		/* Inner selectors present */
68368810c16bSdanmcd 		ASSERT(inndstext != NULL);
68378810c16bSdanmcd 		if ((itp == NULL) ||
68388810c16bSdanmcd 		    (itp->itp_flags & (ITPF_P_ACTIVE | ITPF_P_TUNNEL)) !=
68398810c16bSdanmcd 		    (ITPF_P_ACTIVE | ITPF_P_TUNNEL)) {
68408810c16bSdanmcd 			/*
68418810c16bSdanmcd 			 * If inner packet selectors, we must have negotiate
68428810c16bSdanmcd 			 * tunnel and active policy.  If the tunnel has
68438810c16bSdanmcd 			 * transport-mode policy set on it, or has no policy,
68448810c16bSdanmcd 			 * fail.
68458810c16bSdanmcd 			 */
68468810c16bSdanmcd 			return (ENOENT);
68478810c16bSdanmcd 		} else {
68488810c16bSdanmcd 			/*
68498810c16bSdanmcd 			 * Reset "sel" to indicate inner selectors.  Pass
68508810c16bSdanmcd 			 * inner PF_KEY address extensions for this to happen.
68518810c16bSdanmcd 			 */
68522b24ab6bSSebastien Roy 			if ((err = ipsec_get_inverse_acquire_sel(sel,
68532b24ab6bSSebastien Roy 			    innsrcext, inndstext, diagnostic)) != 0)
68548810c16bSdanmcd 				return (err);
68558810c16bSdanmcd 			/*
68568810c16bSdanmcd 			 * Now look for a tunnel policy based on those inner
68578810c16bSdanmcd 			 * selectors.  (Common code is below.)
68588810c16bSdanmcd 			 */
68598810c16bSdanmcd 		}
68608810c16bSdanmcd 	} else {
68618810c16bSdanmcd 		/* No inner selectors present */
68628810c16bSdanmcd 		if ((itp == NULL) || !(itp->itp_flags & ITPF_P_ACTIVE)) {
68638810c16bSdanmcd 			/*
68648810c16bSdanmcd 			 * Transport mode negotiation with no tunnel policy
68658810c16bSdanmcd 			 * configured - return to indicate a global policy
68668810c16bSdanmcd 			 * check is needed.
68678810c16bSdanmcd 			 */
68688810c16bSdanmcd 			return (0);
68698810c16bSdanmcd 		} else if (itp->itp_flags & ITPF_P_TUNNEL) {
68708810c16bSdanmcd 			/* Tunnel mode set with no inner selectors. */
68718810c16bSdanmcd 			return (ENOENT);
68728810c16bSdanmcd 		}
68738810c16bSdanmcd 		/*
6874bbf21555SRichard Lowe 		 * Else, this is a tunnel policy configured with ifconfig(8)
6875bbf21555SRichard Lowe 		 * or "negotiate transport" with ipsecconf(8).  We have an
68768810c16bSdanmcd 		 * itp with policy set based on any match, so don't bother
68778810c16bSdanmcd 		 * changing fields in "sel".
68788810c16bSdanmcd 		 */
68798810c16bSdanmcd 	}
68808810c16bSdanmcd 
68818810c16bSdanmcd 	ASSERT(itp != NULL);
68828810c16bSdanmcd 	polhead = itp->itp_policy;
68838810c16bSdanmcd 	ASSERT(polhead != NULL);
68848810c16bSdanmcd 	rw_enter(&polhead->iph_lock, RW_READER);
6885bd670b35SErik Nordmark 	*ppp = ipsec_find_policy_head(NULL, polhead, IPSEC_TYPE_INBOUND, sel);
68868810c16bSdanmcd 	rw_exit(&polhead->iph_lock);
68878810c16bSdanmcd 
68888810c16bSdanmcd 	/*
68898810c16bSdanmcd 	 * Don't default to global if we didn't find a matching policy entry.
68908810c16bSdanmcd 	 * Instead, send ENOENT, just like if we hit a transport-mode tunnel.
68918810c16bSdanmcd 	 */
68928810c16bSdanmcd 	if (*ppp == NULL)
68938810c16bSdanmcd 		return (ENOENT);
68948810c16bSdanmcd 
68958810c16bSdanmcd 	return (0);
68967c478bd9Sstevel@tonic-gate }
68977c478bd9Sstevel@tonic-gate 
6898bd670b35SErik Nordmark /*
6899bd670b35SErik Nordmark  * For sctp conn_faddr is the primary address, hence this is of limited
6900bd670b35SErik Nordmark  * use for sctp.
6901bd670b35SErik Nordmark  */
69027c478bd9Sstevel@tonic-gate static void
6903f4b3ec61Sdh ipsec_oth_pol(ipsec_selector_t *sel, ipsec_policy_t **ppp,
6904f4b3ec61Sdh     ip_stack_t *ipst)
69057c478bd9Sstevel@tonic-gate {
69067c478bd9Sstevel@tonic-gate 	boolean_t	isv4 = sel->ips_isv4;
69077c478bd9Sstevel@tonic-gate 	connf_t		*connfp;
69087c478bd9Sstevel@tonic-gate 	conn_t		*connp;
69097c478bd9Sstevel@tonic-gate 
69107c478bd9Sstevel@tonic-gate 	if (isv4) {
6911bd670b35SErik Nordmark 		connfp = &ipst->ips_ipcl_proto_fanout_v4[sel->ips_protocol];
69127c478bd9Sstevel@tonic-gate 	} else {
6913f4b3ec61Sdh 		connfp = &ipst->ips_ipcl_proto_fanout_v6[sel->ips_protocol];
69147c478bd9Sstevel@tonic-gate 	}
69157c478bd9Sstevel@tonic-gate 
69167c478bd9Sstevel@tonic-gate 	mutex_enter(&connfp->connf_lock);
69177c478bd9Sstevel@tonic-gate 	for (connp = connfp->connf_head; connp != NULL;
69187c478bd9Sstevel@tonic-gate 	    connp = connp->conn_next) {
6919bd670b35SErik Nordmark 		if (isv4) {
6920bd670b35SErik Nordmark 			if ((connp->conn_laddr_v4 == INADDR_ANY ||
6921bd670b35SErik Nordmark 			    connp->conn_laddr_v4 == sel->ips_local_addr_v4) &&
6922bd670b35SErik Nordmark 			    (connp->conn_faddr_v4 == INADDR_ANY ||
6923bd670b35SErik Nordmark 			    connp->conn_faddr_v4 == sel->ips_remote_addr_v4))
6924bd670b35SErik Nordmark 				break;
6925bd670b35SErik Nordmark 		} else {
6926bd670b35SErik Nordmark 			if ((IN6_IS_ADDR_UNSPECIFIED(&connp->conn_laddr_v6) ||
6927bd670b35SErik Nordmark 			    IN6_ARE_ADDR_EQUAL(&connp->conn_laddr_v6,
6928bd670b35SErik Nordmark 			    &sel->ips_local_addr_v6)) &&
6929bd670b35SErik Nordmark 			    (IN6_IS_ADDR_UNSPECIFIED(&connp->conn_faddr_v6) ||
6930bd670b35SErik Nordmark 			    IN6_ARE_ADDR_EQUAL(&connp->conn_faddr_v6,
6931bd670b35SErik Nordmark 			    &sel->ips_remote_addr_v6)))
6932bd670b35SErik Nordmark 				break;
69337c478bd9Sstevel@tonic-gate 		}
69347c478bd9Sstevel@tonic-gate 	}
69357c478bd9Sstevel@tonic-gate 	if (connp == NULL) {
69367c478bd9Sstevel@tonic-gate 		mutex_exit(&connfp->connf_lock);
69377c478bd9Sstevel@tonic-gate 		return;
69387c478bd9Sstevel@tonic-gate 	}
69397c478bd9Sstevel@tonic-gate 
69407c478bd9Sstevel@tonic-gate 	CONN_INC_REF(connp);
69417c478bd9Sstevel@tonic-gate 	mutex_exit(&connfp->connf_lock);
69427c478bd9Sstevel@tonic-gate 
69438810c16bSdanmcd 	ipsec_conn_pol(sel, connp, ppp);
6944bd670b35SErik Nordmark 	CONN_DEC_REF(connp);
69457c478bd9Sstevel@tonic-gate }
69467c478bd9Sstevel@tonic-gate 
69477c478bd9Sstevel@tonic-gate /*
69487c478bd9Sstevel@tonic-gate  * Construct an inverse ACQUIRE reply based on:
69497c478bd9Sstevel@tonic-gate  *
69507c478bd9Sstevel@tonic-gate  * 1.) Current global policy.
69517c478bd9Sstevel@tonic-gate  * 2.) An conn_t match depending on what all was passed in the extv[].
69528810c16bSdanmcd  * 3.) A tunnel's policy head.
69537c478bd9Sstevel@tonic-gate  * ...
69547c478bd9Sstevel@tonic-gate  * N.) Other stuff TBD (e.g. identities)
69557c478bd9Sstevel@tonic-gate  *
69567c478bd9Sstevel@tonic-gate  * If there is an error, set sadb_msg_errno and sadb_x_msg_diagnostic
69577c478bd9Sstevel@tonic-gate  * in this function so the caller can extract them where appropriately.
69587c478bd9Sstevel@tonic-gate  *
69597c478bd9Sstevel@tonic-gate  * The SRC address is the local one - just like an outbound ACQUIRE message.
69605d3b8cb7SBill Sommerfeld  *
69615d3b8cb7SBill Sommerfeld  * XXX MLS: key management supplies a label which we just reflect back up
69625d3b8cb7SBill Sommerfeld  * again.  clearly we need to involve the label in the rest of the checks.
69637c478bd9Sstevel@tonic-gate  */
69647c478bd9Sstevel@tonic-gate mblk_t *
6965f4b3ec61Sdh ipsec_construct_inverse_acquire(sadb_msg_t *samsg, sadb_ext_t *extv[],
6966f4b3ec61Sdh     netstack_t *ns)
69677c478bd9Sstevel@tonic-gate {
69687c478bd9Sstevel@tonic-gate 	int err;
69697c478bd9Sstevel@tonic-gate 	int diagnostic;
69707c478bd9Sstevel@tonic-gate 	sadb_address_t *srcext = (sadb_address_t *)extv[SADB_EXT_ADDRESS_SRC],
69718810c16bSdanmcd 	    *dstext = (sadb_address_t *)extv[SADB_EXT_ADDRESS_DST],
69728810c16bSdanmcd 	    *innsrcext = (sadb_address_t *)extv[SADB_X_EXT_ADDRESS_INNER_SRC],
69738810c16bSdanmcd 	    *inndstext = (sadb_address_t *)extv[SADB_X_EXT_ADDRESS_INNER_DST];
69745d3b8cb7SBill Sommerfeld 	sadb_sens_t *sens = (sadb_sens_t *)extv[SADB_EXT_SENSITIVITY];
69758810c16bSdanmcd 	struct sockaddr_in6 *src, *dst;
69768810c16bSdanmcd 	struct sockaddr_in6 *isrc, *idst;
69778810c16bSdanmcd 	ipsec_tun_pol_t *itp = NULL;
69788810c16bSdanmcd 	ipsec_policy_t *pp = NULL;
69798810c16bSdanmcd 	ipsec_selector_t sel, isel;
6980051c7f8aSDan McDonald 	mblk_t *retmp = NULL;
6981f4b3ec61Sdh 	ip_stack_t	*ipst = ns->netstack_ip;
69827c478bd9Sstevel@tonic-gate 
69835d3b8cb7SBill Sommerfeld 
69848810c16bSdanmcd 	/* Normalize addresses */
6985f4b3ec61Sdh 	if (sadb_addrcheck(NULL, (mblk_t *)samsg, (sadb_ext_t *)srcext, 0, ns)
6986f4b3ec61Sdh 	    == KS_IN_ADDR_UNKNOWN) {
69878810c16bSdanmcd 		err = EINVAL;
69888810c16bSdanmcd 		diagnostic = SADB_X_DIAGNOSTIC_BAD_SRC;
69898810c16bSdanmcd 		goto bail;
69908810c16bSdanmcd 	}
69918810c16bSdanmcd 	src = (struct sockaddr_in6 *)(srcext + 1);
6992f4b3ec61Sdh 	if (sadb_addrcheck(NULL, (mblk_t *)samsg, (sadb_ext_t *)dstext, 0, ns)
6993f4b3ec61Sdh 	    == KS_IN_ADDR_UNKNOWN) {
69948810c16bSdanmcd 		err = EINVAL;
69958810c16bSdanmcd 		diagnostic = SADB_X_DIAGNOSTIC_BAD_DST;
69968810c16bSdanmcd 		goto bail;
69978810c16bSdanmcd 	}
69988810c16bSdanmcd 	dst = (struct sockaddr_in6 *)(dstext + 1);
69998810c16bSdanmcd 	if (src->sin6_family != dst->sin6_family) {
70008810c16bSdanmcd 		err = EINVAL;
70018810c16bSdanmcd 		diagnostic = SADB_X_DIAGNOSTIC_AF_MISMATCH;
70028810c16bSdanmcd 		goto bail;
70038810c16bSdanmcd 	}
70048810c16bSdanmcd 
70058810c16bSdanmcd 	/* Check for tunnel mode and act appropriately */
70068810c16bSdanmcd 	if (innsrcext != NULL) {
70078810c16bSdanmcd 		if (inndstext == NULL) {
70087c478bd9Sstevel@tonic-gate 			err = EINVAL;
70098810c16bSdanmcd 			diagnostic = SADB_X_DIAGNOSTIC_MISSING_INNER_DST;
70107c478bd9Sstevel@tonic-gate 			goto bail;
70117c478bd9Sstevel@tonic-gate 		}
70128810c16bSdanmcd 		if (sadb_addrcheck(NULL, (mblk_t *)samsg,
7013437220cdSdanmcd 		    (sadb_ext_t *)innsrcext, 0, ns) == KS_IN_ADDR_UNKNOWN) {
70148810c16bSdanmcd 			err = EINVAL;
70158810c16bSdanmcd 			diagnostic = SADB_X_DIAGNOSTIC_MALFORMED_INNER_SRC;
70168810c16bSdanmcd 			goto bail;
70177c478bd9Sstevel@tonic-gate 		}
70188810c16bSdanmcd 		isrc = (struct sockaddr_in6 *)(innsrcext + 1);
70198810c16bSdanmcd 		if (sadb_addrcheck(NULL, (mblk_t *)samsg,
7020437220cdSdanmcd 		    (sadb_ext_t *)inndstext, 0, ns) == KS_IN_ADDR_UNKNOWN) {
70217c478bd9Sstevel@tonic-gate 			err = EINVAL;
70228810c16bSdanmcd 			diagnostic = SADB_X_DIAGNOSTIC_MALFORMED_INNER_DST;
70237c478bd9Sstevel@tonic-gate 			goto bail;
70247c478bd9Sstevel@tonic-gate 		}
70258810c16bSdanmcd 		idst = (struct sockaddr_in6 *)(inndstext + 1);
70268810c16bSdanmcd 		if (isrc->sin6_family != idst->sin6_family) {
70278810c16bSdanmcd 			err = EINVAL;
70288810c16bSdanmcd 			diagnostic = SADB_X_DIAGNOSTIC_INNER_AF_MISMATCH;
70298810c16bSdanmcd 			goto bail;
70308810c16bSdanmcd 		}
70318810c16bSdanmcd 		if (isrc->sin6_family != AF_INET &&
70328810c16bSdanmcd 		    isrc->sin6_family != AF_INET6) {
70338810c16bSdanmcd 			err = EINVAL;
70348810c16bSdanmcd 			diagnostic = SADB_X_DIAGNOSTIC_BAD_INNER_SRC_AF;
70358810c16bSdanmcd 			goto bail;
70367c478bd9Sstevel@tonic-gate 		}
70378810c16bSdanmcd 	} else if (inndstext != NULL) {
7038437220cdSdanmcd 		err = EINVAL;
7039437220cdSdanmcd 		diagnostic = SADB_X_DIAGNOSTIC_MISSING_INNER_SRC;
7040437220cdSdanmcd 		goto bail;
70418810c16bSdanmcd 	}
70428810c16bSdanmcd 
70438810c16bSdanmcd 	/* Get selectors first, based on outer addresses */
70448810c16bSdanmcd 	err = ipsec_get_inverse_acquire_sel(&sel, srcext, dstext, &diagnostic);
70458810c16bSdanmcd 	if (err != 0)
70468810c16bSdanmcd 		goto bail;
70478810c16bSdanmcd 
70488810c16bSdanmcd 	/* Check for tunnel mode mismatches. */
70498810c16bSdanmcd 	if (innsrcext != NULL &&
70508810c16bSdanmcd 	    ((isrc->sin6_family == AF_INET &&
7051437220cdSdanmcd 	    sel.ips_protocol != IPPROTO_ENCAP && sel.ips_protocol != 0) ||
7052437220cdSdanmcd 	    (isrc->sin6_family == AF_INET6 &&
7053437220cdSdanmcd 	    sel.ips_protocol != IPPROTO_IPV6 && sel.ips_protocol != 0))) {
70548810c16bSdanmcd 		err = EPROTOTYPE;
70558810c16bSdanmcd 		goto bail;
70567c478bd9Sstevel@tonic-gate 	}
70577c478bd9Sstevel@tonic-gate 
70587c478bd9Sstevel@tonic-gate 	/*
70597c478bd9Sstevel@tonic-gate 	 * Okay, we have the addresses and other selector information.
70607c478bd9Sstevel@tonic-gate 	 * Let's first find a conn...
70617c478bd9Sstevel@tonic-gate 	 */
70628810c16bSdanmcd 	pp = NULL;
70637c478bd9Sstevel@tonic-gate 	switch (sel.ips_protocol) {
70647c478bd9Sstevel@tonic-gate 	case IPPROTO_TCP:
7065f4b3ec61Sdh 		ipsec_tcp_pol(&sel, &pp, ipst);
70667c478bd9Sstevel@tonic-gate 		break;
70677c478bd9Sstevel@tonic-gate 	case IPPROTO_UDP:
7068f4b3ec61Sdh 		ipsec_udp_pol(&sel, &pp, ipst);
70697c478bd9Sstevel@tonic-gate 		break;
70707c478bd9Sstevel@tonic-gate 	case IPPROTO_SCTP:
7071f4b3ec61Sdh 		ipsec_sctp_pol(&sel, &pp, ipst);
70728810c16bSdanmcd 		break;
70738810c16bSdanmcd 	case IPPROTO_ENCAP:
70748810c16bSdanmcd 	case IPPROTO_IPV6:
70758810c16bSdanmcd 		/*
70768810c16bSdanmcd 		 * Assume sel.ips_remote_addr_* has the right address at
70778810c16bSdanmcd 		 * that exact position.
70788810c16bSdanmcd 		 */
70792b24ab6bSSebastien Roy 		itp = itp_get_byaddr((uint32_t *)(&sel.ips_local_addr_v6),
70802b24ab6bSSebastien Roy 		    (uint32_t *)(&sel.ips_remote_addr_v6), src->sin6_family,
70812b24ab6bSSebastien Roy 		    ipst);
70822b24ab6bSSebastien Roy 
70838810c16bSdanmcd 		if (innsrcext == NULL) {
70848810c16bSdanmcd 			/*
70858810c16bSdanmcd 			 * Transport-mode tunnel, make sure we fake out isel
70868810c16bSdanmcd 			 * to contain something based on the outer protocol.
70878810c16bSdanmcd 			 */
70888810c16bSdanmcd 			bzero(&isel, sizeof (isel));
70898810c16bSdanmcd 			isel.ips_isv4 = (sel.ips_protocol == IPPROTO_ENCAP);
70908810c16bSdanmcd 		} /* Else isel is initialized by ipsec_tun_pol(). */
70918810c16bSdanmcd 		err = ipsec_tun_pol(&isel, &pp, innsrcext, inndstext, itp,
7092bd670b35SErik Nordmark 		    &diagnostic);
70938810c16bSdanmcd 		/*
70948810c16bSdanmcd 		 * NOTE:  isel isn't used for now, but in RFC 430x IPsec, it
70958810c16bSdanmcd 		 * may be.
70968810c16bSdanmcd 		 */
70978810c16bSdanmcd 		if (err != 0)
70988810c16bSdanmcd 			goto bail;
70997c478bd9Sstevel@tonic-gate 		break;
71007c478bd9Sstevel@tonic-gate 	default:
7101f4b3ec61Sdh 		ipsec_oth_pol(&sel, &pp, ipst);
71027c478bd9Sstevel@tonic-gate 		break;
71037c478bd9Sstevel@tonic-gate 	}
71047c478bd9Sstevel@tonic-gate 
71057c478bd9Sstevel@tonic-gate 	/*
71068810c16bSdanmcd 	 * If we didn't find a matching conn_t or other policy head, take a
71078810c16bSdanmcd 	 * look in the global policy.
71087c478bd9Sstevel@tonic-gate 	 */
71098810c16bSdanmcd 	if (pp == NULL) {
7110bd670b35SErik Nordmark 		pp = ipsec_find_policy(IPSEC_TYPE_OUTBOUND, NULL, &sel, ns);
71117c478bd9Sstevel@tonic-gate 		if (pp == NULL) {
71127c478bd9Sstevel@tonic-gate 			/* There's no global policy. */
71137c478bd9Sstevel@tonic-gate 			err = ENOENT;
71147c478bd9Sstevel@tonic-gate 			diagnostic = 0;
71157c478bd9Sstevel@tonic-gate 			goto bail;
71167c478bd9Sstevel@tonic-gate 		}
71177c478bd9Sstevel@tonic-gate 	}
71187c478bd9Sstevel@tonic-gate 
7119b7daf799SDan McDonald 	ASSERT(pp != NULL);
7120b7daf799SDan McDonald 	retmp = sadb_acquire_msg_base(0, 0, samsg->sadb_msg_seq,
7121b7daf799SDan McDonald 	    samsg->sadb_msg_pid);
7122b7daf799SDan McDonald 	if (retmp != NULL) {
7123b7daf799SDan McDonald 		/* Remove KEYSOCK_OUT, because caller constructs it instead. */
7124b7daf799SDan McDonald 		mblk_t *kso = retmp;
7125b7daf799SDan McDonald 
7126b7daf799SDan McDonald 		retmp = retmp->b_cont;
7127b7daf799SDan McDonald 		freeb(kso);
7128b7daf799SDan McDonald 		/* Append addresses... */
7129b7daf799SDan McDonald 		retmp->b_cont = sadb_acquire_msg_common(&sel, pp, NULL,
7130b7daf799SDan McDonald 		    (itp != NULL && (itp->itp_flags & ITPF_P_TUNNEL)), NULL,
7131b7daf799SDan McDonald 		    sens);
7132b7daf799SDan McDonald 		if (retmp->b_cont == NULL) {
7133b7daf799SDan McDonald 			freemsg(retmp);
7134b7daf799SDan McDonald 			retmp = NULL;
7135b7daf799SDan McDonald 		}
7136b7daf799SDan McDonald 		/* And the policy result. */
7137b7daf799SDan McDonald 		retmp->b_cont->b_cont =
7138b7daf799SDan McDonald 		    sadb_acquire_extended_prop(pp->ipsp_act, ns);
7139b7daf799SDan McDonald 		if (retmp->b_cont->b_cont == NULL) {
7140b7daf799SDan McDonald 			freemsg(retmp);
7141b7daf799SDan McDonald 			retmp = NULL;
7142b7daf799SDan McDonald 		}
7143b7daf799SDan McDonald 		((sadb_msg_t *)retmp->b_rptr)->sadb_msg_len =
7144b7daf799SDan McDonald 		    SADB_8TO64(msgsize(retmp));
7145b7daf799SDan McDonald 	}
7146b7daf799SDan McDonald 
71477c478bd9Sstevel@tonic-gate 	if (pp != NULL) {
7148bd670b35SErik Nordmark 		IPPOL_REFRELE(pp);
71497c478bd9Sstevel@tonic-gate 	}
7150051c7f8aSDan McDonald 	ASSERT(err == 0 && diagnostic == 0);
7151051c7f8aSDan McDonald 	if (retmp == NULL)
7152051c7f8aSDan McDonald 		err = ENOMEM;
7153051c7f8aSDan McDonald bail:
71542b24ab6bSSebastien Roy 	if (itp != NULL) {
71552b24ab6bSSebastien Roy 		ITP_REFRELE(itp, ns);
71562b24ab6bSSebastien Roy 	}
71578810c16bSdanmcd 	samsg->sadb_msg_errno = (uint8_t)err;
71588810c16bSdanmcd 	samsg->sadb_x_msg_diagnostic = (uint16_t)diagnostic;
7159051c7f8aSDan McDonald 	return (retmp);
71607c478bd9Sstevel@tonic-gate }
71617c478bd9Sstevel@tonic-gate 
71627c478bd9Sstevel@tonic-gate /*
7163a14de6c8SDan McDonald  * ipsa_lpkt is a one-element queue, only manipulated by the next two
7164a14de6c8SDan McDonald  * functions.  They have to hold the ipsa_lock because of potential races
7165a14de6c8SDan McDonald  * between key management using SADB_UPDATE, and inbound packets that may
7166a14de6c8SDan McDonald  * queue up on the larval SA (hence the 'l' in "lpkt").
71677c478bd9Sstevel@tonic-gate  */
71687c478bd9Sstevel@tonic-gate 
71697c478bd9Sstevel@tonic-gate /*
7170930af642SDan McDonald  * sadb_set_lpkt:
7171930af642SDan McDonald  *
7172930af642SDan McDonald  * Returns the passed-in packet if the SA is no longer larval.
7173930af642SDan McDonald  *
7174930af642SDan McDonald  * Returns NULL if the SA is larval, and needs to be swapped into the SA for
7175930af642SDan McDonald  * processing after an SADB_UPDATE.
71767c478bd9Sstevel@tonic-gate  */
7177930af642SDan McDonald mblk_t *
7178bd670b35SErik Nordmark sadb_set_lpkt(ipsa_t *ipsa, mblk_t *npkt, ip_recv_attr_t *ira)
71797c478bd9Sstevel@tonic-gate {
7180bd670b35SErik Nordmark 	mblk_t		*opkt;
71817c478bd9Sstevel@tonic-gate 
7182a14de6c8SDan McDonald 	mutex_enter(&ipsa->ipsa_lock);
7183930af642SDan McDonald 	opkt = ipsa->ipsa_lpkt;
7184930af642SDan McDonald 	if (ipsa->ipsa_state == IPSA_STATE_LARVAL) {
7185930af642SDan McDonald 		/*
7186930af642SDan McDonald 		 * Consume npkt and place it in the LARVAL SA's inbound
7187930af642SDan McDonald 		 * packet slot.
7188930af642SDan McDonald 		 */
7189bd670b35SErik Nordmark 		mblk_t	*attrmp;
7190bd670b35SErik Nordmark 
7191bd670b35SErik Nordmark 		attrmp = ip_recv_attr_to_mblk(ira);
7192bd670b35SErik Nordmark 		if (attrmp == NULL) {
7193bd670b35SErik Nordmark 			ill_t *ill = ira->ira_ill;
7194bd670b35SErik Nordmark 
7195bd670b35SErik Nordmark 			BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
7196bd670b35SErik Nordmark 			ip_drop_input("ipIfStatsInDiscards", npkt, ill);
7197bd670b35SErik Nordmark 			freemsg(npkt);
7198bd670b35SErik Nordmark 			opkt = NULL;
7199bd670b35SErik Nordmark 		} else {
7200bd670b35SErik Nordmark 			ASSERT(attrmp->b_cont == NULL);
7201bd670b35SErik Nordmark 			attrmp->b_cont = npkt;
7202930af642SDan McDonald 			ipsa->ipsa_lpkt = attrmp;
7203bd670b35SErik Nordmark 		}
7204930af642SDan McDonald 		npkt = NULL;
7205a14de6c8SDan McDonald 	} else {
7206930af642SDan McDonald 		/*
7207930af642SDan McDonald 		 * If not larval, we lost the race.  NOTE: ipsa_lpkt may still
7208930af642SDan McDonald 		 * have been non-NULL in the non-larval case, because of
7209930af642SDan McDonald 		 * inbound packets arriving prior to sadb_common_add()
7210930af642SDan McDonald 		 * transferring the SA completely out of larval state, but
7211930af642SDan McDonald 		 * after lpkt was grabbed by the AH/ESP-specific add routines.
7212930af642SDan McDonald 		 * We should clear the old ipsa_lpkt in this case to make sure
7213930af642SDan McDonald 		 * that it doesn't linger on the now-MATURE IPsec SA, or get
7214930af642SDan McDonald 		 * picked up as an out-of-order packet.
7215930af642SDan McDonald 		 */
7216930af642SDan McDonald 		ipsa->ipsa_lpkt = NULL;
7217a14de6c8SDan McDonald 	}
7218a14de6c8SDan McDonald 	mutex_exit(&ipsa->ipsa_lock);
72197c478bd9Sstevel@tonic-gate 
7220bd670b35SErik Nordmark 	if (opkt != NULL) {
7221930af642SDan McDonald 		ipsec_stack_t	*ipss;
7222930af642SDan McDonald 
7223930af642SDan McDonald 		ipss = ira->ira_ill->ill_ipst->ips_netstack->netstack_ipsec;
7224bd670b35SErik Nordmark 		opkt = ip_recv_attr_free_mblk(opkt);
7225bd670b35SErik Nordmark 		ip_drop_packet(opkt, B_TRUE, ira->ira_ill,
7226bd670b35SErik Nordmark 		    DROPPER(ipss, ipds_sadb_inlarval_replace),
7227bd670b35SErik Nordmark 		    &ipss->ipsec_sadb_dropper);
7228bd670b35SErik Nordmark 	}
7229930af642SDan McDonald 	return (npkt);
72307c478bd9Sstevel@tonic-gate }
72317c478bd9Sstevel@tonic-gate 
72327c478bd9Sstevel@tonic-gate /*
72337c478bd9Sstevel@tonic-gate  * sadb_clear_lpkt: Atomically clear ipsa->ipsa_lpkt and return the
72347c478bd9Sstevel@tonic-gate  * previous value.
72357c478bd9Sstevel@tonic-gate  */
72367c478bd9Sstevel@tonic-gate mblk_t *
72377c478bd9Sstevel@tonic-gate sadb_clear_lpkt(ipsa_t *ipsa)
72387c478bd9Sstevel@tonic-gate {
72397c478bd9Sstevel@tonic-gate 	mblk_t *opkt;
72407c478bd9Sstevel@tonic-gate 
7241a14de6c8SDan McDonald 	mutex_enter(&ipsa->ipsa_lock);
7242a14de6c8SDan McDonald 	opkt = ipsa->ipsa_lpkt;
7243a14de6c8SDan McDonald 	ipsa->ipsa_lpkt = NULL;
7244a14de6c8SDan McDonald 	mutex_exit(&ipsa->ipsa_lock);
72457c478bd9Sstevel@tonic-gate 	return (opkt);
72467c478bd9Sstevel@tonic-gate }
72477c478bd9Sstevel@tonic-gate 
7248d74f5ecaSDan McDonald /*
7249d74f5ecaSDan McDonald  * Buffer a packet that's in IDLE state as set by Solaris Clustering.
7250d74f5ecaSDan McDonald  */
72519c2c14abSThejaswini Singarajipura void
7252bd670b35SErik Nordmark sadb_buf_pkt(ipsa_t *ipsa, mblk_t *bpkt, ip_recv_attr_t *ira)
72539c2c14abSThejaswini Singarajipura {
7254bd670b35SErik Nordmark 	netstack_t	*ns = ira->ira_ill->ill_ipst->ips_netstack;
72559c2c14abSThejaswini Singarajipura 	ipsec_stack_t   *ipss = ns->netstack_ipsec;
7256d74f5ecaSDan McDonald 	in6_addr_t *srcaddr = (in6_addr_t *)(&ipsa->ipsa_srcaddr);
7257d74f5ecaSDan McDonald 	in6_addr_t *dstaddr = (in6_addr_t *)(&ipsa->ipsa_dstaddr);
7258bd670b35SErik Nordmark 	mblk_t		*mp;
72599c2c14abSThejaswini Singarajipura 
72609c2c14abSThejaswini Singarajipura 	ASSERT(ipsa->ipsa_state == IPSA_STATE_IDLE);
7261d74f5ecaSDan McDonald 
7262d74f5ecaSDan McDonald 	if (cl_inet_idlesa == NULL) {
7263bd670b35SErik Nordmark 		ip_drop_packet(bpkt, B_TRUE, ira->ira_ill,
7264d74f5ecaSDan McDonald 		    DROPPER(ipss, ipds_sadb_inidle_overflow),
7265d74f5ecaSDan McDonald 		    &ipss->ipsec_sadb_dropper);
7266d74f5ecaSDan McDonald 		return;
7267d74f5ecaSDan McDonald 	}
7268d74f5ecaSDan McDonald 
72698e4b770fSLu Huafeng 	cl_inet_idlesa(ns->netstack_stackid,
72708e4b770fSLu Huafeng 	    (ipsa->ipsa_type == SADB_SATYPE_AH) ? IPPROTO_AH : IPPROTO_ESP,
72718e4b770fSLu Huafeng 	    ipsa->ipsa_spi, ipsa->ipsa_addrfam, *srcaddr, *dstaddr, NULL);
7272d74f5ecaSDan McDonald 
7273bd670b35SErik Nordmark 	mp = ip_recv_attr_to_mblk(ira);
7274bd670b35SErik Nordmark 	if (mp == NULL) {
7275bd670b35SErik Nordmark 		ip_drop_packet(bpkt, B_TRUE, ira->ira_ill,
7276bd670b35SErik Nordmark 		    DROPPER(ipss, ipds_sadb_inidle_overflow),
7277bd670b35SErik Nordmark 		    &ipss->ipsec_sadb_dropper);
7278bd670b35SErik Nordmark 		return;
7279bd670b35SErik Nordmark 	}
7280bd670b35SErik Nordmark 	linkb(mp, bpkt);
728173184bc7SDan McDonald 
72829c2c14abSThejaswini Singarajipura 	mutex_enter(&ipsa->ipsa_lock);
72839c2c14abSThejaswini Singarajipura 	ipsa->ipsa_mblkcnt++;
72849c2c14abSThejaswini Singarajipura 	if (ipsa->ipsa_bpkt_head == NULL) {
72859c2c14abSThejaswini Singarajipura 		ipsa->ipsa_bpkt_head = ipsa->ipsa_bpkt_tail = bpkt;
72869c2c14abSThejaswini Singarajipura 	} else {
72879c2c14abSThejaswini Singarajipura 		ipsa->ipsa_bpkt_tail->b_next = bpkt;
72889c2c14abSThejaswini Singarajipura 		ipsa->ipsa_bpkt_tail = bpkt;
72899c2c14abSThejaswini Singarajipura 		if (ipsa->ipsa_mblkcnt > SADB_MAX_IDLEPKTS) {
72909c2c14abSThejaswini Singarajipura 			mblk_t *tmp;
7291bd670b35SErik Nordmark 
72929c2c14abSThejaswini Singarajipura 			tmp = ipsa->ipsa_bpkt_head;
72939c2c14abSThejaswini Singarajipura 			ipsa->ipsa_bpkt_head = ipsa->ipsa_bpkt_head->b_next;
7294bd670b35SErik Nordmark 			tmp = ip_recv_attr_free_mblk(tmp);
7295bd670b35SErik Nordmark 			ip_drop_packet(tmp, B_TRUE, NULL,
72969c2c14abSThejaswini Singarajipura 			    DROPPER(ipss, ipds_sadb_inidle_overflow),
72979c2c14abSThejaswini Singarajipura 			    &ipss->ipsec_sadb_dropper);
72989c2c14abSThejaswini Singarajipura 			ipsa->ipsa_mblkcnt --;
72999c2c14abSThejaswini Singarajipura 		}
73009c2c14abSThejaswini Singarajipura 	}
73019c2c14abSThejaswini Singarajipura 	mutex_exit(&ipsa->ipsa_lock);
73029c2c14abSThejaswini Singarajipura }
73039c2c14abSThejaswini Singarajipura 
73049c2c14abSThejaswini Singarajipura /*
73059c2c14abSThejaswini Singarajipura  * Stub function that taskq_dispatch() invokes to take the mblk (in arg)
73069c2c14abSThejaswini Singarajipura  * and put into STREAMS again.
73079c2c14abSThejaswini Singarajipura  */
73089c2c14abSThejaswini Singarajipura void
73099c2c14abSThejaswini Singarajipura sadb_clear_buf_pkt(void *ipkt)
73109c2c14abSThejaswini Singarajipura {
73119c2c14abSThejaswini Singarajipura 	mblk_t	*tmp, *buf_pkt;
7312bd670b35SErik Nordmark 	ip_recv_attr_t	iras;
73139c2c14abSThejaswini Singarajipura 
73149c2c14abSThejaswini Singarajipura 	buf_pkt = (mblk_t *)ipkt;
73159c2c14abSThejaswini Singarajipura 
73169c2c14abSThejaswini Singarajipura 	while (buf_pkt != NULL) {
7317bd670b35SErik Nordmark 		mblk_t *data_mp;
7318bd670b35SErik Nordmark 
73199c2c14abSThejaswini Singarajipura 		tmp = buf_pkt->b_next;
73209c2c14abSThejaswini Singarajipura 		buf_pkt->b_next = NULL;
7321bd670b35SErik Nordmark 
7322bd670b35SErik Nordmark 		data_mp = buf_pkt->b_cont;
7323bd670b35SErik Nordmark 		buf_pkt->b_cont = NULL;
7324bd670b35SErik Nordmark 		if (!ip_recv_attr_from_mblk(buf_pkt, &iras)) {
7325bd670b35SErik Nordmark 			/* The ill or ip_stack_t disappeared on us. */
7326bd670b35SErik Nordmark 			ip_drop_input("ip_recv_attr_from_mblk", data_mp, NULL);
7327bd670b35SErik Nordmark 			freemsg(data_mp);
7328bd670b35SErik Nordmark 		} else {
7329bd670b35SErik Nordmark 			ip_input_post_ipsec(data_mp, &iras);
7330bd670b35SErik Nordmark 		}
7331bd670b35SErik Nordmark 		ira_cleanup(&iras, B_TRUE);
73329c2c14abSThejaswini Singarajipura 		buf_pkt = tmp;
73339c2c14abSThejaswini Singarajipura 	}
73349c2c14abSThejaswini Singarajipura }
73357c478bd9Sstevel@tonic-gate /*
73367c478bd9Sstevel@tonic-gate  * Walker callback used by sadb_alg_update() to free/create crypto
73377c478bd9Sstevel@tonic-gate  * context template when a crypto software provider is removed or
73387c478bd9Sstevel@tonic-gate  * added.
73397c478bd9Sstevel@tonic-gate  */
73407c478bd9Sstevel@tonic-gate 
73417c478bd9Sstevel@tonic-gate struct sadb_update_alg_state {
73427c478bd9Sstevel@tonic-gate 	ipsec_algtype_t alg_type;
73437c478bd9Sstevel@tonic-gate 	uint8_t alg_id;
73447c478bd9Sstevel@tonic-gate 	boolean_t is_added;
7345bd670b35SErik Nordmark 	boolean_t async_auth;
7346bd670b35SErik Nordmark 	boolean_t async_encr;
73477c478bd9Sstevel@tonic-gate };
73487c478bd9Sstevel@tonic-gate 
73497c478bd9Sstevel@tonic-gate static void
73507c478bd9Sstevel@tonic-gate sadb_alg_update_cb(isaf_t *head, ipsa_t *entry, void *cookie)
73517c478bd9Sstevel@tonic-gate {
73527c478bd9Sstevel@tonic-gate 	struct sadb_update_alg_state *update_state =
73537c478bd9Sstevel@tonic-gate 	    (struct sadb_update_alg_state *)cookie;
73547c478bd9Sstevel@tonic-gate 	crypto_ctx_template_t *ctx_tmpl = NULL;
73557c478bd9Sstevel@tonic-gate 
73567c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&head->isaf_lock));
73577c478bd9Sstevel@tonic-gate 
73587c478bd9Sstevel@tonic-gate 	if (entry->ipsa_state == IPSA_STATE_LARVAL)
73597c478bd9Sstevel@tonic-gate 		return;
73607c478bd9Sstevel@tonic-gate 
73617c478bd9Sstevel@tonic-gate 	mutex_enter(&entry->ipsa_lock);
73627c478bd9Sstevel@tonic-gate 
7363bd670b35SErik Nordmark 	if ((entry->ipsa_encr_alg != SADB_EALG_NONE && entry->ipsa_encr_alg !=
7364bd670b35SErik Nordmark 	    SADB_EALG_NULL && update_state->async_encr) ||
7365bd670b35SErik Nordmark 	    (entry->ipsa_auth_alg != SADB_AALG_NONE &&
7366bd670b35SErik Nordmark 	    update_state->async_auth)) {
7367bd670b35SErik Nordmark 		entry->ipsa_flags |= IPSA_F_ASYNC;
7368bd670b35SErik Nordmark 	} else {
7369bd670b35SErik Nordmark 		entry->ipsa_flags &= ~IPSA_F_ASYNC;
7370bd670b35SErik Nordmark 	}
7371bd670b35SErik Nordmark 
73727c478bd9Sstevel@tonic-gate 	switch (update_state->alg_type) {
73737c478bd9Sstevel@tonic-gate 	case IPSEC_ALG_AUTH:
73747c478bd9Sstevel@tonic-gate 		if (entry->ipsa_auth_alg == update_state->alg_id)
73757c478bd9Sstevel@tonic-gate 			ctx_tmpl = &entry->ipsa_authtmpl;
73767c478bd9Sstevel@tonic-gate 		break;
73777c478bd9Sstevel@tonic-gate 	case IPSEC_ALG_ENCR:
73787c478bd9Sstevel@tonic-gate 		if (entry->ipsa_encr_alg == update_state->alg_id)
73797c478bd9Sstevel@tonic-gate 			ctx_tmpl = &entry->ipsa_encrtmpl;
73807c478bd9Sstevel@tonic-gate 		break;
73817c478bd9Sstevel@tonic-gate 	default:
73827c478bd9Sstevel@tonic-gate 		ctx_tmpl = NULL;
73837c478bd9Sstevel@tonic-gate 	}
73847c478bd9Sstevel@tonic-gate 
73857c478bd9Sstevel@tonic-gate 	if (ctx_tmpl == NULL) {
73867c478bd9Sstevel@tonic-gate 		mutex_exit(&entry->ipsa_lock);
73877c478bd9Sstevel@tonic-gate 		return;
73887c478bd9Sstevel@tonic-gate 	}
73897c478bd9Sstevel@tonic-gate 
73907c478bd9Sstevel@tonic-gate 	/*
73917c478bd9Sstevel@tonic-gate 	 * The context template of the SA may be affected by the change
73927c478bd9Sstevel@tonic-gate 	 * of crypto provider.
73937c478bd9Sstevel@tonic-gate 	 */
73947c478bd9Sstevel@tonic-gate 	if (update_state->is_added) {
73957c478bd9Sstevel@tonic-gate 		/* create the context template if not already done */
73967c478bd9Sstevel@tonic-gate 		if (*ctx_tmpl == NULL) {
73977c478bd9Sstevel@tonic-gate 			(void) ipsec_create_ctx_tmpl(entry,
73987c478bd9Sstevel@tonic-gate 			    update_state->alg_type);
73997c478bd9Sstevel@tonic-gate 		}
74007c478bd9Sstevel@tonic-gate 	} else {
74017c478bd9Sstevel@tonic-gate 		/*
74027c478bd9Sstevel@tonic-gate 		 * The crypto provider was removed. If the context template
74037c478bd9Sstevel@tonic-gate 		 * exists but it is no longer valid, free it.
74047c478bd9Sstevel@tonic-gate 		 */
74057c478bd9Sstevel@tonic-gate 		if (*ctx_tmpl != NULL)
74067c478bd9Sstevel@tonic-gate 			ipsec_destroy_ctx_tmpl(entry, update_state->alg_type);
74077c478bd9Sstevel@tonic-gate 	}
74087c478bd9Sstevel@tonic-gate 
74097c478bd9Sstevel@tonic-gate 	mutex_exit(&entry->ipsa_lock);
74107c478bd9Sstevel@tonic-gate }
74117c478bd9Sstevel@tonic-gate 
74127c478bd9Sstevel@tonic-gate /*
7413bd670b35SErik Nordmark  * Invoked by IP when an software crypto provider has been updated, or if
7414bd670b35SErik Nordmark  * the crypto synchrony changes.  The type and id of the corresponding
7415bd670b35SErik Nordmark  * algorithm is passed as argument.  The type is set to ALL in the case of
7416bd670b35SErik Nordmark  * a synchrony change.
7417bd670b35SErik Nordmark  *
74187c478bd9Sstevel@tonic-gate  * is_added is B_TRUE if the provider was added, B_FALSE if it was
74197c478bd9Sstevel@tonic-gate  * removed. The function updates the SADB and free/creates the
74207c478bd9Sstevel@tonic-gate  * context templates associated with SAs if needed.
74217c478bd9Sstevel@tonic-gate  */
74227c478bd9Sstevel@tonic-gate 
7423fb87b5d2Ssommerfe #define	SADB_ALG_UPDATE_WALK(sadb, table) \
7424fb87b5d2Ssommerfe     sadb_walker((sadb).table, (sadb).sdb_hashsize, sadb_alg_update_cb, \
7425fb87b5d2Ssommerfe 	&update_state)
74267c478bd9Sstevel@tonic-gate 
74277c478bd9Sstevel@tonic-gate void
7428f4b3ec61Sdh sadb_alg_update(ipsec_algtype_t alg_type, uint8_t alg_id, boolean_t is_added,
7429f4b3ec61Sdh     netstack_t *ns)
74307c478bd9Sstevel@tonic-gate {
74317c478bd9Sstevel@tonic-gate 	struct sadb_update_alg_state update_state;
7432f4b3ec61Sdh 	ipsecah_stack_t	*ahstack = ns->netstack_ipsecah;
7433f4b3ec61Sdh 	ipsecesp_stack_t	*espstack = ns->netstack_ipsecesp;
7434bd670b35SErik Nordmark 	ipsec_stack_t *ipss = ns->netstack_ipsec;
74357c478bd9Sstevel@tonic-gate 
74367c478bd9Sstevel@tonic-gate 	update_state.alg_type = alg_type;
74377c478bd9Sstevel@tonic-gate 	update_state.alg_id = alg_id;
74387c478bd9Sstevel@tonic-gate 	update_state.is_added = is_added;
7439bd670b35SErik Nordmark 	update_state.async_auth = ipss->ipsec_algs_exec_mode[IPSEC_ALG_AUTH] ==
7440bd670b35SErik Nordmark 	    IPSEC_ALGS_EXEC_ASYNC;
7441bd670b35SErik Nordmark 	update_state.async_encr = ipss->ipsec_algs_exec_mode[IPSEC_ALG_ENCR] ==
7442bd670b35SErik Nordmark 	    IPSEC_ALGS_EXEC_ASYNC;
74437c478bd9Sstevel@tonic-gate 
7444bd670b35SErik Nordmark 	if (alg_type == IPSEC_ALG_AUTH || alg_type == IPSEC_ALG_ALL) {
74457c478bd9Sstevel@tonic-gate 		/* walk the AH tables only for auth. algorithm changes */
7446f4b3ec61Sdh 		SADB_ALG_UPDATE_WALK(ahstack->ah_sadb.s_v4, sdb_of);
7447f4b3ec61Sdh 		SADB_ALG_UPDATE_WALK(ahstack->ah_sadb.s_v4, sdb_if);
7448f4b3ec61Sdh 		SADB_ALG_UPDATE_WALK(ahstack->ah_sadb.s_v6, sdb_of);
7449f4b3ec61Sdh 		SADB_ALG_UPDATE_WALK(ahstack->ah_sadb.s_v6, sdb_if);
74507c478bd9Sstevel@tonic-gate 	}
74517c478bd9Sstevel@tonic-gate 
74527c478bd9Sstevel@tonic-gate 	/* walk the ESP tables */
7453f4b3ec61Sdh 	SADB_ALG_UPDATE_WALK(espstack->esp_sadb.s_v4, sdb_of);
7454f4b3ec61Sdh 	SADB_ALG_UPDATE_WALK(espstack->esp_sadb.s_v4, sdb_if);
7455f4b3ec61Sdh 	SADB_ALG_UPDATE_WALK(espstack->esp_sadb.s_v6, sdb_of);
7456f4b3ec61Sdh 	SADB_ALG_UPDATE_WALK(espstack->esp_sadb.s_v6, sdb_if);
74577c478bd9Sstevel@tonic-gate }
74587c478bd9Sstevel@tonic-gate 
74597c478bd9Sstevel@tonic-gate /*
74607c478bd9Sstevel@tonic-gate  * Creates a context template for the specified SA. This function
74617c478bd9Sstevel@tonic-gate  * is called when an SA is created and when a context template needs
74627c478bd9Sstevel@tonic-gate  * to be created due to a change of software provider.
74637c478bd9Sstevel@tonic-gate  */
74647c478bd9Sstevel@tonic-gate int
74657c478bd9Sstevel@tonic-gate ipsec_create_ctx_tmpl(ipsa_t *sa, ipsec_algtype_t alg_type)
74667c478bd9Sstevel@tonic-gate {
74677c478bd9Sstevel@tonic-gate 	ipsec_alginfo_t *alg;
74687c478bd9Sstevel@tonic-gate 	crypto_mechanism_t mech;
74697c478bd9Sstevel@tonic-gate 	crypto_key_t *key;
74707c478bd9Sstevel@tonic-gate 	crypto_ctx_template_t *sa_tmpl;
74717c478bd9Sstevel@tonic-gate 	int rv;
7472f4b3ec61Sdh 	ipsec_stack_t	*ipss = sa->ipsa_netstack->netstack_ipsec;
74737c478bd9Sstevel@tonic-gate 
747469e71331SBayard Bell 	ASSERT(RW_READ_HELD(&ipss->ipsec_alg_lock));
74757c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&sa->ipsa_lock));
74767c478bd9Sstevel@tonic-gate 
74777c478bd9Sstevel@tonic-gate 	/* get pointers to the algorithm info, context template, and key */
74787c478bd9Sstevel@tonic-gate 	switch (alg_type) {
74797c478bd9Sstevel@tonic-gate 	case IPSEC_ALG_AUTH:
74807c478bd9Sstevel@tonic-gate 		key = &sa->ipsa_kcfauthkey;
74817c478bd9Sstevel@tonic-gate 		sa_tmpl = &sa->ipsa_authtmpl;
7482f4b3ec61Sdh 		alg = ipss->ipsec_alglists[alg_type][sa->ipsa_auth_alg];
74837c478bd9Sstevel@tonic-gate 		break;
74847c478bd9Sstevel@tonic-gate 	case IPSEC_ALG_ENCR:
74857c478bd9Sstevel@tonic-gate 		key = &sa->ipsa_kcfencrkey;
74867c478bd9Sstevel@tonic-gate 		sa_tmpl = &sa->ipsa_encrtmpl;
7487f4b3ec61Sdh 		alg = ipss->ipsec_alglists[alg_type][sa->ipsa_encr_alg];
74887c478bd9Sstevel@tonic-gate 		break;
74897c478bd9Sstevel@tonic-gate 	default:
74907c478bd9Sstevel@tonic-gate 		alg = NULL;
74917c478bd9Sstevel@tonic-gate 	}
74927c478bd9Sstevel@tonic-gate 
74937c478bd9Sstevel@tonic-gate 	if (alg == NULL || !ALG_VALID(alg))
74947c478bd9Sstevel@tonic-gate 		return (EINVAL);
74957c478bd9Sstevel@tonic-gate 
74967c478bd9Sstevel@tonic-gate 	/* initialize the mech info structure for the framework */
74977c478bd9Sstevel@tonic-gate 	ASSERT(alg->alg_mech_type != CRYPTO_MECHANISM_INVALID);
74987c478bd9Sstevel@tonic-gate 	mech.cm_type = alg->alg_mech_type;
74997c478bd9Sstevel@tonic-gate 	mech.cm_param = NULL;
75007c478bd9Sstevel@tonic-gate 	mech.cm_param_len = 0;
75017c478bd9Sstevel@tonic-gate 
75027c478bd9Sstevel@tonic-gate 	/* create a new context template */
75037c478bd9Sstevel@tonic-gate 	rv = crypto_create_ctx_template(&mech, key, sa_tmpl, KM_NOSLEEP);
75047c478bd9Sstevel@tonic-gate 
75057c478bd9Sstevel@tonic-gate 	/*
75067c478bd9Sstevel@tonic-gate 	 * CRYPTO_MECH_NOT_SUPPORTED can be returned if only hardware
75077c478bd9Sstevel@tonic-gate 	 * providers are available for that mechanism. In that case
75087c478bd9Sstevel@tonic-gate 	 * we don't fail, and will generate the context template from
75097c478bd9Sstevel@tonic-gate 	 * the framework callback when a software provider for that
75107c478bd9Sstevel@tonic-gate 	 * mechanism registers.
75117c478bd9Sstevel@tonic-gate 	 *
75127c478bd9Sstevel@tonic-gate 	 * The context template is assigned the special value
75137c478bd9Sstevel@tonic-gate 	 * IPSEC_CTX_TMPL_ALLOC if the allocation failed due to a
75147c478bd9Sstevel@tonic-gate 	 * lack of memory. No attempt will be made to use
75157c478bd9Sstevel@tonic-gate 	 * the context template if it is set to this value.
75167c478bd9Sstevel@tonic-gate 	 */
75177c478bd9Sstevel@tonic-gate 	if (rv == CRYPTO_HOST_MEMORY) {
75187c478bd9Sstevel@tonic-gate 		*sa_tmpl = IPSEC_CTX_TMPL_ALLOC;
75197c478bd9Sstevel@tonic-gate 	} else if (rv != CRYPTO_SUCCESS) {
75207c478bd9Sstevel@tonic-gate 		*sa_tmpl = NULL;
75217c478bd9Sstevel@tonic-gate 		if (rv != CRYPTO_MECH_NOT_SUPPORTED)
75227c478bd9Sstevel@tonic-gate 			return (EINVAL);
75237c478bd9Sstevel@tonic-gate 	}
75247c478bd9Sstevel@tonic-gate 
75257c478bd9Sstevel@tonic-gate 	return (0);
75267c478bd9Sstevel@tonic-gate }
75277c478bd9Sstevel@tonic-gate 
75287c478bd9Sstevel@tonic-gate /*
75297c478bd9Sstevel@tonic-gate  * Destroy the context template of the specified algorithm type
75307c478bd9Sstevel@tonic-gate  * of the specified SA. Must be called while holding the SA lock.
75317c478bd9Sstevel@tonic-gate  */
75327c478bd9Sstevel@tonic-gate void
75337c478bd9Sstevel@tonic-gate ipsec_destroy_ctx_tmpl(ipsa_t *sa, ipsec_algtype_t alg_type)
75347c478bd9Sstevel@tonic-gate {
75357c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&sa->ipsa_lock));
75367c478bd9Sstevel@tonic-gate 
75377c478bd9Sstevel@tonic-gate 	if (alg_type == IPSEC_ALG_AUTH) {
75387c478bd9Sstevel@tonic-gate 		if (sa->ipsa_authtmpl == IPSEC_CTX_TMPL_ALLOC)
75397c478bd9Sstevel@tonic-gate 			sa->ipsa_authtmpl = NULL;
75407c478bd9Sstevel@tonic-gate 		else if (sa->ipsa_authtmpl != NULL) {
75417c478bd9Sstevel@tonic-gate 			crypto_destroy_ctx_template(sa->ipsa_authtmpl);
75427c478bd9Sstevel@tonic-gate 			sa->ipsa_authtmpl = NULL;
75437c478bd9Sstevel@tonic-gate 		}
75447c478bd9Sstevel@tonic-gate 	} else {
75457c478bd9Sstevel@tonic-gate 		ASSERT(alg_type == IPSEC_ALG_ENCR);
75467c478bd9Sstevel@tonic-gate 		if (sa->ipsa_encrtmpl == IPSEC_CTX_TMPL_ALLOC)
75477c478bd9Sstevel@tonic-gate 			sa->ipsa_encrtmpl = NULL;
75487c478bd9Sstevel@tonic-gate 		else if (sa->ipsa_encrtmpl != NULL) {
75497c478bd9Sstevel@tonic-gate 			crypto_destroy_ctx_template(sa->ipsa_encrtmpl);
75507c478bd9Sstevel@tonic-gate 			sa->ipsa_encrtmpl = NULL;
75517c478bd9Sstevel@tonic-gate 		}
75527c478bd9Sstevel@tonic-gate 	}
75537c478bd9Sstevel@tonic-gate }
75547c478bd9Sstevel@tonic-gate 
75557c478bd9Sstevel@tonic-gate /*
75567c478bd9Sstevel@tonic-gate  * Use the kernel crypto framework to check the validity of a key received
75577c478bd9Sstevel@tonic-gate  * via keysock. Returns 0 if the key is OK, -1 otherwise.
75587c478bd9Sstevel@tonic-gate  */
75597c478bd9Sstevel@tonic-gate int
75607c478bd9Sstevel@tonic-gate ipsec_check_key(crypto_mech_type_t mech_type, sadb_key_t *sadb_key,
75617c478bd9Sstevel@tonic-gate     boolean_t is_auth, int *diag)
75627c478bd9Sstevel@tonic-gate {
75637c478bd9Sstevel@tonic-gate 	crypto_mechanism_t mech;
75647c478bd9Sstevel@tonic-gate 	crypto_key_t crypto_key;
75657c478bd9Sstevel@tonic-gate 	int crypto_rc;
75667c478bd9Sstevel@tonic-gate 
75677c478bd9Sstevel@tonic-gate 	mech.cm_type = mech_type;
75687c478bd9Sstevel@tonic-gate 	mech.cm_param = NULL;
75697c478bd9Sstevel@tonic-gate 	mech.cm_param_len = 0;
75707c478bd9Sstevel@tonic-gate 
75717c478bd9Sstevel@tonic-gate 	crypto_key.ck_format = CRYPTO_KEY_RAW;
75727c478bd9Sstevel@tonic-gate 	crypto_key.ck_data = sadb_key + 1;
75737c478bd9Sstevel@tonic-gate 	crypto_key.ck_length = sadb_key->sadb_key_bits;
75747c478bd9Sstevel@tonic-gate 
75757c478bd9Sstevel@tonic-gate 	crypto_rc = crypto_key_check(&mech, &crypto_key);
75767c478bd9Sstevel@tonic-gate 
75777c478bd9Sstevel@tonic-gate 	switch (crypto_rc) {
75787c478bd9Sstevel@tonic-gate 	case CRYPTO_SUCCESS:
75797c478bd9Sstevel@tonic-gate 		return (0);
75807c478bd9Sstevel@tonic-gate 	case CRYPTO_MECHANISM_INVALID:
75817c478bd9Sstevel@tonic-gate 	case CRYPTO_MECH_NOT_SUPPORTED:
75827c478bd9Sstevel@tonic-gate 		*diag = is_auth ? SADB_X_DIAGNOSTIC_BAD_AALG :
75837c478bd9Sstevel@tonic-gate 		    SADB_X_DIAGNOSTIC_BAD_EALG;
75847c478bd9Sstevel@tonic-gate 		break;
75857c478bd9Sstevel@tonic-gate 	case CRYPTO_KEY_SIZE_RANGE:
75867c478bd9Sstevel@tonic-gate 		*diag = is_auth ? SADB_X_DIAGNOSTIC_BAD_AKEYBITS :
75877c478bd9Sstevel@tonic-gate 		    SADB_X_DIAGNOSTIC_BAD_EKEYBITS;
75887c478bd9Sstevel@tonic-gate 		break;
75897c478bd9Sstevel@tonic-gate 	case CRYPTO_WEAK_KEY:
75907c478bd9Sstevel@tonic-gate 		*diag = is_auth ? SADB_X_DIAGNOSTIC_WEAK_AKEY :
75917c478bd9Sstevel@tonic-gate 		    SADB_X_DIAGNOSTIC_WEAK_EKEY;
75927c478bd9Sstevel@tonic-gate 		break;
75937c478bd9Sstevel@tonic-gate 	}
75947c478bd9Sstevel@tonic-gate 
75957c478bd9Sstevel@tonic-gate 	return (-1);
75967c478bd9Sstevel@tonic-gate }
75975d3b8cb7SBill Sommerfeld 
75985d3b8cb7SBill Sommerfeld /*
75995d3b8cb7SBill Sommerfeld  * Whack options in the outer IP header when ipsec changes the outer label
76005d3b8cb7SBill Sommerfeld  *
76015d3b8cb7SBill Sommerfeld  * This is inelegant and really could use refactoring.
76025d3b8cb7SBill Sommerfeld  */
7603bd670b35SErik Nordmark mblk_t *
7604bd670b35SErik Nordmark sadb_whack_label_v4(mblk_t *mp, ipsa_t *assoc, kstat_named_t *counter,
7605bd670b35SErik Nordmark     ipdropper_t *dropper)
76065d3b8cb7SBill Sommerfeld {
76075d3b8cb7SBill Sommerfeld 	int delta;
76085d3b8cb7SBill Sommerfeld 	int plen;
76095d3b8cb7SBill Sommerfeld 	dblk_t *db;
76105d3b8cb7SBill Sommerfeld 	int hlen;
76115d3b8cb7SBill Sommerfeld 	uint8_t *opt_storage = assoc->ipsa_opt_storage;
76125d3b8cb7SBill Sommerfeld 	ipha_t *ipha = (ipha_t *)mp->b_rptr;
76135d3b8cb7SBill Sommerfeld 
76145d3b8cb7SBill Sommerfeld 	plen = ntohs(ipha->ipha_length);
76155d3b8cb7SBill Sommerfeld 
76165d3b8cb7SBill Sommerfeld 	delta = tsol_remove_secopt(ipha, MBLKL(mp));
76175d3b8cb7SBill Sommerfeld 	mp->b_wptr += delta;
76185d3b8cb7SBill Sommerfeld 	plen += delta;
76195d3b8cb7SBill Sommerfeld 
76205d3b8cb7SBill Sommerfeld 	/* XXX XXX code copied from tsol_check_label */
76215d3b8cb7SBill Sommerfeld 
76225d3b8cb7SBill Sommerfeld 	/* Make sure we have room for the worst-case addition */
76235d3b8cb7SBill Sommerfeld 	hlen = IPH_HDR_LENGTH(ipha) + opt_storage[IPOPT_OLEN];
76245d3b8cb7SBill Sommerfeld 	hlen = (hlen + 3) & ~3;
76255d3b8cb7SBill Sommerfeld 	if (hlen > IP_MAX_HDR_LENGTH)
76265d3b8cb7SBill Sommerfeld 		hlen = IP_MAX_HDR_LENGTH;
76275d3b8cb7SBill Sommerfeld 	hlen -= IPH_HDR_LENGTH(ipha);
76285d3b8cb7SBill Sommerfeld 
76295d3b8cb7SBill Sommerfeld 	db = mp->b_datap;
76305d3b8cb7SBill Sommerfeld 	if ((db->db_ref != 1) || (mp->b_wptr + hlen > db->db_lim)) {
76315d3b8cb7SBill Sommerfeld 		int copylen;
76325d3b8cb7SBill Sommerfeld 		mblk_t *new_mp;
76335d3b8cb7SBill Sommerfeld 
76345d3b8cb7SBill Sommerfeld 		/* allocate enough to be meaningful, but not *too* much */
76355d3b8cb7SBill Sommerfeld 		copylen = MBLKL(mp);
76365d3b8cb7SBill Sommerfeld 		if (copylen > 256)
76375d3b8cb7SBill Sommerfeld 			copylen = 256;
76385d3b8cb7SBill Sommerfeld 		new_mp = allocb_tmpl(hlen + copylen +
76395d3b8cb7SBill Sommerfeld 		    (mp->b_rptr - mp->b_datap->db_base), mp);
76405d3b8cb7SBill Sommerfeld 
7641bd670b35SErik Nordmark 		if (new_mp == NULL) {
7642bd670b35SErik Nordmark 			ip_drop_packet(mp, B_FALSE, NULL, counter,  dropper);
7643bd670b35SErik Nordmark 			return (NULL);
7644bd670b35SErik Nordmark 		}
76455d3b8cb7SBill Sommerfeld 
76465d3b8cb7SBill Sommerfeld 		/* keep the bias */
76475d3b8cb7SBill Sommerfeld 		new_mp->b_rptr += mp->b_rptr - mp->b_datap->db_base;
76485d3b8cb7SBill Sommerfeld 		new_mp->b_wptr = new_mp->b_rptr + copylen;
76495d3b8cb7SBill Sommerfeld 		bcopy(mp->b_rptr, new_mp->b_rptr, copylen);
76505d3b8cb7SBill Sommerfeld 		new_mp->b_cont = mp;
76515d3b8cb7SBill Sommerfeld 		if ((mp->b_rptr += copylen) >= mp->b_wptr) {
76525d3b8cb7SBill Sommerfeld 			new_mp->b_cont = mp->b_cont;
76535d3b8cb7SBill Sommerfeld 			freeb(mp);
76545d3b8cb7SBill Sommerfeld 		}
7655bd670b35SErik Nordmark 		mp = new_mp;
76565d3b8cb7SBill Sommerfeld 		ipha = (ipha_t *)mp->b_rptr;
76575d3b8cb7SBill Sommerfeld 	}
76585d3b8cb7SBill Sommerfeld 
76595d3b8cb7SBill Sommerfeld 	delta = tsol_prepend_option(assoc->ipsa_opt_storage, ipha, MBLKL(mp));
76605d3b8cb7SBill Sommerfeld 
76615d3b8cb7SBill Sommerfeld 	ASSERT(delta != -1);
76625d3b8cb7SBill Sommerfeld 
76635d3b8cb7SBill Sommerfeld 	plen += delta;
76645d3b8cb7SBill Sommerfeld 	mp->b_wptr += delta;
76655d3b8cb7SBill Sommerfeld 
76665d3b8cb7SBill Sommerfeld 	/*
76675d3b8cb7SBill Sommerfeld 	 * Paranoia
76685d3b8cb7SBill Sommerfeld 	 */
76695d3b8cb7SBill Sommerfeld 	db = mp->b_datap;
76705d3b8cb7SBill Sommerfeld 
76715d3b8cb7SBill Sommerfeld 	ASSERT3P(mp->b_wptr, <=, db->db_lim);
76725d3b8cb7SBill Sommerfeld 	ASSERT3P(mp->b_rptr, <=, db->db_lim);
76735d3b8cb7SBill Sommerfeld 
76745d3b8cb7SBill Sommerfeld 	ASSERT3P(mp->b_wptr, >=, db->db_base);
76755d3b8cb7SBill Sommerfeld 	ASSERT3P(mp->b_rptr, >=, db->db_base);
76765d3b8cb7SBill Sommerfeld 	/* End paranoia */
76775d3b8cb7SBill Sommerfeld 
76785d3b8cb7SBill Sommerfeld 	ipha->ipha_length = htons(plen);
76795d3b8cb7SBill Sommerfeld 
7680bd670b35SErik Nordmark 	return (mp);
76815d3b8cb7SBill Sommerfeld }
76825d3b8cb7SBill Sommerfeld 
7683bd670b35SErik Nordmark mblk_t *
7684bd670b35SErik Nordmark sadb_whack_label_v6(mblk_t *mp, ipsa_t *assoc, kstat_named_t *counter,
7685bd670b35SErik Nordmark     ipdropper_t *dropper)
76865d3b8cb7SBill Sommerfeld {
76875d3b8cb7SBill Sommerfeld 	int delta;
76885d3b8cb7SBill Sommerfeld 	int plen;
76895d3b8cb7SBill Sommerfeld 	dblk_t *db;
76905d3b8cb7SBill Sommerfeld 	int hlen;
76915d3b8cb7SBill Sommerfeld 	uint8_t *opt_storage = assoc->ipsa_opt_storage;
76925d3b8cb7SBill Sommerfeld 	uint_t sec_opt_len; /* label option length not including type, len */
76935d3b8cb7SBill Sommerfeld 	ip6_t *ip6h = (ip6_t *)mp->b_rptr;
76945d3b8cb7SBill Sommerfeld 
76955d3b8cb7SBill Sommerfeld 	plen = ntohs(ip6h->ip6_plen);
76965d3b8cb7SBill Sommerfeld 
76975d3b8cb7SBill Sommerfeld 	delta = tsol_remove_secopt_v6(ip6h, MBLKL(mp));
76985d3b8cb7SBill Sommerfeld 	mp->b_wptr += delta;
76995d3b8cb7SBill Sommerfeld 	plen += delta;
77005d3b8cb7SBill Sommerfeld 
77015d3b8cb7SBill Sommerfeld 	/* XXX XXX code copied from tsol_check_label_v6 */
77025d3b8cb7SBill Sommerfeld 	/*
77035d3b8cb7SBill Sommerfeld 	 * Make sure we have room for the worst-case addition. Add 2 bytes for
77045d3b8cb7SBill Sommerfeld 	 * the hop-by-hop ext header's next header and length fields. Add
77055d3b8cb7SBill Sommerfeld 	 * another 2 bytes for the label option type, len and then round
77065d3b8cb7SBill Sommerfeld 	 * up to the next 8-byte multiple.
77075d3b8cb7SBill Sommerfeld 	 */
77085d3b8cb7SBill Sommerfeld 	sec_opt_len = opt_storage[1];
77095d3b8cb7SBill Sommerfeld 
77105d3b8cb7SBill Sommerfeld 	db = mp->b_datap;
77115d3b8cb7SBill Sommerfeld 	hlen = (4 + sec_opt_len + 7) & ~7;
77125d3b8cb7SBill Sommerfeld 
77135d3b8cb7SBill Sommerfeld 	if ((db->db_ref != 1) || (mp->b_wptr + hlen > db->db_lim)) {
77145d3b8cb7SBill Sommerfeld 		int copylen;
77155d3b8cb7SBill Sommerfeld 		mblk_t *new_mp;
77165d3b8cb7SBill Sommerfeld 		uint16_t hdr_len;
77175d3b8cb7SBill Sommerfeld 
77185d3b8cb7SBill Sommerfeld 		hdr_len = ip_hdr_length_v6(mp, ip6h);
77195d3b8cb7SBill Sommerfeld 		/*
77205d3b8cb7SBill Sommerfeld 		 * Allocate enough to be meaningful, but not *too* much.
77215d3b8cb7SBill Sommerfeld 		 * Also all the IPv6 extension headers must be in the same mblk
77225d3b8cb7SBill Sommerfeld 		 */
77235d3b8cb7SBill Sommerfeld 		copylen = MBLKL(mp);
77245d3b8cb7SBill Sommerfeld 		if (copylen > 256)
77255d3b8cb7SBill Sommerfeld 			copylen = 256;
77265d3b8cb7SBill Sommerfeld 		if (copylen < hdr_len)
77275d3b8cb7SBill Sommerfeld 			copylen = hdr_len;
77285d3b8cb7SBill Sommerfeld 		new_mp = allocb_tmpl(hlen + copylen +
77295d3b8cb7SBill Sommerfeld 		    (mp->b_rptr - mp->b_datap->db_base), mp);
7730bd670b35SErik Nordmark 		if (new_mp == NULL) {
7731bd670b35SErik Nordmark 			ip_drop_packet(mp, B_FALSE, NULL, counter,  dropper);
7732bd670b35SErik Nordmark 			return (NULL);
7733bd670b35SErik Nordmark 		}
77345d3b8cb7SBill Sommerfeld 
77355d3b8cb7SBill Sommerfeld 		/* keep the bias */
77365d3b8cb7SBill Sommerfeld 		new_mp->b_rptr += mp->b_rptr - mp->b_datap->db_base;
77375d3b8cb7SBill Sommerfeld 		new_mp->b_wptr = new_mp->b_rptr + copylen;
77385d3b8cb7SBill Sommerfeld 		bcopy(mp->b_rptr, new_mp->b_rptr, copylen);
77395d3b8cb7SBill Sommerfeld 		new_mp->b_cont = mp;
77405d3b8cb7SBill Sommerfeld 		if ((mp->b_rptr += copylen) >= mp->b_wptr) {
77415d3b8cb7SBill Sommerfeld 			new_mp->b_cont = mp->b_cont;
77425d3b8cb7SBill Sommerfeld 			freeb(mp);
77435d3b8cb7SBill Sommerfeld 		}
7744bd670b35SErik Nordmark 		mp = new_mp;
77455d3b8cb7SBill Sommerfeld 		ip6h = (ip6_t *)mp->b_rptr;
77465d3b8cb7SBill Sommerfeld 	}
77475d3b8cb7SBill Sommerfeld 
77485d3b8cb7SBill Sommerfeld 	delta = tsol_prepend_option_v6(assoc->ipsa_opt_storage,
77495d3b8cb7SBill Sommerfeld 	    ip6h, MBLKL(mp));
77505d3b8cb7SBill Sommerfeld 
77515d3b8cb7SBill Sommerfeld 	ASSERT(delta != -1);
77525d3b8cb7SBill Sommerfeld 
77535d3b8cb7SBill Sommerfeld 	plen += delta;
77545d3b8cb7SBill Sommerfeld 	mp->b_wptr += delta;
77555d3b8cb7SBill Sommerfeld 
77565d3b8cb7SBill Sommerfeld 	/*
77575d3b8cb7SBill Sommerfeld 	 * Paranoia
77585d3b8cb7SBill Sommerfeld 	 */
77595d3b8cb7SBill Sommerfeld 	db = mp->b_datap;
77605d3b8cb7SBill Sommerfeld 
77615d3b8cb7SBill Sommerfeld 	ASSERT3P(mp->b_wptr, <=, db->db_lim);
77625d3b8cb7SBill Sommerfeld 	ASSERT3P(mp->b_rptr, <=, db->db_lim);
77635d3b8cb7SBill Sommerfeld 
77645d3b8cb7SBill Sommerfeld 	ASSERT3P(mp->b_wptr, >=, db->db_base);
77655d3b8cb7SBill Sommerfeld 	ASSERT3P(mp->b_rptr, >=, db->db_base);
77665d3b8cb7SBill Sommerfeld 	/* End paranoia */
77675d3b8cb7SBill Sommerfeld 
77685d3b8cb7SBill Sommerfeld 	ip6h->ip6_plen = htons(plen);
77695d3b8cb7SBill Sommerfeld 
7770bd670b35SErik Nordmark 	return (mp);
77715d3b8cb7SBill Sommerfeld }
77725d3b8cb7SBill Sommerfeld 
7773bd670b35SErik Nordmark /* Whack the labels and update ip_xmit_attr_t as needed */
7774bd670b35SErik Nordmark mblk_t *
7775bd670b35SErik Nordmark sadb_whack_label(mblk_t *mp, ipsa_t *assoc, ip_xmit_attr_t *ixa,
7776bd670b35SErik Nordmark     kstat_named_t *counter, ipdropper_t *dropper)
7777bd670b35SErik Nordmark {
7778bd670b35SErik Nordmark 	int adjust;
7779bd670b35SErik Nordmark 	int iplen;
77805d3b8cb7SBill Sommerfeld 
7781bd670b35SErik Nordmark 	if (ixa->ixa_flags & IXAF_IS_IPV4) {
7782bd670b35SErik Nordmark 		ipha_t		*ipha = (ipha_t *)mp->b_rptr;
7783bd670b35SErik Nordmark 
7784bd670b35SErik Nordmark 		ASSERT(IPH_HDR_VERSION(ipha) == IPV4_VERSION);
7785bd670b35SErik Nordmark 		iplen = ntohs(ipha->ipha_length);
7786bd670b35SErik Nordmark 		mp = sadb_whack_label_v4(mp, assoc, counter, dropper);
7787bd670b35SErik Nordmark 		if (mp == NULL)
7788bd670b35SErik Nordmark 			return (NULL);
7789bd670b35SErik Nordmark 
7790bd670b35SErik Nordmark 		ipha = (ipha_t *)mp->b_rptr;
7791bd670b35SErik Nordmark 		ASSERT(IPH_HDR_VERSION(ipha) == IPV4_VERSION);
7792bd670b35SErik Nordmark 		adjust = (int)ntohs(ipha->ipha_length) - iplen;
7793bd670b35SErik Nordmark 	} else {
7794bd670b35SErik Nordmark 		ip6_t		*ip6h = (ip6_t *)mp->b_rptr;
7795bd670b35SErik Nordmark 
7796bd670b35SErik Nordmark 		ASSERT(IPH_HDR_VERSION(ip6h) == IPV6_VERSION);
7797bd670b35SErik Nordmark 		iplen = ntohs(ip6h->ip6_plen);
7798bd670b35SErik Nordmark 		mp = sadb_whack_label_v6(mp, assoc, counter, dropper);
7799bd670b35SErik Nordmark 		if (mp == NULL)
7800bd670b35SErik Nordmark 			return (NULL);
7801bd670b35SErik Nordmark 
7802bd670b35SErik Nordmark 		ip6h = (ip6_t *)mp->b_rptr;
7803bd670b35SErik Nordmark 		ASSERT(IPH_HDR_VERSION(ip6h) == IPV6_VERSION);
7804bd670b35SErik Nordmark 		adjust = (int)ntohs(ip6h->ip6_plen) - iplen;
7805bd670b35SErik Nordmark 	}
7806bd670b35SErik Nordmark 	ixa->ixa_pktlen += adjust;
7807bd670b35SErik Nordmark 	ixa->ixa_ip_hdr_length += adjust;
7808bd670b35SErik Nordmark 	return (mp);
7809bd670b35SErik Nordmark }
78105d3b8cb7SBill Sommerfeld 
781138d95a78Smarkfen /*
781238d95a78Smarkfen  * If this is an outgoing SA then add some fuzz to the
781338d95a78Smarkfen  * SOFT EXPIRE time. The reason for this is to stop
781438d95a78Smarkfen  * peers trying to renegotiate SOFT expiring SA's at
781538d95a78Smarkfen  * the same time. The amount of fuzz needs to be at
78160e9b5742SDan McDonald  * least 8 seconds which is the typical interval
781738d95a78Smarkfen  * sadb_ager(), although this is only a guide as it
781838d95a78Smarkfen  * selftunes.
781938d95a78Smarkfen  */
78205d3b8cb7SBill Sommerfeld static void
782138d95a78Smarkfen lifetime_fuzz(ipsa_t *assoc)
782238d95a78Smarkfen {
782338d95a78Smarkfen 	uint8_t rnd;
782438d95a78Smarkfen 
782538d95a78Smarkfen 	if (assoc->ipsa_softaddlt == 0)
782638d95a78Smarkfen 		return;
782738d95a78Smarkfen 
782838d95a78Smarkfen 	(void) random_get_pseudo_bytes(&rnd, sizeof (rnd));
78290e9b5742SDan McDonald 	rnd = (rnd & 0xF) + 8;
783038d95a78Smarkfen 	assoc->ipsa_softexpiretime -= rnd;
783138d95a78Smarkfen 	assoc->ipsa_softaddlt -= rnd;
783238d95a78Smarkfen }
78335d3b8cb7SBill Sommerfeld 
78345d3b8cb7SBill Sommerfeld static void
783538d95a78Smarkfen destroy_ipsa_pair(ipsap_t *ipsapp)
783638d95a78Smarkfen {
783738d95a78Smarkfen 	/*
783838d95a78Smarkfen 	 * Because of the multi-line macro nature of IPSA_REFRELE, keep
783938d95a78Smarkfen 	 * them in { }.
784038d95a78Smarkfen 	 */
784138d95a78Smarkfen 	if (ipsapp->ipsap_sa_ptr != NULL) {
784238d95a78Smarkfen 		IPSA_REFRELE(ipsapp->ipsap_sa_ptr);
784338d95a78Smarkfen 	}
784438d95a78Smarkfen 	if (ipsapp->ipsap_psa_ptr != NULL) {
784538d95a78Smarkfen 		IPSA_REFRELE(ipsapp->ipsap_psa_ptr);
784638d95a78Smarkfen 	}
78475d3b8cb7SBill Sommerfeld 	init_ipsa_pair(ipsapp);
78485d3b8cb7SBill Sommerfeld }
784938d95a78Smarkfen 
78505d3b8cb7SBill Sommerfeld static void
78515d3b8cb7SBill Sommerfeld init_ipsa_pair(ipsap_t *ipsapp)
78525d3b8cb7SBill Sommerfeld {
78535d3b8cb7SBill Sommerfeld 	ipsapp->ipsap_bucket = NULL;
78545d3b8cb7SBill Sommerfeld 	ipsapp->ipsap_sa_ptr = NULL;
78555d3b8cb7SBill Sommerfeld 	ipsapp->ipsap_pbucket = NULL;
78565d3b8cb7SBill Sommerfeld 	ipsapp->ipsap_psa_ptr = NULL;
785738d95a78Smarkfen }
785838d95a78Smarkfen 
785938d95a78Smarkfen /*
786038d95a78Smarkfen  * The sadb_ager() function walks through the hash tables of SA's and ages
786138d95a78Smarkfen  * them, if the SA expires as a result, its marked as DEAD and will be reaped
786238d95a78Smarkfen  * the next time sadb_ager() runs. SA's which are paired or have a peer (same
786338d95a78Smarkfen  * SA appears in both the inbound and outbound tables because its not possible
786438d95a78Smarkfen  * to determine its direction) are placed on a list when they expire. This is
786538d95a78Smarkfen  * to ensure that pair/peer SA's are reaped at the same time, even if they
786638d95a78Smarkfen  * expire at different times.
786738d95a78Smarkfen  *
786838d95a78Smarkfen  * This function is called twice by sadb_ager(), one after processing the
786938d95a78Smarkfen  * inbound table, then again after processing the outbound table.
787038d95a78Smarkfen  */
787138d95a78Smarkfen void
787238d95a78Smarkfen age_pair_peer_list(templist_t *haspeerlist, sadb_t *sp, boolean_t outbound)
787338d95a78Smarkfen {
787438d95a78Smarkfen 	templist_t *listptr;
787538d95a78Smarkfen 	int outhash;
787638d95a78Smarkfen 	isaf_t *bucket;
787738d95a78Smarkfen 	boolean_t haspeer;
787838d95a78Smarkfen 	ipsa_t *peer_assoc, *dying;
787938d95a78Smarkfen 	/*
788038d95a78Smarkfen 	 * Haspeer cases will contain both IPv4 and IPv6.  This code
788138d95a78Smarkfen 	 * is address independent.
788238d95a78Smarkfen 	 */
788338d95a78Smarkfen 	while (haspeerlist != NULL) {
788438d95a78Smarkfen 		/* "dying" contains the SA that has a peer. */
788538d95a78Smarkfen 		dying = haspeerlist->ipsa;
788638d95a78Smarkfen 		haspeer = (dying->ipsa_haspeer);
788738d95a78Smarkfen 		listptr = haspeerlist;
788838d95a78Smarkfen 		haspeerlist = listptr->next;
788938d95a78Smarkfen 		kmem_free(listptr, sizeof (*listptr));
789038d95a78Smarkfen 		/*
789138d95a78Smarkfen 		 * Pick peer bucket based on addrfam.
789238d95a78Smarkfen 		 */
789338d95a78Smarkfen 		if (outbound) {
789438d95a78Smarkfen 			if (haspeer)
789538d95a78Smarkfen 				bucket = INBOUND_BUCKET(sp, dying->ipsa_spi);
789638d95a78Smarkfen 			else
789738d95a78Smarkfen 				bucket = INBOUND_BUCKET(sp,
789838d95a78Smarkfen 				    dying->ipsa_otherspi);
789938d95a78Smarkfen 		} else { /* inbound */
790038d95a78Smarkfen 			if (haspeer) {
790138d95a78Smarkfen 				if (dying->ipsa_addrfam == AF_INET6) {
790238d95a78Smarkfen 					outhash = OUTBOUND_HASH_V6(sp,
790338d95a78Smarkfen 					    *((in6_addr_t *)&dying->
790438d95a78Smarkfen 					    ipsa_dstaddr));
790538d95a78Smarkfen 				} else {
790638d95a78Smarkfen 					outhash = OUTBOUND_HASH_V4(sp,
790738d95a78Smarkfen 					    *((ipaddr_t *)&dying->
790838d95a78Smarkfen 					    ipsa_dstaddr));
790938d95a78Smarkfen 				}
791038d95a78Smarkfen 			} else if (dying->ipsa_addrfam == AF_INET6) {
791138d95a78Smarkfen 				outhash = OUTBOUND_HASH_V6(sp,
791238d95a78Smarkfen 				    *((in6_addr_t *)&dying->
791338d95a78Smarkfen 				    ipsa_srcaddr));
791438d95a78Smarkfen 			} else {
791538d95a78Smarkfen 				outhash = OUTBOUND_HASH_V4(sp,
791638d95a78Smarkfen 				    *((ipaddr_t *)&dying->
791738d95a78Smarkfen 				    ipsa_srcaddr));
791838d95a78Smarkfen 			}
7919bd670b35SErik Nordmark 			bucket = &(sp->sdb_of[outhash]);
792038d95a78Smarkfen 		}
792138d95a78Smarkfen 
792238d95a78Smarkfen 		mutex_enter(&bucket->isaf_lock);
792338d95a78Smarkfen 		/*
792438d95a78Smarkfen 		 * "haspeer" SA's have the same src/dst address ordering,
792538d95a78Smarkfen 		 * "paired" SA's have the src/dst addresses reversed.
792638d95a78Smarkfen 		 */
792738d95a78Smarkfen 		if (haspeer) {
792838d95a78Smarkfen 			peer_assoc = ipsec_getassocbyspi(bucket,
792938d95a78Smarkfen 			    dying->ipsa_spi, dying->ipsa_srcaddr,
793038d95a78Smarkfen 			    dying->ipsa_dstaddr, dying->ipsa_addrfam);
793138d95a78Smarkfen 		} else {
793238d95a78Smarkfen 			peer_assoc = ipsec_getassocbyspi(bucket,
793338d95a78Smarkfen 			    dying->ipsa_otherspi, dying->ipsa_dstaddr,
793438d95a78Smarkfen 			    dying->ipsa_srcaddr, dying->ipsa_addrfam);
793538d95a78Smarkfen 		}
793638d95a78Smarkfen 
793738d95a78Smarkfen 		mutex_exit(&bucket->isaf_lock);
793838d95a78Smarkfen 		if (peer_assoc != NULL) {
793938d95a78Smarkfen 			mutex_enter(&peer_assoc->ipsa_lock);
794038d95a78Smarkfen 			mutex_enter(&dying->ipsa_lock);
794138d95a78Smarkfen 			if (!haspeer) {
794238d95a78Smarkfen 				/*
794338d95a78Smarkfen 				 * Only SA's which have a "peer" or are
794438d95a78Smarkfen 				 * "paired" end up on this list, so this
794538d95a78Smarkfen 				 * must be a "paired" SA, update the flags
794638d95a78Smarkfen 				 * to break the pair.
794738d95a78Smarkfen 				 */
794838d95a78Smarkfen 				peer_assoc->ipsa_otherspi = 0;
794938d95a78Smarkfen 				peer_assoc->ipsa_flags &= ~IPSA_F_PAIRED;
795038d95a78Smarkfen 				dying->ipsa_otherspi = 0;
795138d95a78Smarkfen 				dying->ipsa_flags &= ~IPSA_F_PAIRED;
795238d95a78Smarkfen 			}
795338d95a78Smarkfen 			if (haspeer || outbound) {
795438d95a78Smarkfen 				/*
795538d95a78Smarkfen 				 * Update the state of the "inbound" SA when
795638d95a78Smarkfen 				 * the "outbound" SA has expired. Don't update
795738d95a78Smarkfen 				 * the "outbound" SA when the "inbound" SA
795838d95a78Smarkfen 				 * SA expires because setting the hard_addtime
795938d95a78Smarkfen 				 * below will cause this to happen.
796038d95a78Smarkfen 				 */
796138d95a78Smarkfen 				peer_assoc->ipsa_state = dying->ipsa_state;
796238d95a78Smarkfen 			}
796338d95a78Smarkfen 			if (dying->ipsa_state == IPSA_STATE_DEAD)
796438d95a78Smarkfen 				peer_assoc->ipsa_hardexpiretime = 1;
796538d95a78Smarkfen 
796638d95a78Smarkfen 			mutex_exit(&dying->ipsa_lock);
796738d95a78Smarkfen 			mutex_exit(&peer_assoc->ipsa_lock);
796838d95a78Smarkfen 			IPSA_REFRELE(peer_assoc);
796938d95a78Smarkfen 		}
797038d95a78Smarkfen 		IPSA_REFRELE(dying);
797138d95a78Smarkfen 	}
797238d95a78Smarkfen }
7973628b0c67SMark Fenwick 
7974628b0c67SMark Fenwick /*
7975628b0c67SMark Fenwick  * Ensure that the IV used for CCM mode never repeats. The IV should
7976628b0c67SMark Fenwick  * only be updated by this function. Also check to see if the IV
7977628b0c67SMark Fenwick  * is about to wrap and generate a SOFT Expire. This function is only
7978628b0c67SMark Fenwick  * called for outgoing packets, the IV for incomming packets is taken
7979628b0c67SMark Fenwick  * from the wire. If the outgoing SA needs to be expired, update
7980628b0c67SMark Fenwick  * the matching incomming SA.
7981628b0c67SMark Fenwick  */
7982628b0c67SMark Fenwick boolean_t
7983628b0c67SMark Fenwick update_iv(uint8_t *iv_ptr, queue_t *pfkey_q, ipsa_t *assoc,
7984628b0c67SMark Fenwick     ipsecesp_stack_t *espstack)
7985628b0c67SMark Fenwick {
7986628b0c67SMark Fenwick 	boolean_t rc = B_TRUE;
7987628b0c67SMark Fenwick 	isaf_t *inbound_bucket;
7988628b0c67SMark Fenwick 	sadb_t *sp;
7989628b0c67SMark Fenwick 	ipsa_t *pair_sa = NULL;
7990628b0c67SMark Fenwick 	int sa_new_state = 0;
7991628b0c67SMark Fenwick 
7992628b0c67SMark Fenwick 	/* For non counter modes, the IV is random data. */
7993628b0c67SMark Fenwick 	if (!(assoc->ipsa_flags & IPSA_F_COUNTERMODE)) {
7994628b0c67SMark Fenwick 		(void) random_get_pseudo_bytes(iv_ptr, assoc->ipsa_iv_len);
7995628b0c67SMark Fenwick 		return (rc);
7996628b0c67SMark Fenwick 	}
7997628b0c67SMark Fenwick 
7998628b0c67SMark Fenwick 	mutex_enter(&assoc->ipsa_lock);
7999628b0c67SMark Fenwick 
8000628b0c67SMark Fenwick 	(*assoc->ipsa_iv)++;
8001628b0c67SMark Fenwick 
8002628b0c67SMark Fenwick 	if (*assoc->ipsa_iv == assoc->ipsa_iv_hardexpire) {
8003628b0c67SMark Fenwick 		sa_new_state = IPSA_STATE_DEAD;
8004628b0c67SMark Fenwick 		rc = B_FALSE;
8005628b0c67SMark Fenwick 	} else if (*assoc->ipsa_iv == assoc->ipsa_iv_softexpire) {
8006628b0c67SMark Fenwick 		if (assoc->ipsa_state != IPSA_STATE_DYING) {
8007628b0c67SMark Fenwick 			/*
8008628b0c67SMark Fenwick 			 * This SA may have already been expired when its
8009628b0c67SMark Fenwick 			 * PAIR_SA expired.
8010628b0c67SMark Fenwick 			 */
8011628b0c67SMark Fenwick 			sa_new_state = IPSA_STATE_DYING;
8012628b0c67SMark Fenwick 		}
8013628b0c67SMark Fenwick 	}
8014628b0c67SMark Fenwick 	if (sa_new_state) {
8015628b0c67SMark Fenwick 		/*
8016628b0c67SMark Fenwick 		 * If there is a state change, we need to update this SA
8017628b0c67SMark Fenwick 		 * and its "pair", we can find the bucket for the "pair" SA
8018628b0c67SMark Fenwick 		 * while holding the ipsa_t mutex, but we won't actually
8019628b0c67SMark Fenwick 		 * update anything untill the ipsa_t mutex has been released
8020628b0c67SMark Fenwick 		 * for _this_ SA.
8021628b0c67SMark Fenwick 		 */
8022628b0c67SMark Fenwick 		assoc->ipsa_state = sa_new_state;
8023628b0c67SMark Fenwick 		if (assoc->ipsa_addrfam == AF_INET6) {
8024628b0c67SMark Fenwick 			sp = &espstack->esp_sadb.s_v6;
8025628b0c67SMark Fenwick 		} else {
8026628b0c67SMark Fenwick 			sp = &espstack->esp_sadb.s_v4;
8027628b0c67SMark Fenwick 		}
8028628b0c67SMark Fenwick 		inbound_bucket = INBOUND_BUCKET(sp, assoc->ipsa_otherspi);
8029628b0c67SMark Fenwick 		sadb_expire_assoc(pfkey_q, assoc);
8030628b0c67SMark Fenwick 	}
8031628b0c67SMark Fenwick 	if (rc == B_TRUE)
8032628b0c67SMark Fenwick 		bcopy(assoc->ipsa_iv, iv_ptr, assoc->ipsa_iv_len);
8033628b0c67SMark Fenwick 
8034628b0c67SMark Fenwick 	mutex_exit(&assoc->ipsa_lock);
8035628b0c67SMark Fenwick 
8036628b0c67SMark Fenwick 	if (sa_new_state) {
8037628b0c67SMark Fenwick 		/* Find the inbound SA, need to lock hash bucket. */
8038628b0c67SMark Fenwick 		mutex_enter(&inbound_bucket->isaf_lock);
8039628b0c67SMark Fenwick 		pair_sa = ipsec_getassocbyspi(inbound_bucket,
8040628b0c67SMark Fenwick 		    assoc->ipsa_otherspi, assoc->ipsa_dstaddr,
8041628b0c67SMark Fenwick 		    assoc->ipsa_srcaddr, assoc->ipsa_addrfam);
8042628b0c67SMark Fenwick 		mutex_exit(&inbound_bucket->isaf_lock);
8043628b0c67SMark Fenwick 		if (pair_sa != NULL) {
8044628b0c67SMark Fenwick 			mutex_enter(&pair_sa->ipsa_lock);
8045628b0c67SMark Fenwick 			pair_sa->ipsa_state = sa_new_state;
8046628b0c67SMark Fenwick 			mutex_exit(&pair_sa->ipsa_lock);
8047628b0c67SMark Fenwick 			IPSA_REFRELE(pair_sa);
8048628b0c67SMark Fenwick 		}
8049628b0c67SMark Fenwick 	}
8050628b0c67SMark Fenwick 
8051628b0c67SMark Fenwick 	return (rc);
8052628b0c67SMark Fenwick }
8053628b0c67SMark Fenwick 
8054628b0c67SMark Fenwick void
8055628b0c67SMark Fenwick ccm_params_init(ipsa_t *assoc, uchar_t *esph, uint_t data_len, uchar_t *iv_ptr,
8056628b0c67SMark Fenwick     ipsa_cm_mech_t *cm_mech, crypto_data_t *crypto_data)
8057628b0c67SMark Fenwick {
8058628b0c67SMark Fenwick 	uchar_t *nonce;
8059628b0c67SMark Fenwick 	crypto_mechanism_t *combined_mech;
8060628b0c67SMark Fenwick 	CK_AES_CCM_PARAMS *params;
8061628b0c67SMark Fenwick 
8062628b0c67SMark Fenwick 	combined_mech = (crypto_mechanism_t *)cm_mech;
8063628b0c67SMark Fenwick 	params = (CK_AES_CCM_PARAMS *)(combined_mech + 1);
8064628b0c67SMark Fenwick 	nonce = (uchar_t *)(params + 1);
8065628b0c67SMark Fenwick 	params->ulMACSize = assoc->ipsa_mac_len;
8066628b0c67SMark Fenwick 	params->ulNonceSize = assoc->ipsa_nonce_len;
8067628b0c67SMark Fenwick 	params->ulAuthDataSize = sizeof (esph_t);
8068628b0c67SMark Fenwick 	params->ulDataSize = data_len;
8069628b0c67SMark Fenwick 	params->nonce = nonce;
8070628b0c67SMark Fenwick 	params->authData = esph;
8071628b0c67SMark Fenwick 
8072628b0c67SMark Fenwick 	cm_mech->combined_mech.cm_type = assoc->ipsa_emech.cm_type;
8073628b0c67SMark Fenwick 	cm_mech->combined_mech.cm_param_len = sizeof (CK_AES_CCM_PARAMS);
8074628b0c67SMark Fenwick 	cm_mech->combined_mech.cm_param = (caddr_t)params;
8075628b0c67SMark Fenwick 	/* See gcm_params_init() for comments. */
8076628b0c67SMark Fenwick 	bcopy(assoc->ipsa_nonce, nonce, assoc->ipsa_saltlen);
8077628b0c67SMark Fenwick 	nonce += assoc->ipsa_saltlen;
8078628b0c67SMark Fenwick 	bcopy(iv_ptr, nonce, assoc->ipsa_iv_len);
8079628b0c67SMark Fenwick 	crypto_data->cd_miscdata = NULL;
8080628b0c67SMark Fenwick }
8081628b0c67SMark Fenwick 
8082628b0c67SMark Fenwick /* ARGSUSED */
8083628b0c67SMark Fenwick void
8084628b0c67SMark Fenwick cbc_params_init(ipsa_t *assoc, uchar_t *esph, uint_t data_len, uchar_t *iv_ptr,
8085628b0c67SMark Fenwick     ipsa_cm_mech_t *cm_mech, crypto_data_t *crypto_data)
8086628b0c67SMark Fenwick {
8087628b0c67SMark Fenwick 	cm_mech->combined_mech.cm_type = assoc->ipsa_emech.cm_type;
8088628b0c67SMark Fenwick 	cm_mech->combined_mech.cm_param_len = 0;
8089628b0c67SMark Fenwick 	cm_mech->combined_mech.cm_param = NULL;
8090628b0c67SMark Fenwick 	crypto_data->cd_miscdata = (char *)iv_ptr;
8091628b0c67SMark Fenwick }
8092628b0c67SMark Fenwick 
8093628b0c67SMark Fenwick /* ARGSUSED */
8094628b0c67SMark Fenwick void
8095628b0c67SMark Fenwick gcm_params_init(ipsa_t *assoc, uchar_t *esph, uint_t data_len, uchar_t *iv_ptr,
8096628b0c67SMark Fenwick     ipsa_cm_mech_t *cm_mech, crypto_data_t *crypto_data)
8097628b0c67SMark Fenwick {
8098628b0c67SMark Fenwick 	uchar_t *nonce;
8099628b0c67SMark Fenwick 	crypto_mechanism_t *combined_mech;
8100628b0c67SMark Fenwick 	CK_AES_GCM_PARAMS *params;
8101628b0c67SMark Fenwick 
8102628b0c67SMark Fenwick 	combined_mech = (crypto_mechanism_t *)cm_mech;
8103628b0c67SMark Fenwick 	params = (CK_AES_GCM_PARAMS *)(combined_mech + 1);
8104628b0c67SMark Fenwick 	nonce = (uchar_t *)(params + 1);
8105628b0c67SMark Fenwick 
8106628b0c67SMark Fenwick 	params->pIv = nonce;
8107628b0c67SMark Fenwick 	params->ulIvLen = assoc->ipsa_nonce_len;
8108628b0c67SMark Fenwick 	params->ulIvBits = SADB_8TO1(assoc->ipsa_nonce_len);
8109628b0c67SMark Fenwick 	params->pAAD = esph;
8110628b0c67SMark Fenwick 	params->ulAADLen = sizeof (esph_t);
8111628b0c67SMark Fenwick 	params->ulTagBits = SADB_8TO1(assoc->ipsa_mac_len);
8112628b0c67SMark Fenwick 
8113628b0c67SMark Fenwick 	cm_mech->combined_mech.cm_type = assoc->ipsa_emech.cm_type;
8114628b0c67SMark Fenwick 	cm_mech->combined_mech.cm_param_len = sizeof (CK_AES_GCM_PARAMS);
8115628b0c67SMark Fenwick 	cm_mech->combined_mech.cm_param = (caddr_t)params;
8116628b0c67SMark Fenwick 	/*
8117628b0c67SMark Fenwick 	 * Create the nonce, which is made up of the salt and the IV.
8118628b0c67SMark Fenwick 	 * Copy the salt from the SA and the IV from the packet.
8119628b0c67SMark Fenwick 	 * For inbound packets we copy the IV from the packet because it
8120628b0c67SMark Fenwick 	 * was set by the sending system, for outbound packets we copy the IV
8121628b0c67SMark Fenwick 	 * from the packet because the IV in the SA may be changed by another
8122628b0c67SMark Fenwick 	 * thread, the IV in the packet was created while holding a mutex.
8123628b0c67SMark Fenwick 	 */
8124628b0c67SMark Fenwick 	bcopy(assoc->ipsa_nonce, nonce, assoc->ipsa_saltlen);
8125628b0c67SMark Fenwick 	nonce += assoc->ipsa_saltlen;
8126628b0c67SMark Fenwick 	bcopy(iv_ptr, nonce, assoc->ipsa_iv_len);
8127628b0c67SMark Fenwick 	crypto_data->cd_miscdata = NULL;
8128628b0c67SMark Fenwick }
8129