xref: /titanic_44/usr/src/uts/common/inet/ip/sadb.c (revision 75d94465dbafa487b716482dc36d5150a4ec9853)
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.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate #include <sys/types.h>
277c478bd9Sstevel@tonic-gate #include <sys/stream.h>
287c478bd9Sstevel@tonic-gate #include <sys/stropts.h>
29de8c4a14SErik Nordmark #include <sys/strsubr.h>
3038d95a78Smarkfen #include <sys/errno.h>
317c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
327c478bd9Sstevel@tonic-gate #include <sys/debug.h>
337c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
347c478bd9Sstevel@tonic-gate #include <sys/stream.h>
357c478bd9Sstevel@tonic-gate #include <sys/strlog.h>
367c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
377c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
387c478bd9Sstevel@tonic-gate #include <sys/tihdr.h>
397c478bd9Sstevel@tonic-gate #include <sys/atomic.h>
407c478bd9Sstevel@tonic-gate #include <sys/socket.h>
417c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
427c478bd9Sstevel@tonic-gate #include <sys/crypto/common.h>
437c478bd9Sstevel@tonic-gate #include <sys/crypto/api.h>
447c478bd9Sstevel@tonic-gate #include <sys/zone.h>
457c478bd9Sstevel@tonic-gate #include <netinet/in.h>
467c478bd9Sstevel@tonic-gate #include <net/if.h>
477c478bd9Sstevel@tonic-gate #include <net/pfkeyv2.h>
48628b0c67SMark Fenwick #include <net/pfpolicy.h>
497c478bd9Sstevel@tonic-gate #include <inet/common.h>
507c478bd9Sstevel@tonic-gate #include <netinet/ip6.h>
517c478bd9Sstevel@tonic-gate #include <inet/ip.h>
528810c16bSdanmcd #include <inet/ip_ire.h>
537c478bd9Sstevel@tonic-gate #include <inet/ip6.h>
547c478bd9Sstevel@tonic-gate #include <inet/ipsec_info.h>
557c478bd9Sstevel@tonic-gate #include <inet/tcp.h>
567c478bd9Sstevel@tonic-gate #include <inet/sadb.h>
57f4b3ec61Sdh155122 #include <inet/ipsec_impl.h>
587c478bd9Sstevel@tonic-gate #include <inet/ipsecah.h>
597c478bd9Sstevel@tonic-gate #include <inet/ipsecesp.h>
607c478bd9Sstevel@tonic-gate #include <sys/random.h>
617c478bd9Sstevel@tonic-gate #include <sys/dlpi.h>
625d3b8cb7SBill Sommerfeld #include <sys/strsun.h>
635d3b8cb7SBill Sommerfeld #include <sys/strsubr.h>
647c478bd9Sstevel@tonic-gate #include <inet/ip_if.h>
657c478bd9Sstevel@tonic-gate #include <inet/ipdrop.h>
667c478bd9Sstevel@tonic-gate #include <inet/ipclassifier.h>
677c478bd9Sstevel@tonic-gate #include <inet/sctp_ip.h>
685d3b8cb7SBill Sommerfeld #include <sys/tsol/tnet.h>
697c478bd9Sstevel@tonic-gate 
707c478bd9Sstevel@tonic-gate /*
717c478bd9Sstevel@tonic-gate  * This source file contains Security Association Database (SADB) common
727c478bd9Sstevel@tonic-gate  * routines.  They are linked in with the AH module.  Since AH has no chance
737c478bd9Sstevel@tonic-gate  * of falling under export control, it was safe to link it in there.
747c478bd9Sstevel@tonic-gate  */
757c478bd9Sstevel@tonic-gate 
767c478bd9Sstevel@tonic-gate static mblk_t *sadb_extended_acquire(ipsec_selector_t *, ipsec_policy_t *,
775d3b8cb7SBill Sommerfeld     ipsec_action_t *, boolean_t, uint32_t, uint32_t, sadb_sens_t *,
785d3b8cb7SBill Sommerfeld     netstack_t *);
79bd670b35SErik Nordmark static ipsa_t *sadb_torch_assoc(isaf_t *, ipsa_t *);
80f4b3ec61Sdh155122 static void sadb_destroy_acqlist(iacqf_t **, uint_t, boolean_t,
81f4b3ec61Sdh155122 			    netstack_t *);
82f4b3ec61Sdh155122 static void sadb_destroy(sadb_t *, netstack_t *);
838810c16bSdanmcd static mblk_t *sadb_sa2msg(ipsa_t *, sadb_msg_t *);
84bd670b35SErik Nordmark static ts_label_t *sadb_label_from_sens(sadb_sens_t *, uint64_t *);
85bd670b35SErik Nordmark static sadb_sens_t *sadb_make_sens_ext(ts_label_t *tsl, int *len);
867c478bd9Sstevel@tonic-gate 
878810c16bSdanmcd static time_t sadb_add_time(time_t, uint64_t);
8838d95a78Smarkfen static void lifetime_fuzz(ipsa_t *);
8938d95a78Smarkfen static void age_pair_peer_list(templist_t *, sadb_t *, boolean_t);
905d3b8cb7SBill Sommerfeld static int get_ipsa_pair(ipsa_query_t *, ipsap_t *, int *);
915d3b8cb7SBill Sommerfeld static void init_ipsa_pair(ipsap_t *);
925d3b8cb7SBill Sommerfeld static void destroy_ipsa_pair(ipsap_t *);
935d3b8cb7SBill Sommerfeld static int update_pairing(ipsap_t *, ipsa_query_t *, keysock_in_t *, int *);
949c2c14abSThejaswini Singarajipura static void ipsa_set_replay(ipsa_t *ipsa, uint32_t offset);
959c2c14abSThejaswini Singarajipura 
96bffb04cfSmarkfen /*
97bffb04cfSmarkfen  * ipsacq_maxpackets is defined here to make it tunable
98bffb04cfSmarkfen  * from /etc/system.
99bffb04cfSmarkfen  */
100bffb04cfSmarkfen extern uint64_t ipsacq_maxpackets;
101bffb04cfSmarkfen 
1027c478bd9Sstevel@tonic-gate #define	SET_EXPIRE(sa, delta, exp) {				\
1037c478bd9Sstevel@tonic-gate 	if (((sa)->ipsa_ ## delta) != 0) {				\
1047c478bd9Sstevel@tonic-gate 		(sa)->ipsa_ ## exp = sadb_add_time((sa)->ipsa_addtime,	\
1057c478bd9Sstevel@tonic-gate 			(sa)->ipsa_ ## delta);				\
1067c478bd9Sstevel@tonic-gate 	}								\
1077c478bd9Sstevel@tonic-gate }
1087c478bd9Sstevel@tonic-gate 
1097c478bd9Sstevel@tonic-gate #define	UPDATE_EXPIRE(sa, delta, exp) {					\
1107c478bd9Sstevel@tonic-gate 	if (((sa)->ipsa_ ## delta) != 0) {				\
1117c478bd9Sstevel@tonic-gate 		time_t tmp = sadb_add_time((sa)->ipsa_usetime,		\
1127c478bd9Sstevel@tonic-gate 			(sa)->ipsa_ ## delta);				\
1137c478bd9Sstevel@tonic-gate 		if (((sa)->ipsa_ ## exp) == 0)				\
1147c478bd9Sstevel@tonic-gate 			(sa)->ipsa_ ## exp = tmp;			\
1157c478bd9Sstevel@tonic-gate 		else							\
1167c478bd9Sstevel@tonic-gate 			(sa)->ipsa_ ## exp = 				\
1177c478bd9Sstevel@tonic-gate 			    MIN((sa)->ipsa_ ## exp, tmp); 		\
1187c478bd9Sstevel@tonic-gate 	}								\
1197c478bd9Sstevel@tonic-gate }
1207c478bd9Sstevel@tonic-gate 
1217c478bd9Sstevel@tonic-gate 
1227c478bd9Sstevel@tonic-gate /* wrap the macro so we can pass it as a function pointer */
1237c478bd9Sstevel@tonic-gate void
sadb_sa_refrele(void * target)1247c478bd9Sstevel@tonic-gate sadb_sa_refrele(void *target)
1257c478bd9Sstevel@tonic-gate {
1267c478bd9Sstevel@tonic-gate 	IPSA_REFRELE(((ipsa_t *)target));
1277c478bd9Sstevel@tonic-gate }
1287c478bd9Sstevel@tonic-gate 
1297c478bd9Sstevel@tonic-gate /*
1307c478bd9Sstevel@tonic-gate  * We presume that sizeof (long) == sizeof (time_t) and that time_t is
1317c478bd9Sstevel@tonic-gate  * a signed type.
1327c478bd9Sstevel@tonic-gate  */
1337c478bd9Sstevel@tonic-gate #define	TIME_MAX LONG_MAX
1347c478bd9Sstevel@tonic-gate 
1357c478bd9Sstevel@tonic-gate /*
1367c478bd9Sstevel@tonic-gate  * PF_KEY gives us lifetimes in uint64_t seconds.  We presume that
1377c478bd9Sstevel@tonic-gate  * time_t is defined to be a signed type with the same range as
1387c478bd9Sstevel@tonic-gate  * "long".  On ILP32 systems, we thus run the risk of wrapping around
1397c478bd9Sstevel@tonic-gate  * at end of time, as well as "overwrapping" the clock back around
1407c478bd9Sstevel@tonic-gate  * into a seemingly valid but incorrect future date earlier than the
1417c478bd9Sstevel@tonic-gate  * desired expiration.
1427c478bd9Sstevel@tonic-gate  *
1437c478bd9Sstevel@tonic-gate  * In order to avoid odd behavior (either negative lifetimes or loss
1447c478bd9Sstevel@tonic-gate  * of high order bits) when someone asks for bizarrely long SA
1457c478bd9Sstevel@tonic-gate  * lifetimes, we do a saturating add for expire times.
1467c478bd9Sstevel@tonic-gate  *
1477c478bd9Sstevel@tonic-gate  * We presume that ILP32 systems will be past end of support life when
1487c478bd9Sstevel@tonic-gate  * the 32-bit time_t overflows (a dangerous assumption, mind you..).
1497c478bd9Sstevel@tonic-gate  *
1507c478bd9Sstevel@tonic-gate  * On LP64, 2^64 seconds are about 5.8e11 years, at which point we
1517c478bd9Sstevel@tonic-gate  * will hopefully have figured out clever ways to avoid the use of
1527c478bd9Sstevel@tonic-gate  * fixed-sized integers in computation.
1537c478bd9Sstevel@tonic-gate  */
1547c478bd9Sstevel@tonic-gate static time_t
sadb_add_time(time_t base,uint64_t delta)1557c478bd9Sstevel@tonic-gate sadb_add_time(time_t base, uint64_t delta)
1567c478bd9Sstevel@tonic-gate {
1577c478bd9Sstevel@tonic-gate 	time_t sum;
1587c478bd9Sstevel@tonic-gate 
1597c478bd9Sstevel@tonic-gate 	/*
1607c478bd9Sstevel@tonic-gate 	 * Clip delta to the maximum possible time_t value to
1617c478bd9Sstevel@tonic-gate 	 * prevent "overwrapping" back into a shorter-than-desired
1627c478bd9Sstevel@tonic-gate 	 * future time.
1637c478bd9Sstevel@tonic-gate 	 */
1647c478bd9Sstevel@tonic-gate 	if (delta > TIME_MAX)
1657c478bd9Sstevel@tonic-gate 		delta = TIME_MAX;
1667c478bd9Sstevel@tonic-gate 	/*
1677c478bd9Sstevel@tonic-gate 	 * This sum may still overflow.
1687c478bd9Sstevel@tonic-gate 	 */
1697c478bd9Sstevel@tonic-gate 	sum = base + delta;
1707c478bd9Sstevel@tonic-gate 
1717c478bd9Sstevel@tonic-gate 	/*
1727c478bd9Sstevel@tonic-gate 	 * .. so if the result is less than the base, we overflowed.
1737c478bd9Sstevel@tonic-gate 	 */
1747c478bd9Sstevel@tonic-gate 	if (sum < base)
1757c478bd9Sstevel@tonic-gate 		sum = TIME_MAX;
1767c478bd9Sstevel@tonic-gate 
1777c478bd9Sstevel@tonic-gate 	return (sum);
1787c478bd9Sstevel@tonic-gate }
1797c478bd9Sstevel@tonic-gate 
1807c478bd9Sstevel@tonic-gate /*
1817c478bd9Sstevel@tonic-gate  * Callers of this function have already created a working security
1827c478bd9Sstevel@tonic-gate  * association, and have found the appropriate table & hash chain.  All this
1837c478bd9Sstevel@tonic-gate  * function does is check duplicates, and insert the SA.  The caller needs to
1847c478bd9Sstevel@tonic-gate  * hold the hash bucket lock and increment the refcnt before insertion.
1857c478bd9Sstevel@tonic-gate  *
1867c478bd9Sstevel@tonic-gate  * Return 0 if success, EEXIST if collision.
1877c478bd9Sstevel@tonic-gate  */
188437220cdSdanmcd #define	SA_UNIQUE_MATCH(sa1, sa2) \
189437220cdSdanmcd 	(((sa1)->ipsa_unique_id & (sa1)->ipsa_unique_mask) == \
190437220cdSdanmcd 	((sa2)->ipsa_unique_id & (sa2)->ipsa_unique_mask))
191437220cdSdanmcd 
1927c478bd9Sstevel@tonic-gate int
sadb_insertassoc(ipsa_t * ipsa,isaf_t * bucket)1937c478bd9Sstevel@tonic-gate sadb_insertassoc(ipsa_t *ipsa, isaf_t *bucket)
1947c478bd9Sstevel@tonic-gate {
1957c478bd9Sstevel@tonic-gate 	ipsa_t **ptpn = NULL;
1967c478bd9Sstevel@tonic-gate 	ipsa_t *walker;
1977c478bd9Sstevel@tonic-gate 	boolean_t unspecsrc;
1987c478bd9Sstevel@tonic-gate 
1997c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&bucket->isaf_lock));
2007c478bd9Sstevel@tonic-gate 
2017c478bd9Sstevel@tonic-gate 	unspecsrc = IPSA_IS_ADDR_UNSPEC(ipsa->ipsa_srcaddr, ipsa->ipsa_addrfam);
2027c478bd9Sstevel@tonic-gate 
2037c478bd9Sstevel@tonic-gate 	walker = bucket->isaf_ipsa;
2047c478bd9Sstevel@tonic-gate 	ASSERT(walker == NULL || ipsa->ipsa_addrfam == walker->ipsa_addrfam);
2057c478bd9Sstevel@tonic-gate 
2067c478bd9Sstevel@tonic-gate 	/*
2077c478bd9Sstevel@tonic-gate 	 * Find insertion point (pointed to with **ptpn).  Insert at the head
2087c478bd9Sstevel@tonic-gate 	 * of the list unless there's an unspecified source address, then
2097c478bd9Sstevel@tonic-gate 	 * insert it after the last SA with a specified source address.
2107c478bd9Sstevel@tonic-gate 	 *
2117c478bd9Sstevel@tonic-gate 	 * BTW, you'll have to walk the whole chain, matching on {DST, SPI}
2127c478bd9Sstevel@tonic-gate 	 * checking for collisions.
2137c478bd9Sstevel@tonic-gate 	 */
2147c478bd9Sstevel@tonic-gate 
2157c478bd9Sstevel@tonic-gate 	while (walker != NULL) {
2167c478bd9Sstevel@tonic-gate 		if (IPSA_ARE_ADDR_EQUAL(walker->ipsa_dstaddr,
2177c478bd9Sstevel@tonic-gate 		    ipsa->ipsa_dstaddr, ipsa->ipsa_addrfam)) {
2187c478bd9Sstevel@tonic-gate 			if (walker->ipsa_spi == ipsa->ipsa_spi)
2197c478bd9Sstevel@tonic-gate 				return (EEXIST);
2207c478bd9Sstevel@tonic-gate 
2217c478bd9Sstevel@tonic-gate 			mutex_enter(&walker->ipsa_lock);
2227c478bd9Sstevel@tonic-gate 			if (ipsa->ipsa_state == IPSA_STATE_MATURE &&
2237c478bd9Sstevel@tonic-gate 			    (walker->ipsa_flags & IPSA_F_USED) &&
224437220cdSdanmcd 			    SA_UNIQUE_MATCH(walker, ipsa)) {
2257c478bd9Sstevel@tonic-gate 				walker->ipsa_flags |= IPSA_F_CINVALID;
2267c478bd9Sstevel@tonic-gate 			}
2277c478bd9Sstevel@tonic-gate 			mutex_exit(&walker->ipsa_lock);
2287c478bd9Sstevel@tonic-gate 		}
2297c478bd9Sstevel@tonic-gate 
2307c478bd9Sstevel@tonic-gate 		if (ptpn == NULL && unspecsrc) {
2317c478bd9Sstevel@tonic-gate 			if (IPSA_IS_ADDR_UNSPEC(walker->ipsa_srcaddr,
2327c478bd9Sstevel@tonic-gate 			    walker->ipsa_addrfam))
2337c478bd9Sstevel@tonic-gate 				ptpn = walker->ipsa_ptpn;
2347c478bd9Sstevel@tonic-gate 			else if (walker->ipsa_next == NULL)
2357c478bd9Sstevel@tonic-gate 				ptpn = &walker->ipsa_next;
2367c478bd9Sstevel@tonic-gate 		}
2377c478bd9Sstevel@tonic-gate 
2387c478bd9Sstevel@tonic-gate 		walker = walker->ipsa_next;
2397c478bd9Sstevel@tonic-gate 	}
2407c478bd9Sstevel@tonic-gate 
2417c478bd9Sstevel@tonic-gate 	if (ptpn == NULL)
2427c478bd9Sstevel@tonic-gate 		ptpn = &bucket->isaf_ipsa;
2437c478bd9Sstevel@tonic-gate 	ipsa->ipsa_next = *ptpn;
2447c478bd9Sstevel@tonic-gate 	ipsa->ipsa_ptpn = ptpn;
2457c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_next != NULL)
2467c478bd9Sstevel@tonic-gate 		ipsa->ipsa_next->ipsa_ptpn = &ipsa->ipsa_next;
2477c478bd9Sstevel@tonic-gate 	*ptpn = ipsa;
2487c478bd9Sstevel@tonic-gate 	ipsa->ipsa_linklock = &bucket->isaf_lock;
2497c478bd9Sstevel@tonic-gate 
2507c478bd9Sstevel@tonic-gate 	return (0);
2517c478bd9Sstevel@tonic-gate }
252437220cdSdanmcd #undef SA_UNIQUE_MATCH
2537c478bd9Sstevel@tonic-gate 
2547c478bd9Sstevel@tonic-gate /*
2557c478bd9Sstevel@tonic-gate  * Free a security association.  Its reference count is 0, which means
2567c478bd9Sstevel@tonic-gate  * I must free it.  The SA must be unlocked and must not be linked into
2577c478bd9Sstevel@tonic-gate  * any fanout list.
2587c478bd9Sstevel@tonic-gate  */
2597c478bd9Sstevel@tonic-gate static void
sadb_freeassoc(ipsa_t * ipsa)2607c478bd9Sstevel@tonic-gate sadb_freeassoc(ipsa_t *ipsa)
2617c478bd9Sstevel@tonic-gate {
262f4b3ec61Sdh155122 	ipsec_stack_t	*ipss = ipsa->ipsa_netstack->netstack_ipsec;
263bd670b35SErik Nordmark 	mblk_t		*asyncmp, *mp;
264f4b3ec61Sdh155122 
265f4b3ec61Sdh155122 	ASSERT(ipss != NULL);
2660c0328cdSBill Sommerfeld 	ASSERT(MUTEX_NOT_HELD(&ipsa->ipsa_lock));
2677c478bd9Sstevel@tonic-gate 	ASSERT(ipsa->ipsa_refcnt == 0);
2687c478bd9Sstevel@tonic-gate 	ASSERT(ipsa->ipsa_next == NULL);
2697c478bd9Sstevel@tonic-gate 	ASSERT(ipsa->ipsa_ptpn == NULL);
2707c478bd9Sstevel@tonic-gate 
271bd670b35SErik Nordmark 
272bd670b35SErik Nordmark 	asyncmp = sadb_clear_lpkt(ipsa);
273bd670b35SErik Nordmark 	if (asyncmp != NULL) {
274bd670b35SErik Nordmark 		mp = ip_recv_attr_free_mblk(asyncmp);
275bd670b35SErik Nordmark 		ip_drop_packet(mp, B_TRUE, NULL,
276f4b3ec61Sdh155122 		    DROPPER(ipss, ipds_sadb_inlarval_timeout),
277f4b3ec61Sdh155122 		    &ipss->ipsec_sadb_dropper);
278bd670b35SErik Nordmark 	}
279bd670b35SErik Nordmark 	mutex_enter(&ipsa->ipsa_lock);
2805d3b8cb7SBill Sommerfeld 
281bd670b35SErik Nordmark 	if (ipsa->ipsa_tsl != NULL) {
282bd670b35SErik Nordmark 		label_rele(ipsa->ipsa_tsl);
283bd670b35SErik Nordmark 		ipsa->ipsa_tsl = NULL;
2845d3b8cb7SBill Sommerfeld 	}
2855d3b8cb7SBill Sommerfeld 
286bd670b35SErik Nordmark 	if (ipsa->ipsa_otsl != NULL) {
287bd670b35SErik Nordmark 		label_rele(ipsa->ipsa_otsl);
288bd670b35SErik Nordmark 		ipsa->ipsa_otsl = NULL;
2895d3b8cb7SBill Sommerfeld 	}
2905d3b8cb7SBill Sommerfeld 
2917c478bd9Sstevel@tonic-gate 	ipsec_destroy_ctx_tmpl(ipsa, IPSEC_ALG_AUTH);
2927c478bd9Sstevel@tonic-gate 	ipsec_destroy_ctx_tmpl(ipsa, IPSEC_ALG_ENCR);
2937c478bd9Sstevel@tonic-gate 	mutex_exit(&ipsa->ipsa_lock);
2947c478bd9Sstevel@tonic-gate 
2957c478bd9Sstevel@tonic-gate 	/* bzero() these fields for paranoia's sake. */
2967c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_authkey != NULL) {
2977c478bd9Sstevel@tonic-gate 		bzero(ipsa->ipsa_authkey, ipsa->ipsa_authkeylen);
2987c478bd9Sstevel@tonic-gate 		kmem_free(ipsa->ipsa_authkey, ipsa->ipsa_authkeylen);
2997c478bd9Sstevel@tonic-gate 	}
3007c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_encrkey != NULL) {
3017c478bd9Sstevel@tonic-gate 		bzero(ipsa->ipsa_encrkey, ipsa->ipsa_encrkeylen);
3027c478bd9Sstevel@tonic-gate 		kmem_free(ipsa->ipsa_encrkey, ipsa->ipsa_encrkeylen);
3037c478bd9Sstevel@tonic-gate 	}
304628b0c67SMark Fenwick 	if (ipsa->ipsa_nonce_buf != NULL) {
305628b0c67SMark Fenwick 		bzero(ipsa->ipsa_nonce_buf, sizeof (ipsec_nonce_t));
306628b0c67SMark Fenwick 		kmem_free(ipsa->ipsa_nonce_buf, sizeof (ipsec_nonce_t));
307628b0c67SMark Fenwick 	}
3087c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_src_cid != NULL) {
3097c478bd9Sstevel@tonic-gate 		IPSID_REFRELE(ipsa->ipsa_src_cid);
3107c478bd9Sstevel@tonic-gate 	}
3117c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_dst_cid != NULL) {
3127c478bd9Sstevel@tonic-gate 		IPSID_REFRELE(ipsa->ipsa_dst_cid);
3137c478bd9Sstevel@tonic-gate 	}
314628b0c67SMark Fenwick 	if (ipsa->ipsa_emech.cm_param != NULL)
315628b0c67SMark Fenwick 		kmem_free(ipsa->ipsa_emech.cm_param,
316628b0c67SMark Fenwick 		    ipsa->ipsa_emech.cm_param_len);
3177c478bd9Sstevel@tonic-gate 
3187c478bd9Sstevel@tonic-gate 	mutex_destroy(&ipsa->ipsa_lock);
3197c478bd9Sstevel@tonic-gate 	kmem_free(ipsa, sizeof (*ipsa));
3207c478bd9Sstevel@tonic-gate }
3217c478bd9Sstevel@tonic-gate 
3227c478bd9Sstevel@tonic-gate /*
3237c478bd9Sstevel@tonic-gate  * Unlink a security association from a hash bucket.  Assume the hash bucket
3247c478bd9Sstevel@tonic-gate  * lock is held, but the association's lock is not.
3257c478bd9Sstevel@tonic-gate  *
3267c478bd9Sstevel@tonic-gate  * Note that we do not bump the bucket's generation number here because
3277c478bd9Sstevel@tonic-gate  * we might not be making a visible change to the set of visible SA's.
3287c478bd9Sstevel@tonic-gate  * All callers MUST bump the bucket's generation number before they unlock
3297c478bd9Sstevel@tonic-gate  * the bucket if they use sadb_unlinkassoc to permanetly remove an SA which
3307c478bd9Sstevel@tonic-gate  * was present in the bucket at the time it was locked.
3317c478bd9Sstevel@tonic-gate  */
3327c478bd9Sstevel@tonic-gate void
sadb_unlinkassoc(ipsa_t * ipsa)3337c478bd9Sstevel@tonic-gate sadb_unlinkassoc(ipsa_t *ipsa)
3347c478bd9Sstevel@tonic-gate {
3357c478bd9Sstevel@tonic-gate 	ASSERT(ipsa->ipsa_linklock != NULL);
3367c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(ipsa->ipsa_linklock));
3377c478bd9Sstevel@tonic-gate 
3387c478bd9Sstevel@tonic-gate 	/* These fields are protected by the link lock. */
3397c478bd9Sstevel@tonic-gate 	*(ipsa->ipsa_ptpn) = ipsa->ipsa_next;
3407c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_next != NULL) {
3417c478bd9Sstevel@tonic-gate 		ipsa->ipsa_next->ipsa_ptpn = ipsa->ipsa_ptpn;
3427c478bd9Sstevel@tonic-gate 		ipsa->ipsa_next = NULL;
3437c478bd9Sstevel@tonic-gate 	}
3447c478bd9Sstevel@tonic-gate 
3457c478bd9Sstevel@tonic-gate 	ipsa->ipsa_ptpn = NULL;
3467c478bd9Sstevel@tonic-gate 
3477c478bd9Sstevel@tonic-gate 	/* This may destroy the SA. */
3487c478bd9Sstevel@tonic-gate 	IPSA_REFRELE(ipsa);
3497c478bd9Sstevel@tonic-gate }
3507c478bd9Sstevel@tonic-gate 
3519c2c14abSThejaswini Singarajipura void
sadb_delete_cluster(ipsa_t * assoc)3529c2c14abSThejaswini Singarajipura sadb_delete_cluster(ipsa_t *assoc)
3539c2c14abSThejaswini Singarajipura {
3549c2c14abSThejaswini Singarajipura 	uint8_t protocol;
3559c2c14abSThejaswini Singarajipura 
3569c2c14abSThejaswini Singarajipura 	if (cl_inet_deletespi &&
3579c2c14abSThejaswini Singarajipura 	    ((assoc->ipsa_state == IPSA_STATE_LARVAL) ||
3589c2c14abSThejaswini Singarajipura 	    (assoc->ipsa_state == IPSA_STATE_MATURE))) {
3599c2c14abSThejaswini Singarajipura 		protocol = (assoc->ipsa_type == SADB_SATYPE_AH) ?
3609c2c14abSThejaswini Singarajipura 		    IPPROTO_AH : IPPROTO_ESP;
3618e4b770fSLu Huafeng 		cl_inet_deletespi(assoc->ipsa_netstack->netstack_stackid,
3628e4b770fSLu Huafeng 		    protocol, assoc->ipsa_spi, NULL);
3639c2c14abSThejaswini Singarajipura 	}
3649c2c14abSThejaswini Singarajipura }
3659c2c14abSThejaswini Singarajipura 
3667c478bd9Sstevel@tonic-gate /*
3677c478bd9Sstevel@tonic-gate  * Create a larval security association with the specified SPI.	 All other
3687c478bd9Sstevel@tonic-gate  * fields are zeroed.
3697c478bd9Sstevel@tonic-gate  */
3707c478bd9Sstevel@tonic-gate static ipsa_t *
sadb_makelarvalassoc(uint32_t spi,uint32_t * src,uint32_t * dst,int addrfam,netstack_t * ns)371f4b3ec61Sdh155122 sadb_makelarvalassoc(uint32_t spi, uint32_t *src, uint32_t *dst, int addrfam,
372f4b3ec61Sdh155122     netstack_t *ns)
3737c478bd9Sstevel@tonic-gate {
3747c478bd9Sstevel@tonic-gate 	ipsa_t *newbie;
3757c478bd9Sstevel@tonic-gate 
3767c478bd9Sstevel@tonic-gate 	/*
3777c478bd9Sstevel@tonic-gate 	 * Allocate...
3787c478bd9Sstevel@tonic-gate 	 */
3797c478bd9Sstevel@tonic-gate 
3807c478bd9Sstevel@tonic-gate 	newbie = (ipsa_t *)kmem_zalloc(sizeof (ipsa_t), KM_NOSLEEP);
3817c478bd9Sstevel@tonic-gate 	if (newbie == NULL) {
3827c478bd9Sstevel@tonic-gate 		/* Can't make new larval SA. */
3837c478bd9Sstevel@tonic-gate 		return (NULL);
3847c478bd9Sstevel@tonic-gate 	}
3857c478bd9Sstevel@tonic-gate 
3867c478bd9Sstevel@tonic-gate 	/* Assigned requested SPI, assume caller does SPI allocation magic. */
3877c478bd9Sstevel@tonic-gate 	newbie->ipsa_spi = spi;
388f4b3ec61Sdh155122 	newbie->ipsa_netstack = ns;	/* No netstack_hold */
3897c478bd9Sstevel@tonic-gate 
3907c478bd9Sstevel@tonic-gate 	/*
3917c478bd9Sstevel@tonic-gate 	 * Copy addresses...
3927c478bd9Sstevel@tonic-gate 	 */
3937c478bd9Sstevel@tonic-gate 
3947c478bd9Sstevel@tonic-gate 	IPSA_COPY_ADDR(newbie->ipsa_srcaddr, src, addrfam);
3957c478bd9Sstevel@tonic-gate 	IPSA_COPY_ADDR(newbie->ipsa_dstaddr, dst, addrfam);
3967c478bd9Sstevel@tonic-gate 
3977c478bd9Sstevel@tonic-gate 	newbie->ipsa_addrfam = addrfam;
3987c478bd9Sstevel@tonic-gate 
3997c478bd9Sstevel@tonic-gate 	/*
4007c478bd9Sstevel@tonic-gate 	 * Set common initialization values, including refcnt.
4017c478bd9Sstevel@tonic-gate 	 */
4027c478bd9Sstevel@tonic-gate 	mutex_init(&newbie->ipsa_lock, NULL, MUTEX_DEFAULT, NULL);
4037c478bd9Sstevel@tonic-gate 	newbie->ipsa_state = IPSA_STATE_LARVAL;
4047c478bd9Sstevel@tonic-gate 	newbie->ipsa_refcnt = 1;
4057c478bd9Sstevel@tonic-gate 	newbie->ipsa_freefunc = sadb_freeassoc;
4067c478bd9Sstevel@tonic-gate 
4077c478bd9Sstevel@tonic-gate 	/*
4087c478bd9Sstevel@tonic-gate 	 * There aren't a lot of other common initialization values, as
4097c478bd9Sstevel@tonic-gate 	 * they are copied in from the PF_KEY message.
4107c478bd9Sstevel@tonic-gate 	 */
4117c478bd9Sstevel@tonic-gate 
4127c478bd9Sstevel@tonic-gate 	return (newbie);
4137c478bd9Sstevel@tonic-gate }
4147c478bd9Sstevel@tonic-gate 
4157c478bd9Sstevel@tonic-gate /*
4167c478bd9Sstevel@tonic-gate  * Call me to initialize a security association fanout.
4177c478bd9Sstevel@tonic-gate  */
418fb87b5d2Ssommerfe static int
sadb_init_fanout(isaf_t ** tablep,uint_t size,int kmflag)419fb87b5d2Ssommerfe sadb_init_fanout(isaf_t **tablep, uint_t size, int kmflag)
4207c478bd9Sstevel@tonic-gate {
4217c478bd9Sstevel@tonic-gate 	isaf_t *table;
4227c478bd9Sstevel@tonic-gate 	int i;
4237c478bd9Sstevel@tonic-gate 
424fb87b5d2Ssommerfe 	table = (isaf_t *)kmem_alloc(size * sizeof (*table), kmflag);
4257c478bd9Sstevel@tonic-gate 	*tablep = table;
4267c478bd9Sstevel@tonic-gate 
427fb87b5d2Ssommerfe 	if (table == NULL)
428fb87b5d2Ssommerfe 		return (ENOMEM);
429fb87b5d2Ssommerfe 
430fb87b5d2Ssommerfe 	for (i = 0; i < size; i++) {
4317c478bd9Sstevel@tonic-gate 		mutex_init(&(table[i].isaf_lock), NULL, MUTEX_DEFAULT, NULL);
4327c478bd9Sstevel@tonic-gate 		table[i].isaf_ipsa = NULL;
4337c478bd9Sstevel@tonic-gate 		table[i].isaf_gen = 0;
4347c478bd9Sstevel@tonic-gate 	}
435fb87b5d2Ssommerfe 
436fb87b5d2Ssommerfe 	return (0);
4377c478bd9Sstevel@tonic-gate }
4387c478bd9Sstevel@tonic-gate 
439fb87b5d2Ssommerfe /*
440fb87b5d2Ssommerfe  * Call me to initialize an acquire fanout
441fb87b5d2Ssommerfe  */
442fb87b5d2Ssommerfe static int
sadb_init_acfanout(iacqf_t ** tablep,uint_t size,int kmflag)443fb87b5d2Ssommerfe sadb_init_acfanout(iacqf_t **tablep, uint_t size, int kmflag)
4447c478bd9Sstevel@tonic-gate {
4457c478bd9Sstevel@tonic-gate 	iacqf_t *table;
4467c478bd9Sstevel@tonic-gate 	int i;
4477c478bd9Sstevel@tonic-gate 
448fb87b5d2Ssommerfe 	table = (iacqf_t *)kmem_alloc(size * sizeof (*table), kmflag);
4497c478bd9Sstevel@tonic-gate 	*tablep = table;
4507c478bd9Sstevel@tonic-gate 
451fb87b5d2Ssommerfe 	if (table == NULL)
452fb87b5d2Ssommerfe 		return (ENOMEM);
453fb87b5d2Ssommerfe 
454fb87b5d2Ssommerfe 	for (i = 0; i < size; i++) {
4557c478bd9Sstevel@tonic-gate 		mutex_init(&(table[i].iacqf_lock), NULL, MUTEX_DEFAULT, NULL);
4567c478bd9Sstevel@tonic-gate 		table[i].iacqf_ipsacq = NULL;
4577c478bd9Sstevel@tonic-gate 	}
458fb87b5d2Ssommerfe 
459fb87b5d2Ssommerfe 	return (0);
4607c478bd9Sstevel@tonic-gate }
4617c478bd9Sstevel@tonic-gate 
4627c478bd9Sstevel@tonic-gate /*
463fb87b5d2Ssommerfe  * Attempt to initialize an SADB instance.  On failure, return ENOMEM;
464fb87b5d2Ssommerfe  * caller must clean up partial allocations.
4657c478bd9Sstevel@tonic-gate  */
466fb87b5d2Ssommerfe static int
sadb_init_trial(sadb_t * sp,uint_t size,int kmflag)467fb87b5d2Ssommerfe sadb_init_trial(sadb_t *sp, uint_t size, int kmflag)
4687c478bd9Sstevel@tonic-gate {
469fb87b5d2Ssommerfe 	ASSERT(sp->sdb_of == NULL);
470fb87b5d2Ssommerfe 	ASSERT(sp->sdb_if == NULL);
471fb87b5d2Ssommerfe 	ASSERT(sp->sdb_acq == NULL);
472fb87b5d2Ssommerfe 
473fb87b5d2Ssommerfe 	sp->sdb_hashsize = size;
474fb87b5d2Ssommerfe 	if (sadb_init_fanout(&sp->sdb_of, size, kmflag) != 0)
475fb87b5d2Ssommerfe 		return (ENOMEM);
476fb87b5d2Ssommerfe 	if (sadb_init_fanout(&sp->sdb_if, size, kmflag) != 0)
477fb87b5d2Ssommerfe 		return (ENOMEM);
478fb87b5d2Ssommerfe 	if (sadb_init_acfanout(&sp->sdb_acq, size, kmflag) != 0)
479fb87b5d2Ssommerfe 		return (ENOMEM);
480fb87b5d2Ssommerfe 
481fb87b5d2Ssommerfe 	return (0);
4827c478bd9Sstevel@tonic-gate }
4837c478bd9Sstevel@tonic-gate 
4847c478bd9Sstevel@tonic-gate /*
485fb87b5d2Ssommerfe  * Call me to initialize an SADB instance; fall back to default size on failure.
486fb87b5d2Ssommerfe  */
487fb87b5d2Ssommerfe static void
sadb_init(const char * name,sadb_t * sp,uint_t size,uint_t ver,netstack_t * ns)488f4b3ec61Sdh155122 sadb_init(const char *name, sadb_t *sp, uint_t size, uint_t ver,
489f4b3ec61Sdh155122     netstack_t *ns)
490fb87b5d2Ssommerfe {
491fb87b5d2Ssommerfe 	ASSERT(sp->sdb_of == NULL);
492fb87b5d2Ssommerfe 	ASSERT(sp->sdb_if == NULL);
493fb87b5d2Ssommerfe 	ASSERT(sp->sdb_acq == NULL);
494fb87b5d2Ssommerfe 
495fb87b5d2Ssommerfe 	if (size < IPSEC_DEFAULT_HASH_SIZE)
496fb87b5d2Ssommerfe 		size = IPSEC_DEFAULT_HASH_SIZE;
497fb87b5d2Ssommerfe 
498fb87b5d2Ssommerfe 	if (sadb_init_trial(sp, size, KM_NOSLEEP) != 0) {
499fb87b5d2Ssommerfe 
500fb87b5d2Ssommerfe 		cmn_err(CE_WARN,
501fb87b5d2Ssommerfe 		    "Unable to allocate %u entry IPv%u %s SADB hash table",
502fb87b5d2Ssommerfe 		    size, ver, name);
503fb87b5d2Ssommerfe 
504f4b3ec61Sdh155122 		sadb_destroy(sp, ns);
505fb87b5d2Ssommerfe 		size = IPSEC_DEFAULT_HASH_SIZE;
506fb87b5d2Ssommerfe 		cmn_err(CE_WARN, "Falling back to %d entries", size);
507fb87b5d2Ssommerfe 		(void) sadb_init_trial(sp, size, KM_SLEEP);
508fb87b5d2Ssommerfe 	}
509fb87b5d2Ssommerfe }
510fb87b5d2Ssommerfe 
511fb87b5d2Ssommerfe 
512fb87b5d2Ssommerfe /*
5137c478bd9Sstevel@tonic-gate  * Initialize an SADB-pair.
5147c478bd9Sstevel@tonic-gate  */
5157c478bd9Sstevel@tonic-gate void
sadbp_init(const char * name,sadbp_t * sp,int type,int size,netstack_t * ns)516f4b3ec61Sdh155122 sadbp_init(const char *name, sadbp_t *sp, int type, int size, netstack_t *ns)
5177c478bd9Sstevel@tonic-gate {
518f4b3ec61Sdh155122 	sadb_init(name, &sp->s_v4, size, 4, ns);
519f4b3ec61Sdh155122 	sadb_init(name, &sp->s_v6, size, 6, ns);
5207c478bd9Sstevel@tonic-gate 
5217c478bd9Sstevel@tonic-gate 	sp->s_satype = type;
5227c478bd9Sstevel@tonic-gate 
5237c478bd9Sstevel@tonic-gate 	ASSERT((type == SADB_SATYPE_AH) || (type == SADB_SATYPE_ESP));
524f4b3ec61Sdh155122 	if (type == SADB_SATYPE_AH) {
525f4b3ec61Sdh155122 		ipsec_stack_t	*ipss = ns->netstack_ipsec;
526f4b3ec61Sdh155122 
527f4b3ec61Sdh155122 		ip_drop_register(&ipss->ipsec_sadb_dropper, "IPsec SADB");
52872bd9b6bSdanmcd 		sp->s_addflags = AH_ADD_SETTABLE_FLAGS;
52972bd9b6bSdanmcd 		sp->s_updateflags = AH_UPDATE_SETTABLE_FLAGS;
53072bd9b6bSdanmcd 	} else {
53172bd9b6bSdanmcd 		sp->s_addflags = ESP_ADD_SETTABLE_FLAGS;
53272bd9b6bSdanmcd 		sp->s_updateflags = ESP_UPDATE_SETTABLE_FLAGS;
533f4b3ec61Sdh155122 	}
5347c478bd9Sstevel@tonic-gate }
5357c478bd9Sstevel@tonic-gate 
5367c478bd9Sstevel@tonic-gate /*
5377c478bd9Sstevel@tonic-gate  * Deliver a single SADB_DUMP message representing a single SA.  This is
5387c478bd9Sstevel@tonic-gate  * called many times by sadb_dump().
5397c478bd9Sstevel@tonic-gate  *
5407c478bd9Sstevel@tonic-gate  * If the return value of this is ENOBUFS (not the same as ENOMEM), then
5417c478bd9Sstevel@tonic-gate  * the caller should take that as a hint that dupb() on the "original answer"
5427c478bd9Sstevel@tonic-gate  * failed, and that perhaps the caller should try again with a copyb()ed
5437c478bd9Sstevel@tonic-gate  * "original answer".
5447c478bd9Sstevel@tonic-gate  */
5457c478bd9Sstevel@tonic-gate static int
sadb_dump_deliver(queue_t * pfkey_q,mblk_t * original_answer,ipsa_t * ipsa,sadb_msg_t * samsg)5467c478bd9Sstevel@tonic-gate sadb_dump_deliver(queue_t *pfkey_q, mblk_t *original_answer, ipsa_t *ipsa,
5477c478bd9Sstevel@tonic-gate     sadb_msg_t *samsg)
5487c478bd9Sstevel@tonic-gate {
5497c478bd9Sstevel@tonic-gate 	mblk_t *answer;
5507c478bd9Sstevel@tonic-gate 
5517c478bd9Sstevel@tonic-gate 	answer = dupb(original_answer);
5527c478bd9Sstevel@tonic-gate 	if (answer == NULL)
5537c478bd9Sstevel@tonic-gate 		return (ENOBUFS);
5547c478bd9Sstevel@tonic-gate 	answer->b_cont = sadb_sa2msg(ipsa, samsg);
5557c478bd9Sstevel@tonic-gate 	if (answer->b_cont == NULL) {
5567c478bd9Sstevel@tonic-gate 		freeb(answer);
5577c478bd9Sstevel@tonic-gate 		return (ENOMEM);
5587c478bd9Sstevel@tonic-gate 	}
5597c478bd9Sstevel@tonic-gate 
5607c478bd9Sstevel@tonic-gate 	/* Just do a putnext, and let keysock deal with flow control. */
5617c478bd9Sstevel@tonic-gate 	putnext(pfkey_q, answer);
5627c478bd9Sstevel@tonic-gate 	return (0);
5637c478bd9Sstevel@tonic-gate }
5647c478bd9Sstevel@tonic-gate 
5657c478bd9Sstevel@tonic-gate /*
5667c478bd9Sstevel@tonic-gate  * Common function to allocate and prepare a keysock_out_t M_CTL message.
5677c478bd9Sstevel@tonic-gate  */
5687c478bd9Sstevel@tonic-gate mblk_t *
sadb_keysock_out(minor_t serial)5697c478bd9Sstevel@tonic-gate sadb_keysock_out(minor_t serial)
5707c478bd9Sstevel@tonic-gate {
5717c478bd9Sstevel@tonic-gate 	mblk_t *mp;
5727c478bd9Sstevel@tonic-gate 	keysock_out_t *kso;
5737c478bd9Sstevel@tonic-gate 
5747c478bd9Sstevel@tonic-gate 	mp = allocb(sizeof (ipsec_info_t), BPRI_HI);
5757c478bd9Sstevel@tonic-gate 	if (mp != NULL) {
5767c478bd9Sstevel@tonic-gate 		mp->b_datap->db_type = M_CTL;
5777c478bd9Sstevel@tonic-gate 		mp->b_wptr += sizeof (ipsec_info_t);
5787c478bd9Sstevel@tonic-gate 		kso = (keysock_out_t *)mp->b_rptr;
5797c478bd9Sstevel@tonic-gate 		kso->ks_out_type = KEYSOCK_OUT;
5807c478bd9Sstevel@tonic-gate 		kso->ks_out_len = sizeof (*kso);
5817c478bd9Sstevel@tonic-gate 		kso->ks_out_serial = serial;
5827c478bd9Sstevel@tonic-gate 	}
5837c478bd9Sstevel@tonic-gate 
5847c478bd9Sstevel@tonic-gate 	return (mp);
5857c478bd9Sstevel@tonic-gate }
5867c478bd9Sstevel@tonic-gate 
5877c478bd9Sstevel@tonic-gate /*
5887c478bd9Sstevel@tonic-gate  * Perform an SADB_DUMP, spewing out every SA in an array of SA fanouts
5897c478bd9Sstevel@tonic-gate  * to keysock.
5907c478bd9Sstevel@tonic-gate  */
5917c478bd9Sstevel@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)5927c478bd9Sstevel@tonic-gate sadb_dump_fanout(queue_t *pfkey_q, mblk_t *mp, minor_t serial, isaf_t *fanout,
5939c2c14abSThejaswini Singarajipura     int num_entries, boolean_t do_peers, time_t active_time)
5947c478bd9Sstevel@tonic-gate {
5957c478bd9Sstevel@tonic-gate 	int i, error = 0;
5967c478bd9Sstevel@tonic-gate 	mblk_t *original_answer;
5977c478bd9Sstevel@tonic-gate 	ipsa_t *walker;
5987c478bd9Sstevel@tonic-gate 	sadb_msg_t *samsg;
5999c2c14abSThejaswini Singarajipura 	time_t	current;
6007c478bd9Sstevel@tonic-gate 
6017c478bd9Sstevel@tonic-gate 	/*
6027c478bd9Sstevel@tonic-gate 	 * For each IPSA hash bucket do:
6037c478bd9Sstevel@tonic-gate 	 *	- Hold the mutex
6047c478bd9Sstevel@tonic-gate 	 *	- Walk each entry, doing an sadb_dump_deliver() on it.
6057c478bd9Sstevel@tonic-gate 	 */
6067c478bd9Sstevel@tonic-gate 	ASSERT(mp->b_cont != NULL);
6077c478bd9Sstevel@tonic-gate 	samsg = (sadb_msg_t *)mp->b_cont->b_rptr;
6087c478bd9Sstevel@tonic-gate 
6097c478bd9Sstevel@tonic-gate 	original_answer = sadb_keysock_out(serial);
6107c478bd9Sstevel@tonic-gate 	if (original_answer == NULL)
6117c478bd9Sstevel@tonic-gate 		return (ENOMEM);
6127c478bd9Sstevel@tonic-gate 
6139c2c14abSThejaswini Singarajipura 	current = gethrestime_sec();
6147c478bd9Sstevel@tonic-gate 	for (i = 0; i < num_entries; i++) {
6157c478bd9Sstevel@tonic-gate 		mutex_enter(&fanout[i].isaf_lock);
6167c478bd9Sstevel@tonic-gate 		for (walker = fanout[i].isaf_ipsa; walker != NULL;
6177c478bd9Sstevel@tonic-gate 		    walker = walker->ipsa_next) {
6187c478bd9Sstevel@tonic-gate 			if (!do_peers && walker->ipsa_haspeer)
6197c478bd9Sstevel@tonic-gate 				continue;
6209c2c14abSThejaswini Singarajipura 			if ((active_time != 0) &&
6219c2c14abSThejaswini Singarajipura 			    ((current - walker->ipsa_lastuse) > active_time))
6229c2c14abSThejaswini Singarajipura 				continue;
6237c478bd9Sstevel@tonic-gate 			error = sadb_dump_deliver(pfkey_q, original_answer,
6247c478bd9Sstevel@tonic-gate 			    walker, samsg);
6257c478bd9Sstevel@tonic-gate 			if (error == ENOBUFS) {
6267c478bd9Sstevel@tonic-gate 				mblk_t *new_original_answer;
6277c478bd9Sstevel@tonic-gate 
6287c478bd9Sstevel@tonic-gate 				/* Ran out of dupb's.  Try a copyb. */
6297c478bd9Sstevel@tonic-gate 				new_original_answer = copyb(original_answer);
6307c478bd9Sstevel@tonic-gate 				if (new_original_answer == NULL) {
6317c478bd9Sstevel@tonic-gate 					error = ENOMEM;
6327c478bd9Sstevel@tonic-gate 				} else {
6337c478bd9Sstevel@tonic-gate 					freeb(original_answer);
6347c478bd9Sstevel@tonic-gate 					original_answer = new_original_answer;
6357c478bd9Sstevel@tonic-gate 					error = sadb_dump_deliver(pfkey_q,
6367c478bd9Sstevel@tonic-gate 					    original_answer, walker, samsg);
6377c478bd9Sstevel@tonic-gate 				}
6387c478bd9Sstevel@tonic-gate 			}
6397c478bd9Sstevel@tonic-gate 			if (error != 0)
6407c478bd9Sstevel@tonic-gate 				break;	/* out of for loop. */
6417c478bd9Sstevel@tonic-gate 		}
6427c478bd9Sstevel@tonic-gate 		mutex_exit(&fanout[i].isaf_lock);
6437c478bd9Sstevel@tonic-gate 		if (error != 0)
6447c478bd9Sstevel@tonic-gate 			break;	/* out of for loop. */
6457c478bd9Sstevel@tonic-gate 	}
6467c478bd9Sstevel@tonic-gate 
6477c478bd9Sstevel@tonic-gate 	freeb(original_answer);
6487c478bd9Sstevel@tonic-gate 	return (error);
6497c478bd9Sstevel@tonic-gate }
6507c478bd9Sstevel@tonic-gate 
6517c478bd9Sstevel@tonic-gate /*
6527c478bd9Sstevel@tonic-gate  * Dump an entire SADB; outbound first, then inbound.
6537c478bd9Sstevel@tonic-gate  */
6547c478bd9Sstevel@tonic-gate 
6557c478bd9Sstevel@tonic-gate int
sadb_dump(queue_t * pfkey_q,mblk_t * mp,keysock_in_t * ksi,sadb_t * sp)6569c2c14abSThejaswini Singarajipura sadb_dump(queue_t *pfkey_q, mblk_t *mp, keysock_in_t *ksi, sadb_t *sp)
6577c478bd9Sstevel@tonic-gate {
6587c478bd9Sstevel@tonic-gate 	int error;
6599c2c14abSThejaswini Singarajipura 	time_t	active_time = 0;
6609c2c14abSThejaswini Singarajipura 	sadb_x_edump_t	*edump =
6619c2c14abSThejaswini Singarajipura 	    (sadb_x_edump_t *)ksi->ks_in_extv[SADB_X_EXT_EDUMP];
6629c2c14abSThejaswini Singarajipura 
6639c2c14abSThejaswini Singarajipura 	if (edump != NULL) {
6649c2c14abSThejaswini Singarajipura 		active_time = edump->sadb_x_edump_timeout;
6659c2c14abSThejaswini Singarajipura 	}
6667c478bd9Sstevel@tonic-gate 
6677c478bd9Sstevel@tonic-gate 	/* Dump outbound */
6689c2c14abSThejaswini Singarajipura 	error = sadb_dump_fanout(pfkey_q, mp, ksi->ks_in_serial, sp->sdb_of,
6699c2c14abSThejaswini Singarajipura 	    sp->sdb_hashsize, B_TRUE, active_time);
6707c478bd9Sstevel@tonic-gate 	if (error)
6717c478bd9Sstevel@tonic-gate 		return (error);
6727c478bd9Sstevel@tonic-gate 
6737c478bd9Sstevel@tonic-gate 	/* Dump inbound */
6749c2c14abSThejaswini Singarajipura 	return sadb_dump_fanout(pfkey_q, mp, ksi->ks_in_serial, sp->sdb_if,
6759c2c14abSThejaswini Singarajipura 	    sp->sdb_hashsize, B_FALSE, active_time);
6767c478bd9Sstevel@tonic-gate }
6777c478bd9Sstevel@tonic-gate 
6787c478bd9Sstevel@tonic-gate /*
6797c478bd9Sstevel@tonic-gate  * Generic sadb table walker.
6807c478bd9Sstevel@tonic-gate  *
6817c478bd9Sstevel@tonic-gate  * Call "walkfn" for each SA in each bucket in "table"; pass the
6827c478bd9Sstevel@tonic-gate  * bucket, the entry and "cookie" to the callback function.
6837c478bd9Sstevel@tonic-gate  * Take care to ensure that walkfn can delete the SA without screwing
6847c478bd9Sstevel@tonic-gate  * up our traverse.
6857c478bd9Sstevel@tonic-gate  *
6867c478bd9Sstevel@tonic-gate  * The bucket is locked for the duration of the callback, both so that the
6877c478bd9Sstevel@tonic-gate  * callback can just call sadb_unlinkassoc() when it wants to delete something,
6887c478bd9Sstevel@tonic-gate  * and so that no new entries are added while we're walking the list.
6897c478bd9Sstevel@tonic-gate  */
6907c478bd9Sstevel@tonic-gate static void
sadb_walker(isaf_t * table,uint_t numentries,void (* walkfn)(isaf_t * head,ipsa_t * entry,void * cookie),void * cookie)6917c478bd9Sstevel@tonic-gate sadb_walker(isaf_t *table, uint_t numentries,
6927c478bd9Sstevel@tonic-gate     void (*walkfn)(isaf_t *head, ipsa_t *entry, void *cookie),
6937c478bd9Sstevel@tonic-gate     void *cookie)
6947c478bd9Sstevel@tonic-gate {
6957c478bd9Sstevel@tonic-gate 	int i;
6967c478bd9Sstevel@tonic-gate 	for (i = 0; i < numentries; i++) {
6977c478bd9Sstevel@tonic-gate 		ipsa_t *entry, *next;
6987c478bd9Sstevel@tonic-gate 
6997c478bd9Sstevel@tonic-gate 		mutex_enter(&table[i].isaf_lock);
7007c478bd9Sstevel@tonic-gate 
7017c478bd9Sstevel@tonic-gate 		for (entry = table[i].isaf_ipsa; entry != NULL;
7027c478bd9Sstevel@tonic-gate 		    entry = next) {
7037c478bd9Sstevel@tonic-gate 			next = entry->ipsa_next;
7047c478bd9Sstevel@tonic-gate 			(*walkfn)(&table[i], entry, cookie);
7057c478bd9Sstevel@tonic-gate 		}
7067c478bd9Sstevel@tonic-gate 		mutex_exit(&table[i].isaf_lock);
7077c478bd9Sstevel@tonic-gate 	}
7087c478bd9Sstevel@tonic-gate }
7097c478bd9Sstevel@tonic-gate 
7107c478bd9Sstevel@tonic-gate /*
7117c478bd9Sstevel@tonic-gate  * Call me to free up a security association fanout.  Use the forever
7127c478bd9Sstevel@tonic-gate  * variable to indicate freeing up the SAs (forever == B_FALSE, e.g.
7137c478bd9Sstevel@tonic-gate  * an SADB_FLUSH message), or destroying everything (forever == B_TRUE,
7147c478bd9Sstevel@tonic-gate  * when a module is unloaded).
7157c478bd9Sstevel@tonic-gate  */
7167c478bd9Sstevel@tonic-gate static void
sadb_destroyer(isaf_t ** tablep,uint_t numentries,boolean_t forever,boolean_t inbound)7179c2c14abSThejaswini Singarajipura sadb_destroyer(isaf_t **tablep, uint_t numentries, boolean_t forever,
7189c2c14abSThejaswini Singarajipura     boolean_t inbound)
7197c478bd9Sstevel@tonic-gate {
7207c478bd9Sstevel@tonic-gate 	int i;
721fb87b5d2Ssommerfe 	isaf_t *table = *tablep;
7229c2c14abSThejaswini Singarajipura 	uint8_t protocol;
7238e4b770fSLu Huafeng 	ipsa_t *sa;
7248e4b770fSLu Huafeng 	netstackid_t sid;
725fb87b5d2Ssommerfe 
726fb87b5d2Ssommerfe 	if (table == NULL)
727fb87b5d2Ssommerfe 		return;
7287c478bd9Sstevel@tonic-gate 
7297c478bd9Sstevel@tonic-gate 	for (i = 0; i < numentries; i++) {
7307c478bd9Sstevel@tonic-gate 		mutex_enter(&table[i].isaf_lock);
7318e4b770fSLu Huafeng 		while ((sa = table[i].isaf_ipsa) != NULL) {
7329c2c14abSThejaswini Singarajipura 			if (inbound && cl_inet_deletespi &&
7338e4b770fSLu Huafeng 			    (sa->ipsa_state != IPSA_STATE_ACTIVE_ELSEWHERE) &&
7348e4b770fSLu Huafeng 			    (sa->ipsa_state != IPSA_STATE_IDLE)) {
7358e4b770fSLu Huafeng 				protocol = (sa->ipsa_type == SADB_SATYPE_AH) ?
7368e4b770fSLu Huafeng 				    IPPROTO_AH : IPPROTO_ESP;
7378e4b770fSLu Huafeng 				sid = sa->ipsa_netstack->netstack_stackid;
7388e4b770fSLu Huafeng 				cl_inet_deletespi(sid, protocol, sa->ipsa_spi,
7398e4b770fSLu Huafeng 				    NULL);
7409c2c14abSThejaswini Singarajipura 			}
7418e4b770fSLu Huafeng 			sadb_unlinkassoc(sa);
7429c2c14abSThejaswini Singarajipura 		}
7437c478bd9Sstevel@tonic-gate 		table[i].isaf_gen++;
7447c478bd9Sstevel@tonic-gate 		mutex_exit(&table[i].isaf_lock);
7457c478bd9Sstevel@tonic-gate 		if (forever)
7467c478bd9Sstevel@tonic-gate 			mutex_destroy(&(table[i].isaf_lock));
7477c478bd9Sstevel@tonic-gate 	}
7487c478bd9Sstevel@tonic-gate 
749fb87b5d2Ssommerfe 	if (forever) {
750fb87b5d2Ssommerfe 		*tablep = NULL;
7517c478bd9Sstevel@tonic-gate 		kmem_free(table, numentries * sizeof (*table));
7527c478bd9Sstevel@tonic-gate 	}
753fb87b5d2Ssommerfe }
7547c478bd9Sstevel@tonic-gate 
7557c478bd9Sstevel@tonic-gate /*
7567c478bd9Sstevel@tonic-gate  * Entry points to sadb_destroyer().
7577c478bd9Sstevel@tonic-gate  */
7587c478bd9Sstevel@tonic-gate static void
sadb_flush(sadb_t * sp,netstack_t * ns)759f4b3ec61Sdh155122 sadb_flush(sadb_t *sp, netstack_t *ns)
7607c478bd9Sstevel@tonic-gate {
7617c478bd9Sstevel@tonic-gate 	/*
7627c478bd9Sstevel@tonic-gate 	 * Flush out each bucket, one at a time.  Were it not for keysock's
7637c478bd9Sstevel@tonic-gate 	 * enforcement, there would be a subtlety where I could add on the
7647c478bd9Sstevel@tonic-gate 	 * heels of a flush.  With keysock's enforcement, however, this
7657c478bd9Sstevel@tonic-gate 	 * makes ESP's job easy.
7667c478bd9Sstevel@tonic-gate 	 */
7679c2c14abSThejaswini Singarajipura 	sadb_destroyer(&sp->sdb_of, sp->sdb_hashsize, B_FALSE, B_FALSE);
7689c2c14abSThejaswini Singarajipura 	sadb_destroyer(&sp->sdb_if, sp->sdb_hashsize, B_FALSE, B_TRUE);
7697c478bd9Sstevel@tonic-gate 
7707c478bd9Sstevel@tonic-gate 	/* For each acquire, destroy it; leave the bucket mutex alone. */
771f4b3ec61Sdh155122 	sadb_destroy_acqlist(&sp->sdb_acq, sp->sdb_hashsize, B_FALSE, ns);
7727c478bd9Sstevel@tonic-gate }
7737c478bd9Sstevel@tonic-gate 
7747c478bd9Sstevel@tonic-gate static void
sadb_destroy(sadb_t * sp,netstack_t * ns)775f4b3ec61Sdh155122 sadb_destroy(sadb_t *sp, netstack_t *ns)
7767c478bd9Sstevel@tonic-gate {
7779c2c14abSThejaswini Singarajipura 	sadb_destroyer(&sp->sdb_of, sp->sdb_hashsize, B_TRUE, B_FALSE);
7789c2c14abSThejaswini Singarajipura 	sadb_destroyer(&sp->sdb_if, sp->sdb_hashsize, B_TRUE, B_TRUE);
7797c478bd9Sstevel@tonic-gate 
7807c478bd9Sstevel@tonic-gate 	/* For each acquire, destroy it, including the bucket mutex. */
781f4b3ec61Sdh155122 	sadb_destroy_acqlist(&sp->sdb_acq, sp->sdb_hashsize, B_TRUE, ns);
782fb87b5d2Ssommerfe 
783fb87b5d2Ssommerfe 	ASSERT(sp->sdb_of == NULL);
784fb87b5d2Ssommerfe 	ASSERT(sp->sdb_if == NULL);
785fb87b5d2Ssommerfe 	ASSERT(sp->sdb_acq == NULL);
7867c478bd9Sstevel@tonic-gate }
7877c478bd9Sstevel@tonic-gate 
7887c478bd9Sstevel@tonic-gate void
sadbp_flush(sadbp_t * spp,netstack_t * ns)789f4b3ec61Sdh155122 sadbp_flush(sadbp_t *spp, netstack_t *ns)
7907c478bd9Sstevel@tonic-gate {
791f4b3ec61Sdh155122 	sadb_flush(&spp->s_v4, ns);
792f4b3ec61Sdh155122 	sadb_flush(&spp->s_v6, ns);
7937c478bd9Sstevel@tonic-gate }
7947c478bd9Sstevel@tonic-gate 
7957c478bd9Sstevel@tonic-gate void
sadbp_destroy(sadbp_t * spp,netstack_t * ns)796f4b3ec61Sdh155122 sadbp_destroy(sadbp_t *spp, netstack_t *ns)
7977c478bd9Sstevel@tonic-gate {
798f4b3ec61Sdh155122 	sadb_destroy(&spp->s_v4, ns);
799f4b3ec61Sdh155122 	sadb_destroy(&spp->s_v6, ns);
8007c478bd9Sstevel@tonic-gate 
801f4b3ec61Sdh155122 	if (spp->s_satype == SADB_SATYPE_AH) {
802f4b3ec61Sdh155122 		ipsec_stack_t	*ipss = ns->netstack_ipsec;
803f4b3ec61Sdh155122 
804f4b3ec61Sdh155122 		ip_drop_unregister(&ipss->ipsec_sadb_dropper);
805f4b3ec61Sdh155122 	}
8067c478bd9Sstevel@tonic-gate }
8077c478bd9Sstevel@tonic-gate 
8087c478bd9Sstevel@tonic-gate 
8097c478bd9Sstevel@tonic-gate /*
8107c478bd9Sstevel@tonic-gate  * Check hard vs. soft lifetimes.  If there's a reality mismatch (e.g.
8117c478bd9Sstevel@tonic-gate  * soft lifetimes > hard lifetimes) return an appropriate diagnostic for
8127c478bd9Sstevel@tonic-gate  * EINVAL.
8137c478bd9Sstevel@tonic-gate  */
8147c478bd9Sstevel@tonic-gate int
sadb_hardsoftchk(sadb_lifetime_t * hard,sadb_lifetime_t * soft,sadb_lifetime_t * idle)8159c2c14abSThejaswini Singarajipura sadb_hardsoftchk(sadb_lifetime_t *hard, sadb_lifetime_t *soft,
8169c2c14abSThejaswini Singarajipura     sadb_lifetime_t *idle)
8177c478bd9Sstevel@tonic-gate {
8187c478bd9Sstevel@tonic-gate 	if (hard == NULL || soft == NULL)
8197c478bd9Sstevel@tonic-gate 		return (0);
8207c478bd9Sstevel@tonic-gate 
8217c478bd9Sstevel@tonic-gate 	if (hard->sadb_lifetime_allocations != 0 &&
8227c478bd9Sstevel@tonic-gate 	    soft->sadb_lifetime_allocations != 0 &&
8237c478bd9Sstevel@tonic-gate 	    hard->sadb_lifetime_allocations < soft->sadb_lifetime_allocations)
8247c478bd9Sstevel@tonic-gate 		return (SADB_X_DIAGNOSTIC_ALLOC_HSERR);
8257c478bd9Sstevel@tonic-gate 
8267c478bd9Sstevel@tonic-gate 	if (hard->sadb_lifetime_bytes != 0 &&
8277c478bd9Sstevel@tonic-gate 	    soft->sadb_lifetime_bytes != 0 &&
8287c478bd9Sstevel@tonic-gate 	    hard->sadb_lifetime_bytes < soft->sadb_lifetime_bytes)
8297c478bd9Sstevel@tonic-gate 		return (SADB_X_DIAGNOSTIC_BYTES_HSERR);
8307c478bd9Sstevel@tonic-gate 
8317c478bd9Sstevel@tonic-gate 	if (hard->sadb_lifetime_addtime != 0 &&
8327c478bd9Sstevel@tonic-gate 	    soft->sadb_lifetime_addtime != 0 &&
8337c478bd9Sstevel@tonic-gate 	    hard->sadb_lifetime_addtime < soft->sadb_lifetime_addtime)
8347c478bd9Sstevel@tonic-gate 		return (SADB_X_DIAGNOSTIC_ADDTIME_HSERR);
8357c478bd9Sstevel@tonic-gate 
8367c478bd9Sstevel@tonic-gate 	if (hard->sadb_lifetime_usetime != 0 &&
8377c478bd9Sstevel@tonic-gate 	    soft->sadb_lifetime_usetime != 0 &&
8387c478bd9Sstevel@tonic-gate 	    hard->sadb_lifetime_usetime < soft->sadb_lifetime_usetime)
8397c478bd9Sstevel@tonic-gate 		return (SADB_X_DIAGNOSTIC_USETIME_HSERR);
8407c478bd9Sstevel@tonic-gate 
8419c2c14abSThejaswini Singarajipura 	if (idle != NULL) {
8429c2c14abSThejaswini Singarajipura 		if (hard->sadb_lifetime_addtime != 0 &&
8439c2c14abSThejaswini Singarajipura 		    idle->sadb_lifetime_addtime != 0 &&
8449c2c14abSThejaswini Singarajipura 		    hard->sadb_lifetime_addtime < idle->sadb_lifetime_addtime)
8459c2c14abSThejaswini Singarajipura 			return (SADB_X_DIAGNOSTIC_ADDTIME_HSERR);
8469c2c14abSThejaswini Singarajipura 
8479c2c14abSThejaswini Singarajipura 		if (soft->sadb_lifetime_addtime != 0 &&
8489c2c14abSThejaswini Singarajipura 		    idle->sadb_lifetime_addtime != 0 &&
8499c2c14abSThejaswini Singarajipura 		    soft->sadb_lifetime_addtime < idle->sadb_lifetime_addtime)
8509c2c14abSThejaswini Singarajipura 			return (SADB_X_DIAGNOSTIC_ADDTIME_HSERR);
8519c2c14abSThejaswini Singarajipura 
8529c2c14abSThejaswini Singarajipura 		if (hard->sadb_lifetime_usetime != 0 &&
8539c2c14abSThejaswini Singarajipura 		    idle->sadb_lifetime_usetime != 0 &&
8549c2c14abSThejaswini Singarajipura 		    hard->sadb_lifetime_usetime < idle->sadb_lifetime_usetime)
8559c2c14abSThejaswini Singarajipura 			return (SADB_X_DIAGNOSTIC_USETIME_HSERR);
8569c2c14abSThejaswini Singarajipura 
8579c2c14abSThejaswini Singarajipura 		if (soft->sadb_lifetime_usetime != 0 &&
8589c2c14abSThejaswini Singarajipura 		    idle->sadb_lifetime_usetime != 0 &&
8599c2c14abSThejaswini Singarajipura 		    soft->sadb_lifetime_usetime < idle->sadb_lifetime_usetime)
8609c2c14abSThejaswini Singarajipura 			return (SADB_X_DIAGNOSTIC_USETIME_HSERR);
8619c2c14abSThejaswini Singarajipura 	}
8629c2c14abSThejaswini Singarajipura 
8637c478bd9Sstevel@tonic-gate 	return (0);
8647c478bd9Sstevel@tonic-gate }
8657c478bd9Sstevel@tonic-gate 
8667c478bd9Sstevel@tonic-gate /*
8675d3b8cb7SBill Sommerfeld  * Sanity check sensitivity labels.
8685d3b8cb7SBill Sommerfeld  *
8695d3b8cb7SBill Sommerfeld  * For now, just reject labels on unlabeled systems.
8705d3b8cb7SBill Sommerfeld  */
8715d3b8cb7SBill Sommerfeld int
sadb_labelchk(keysock_in_t * ksi)8725d3b8cb7SBill Sommerfeld sadb_labelchk(keysock_in_t *ksi)
8735d3b8cb7SBill Sommerfeld {
8745d3b8cb7SBill Sommerfeld 	if (!is_system_labeled()) {
8755d3b8cb7SBill Sommerfeld 		if (ksi->ks_in_extv[SADB_EXT_SENSITIVITY] != NULL)
8765d3b8cb7SBill Sommerfeld 			return (SADB_X_DIAGNOSTIC_BAD_LABEL);
8775d3b8cb7SBill Sommerfeld 
8785d3b8cb7SBill Sommerfeld 		if (ksi->ks_in_extv[SADB_X_EXT_OUTER_SENS] != NULL)
8795d3b8cb7SBill Sommerfeld 			return (SADB_X_DIAGNOSTIC_BAD_LABEL);
8805d3b8cb7SBill Sommerfeld 	}
8815d3b8cb7SBill Sommerfeld 
8825d3b8cb7SBill Sommerfeld 	return (0);
8835d3b8cb7SBill Sommerfeld }
8845d3b8cb7SBill Sommerfeld 
8855d3b8cb7SBill Sommerfeld /*
8867c478bd9Sstevel@tonic-gate  * Clone a security association for the purposes of inserting a single SA
88738d95a78Smarkfen  * into inbound and outbound tables respectively. This function should only
88838d95a78Smarkfen  * be called from sadb_common_add().
8897c478bd9Sstevel@tonic-gate  */
8907c478bd9Sstevel@tonic-gate static ipsa_t *
sadb_cloneassoc(ipsa_t * ipsa)8917c478bd9Sstevel@tonic-gate sadb_cloneassoc(ipsa_t *ipsa)
8927c478bd9Sstevel@tonic-gate {
8937c478bd9Sstevel@tonic-gate 	ipsa_t *newbie;
8947c478bd9Sstevel@tonic-gate 	boolean_t error = B_FALSE;
8957c478bd9Sstevel@tonic-gate 
8960c0328cdSBill Sommerfeld 	ASSERT(MUTEX_NOT_HELD(&(ipsa->ipsa_lock)));
8977c478bd9Sstevel@tonic-gate 
8987c478bd9Sstevel@tonic-gate 	newbie = kmem_alloc(sizeof (ipsa_t), KM_NOSLEEP);
8997c478bd9Sstevel@tonic-gate 	if (newbie == NULL)
9007c478bd9Sstevel@tonic-gate 		return (NULL);
9017c478bd9Sstevel@tonic-gate 
9027c478bd9Sstevel@tonic-gate 	/* Copy over what we can. */
9037c478bd9Sstevel@tonic-gate 	*newbie = *ipsa;
9047c478bd9Sstevel@tonic-gate 
9057c478bd9Sstevel@tonic-gate 	/* bzero and initialize locks, in case *_init() allocates... */
9067c478bd9Sstevel@tonic-gate 	mutex_init(&newbie->ipsa_lock, NULL, MUTEX_DEFAULT, NULL);
9077c478bd9Sstevel@tonic-gate 
908bd670b35SErik Nordmark 	if (newbie->ipsa_tsl != NULL)
909bd670b35SErik Nordmark 		label_hold(newbie->ipsa_tsl);
9105d3b8cb7SBill Sommerfeld 
911bd670b35SErik Nordmark 	if (newbie->ipsa_otsl != NULL)
912bd670b35SErik Nordmark 		label_hold(newbie->ipsa_otsl);
9135d3b8cb7SBill Sommerfeld 
9147c478bd9Sstevel@tonic-gate 	/*
9157c478bd9Sstevel@tonic-gate 	 * While somewhat dain-bramaged, the most graceful way to
9167c478bd9Sstevel@tonic-gate 	 * recover from errors is to keep plowing through the
9177c478bd9Sstevel@tonic-gate 	 * allocations, and getting what I can.  It's easier to call
9187c478bd9Sstevel@tonic-gate 	 * sadb_freeassoc() on the stillborn clone when all the
9197c478bd9Sstevel@tonic-gate 	 * pointers aren't pointing to the parent's data.
9207c478bd9Sstevel@tonic-gate 	 */
9217c478bd9Sstevel@tonic-gate 
9227c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_authkey != NULL) {
9237c478bd9Sstevel@tonic-gate 		newbie->ipsa_authkey = kmem_alloc(newbie->ipsa_authkeylen,
9247c478bd9Sstevel@tonic-gate 		    KM_NOSLEEP);
9257c478bd9Sstevel@tonic-gate 		if (newbie->ipsa_authkey == NULL) {
9267c478bd9Sstevel@tonic-gate 			error = B_TRUE;
9277c478bd9Sstevel@tonic-gate 		} else {
9287c478bd9Sstevel@tonic-gate 			bcopy(ipsa->ipsa_authkey, newbie->ipsa_authkey,
9297c478bd9Sstevel@tonic-gate 			    newbie->ipsa_authkeylen);
9307c478bd9Sstevel@tonic-gate 
9317c478bd9Sstevel@tonic-gate 			newbie->ipsa_kcfauthkey.ck_data =
9327c478bd9Sstevel@tonic-gate 			    newbie->ipsa_authkey;
9337c478bd9Sstevel@tonic-gate 		}
9347c478bd9Sstevel@tonic-gate 
9357c478bd9Sstevel@tonic-gate 		if (newbie->ipsa_amech.cm_param != NULL) {
9367c478bd9Sstevel@tonic-gate 			newbie->ipsa_amech.cm_param =
9377c478bd9Sstevel@tonic-gate 			    (char *)&newbie->ipsa_mac_len;
9387c478bd9Sstevel@tonic-gate 		}
9397c478bd9Sstevel@tonic-gate 	}
9407c478bd9Sstevel@tonic-gate 
9417c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_encrkey != NULL) {
9427c478bd9Sstevel@tonic-gate 		newbie->ipsa_encrkey = kmem_alloc(newbie->ipsa_encrkeylen,
9437c478bd9Sstevel@tonic-gate 		    KM_NOSLEEP);
9447c478bd9Sstevel@tonic-gate 		if (newbie->ipsa_encrkey == NULL) {
9457c478bd9Sstevel@tonic-gate 			error = B_TRUE;
9467c478bd9Sstevel@tonic-gate 		} else {
9477c478bd9Sstevel@tonic-gate 			bcopy(ipsa->ipsa_encrkey, newbie->ipsa_encrkey,
9487c478bd9Sstevel@tonic-gate 			    newbie->ipsa_encrkeylen);
9497c478bd9Sstevel@tonic-gate 
9507c478bd9Sstevel@tonic-gate 			newbie->ipsa_kcfencrkey.ck_data =
9517c478bd9Sstevel@tonic-gate 			    newbie->ipsa_encrkey;
9527c478bd9Sstevel@tonic-gate 		}
9537c478bd9Sstevel@tonic-gate 	}
9547c478bd9Sstevel@tonic-gate 
9557c478bd9Sstevel@tonic-gate 	newbie->ipsa_authtmpl = NULL;
9567c478bd9Sstevel@tonic-gate 	newbie->ipsa_encrtmpl = NULL;
95738d95a78Smarkfen 	newbie->ipsa_haspeer = B_TRUE;
9587c478bd9Sstevel@tonic-gate 
9597c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_src_cid != NULL) {
9607c478bd9Sstevel@tonic-gate 		newbie->ipsa_src_cid = ipsa->ipsa_src_cid;
9617c478bd9Sstevel@tonic-gate 		IPSID_REFHOLD(ipsa->ipsa_src_cid);
9627c478bd9Sstevel@tonic-gate 	}
9637c478bd9Sstevel@tonic-gate 
9647c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_dst_cid != NULL) {
9657c478bd9Sstevel@tonic-gate 		newbie->ipsa_dst_cid = ipsa->ipsa_dst_cid;
9667c478bd9Sstevel@tonic-gate 		IPSID_REFHOLD(ipsa->ipsa_dst_cid);
9677c478bd9Sstevel@tonic-gate 	}
9687c478bd9Sstevel@tonic-gate 
9697c478bd9Sstevel@tonic-gate 	if (error) {
9707c478bd9Sstevel@tonic-gate 		sadb_freeassoc(newbie);
9717c478bd9Sstevel@tonic-gate 		return (NULL);
9727c478bd9Sstevel@tonic-gate 	}
9737c478bd9Sstevel@tonic-gate 
9747c478bd9Sstevel@tonic-gate 	return (newbie);
9757c478bd9Sstevel@tonic-gate }
9767c478bd9Sstevel@tonic-gate 
9777c478bd9Sstevel@tonic-gate /*
9787c478bd9Sstevel@tonic-gate  * Initialize a SADB address extension at the address specified by addrext.
9797c478bd9Sstevel@tonic-gate  * Return a pointer to the end of the new address extension.
9807c478bd9Sstevel@tonic-gate  */
9817c478bd9Sstevel@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)9827c478bd9Sstevel@tonic-gate sadb_make_addr_ext(uint8_t *start, uint8_t *end, uint16_t exttype,
9838810c16bSdanmcd     sa_family_t af, uint32_t *addr, uint16_t port, uint8_t proto, int prefix)
9847c478bd9Sstevel@tonic-gate {
9857c478bd9Sstevel@tonic-gate 	struct sockaddr_in *sin;
9867c478bd9Sstevel@tonic-gate 	struct sockaddr_in6 *sin6;
9877c478bd9Sstevel@tonic-gate 	uint8_t *cur = start;
9887c478bd9Sstevel@tonic-gate 	int addrext_len;
9897c478bd9Sstevel@tonic-gate 	int sin_len;
9907c478bd9Sstevel@tonic-gate 	sadb_address_t *addrext	= (sadb_address_t *)cur;
9917c478bd9Sstevel@tonic-gate 
9927c478bd9Sstevel@tonic-gate 	if (cur == NULL)
9937c478bd9Sstevel@tonic-gate 		return (NULL);
9947c478bd9Sstevel@tonic-gate 
9957c478bd9Sstevel@tonic-gate 	cur += sizeof (*addrext);
9967c478bd9Sstevel@tonic-gate 	if (cur > end)
9977c478bd9Sstevel@tonic-gate 		return (NULL);
9987c478bd9Sstevel@tonic-gate 
9997c478bd9Sstevel@tonic-gate 	addrext->sadb_address_proto = proto;
10008810c16bSdanmcd 	addrext->sadb_address_prefixlen = prefix;
10017c478bd9Sstevel@tonic-gate 	addrext->sadb_address_reserved = 0;
10027c478bd9Sstevel@tonic-gate 	addrext->sadb_address_exttype = exttype;
10037c478bd9Sstevel@tonic-gate 
10047c478bd9Sstevel@tonic-gate 	switch (af) {
10057c478bd9Sstevel@tonic-gate 	case AF_INET:
10067c478bd9Sstevel@tonic-gate 		sin = (struct sockaddr_in *)cur;
10077c478bd9Sstevel@tonic-gate 		sin_len = sizeof (*sin);
10087c478bd9Sstevel@tonic-gate 		cur += sin_len;
10097c478bd9Sstevel@tonic-gate 		if (cur > end)
10107c478bd9Sstevel@tonic-gate 			return (NULL);
10117c478bd9Sstevel@tonic-gate 
10127c478bd9Sstevel@tonic-gate 		sin->sin_family = af;
10137c478bd9Sstevel@tonic-gate 		bzero(sin->sin_zero, sizeof (sin->sin_zero));
10147c478bd9Sstevel@tonic-gate 		sin->sin_port = port;
10157c478bd9Sstevel@tonic-gate 		IPSA_COPY_ADDR(&sin->sin_addr, addr, af);
10167c478bd9Sstevel@tonic-gate 		break;
10177c478bd9Sstevel@tonic-gate 	case AF_INET6:
10187c478bd9Sstevel@tonic-gate 		sin6 = (struct sockaddr_in6 *)cur;
10197c478bd9Sstevel@tonic-gate 		sin_len = sizeof (*sin6);
10207c478bd9Sstevel@tonic-gate 		cur += sin_len;
10217c478bd9Sstevel@tonic-gate 		if (cur > end)
10227c478bd9Sstevel@tonic-gate 			return (NULL);
10237c478bd9Sstevel@tonic-gate 
10247c478bd9Sstevel@tonic-gate 		bzero(sin6, sizeof (*sin6));
10257c478bd9Sstevel@tonic-gate 		sin6->sin6_family = af;
10267c478bd9Sstevel@tonic-gate 		sin6->sin6_port = port;
10277c478bd9Sstevel@tonic-gate 		IPSA_COPY_ADDR(&sin6->sin6_addr, addr, af);
10287c478bd9Sstevel@tonic-gate 		break;
10297c478bd9Sstevel@tonic-gate 	}
10307c478bd9Sstevel@tonic-gate 
10317c478bd9Sstevel@tonic-gate 	addrext_len = roundup(cur - start, sizeof (uint64_t));
10327c478bd9Sstevel@tonic-gate 	addrext->sadb_address_len = SADB_8TO64(addrext_len);
10337c478bd9Sstevel@tonic-gate 
10347c478bd9Sstevel@tonic-gate 	cur = start + addrext_len;
10357c478bd9Sstevel@tonic-gate 	if (cur > end)
10367c478bd9Sstevel@tonic-gate 		cur = NULL;
10377c478bd9Sstevel@tonic-gate 
10387c478bd9Sstevel@tonic-gate 	return (cur);
10397c478bd9Sstevel@tonic-gate }
10407c478bd9Sstevel@tonic-gate 
10417c478bd9Sstevel@tonic-gate /*
10427c478bd9Sstevel@tonic-gate  * Construct a key management cookie extension.
10437c478bd9Sstevel@tonic-gate  */
10447c478bd9Sstevel@tonic-gate 
10457c478bd9Sstevel@tonic-gate static uint8_t *
sadb_make_kmc_ext(uint8_t * cur,uint8_t * end,uint32_t kmp,uint32_t kmc)10467c478bd9Sstevel@tonic-gate sadb_make_kmc_ext(uint8_t *cur, uint8_t *end, uint32_t kmp, uint32_t kmc)
10477c478bd9Sstevel@tonic-gate {
10487c478bd9Sstevel@tonic-gate 	sadb_x_kmc_t *kmcext = (sadb_x_kmc_t *)cur;
10497c478bd9Sstevel@tonic-gate 
10507c478bd9Sstevel@tonic-gate 	if (cur == NULL)
10517c478bd9Sstevel@tonic-gate 		return (NULL);
10527c478bd9Sstevel@tonic-gate 
10537c478bd9Sstevel@tonic-gate 	cur += sizeof (*kmcext);
10547c478bd9Sstevel@tonic-gate 
10557c478bd9Sstevel@tonic-gate 	if (cur > end)
10567c478bd9Sstevel@tonic-gate 		return (NULL);
10577c478bd9Sstevel@tonic-gate 
10587c478bd9Sstevel@tonic-gate 	kmcext->sadb_x_kmc_len = SADB_8TO64(sizeof (*kmcext));
10597c478bd9Sstevel@tonic-gate 	kmcext->sadb_x_kmc_exttype = SADB_X_EXT_KM_COOKIE;
10607c478bd9Sstevel@tonic-gate 	kmcext->sadb_x_kmc_proto = kmp;
10617c478bd9Sstevel@tonic-gate 	kmcext->sadb_x_kmc_cookie = kmc;
10627c478bd9Sstevel@tonic-gate 	kmcext->sadb_x_kmc_reserved = 0;
10637c478bd9Sstevel@tonic-gate 
10647c478bd9Sstevel@tonic-gate 	return (cur);
10657c478bd9Sstevel@tonic-gate }
10667c478bd9Sstevel@tonic-gate 
10677c478bd9Sstevel@tonic-gate /*
10687c478bd9Sstevel@tonic-gate  * Given an original message header with sufficient space following it, and an
10697c478bd9Sstevel@tonic-gate  * SA, construct a full PF_KEY message with all of the relevant extensions.
10707c478bd9Sstevel@tonic-gate  * This is mostly used for SADB_GET, and SADB_DUMP.
10717c478bd9Sstevel@tonic-gate  */
10728810c16bSdanmcd static mblk_t *
sadb_sa2msg(ipsa_t * ipsa,sadb_msg_t * samsg)10737c478bd9Sstevel@tonic-gate sadb_sa2msg(ipsa_t *ipsa, sadb_msg_t *samsg)
10747c478bd9Sstevel@tonic-gate {
10757c478bd9Sstevel@tonic-gate 	int alloclen, addrsize, paddrsize, authsize, encrsize;
10765d3b8cb7SBill Sommerfeld 	int srcidsize, dstidsize, senslen, osenslen;
10777c478bd9Sstevel@tonic-gate 	sa_family_t fam, pfam;	/* Address family for SADB_EXT_ADDRESS */
10787c478bd9Sstevel@tonic-gate 				/* src/dst and proxy sockaddrs. */
10797c478bd9Sstevel@tonic-gate 	/*
10807c478bd9Sstevel@tonic-gate 	 * The following are pointers into the PF_KEY message this PF_KEY
10817c478bd9Sstevel@tonic-gate 	 * message creates.
10827c478bd9Sstevel@tonic-gate 	 */
10837c478bd9Sstevel@tonic-gate 	sadb_msg_t *newsamsg;
10847c478bd9Sstevel@tonic-gate 	sadb_sa_t *assoc;
10857c478bd9Sstevel@tonic-gate 	sadb_lifetime_t *lt;
10867c478bd9Sstevel@tonic-gate 	sadb_key_t *key;
10877c478bd9Sstevel@tonic-gate 	sadb_ident_t *ident;
10887c478bd9Sstevel@tonic-gate 	sadb_sens_t *sens;
10897c478bd9Sstevel@tonic-gate 	sadb_ext_t *walker;	/* For when we need a generic ext. pointer. */
10909c2c14abSThejaswini Singarajipura 	sadb_x_replay_ctr_t *repl_ctr;
109138d95a78Smarkfen 	sadb_x_pair_t *pair_ext;
109238d95a78Smarkfen 
10937c478bd9Sstevel@tonic-gate 	mblk_t *mp;
10947c478bd9Sstevel@tonic-gate 	uint8_t *cur, *end;
10957c478bd9Sstevel@tonic-gate 	/* These indicate the presence of the above extension fields. */
10965d3b8cb7SBill Sommerfeld 	boolean_t soft = B_FALSE, hard = B_FALSE;
10975d3b8cb7SBill Sommerfeld 	boolean_t isrc = B_FALSE, idst = B_FALSE;
10985d3b8cb7SBill Sommerfeld 	boolean_t auth = B_FALSE, encr = B_FALSE;
10995d3b8cb7SBill Sommerfeld 	boolean_t sensinteg = B_FALSE, osensinteg = B_FALSE;
11005d3b8cb7SBill Sommerfeld 	boolean_t srcid = B_FALSE, dstid = B_FALSE;
11019c2c14abSThejaswini Singarajipura 	boolean_t idle;
110238d95a78Smarkfen 	boolean_t paired;
110338d95a78Smarkfen 	uint32_t otherspi;
11047c478bd9Sstevel@tonic-gate 
11057c478bd9Sstevel@tonic-gate 	/* First off, figure out the allocation length for this message. */
11067c478bd9Sstevel@tonic-gate 	/*
11077c478bd9Sstevel@tonic-gate 	 * Constant stuff.  This includes base, SA, address (src, dst),
11087c478bd9Sstevel@tonic-gate 	 * and lifetime (current).
11097c478bd9Sstevel@tonic-gate 	 */
11107c478bd9Sstevel@tonic-gate 	alloclen = sizeof (sadb_msg_t) + sizeof (sadb_sa_t) +
11117c478bd9Sstevel@tonic-gate 	    sizeof (sadb_lifetime_t);
11127c478bd9Sstevel@tonic-gate 
11137c478bd9Sstevel@tonic-gate 	fam = ipsa->ipsa_addrfam;
11147c478bd9Sstevel@tonic-gate 	switch (fam) {
11157c478bd9Sstevel@tonic-gate 	case AF_INET:
11167c478bd9Sstevel@tonic-gate 		addrsize = roundup(sizeof (struct sockaddr_in) +
11177c478bd9Sstevel@tonic-gate 		    sizeof (sadb_address_t), sizeof (uint64_t));
11187c478bd9Sstevel@tonic-gate 		break;
11197c478bd9Sstevel@tonic-gate 	case AF_INET6:
11207c478bd9Sstevel@tonic-gate 		addrsize = roundup(sizeof (struct sockaddr_in6) +
11217c478bd9Sstevel@tonic-gate 		    sizeof (sadb_address_t), sizeof (uint64_t));
11227c478bd9Sstevel@tonic-gate 		break;
11237c478bd9Sstevel@tonic-gate 	default:
11247c478bd9Sstevel@tonic-gate 		return (NULL);
11257c478bd9Sstevel@tonic-gate 	}
11267c478bd9Sstevel@tonic-gate 	/*
11277c478bd9Sstevel@tonic-gate 	 * Allocate TWO address extensions, for source and destination.
11287c478bd9Sstevel@tonic-gate 	 * (Thus, the * 2.)
11297c478bd9Sstevel@tonic-gate 	 */
11307c478bd9Sstevel@tonic-gate 	alloclen += addrsize * 2;
11317c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_flags & IPSA_F_NATT_REM)
11327c478bd9Sstevel@tonic-gate 		alloclen += addrsize;
11337c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_flags & IPSA_F_NATT_LOC)
11347c478bd9Sstevel@tonic-gate 		alloclen += addrsize;
11357c478bd9Sstevel@tonic-gate 
113638d95a78Smarkfen 	if (ipsa->ipsa_flags & IPSA_F_PAIRED) {
113738d95a78Smarkfen 		paired = B_TRUE;
113838d95a78Smarkfen 		alloclen += sizeof (sadb_x_pair_t);
113938d95a78Smarkfen 		otherspi = ipsa->ipsa_otherspi;
114038d95a78Smarkfen 	} else {
114138d95a78Smarkfen 		paired = B_FALSE;
114238d95a78Smarkfen 	}
11437c478bd9Sstevel@tonic-gate 
11447c478bd9Sstevel@tonic-gate 	/* How 'bout other lifetimes? */
11457c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_softaddlt != 0 || ipsa->ipsa_softuselt != 0 ||
11467c478bd9Sstevel@tonic-gate 	    ipsa->ipsa_softbyteslt != 0 || ipsa->ipsa_softalloc != 0) {
11477c478bd9Sstevel@tonic-gate 		alloclen += sizeof (sadb_lifetime_t);
11487c478bd9Sstevel@tonic-gate 		soft = B_TRUE;
11497c478bd9Sstevel@tonic-gate 	}
11507c478bd9Sstevel@tonic-gate 
11517c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_hardaddlt != 0 || ipsa->ipsa_harduselt != 0 ||
11527c478bd9Sstevel@tonic-gate 	    ipsa->ipsa_hardbyteslt != 0 || ipsa->ipsa_hardalloc != 0) {
11537c478bd9Sstevel@tonic-gate 		alloclen += sizeof (sadb_lifetime_t);
11547c478bd9Sstevel@tonic-gate 		hard = B_TRUE;
11557c478bd9Sstevel@tonic-gate 	}
11567c478bd9Sstevel@tonic-gate 
11579c2c14abSThejaswini Singarajipura 	if (ipsa->ipsa_idleaddlt != 0 || ipsa->ipsa_idleuselt != 0) {
11589c2c14abSThejaswini Singarajipura 		alloclen += sizeof (sadb_lifetime_t);
11599c2c14abSThejaswini Singarajipura 		idle = B_TRUE;
11609c2c14abSThejaswini Singarajipura 	} else {
11619c2c14abSThejaswini Singarajipura 		idle = B_FALSE;
11629c2c14abSThejaswini Singarajipura 	}
11639c2c14abSThejaswini Singarajipura 
11648810c16bSdanmcd 	/* Inner addresses. */
11655d3b8cb7SBill Sommerfeld 	if (ipsa->ipsa_innerfam != 0) {
11668810c16bSdanmcd 		pfam = ipsa->ipsa_innerfam;
11677c478bd9Sstevel@tonic-gate 		switch (pfam) {
11687c478bd9Sstevel@tonic-gate 		case AF_INET6:
11697c478bd9Sstevel@tonic-gate 			paddrsize = roundup(sizeof (struct sockaddr_in6) +
11707c478bd9Sstevel@tonic-gate 			    sizeof (sadb_address_t), sizeof (uint64_t));
11717c478bd9Sstevel@tonic-gate 			break;
11727c478bd9Sstevel@tonic-gate 		case AF_INET:
11737c478bd9Sstevel@tonic-gate 			paddrsize = roundup(sizeof (struct sockaddr_in) +
11747c478bd9Sstevel@tonic-gate 			    sizeof (sadb_address_t), sizeof (uint64_t));
11757c478bd9Sstevel@tonic-gate 			break;
11767c478bd9Sstevel@tonic-gate 		default:
11777c478bd9Sstevel@tonic-gate 			cmn_err(CE_PANIC,
11787c478bd9Sstevel@tonic-gate 			    "IPsec SADB: Proxy length failure.\n");
11797c478bd9Sstevel@tonic-gate 			break;
11807c478bd9Sstevel@tonic-gate 		}
11818810c16bSdanmcd 		isrc = B_TRUE;
11828810c16bSdanmcd 		idst = B_TRUE;
11838810c16bSdanmcd 		alloclen += 2 * paddrsize;
11847c478bd9Sstevel@tonic-gate 	}
11857c478bd9Sstevel@tonic-gate 
11867c478bd9Sstevel@tonic-gate 	/* For the following fields, assume that length != 0 ==> stuff */
11877c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_authkeylen != 0) {
11887c478bd9Sstevel@tonic-gate 		authsize = roundup(sizeof (sadb_key_t) + ipsa->ipsa_authkeylen,
11897c478bd9Sstevel@tonic-gate 		    sizeof (uint64_t));
11907c478bd9Sstevel@tonic-gate 		alloclen += authsize;
11917c478bd9Sstevel@tonic-gate 		auth = B_TRUE;
11927c478bd9Sstevel@tonic-gate 	}
11937c478bd9Sstevel@tonic-gate 
11947c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_encrkeylen != 0) {
1195628b0c67SMark Fenwick 		encrsize = roundup(sizeof (sadb_key_t) + ipsa->ipsa_encrkeylen +
1196628b0c67SMark Fenwick 		    ipsa->ipsa_nonce_len, sizeof (uint64_t));
11977c478bd9Sstevel@tonic-gate 		alloclen += encrsize;
11987c478bd9Sstevel@tonic-gate 		encr = B_TRUE;
11997c478bd9Sstevel@tonic-gate 	} else {
12007c478bd9Sstevel@tonic-gate 		encr = B_FALSE;
12017c478bd9Sstevel@tonic-gate 	}
12027c478bd9Sstevel@tonic-gate 
1203bd670b35SErik Nordmark 	if (ipsa->ipsa_tsl != NULL) {
1204bd670b35SErik Nordmark 		senslen = sadb_sens_len_from_label(ipsa->ipsa_tsl);
12055d3b8cb7SBill Sommerfeld 		alloclen += senslen;
12067c478bd9Sstevel@tonic-gate 		sensinteg = B_TRUE;
12075d3b8cb7SBill Sommerfeld 	}
12085d3b8cb7SBill Sommerfeld 
1209bd670b35SErik Nordmark 	if (ipsa->ipsa_otsl != NULL) {
1210bd670b35SErik Nordmark 		osenslen = sadb_sens_len_from_label(ipsa->ipsa_otsl);
12115d3b8cb7SBill Sommerfeld 		alloclen += osenslen;
12125d3b8cb7SBill Sommerfeld 		osensinteg = B_TRUE;
12137c478bd9Sstevel@tonic-gate 	}
12147c478bd9Sstevel@tonic-gate 
12157c478bd9Sstevel@tonic-gate 	/*
12167c478bd9Sstevel@tonic-gate 	 * Must use strlen() here for lengths.	Identities use NULL
12177c478bd9Sstevel@tonic-gate 	 * pointers to indicate their nonexistence.
12187c478bd9Sstevel@tonic-gate 	 */
12197c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_src_cid != NULL) {
12207c478bd9Sstevel@tonic-gate 		srcidsize = roundup(sizeof (sadb_ident_t) +
12217c478bd9Sstevel@tonic-gate 		    strlen(ipsa->ipsa_src_cid->ipsid_cid) + 1,
12227c478bd9Sstevel@tonic-gate 		    sizeof (uint64_t));
12237c478bd9Sstevel@tonic-gate 		alloclen += srcidsize;
12247c478bd9Sstevel@tonic-gate 		srcid = B_TRUE;
12257c478bd9Sstevel@tonic-gate 	}
12267c478bd9Sstevel@tonic-gate 
12277c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_dst_cid != NULL) {
12287c478bd9Sstevel@tonic-gate 		dstidsize = roundup(sizeof (sadb_ident_t) +
12297c478bd9Sstevel@tonic-gate 		    strlen(ipsa->ipsa_dst_cid->ipsid_cid) + 1,
12307c478bd9Sstevel@tonic-gate 		    sizeof (uint64_t));
12317c478bd9Sstevel@tonic-gate 		alloclen += dstidsize;
12327c478bd9Sstevel@tonic-gate 		dstid = B_TRUE;
12337c478bd9Sstevel@tonic-gate 	}
12347c478bd9Sstevel@tonic-gate 
12357c478bd9Sstevel@tonic-gate 	if ((ipsa->ipsa_kmp != 0) || (ipsa->ipsa_kmc != 0))
12367c478bd9Sstevel@tonic-gate 		alloclen += sizeof (sadb_x_kmc_t);
12377c478bd9Sstevel@tonic-gate 
12389c2c14abSThejaswini Singarajipura 	if (ipsa->ipsa_replay != 0) {
12399c2c14abSThejaswini Singarajipura 		alloclen += sizeof (sadb_x_replay_ctr_t);
12409c2c14abSThejaswini Singarajipura 	}
12419c2c14abSThejaswini Singarajipura 
12427c478bd9Sstevel@tonic-gate 	/* Make sure the allocation length is a multiple of 8 bytes. */
12437c478bd9Sstevel@tonic-gate 	ASSERT((alloclen & 0x7) == 0);
12447c478bd9Sstevel@tonic-gate 
12457c478bd9Sstevel@tonic-gate 	/* XXX Possibly make it esballoc, with a bzero-ing free_ftn. */
12467c478bd9Sstevel@tonic-gate 	mp = allocb(alloclen, BPRI_HI);
12477c478bd9Sstevel@tonic-gate 	if (mp == NULL)
12487c478bd9Sstevel@tonic-gate 		return (NULL);
12495d3b8cb7SBill Sommerfeld 	bzero(mp->b_rptr, alloclen);
12507c478bd9Sstevel@tonic-gate 
12517c478bd9Sstevel@tonic-gate 	mp->b_wptr += alloclen;
12527c478bd9Sstevel@tonic-gate 	end = mp->b_wptr;
12537c478bd9Sstevel@tonic-gate 	newsamsg = (sadb_msg_t *)mp->b_rptr;
12547c478bd9Sstevel@tonic-gate 	*newsamsg = *samsg;
12557c478bd9Sstevel@tonic-gate 	newsamsg->sadb_msg_len = (uint16_t)SADB_8TO64(alloclen);
12567c478bd9Sstevel@tonic-gate 
12577c478bd9Sstevel@tonic-gate 	mutex_enter(&ipsa->ipsa_lock);	/* Since I'm grabbing SA fields... */
12587c478bd9Sstevel@tonic-gate 
12597c478bd9Sstevel@tonic-gate 	newsamsg->sadb_msg_satype = ipsa->ipsa_type;
12607c478bd9Sstevel@tonic-gate 
12617c478bd9Sstevel@tonic-gate 	assoc = (sadb_sa_t *)(newsamsg + 1);
12627c478bd9Sstevel@tonic-gate 	assoc->sadb_sa_len = SADB_8TO64(sizeof (*assoc));
12637c478bd9Sstevel@tonic-gate 	assoc->sadb_sa_exttype = SADB_EXT_SA;
12647c478bd9Sstevel@tonic-gate 	assoc->sadb_sa_spi = ipsa->ipsa_spi;
12657c478bd9Sstevel@tonic-gate 	assoc->sadb_sa_replay = ipsa->ipsa_replay_wsize;
12667c478bd9Sstevel@tonic-gate 	assoc->sadb_sa_state = ipsa->ipsa_state;
12677c478bd9Sstevel@tonic-gate 	assoc->sadb_sa_auth = ipsa->ipsa_auth_alg;
12687c478bd9Sstevel@tonic-gate 	assoc->sadb_sa_encrypt = ipsa->ipsa_encr_alg;
12697c478bd9Sstevel@tonic-gate 	assoc->sadb_sa_flags = ipsa->ipsa_flags;
12707c478bd9Sstevel@tonic-gate 
12717c478bd9Sstevel@tonic-gate 	lt = (sadb_lifetime_t *)(assoc + 1);
12727c478bd9Sstevel@tonic-gate 	lt->sadb_lifetime_len = SADB_8TO64(sizeof (*lt));
12737c478bd9Sstevel@tonic-gate 	lt->sadb_lifetime_exttype = SADB_EXT_LIFETIME_CURRENT;
1274437220cdSdanmcd 	/* We do not support the concept. */
1275437220cdSdanmcd 	lt->sadb_lifetime_allocations = 0;
12767c478bd9Sstevel@tonic-gate 	lt->sadb_lifetime_bytes = ipsa->ipsa_bytes;
12777c478bd9Sstevel@tonic-gate 	lt->sadb_lifetime_addtime = ipsa->ipsa_addtime;
12787c478bd9Sstevel@tonic-gate 	lt->sadb_lifetime_usetime = ipsa->ipsa_usetime;
12797c478bd9Sstevel@tonic-gate 
12807c478bd9Sstevel@tonic-gate 	if (hard) {
12817c478bd9Sstevel@tonic-gate 		lt++;
12827c478bd9Sstevel@tonic-gate 		lt->sadb_lifetime_len = SADB_8TO64(sizeof (*lt));
12837c478bd9Sstevel@tonic-gate 		lt->sadb_lifetime_exttype = SADB_EXT_LIFETIME_HARD;
12847c478bd9Sstevel@tonic-gate 		lt->sadb_lifetime_allocations = ipsa->ipsa_hardalloc;
12857c478bd9Sstevel@tonic-gate 		lt->sadb_lifetime_bytes = ipsa->ipsa_hardbyteslt;
12867c478bd9Sstevel@tonic-gate 		lt->sadb_lifetime_addtime = ipsa->ipsa_hardaddlt;
12877c478bd9Sstevel@tonic-gate 		lt->sadb_lifetime_usetime = ipsa->ipsa_harduselt;
12887c478bd9Sstevel@tonic-gate 	}
12897c478bd9Sstevel@tonic-gate 
12907c478bd9Sstevel@tonic-gate 	if (soft) {
12917c478bd9Sstevel@tonic-gate 		lt++;
12927c478bd9Sstevel@tonic-gate 		lt->sadb_lifetime_len = SADB_8TO64(sizeof (*lt));
12937c478bd9Sstevel@tonic-gate 		lt->sadb_lifetime_exttype = SADB_EXT_LIFETIME_SOFT;
12947c478bd9Sstevel@tonic-gate 		lt->sadb_lifetime_allocations = ipsa->ipsa_softalloc;
12957c478bd9Sstevel@tonic-gate 		lt->sadb_lifetime_bytes = ipsa->ipsa_softbyteslt;
12967c478bd9Sstevel@tonic-gate 		lt->sadb_lifetime_addtime = ipsa->ipsa_softaddlt;
12977c478bd9Sstevel@tonic-gate 		lt->sadb_lifetime_usetime = ipsa->ipsa_softuselt;
12987c478bd9Sstevel@tonic-gate 	}
12997c478bd9Sstevel@tonic-gate 
13009c2c14abSThejaswini Singarajipura 	if (idle) {
13019c2c14abSThejaswini Singarajipura 		lt++;
13029c2c14abSThejaswini Singarajipura 		lt->sadb_lifetime_len = SADB_8TO64(sizeof (*lt));
13039c2c14abSThejaswini Singarajipura 		lt->sadb_lifetime_exttype = SADB_X_EXT_LIFETIME_IDLE;
13049c2c14abSThejaswini Singarajipura 		lt->sadb_lifetime_addtime = ipsa->ipsa_idleaddlt;
13059c2c14abSThejaswini Singarajipura 		lt->sadb_lifetime_usetime = ipsa->ipsa_idleuselt;
13069c2c14abSThejaswini Singarajipura 	}
13079c2c14abSThejaswini Singarajipura 
13087c478bd9Sstevel@tonic-gate 	cur = (uint8_t *)(lt + 1);
13097c478bd9Sstevel@tonic-gate 
13108810c16bSdanmcd 	/* NOTE:  Don't fill in ports here if we are a tunnel-mode SA. */
13117c478bd9Sstevel@tonic-gate 	cur = sadb_make_addr_ext(cur, end, SADB_EXT_ADDRESS_SRC, fam,
13128810c16bSdanmcd 	    ipsa->ipsa_srcaddr, (!isrc && !idst) ? SA_SRCPORT(ipsa) : 0,
13138810c16bSdanmcd 	    SA_PROTO(ipsa), 0);
13147c478bd9Sstevel@tonic-gate 	if (cur == NULL) {
13157c478bd9Sstevel@tonic-gate 		freemsg(mp);
13167c478bd9Sstevel@tonic-gate 		mp = NULL;
13177c478bd9Sstevel@tonic-gate 		goto bail;
13187c478bd9Sstevel@tonic-gate 	}
13197c478bd9Sstevel@tonic-gate 
13207c478bd9Sstevel@tonic-gate 	cur = sadb_make_addr_ext(cur, end, SADB_EXT_ADDRESS_DST, fam,
13218810c16bSdanmcd 	    ipsa->ipsa_dstaddr, (!isrc && !idst) ? SA_DSTPORT(ipsa) : 0,
13228810c16bSdanmcd 	    SA_PROTO(ipsa), 0);
13237c478bd9Sstevel@tonic-gate 	if (cur == NULL) {
13247c478bd9Sstevel@tonic-gate 		freemsg(mp);
13257c478bd9Sstevel@tonic-gate 		mp = NULL;
13267c478bd9Sstevel@tonic-gate 		goto bail;
13277c478bd9Sstevel@tonic-gate 	}
13287c478bd9Sstevel@tonic-gate 
13297c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_flags & IPSA_F_NATT_LOC) {
13307c478bd9Sstevel@tonic-gate 		cur = sadb_make_addr_ext(cur, end, SADB_X_EXT_ADDRESS_NATT_LOC,
1331437220cdSdanmcd 		    fam, &ipsa->ipsa_natt_addr_loc, ipsa->ipsa_local_nat_port,
1332437220cdSdanmcd 		    IPPROTO_UDP, 0);
13337c478bd9Sstevel@tonic-gate 		if (cur == NULL) {
13347c478bd9Sstevel@tonic-gate 			freemsg(mp);
13357c478bd9Sstevel@tonic-gate 			mp = NULL;
13367c478bd9Sstevel@tonic-gate 			goto bail;
13377c478bd9Sstevel@tonic-gate 		}
13387c478bd9Sstevel@tonic-gate 	}
13397c478bd9Sstevel@tonic-gate 
13407c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_flags & IPSA_F_NATT_REM) {
13417c478bd9Sstevel@tonic-gate 		cur = sadb_make_addr_ext(cur, end, SADB_X_EXT_ADDRESS_NATT_REM,
1342437220cdSdanmcd 		    fam, &ipsa->ipsa_natt_addr_rem, ipsa->ipsa_remote_nat_port,
13438810c16bSdanmcd 		    IPPROTO_UDP, 0);
13447c478bd9Sstevel@tonic-gate 		if (cur == NULL) {
13457c478bd9Sstevel@tonic-gate 			freemsg(mp);
13467c478bd9Sstevel@tonic-gate 			mp = NULL;
13477c478bd9Sstevel@tonic-gate 			goto bail;
13487c478bd9Sstevel@tonic-gate 		}
13497c478bd9Sstevel@tonic-gate 	}
13507c478bd9Sstevel@tonic-gate 
13518810c16bSdanmcd 	/* If we are a tunnel-mode SA, fill in the inner-selectors. */
13528810c16bSdanmcd 	if (isrc) {
13538810c16bSdanmcd 		cur = sadb_make_addr_ext(cur, end, SADB_X_EXT_ADDRESS_INNER_SRC,
13548810c16bSdanmcd 		    pfam, ipsa->ipsa_innersrc, SA_SRCPORT(ipsa),
13558810c16bSdanmcd 		    SA_IPROTO(ipsa), ipsa->ipsa_innersrcpfx);
13568810c16bSdanmcd 		if (cur == NULL) {
13578810c16bSdanmcd 			freemsg(mp);
13588810c16bSdanmcd 			mp = NULL;
13598810c16bSdanmcd 			goto bail;
13608810c16bSdanmcd 		}
13618810c16bSdanmcd 	}
13628810c16bSdanmcd 
13638810c16bSdanmcd 	if (idst) {
13648810c16bSdanmcd 		cur = sadb_make_addr_ext(cur, end, SADB_X_EXT_ADDRESS_INNER_DST,
13658810c16bSdanmcd 		    pfam, ipsa->ipsa_innerdst, SA_DSTPORT(ipsa),
13668810c16bSdanmcd 		    SA_IPROTO(ipsa), ipsa->ipsa_innerdstpfx);
13677c478bd9Sstevel@tonic-gate 		if (cur == NULL) {
13687c478bd9Sstevel@tonic-gate 			freemsg(mp);
13697c478bd9Sstevel@tonic-gate 			mp = NULL;
13707c478bd9Sstevel@tonic-gate 			goto bail;
13717c478bd9Sstevel@tonic-gate 		}
13727c478bd9Sstevel@tonic-gate 	}
13737c478bd9Sstevel@tonic-gate 
13747c478bd9Sstevel@tonic-gate 	if ((ipsa->ipsa_kmp != 0) || (ipsa->ipsa_kmc != 0)) {
13757c478bd9Sstevel@tonic-gate 		cur = sadb_make_kmc_ext(cur, end,
13767c478bd9Sstevel@tonic-gate 		    ipsa->ipsa_kmp, ipsa->ipsa_kmc);
13777c478bd9Sstevel@tonic-gate 		if (cur == NULL) {
13787c478bd9Sstevel@tonic-gate 			freemsg(mp);
13797c478bd9Sstevel@tonic-gate 			mp = NULL;
13807c478bd9Sstevel@tonic-gate 			goto bail;
13817c478bd9Sstevel@tonic-gate 		}
13827c478bd9Sstevel@tonic-gate 	}
13837c478bd9Sstevel@tonic-gate 
13847c478bd9Sstevel@tonic-gate 	walker = (sadb_ext_t *)cur;
13857c478bd9Sstevel@tonic-gate 	if (auth) {
13867c478bd9Sstevel@tonic-gate 		key = (sadb_key_t *)walker;
13877c478bd9Sstevel@tonic-gate 		key->sadb_key_len = SADB_8TO64(authsize);
13887c478bd9Sstevel@tonic-gate 		key->sadb_key_exttype = SADB_EXT_KEY_AUTH;
13897c478bd9Sstevel@tonic-gate 		key->sadb_key_bits = ipsa->ipsa_authkeybits;
13907c478bd9Sstevel@tonic-gate 		key->sadb_key_reserved = 0;
13917c478bd9Sstevel@tonic-gate 		bcopy(ipsa->ipsa_authkey, key + 1, ipsa->ipsa_authkeylen);
13927c478bd9Sstevel@tonic-gate 		walker = (sadb_ext_t *)((uint64_t *)walker +
13937c478bd9Sstevel@tonic-gate 		    walker->sadb_ext_len);
13947c478bd9Sstevel@tonic-gate 	}
13957c478bd9Sstevel@tonic-gate 
13967c478bd9Sstevel@tonic-gate 	if (encr) {
1397628b0c67SMark Fenwick 		uint8_t *buf_ptr;
13987c478bd9Sstevel@tonic-gate 		key = (sadb_key_t *)walker;
13997c478bd9Sstevel@tonic-gate 		key->sadb_key_len = SADB_8TO64(encrsize);
14007c478bd9Sstevel@tonic-gate 		key->sadb_key_exttype = SADB_EXT_KEY_ENCRYPT;
14017c478bd9Sstevel@tonic-gate 		key->sadb_key_bits = ipsa->ipsa_encrkeybits;
1402628b0c67SMark Fenwick 		key->sadb_key_reserved = ipsa->ipsa_saltbits;
1403628b0c67SMark Fenwick 		buf_ptr = (uint8_t *)(key + 1);
1404628b0c67SMark Fenwick 		bcopy(ipsa->ipsa_encrkey, buf_ptr, ipsa->ipsa_encrkeylen);
1405628b0c67SMark Fenwick 		if (ipsa->ipsa_salt != NULL) {
1406628b0c67SMark Fenwick 			buf_ptr += ipsa->ipsa_encrkeylen;
1407628b0c67SMark Fenwick 			bcopy(ipsa->ipsa_salt, buf_ptr, ipsa->ipsa_saltlen);
1408628b0c67SMark Fenwick 		}
14097c478bd9Sstevel@tonic-gate 		walker = (sadb_ext_t *)((uint64_t *)walker +
14107c478bd9Sstevel@tonic-gate 		    walker->sadb_ext_len);
14117c478bd9Sstevel@tonic-gate 	}
14127c478bd9Sstevel@tonic-gate 
14137c478bd9Sstevel@tonic-gate 	if (srcid) {
14147c478bd9Sstevel@tonic-gate 		ident = (sadb_ident_t *)walker;
14157c478bd9Sstevel@tonic-gate 		ident->sadb_ident_len = SADB_8TO64(srcidsize);
14167c478bd9Sstevel@tonic-gate 		ident->sadb_ident_exttype = SADB_EXT_IDENTITY_SRC;
14177c478bd9Sstevel@tonic-gate 		ident->sadb_ident_type = ipsa->ipsa_src_cid->ipsid_type;
14187c478bd9Sstevel@tonic-gate 		ident->sadb_ident_id = 0;
14197c478bd9Sstevel@tonic-gate 		ident->sadb_ident_reserved = 0;
14207c478bd9Sstevel@tonic-gate 		(void) strcpy((char *)(ident + 1),
14217c478bd9Sstevel@tonic-gate 		    ipsa->ipsa_src_cid->ipsid_cid);
14227c478bd9Sstevel@tonic-gate 		walker = (sadb_ext_t *)((uint64_t *)walker +
14237c478bd9Sstevel@tonic-gate 		    walker->sadb_ext_len);
14247c478bd9Sstevel@tonic-gate 	}
14257c478bd9Sstevel@tonic-gate 
14267c478bd9Sstevel@tonic-gate 	if (dstid) {
14277c478bd9Sstevel@tonic-gate 		ident = (sadb_ident_t *)walker;
14287c478bd9Sstevel@tonic-gate 		ident->sadb_ident_len = SADB_8TO64(dstidsize);
14297c478bd9Sstevel@tonic-gate 		ident->sadb_ident_exttype = SADB_EXT_IDENTITY_DST;
14307c478bd9Sstevel@tonic-gate 		ident->sadb_ident_type = ipsa->ipsa_dst_cid->ipsid_type;
14317c478bd9Sstevel@tonic-gate 		ident->sadb_ident_id = 0;
14327c478bd9Sstevel@tonic-gate 		ident->sadb_ident_reserved = 0;
14337c478bd9Sstevel@tonic-gate 		(void) strcpy((char *)(ident + 1),
14347c478bd9Sstevel@tonic-gate 		    ipsa->ipsa_dst_cid->ipsid_cid);
14357c478bd9Sstevel@tonic-gate 		walker = (sadb_ext_t *)((uint64_t *)walker +
14367c478bd9Sstevel@tonic-gate 		    walker->sadb_ext_len);
14377c478bd9Sstevel@tonic-gate 	}
14387c478bd9Sstevel@tonic-gate 
14397c478bd9Sstevel@tonic-gate 	if (sensinteg) {
14407c478bd9Sstevel@tonic-gate 		sens = (sadb_sens_t *)walker;
1441bd670b35SErik Nordmark 		sadb_sens_from_label(sens, SADB_EXT_SENSITIVITY,
1442bd670b35SErik Nordmark 		    ipsa->ipsa_tsl, senslen);
14435d3b8cb7SBill Sommerfeld 
14445d3b8cb7SBill Sommerfeld 		walker = (sadb_ext_t *)((uint64_t *)walker +
14455d3b8cb7SBill Sommerfeld 		    walker->sadb_ext_len);
14467c478bd9Sstevel@tonic-gate 	}
14475d3b8cb7SBill Sommerfeld 
14485d3b8cb7SBill Sommerfeld 	if (osensinteg) {
14495d3b8cb7SBill Sommerfeld 		sens = (sadb_sens_t *)walker;
14505d3b8cb7SBill Sommerfeld 
1451bd670b35SErik Nordmark 		sadb_sens_from_label(sens, SADB_X_EXT_OUTER_SENS,
1452bd670b35SErik Nordmark 		    ipsa->ipsa_otsl, osenslen);
14535d3b8cb7SBill Sommerfeld 		if (ipsa->ipsa_mac_exempt)
14545d3b8cb7SBill Sommerfeld 			sens->sadb_x_sens_flags = SADB_X_SENS_IMPLICIT;
14555d3b8cb7SBill Sommerfeld 
14567c478bd9Sstevel@tonic-gate 		walker = (sadb_ext_t *)((uint64_t *)walker +
14577c478bd9Sstevel@tonic-gate 		    walker->sadb_ext_len);
14587c478bd9Sstevel@tonic-gate 	}
14597c478bd9Sstevel@tonic-gate 
146038d95a78Smarkfen 	if (paired) {
146138d95a78Smarkfen 		pair_ext = (sadb_x_pair_t *)walker;
146238d95a78Smarkfen 
146338d95a78Smarkfen 		pair_ext->sadb_x_pair_len = SADB_8TO64(sizeof (sadb_x_pair_t));
146438d95a78Smarkfen 		pair_ext->sadb_x_pair_exttype = SADB_X_EXT_PAIR;
146538d95a78Smarkfen 		pair_ext->sadb_x_pair_spi = otherspi;
146638d95a78Smarkfen 
146738d95a78Smarkfen 		walker = (sadb_ext_t *)((uint64_t *)walker +
146838d95a78Smarkfen 		    walker->sadb_ext_len);
146938d95a78Smarkfen 	}
147038d95a78Smarkfen 
14719c2c14abSThejaswini Singarajipura 	if (ipsa->ipsa_replay != 0) {
14729c2c14abSThejaswini Singarajipura 		repl_ctr = (sadb_x_replay_ctr_t *)walker;
14739c2c14abSThejaswini Singarajipura 		repl_ctr->sadb_x_rc_len = SADB_8TO64(sizeof (*repl_ctr));
14749c2c14abSThejaswini Singarajipura 		repl_ctr->sadb_x_rc_exttype = SADB_X_EXT_REPLAY_VALUE;
14759c2c14abSThejaswini Singarajipura 		repl_ctr->sadb_x_rc_replay32 = ipsa->ipsa_replay;
14769c2c14abSThejaswini Singarajipura 		repl_ctr->sadb_x_rc_replay64 = 0;
14779c2c14abSThejaswini Singarajipura 		walker = (sadb_ext_t *)(repl_ctr + 1);
14789c2c14abSThejaswini Singarajipura 	}
14799c2c14abSThejaswini Singarajipura 
14807c478bd9Sstevel@tonic-gate bail:
14817c478bd9Sstevel@tonic-gate 	/* Pardon any delays... */
14827c478bd9Sstevel@tonic-gate 	mutex_exit(&ipsa->ipsa_lock);
14837c478bd9Sstevel@tonic-gate 
14847c478bd9Sstevel@tonic-gate 	return (mp);
14857c478bd9Sstevel@tonic-gate }
14867c478bd9Sstevel@tonic-gate 
14877c478bd9Sstevel@tonic-gate /*
14887c478bd9Sstevel@tonic-gate  * Strip out key headers or unmarked headers (SADB_EXT_KEY_*, SADB_EXT_UNKNOWN)
14897c478bd9Sstevel@tonic-gate  * and adjust base message accordingly.
14907c478bd9Sstevel@tonic-gate  *
14917c478bd9Sstevel@tonic-gate  * Assume message is pulled up in one piece of contiguous memory.
14927c478bd9Sstevel@tonic-gate  *
14937c478bd9Sstevel@tonic-gate  * Say if we start off with:
14947c478bd9Sstevel@tonic-gate  *
14957c478bd9Sstevel@tonic-gate  * +------+----+-------------+-----------+---------------+---------------+
14967c478bd9Sstevel@tonic-gate  * | base | SA | source addr | dest addr | rsrvd. or key | soft lifetime |
14977c478bd9Sstevel@tonic-gate  * +------+----+-------------+-----------+---------------+---------------+
14987c478bd9Sstevel@tonic-gate  *
14997c478bd9Sstevel@tonic-gate  * we will end up with
15007c478bd9Sstevel@tonic-gate  *
15017c478bd9Sstevel@tonic-gate  * +------+----+-------------+-----------+---------------+
15027c478bd9Sstevel@tonic-gate  * | base | SA | source addr | dest addr | soft lifetime |
15037c478bd9Sstevel@tonic-gate  * +------+----+-------------+-----------+---------------+
15047c478bd9Sstevel@tonic-gate  */
15057c478bd9Sstevel@tonic-gate static void
sadb_strip(sadb_msg_t * samsg)15067c478bd9Sstevel@tonic-gate sadb_strip(sadb_msg_t *samsg)
15077c478bd9Sstevel@tonic-gate {
15087c478bd9Sstevel@tonic-gate 	sadb_ext_t *ext;
15097c478bd9Sstevel@tonic-gate 	uint8_t *target = NULL;
15107c478bd9Sstevel@tonic-gate 	uint8_t *msgend;
15117c478bd9Sstevel@tonic-gate 	int sofar = SADB_8TO64(sizeof (*samsg));
15127c478bd9Sstevel@tonic-gate 	int copylen;
15137c478bd9Sstevel@tonic-gate 
15147c478bd9Sstevel@tonic-gate 	ext = (sadb_ext_t *)(samsg + 1);
15157c478bd9Sstevel@tonic-gate 	msgend = (uint8_t *)samsg;
15167c478bd9Sstevel@tonic-gate 	msgend += SADB_64TO8(samsg->sadb_msg_len);
15177c478bd9Sstevel@tonic-gate 	while ((uint8_t *)ext < msgend) {
15187c478bd9Sstevel@tonic-gate 		if (ext->sadb_ext_type == SADB_EXT_RESERVED ||
15197c478bd9Sstevel@tonic-gate 		    ext->sadb_ext_type == SADB_EXT_KEY_AUTH ||
15209c2c14abSThejaswini Singarajipura 		    ext->sadb_ext_type == SADB_X_EXT_EDUMP ||
15217c478bd9Sstevel@tonic-gate 		    ext->sadb_ext_type == SADB_EXT_KEY_ENCRYPT) {
15227c478bd9Sstevel@tonic-gate 			/*
15237c478bd9Sstevel@tonic-gate 			 * Aha!	 I found a header to be erased.
15247c478bd9Sstevel@tonic-gate 			 */
15257c478bd9Sstevel@tonic-gate 
15267c478bd9Sstevel@tonic-gate 			if (target != NULL) {
15277c478bd9Sstevel@tonic-gate 				/*
15287c478bd9Sstevel@tonic-gate 				 * If I had a previous header to be erased,
15297c478bd9Sstevel@tonic-gate 				 * copy over it.  I can get away with just
15307c478bd9Sstevel@tonic-gate 				 * copying backwards because the target will
15317c478bd9Sstevel@tonic-gate 				 * always be 8 bytes behind the source.
15327c478bd9Sstevel@tonic-gate 				 */
15337c478bd9Sstevel@tonic-gate 				copylen = ((uint8_t *)ext) - (target +
15347c478bd9Sstevel@tonic-gate 				    SADB_64TO8(
15357c478bd9Sstevel@tonic-gate 				    ((sadb_ext_t *)target)->sadb_ext_len));
15367c478bd9Sstevel@tonic-gate 				ovbcopy(((uint8_t *)ext - copylen), target,
15377c478bd9Sstevel@tonic-gate 				    copylen);
15387c478bd9Sstevel@tonic-gate 				target += copylen;
15397c478bd9Sstevel@tonic-gate 				((sadb_ext_t *)target)->sadb_ext_len =
15407c478bd9Sstevel@tonic-gate 				    SADB_8TO64(((uint8_t *)ext) - target +
15417c478bd9Sstevel@tonic-gate 				    SADB_64TO8(ext->sadb_ext_len));
15427c478bd9Sstevel@tonic-gate 			} else {
15437c478bd9Sstevel@tonic-gate 				target = (uint8_t *)ext;
15447c478bd9Sstevel@tonic-gate 			}
15457c478bd9Sstevel@tonic-gate 		} else {
15467c478bd9Sstevel@tonic-gate 			sofar += ext->sadb_ext_len;
15477c478bd9Sstevel@tonic-gate 		}
15487c478bd9Sstevel@tonic-gate 
15497c478bd9Sstevel@tonic-gate 		ext = (sadb_ext_t *)(((uint64_t *)ext) + ext->sadb_ext_len);
15507c478bd9Sstevel@tonic-gate 	}
15517c478bd9Sstevel@tonic-gate 
15527c478bd9Sstevel@tonic-gate 	ASSERT((uint8_t *)ext == msgend);
15537c478bd9Sstevel@tonic-gate 
15547c478bd9Sstevel@tonic-gate 	if (target != NULL) {
15557c478bd9Sstevel@tonic-gate 		copylen = ((uint8_t *)ext) - (target +
15567c478bd9Sstevel@tonic-gate 		    SADB_64TO8(((sadb_ext_t *)target)->sadb_ext_len));
15577c478bd9Sstevel@tonic-gate 		if (copylen != 0)
15587c478bd9Sstevel@tonic-gate 			ovbcopy(((uint8_t *)ext - copylen), target, copylen);
15597c478bd9Sstevel@tonic-gate 	}
15607c478bd9Sstevel@tonic-gate 
15617c478bd9Sstevel@tonic-gate 	/* Adjust samsg. */
15627c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_len = (uint16_t)sofar;
15637c478bd9Sstevel@tonic-gate 
15647c478bd9Sstevel@tonic-gate 	/* Assume all of the rest is cleared by caller in sadb_pfkey_echo(). */
15657c478bd9Sstevel@tonic-gate }
15667c478bd9Sstevel@tonic-gate 
15677c478bd9Sstevel@tonic-gate /*
15687c478bd9Sstevel@tonic-gate  * AH needs to send an error to PF_KEY.	 Assume mp points to an M_CTL
15697c478bd9Sstevel@tonic-gate  * followed by an M_DATA with a PF_KEY message in it.  The serial of
15707c478bd9Sstevel@tonic-gate  * the sending keysock instance is included.
15717c478bd9Sstevel@tonic-gate  */
15727c478bd9Sstevel@tonic-gate void
sadb_pfkey_error(queue_t * pfkey_q,mblk_t * mp,int error,int diagnostic,uint_t serial)15737c478bd9Sstevel@tonic-gate sadb_pfkey_error(queue_t *pfkey_q, mblk_t *mp, int error, int diagnostic,
15747c478bd9Sstevel@tonic-gate     uint_t serial)
15757c478bd9Sstevel@tonic-gate {
15767c478bd9Sstevel@tonic-gate 	mblk_t *msg = mp->b_cont;
15777c478bd9Sstevel@tonic-gate 	sadb_msg_t *samsg;
15787c478bd9Sstevel@tonic-gate 	keysock_out_t *kso;
15797c478bd9Sstevel@tonic-gate 
15807c478bd9Sstevel@tonic-gate 	/*
15817c478bd9Sstevel@tonic-gate 	 * Enough functions call this to merit a NULL queue check.
15827c478bd9Sstevel@tonic-gate 	 */
15837c478bd9Sstevel@tonic-gate 	if (pfkey_q == NULL) {
15847c478bd9Sstevel@tonic-gate 		freemsg(mp);
15857c478bd9Sstevel@tonic-gate 		return;
15867c478bd9Sstevel@tonic-gate 	}
15877c478bd9Sstevel@tonic-gate 
15887c478bd9Sstevel@tonic-gate 	ASSERT(msg != NULL);
15897c478bd9Sstevel@tonic-gate 	ASSERT((mp->b_wptr - mp->b_rptr) == sizeof (ipsec_info_t));
15907c478bd9Sstevel@tonic-gate 	ASSERT((msg->b_wptr - msg->b_rptr) >= sizeof (sadb_msg_t));
15917c478bd9Sstevel@tonic-gate 	samsg = (sadb_msg_t *)msg->b_rptr;
15927c478bd9Sstevel@tonic-gate 	kso = (keysock_out_t *)mp->b_rptr;
15937c478bd9Sstevel@tonic-gate 
15947c478bd9Sstevel@tonic-gate 	kso->ks_out_type = KEYSOCK_OUT;
15957c478bd9Sstevel@tonic-gate 	kso->ks_out_len = sizeof (*kso);
15967c478bd9Sstevel@tonic-gate 	kso->ks_out_serial = serial;
15977c478bd9Sstevel@tonic-gate 
15987c478bd9Sstevel@tonic-gate 	/*
15997c478bd9Sstevel@tonic-gate 	 * Only send the base message up in the event of an error.
16007c478bd9Sstevel@tonic-gate 	 * Don't worry about bzero()-ing, because it was probably bogus
16017c478bd9Sstevel@tonic-gate 	 * anyway.
16027c478bd9Sstevel@tonic-gate 	 */
16037c478bd9Sstevel@tonic-gate 	msg->b_wptr = msg->b_rptr + sizeof (*samsg);
16047c478bd9Sstevel@tonic-gate 	samsg = (sadb_msg_t *)msg->b_rptr;
16057c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_len = SADB_8TO64(sizeof (*samsg));
16067c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_errno = (uint8_t)error;
16077c478bd9Sstevel@tonic-gate 	if (diagnostic != SADB_X_DIAGNOSTIC_PRESET)
16087c478bd9Sstevel@tonic-gate 		samsg->sadb_x_msg_diagnostic = (uint16_t)diagnostic;
16097c478bd9Sstevel@tonic-gate 
16107c478bd9Sstevel@tonic-gate 	putnext(pfkey_q, mp);
16117c478bd9Sstevel@tonic-gate }
16127c478bd9Sstevel@tonic-gate 
16137c478bd9Sstevel@tonic-gate /*
16147c478bd9Sstevel@tonic-gate  * Send a successful return packet back to keysock via the queue in pfkey_q.
16157c478bd9Sstevel@tonic-gate  *
16167c478bd9Sstevel@tonic-gate  * Often, an SA is associated with the reply message, it's passed in if needed,
16177c478bd9Sstevel@tonic-gate  * and NULL if not.  BTW, that ipsa will have its refcnt appropriately held,
16187c478bd9Sstevel@tonic-gate  * and the caller will release said refcnt.
16197c478bd9Sstevel@tonic-gate  */
16207c478bd9Sstevel@tonic-gate void
sadb_pfkey_echo(queue_t * pfkey_q,mblk_t * mp,sadb_msg_t * samsg,keysock_in_t * ksi,ipsa_t * ipsa)16217c478bd9Sstevel@tonic-gate sadb_pfkey_echo(queue_t *pfkey_q, mblk_t *mp, sadb_msg_t *samsg,
16227c478bd9Sstevel@tonic-gate     keysock_in_t *ksi, ipsa_t *ipsa)
16237c478bd9Sstevel@tonic-gate {
16247c478bd9Sstevel@tonic-gate 	keysock_out_t *kso;
16257c478bd9Sstevel@tonic-gate 	mblk_t *mp1;
16267c478bd9Sstevel@tonic-gate 	sadb_msg_t *newsamsg;
16277c478bd9Sstevel@tonic-gate 	uint8_t *oldend;
16287c478bd9Sstevel@tonic-gate 
16297c478bd9Sstevel@tonic-gate 	ASSERT((mp->b_cont != NULL) &&
16307c478bd9Sstevel@tonic-gate 	    ((void *)samsg == (void *)mp->b_cont->b_rptr) &&
16317c478bd9Sstevel@tonic-gate 	    ((void *)mp->b_rptr == (void *)ksi));
16327c478bd9Sstevel@tonic-gate 
16337c478bd9Sstevel@tonic-gate 	switch (samsg->sadb_msg_type) {
16347c478bd9Sstevel@tonic-gate 	case SADB_ADD:
16357c478bd9Sstevel@tonic-gate 	case SADB_UPDATE:
163638d95a78Smarkfen 	case SADB_X_UPDATEPAIR:
16379c2c14abSThejaswini Singarajipura 	case SADB_X_DELPAIR_STATE:
16387c478bd9Sstevel@tonic-gate 	case SADB_FLUSH:
16397c478bd9Sstevel@tonic-gate 	case SADB_DUMP:
16407c478bd9Sstevel@tonic-gate 		/*
16417c478bd9Sstevel@tonic-gate 		 * I have all of the message already.  I just need to strip
16427c478bd9Sstevel@tonic-gate 		 * out the keying material and echo the message back.
16437c478bd9Sstevel@tonic-gate 		 *
16447c478bd9Sstevel@tonic-gate 		 * NOTE: for SADB_DUMP, the function sadb_dump() did the
16457c478bd9Sstevel@tonic-gate 		 * work.  When DUMP reaches here, it should only be a base
16467c478bd9Sstevel@tonic-gate 		 * message.
16477c478bd9Sstevel@tonic-gate 		 */
16487c478bd9Sstevel@tonic-gate 	justecho:
16497c478bd9Sstevel@tonic-gate 		if (ksi->ks_in_extv[SADB_EXT_KEY_AUTH] != NULL ||
16509c2c14abSThejaswini Singarajipura 		    ksi->ks_in_extv[SADB_EXT_KEY_ENCRYPT] != NULL ||
16519c2c14abSThejaswini Singarajipura 		    ksi->ks_in_extv[SADB_X_EXT_EDUMP] != NULL) {
16527c478bd9Sstevel@tonic-gate 			sadb_strip(samsg);
16537c478bd9Sstevel@tonic-gate 			/* Assume PF_KEY message is contiguous. */
16547c478bd9Sstevel@tonic-gate 			ASSERT(mp->b_cont->b_cont == NULL);
16557c478bd9Sstevel@tonic-gate 			oldend = mp->b_cont->b_wptr;
16567c478bd9Sstevel@tonic-gate 			mp->b_cont->b_wptr = mp->b_cont->b_rptr +
16577c478bd9Sstevel@tonic-gate 			    SADB_64TO8(samsg->sadb_msg_len);
16587c478bd9Sstevel@tonic-gate 			bzero(mp->b_cont->b_wptr, oldend - mp->b_cont->b_wptr);
16597c478bd9Sstevel@tonic-gate 		}
16607c478bd9Sstevel@tonic-gate 		break;
16617c478bd9Sstevel@tonic-gate 	case SADB_GET:
16627c478bd9Sstevel@tonic-gate 		/*
16637c478bd9Sstevel@tonic-gate 		 * Do a lot of work here, because of the ipsa I just found.
16648810c16bSdanmcd 		 * First construct the new PF_KEY message, then abandon
16658810c16bSdanmcd 		 * the old one.
16667c478bd9Sstevel@tonic-gate 		 */
16677c478bd9Sstevel@tonic-gate 		mp1 = sadb_sa2msg(ipsa, samsg);
16687c478bd9Sstevel@tonic-gate 		if (mp1 == NULL) {
16697c478bd9Sstevel@tonic-gate 			sadb_pfkey_error(pfkey_q, mp, ENOMEM,
16707c478bd9Sstevel@tonic-gate 			    SADB_X_DIAGNOSTIC_NONE, ksi->ks_in_serial);
16717c478bd9Sstevel@tonic-gate 			return;
16727c478bd9Sstevel@tonic-gate 		}
16737c478bd9Sstevel@tonic-gate 		freemsg(mp->b_cont);
16747c478bd9Sstevel@tonic-gate 		mp->b_cont = mp1;
16757c478bd9Sstevel@tonic-gate 		break;
16767c478bd9Sstevel@tonic-gate 	case SADB_DELETE:
167738d95a78Smarkfen 	case SADB_X_DELPAIR:
16787c478bd9Sstevel@tonic-gate 		if (ipsa == NULL)
16797c478bd9Sstevel@tonic-gate 			goto justecho;
16807c478bd9Sstevel@tonic-gate 		/*
16817c478bd9Sstevel@tonic-gate 		 * Because listening KMds may require more info, treat
16827c478bd9Sstevel@tonic-gate 		 * DELETE like a special case of GET.
16837c478bd9Sstevel@tonic-gate 		 */
16847c478bd9Sstevel@tonic-gate 		mp1 = sadb_sa2msg(ipsa, samsg);
16857c478bd9Sstevel@tonic-gate 		if (mp1 == NULL) {
16867c478bd9Sstevel@tonic-gate 			sadb_pfkey_error(pfkey_q, mp, ENOMEM,
16877c478bd9Sstevel@tonic-gate 			    SADB_X_DIAGNOSTIC_NONE, ksi->ks_in_serial);
16887c478bd9Sstevel@tonic-gate 			return;
16897c478bd9Sstevel@tonic-gate 		}
16907c478bd9Sstevel@tonic-gate 		newsamsg = (sadb_msg_t *)mp1->b_rptr;
16917c478bd9Sstevel@tonic-gate 		sadb_strip(newsamsg);
16927c478bd9Sstevel@tonic-gate 		oldend = mp1->b_wptr;
16937c478bd9Sstevel@tonic-gate 		mp1->b_wptr = mp1->b_rptr + SADB_64TO8(newsamsg->sadb_msg_len);
16947c478bd9Sstevel@tonic-gate 		bzero(mp1->b_wptr, oldend - mp1->b_wptr);
16957c478bd9Sstevel@tonic-gate 		freemsg(mp->b_cont);
16967c478bd9Sstevel@tonic-gate 		mp->b_cont = mp1;
16977c478bd9Sstevel@tonic-gate 		break;
16987c478bd9Sstevel@tonic-gate 	default:
16997c478bd9Sstevel@tonic-gate 		if (mp != NULL)
17007c478bd9Sstevel@tonic-gate 			freemsg(mp);
17017c478bd9Sstevel@tonic-gate 		return;
17027c478bd9Sstevel@tonic-gate 	}
17037c478bd9Sstevel@tonic-gate 
17047c478bd9Sstevel@tonic-gate 	/* ksi is now null and void. */
17057c478bd9Sstevel@tonic-gate 	kso = (keysock_out_t *)ksi;
17067c478bd9Sstevel@tonic-gate 	kso->ks_out_type = KEYSOCK_OUT;
17077c478bd9Sstevel@tonic-gate 	kso->ks_out_len = sizeof (*kso);
17087c478bd9Sstevel@tonic-gate 	kso->ks_out_serial = ksi->ks_in_serial;
17097c478bd9Sstevel@tonic-gate 	/* We're ready to send... */
17107c478bd9Sstevel@tonic-gate 	putnext(pfkey_q, mp);
17117c478bd9Sstevel@tonic-gate }
17127c478bd9Sstevel@tonic-gate 
17137c478bd9Sstevel@tonic-gate /*
17147c478bd9Sstevel@tonic-gate  * Set up a global pfkey_q instance for AH, ESP, or some other consumer.
17157c478bd9Sstevel@tonic-gate  */
17167c478bd9Sstevel@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)17177c478bd9Sstevel@tonic-gate sadb_keysock_hello(queue_t **pfkey_qp, queue_t *q, mblk_t *mp,
1718f4b3ec61Sdh155122     void (*ager)(void *), void *agerarg, timeout_id_t *top, int satype)
17197c478bd9Sstevel@tonic-gate {
17207c478bd9Sstevel@tonic-gate 	keysock_hello_ack_t *kha;
17217c478bd9Sstevel@tonic-gate 	queue_t *oldq;
17227c478bd9Sstevel@tonic-gate 
17237c478bd9Sstevel@tonic-gate 	ASSERT(OTHERQ(q) != NULL);
17247c478bd9Sstevel@tonic-gate 
17257c478bd9Sstevel@tonic-gate 	/*
17267c478bd9Sstevel@tonic-gate 	 * First, check atomically that I'm the first and only keysock
17277c478bd9Sstevel@tonic-gate 	 * instance.
17287c478bd9Sstevel@tonic-gate 	 *
17297c478bd9Sstevel@tonic-gate 	 * Use OTHERQ(q), because qreply(q, mp) == putnext(OTHERQ(q), mp),
17307c478bd9Sstevel@tonic-gate 	 * and I want this module to say putnext(*_pfkey_q, mp) for PF_KEY
17317c478bd9Sstevel@tonic-gate 	 * messages.
17327c478bd9Sstevel@tonic-gate 	 */
17337c478bd9Sstevel@tonic-gate 
1734*75d94465SJosef 'Jeff' Sipek 	oldq = atomic_cas_ptr((void **)pfkey_qp, NULL, OTHERQ(q));
17357c478bd9Sstevel@tonic-gate 	if (oldq != NULL) {
17367c478bd9Sstevel@tonic-gate 		ASSERT(oldq != q);
17377c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "Danger!  Multiple keysocks on top of %s.\n",
17387c478bd9Sstevel@tonic-gate 		    (satype == SADB_SATYPE_ESP)? "ESP" : "AH or other");
17397c478bd9Sstevel@tonic-gate 		freemsg(mp);
17407c478bd9Sstevel@tonic-gate 		return;
17417c478bd9Sstevel@tonic-gate 	}
17427c478bd9Sstevel@tonic-gate 
17437c478bd9Sstevel@tonic-gate 	kha = (keysock_hello_ack_t *)mp->b_rptr;
17447c478bd9Sstevel@tonic-gate 	kha->ks_hello_len = sizeof (keysock_hello_ack_t);
17457c478bd9Sstevel@tonic-gate 	kha->ks_hello_type = KEYSOCK_HELLO_ACK;
17467c478bd9Sstevel@tonic-gate 	kha->ks_hello_satype = (uint8_t)satype;
17477c478bd9Sstevel@tonic-gate 
17487c478bd9Sstevel@tonic-gate 	/*
1749*75d94465SJosef 'Jeff' Sipek 	 * If we made it past the atomic_cas_ptr, then we have "exclusive"
1750*75d94465SJosef 'Jeff' Sipek 	 * access to the timeout handle.  Fire it off after the default ager
17510e9b5742SDan McDonald 	 * interval.
17527c478bd9Sstevel@tonic-gate 	 */
17530e9b5742SDan McDonald 	*top = qtimeout(*pfkey_qp, ager, agerarg,
17540e9b5742SDan McDonald 	    drv_usectohz(SADB_AGE_INTERVAL_DEFAULT * 1000));
17557c478bd9Sstevel@tonic-gate 
17567c478bd9Sstevel@tonic-gate 	putnext(*pfkey_qp, mp);
17577c478bd9Sstevel@tonic-gate }
17587c478bd9Sstevel@tonic-gate 
17597c478bd9Sstevel@tonic-gate /*
17608810c16bSdanmcd  * Normalize IPv4-mapped IPv6 addresses (and prefixes) as appropriate.
17617c478bd9Sstevel@tonic-gate  *
17628810c16bSdanmcd  * Check addresses themselves for wildcard or multicast.
17638810c16bSdanmcd  * Check ire table for local/non-local/broadcast.
17647c478bd9Sstevel@tonic-gate  */
17657c478bd9Sstevel@tonic-gate int
sadb_addrcheck(queue_t * pfkey_q,mblk_t * mp,sadb_ext_t * ext,uint_t serial,netstack_t * ns)1766f4b3ec61Sdh155122 sadb_addrcheck(queue_t *pfkey_q, mblk_t *mp, sadb_ext_t *ext, uint_t serial,
1767f4b3ec61Sdh155122     netstack_t *ns)
17687c478bd9Sstevel@tonic-gate {
17697c478bd9Sstevel@tonic-gate 	sadb_address_t *addr = (sadb_address_t *)ext;
17707c478bd9Sstevel@tonic-gate 	struct sockaddr_in *sin;
17717c478bd9Sstevel@tonic-gate 	struct sockaddr_in6 *sin6;
17728810c16bSdanmcd 	int diagnostic, type;
17738810c16bSdanmcd 	boolean_t normalized = B_FALSE;
17747c478bd9Sstevel@tonic-gate 
17757c478bd9Sstevel@tonic-gate 	ASSERT(ext != NULL);
17767c478bd9Sstevel@tonic-gate 	ASSERT((ext->sadb_ext_type == SADB_EXT_ADDRESS_SRC) ||
17777c478bd9Sstevel@tonic-gate 	    (ext->sadb_ext_type == SADB_EXT_ADDRESS_DST) ||
17788810c16bSdanmcd 	    (ext->sadb_ext_type == SADB_X_EXT_ADDRESS_INNER_SRC) ||
17798810c16bSdanmcd 	    (ext->sadb_ext_type == SADB_X_EXT_ADDRESS_INNER_DST) ||
17808810c16bSdanmcd 	    (ext->sadb_ext_type == SADB_X_EXT_ADDRESS_NATT_LOC) ||
17818810c16bSdanmcd 	    (ext->sadb_ext_type == SADB_X_EXT_ADDRESS_NATT_REM));
17827c478bd9Sstevel@tonic-gate 
17837c478bd9Sstevel@tonic-gate 	/* Assign both sockaddrs, the compiler will do the right thing. */
17847c478bd9Sstevel@tonic-gate 	sin = (struct sockaddr_in *)(addr + 1);
17857c478bd9Sstevel@tonic-gate 	sin6 = (struct sockaddr_in6 *)(addr + 1);
17867c478bd9Sstevel@tonic-gate 
17878810c16bSdanmcd 	if (sin6->sin6_family == AF_INET6) {
17888810c16bSdanmcd 		if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
17897c478bd9Sstevel@tonic-gate 			/*
17908810c16bSdanmcd 			 * Convert to an AF_INET sockaddr.  This means the
17918810c16bSdanmcd 			 * return messages will have the extra space, but have
17928810c16bSdanmcd 			 * AF_INET sockaddrs instead of AF_INET6.
17937c478bd9Sstevel@tonic-gate 			 *
17947c478bd9Sstevel@tonic-gate 			 * Yes, RFC 2367 isn't clear on what to do here w.r.t.
17957c478bd9Sstevel@tonic-gate 			 * mapped addresses, but since AF_INET6 ::ffff:<v4> is
17967c478bd9Sstevel@tonic-gate 			 * equal to AF_INET <v4>, it shouldnt be a huge
17977c478bd9Sstevel@tonic-gate 			 * problem.
17987c478bd9Sstevel@tonic-gate 			 */
17997c478bd9Sstevel@tonic-gate 			sin->sin_family = AF_INET;
18008810c16bSdanmcd 			IN6_V4MAPPED_TO_INADDR(&sin6->sin6_addr,
18018810c16bSdanmcd 			    &sin->sin_addr);
18027c478bd9Sstevel@tonic-gate 			bzero(&sin->sin_zero, sizeof (sin->sin_zero));
18038810c16bSdanmcd 			normalized = B_TRUE;
18047c478bd9Sstevel@tonic-gate 		}
18058810c16bSdanmcd 	} else if (sin->sin_family != AF_INET) {
18067c478bd9Sstevel@tonic-gate 		switch (ext->sadb_ext_type) {
18077c478bd9Sstevel@tonic-gate 		case SADB_EXT_ADDRESS_SRC:
18087c478bd9Sstevel@tonic-gate 			diagnostic = SADB_X_DIAGNOSTIC_BAD_SRC_AF;
18097c478bd9Sstevel@tonic-gate 			break;
18107c478bd9Sstevel@tonic-gate 		case SADB_EXT_ADDRESS_DST:
18117c478bd9Sstevel@tonic-gate 			diagnostic = SADB_X_DIAGNOSTIC_BAD_DST_AF;
18127c478bd9Sstevel@tonic-gate 			break;
18138810c16bSdanmcd 		case SADB_X_EXT_ADDRESS_INNER_SRC:
18147c478bd9Sstevel@tonic-gate 			diagnostic = SADB_X_DIAGNOSTIC_BAD_PROXY_AF;
18157c478bd9Sstevel@tonic-gate 			break;
18168810c16bSdanmcd 		case SADB_X_EXT_ADDRESS_INNER_DST:
18178810c16bSdanmcd 			diagnostic = SADB_X_DIAGNOSTIC_BAD_INNER_DST_AF;
18188810c16bSdanmcd 			break;
18198810c16bSdanmcd 		case SADB_X_EXT_ADDRESS_NATT_LOC:
18208810c16bSdanmcd 			diagnostic = SADB_X_DIAGNOSTIC_BAD_NATT_LOC_AF;
18218810c16bSdanmcd 			break;
18228810c16bSdanmcd 		case SADB_X_EXT_ADDRESS_NATT_REM:
18238810c16bSdanmcd 			diagnostic = SADB_X_DIAGNOSTIC_BAD_NATT_REM_AF;
18248810c16bSdanmcd 			break;
18257c478bd9Sstevel@tonic-gate 			/* There is no default, see above ASSERT. */
18267c478bd9Sstevel@tonic-gate 		}
18278810c16bSdanmcd bail:
18288810c16bSdanmcd 		if (pfkey_q != NULL) {
18298810c16bSdanmcd 			sadb_pfkey_error(pfkey_q, mp, EINVAL, diagnostic,
18308810c16bSdanmcd 			    serial);
18318810c16bSdanmcd 		} else {
18328810c16bSdanmcd 			/*
18338810c16bSdanmcd 			 * Scribble in sadb_msg that we got passed in.
18348810c16bSdanmcd 			 * Overload "mp" to be an sadb_msg pointer.
18358810c16bSdanmcd 			 */
18368810c16bSdanmcd 			sadb_msg_t *samsg = (sadb_msg_t *)mp;
18377c478bd9Sstevel@tonic-gate 
18388810c16bSdanmcd 			samsg->sadb_msg_errno = EINVAL;
18398810c16bSdanmcd 			samsg->sadb_x_msg_diagnostic = diagnostic;
18408810c16bSdanmcd 		}
18417c478bd9Sstevel@tonic-gate 		return (KS_IN_ADDR_UNKNOWN);
18427c478bd9Sstevel@tonic-gate 	}
18437c478bd9Sstevel@tonic-gate 
18448810c16bSdanmcd 	if (ext->sadb_ext_type == SADB_X_EXT_ADDRESS_INNER_SRC ||
18458810c16bSdanmcd 	    ext->sadb_ext_type == SADB_X_EXT_ADDRESS_INNER_DST) {
18468810c16bSdanmcd 		/*
18478810c16bSdanmcd 		 * We need only check for prefix issues.
18488810c16bSdanmcd 		 */
18498810c16bSdanmcd 
18508810c16bSdanmcd 		/* Set diagnostic now, in case we need it later. */
18518810c16bSdanmcd 		diagnostic =
18528810c16bSdanmcd 		    (ext->sadb_ext_type == SADB_X_EXT_ADDRESS_INNER_SRC) ?
18538810c16bSdanmcd 		    SADB_X_DIAGNOSTIC_PREFIX_INNER_SRC :
18548810c16bSdanmcd 		    SADB_X_DIAGNOSTIC_PREFIX_INNER_DST;
18558810c16bSdanmcd 
18568810c16bSdanmcd 		if (normalized)
18578810c16bSdanmcd 			addr->sadb_address_prefixlen -= 96;
18588810c16bSdanmcd 
18598810c16bSdanmcd 		/*
18608810c16bSdanmcd 		 * Verify and mask out inner-addresses based on prefix length.
18618810c16bSdanmcd 		 */
18628810c16bSdanmcd 		if (sin->sin_family == AF_INET) {
18638810c16bSdanmcd 			if (addr->sadb_address_prefixlen > 32)
18648810c16bSdanmcd 				goto bail;
18658810c16bSdanmcd 			sin->sin_addr.s_addr &=
18668810c16bSdanmcd 			    ip_plen_to_mask(addr->sadb_address_prefixlen);
18678810c16bSdanmcd 		} else {
18688810c16bSdanmcd 			in6_addr_t mask;
18698810c16bSdanmcd 
18708810c16bSdanmcd 			ASSERT(sin->sin_family == AF_INET6);
18718810c16bSdanmcd 			/*
18728810c16bSdanmcd 			 * ip_plen_to_mask_v6() returns NULL if the value in
18738810c16bSdanmcd 			 * question is out of range.
18748810c16bSdanmcd 			 */
18758810c16bSdanmcd 			if (ip_plen_to_mask_v6(addr->sadb_address_prefixlen,
18768810c16bSdanmcd 			    &mask) == NULL)
18778810c16bSdanmcd 				goto bail;
18788810c16bSdanmcd 			sin6->sin6_addr.s6_addr32[0] &= mask.s6_addr32[0];
18798810c16bSdanmcd 			sin6->sin6_addr.s6_addr32[1] &= mask.s6_addr32[1];
18808810c16bSdanmcd 			sin6->sin6_addr.s6_addr32[2] &= mask.s6_addr32[2];
18818810c16bSdanmcd 			sin6->sin6_addr.s6_addr32[3] &= mask.s6_addr32[3];
18828810c16bSdanmcd 		}
18838810c16bSdanmcd 
18848810c16bSdanmcd 		/* We don't care in these cases. */
18858810c16bSdanmcd 		return (KS_IN_ADDR_DONTCARE);
18868810c16bSdanmcd 	}
18878810c16bSdanmcd 
18888810c16bSdanmcd 	if (sin->sin_family == AF_INET6) {
18898810c16bSdanmcd 		/* Check the easy ones now. */
18908810c16bSdanmcd 		if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
18918810c16bSdanmcd 			return (KS_IN_ADDR_MBCAST);
18928810c16bSdanmcd 		if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr))
18938810c16bSdanmcd 			return (KS_IN_ADDR_UNSPEC);
18948810c16bSdanmcd 		/*
18958810c16bSdanmcd 		 * At this point, we're a unicast IPv6 address.
18968810c16bSdanmcd 		 *
18978810c16bSdanmcd 		 * XXX Zones alert -> me/notme decision needs to be tempered
18988810c16bSdanmcd 		 * by what zone we're in when we go to zone-aware IPsec.
18998810c16bSdanmcd 		 */
1900bd670b35SErik Nordmark 		if (ip_type_v6(&sin6->sin6_addr, ns->netstack_ip) ==
1901bd670b35SErik Nordmark 		    IRE_LOCAL) {
19028810c16bSdanmcd 			/* Hey hey, it's local. */
19038810c16bSdanmcd 			return (KS_IN_ADDR_ME);
19048810c16bSdanmcd 		}
19058810c16bSdanmcd 	} else {
19068810c16bSdanmcd 		ASSERT(sin->sin_family == AF_INET);
19078810c16bSdanmcd 		if (sin->sin_addr.s_addr == INADDR_ANY)
19088810c16bSdanmcd 			return (KS_IN_ADDR_UNSPEC);
19098810c16bSdanmcd 		if (CLASSD(sin->sin_addr.s_addr))
19108810c16bSdanmcd 			return (KS_IN_ADDR_MBCAST);
19118810c16bSdanmcd 		/*
19128810c16bSdanmcd 		 * At this point we're a unicast or broadcast IPv4 address.
19138810c16bSdanmcd 		 *
1914bd670b35SErik Nordmark 		 * Check if the address is IRE_BROADCAST or IRE_LOCAL.
19158810c16bSdanmcd 		 *
19168810c16bSdanmcd 		 * XXX Zones alert -> me/notme decision needs to be tempered
19178810c16bSdanmcd 		 * by what zone we're in when we go to zone-aware IPsec.
19188810c16bSdanmcd 		 */
1919bd670b35SErik Nordmark 		type = ip_type_v4(sin->sin_addr.s_addr, ns->netstack_ip);
1920bd670b35SErik Nordmark 		switch (type) {
1921bd670b35SErik Nordmark 		case IRE_LOCAL:
1922bd670b35SErik Nordmark 			return (KS_IN_ADDR_ME);
1923bd670b35SErik Nordmark 		case IRE_BROADCAST:
1924bd670b35SErik Nordmark 			return (KS_IN_ADDR_MBCAST);
19258810c16bSdanmcd 		}
19268810c16bSdanmcd 	}
19278810c16bSdanmcd 
19288810c16bSdanmcd 	return (KS_IN_ADDR_NOTME);
19297c478bd9Sstevel@tonic-gate }
19307c478bd9Sstevel@tonic-gate 
19317c478bd9Sstevel@tonic-gate /*
19328810c16bSdanmcd  * Address normalizations and reality checks for inbound PF_KEY messages.
19338810c16bSdanmcd  *
19347c478bd9Sstevel@tonic-gate  * For the case of src == unspecified AF_INET6, and dst == AF_INET, convert
19358810c16bSdanmcd  * the source to AF_INET.  Do the same for the inner sources.
19367c478bd9Sstevel@tonic-gate  */
19378810c16bSdanmcd boolean_t
sadb_addrfix(keysock_in_t * ksi,queue_t * pfkey_q,mblk_t * mp,netstack_t * ns)1938f4b3ec61Sdh155122 sadb_addrfix(keysock_in_t *ksi, queue_t *pfkey_q, mblk_t *mp, netstack_t *ns)
19397c478bd9Sstevel@tonic-gate {
19408810c16bSdanmcd 	struct sockaddr_in *src, *isrc;
19418810c16bSdanmcd 	struct sockaddr_in6 *dst, *idst;
19427c478bd9Sstevel@tonic-gate 	sadb_address_t *srcext, *dstext;
194307b56925Ssommerfe 	uint16_t sport;
19448810c16bSdanmcd 	sadb_ext_t **extv = ksi->ks_in_extv;
19458810c16bSdanmcd 	int rc;
19467c478bd9Sstevel@tonic-gate 
19478810c16bSdanmcd 	if (extv[SADB_EXT_ADDRESS_SRC] != NULL) {
19488810c16bSdanmcd 		rc = sadb_addrcheck(pfkey_q, mp, extv[SADB_EXT_ADDRESS_SRC],
1949f4b3ec61Sdh155122 		    ksi->ks_in_serial, ns);
19508810c16bSdanmcd 		if (rc == KS_IN_ADDR_UNKNOWN)
19518810c16bSdanmcd 			return (B_FALSE);
19528810c16bSdanmcd 		if (rc == KS_IN_ADDR_MBCAST) {
19538810c16bSdanmcd 			sadb_pfkey_error(pfkey_q, mp, EINVAL,
19548810c16bSdanmcd 			    SADB_X_DIAGNOSTIC_BAD_SRC, ksi->ks_in_serial);
19558810c16bSdanmcd 			return (B_FALSE);
19568810c16bSdanmcd 		}
19578810c16bSdanmcd 		ksi->ks_in_srctype = rc;
19588810c16bSdanmcd 	}
19597c478bd9Sstevel@tonic-gate 
19608810c16bSdanmcd 	if (extv[SADB_EXT_ADDRESS_DST] != NULL) {
19618810c16bSdanmcd 		rc = sadb_addrcheck(pfkey_q, mp, extv[SADB_EXT_ADDRESS_DST],
1962f4b3ec61Sdh155122 		    ksi->ks_in_serial, ns);
19638810c16bSdanmcd 		if (rc == KS_IN_ADDR_UNKNOWN)
19648810c16bSdanmcd 			return (B_FALSE);
19658810c16bSdanmcd 		if (rc == KS_IN_ADDR_UNSPEC) {
19668810c16bSdanmcd 			sadb_pfkey_error(pfkey_q, mp, EINVAL,
19678810c16bSdanmcd 			    SADB_X_DIAGNOSTIC_BAD_DST, ksi->ks_in_serial);
19688810c16bSdanmcd 			return (B_FALSE);
19698810c16bSdanmcd 		}
19708810c16bSdanmcd 		ksi->ks_in_dsttype = rc;
19718810c16bSdanmcd 	}
19727c478bd9Sstevel@tonic-gate 
19737c478bd9Sstevel@tonic-gate 	/*
19748810c16bSdanmcd 	 * NAT-Traversal addrs are simple enough to not require all of
19758810c16bSdanmcd 	 * the checks in sadb_addrcheck().  Just normalize or reject if not
19768810c16bSdanmcd 	 * AF_INET.
19777c478bd9Sstevel@tonic-gate 	 */
19788810c16bSdanmcd 	if (extv[SADB_X_EXT_ADDRESS_NATT_LOC] != NULL) {
19798810c16bSdanmcd 		rc = sadb_addrcheck(pfkey_q, mp,
1980f4b3ec61Sdh155122 		    extv[SADB_X_EXT_ADDRESS_NATT_LOC], ksi->ks_in_serial, ns);
19818810c16bSdanmcd 
19828810c16bSdanmcd 		/*
1983437220cdSdanmcd 		 * Local NAT-T addresses never use an IRE_LOCAL, so it should
1984437220cdSdanmcd 		 * always be NOTME, or UNSPEC (to handle both tunnel mode
1985437220cdSdanmcd 		 * AND local-port flexibility).
19868810c16bSdanmcd 		 */
1987437220cdSdanmcd 		if (rc != KS_IN_ADDR_NOTME && rc != KS_IN_ADDR_UNSPEC) {
19888810c16bSdanmcd 			sadb_pfkey_error(pfkey_q, mp, EINVAL,
19898810c16bSdanmcd 			    SADB_X_DIAGNOSTIC_MALFORMED_NATT_LOC,
19908810c16bSdanmcd 			    ksi->ks_in_serial);
19918810c16bSdanmcd 			return (B_FALSE);
19928810c16bSdanmcd 		}
19938810c16bSdanmcd 		src = (struct sockaddr_in *)
19948810c16bSdanmcd 		    (((sadb_address_t *)extv[SADB_X_EXT_ADDRESS_NATT_LOC]) + 1);
19958810c16bSdanmcd 		if (src->sin_family != AF_INET) {
19968810c16bSdanmcd 			sadb_pfkey_error(pfkey_q, mp, EINVAL,
19978810c16bSdanmcd 			    SADB_X_DIAGNOSTIC_BAD_NATT_LOC_AF,
19988810c16bSdanmcd 			    ksi->ks_in_serial);
19998810c16bSdanmcd 			return (B_FALSE);
20008810c16bSdanmcd 		}
20018810c16bSdanmcd 	}
20028810c16bSdanmcd 
20038810c16bSdanmcd 	if (extv[SADB_X_EXT_ADDRESS_NATT_REM] != NULL) {
20048810c16bSdanmcd 		rc = sadb_addrcheck(pfkey_q, mp,
2005f4b3ec61Sdh155122 		    extv[SADB_X_EXT_ADDRESS_NATT_REM], ksi->ks_in_serial, ns);
20068810c16bSdanmcd 
20078810c16bSdanmcd 		/*
2008437220cdSdanmcd 		 * Remote NAT-T addresses never use an IRE_LOCAL, so it should
20098810c16bSdanmcd 		 * always be NOTME, or UNSPEC if it's a tunnel-mode SA.
20108810c16bSdanmcd 		 */
20118810c16bSdanmcd 		if (rc != KS_IN_ADDR_NOTME &&
20128810c16bSdanmcd 		    !(extv[SADB_X_EXT_ADDRESS_INNER_SRC] != NULL &&
20138810c16bSdanmcd 		    rc == KS_IN_ADDR_UNSPEC)) {
20148810c16bSdanmcd 			sadb_pfkey_error(pfkey_q, mp, EINVAL,
20158810c16bSdanmcd 			    SADB_X_DIAGNOSTIC_MALFORMED_NATT_REM,
20168810c16bSdanmcd 			    ksi->ks_in_serial);
20178810c16bSdanmcd 			return (B_FALSE);
20188810c16bSdanmcd 		}
20198810c16bSdanmcd 		src = (struct sockaddr_in *)
20208810c16bSdanmcd 		    (((sadb_address_t *)extv[SADB_X_EXT_ADDRESS_NATT_REM]) + 1);
20218810c16bSdanmcd 		if (src->sin_family != AF_INET) {
20228810c16bSdanmcd 			sadb_pfkey_error(pfkey_q, mp, EINVAL,
20238810c16bSdanmcd 			    SADB_X_DIAGNOSTIC_BAD_NATT_REM_AF,
20248810c16bSdanmcd 			    ksi->ks_in_serial);
20258810c16bSdanmcd 			return (B_FALSE);
20268810c16bSdanmcd 		}
20278810c16bSdanmcd 	}
20288810c16bSdanmcd 
20298810c16bSdanmcd 	if (extv[SADB_X_EXT_ADDRESS_INNER_SRC] != NULL) {
20308810c16bSdanmcd 		if (extv[SADB_X_EXT_ADDRESS_INNER_DST] == NULL) {
20318810c16bSdanmcd 			sadb_pfkey_error(pfkey_q, mp, EINVAL,
20328810c16bSdanmcd 			    SADB_X_DIAGNOSTIC_MISSING_INNER_DST,
20338810c16bSdanmcd 			    ksi->ks_in_serial);
20348810c16bSdanmcd 			return (B_FALSE);
20358810c16bSdanmcd 		}
20368810c16bSdanmcd 
20378810c16bSdanmcd 		if (sadb_addrcheck(pfkey_q, mp,
2038f4b3ec61Sdh155122 		    extv[SADB_X_EXT_ADDRESS_INNER_DST], ksi->ks_in_serial, ns)
20398810c16bSdanmcd 		    == KS_IN_ADDR_UNKNOWN ||
20408810c16bSdanmcd 		    sadb_addrcheck(pfkey_q, mp,
2041f4b3ec61Sdh155122 		    extv[SADB_X_EXT_ADDRESS_INNER_SRC], ksi->ks_in_serial, ns)
20428810c16bSdanmcd 		    == KS_IN_ADDR_UNKNOWN)
20438810c16bSdanmcd 			return (B_FALSE);
20448810c16bSdanmcd 
20458810c16bSdanmcd 		isrc = (struct sockaddr_in *)
20468810c16bSdanmcd 		    (((sadb_address_t *)extv[SADB_X_EXT_ADDRESS_INNER_SRC]) +
20478810c16bSdanmcd 		    1);
20488810c16bSdanmcd 		idst = (struct sockaddr_in6 *)
20498810c16bSdanmcd 		    (((sadb_address_t *)extv[SADB_X_EXT_ADDRESS_INNER_DST]) +
20508810c16bSdanmcd 		    1);
20518810c16bSdanmcd 		if (isrc->sin_family != idst->sin6_family) {
20528810c16bSdanmcd 			sadb_pfkey_error(pfkey_q, mp, EINVAL,
20538810c16bSdanmcd 			    SADB_X_DIAGNOSTIC_INNER_AF_MISMATCH,
20548810c16bSdanmcd 			    ksi->ks_in_serial);
20558810c16bSdanmcd 			return (B_FALSE);
20568810c16bSdanmcd 		}
20578810c16bSdanmcd 	} else if (extv[SADB_X_EXT_ADDRESS_INNER_DST] != NULL) {
20588810c16bSdanmcd 			sadb_pfkey_error(pfkey_q, mp, EINVAL,
20598810c16bSdanmcd 			    SADB_X_DIAGNOSTIC_MISSING_INNER_SRC,
20608810c16bSdanmcd 			    ksi->ks_in_serial);
20618810c16bSdanmcd 			return (B_FALSE);
20628810c16bSdanmcd 	} else {
20638810c16bSdanmcd 		isrc = NULL;	/* For inner/outer port check below. */
20648810c16bSdanmcd 	}
20658810c16bSdanmcd 
20668810c16bSdanmcd 	dstext = (sadb_address_t *)extv[SADB_EXT_ADDRESS_DST];
20678810c16bSdanmcd 	srcext = (sadb_address_t *)extv[SADB_EXT_ADDRESS_SRC];
20688810c16bSdanmcd 
20698810c16bSdanmcd 	if (dstext == NULL || srcext == NULL)
20708810c16bSdanmcd 		return (B_TRUE);
20718810c16bSdanmcd 
20728810c16bSdanmcd 	dst = (struct sockaddr_in6 *)(dstext + 1);
20738810c16bSdanmcd 	src = (struct sockaddr_in *)(srcext + 1);
20748810c16bSdanmcd 
20758810c16bSdanmcd 	if (isrc != NULL &&
20768810c16bSdanmcd 	    (isrc->sin_port != 0 || idst->sin6_port != 0) &&
20778810c16bSdanmcd 	    (src->sin_port != 0 || dst->sin6_port != 0)) {
20788810c16bSdanmcd 		/* Can't set inner and outer ports in one SA. */
20798810c16bSdanmcd 		sadb_pfkey_error(pfkey_q, mp, EINVAL,
20808810c16bSdanmcd 		    SADB_X_DIAGNOSTIC_DUAL_PORT_SETS,
20818810c16bSdanmcd 		    ksi->ks_in_serial);
20828810c16bSdanmcd 		return (B_FALSE);
20838810c16bSdanmcd 	}
20848810c16bSdanmcd 
20858810c16bSdanmcd 	if (dst->sin6_family == src->sin_family)
20868810c16bSdanmcd 		return (B_TRUE);
20878810c16bSdanmcd 
20888810c16bSdanmcd 	if (srcext->sadb_address_proto != dstext->sadb_address_proto) {
20898810c16bSdanmcd 		if (srcext->sadb_address_proto == 0) {
20908810c16bSdanmcd 			srcext->sadb_address_proto = dstext->sadb_address_proto;
20918810c16bSdanmcd 		} else if (dstext->sadb_address_proto == 0) {
20928810c16bSdanmcd 			dstext->sadb_address_proto = srcext->sadb_address_proto;
20938810c16bSdanmcd 		} else {
20948810c16bSdanmcd 			/* Inequal protocols, neither were 0.  Report error. */
20958810c16bSdanmcd 			sadb_pfkey_error(pfkey_q, mp, EINVAL,
20968810c16bSdanmcd 			    SADB_X_DIAGNOSTIC_PROTO_MISMATCH,
20978810c16bSdanmcd 			    ksi->ks_in_serial);
20988810c16bSdanmcd 			return (B_FALSE);
20998810c16bSdanmcd 		}
21008810c16bSdanmcd 	}
21018810c16bSdanmcd 
21028810c16bSdanmcd 	/*
21038810c16bSdanmcd 	 * With the exception of an unspec IPv6 source and an IPv4
21048810c16bSdanmcd 	 * destination, address families MUST me matched.
21058810c16bSdanmcd 	 */
21068810c16bSdanmcd 	if (src->sin_family == AF_INET ||
21078810c16bSdanmcd 	    ksi->ks_in_srctype != KS_IN_ADDR_UNSPEC) {
21088810c16bSdanmcd 		sadb_pfkey_error(pfkey_q, mp, EINVAL,
21098810c16bSdanmcd 		    SADB_X_DIAGNOSTIC_AF_MISMATCH, ksi->ks_in_serial);
21108810c16bSdanmcd 		return (B_FALSE);
21118810c16bSdanmcd 	}
21127c478bd9Sstevel@tonic-gate 
211307b56925Ssommerfe 	/*
211407b56925Ssommerfe 	 * Convert "src" to AF_INET INADDR_ANY.  We rely on sin_port being
211507b56925Ssommerfe 	 * in the same place for sockaddr_in and sockaddr_in6.
211607b56925Ssommerfe 	 */
211707b56925Ssommerfe 	sport = src->sin_port;
21187c478bd9Sstevel@tonic-gate 	bzero(src, sizeof (*src));
21197c478bd9Sstevel@tonic-gate 	src->sin_family = AF_INET;
212007b56925Ssommerfe 	src->sin_port = sport;
21218810c16bSdanmcd 
21228810c16bSdanmcd 	return (B_TRUE);
21237c478bd9Sstevel@tonic-gate }
21247c478bd9Sstevel@tonic-gate 
21257c478bd9Sstevel@tonic-gate /*
21267c478bd9Sstevel@tonic-gate  * Set the results in "addrtype", given an IRE as requested by
21277c478bd9Sstevel@tonic-gate  * sadb_addrcheck().
21287c478bd9Sstevel@tonic-gate  */
21297c478bd9Sstevel@tonic-gate int
sadb_addrset(ire_t * ire)21307c478bd9Sstevel@tonic-gate sadb_addrset(ire_t *ire)
21317c478bd9Sstevel@tonic-gate {
21327c478bd9Sstevel@tonic-gate 	if ((ire->ire_type & IRE_BROADCAST) ||
21337c478bd9Sstevel@tonic-gate 	    (ire->ire_ipversion == IPV4_VERSION && CLASSD(ire->ire_addr)) ||
21347c478bd9Sstevel@tonic-gate 	    (ire->ire_ipversion == IPV6_VERSION &&
21357c478bd9Sstevel@tonic-gate 	    IN6_IS_ADDR_MULTICAST(&(ire->ire_addr_v6))))
21367c478bd9Sstevel@tonic-gate 		return (KS_IN_ADDR_MBCAST);
21377c478bd9Sstevel@tonic-gate 	if (ire->ire_type & (IRE_LOCAL | IRE_LOOPBACK))
21387c478bd9Sstevel@tonic-gate 		return (KS_IN_ADDR_ME);
21397c478bd9Sstevel@tonic-gate 	return (KS_IN_ADDR_NOTME);
21407c478bd9Sstevel@tonic-gate }
21417c478bd9Sstevel@tonic-gate 
21425d3b8cb7SBill Sommerfeld /*
21435d3b8cb7SBill Sommerfeld  * Match primitives..
21445d3b8cb7SBill Sommerfeld  * !!! TODO: short term: inner selectors
21455d3b8cb7SBill Sommerfeld  *		ipv6 scope id (ifindex)
21465d3b8cb7SBill Sommerfeld  * longer term:  zone id.  sensitivity label. uid.
21475d3b8cb7SBill Sommerfeld  */
21485d3b8cb7SBill Sommerfeld boolean_t
sadb_match_spi(ipsa_query_t * sq,ipsa_t * sa)21495d3b8cb7SBill Sommerfeld sadb_match_spi(ipsa_query_t *sq, ipsa_t *sa)
21505d3b8cb7SBill Sommerfeld {
21515d3b8cb7SBill Sommerfeld 	return (sq->spi == sa->ipsa_spi);
21525d3b8cb7SBill Sommerfeld }
21535d3b8cb7SBill Sommerfeld 
21545d3b8cb7SBill Sommerfeld boolean_t
sadb_match_dst_v6(ipsa_query_t * sq,ipsa_t * sa)21555d3b8cb7SBill Sommerfeld sadb_match_dst_v6(ipsa_query_t *sq, ipsa_t *sa)
21565d3b8cb7SBill Sommerfeld {
21575d3b8cb7SBill Sommerfeld 	return (IPSA_ARE_ADDR_EQUAL(sa->ipsa_dstaddr, sq->dstaddr, AF_INET6));
21585d3b8cb7SBill Sommerfeld }
21595d3b8cb7SBill Sommerfeld 
21605d3b8cb7SBill Sommerfeld boolean_t
sadb_match_src_v6(ipsa_query_t * sq,ipsa_t * sa)21615d3b8cb7SBill Sommerfeld sadb_match_src_v6(ipsa_query_t *sq, ipsa_t *sa)
21625d3b8cb7SBill Sommerfeld {
21635d3b8cb7SBill Sommerfeld 	return (IPSA_ARE_ADDR_EQUAL(sa->ipsa_srcaddr, sq->srcaddr, AF_INET6));
21645d3b8cb7SBill Sommerfeld }
21655d3b8cb7SBill Sommerfeld 
21665d3b8cb7SBill Sommerfeld boolean_t
sadb_match_dst_v4(ipsa_query_t * sq,ipsa_t * sa)21675d3b8cb7SBill Sommerfeld sadb_match_dst_v4(ipsa_query_t *sq, ipsa_t *sa)
21685d3b8cb7SBill Sommerfeld {
21695d3b8cb7SBill Sommerfeld 	return (sq->dstaddr[0] == sa->ipsa_dstaddr[0]);
21705d3b8cb7SBill Sommerfeld }
21715d3b8cb7SBill Sommerfeld 
21725d3b8cb7SBill Sommerfeld boolean_t
sadb_match_src_v4(ipsa_query_t * sq,ipsa_t * sa)21735d3b8cb7SBill Sommerfeld sadb_match_src_v4(ipsa_query_t *sq, ipsa_t *sa)
21745d3b8cb7SBill Sommerfeld {
21755d3b8cb7SBill Sommerfeld 	return (sq->srcaddr[0] == sa->ipsa_srcaddr[0]);
21765d3b8cb7SBill Sommerfeld }
21775d3b8cb7SBill Sommerfeld 
21785d3b8cb7SBill Sommerfeld boolean_t
sadb_match_dstid(ipsa_query_t * sq,ipsa_t * sa)21795d3b8cb7SBill Sommerfeld sadb_match_dstid(ipsa_query_t *sq, ipsa_t *sa)
21805d3b8cb7SBill Sommerfeld {
21815d3b8cb7SBill Sommerfeld 	return ((sa->ipsa_dst_cid != NULL) &&
21825d3b8cb7SBill Sommerfeld 	    (sq->didtype == sa->ipsa_dst_cid->ipsid_type) &&
21835d3b8cb7SBill Sommerfeld 	    (strcmp(sq->didstr, sa->ipsa_dst_cid->ipsid_cid) == 0));
21845d3b8cb7SBill Sommerfeld 
21855d3b8cb7SBill Sommerfeld }
21865d3b8cb7SBill Sommerfeld boolean_t
sadb_match_srcid(ipsa_query_t * sq,ipsa_t * sa)21875d3b8cb7SBill Sommerfeld sadb_match_srcid(ipsa_query_t *sq, ipsa_t *sa)
21885d3b8cb7SBill Sommerfeld {
21895d3b8cb7SBill Sommerfeld 	return ((sa->ipsa_src_cid != NULL) &&
21905d3b8cb7SBill Sommerfeld 	    (sq->sidtype == sa->ipsa_src_cid->ipsid_type) &&
21915d3b8cb7SBill Sommerfeld 	    (strcmp(sq->sidstr, sa->ipsa_src_cid->ipsid_cid) == 0));
21925d3b8cb7SBill Sommerfeld }
21935d3b8cb7SBill Sommerfeld 
21945d3b8cb7SBill Sommerfeld boolean_t
sadb_match_kmc(ipsa_query_t * sq,ipsa_t * sa)21955d3b8cb7SBill Sommerfeld sadb_match_kmc(ipsa_query_t *sq, ipsa_t *sa)
21965d3b8cb7SBill Sommerfeld {
21975d3b8cb7SBill Sommerfeld #define	M(a, b) (((a) == 0) || ((b) == 0) || ((a) == (b)))
21985d3b8cb7SBill Sommerfeld 
21995d3b8cb7SBill Sommerfeld 	return (M(sq->kmc, sa->ipsa_kmc) && M(sq->kmp, sa->ipsa_kmp));
22005d3b8cb7SBill Sommerfeld 
22015d3b8cb7SBill Sommerfeld #undef M
22025d3b8cb7SBill Sommerfeld }
22035d3b8cb7SBill Sommerfeld 
22045d3b8cb7SBill Sommerfeld /*
22055d3b8cb7SBill Sommerfeld  * Common function which extracts several PF_KEY extensions for ease of
22065d3b8cb7SBill Sommerfeld  * SADB matching.
22075d3b8cb7SBill Sommerfeld  *
22085d3b8cb7SBill Sommerfeld  * XXX TODO: weed out ipsa_query_t fields not used during matching
22095d3b8cb7SBill Sommerfeld  * or afterwards?
22105d3b8cb7SBill Sommerfeld  */
22115d3b8cb7SBill Sommerfeld int
sadb_form_query(keysock_in_t * ksi,uint32_t req,uint32_t match,ipsa_query_t * sq,int * diagnostic)22125d3b8cb7SBill Sommerfeld sadb_form_query(keysock_in_t *ksi, uint32_t req, uint32_t match,
22135d3b8cb7SBill Sommerfeld     ipsa_query_t *sq, int *diagnostic)
22145d3b8cb7SBill Sommerfeld {
22155d3b8cb7SBill Sommerfeld 	int i;
22165d3b8cb7SBill Sommerfeld 	ipsa_match_fn_t *mfpp = &(sq->matchers[0]);
22175d3b8cb7SBill Sommerfeld 
22185d3b8cb7SBill Sommerfeld 	for (i = 0; i < IPSA_NMATCH; i++)
22195d3b8cb7SBill Sommerfeld 		sq->matchers[i] = NULL;
22205d3b8cb7SBill Sommerfeld 
22215d3b8cb7SBill Sommerfeld 	ASSERT((req & ~match) == 0);
22225d3b8cb7SBill Sommerfeld 
22235d3b8cb7SBill Sommerfeld 	sq->req = req;
22245d3b8cb7SBill Sommerfeld 	sq->dstext = (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_DST];
22255d3b8cb7SBill Sommerfeld 	sq->srcext = (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_SRC];
22265d3b8cb7SBill Sommerfeld 	sq->assoc = (sadb_sa_t *)ksi->ks_in_extv[SADB_EXT_SA];
22275d3b8cb7SBill Sommerfeld 
22285d3b8cb7SBill Sommerfeld 	if ((req & IPSA_Q_DST) && (sq->dstext == NULL)) {
22295d3b8cb7SBill Sommerfeld 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_DST;
22305d3b8cb7SBill Sommerfeld 		return (EINVAL);
22315d3b8cb7SBill Sommerfeld 	}
22325d3b8cb7SBill Sommerfeld 	if ((req & IPSA_Q_SRC) && (sq->srcext == NULL)) {
22335d3b8cb7SBill Sommerfeld 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_SRC;
22345d3b8cb7SBill Sommerfeld 		return (EINVAL);
22355d3b8cb7SBill Sommerfeld 	}
22365d3b8cb7SBill Sommerfeld 	if ((req & IPSA_Q_SA) && (sq->assoc == NULL)) {
22375d3b8cb7SBill Sommerfeld 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_SA;
22385d3b8cb7SBill Sommerfeld 		return (EINVAL);
22395d3b8cb7SBill Sommerfeld 	}
22405d3b8cb7SBill Sommerfeld 
22415d3b8cb7SBill Sommerfeld 	if (match & IPSA_Q_SA) {
22425d3b8cb7SBill Sommerfeld 		*mfpp++ = sadb_match_spi;
22435d3b8cb7SBill Sommerfeld 		sq->spi = sq->assoc->sadb_sa_spi;
22445d3b8cb7SBill Sommerfeld 	}
22455d3b8cb7SBill Sommerfeld 
22465d3b8cb7SBill Sommerfeld 	if (sq->dstext != NULL)
22475d3b8cb7SBill Sommerfeld 		sq->dst = (struct sockaddr_in *)(sq->dstext + 1);
22485d3b8cb7SBill Sommerfeld 	else {
22495d3b8cb7SBill Sommerfeld 		sq->dst = NULL;
22505d3b8cb7SBill Sommerfeld 		sq->dst6 = NULL;
22515d3b8cb7SBill Sommerfeld 		sq->dstaddr = NULL;
22525d3b8cb7SBill Sommerfeld 	}
22535d3b8cb7SBill Sommerfeld 
22545d3b8cb7SBill Sommerfeld 	if (sq->srcext != NULL)
22555d3b8cb7SBill Sommerfeld 		sq->src = (struct sockaddr_in *)(sq->srcext + 1);
22565d3b8cb7SBill Sommerfeld 	else {
22575d3b8cb7SBill Sommerfeld 		sq->src = NULL;
22585d3b8cb7SBill Sommerfeld 		sq->src6 = NULL;
22595d3b8cb7SBill Sommerfeld 		sq->srcaddr = NULL;
22605d3b8cb7SBill Sommerfeld 	}
22615d3b8cb7SBill Sommerfeld 
22625d3b8cb7SBill Sommerfeld 	if (sq->dst != NULL)
22635d3b8cb7SBill Sommerfeld 		sq->af = sq->dst->sin_family;
22645d3b8cb7SBill Sommerfeld 	else if (sq->src != NULL)
22655d3b8cb7SBill Sommerfeld 		sq->af = sq->src->sin_family;
22665d3b8cb7SBill Sommerfeld 	else
22675d3b8cb7SBill Sommerfeld 		sq->af = AF_INET;
22685d3b8cb7SBill Sommerfeld 
22695d3b8cb7SBill Sommerfeld 	if (sq->af == AF_INET6) {
22705d3b8cb7SBill Sommerfeld 		if ((match & IPSA_Q_DST) && (sq->dstext != NULL)) {
22715d3b8cb7SBill Sommerfeld 			*mfpp++ = sadb_match_dst_v6;
22725d3b8cb7SBill Sommerfeld 			sq->dst6 = (struct sockaddr_in6 *)sq->dst;
22735d3b8cb7SBill Sommerfeld 			sq->dstaddr = (uint32_t *)&(sq->dst6->sin6_addr);
22745d3b8cb7SBill Sommerfeld 		} else {
22755d3b8cb7SBill Sommerfeld 			match &= ~IPSA_Q_DST;
22765d3b8cb7SBill Sommerfeld 			sq->dstaddr = ALL_ZEROES_PTR;
22775d3b8cb7SBill Sommerfeld 		}
22785d3b8cb7SBill Sommerfeld 
22795d3b8cb7SBill Sommerfeld 		if ((match & IPSA_Q_SRC) && (sq->srcext != NULL)) {
22805d3b8cb7SBill Sommerfeld 			sq->src6 = (struct sockaddr_in6 *)(sq->srcext + 1);
22815d3b8cb7SBill Sommerfeld 			sq->srcaddr = (uint32_t *)&sq->src6->sin6_addr;
22825d3b8cb7SBill Sommerfeld 			if (sq->src6->sin6_family != AF_INET6) {
22835d3b8cb7SBill Sommerfeld 				*diagnostic = SADB_X_DIAGNOSTIC_AF_MISMATCH;
22845d3b8cb7SBill Sommerfeld 				return (EINVAL);
22855d3b8cb7SBill Sommerfeld 			}
22865d3b8cb7SBill Sommerfeld 			*mfpp++ = sadb_match_src_v6;
22875d3b8cb7SBill Sommerfeld 		} else {
22885d3b8cb7SBill Sommerfeld 			match &= ~IPSA_Q_SRC;
22895d3b8cb7SBill Sommerfeld 			sq->srcaddr = ALL_ZEROES_PTR;
22905d3b8cb7SBill Sommerfeld 		}
22915d3b8cb7SBill Sommerfeld 	} else {
22925d3b8cb7SBill Sommerfeld 		sq->src6 = sq->dst6 = NULL;
22935d3b8cb7SBill Sommerfeld 		if ((match & IPSA_Q_DST) && (sq->dstext != NULL)) {
22945d3b8cb7SBill Sommerfeld 			*mfpp++ = sadb_match_dst_v4;
22955d3b8cb7SBill Sommerfeld 			sq->dstaddr = (uint32_t *)&sq->dst->sin_addr;
22965d3b8cb7SBill Sommerfeld 		} else {
22975d3b8cb7SBill Sommerfeld 			match &= ~IPSA_Q_DST;
22985d3b8cb7SBill Sommerfeld 			sq->dstaddr = ALL_ZEROES_PTR;
22995d3b8cb7SBill Sommerfeld 		}
23005d3b8cb7SBill Sommerfeld 		if ((match & IPSA_Q_SRC) && (sq->srcext != NULL)) {
23015d3b8cb7SBill Sommerfeld 			sq->srcaddr = (uint32_t *)&sq->src->sin_addr;
23025d3b8cb7SBill Sommerfeld 			if (sq->src->sin_family != AF_INET) {
23035d3b8cb7SBill Sommerfeld 				*diagnostic = SADB_X_DIAGNOSTIC_AF_MISMATCH;
23045d3b8cb7SBill Sommerfeld 				return (EINVAL);
23055d3b8cb7SBill Sommerfeld 			}
23065d3b8cb7SBill Sommerfeld 			*mfpp++ = sadb_match_src_v4;
23075d3b8cb7SBill Sommerfeld 		} else {
23085d3b8cb7SBill Sommerfeld 			match &= ~IPSA_Q_SRC;
23095d3b8cb7SBill Sommerfeld 			sq->srcaddr = ALL_ZEROES_PTR;
23105d3b8cb7SBill Sommerfeld 		}
23115d3b8cb7SBill Sommerfeld 	}
23125d3b8cb7SBill Sommerfeld 
23135d3b8cb7SBill Sommerfeld 	sq->dstid = (sadb_ident_t *)ksi->ks_in_extv[SADB_EXT_IDENTITY_DST];
23145d3b8cb7SBill Sommerfeld 	if ((match & IPSA_Q_DSTID) && (sq->dstid != NULL)) {
23155d3b8cb7SBill Sommerfeld 		sq->didstr = (char *)(sq->dstid + 1);
23165d3b8cb7SBill Sommerfeld 		sq->didtype = sq->dstid->sadb_ident_type;
23175d3b8cb7SBill Sommerfeld 		*mfpp++ = sadb_match_dstid;
23185d3b8cb7SBill Sommerfeld 	}
23195d3b8cb7SBill Sommerfeld 
23205d3b8cb7SBill Sommerfeld 	sq->srcid = (sadb_ident_t *)ksi->ks_in_extv[SADB_EXT_IDENTITY_SRC];
23215d3b8cb7SBill Sommerfeld 
23225d3b8cb7SBill Sommerfeld 	if ((match & IPSA_Q_SRCID) && (sq->srcid != NULL)) {
23235d3b8cb7SBill Sommerfeld 		sq->sidstr = (char *)(sq->srcid + 1);
23245d3b8cb7SBill Sommerfeld 		sq->sidtype = sq->srcid->sadb_ident_type;
23255d3b8cb7SBill Sommerfeld 		*mfpp++ = sadb_match_srcid;
23265d3b8cb7SBill Sommerfeld 	}
23275d3b8cb7SBill Sommerfeld 
23285d3b8cb7SBill Sommerfeld 	sq->kmcext = (sadb_x_kmc_t *)ksi->ks_in_extv[SADB_X_EXT_KM_COOKIE];
23295d3b8cb7SBill Sommerfeld 	sq->kmc = 0;
23305d3b8cb7SBill Sommerfeld 	sq->kmp = 0;
23315d3b8cb7SBill Sommerfeld 
23325d3b8cb7SBill Sommerfeld 	if ((match & IPSA_Q_KMC) && (sq->kmcext)) {
23335d3b8cb7SBill Sommerfeld 		sq->kmc = sq->kmcext->sadb_x_kmc_cookie;
23345d3b8cb7SBill Sommerfeld 		sq->kmp = sq->kmcext->sadb_x_kmc_proto;
23355d3b8cb7SBill Sommerfeld 		*mfpp++ = sadb_match_kmc;
23365d3b8cb7SBill Sommerfeld 	}
23375d3b8cb7SBill Sommerfeld 
23385d3b8cb7SBill Sommerfeld 	if (match & (IPSA_Q_INBOUND|IPSA_Q_OUTBOUND)) {
23395d3b8cb7SBill Sommerfeld 		if (sq->af == AF_INET6)
23405d3b8cb7SBill Sommerfeld 			sq->sp = &sq->spp->s_v6;
23415d3b8cb7SBill Sommerfeld 		else
23425d3b8cb7SBill Sommerfeld 			sq->sp = &sq->spp->s_v4;
23435d3b8cb7SBill Sommerfeld 	} else {
23445d3b8cb7SBill Sommerfeld 		sq->sp = NULL;
23455d3b8cb7SBill Sommerfeld 	}
23465d3b8cb7SBill Sommerfeld 
23475d3b8cb7SBill Sommerfeld 	if (match & IPSA_Q_INBOUND) {
23485d3b8cb7SBill Sommerfeld 		sq->inhash = INBOUND_HASH(sq->sp, sq->assoc->sadb_sa_spi);
23495d3b8cb7SBill Sommerfeld 		sq->inbound = &sq->sp->sdb_if[sq->inhash];
23505d3b8cb7SBill Sommerfeld 	} else {
23515d3b8cb7SBill Sommerfeld 		sq->inhash = 0;
23525d3b8cb7SBill Sommerfeld 		sq->inbound = NULL;
23535d3b8cb7SBill Sommerfeld 	}
23545d3b8cb7SBill Sommerfeld 
23555d3b8cb7SBill Sommerfeld 	if (match & IPSA_Q_OUTBOUND) {
23565d3b8cb7SBill Sommerfeld 		if (sq->af == AF_INET6) {
23575d3b8cb7SBill Sommerfeld 			sq->outhash = OUTBOUND_HASH_V6(sq->sp, *(sq->dstaddr));
23585d3b8cb7SBill Sommerfeld 		} else {
23595d3b8cb7SBill Sommerfeld 			sq->outhash = OUTBOUND_HASH_V4(sq->sp, *(sq->dstaddr));
23605d3b8cb7SBill Sommerfeld 		}
23615d3b8cb7SBill Sommerfeld 		sq->outbound = &sq->sp->sdb_of[sq->outhash];
23625d3b8cb7SBill Sommerfeld 	} else {
23635d3b8cb7SBill Sommerfeld 		sq->outhash = 0;
23645d3b8cb7SBill Sommerfeld 		sq->outbound = NULL;
23655d3b8cb7SBill Sommerfeld 	}
23665d3b8cb7SBill Sommerfeld 	sq->match = match;
23675d3b8cb7SBill Sommerfeld 	return (0);
23685d3b8cb7SBill Sommerfeld }
23695d3b8cb7SBill Sommerfeld 
23705d3b8cb7SBill Sommerfeld /*
23715d3b8cb7SBill Sommerfeld  * Match an initialized query structure with a security association;
23725d3b8cb7SBill Sommerfeld  * return B_TRUE on a match, B_FALSE on a miss.
23735d3b8cb7SBill Sommerfeld  * Applies match functions set up by sadb_form_query() until one returns false.
23745d3b8cb7SBill Sommerfeld  */
23755d3b8cb7SBill Sommerfeld boolean_t
sadb_match_query(ipsa_query_t * sq,ipsa_t * sa)23765d3b8cb7SBill Sommerfeld sadb_match_query(ipsa_query_t *sq, ipsa_t *sa)
23775d3b8cb7SBill Sommerfeld {
23785d3b8cb7SBill Sommerfeld 	ipsa_match_fn_t *mfpp = &(sq->matchers[0]);
23795d3b8cb7SBill Sommerfeld 	ipsa_match_fn_t mfp;
23805d3b8cb7SBill Sommerfeld 
23815d3b8cb7SBill Sommerfeld 	for (mfp = *mfpp++; mfp != NULL; mfp = *mfpp++) {
23825d3b8cb7SBill Sommerfeld 		if (!mfp(sq, sa))
23835d3b8cb7SBill Sommerfeld 			return (B_FALSE);
23845d3b8cb7SBill Sommerfeld 	}
23855d3b8cb7SBill Sommerfeld 	return (B_TRUE);
23865d3b8cb7SBill Sommerfeld }
23877c478bd9Sstevel@tonic-gate 
23887c478bd9Sstevel@tonic-gate /*
23897c478bd9Sstevel@tonic-gate  * Walker callback function to delete sa's based on src/dst address.
23907c478bd9Sstevel@tonic-gate  * Assumes that we're called with *head locked, no other locks held;
23917c478bd9Sstevel@tonic-gate  * Conveniently, and not coincidentally, this is both what sadb_walker
23927c478bd9Sstevel@tonic-gate  * gives us and also what sadb_unlinkassoc expects.
23937c478bd9Sstevel@tonic-gate  */
23947c478bd9Sstevel@tonic-gate struct sadb_purge_state
23957c478bd9Sstevel@tonic-gate {
23965d3b8cb7SBill Sommerfeld 	ipsa_query_t sq;
23977c478bd9Sstevel@tonic-gate 	boolean_t inbnd;
23989c2c14abSThejaswini Singarajipura 	uint8_t sadb_sa_state;
23997c478bd9Sstevel@tonic-gate };
24007c478bd9Sstevel@tonic-gate 
24017c478bd9Sstevel@tonic-gate static void
sadb_purge_cb(isaf_t * head,ipsa_t * entry,void * cookie)24027c478bd9Sstevel@tonic-gate sadb_purge_cb(isaf_t *head, ipsa_t *entry, void *cookie)
24037c478bd9Sstevel@tonic-gate {
24047c478bd9Sstevel@tonic-gate 	struct sadb_purge_state *ps = (struct sadb_purge_state *)cookie;
24057c478bd9Sstevel@tonic-gate 
24067c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&head->isaf_lock));
24077c478bd9Sstevel@tonic-gate 
24087c478bd9Sstevel@tonic-gate 	mutex_enter(&entry->ipsa_lock);
24097c478bd9Sstevel@tonic-gate 
24105d3b8cb7SBill Sommerfeld 	if (entry->ipsa_state == IPSA_STATE_LARVAL ||
24115d3b8cb7SBill Sommerfeld 	    !sadb_match_query(&ps->sq, entry)) {
24127c478bd9Sstevel@tonic-gate 		mutex_exit(&entry->ipsa_lock);
24137c478bd9Sstevel@tonic-gate 		return;
24147c478bd9Sstevel@tonic-gate 	}
24157c478bd9Sstevel@tonic-gate 
24169c2c14abSThejaswini Singarajipura 	if (ps->inbnd) {
24179c2c14abSThejaswini Singarajipura 		sadb_delete_cluster(entry);
24189c2c14abSThejaswini Singarajipura 	}
24197c478bd9Sstevel@tonic-gate 	entry->ipsa_state = IPSA_STATE_DEAD;
2420bd670b35SErik Nordmark 	(void) sadb_torch_assoc(head, entry);
24217c478bd9Sstevel@tonic-gate }
24227c478bd9Sstevel@tonic-gate 
24237c478bd9Sstevel@tonic-gate /*
24247c478bd9Sstevel@tonic-gate  * Common code to purge an SA with a matching src or dst address.
24257c478bd9Sstevel@tonic-gate  * Don't kill larval SA's in such a purge.
24267c478bd9Sstevel@tonic-gate  */
24277c478bd9Sstevel@tonic-gate int
sadb_purge_sa(mblk_t * mp,keysock_in_t * ksi,sadb_t * sp,int * diagnostic,queue_t * pfkey_q)24285d3b8cb7SBill Sommerfeld sadb_purge_sa(mblk_t *mp, keysock_in_t *ksi, sadb_t *sp,
2429bd670b35SErik Nordmark 	int *diagnostic, queue_t *pfkey_q)
24307c478bd9Sstevel@tonic-gate {
24317c478bd9Sstevel@tonic-gate 	struct sadb_purge_state ps;
24325d3b8cb7SBill Sommerfeld 	int error = sadb_form_query(ksi, 0,
24335d3b8cb7SBill Sommerfeld 	    IPSA_Q_SRC|IPSA_Q_DST|IPSA_Q_SRCID|IPSA_Q_DSTID|IPSA_Q_KMC,
24345d3b8cb7SBill Sommerfeld 	    &ps.sq, diagnostic);
24357c478bd9Sstevel@tonic-gate 
24365d3b8cb7SBill Sommerfeld 	if (error != 0)
24375d3b8cb7SBill Sommerfeld 		return (error);
24387c478bd9Sstevel@tonic-gate 
24397c478bd9Sstevel@tonic-gate 	/*
24407c478bd9Sstevel@tonic-gate 	 * This is simple, crude, and effective.
24417c478bd9Sstevel@tonic-gate 	 * Unimplemented optimizations (TBD):
24427c478bd9Sstevel@tonic-gate 	 * - we can limit how many places we search based on where we
24437c478bd9Sstevel@tonic-gate 	 * think the SA is filed.
24447c478bd9Sstevel@tonic-gate 	 * - if we get a dst address, we can hash based on dst addr to find
24457c478bd9Sstevel@tonic-gate 	 * the correct bucket in the outbound table.
24467c478bd9Sstevel@tonic-gate 	 */
24477c478bd9Sstevel@tonic-gate 	ps.inbnd = B_TRUE;
2448fb87b5d2Ssommerfe 	sadb_walker(sp->sdb_if, sp->sdb_hashsize, sadb_purge_cb, &ps);
24497c478bd9Sstevel@tonic-gate 	ps.inbnd = B_FALSE;
2450fb87b5d2Ssommerfe 	sadb_walker(sp->sdb_of, sp->sdb_hashsize, sadb_purge_cb, &ps);
24517c478bd9Sstevel@tonic-gate 
24527c478bd9Sstevel@tonic-gate 	ASSERT(mp->b_cont != NULL);
24537c478bd9Sstevel@tonic-gate 	sadb_pfkey_echo(pfkey_q, mp, (sadb_msg_t *)mp->b_cont->b_rptr, ksi,
24547c478bd9Sstevel@tonic-gate 	    NULL);
24557c478bd9Sstevel@tonic-gate 	return (0);
24567c478bd9Sstevel@tonic-gate }
24577c478bd9Sstevel@tonic-gate 
24589c2c14abSThejaswini Singarajipura static void
sadb_delpair_state_one(isaf_t * head,ipsa_t * entry,void * cookie)24595d3b8cb7SBill Sommerfeld sadb_delpair_state_one(isaf_t *head, ipsa_t *entry, void *cookie)
24609c2c14abSThejaswini Singarajipura {
24619c2c14abSThejaswini Singarajipura 	struct sadb_purge_state *ps = (struct sadb_purge_state *)cookie;
24629c2c14abSThejaswini Singarajipura 	isaf_t  *inbound_bucket;
24639c2c14abSThejaswini Singarajipura 	ipsa_t *peer_assoc;
24645d3b8cb7SBill Sommerfeld 	ipsa_query_t *sq = &ps->sq;
24659c2c14abSThejaswini Singarajipura 
24669c2c14abSThejaswini Singarajipura 	ASSERT(MUTEX_HELD(&head->isaf_lock));
24679c2c14abSThejaswini Singarajipura 
24689c2c14abSThejaswini Singarajipura 	mutex_enter(&entry->ipsa_lock);
24699c2c14abSThejaswini Singarajipura 
24709c2c14abSThejaswini Singarajipura 	if ((entry->ipsa_state != ps->sadb_sa_state) ||
24715d3b8cb7SBill Sommerfeld 	    ((sq->srcaddr != NULL) &&
24725d3b8cb7SBill Sommerfeld 	    !IPSA_ARE_ADDR_EQUAL(entry->ipsa_srcaddr, sq->srcaddr, sq->af))) {
24739c2c14abSThejaswini Singarajipura 		mutex_exit(&entry->ipsa_lock);
24749c2c14abSThejaswini Singarajipura 		return;
24759c2c14abSThejaswini Singarajipura 	}
24769c2c14abSThejaswini Singarajipura 
24779c2c14abSThejaswini Singarajipura 	/*
24789c2c14abSThejaswini Singarajipura 	 * The isaf_t *, which is passed in , is always an outbound bucket,
24799c2c14abSThejaswini Singarajipura 	 * and we are preserving the outbound-then-inbound hash-bucket lock
24809c2c14abSThejaswini Singarajipura 	 * ordering. The sadb_walker() which triggers this function is called
24819c2c14abSThejaswini Singarajipura 	 * only on the outbound fanout, and the corresponding inbound bucket
24829c2c14abSThejaswini Singarajipura 	 * lock is safe to acquire here.
24839c2c14abSThejaswini Singarajipura 	 */
24849c2c14abSThejaswini Singarajipura 
24859c2c14abSThejaswini Singarajipura 	if (entry->ipsa_haspeer) {
24865d3b8cb7SBill Sommerfeld 		inbound_bucket = INBOUND_BUCKET(sq->sp, entry->ipsa_spi);
24879c2c14abSThejaswini Singarajipura 		mutex_enter(&inbound_bucket->isaf_lock);
24889c2c14abSThejaswini Singarajipura 		peer_assoc = ipsec_getassocbyspi(inbound_bucket,
24899c2c14abSThejaswini Singarajipura 		    entry->ipsa_spi, entry->ipsa_srcaddr,
24909c2c14abSThejaswini Singarajipura 		    entry->ipsa_dstaddr, entry->ipsa_addrfam);
24919c2c14abSThejaswini Singarajipura 	} else {
24925d3b8cb7SBill Sommerfeld 		inbound_bucket = INBOUND_BUCKET(sq->sp, entry->ipsa_otherspi);
24939c2c14abSThejaswini Singarajipura 		mutex_enter(&inbound_bucket->isaf_lock);
24949c2c14abSThejaswini Singarajipura 		peer_assoc = ipsec_getassocbyspi(inbound_bucket,
24959c2c14abSThejaswini Singarajipura 		    entry->ipsa_otherspi, entry->ipsa_dstaddr,
24969c2c14abSThejaswini Singarajipura 		    entry->ipsa_srcaddr, entry->ipsa_addrfam);
24979c2c14abSThejaswini Singarajipura 	}
24989c2c14abSThejaswini Singarajipura 
24999c2c14abSThejaswini Singarajipura 	entry->ipsa_state = IPSA_STATE_DEAD;
2500bd670b35SErik Nordmark 	(void) sadb_torch_assoc(head, entry);
25019c2c14abSThejaswini Singarajipura 	if (peer_assoc != NULL) {
25029c2c14abSThejaswini Singarajipura 		mutex_enter(&peer_assoc->ipsa_lock);
25039c2c14abSThejaswini Singarajipura 		peer_assoc->ipsa_state = IPSA_STATE_DEAD;
2504bd670b35SErik Nordmark 		(void) sadb_torch_assoc(inbound_bucket, peer_assoc);
25059c2c14abSThejaswini Singarajipura 	}
25069c2c14abSThejaswini Singarajipura 	mutex_exit(&inbound_bucket->isaf_lock);
25079c2c14abSThejaswini Singarajipura }
25089c2c14abSThejaswini Singarajipura 
25095d3b8cb7SBill Sommerfeld static int
sadb_delpair_state(mblk_t * mp,keysock_in_t * ksi,sadbp_t * spp,int * diagnostic,queue_t * pfkey_q)25105d3b8cb7SBill Sommerfeld sadb_delpair_state(mblk_t *mp, keysock_in_t *ksi, sadbp_t *spp,
25115d3b8cb7SBill Sommerfeld     int *diagnostic, queue_t *pfkey_q)
25127c478bd9Sstevel@tonic-gate {
25137c478bd9Sstevel@tonic-gate 	sadb_sa_t *assoc = (sadb_sa_t *)ksi->ks_in_extv[SADB_EXT_SA];
25149c2c14abSThejaswini Singarajipura 	struct sadb_purge_state ps;
25155d3b8cb7SBill Sommerfeld 	int error;
25169c2c14abSThejaswini Singarajipura 
25175d3b8cb7SBill Sommerfeld 	ps.sq.spp = spp;		/* XXX param */
25185d3b8cb7SBill Sommerfeld 
25195d3b8cb7SBill Sommerfeld 	error = sadb_form_query(ksi, IPSA_Q_DST|IPSA_Q_SRC,
25205d3b8cb7SBill Sommerfeld 	    IPSA_Q_SRC|IPSA_Q_DST|IPSA_Q_SRCID|IPSA_Q_DSTID|IPSA_Q_KMC,
25215d3b8cb7SBill Sommerfeld 	    &ps.sq, diagnostic);
25225d3b8cb7SBill Sommerfeld 	if (error != 0)
25235d3b8cb7SBill Sommerfeld 		return (error);
25245d3b8cb7SBill Sommerfeld 
25259c2c14abSThejaswini Singarajipura 	ps.inbnd = B_FALSE;
25269c2c14abSThejaswini Singarajipura 	ps.sadb_sa_state = assoc->sadb_sa_state;
25275d3b8cb7SBill Sommerfeld 	sadb_walker(ps.sq.sp->sdb_of, ps.sq.sp->sdb_hashsize,
25285d3b8cb7SBill Sommerfeld 	    sadb_delpair_state_one, &ps);
25299c2c14abSThejaswini Singarajipura 
25309c2c14abSThejaswini Singarajipura 	ASSERT(mp->b_cont != NULL);
25319c2c14abSThejaswini Singarajipura 	sadb_pfkey_echo(pfkey_q, mp, (sadb_msg_t *)mp->b_cont->b_rptr,
25329c2c14abSThejaswini Singarajipura 	    ksi, NULL);
25339c2c14abSThejaswini Singarajipura 	return (0);
25349c2c14abSThejaswini Singarajipura }
25359c2c14abSThejaswini Singarajipura 
25365d3b8cb7SBill Sommerfeld /*
25375d3b8cb7SBill Sommerfeld  * Common code to delete/get an SA.
25385d3b8cb7SBill Sommerfeld  */
25395d3b8cb7SBill Sommerfeld 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)25405d3b8cb7SBill Sommerfeld sadb_delget_sa(mblk_t *mp, keysock_in_t *ksi, sadbp_t *spp,
25415d3b8cb7SBill Sommerfeld     int *diagnostic, queue_t *pfkey_q, uint8_t sadb_msg_type)
25425d3b8cb7SBill Sommerfeld {
25435d3b8cb7SBill Sommerfeld 	ipsa_query_t sq;
25445d3b8cb7SBill Sommerfeld 	ipsa_t *echo_target = NULL;
25455d3b8cb7SBill Sommerfeld 	ipsap_t ipsapp;
25465d3b8cb7SBill Sommerfeld 	uint_t	error = 0;
25475d3b8cb7SBill Sommerfeld 
25485d3b8cb7SBill Sommerfeld 	if (sadb_msg_type == SADB_X_DELPAIR_STATE)
25495d3b8cb7SBill Sommerfeld 		return (sadb_delpair_state(mp, ksi, spp, diagnostic, pfkey_q));
25505d3b8cb7SBill Sommerfeld 
25515d3b8cb7SBill Sommerfeld 	sq.spp = spp;		/* XXX param */
25525d3b8cb7SBill Sommerfeld 	error = sadb_form_query(ksi, IPSA_Q_DST|IPSA_Q_SA,
25535d3b8cb7SBill Sommerfeld 	    IPSA_Q_SRC|IPSA_Q_DST|IPSA_Q_SA|IPSA_Q_INBOUND|IPSA_Q_OUTBOUND,
25545d3b8cb7SBill Sommerfeld 	    &sq, diagnostic);
25555d3b8cb7SBill Sommerfeld 	if (error != 0)
25565d3b8cb7SBill Sommerfeld 		return (error);
25575d3b8cb7SBill Sommerfeld 
25585d3b8cb7SBill Sommerfeld 	error = get_ipsa_pair(&sq, &ipsapp, diagnostic);
25595d3b8cb7SBill Sommerfeld 	if (error != 0) {
25605d3b8cb7SBill Sommerfeld 		return (error);
25619c2c14abSThejaswini Singarajipura 	}
25629c2c14abSThejaswini Singarajipura 
25635d3b8cb7SBill Sommerfeld 	echo_target = ipsapp.ipsap_sa_ptr;
256438d95a78Smarkfen 	if (echo_target == NULL)
25655d3b8cb7SBill Sommerfeld 		echo_target = ipsapp.ipsap_psa_ptr;
256638d95a78Smarkfen 
256738d95a78Smarkfen 	if (sadb_msg_type == SADB_DELETE || sadb_msg_type == SADB_X_DELPAIR) {
256838d95a78Smarkfen 		/*
256938d95a78Smarkfen 		 * Bucket locks will be required if SA is actually unlinked.
257038d95a78Smarkfen 		 * get_ipsa_pair() returns valid hash bucket pointers even
2571a1ba8781SMark Fenwick 		 * if it can't find a pair SA pointer. To prevent a potential
2572a1ba8781SMark Fenwick 		 * deadlock, always lock the outbound bucket before the inbound.
257338d95a78Smarkfen 		 */
25745d3b8cb7SBill Sommerfeld 		if (ipsapp.in_inbound_table) {
25755d3b8cb7SBill Sommerfeld 			mutex_enter(&ipsapp.ipsap_pbucket->isaf_lock);
25765d3b8cb7SBill Sommerfeld 			mutex_enter(&ipsapp.ipsap_bucket->isaf_lock);
2577a1ba8781SMark Fenwick 		} else {
25785d3b8cb7SBill Sommerfeld 			mutex_enter(&ipsapp.ipsap_bucket->isaf_lock);
25795d3b8cb7SBill Sommerfeld 			mutex_enter(&ipsapp.ipsap_pbucket->isaf_lock);
2580a1ba8781SMark Fenwick 		}
258138d95a78Smarkfen 
25825d3b8cb7SBill Sommerfeld 		if (ipsapp.ipsap_sa_ptr != NULL) {
25835d3b8cb7SBill Sommerfeld 			mutex_enter(&ipsapp.ipsap_sa_ptr->ipsa_lock);
25845d3b8cb7SBill Sommerfeld 			if (ipsapp.ipsap_sa_ptr->ipsa_flags & IPSA_F_INBOUND) {
25855d3b8cb7SBill Sommerfeld 				sadb_delete_cluster(ipsapp.ipsap_sa_ptr);
25869c2c14abSThejaswini Singarajipura 			}
25875d3b8cb7SBill Sommerfeld 			ipsapp.ipsap_sa_ptr->ipsa_state = IPSA_STATE_DEAD;
25885d3b8cb7SBill Sommerfeld 			(void) sadb_torch_assoc(ipsapp.ipsap_bucket,
2589bd670b35SErik Nordmark 			    ipsapp.ipsap_sa_ptr);
259038d95a78Smarkfen 			/*
259138d95a78Smarkfen 			 * sadb_torch_assoc() releases the ipsa_lock
259238d95a78Smarkfen 			 * and calls sadb_unlinkassoc() which does a
259338d95a78Smarkfen 			 * IPSA_REFRELE.
259438d95a78Smarkfen 			 */
259538d95a78Smarkfen 		}
25965d3b8cb7SBill Sommerfeld 		if (ipsapp.ipsap_psa_ptr != NULL) {
25975d3b8cb7SBill Sommerfeld 			mutex_enter(&ipsapp.ipsap_psa_ptr->ipsa_lock);
2598fc809ee9SPaul Wernau 			if (sadb_msg_type == SADB_X_DELPAIR ||
25995d3b8cb7SBill Sommerfeld 			    ipsapp.ipsap_psa_ptr->ipsa_haspeer) {
26005d3b8cb7SBill Sommerfeld 				if (ipsapp.ipsap_psa_ptr->ipsa_flags &
26019c2c14abSThejaswini Singarajipura 				    IPSA_F_INBOUND) {
26025d3b8cb7SBill Sommerfeld 					sadb_delete_cluster
26035d3b8cb7SBill Sommerfeld 					    (ipsapp.ipsap_psa_ptr);
26049c2c14abSThejaswini Singarajipura 				}
26055d3b8cb7SBill Sommerfeld 				ipsapp.ipsap_psa_ptr->ipsa_state =
260638d95a78Smarkfen 				    IPSA_STATE_DEAD;
26075d3b8cb7SBill Sommerfeld 				(void) sadb_torch_assoc(ipsapp.ipsap_pbucket,
2608bd670b35SErik Nordmark 				    ipsapp.ipsap_psa_ptr);
260938d95a78Smarkfen 			} else {
261038d95a78Smarkfen 				/*
261138d95a78Smarkfen 				 * Only half of the "pair" has been deleted.
261238d95a78Smarkfen 				 * Update the remaining SA and remove references
261338d95a78Smarkfen 				 * to its pair SA, which is now gone.
261438d95a78Smarkfen 				 */
26155d3b8cb7SBill Sommerfeld 				ipsapp.ipsap_psa_ptr->ipsa_otherspi = 0;
26165d3b8cb7SBill Sommerfeld 				ipsapp.ipsap_psa_ptr->ipsa_flags &=
261738d95a78Smarkfen 				    ~IPSA_F_PAIRED;
26185d3b8cb7SBill Sommerfeld 				mutex_exit(&ipsapp.ipsap_psa_ptr->ipsa_lock);
261938d95a78Smarkfen 			}
262038d95a78Smarkfen 		} else if (sadb_msg_type == SADB_X_DELPAIR) {
262138d95a78Smarkfen 			*diagnostic = SADB_X_DIAGNOSTIC_PAIR_SA_NOTFOUND;
262238d95a78Smarkfen 			error = ESRCH;
262338d95a78Smarkfen 		}
26245d3b8cb7SBill Sommerfeld 		mutex_exit(&ipsapp.ipsap_bucket->isaf_lock);
26255d3b8cb7SBill Sommerfeld 		mutex_exit(&ipsapp.ipsap_pbucket->isaf_lock);
262638d95a78Smarkfen 	}
262738d95a78Smarkfen 
262838d95a78Smarkfen 	ASSERT(mp->b_cont != NULL);
262938d95a78Smarkfen 
263038d95a78Smarkfen 	if (error == 0)
263138d95a78Smarkfen 		sadb_pfkey_echo(pfkey_q, mp, (sadb_msg_t *)
263238d95a78Smarkfen 		    mp->b_cont->b_rptr, ksi, echo_target);
263338d95a78Smarkfen 
26345d3b8cb7SBill Sommerfeld 	destroy_ipsa_pair(&ipsapp);
263538d95a78Smarkfen 
263638d95a78Smarkfen 	return (error);
263738d95a78Smarkfen }
263838d95a78Smarkfen 
263938d95a78Smarkfen /*
264038d95a78Smarkfen  * This function takes a sadb_sa_t and finds the ipsa_t structure
264138d95a78Smarkfen  * and the isaf_t (hash bucket) that its stored under. If the security
264238d95a78Smarkfen  * association has a peer, the ipsa_t structure and bucket for that security
264338d95a78Smarkfen  * association are also searched for. The "pair" of ipsa_t's and isaf_t's
264438d95a78Smarkfen  * are returned as a ipsap_t.
264538d95a78Smarkfen  *
2646a1ba8781SMark Fenwick  * The hash buckets are returned for convenience, if the calling function
2647a1ba8781SMark Fenwick  * needs to use the hash bucket locks, say to remove the SA's, it should
2648a1ba8781SMark Fenwick  * take care to observe the convention of locking outbound bucket then
2649a1ba8781SMark Fenwick  * inbound bucket. The flag in_inbound_table provides direction.
2650a1ba8781SMark Fenwick  *
265138d95a78Smarkfen  * Note that a "pair" is defined as one (but not both) of the following:
265238d95a78Smarkfen  *
265338d95a78Smarkfen  * A security association which has a soft reference to another security
265438d95a78Smarkfen  * association via its SPI.
265538d95a78Smarkfen  *
265638d95a78Smarkfen  * A security association that is not obviously "inbound" or "outbound" so
265738d95a78Smarkfen  * it appears in both hash tables, the "peer" being the same security
265838d95a78Smarkfen  * association in the other hash table.
265938d95a78Smarkfen  *
266038d95a78Smarkfen  * This function will return NULL if the ipsa_t can't be found in the
266138d95a78Smarkfen  * inbound or outbound  hash tables (not found). If only one ipsa_t is
266238d95a78Smarkfen  * found, the pair ipsa_t will be NULL. Both isaf_t values are valid
266338d95a78Smarkfen  * provided at least one ipsa_t is found.
266438d95a78Smarkfen  */
26655d3b8cb7SBill Sommerfeld static int
get_ipsa_pair(ipsa_query_t * sq,ipsap_t * ipsapp,int * diagnostic)26665d3b8cb7SBill Sommerfeld get_ipsa_pair(ipsa_query_t *sq, ipsap_t *ipsapp, int *diagnostic)
266738d95a78Smarkfen {
266838d95a78Smarkfen 	uint32_t pair_srcaddr[IPSA_MAX_ADDRLEN];
266938d95a78Smarkfen 	uint32_t pair_dstaddr[IPSA_MAX_ADDRLEN];
267038d95a78Smarkfen 	uint32_t pair_spi;
267138d95a78Smarkfen 
26725d3b8cb7SBill Sommerfeld 	init_ipsa_pair(ipsapp);
267338d95a78Smarkfen 
2674a1ba8781SMark Fenwick 	ipsapp->in_inbound_table = B_FALSE;
2675a1ba8781SMark Fenwick 
26767c478bd9Sstevel@tonic-gate 	/* Lock down both buckets. */
26775d3b8cb7SBill Sommerfeld 	mutex_enter(&sq->outbound->isaf_lock);
26785d3b8cb7SBill Sommerfeld 	mutex_enter(&sq->inbound->isaf_lock);
26797c478bd9Sstevel@tonic-gate 
26805d3b8cb7SBill Sommerfeld 	if (sq->assoc->sadb_sa_flags & IPSA_F_INBOUND) {
26815d3b8cb7SBill Sommerfeld 		ipsapp->ipsap_sa_ptr = ipsec_getassocbyspi(sq->inbound,
26825d3b8cb7SBill Sommerfeld 		    sq->assoc->sadb_sa_spi, sq->srcaddr, sq->dstaddr, sq->af);
268338d95a78Smarkfen 		if (ipsapp->ipsap_sa_ptr != NULL) {
26845d3b8cb7SBill Sommerfeld 			ipsapp->ipsap_bucket = sq->inbound;
26855d3b8cb7SBill Sommerfeld 			ipsapp->ipsap_pbucket = sq->outbound;
2686a1ba8781SMark Fenwick 			ipsapp->in_inbound_table = B_TRUE;
26877c478bd9Sstevel@tonic-gate 		} else {
26885d3b8cb7SBill Sommerfeld 			ipsapp->ipsap_sa_ptr = ipsec_getassocbyspi(sq->outbound,
26895d3b8cb7SBill Sommerfeld 			    sq->assoc->sadb_sa_spi, sq->srcaddr, sq->dstaddr,
26905d3b8cb7SBill Sommerfeld 			    sq->af);
26915d3b8cb7SBill Sommerfeld 			ipsapp->ipsap_bucket = sq->outbound;
26925d3b8cb7SBill Sommerfeld 			ipsapp->ipsap_pbucket = sq->inbound;
26937c478bd9Sstevel@tonic-gate 		}
269438d95a78Smarkfen 	} else {
269538d95a78Smarkfen 		/* IPSA_F_OUTBOUND is set *or* no directions flags set. */
269638d95a78Smarkfen 		ipsapp->ipsap_sa_ptr =
26975d3b8cb7SBill Sommerfeld 		    ipsec_getassocbyspi(sq->outbound,
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->outbound;
27015d3b8cb7SBill Sommerfeld 			ipsapp->ipsap_pbucket = sq->inbound;
270238d95a78Smarkfen 		} else {
27035d3b8cb7SBill Sommerfeld 			ipsapp->ipsap_sa_ptr = ipsec_getassocbyspi(sq->inbound,
27045d3b8cb7SBill Sommerfeld 			    sq->assoc->sadb_sa_spi, sq->srcaddr, sq->dstaddr,
27055d3b8cb7SBill Sommerfeld 			    sq->af);
27065d3b8cb7SBill Sommerfeld 			ipsapp->ipsap_bucket = sq->inbound;
27075d3b8cb7SBill Sommerfeld 			ipsapp->ipsap_pbucket = sq->outbound;
270838d95a78Smarkfen 			if (ipsapp->ipsap_sa_ptr != NULL)
2709a1ba8781SMark Fenwick 				ipsapp->in_inbound_table = B_TRUE;
27107c478bd9Sstevel@tonic-gate 		}
27117c478bd9Sstevel@tonic-gate 	}
27127c478bd9Sstevel@tonic-gate 
271338d95a78Smarkfen 	if (ipsapp->ipsap_sa_ptr == NULL) {
27145d3b8cb7SBill Sommerfeld 		mutex_exit(&sq->outbound->isaf_lock);
27155d3b8cb7SBill Sommerfeld 		mutex_exit(&sq->inbound->isaf_lock);
27165d3b8cb7SBill Sommerfeld 		*diagnostic = SADB_X_DIAGNOSTIC_SA_NOTFOUND;
27175d3b8cb7SBill Sommerfeld 		return (ESRCH);
271838d95a78Smarkfen 	}
27197c478bd9Sstevel@tonic-gate 
272038d95a78Smarkfen 	if ((ipsapp->ipsap_sa_ptr->ipsa_state == IPSA_STATE_LARVAL) &&
2721a1ba8781SMark Fenwick 	    ipsapp->in_inbound_table) {
27225d3b8cb7SBill Sommerfeld 		mutex_exit(&sq->outbound->isaf_lock);
27235d3b8cb7SBill Sommerfeld 		mutex_exit(&sq->inbound->isaf_lock);
27245d3b8cb7SBill Sommerfeld 		return (0);
272538d95a78Smarkfen 	}
27267c478bd9Sstevel@tonic-gate 
272738d95a78Smarkfen 	mutex_enter(&ipsapp->ipsap_sa_ptr->ipsa_lock);
272838d95a78Smarkfen 	if (ipsapp->ipsap_sa_ptr->ipsa_haspeer) {
27297c478bd9Sstevel@tonic-gate 		/*
273038d95a78Smarkfen 		 * haspeer implies no sa_pairing, look for same spi
273138d95a78Smarkfen 		 * in other hashtable.
27327c478bd9Sstevel@tonic-gate 		 */
273338d95a78Smarkfen 		ipsapp->ipsap_psa_ptr =
273438d95a78Smarkfen 		    ipsec_getassocbyspi(ipsapp->ipsap_pbucket,
27355d3b8cb7SBill Sommerfeld 		    sq->assoc->sadb_sa_spi, sq->srcaddr, sq->dstaddr, sq->af);
273638d95a78Smarkfen 		mutex_exit(&ipsapp->ipsap_sa_ptr->ipsa_lock);
27375d3b8cb7SBill Sommerfeld 		mutex_exit(&sq->outbound->isaf_lock);
27385d3b8cb7SBill Sommerfeld 		mutex_exit(&sq->inbound->isaf_lock);
27395d3b8cb7SBill Sommerfeld 		return (0);
27407c478bd9Sstevel@tonic-gate 	}
274138d95a78Smarkfen 	pair_spi = ipsapp->ipsap_sa_ptr->ipsa_otherspi;
274238d95a78Smarkfen 	IPSA_COPY_ADDR(&pair_srcaddr,
27435d3b8cb7SBill Sommerfeld 	    ipsapp->ipsap_sa_ptr->ipsa_srcaddr, sq->af);
274438d95a78Smarkfen 	IPSA_COPY_ADDR(&pair_dstaddr,
27455d3b8cb7SBill Sommerfeld 	    ipsapp->ipsap_sa_ptr->ipsa_dstaddr, sq->af);
274638d95a78Smarkfen 	mutex_exit(&ipsapp->ipsap_sa_ptr->ipsa_lock);
27475d3b8cb7SBill Sommerfeld 	mutex_exit(&sq->inbound->isaf_lock);
27485d3b8cb7SBill Sommerfeld 	mutex_exit(&sq->outbound->isaf_lock);
274938d95a78Smarkfen 
275038d95a78Smarkfen 	if (pair_spi == 0) {
275138d95a78Smarkfen 		ASSERT(ipsapp->ipsap_bucket != NULL);
275238d95a78Smarkfen 		ASSERT(ipsapp->ipsap_pbucket != NULL);
27535d3b8cb7SBill Sommerfeld 		return (0);
27547c478bd9Sstevel@tonic-gate 	}
27557c478bd9Sstevel@tonic-gate 
275638d95a78Smarkfen 	/* found sa in outbound sadb, peer should be inbound */
275738d95a78Smarkfen 
2758a1ba8781SMark Fenwick 	if (ipsapp->in_inbound_table) {
275938d95a78Smarkfen 		/* Found SA in inbound table, pair will be in outbound. */
27605d3b8cb7SBill Sommerfeld 		if (sq->af == AF_INET6) {
27615d3b8cb7SBill Sommerfeld 			ipsapp->ipsap_pbucket = OUTBOUND_BUCKET_V6(sq->sp,
276238d95a78Smarkfen 			    *(uint32_t *)pair_srcaddr);
276338d95a78Smarkfen 		} else {
27645d3b8cb7SBill Sommerfeld 			ipsapp->ipsap_pbucket = OUTBOUND_BUCKET_V4(sq->sp,
276538d95a78Smarkfen 			    *(uint32_t *)pair_srcaddr);
276638d95a78Smarkfen 		}
276738d95a78Smarkfen 	} else {
27685d3b8cb7SBill Sommerfeld 		ipsapp->ipsap_pbucket = INBOUND_BUCKET(sq->sp, pair_spi);
276938d95a78Smarkfen 	}
277038d95a78Smarkfen 	mutex_enter(&ipsapp->ipsap_pbucket->isaf_lock);
277138d95a78Smarkfen 	ipsapp->ipsap_psa_ptr = ipsec_getassocbyspi(ipsapp->ipsap_pbucket,
27725d3b8cb7SBill Sommerfeld 	    pair_spi, pair_dstaddr, pair_srcaddr, sq->af);
277338d95a78Smarkfen 	mutex_exit(&ipsapp->ipsap_pbucket->isaf_lock);
277438d95a78Smarkfen 	ASSERT(ipsapp->ipsap_bucket != NULL);
277538d95a78Smarkfen 	ASSERT(ipsapp->ipsap_pbucket != NULL);
27765d3b8cb7SBill Sommerfeld 	return (0);
27777c478bd9Sstevel@tonic-gate }
27787c478bd9Sstevel@tonic-gate 
27797c478bd9Sstevel@tonic-gate /*
27808810c16bSdanmcd  * Perform NAT-traversal cached checksum offset calculations here.
27817c478bd9Sstevel@tonic-gate  */
27828810c16bSdanmcd 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)27838810c16bSdanmcd sadb_nat_calculations(ipsa_t *newbie, sadb_address_t *natt_loc_ext,
27848810c16bSdanmcd     sadb_address_t *natt_rem_ext, uint32_t *src_addr_ptr,
27858810c16bSdanmcd     uint32_t *dst_addr_ptr)
27867c478bd9Sstevel@tonic-gate {
27878810c16bSdanmcd 	struct sockaddr_in *natt_loc, *natt_rem;
27887c478bd9Sstevel@tonic-gate 	uint32_t *natt_loc_ptr = NULL, *natt_rem_ptr = NULL;
27897c478bd9Sstevel@tonic-gate 	uint32_t running_sum = 0;
27907c478bd9Sstevel@tonic-gate 
27917c478bd9Sstevel@tonic-gate #define	DOWN_SUM(x) (x) = ((x) & 0xFFFF) +	 ((x) >> 16)
27927c478bd9Sstevel@tonic-gate 
27937c478bd9Sstevel@tonic-gate 	if (natt_rem_ext != NULL) {
27947c478bd9Sstevel@tonic-gate 		uint32_t l_src;
27957c478bd9Sstevel@tonic-gate 		uint32_t l_rem;
27967c478bd9Sstevel@tonic-gate 
27977c478bd9Sstevel@tonic-gate 		natt_rem = (struct sockaddr_in *)(natt_rem_ext + 1);
27987c478bd9Sstevel@tonic-gate 
27998810c16bSdanmcd 		/* Ensured by sadb_addrfix(). */
28008810c16bSdanmcd 		ASSERT(natt_rem->sin_family == AF_INET);
28018810c16bSdanmcd 
28027c478bd9Sstevel@tonic-gate 		natt_rem_ptr = (uint32_t *)(&natt_rem->sin_addr);
2803437220cdSdanmcd 		newbie->ipsa_remote_nat_port = natt_rem->sin_port;
28047c478bd9Sstevel@tonic-gate 		l_src = *src_addr_ptr;
28057c478bd9Sstevel@tonic-gate 		l_rem = *natt_rem_ptr;
28067c478bd9Sstevel@tonic-gate 
28078810c16bSdanmcd 		/* Instead of IPSA_COPY_ADDR(), just copy first 32 bits. */
2808437220cdSdanmcd 		newbie->ipsa_natt_addr_rem = *natt_rem_ptr;
28097c478bd9Sstevel@tonic-gate 
28107c478bd9Sstevel@tonic-gate 		l_src = ntohl(l_src);
28117c478bd9Sstevel@tonic-gate 		DOWN_SUM(l_src);
28127c478bd9Sstevel@tonic-gate 		DOWN_SUM(l_src);
28137c478bd9Sstevel@tonic-gate 		l_rem = ntohl(l_rem);
28147c478bd9Sstevel@tonic-gate 		DOWN_SUM(l_rem);
28157c478bd9Sstevel@tonic-gate 		DOWN_SUM(l_rem);
28167c478bd9Sstevel@tonic-gate 
28177c478bd9Sstevel@tonic-gate 		/*
28187c478bd9Sstevel@tonic-gate 		 * We're 1's complement for checksums, so check for wraparound
28197c478bd9Sstevel@tonic-gate 		 * here.
28207c478bd9Sstevel@tonic-gate 		 */
28217c478bd9Sstevel@tonic-gate 		if (l_rem > l_src)
28227c478bd9Sstevel@tonic-gate 			l_src--;
28237c478bd9Sstevel@tonic-gate 
28247c478bd9Sstevel@tonic-gate 		running_sum += l_src - l_rem;
28257c478bd9Sstevel@tonic-gate 
28267c478bd9Sstevel@tonic-gate 		DOWN_SUM(running_sum);
28277c478bd9Sstevel@tonic-gate 		DOWN_SUM(running_sum);
28287c478bd9Sstevel@tonic-gate 	}
28297c478bd9Sstevel@tonic-gate 
28307c478bd9Sstevel@tonic-gate 	if (natt_loc_ext != NULL) {
28317c478bd9Sstevel@tonic-gate 		natt_loc = (struct sockaddr_in *)(natt_loc_ext + 1);
28327c478bd9Sstevel@tonic-gate 
28338810c16bSdanmcd 		/* Ensured by sadb_addrfix(). */
28348810c16bSdanmcd 		ASSERT(natt_loc->sin_family == AF_INET);
28358810c16bSdanmcd 
2836437220cdSdanmcd 		natt_loc_ptr = (uint32_t *)(&natt_loc->sin_addr);
2837437220cdSdanmcd 		newbie->ipsa_local_nat_port = natt_loc->sin_port;
28387c478bd9Sstevel@tonic-gate 
28398810c16bSdanmcd 		/* Instead of IPSA_COPY_ADDR(), just copy first 32 bits. */
2840437220cdSdanmcd 		newbie->ipsa_natt_addr_loc = *natt_loc_ptr;
28417c478bd9Sstevel@tonic-gate 
2842437220cdSdanmcd 		/*
2843437220cdSdanmcd 		 * NAT-T port agility means we may have natt_loc_ext, but
2844437220cdSdanmcd 		 * only for a local-port change.
2845437220cdSdanmcd 		 */
2846437220cdSdanmcd 		if (natt_loc->sin_addr.s_addr != INADDR_ANY) {
2847437220cdSdanmcd 			uint32_t l_dst = ntohl(*dst_addr_ptr);
2848437220cdSdanmcd 			uint32_t l_loc = ntohl(*natt_loc_ptr);
2849437220cdSdanmcd 
28507c478bd9Sstevel@tonic-gate 			DOWN_SUM(l_loc);
28517c478bd9Sstevel@tonic-gate 			DOWN_SUM(l_loc);
28527c478bd9Sstevel@tonic-gate 			DOWN_SUM(l_dst);
28537c478bd9Sstevel@tonic-gate 			DOWN_SUM(l_dst);
28547c478bd9Sstevel@tonic-gate 
28557c478bd9Sstevel@tonic-gate 			/*
2856437220cdSdanmcd 			 * We're 1's complement for checksums, so check for
2857437220cdSdanmcd 			 * wraparound here.
28587c478bd9Sstevel@tonic-gate 			 */
28597c478bd9Sstevel@tonic-gate 			if (l_loc > l_dst)
28607c478bd9Sstevel@tonic-gate 				l_dst--;
28617c478bd9Sstevel@tonic-gate 
28627c478bd9Sstevel@tonic-gate 			running_sum += l_dst - l_loc;
28637c478bd9Sstevel@tonic-gate 			DOWN_SUM(running_sum);
28647c478bd9Sstevel@tonic-gate 			DOWN_SUM(running_sum);
28657c478bd9Sstevel@tonic-gate 		}
2866437220cdSdanmcd 	}
28677c478bd9Sstevel@tonic-gate 
28687c478bd9Sstevel@tonic-gate 	newbie->ipsa_inbound_cksum = running_sum;
28697c478bd9Sstevel@tonic-gate #undef DOWN_SUM
28708810c16bSdanmcd }
28718810c16bSdanmcd 
28728810c16bSdanmcd /*
28738810c16bSdanmcd  * This function is called from consumers that need to insert a fully-grown
28748810c16bSdanmcd  * security association into its tables.  This function takes into account that
28758810c16bSdanmcd  * SAs can be "inbound", "outbound", or "both".	 The "primary" and "secondary"
28768810c16bSdanmcd  * hash bucket parameters are set in order of what the SA will be most of the
28778810c16bSdanmcd  * time.  (For example, an SA with an unspecified source, and a multicast
28788810c16bSdanmcd  * destination will primarily be an outbound SA.  OTOH, if that destination
28798810c16bSdanmcd  * is unicast for this node, then the SA will primarily be inbound.)
28808810c16bSdanmcd  *
28818810c16bSdanmcd  * It takes a lot of parameters because even if clone is B_FALSE, this needs
28828810c16bSdanmcd  * to check both buckets for purposes of collision.
28838810c16bSdanmcd  *
28848810c16bSdanmcd  * Return 0 upon success.  Return various errnos (ENOMEM, EEXIST) for
28858810c16bSdanmcd  * various error conditions.  We may need to set samsg->sadb_x_msg_diagnostic
28868810c16bSdanmcd  * with additional diagnostic information because there is at least one EINVAL
28878810c16bSdanmcd  * case here.
28888810c16bSdanmcd  */
28898810c16bSdanmcd 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)2890bd670b35SErik Nordmark sadb_common_add(queue_t *pfkey_q, mblk_t *mp, sadb_msg_t *samsg,
28918810c16bSdanmcd     keysock_in_t *ksi, isaf_t *primary, isaf_t *secondary,
2892f4b3ec61Sdh155122     ipsa_t *newbie, boolean_t clone, boolean_t is_inbound, int *diagnostic,
289338d95a78Smarkfen     netstack_t *ns, sadbp_t *spp)
28948810c16bSdanmcd {
28958810c16bSdanmcd 	ipsa_t *newbie_clone = NULL, *scratch;
28965d3b8cb7SBill Sommerfeld 	ipsap_t ipsapp;
28978810c16bSdanmcd 	sadb_sa_t *assoc = (sadb_sa_t *)ksi->ks_in_extv[SADB_EXT_SA];
28988810c16bSdanmcd 	sadb_address_t *srcext =
28998810c16bSdanmcd 	    (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_SRC];
29008810c16bSdanmcd 	sadb_address_t *dstext =
29018810c16bSdanmcd 	    (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_DST];
29028810c16bSdanmcd 	sadb_address_t *isrcext =
29038810c16bSdanmcd 	    (sadb_address_t *)ksi->ks_in_extv[SADB_X_EXT_ADDRESS_INNER_SRC];
29048810c16bSdanmcd 	sadb_address_t *idstext =
29058810c16bSdanmcd 	    (sadb_address_t *)ksi->ks_in_extv[SADB_X_EXT_ADDRESS_INNER_DST];
29068810c16bSdanmcd 	sadb_x_kmc_t *kmcext =
29078810c16bSdanmcd 	    (sadb_x_kmc_t *)ksi->ks_in_extv[SADB_X_EXT_KM_COOKIE];
29088810c16bSdanmcd 	sadb_key_t *akey = (sadb_key_t *)ksi->ks_in_extv[SADB_EXT_KEY_AUTH];
29098810c16bSdanmcd 	sadb_key_t *ekey = (sadb_key_t *)ksi->ks_in_extv[SADB_EXT_KEY_ENCRYPT];
29105d3b8cb7SBill Sommerfeld 	sadb_sens_t *sens =
29115d3b8cb7SBill Sommerfeld 	    (sadb_sens_t *)ksi->ks_in_extv[SADB_EXT_SENSITIVITY];
29125d3b8cb7SBill Sommerfeld 	sadb_sens_t *osens =
29135d3b8cb7SBill Sommerfeld 	    (sadb_sens_t *)ksi->ks_in_extv[SADB_X_EXT_OUTER_SENS];
291438d95a78Smarkfen 	sadb_x_pair_t *pair_ext =
291538d95a78Smarkfen 	    (sadb_x_pair_t *)ksi->ks_in_extv[SADB_X_EXT_PAIR];
29169c2c14abSThejaswini Singarajipura 	sadb_x_replay_ctr_t *replayext =
29179c2c14abSThejaswini Singarajipura 	    (sadb_x_replay_ctr_t *)ksi->ks_in_extv[SADB_X_EXT_REPLAY_VALUE];
29189c2c14abSThejaswini Singarajipura 	uint8_t protocol =
29199c2c14abSThejaswini Singarajipura 	    (samsg->sadb_msg_satype == SADB_SATYPE_AH) ? IPPROTO_AH:IPPROTO_ESP;
2920628b0c67SMark Fenwick 	int salt_offset;
2921628b0c67SMark Fenwick 	uint8_t *buf_ptr;
29228810c16bSdanmcd 	struct sockaddr_in *src, *dst, *isrc, *idst;
29238810c16bSdanmcd 	struct sockaddr_in6 *src6, *dst6, *isrc6, *idst6;
29248810c16bSdanmcd 	sadb_lifetime_t *soft =
29258810c16bSdanmcd 	    (sadb_lifetime_t *)ksi->ks_in_extv[SADB_EXT_LIFETIME_SOFT];
29268810c16bSdanmcd 	sadb_lifetime_t *hard =
29278810c16bSdanmcd 	    (sadb_lifetime_t *)ksi->ks_in_extv[SADB_EXT_LIFETIME_HARD];
29289c2c14abSThejaswini Singarajipura 	sadb_lifetime_t	*idle =
29299c2c14abSThejaswini Singarajipura 	    (sadb_lifetime_t *)ksi->ks_in_extv[SADB_X_EXT_LIFETIME_IDLE];
29308810c16bSdanmcd 	sa_family_t af;
29318810c16bSdanmcd 	int error = 0;
29328810c16bSdanmcd 	boolean_t isupdate = (newbie != NULL);
29338810c16bSdanmcd 	uint32_t *src_addr_ptr, *dst_addr_ptr, *isrc_addr_ptr, *idst_addr_ptr;
2934f4b3ec61Sdh155122 	ipsec_stack_t	*ipss = ns->netstack_ipsec;
29355d3b8cb7SBill Sommerfeld 	ip_stack_t 	*ipst = ns->netstack_ip;
2936628b0c67SMark Fenwick 	ipsec_alginfo_t *alg;
29379c2c14abSThejaswini Singarajipura 	int		rcode;
2938bd670b35SErik Nordmark 	boolean_t	async = B_FALSE;
29398810c16bSdanmcd 
29405d3b8cb7SBill Sommerfeld 	init_ipsa_pair(&ipsapp);
29415d3b8cb7SBill Sommerfeld 
294238d95a78Smarkfen 	if (srcext == NULL) {
294338d95a78Smarkfen 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_SRC;
294438d95a78Smarkfen 		return (EINVAL);
294538d95a78Smarkfen 	}
294638d95a78Smarkfen 	if (dstext == NULL) {
294738d95a78Smarkfen 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_DST;
294838d95a78Smarkfen 		return (EINVAL);
294938d95a78Smarkfen 	}
295038d95a78Smarkfen 	if (assoc == NULL) {
295138d95a78Smarkfen 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_SA;
295238d95a78Smarkfen 		return (EINVAL);
295338d95a78Smarkfen 	}
295438d95a78Smarkfen 
29558810c16bSdanmcd 	src = (struct sockaddr_in *)(srcext + 1);
29568810c16bSdanmcd 	src6 = (struct sockaddr_in6 *)(srcext + 1);
29578810c16bSdanmcd 	dst = (struct sockaddr_in *)(dstext + 1);
29588810c16bSdanmcd 	dst6 = (struct sockaddr_in6 *)(dstext + 1);
29598810c16bSdanmcd 	if (isrcext != NULL) {
29608810c16bSdanmcd 		isrc = (struct sockaddr_in *)(isrcext + 1);
29618810c16bSdanmcd 		isrc6 = (struct sockaddr_in6 *)(isrcext + 1);
29628810c16bSdanmcd 		ASSERT(idstext != NULL);
29638810c16bSdanmcd 		idst = (struct sockaddr_in *)(idstext + 1);
29648810c16bSdanmcd 		idst6 = (struct sockaddr_in6 *)(idstext + 1);
29658810c16bSdanmcd 	} else {
29668810c16bSdanmcd 		isrc = NULL;
29678810c16bSdanmcd 		isrc6 = NULL;
29688810c16bSdanmcd 	}
29698810c16bSdanmcd 
29708810c16bSdanmcd 	af = src->sin_family;
29718810c16bSdanmcd 
29728810c16bSdanmcd 	if (af == AF_INET) {
29738810c16bSdanmcd 		src_addr_ptr = (uint32_t *)&src->sin_addr;
29748810c16bSdanmcd 		dst_addr_ptr = (uint32_t *)&dst->sin_addr;
29758810c16bSdanmcd 	} else {
29768810c16bSdanmcd 		ASSERT(af == AF_INET6);
29778810c16bSdanmcd 		src_addr_ptr = (uint32_t *)&src6->sin6_addr;
29788810c16bSdanmcd 		dst_addr_ptr = (uint32_t *)&dst6->sin6_addr;
29798810c16bSdanmcd 	}
29808810c16bSdanmcd 
29819c2c14abSThejaswini Singarajipura 	if (!isupdate && (clone == B_TRUE || is_inbound == B_TRUE) &&
29829c2c14abSThejaswini Singarajipura 	    cl_inet_checkspi &&
29839c2c14abSThejaswini Singarajipura 	    (assoc->sadb_sa_state != SADB_X_SASTATE_ACTIVE_ELSEWHERE)) {
29848e4b770fSLu Huafeng 		rcode = cl_inet_checkspi(ns->netstack_stackid, protocol,
29858e4b770fSLu Huafeng 		    assoc->sadb_sa_spi, NULL);
29869c2c14abSThejaswini Singarajipura 		if (rcode == -1) {
29879c2c14abSThejaswini Singarajipura 			return (EEXIST);
29889c2c14abSThejaswini Singarajipura 		}
29899c2c14abSThejaswini Singarajipura 	}
29909c2c14abSThejaswini Singarajipura 
299138d95a78Smarkfen 	/*
299238d95a78Smarkfen 	 * Check to see if the new SA will be cloned AND paired. The
299338d95a78Smarkfen 	 * reason a SA will be cloned is the source or destination addresses
299438d95a78Smarkfen 	 * are not specific enough to determine if the SA goes in the outbound
299538d95a78Smarkfen 	 * or the inbound hash table, so its cloned and put in both. If
299638d95a78Smarkfen 	 * the SA is paired, it's soft linked to another SA for the other
299738d95a78Smarkfen 	 * direction. Keeping track and looking up SA's that are direction
299838d95a78Smarkfen 	 * unspecific and linked is too hard.
299938d95a78Smarkfen 	 */
300038d95a78Smarkfen 	if (clone && (pair_ext != NULL)) {
300138d95a78Smarkfen 		*diagnostic = SADB_X_DIAGNOSTIC_PAIR_INAPPROPRIATE;
300238d95a78Smarkfen 		return (EINVAL);
300338d95a78Smarkfen 	}
300438d95a78Smarkfen 
30058810c16bSdanmcd 	if (!isupdate) {
30068810c16bSdanmcd 		newbie = sadb_makelarvalassoc(assoc->sadb_sa_spi,
3007f4b3ec61Sdh155122 		    src_addr_ptr, dst_addr_ptr, af, ns);
30088810c16bSdanmcd 		if (newbie == NULL)
30098810c16bSdanmcd 			return (ENOMEM);
30108810c16bSdanmcd 	}
30118810c16bSdanmcd 
30128810c16bSdanmcd 	mutex_enter(&newbie->ipsa_lock);
30138810c16bSdanmcd 
30148810c16bSdanmcd 	if (isrc != NULL) {
30158810c16bSdanmcd 		if (isrc->sin_family == AF_INET) {
30168810c16bSdanmcd 			if (srcext->sadb_address_proto != IPPROTO_ENCAP) {
30178810c16bSdanmcd 				if (srcext->sadb_address_proto != 0) {
30188810c16bSdanmcd 					/*
30198810c16bSdanmcd 					 * Mismatched outer-packet protocol
30208810c16bSdanmcd 					 * and inner-packet address family.
30218810c16bSdanmcd 					 */
30228810c16bSdanmcd 					mutex_exit(&newbie->ipsa_lock);
30238810c16bSdanmcd 					error = EPROTOTYPE;
3024a1ba8781SMark Fenwick 					*diagnostic =
3025a1ba8781SMark Fenwick 					    SADB_X_DIAGNOSTIC_INNER_AF_MISMATCH;
30268810c16bSdanmcd 					goto error;
30278810c16bSdanmcd 				} else {
30288810c16bSdanmcd 					/* Fill in with explicit protocol. */
30298810c16bSdanmcd 					srcext->sadb_address_proto =
30308810c16bSdanmcd 					    IPPROTO_ENCAP;
30318810c16bSdanmcd 					dstext->sadb_address_proto =
30328810c16bSdanmcd 					    IPPROTO_ENCAP;
30338810c16bSdanmcd 				}
30348810c16bSdanmcd 			}
30358810c16bSdanmcd 			isrc_addr_ptr = (uint32_t *)&isrc->sin_addr;
30368810c16bSdanmcd 			idst_addr_ptr = (uint32_t *)&idst->sin_addr;
30378810c16bSdanmcd 		} else {
30388810c16bSdanmcd 			ASSERT(isrc->sin_family == AF_INET6);
30398810c16bSdanmcd 			if (srcext->sadb_address_proto != IPPROTO_IPV6) {
30408810c16bSdanmcd 				if (srcext->sadb_address_proto != 0) {
30418810c16bSdanmcd 					/*
30428810c16bSdanmcd 					 * Mismatched outer-packet protocol
30438810c16bSdanmcd 					 * and inner-packet address family.
30448810c16bSdanmcd 					 */
30458810c16bSdanmcd 					mutex_exit(&newbie->ipsa_lock);
30468810c16bSdanmcd 					error = EPROTOTYPE;
3047a1ba8781SMark Fenwick 					*diagnostic =
3048a1ba8781SMark Fenwick 					    SADB_X_DIAGNOSTIC_INNER_AF_MISMATCH;
30498810c16bSdanmcd 					goto error;
30508810c16bSdanmcd 				} else {
30518810c16bSdanmcd 					/* Fill in with explicit protocol. */
30528810c16bSdanmcd 					srcext->sadb_address_proto =
30538810c16bSdanmcd 					    IPPROTO_IPV6;
30548810c16bSdanmcd 					dstext->sadb_address_proto =
30558810c16bSdanmcd 					    IPPROTO_IPV6;
30568810c16bSdanmcd 				}
30578810c16bSdanmcd 			}
30588810c16bSdanmcd 			isrc_addr_ptr = (uint32_t *)&isrc6->sin6_addr;
30598810c16bSdanmcd 			idst_addr_ptr = (uint32_t *)&idst6->sin6_addr;
30608810c16bSdanmcd 		}
30618810c16bSdanmcd 		newbie->ipsa_innerfam = isrc->sin_family;
30628810c16bSdanmcd 
30638810c16bSdanmcd 		IPSA_COPY_ADDR(newbie->ipsa_innersrc, isrc_addr_ptr,
30648810c16bSdanmcd 		    newbie->ipsa_innerfam);
30658810c16bSdanmcd 		IPSA_COPY_ADDR(newbie->ipsa_innerdst, idst_addr_ptr,
30668810c16bSdanmcd 		    newbie->ipsa_innerfam);
30678810c16bSdanmcd 		newbie->ipsa_innersrcpfx = isrcext->sadb_address_prefixlen;
30688810c16bSdanmcd 		newbie->ipsa_innerdstpfx = idstext->sadb_address_prefixlen;
30698810c16bSdanmcd 
30708810c16bSdanmcd 		/* Unique value uses inner-ports for Tunnel Mode... */
30718810c16bSdanmcd 		newbie->ipsa_unique_id = SA_UNIQUE_ID(isrc->sin_port,
30728810c16bSdanmcd 		    idst->sin_port, dstext->sadb_address_proto,
30738810c16bSdanmcd 		    idstext->sadb_address_proto);
30748810c16bSdanmcd 		newbie->ipsa_unique_mask = SA_UNIQUE_MASK(isrc->sin_port,
30758810c16bSdanmcd 		    idst->sin_port, dstext->sadb_address_proto,
30768810c16bSdanmcd 		    idstext->sadb_address_proto);
30778810c16bSdanmcd 	} else {
30788810c16bSdanmcd 		/* ... and outer-ports for Transport Mode. */
30798810c16bSdanmcd 		newbie->ipsa_unique_id = SA_UNIQUE_ID(src->sin_port,
30808810c16bSdanmcd 		    dst->sin_port, dstext->sadb_address_proto, 0);
30818810c16bSdanmcd 		newbie->ipsa_unique_mask = SA_UNIQUE_MASK(src->sin_port,
30828810c16bSdanmcd 		    dst->sin_port, dstext->sadb_address_proto, 0);
30838810c16bSdanmcd 	}
30848810c16bSdanmcd 	if (newbie->ipsa_unique_mask != (uint64_t)0)
30858810c16bSdanmcd 		newbie->ipsa_flags |= IPSA_F_UNIQUE;
30868810c16bSdanmcd 
30878810c16bSdanmcd 	sadb_nat_calculations(newbie,
30888810c16bSdanmcd 	    (sadb_address_t *)ksi->ks_in_extv[SADB_X_EXT_ADDRESS_NATT_LOC],
30898810c16bSdanmcd 	    (sadb_address_t *)ksi->ks_in_extv[SADB_X_EXT_ADDRESS_NATT_REM],
30908810c16bSdanmcd 	    src_addr_ptr, dst_addr_ptr);
30917c478bd9Sstevel@tonic-gate 
30927c478bd9Sstevel@tonic-gate 	newbie->ipsa_type = samsg->sadb_msg_satype;
3093a1ba8781SMark Fenwick 
30949c2c14abSThejaswini Singarajipura 	ASSERT((assoc->sadb_sa_state == SADB_SASTATE_MATURE) ||
30959c2c14abSThejaswini Singarajipura 	    (assoc->sadb_sa_state == SADB_X_SASTATE_ACTIVE_ELSEWHERE));
30967c478bd9Sstevel@tonic-gate 	newbie->ipsa_auth_alg = assoc->sadb_sa_auth;
30977c478bd9Sstevel@tonic-gate 	newbie->ipsa_encr_alg = assoc->sadb_sa_encrypt;
309838d95a78Smarkfen 
30998810c16bSdanmcd 	newbie->ipsa_flags |= assoc->sadb_sa_flags;
3100a1ba8781SMark Fenwick 	if (newbie->ipsa_flags & SADB_X_SAFLAGS_NATT_LOC &&
3101a1ba8781SMark Fenwick 	    ksi->ks_in_extv[SADB_X_EXT_ADDRESS_NATT_LOC] == NULL) {
31028810c16bSdanmcd 		mutex_exit(&newbie->ipsa_lock);
3103a1ba8781SMark Fenwick 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_NATT_LOC;
3104a1ba8781SMark Fenwick 		error = EINVAL;
3105a1ba8781SMark Fenwick 		goto error;
3106a1ba8781SMark Fenwick 	}
3107a1ba8781SMark Fenwick 	if (newbie->ipsa_flags & SADB_X_SAFLAGS_NATT_REM &&
3108a1ba8781SMark Fenwick 	    ksi->ks_in_extv[SADB_X_EXT_ADDRESS_NATT_REM] == NULL) {
3109a1ba8781SMark Fenwick 		mutex_exit(&newbie->ipsa_lock);
3110a1ba8781SMark Fenwick 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_NATT_REM;
3111a1ba8781SMark Fenwick 		error = EINVAL;
3112a1ba8781SMark Fenwick 		goto error;
3113a1ba8781SMark Fenwick 	}
3114a1ba8781SMark Fenwick 	if (newbie->ipsa_flags & SADB_X_SAFLAGS_TUNNEL &&
3115a1ba8781SMark Fenwick 	    ksi->ks_in_extv[SADB_X_EXT_ADDRESS_INNER_SRC] == NULL) {
3116a1ba8781SMark Fenwick 		mutex_exit(&newbie->ipsa_lock);
3117a1ba8781SMark Fenwick 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_INNER_SRC;
31188810c16bSdanmcd 		error = EINVAL;
31198810c16bSdanmcd 		goto error;
31208810c16bSdanmcd 	}
31217c478bd9Sstevel@tonic-gate 	/*
31227c478bd9Sstevel@tonic-gate 	 * If unspecified source address, force replay_wsize to 0.
31237c478bd9Sstevel@tonic-gate 	 * This is because an SA that has multiple sources of secure
31247c478bd9Sstevel@tonic-gate 	 * traffic cannot enforce a replay counter w/o synchronizing the
31257c478bd9Sstevel@tonic-gate 	 * senders.
31267c478bd9Sstevel@tonic-gate 	 */
31277c478bd9Sstevel@tonic-gate 	if (ksi->ks_in_srctype != KS_IN_ADDR_UNSPEC)
31287c478bd9Sstevel@tonic-gate 		newbie->ipsa_replay_wsize = assoc->sadb_sa_replay;
31297c478bd9Sstevel@tonic-gate 	else
31307c478bd9Sstevel@tonic-gate 		newbie->ipsa_replay_wsize = 0;
31317c478bd9Sstevel@tonic-gate 
3132437220cdSdanmcd 	newbie->ipsa_addtime = gethrestime_sec();
31337c478bd9Sstevel@tonic-gate 
31347c478bd9Sstevel@tonic-gate 	if (kmcext != NULL) {
31357c478bd9Sstevel@tonic-gate 		newbie->ipsa_kmp = kmcext->sadb_x_kmc_proto;
31367c478bd9Sstevel@tonic-gate 		newbie->ipsa_kmc = kmcext->sadb_x_kmc_cookie;
31377c478bd9Sstevel@tonic-gate 	}
31387c478bd9Sstevel@tonic-gate 
31397c478bd9Sstevel@tonic-gate 	/*
31407c478bd9Sstevel@tonic-gate 	 * XXX CURRENT lifetime checks MAY BE needed for an UPDATE.
31417c478bd9Sstevel@tonic-gate 	 * The spec says that one can update current lifetimes, but
31427c478bd9Sstevel@tonic-gate 	 * that seems impractical, especially in the larval-to-mature
31437c478bd9Sstevel@tonic-gate 	 * update that this function performs.
31447c478bd9Sstevel@tonic-gate 	 */
31457c478bd9Sstevel@tonic-gate 	if (soft != NULL) {
31467c478bd9Sstevel@tonic-gate 		newbie->ipsa_softaddlt = soft->sadb_lifetime_addtime;
31477c478bd9Sstevel@tonic-gate 		newbie->ipsa_softuselt = soft->sadb_lifetime_usetime;
31487c478bd9Sstevel@tonic-gate 		newbie->ipsa_softbyteslt = soft->sadb_lifetime_bytes;
31497c478bd9Sstevel@tonic-gate 		newbie->ipsa_softalloc = soft->sadb_lifetime_allocations;
31507c478bd9Sstevel@tonic-gate 		SET_EXPIRE(newbie, softaddlt, softexpiretime);
31517c478bd9Sstevel@tonic-gate 	}
31527c478bd9Sstevel@tonic-gate 	if (hard != NULL) {
31537c478bd9Sstevel@tonic-gate 		newbie->ipsa_hardaddlt = hard->sadb_lifetime_addtime;
31547c478bd9Sstevel@tonic-gate 		newbie->ipsa_harduselt = hard->sadb_lifetime_usetime;
31557c478bd9Sstevel@tonic-gate 		newbie->ipsa_hardbyteslt = hard->sadb_lifetime_bytes;
31567c478bd9Sstevel@tonic-gate 		newbie->ipsa_hardalloc = hard->sadb_lifetime_allocations;
31577c478bd9Sstevel@tonic-gate 		SET_EXPIRE(newbie, hardaddlt, hardexpiretime);
31587c478bd9Sstevel@tonic-gate 	}
31599c2c14abSThejaswini Singarajipura 	if (idle != NULL) {
31609c2c14abSThejaswini Singarajipura 		newbie->ipsa_idleaddlt = idle->sadb_lifetime_addtime;
31619c2c14abSThejaswini Singarajipura 		newbie->ipsa_idleuselt = idle->sadb_lifetime_usetime;
31629c2c14abSThejaswini Singarajipura 		newbie->ipsa_idleexpiretime = newbie->ipsa_addtime +
31639c2c14abSThejaswini Singarajipura 		    newbie->ipsa_idleaddlt;
31649c2c14abSThejaswini Singarajipura 		newbie->ipsa_idletime = newbie->ipsa_idleaddlt;
31659c2c14abSThejaswini Singarajipura 	}
31667c478bd9Sstevel@tonic-gate 
31677c478bd9Sstevel@tonic-gate 	newbie->ipsa_authtmpl = NULL;
31687c478bd9Sstevel@tonic-gate 	newbie->ipsa_encrtmpl = NULL;
31697c478bd9Sstevel@tonic-gate 
3170bd670b35SErik Nordmark #ifdef IPSEC_LATENCY_TEST
3171bd670b35SErik Nordmark 	if (akey != NULL && newbie->ipsa_auth_alg != SADB_AALG_NONE) {
3172bd670b35SErik Nordmark #else
31737c478bd9Sstevel@tonic-gate 	if (akey != NULL) {
3174bd670b35SErik Nordmark #endif
3175bd670b35SErik Nordmark 		async = (ipss->ipsec_algs_exec_mode[IPSEC_ALG_AUTH] ==
3176bd670b35SErik Nordmark 		    IPSEC_ALGS_EXEC_ASYNC);
3177bd670b35SErik Nordmark 
31787c478bd9Sstevel@tonic-gate 		newbie->ipsa_authkeybits = akey->sadb_key_bits;
31797c478bd9Sstevel@tonic-gate 		newbie->ipsa_authkeylen = SADB_1TO8(akey->sadb_key_bits);
31807c478bd9Sstevel@tonic-gate 		/* In case we have to round up to the next byte... */
31817c478bd9Sstevel@tonic-gate 		if ((akey->sadb_key_bits & 0x7) != 0)
31827c478bd9Sstevel@tonic-gate 			newbie->ipsa_authkeylen++;
31837c478bd9Sstevel@tonic-gate 		newbie->ipsa_authkey = kmem_alloc(newbie->ipsa_authkeylen,
31847c478bd9Sstevel@tonic-gate 		    KM_NOSLEEP);
31857c478bd9Sstevel@tonic-gate 		if (newbie->ipsa_authkey == NULL) {
31867c478bd9Sstevel@tonic-gate 			error = ENOMEM;
31877c478bd9Sstevel@tonic-gate 			mutex_exit(&newbie->ipsa_lock);
31887c478bd9Sstevel@tonic-gate 			goto error;
31897c478bd9Sstevel@tonic-gate 		}
31907c478bd9Sstevel@tonic-gate 		bcopy(akey + 1, newbie->ipsa_authkey, newbie->ipsa_authkeylen);
31917c478bd9Sstevel@tonic-gate 		bzero(akey + 1, newbie->ipsa_authkeylen);
31927c478bd9Sstevel@tonic-gate 
31937c478bd9Sstevel@tonic-gate 		/*
31947c478bd9Sstevel@tonic-gate 		 * Pre-initialize the kernel crypto framework key
31957c478bd9Sstevel@tonic-gate 		 * structure.
31967c478bd9Sstevel@tonic-gate 		 */
31977c478bd9Sstevel@tonic-gate 		newbie->ipsa_kcfauthkey.ck_format = CRYPTO_KEY_RAW;
31987c478bd9Sstevel@tonic-gate 		newbie->ipsa_kcfauthkey.ck_length = newbie->ipsa_authkeybits;
31997c478bd9Sstevel@tonic-gate 		newbie->ipsa_kcfauthkey.ck_data = newbie->ipsa_authkey;
32007c478bd9Sstevel@tonic-gate 
3201f4b3ec61Sdh155122 		mutex_enter(&ipss->ipsec_alg_lock);
3202628b0c67SMark Fenwick 		alg = ipss->ipsec_alglists[IPSEC_ALG_AUTH]
3203628b0c67SMark Fenwick 		    [newbie->ipsa_auth_alg];
3204628b0c67SMark Fenwick 		if (alg != NULL && ALG_VALID(alg)) {
3205628b0c67SMark Fenwick 			newbie->ipsa_amech.cm_type = alg->alg_mech_type;
3206628b0c67SMark Fenwick 			newbie->ipsa_amech.cm_param =
3207628b0c67SMark Fenwick 			    (char *)&newbie->ipsa_mac_len;
3208628b0c67SMark Fenwick 			newbie->ipsa_amech.cm_param_len = sizeof (size_t);
3209628b0c67SMark Fenwick 			newbie->ipsa_mac_len = (size_t)alg->alg_datalen;
3210628b0c67SMark Fenwick 		} else {
3211628b0c67SMark Fenwick 			newbie->ipsa_amech.cm_type = CRYPTO_MECHANISM_INVALID;
3212628b0c67SMark Fenwick 		}
32137c478bd9Sstevel@tonic-gate 		error = ipsec_create_ctx_tmpl(newbie, IPSEC_ALG_AUTH);
3214f4b3ec61Sdh155122 		mutex_exit(&ipss->ipsec_alg_lock);
32157c478bd9Sstevel@tonic-gate 		if (error != 0) {
32167c478bd9Sstevel@tonic-gate 			mutex_exit(&newbie->ipsa_lock);
3217a1ba8781SMark Fenwick 			/*
3218a1ba8781SMark Fenwick 			 * An error here indicates that alg is the wrong type
3219a1ba8781SMark Fenwick 			 * (IE: not authentication) or its not in the alg tables
3220a1ba8781SMark Fenwick 			 * created by ipsecalgs(1m), or Kcf does not like the
3221a1ba8781SMark Fenwick 			 * parameters passed in with this algorithm, which is
3222a1ba8781SMark Fenwick 			 * probably a coding error!
3223a1ba8781SMark Fenwick 			 */
3224a1ba8781SMark Fenwick 			*diagnostic = SADB_X_DIAGNOSTIC_BAD_CTX;
3225628b0c67SMark Fenwick 
32267c478bd9Sstevel@tonic-gate 			goto error;
32277c478bd9Sstevel@tonic-gate 		}
32287c478bd9Sstevel@tonic-gate 	}
32297c478bd9Sstevel@tonic-gate 
32307c478bd9Sstevel@tonic-gate 	if (ekey != NULL) {
3231628b0c67SMark Fenwick 		mutex_enter(&ipss->ipsec_alg_lock);
3232bd670b35SErik Nordmark 		async = async || (ipss->ipsec_algs_exec_mode[IPSEC_ALG_ENCR] ==
3233bd670b35SErik Nordmark 		    IPSEC_ALGS_EXEC_ASYNC);
3234628b0c67SMark Fenwick 		alg = ipss->ipsec_alglists[IPSEC_ALG_ENCR]
3235628b0c67SMark Fenwick 		    [newbie->ipsa_encr_alg];
3236628b0c67SMark Fenwick 
3237628b0c67SMark Fenwick 		if (alg != NULL && ALG_VALID(alg)) {
3238628b0c67SMark Fenwick 			newbie->ipsa_emech.cm_type = alg->alg_mech_type;
3239628b0c67SMark Fenwick 			newbie->ipsa_datalen = alg->alg_datalen;
3240628b0c67SMark Fenwick 			if (alg->alg_flags & ALG_FLAG_COUNTERMODE)
3241628b0c67SMark Fenwick 				newbie->ipsa_flags |= IPSA_F_COUNTERMODE;
3242628b0c67SMark Fenwick 
3243628b0c67SMark Fenwick 			if (alg->alg_flags & ALG_FLAG_COMBINED) {
3244628b0c67SMark Fenwick 				newbie->ipsa_flags |= IPSA_F_COMBINED;
3245628b0c67SMark Fenwick 				newbie->ipsa_mac_len =  alg->alg_icvlen;
3246628b0c67SMark Fenwick 			}
3247628b0c67SMark Fenwick 
3248628b0c67SMark Fenwick 			if (alg->alg_flags & ALG_FLAG_CCM)
3249628b0c67SMark Fenwick 				newbie->ipsa_noncefunc = ccm_params_init;
3250628b0c67SMark Fenwick 			else if (alg->alg_flags & ALG_FLAG_GCM)
3251628b0c67SMark Fenwick 				newbie->ipsa_noncefunc = gcm_params_init;
3252628b0c67SMark Fenwick 			else newbie->ipsa_noncefunc = cbc_params_init;
3253628b0c67SMark Fenwick 
3254628b0c67SMark Fenwick 			newbie->ipsa_saltlen = alg->alg_saltlen;
3255628b0c67SMark Fenwick 			newbie->ipsa_saltbits = SADB_8TO1(newbie->ipsa_saltlen);
3256628b0c67SMark Fenwick 			newbie->ipsa_iv_len = alg->alg_ivlen;
3257628b0c67SMark Fenwick 			newbie->ipsa_nonce_len = newbie->ipsa_saltlen +
3258628b0c67SMark Fenwick 			    newbie->ipsa_iv_len;
3259628b0c67SMark Fenwick 			newbie->ipsa_emech.cm_param = NULL;
3260628b0c67SMark Fenwick 			newbie->ipsa_emech.cm_param_len = 0;
3261628b0c67SMark Fenwick 		} else {
3262628b0c67SMark Fenwick 			newbie->ipsa_emech.cm_type = CRYPTO_MECHANISM_INVALID;
3263628b0c67SMark Fenwick 		}
3264628b0c67SMark Fenwick 		mutex_exit(&ipss->ipsec_alg_lock);
3265628b0c67SMark Fenwick 
3266628b0c67SMark Fenwick 		/*
3267628b0c67SMark Fenwick 		 * The byte stream following the sadb_key_t is made up of:
3268628b0c67SMark Fenwick 		 * key bytes, [salt bytes], [IV initial value]
3269628b0c67SMark Fenwick 		 * All of these have variable length. The IV is typically
3270628b0c67SMark Fenwick 		 * randomly generated by this function and not passed in.
3271628b0c67SMark Fenwick 		 * By supporting the injection of a known IV, the whole
3272628b0c67SMark Fenwick 		 * IPsec subsystem and the underlying crypto subsystem
3273628b0c67SMark Fenwick 		 * can be tested with known test vectors.
3274628b0c67SMark Fenwick 		 *
3275628b0c67SMark Fenwick 		 * The keying material has been checked by ext_check()
3276628b0c67SMark Fenwick 		 * and ipsec_valid_key_size(), after removing salt/IV
3277628b0c67SMark Fenwick 		 * bits, whats left is the encryption key. If this is too
3278628b0c67SMark Fenwick 		 * short, ipsec_create_ctx_tmpl() will fail and the SA
3279628b0c67SMark Fenwick 		 * won't get created.
3280628b0c67SMark Fenwick 		 *
3281628b0c67SMark Fenwick 		 * set ipsa_encrkeylen to length of key only.
3282628b0c67SMark Fenwick 		 */
32837c478bd9Sstevel@tonic-gate 		newbie->ipsa_encrkeybits = ekey->sadb_key_bits;
3284628b0c67SMark Fenwick 		newbie->ipsa_encrkeybits -= ekey->sadb_key_reserved;
3285628b0c67SMark Fenwick 		newbie->ipsa_encrkeybits -= newbie->ipsa_saltbits;
3286628b0c67SMark Fenwick 		newbie->ipsa_encrkeylen = SADB_1TO8(newbie->ipsa_encrkeybits);
3287628b0c67SMark Fenwick 
32887c478bd9Sstevel@tonic-gate 		/* In case we have to round up to the next byte... */
32897c478bd9Sstevel@tonic-gate 		if ((ekey->sadb_key_bits & 0x7) != 0)
32907c478bd9Sstevel@tonic-gate 			newbie->ipsa_encrkeylen++;
3291628b0c67SMark Fenwick 
32927c478bd9Sstevel@tonic-gate 		newbie->ipsa_encrkey = kmem_alloc(newbie->ipsa_encrkeylen,
32937c478bd9Sstevel@tonic-gate 		    KM_NOSLEEP);
32947c478bd9Sstevel@tonic-gate 		if (newbie->ipsa_encrkey == NULL) {
32957c478bd9Sstevel@tonic-gate 			error = ENOMEM;
32967c478bd9Sstevel@tonic-gate 			mutex_exit(&newbie->ipsa_lock);
32977c478bd9Sstevel@tonic-gate 			goto error;
32987c478bd9Sstevel@tonic-gate 		}
3299628b0c67SMark Fenwick 
3300628b0c67SMark Fenwick 		buf_ptr = (uint8_t *)(ekey + 1);
3301628b0c67SMark Fenwick 		bcopy(buf_ptr, newbie->ipsa_encrkey, newbie->ipsa_encrkeylen);
3302628b0c67SMark Fenwick 
3303628b0c67SMark Fenwick 		if (newbie->ipsa_flags & IPSA_F_COMBINED) {
3304628b0c67SMark Fenwick 			/*
3305628b0c67SMark Fenwick 			 * Combined mode algs need a nonce. Copy the salt and
3306628b0c67SMark Fenwick 			 * IV into a buffer. The ipsa_nonce is a pointer into
3307628b0c67SMark Fenwick 			 * this buffer, some bytes at the start of the buffer
3308628b0c67SMark Fenwick 			 * may be unused, depends on the salt length. The IV
3309628b0c67SMark Fenwick 			 * is 64 bit aligned so it can be incremented as a
3310628b0c67SMark Fenwick 			 * uint64_t. Zero out key in samsg_t before freeing.
3311628b0c67SMark Fenwick 			 */
3312628b0c67SMark Fenwick 
3313628b0c67SMark Fenwick 			newbie->ipsa_nonce_buf = kmem_alloc(
3314628b0c67SMark Fenwick 			    sizeof (ipsec_nonce_t), KM_NOSLEEP);
3315628b0c67SMark Fenwick 			if (newbie->ipsa_nonce_buf == NULL) {
3316628b0c67SMark Fenwick 				error = ENOMEM;
3317628b0c67SMark Fenwick 				mutex_exit(&newbie->ipsa_lock);
3318628b0c67SMark Fenwick 				goto error;
3319628b0c67SMark Fenwick 			}
3320628b0c67SMark Fenwick 			/*
3321628b0c67SMark Fenwick 			 * Initialize nonce and salt pointers to point
3322628b0c67SMark Fenwick 			 * to the nonce buffer. This is just in case we get
3323628b0c67SMark Fenwick 			 * bad data, the pointers will be valid, the data
3324628b0c67SMark Fenwick 			 * won't be.
3325628b0c67SMark Fenwick 			 *
3326628b0c67SMark Fenwick 			 * See sadb.h for layout of nonce.
3327628b0c67SMark Fenwick 			 */
3328628b0c67SMark Fenwick 			newbie->ipsa_iv = &newbie->ipsa_nonce_buf->iv;
3329628b0c67SMark Fenwick 			newbie->ipsa_salt = (uint8_t *)newbie->ipsa_nonce_buf;
3330628b0c67SMark Fenwick 			newbie->ipsa_nonce = newbie->ipsa_salt;
3331628b0c67SMark Fenwick 			if (newbie->ipsa_saltlen != 0) {
3332628b0c67SMark Fenwick 				salt_offset = MAXSALTSIZE -
3333628b0c67SMark Fenwick 				    newbie->ipsa_saltlen;
3334628b0c67SMark Fenwick 				newbie->ipsa_salt = (uint8_t *)
3335628b0c67SMark Fenwick 				    &newbie->ipsa_nonce_buf->salt[salt_offset];
3336628b0c67SMark Fenwick 				newbie->ipsa_nonce = newbie->ipsa_salt;
3337628b0c67SMark Fenwick 				buf_ptr += newbie->ipsa_encrkeylen;
3338628b0c67SMark Fenwick 				bcopy(buf_ptr, newbie->ipsa_salt,
3339628b0c67SMark Fenwick 				    newbie->ipsa_saltlen);
3340628b0c67SMark Fenwick 			}
3341628b0c67SMark Fenwick 			/*
3342628b0c67SMark Fenwick 			 * The IV for CCM/GCM mode increments, it should not
3343628b0c67SMark Fenwick 			 * repeat. Get a random value for the IV, make a
3344628b0c67SMark Fenwick 			 * copy, the SA will expire when/if the IV ever
3345628b0c67SMark Fenwick 			 * wraps back to the initial value. If an Initial IV
3346628b0c67SMark Fenwick 			 * is passed in via PF_KEY, save this in the SA.
3347628b0c67SMark Fenwick 			 * Initialising IV for inbound is pointless as its
3348628b0c67SMark Fenwick 			 * taken from the inbound packet.
3349628b0c67SMark Fenwick 			 */
3350628b0c67SMark Fenwick 			if (!is_inbound) {
3351628b0c67SMark Fenwick 				if (ekey->sadb_key_reserved != 0) {
3352628b0c67SMark Fenwick 					buf_ptr += newbie->ipsa_saltlen;
3353628b0c67SMark Fenwick 					bcopy(buf_ptr, (uint8_t *)newbie->
3354628b0c67SMark Fenwick 					    ipsa_iv, SADB_1TO8(ekey->
3355628b0c67SMark Fenwick 					    sadb_key_reserved));
3356628b0c67SMark Fenwick 				} else {
3357628b0c67SMark Fenwick 					(void) random_get_pseudo_bytes(
3358628b0c67SMark Fenwick 					    (uint8_t *)newbie->ipsa_iv,
3359628b0c67SMark Fenwick 					    newbie->ipsa_iv_len);
3360628b0c67SMark Fenwick 				}
3361628b0c67SMark Fenwick 				newbie->ipsa_iv_softexpire =
3362628b0c67SMark Fenwick 				    (*newbie->ipsa_iv) << 9;
3363628b0c67SMark Fenwick 				newbie->ipsa_iv_hardexpire = *newbie->ipsa_iv;
3364628b0c67SMark Fenwick 			}
3365628b0c67SMark Fenwick 		}
3366628b0c67SMark Fenwick 		bzero((ekey + 1), SADB_1TO8(ekey->sadb_key_bits));
33677c478bd9Sstevel@tonic-gate 
33687c478bd9Sstevel@tonic-gate 		/*
33697c478bd9Sstevel@tonic-gate 		 * Pre-initialize the kernel crypto framework key
33707c478bd9Sstevel@tonic-gate 		 * structure.
33717c478bd9Sstevel@tonic-gate 		 */
33727c478bd9Sstevel@tonic-gate 		newbie->ipsa_kcfencrkey.ck_format = CRYPTO_KEY_RAW;
33737c478bd9Sstevel@tonic-gate 		newbie->ipsa_kcfencrkey.ck_length = newbie->ipsa_encrkeybits;
33747c478bd9Sstevel@tonic-gate 		newbie->ipsa_kcfencrkey.ck_data = newbie->ipsa_encrkey;
33757c478bd9Sstevel@tonic-gate 
3376f4b3ec61Sdh155122 		mutex_enter(&ipss->ipsec_alg_lock);
33777c478bd9Sstevel@tonic-gate 		error = ipsec_create_ctx_tmpl(newbie, IPSEC_ALG_ENCR);
3378f4b3ec61Sdh155122 		mutex_exit(&ipss->ipsec_alg_lock);
33797c478bd9Sstevel@tonic-gate 		if (error != 0) {
33807c478bd9Sstevel@tonic-gate 			mutex_exit(&newbie->ipsa_lock);
3381a1ba8781SMark Fenwick 			/* See above for error explanation. */
3382a1ba8781SMark Fenwick 			*diagnostic = SADB_X_DIAGNOSTIC_BAD_CTX;
33837c478bd9Sstevel@tonic-gate 			goto error;
33847c478bd9Sstevel@tonic-gate 		}
33857c478bd9Sstevel@tonic-gate 	}
33867c478bd9Sstevel@tonic-gate 
3387bd670b35SErik Nordmark 	if (async)
3388bd670b35SErik Nordmark 		newbie->ipsa_flags |= IPSA_F_ASYNC;
3389bd670b35SErik Nordmark 
33907c478bd9Sstevel@tonic-gate 	/*
33917c478bd9Sstevel@tonic-gate 	 * Ptrs to processing functions.
33927c478bd9Sstevel@tonic-gate 	 */
33937c478bd9Sstevel@tonic-gate 	if (newbie->ipsa_type == SADB_SATYPE_ESP)
33947c478bd9Sstevel@tonic-gate 		ipsecesp_init_funcs(newbie);
33957c478bd9Sstevel@tonic-gate 	else
33967c478bd9Sstevel@tonic-gate 		ipsecah_init_funcs(newbie);
33977c478bd9Sstevel@tonic-gate 	ASSERT(newbie->ipsa_output_func != NULL &&
33987c478bd9Sstevel@tonic-gate 	    newbie->ipsa_input_func != NULL);
33997c478bd9Sstevel@tonic-gate 
34007c478bd9Sstevel@tonic-gate 	/*
34017c478bd9Sstevel@tonic-gate 	 * Certificate ID stuff.
34027c478bd9Sstevel@tonic-gate 	 */
34037c478bd9Sstevel@tonic-gate 	if (ksi->ks_in_extv[SADB_EXT_IDENTITY_SRC] != NULL) {
34047c478bd9Sstevel@tonic-gate 		sadb_ident_t *id =
34057c478bd9Sstevel@tonic-gate 		    (sadb_ident_t *)ksi->ks_in_extv[SADB_EXT_IDENTITY_SRC];
34067c478bd9Sstevel@tonic-gate 
34077c478bd9Sstevel@tonic-gate 		/*
34087c478bd9Sstevel@tonic-gate 		 * Can assume strlen() will return okay because ext_check() in
34097c478bd9Sstevel@tonic-gate 		 * keysock.c prepares the string for us.
34107c478bd9Sstevel@tonic-gate 		 */
34117c478bd9Sstevel@tonic-gate 		newbie->ipsa_src_cid = ipsid_lookup(id->sadb_ident_type,
3412f4b3ec61Sdh155122 		    (char *)(id+1), ns);
34137c478bd9Sstevel@tonic-gate 		if (newbie->ipsa_src_cid == NULL) {
34147c478bd9Sstevel@tonic-gate 			error = ENOMEM;
34157c478bd9Sstevel@tonic-gate 			mutex_exit(&newbie->ipsa_lock);
34167c478bd9Sstevel@tonic-gate 			goto error;
34177c478bd9Sstevel@tonic-gate 		}
34187c478bd9Sstevel@tonic-gate 	}
34197c478bd9Sstevel@tonic-gate 
34207c478bd9Sstevel@tonic-gate 	if (ksi->ks_in_extv[SADB_EXT_IDENTITY_DST] != NULL) {
34217c478bd9Sstevel@tonic-gate 		sadb_ident_t *id =
34227c478bd9Sstevel@tonic-gate 		    (sadb_ident_t *)ksi->ks_in_extv[SADB_EXT_IDENTITY_DST];
34237c478bd9Sstevel@tonic-gate 
34247c478bd9Sstevel@tonic-gate 		/*
34257c478bd9Sstevel@tonic-gate 		 * Can assume strlen() will return okay because ext_check() in
34267c478bd9Sstevel@tonic-gate 		 * keysock.c prepares the string for us.
34277c478bd9Sstevel@tonic-gate 		 */
34287c478bd9Sstevel@tonic-gate 		newbie->ipsa_dst_cid = ipsid_lookup(id->sadb_ident_type,
3429f4b3ec61Sdh155122 		    (char *)(id+1), ns);
34307c478bd9Sstevel@tonic-gate 		if (newbie->ipsa_dst_cid == NULL) {
34317c478bd9Sstevel@tonic-gate 			error = ENOMEM;
34327c478bd9Sstevel@tonic-gate 			mutex_exit(&newbie->ipsa_lock);
34337c478bd9Sstevel@tonic-gate 			goto error;
34347c478bd9Sstevel@tonic-gate 		}
34357c478bd9Sstevel@tonic-gate 	}
34367c478bd9Sstevel@tonic-gate 
34375d3b8cb7SBill Sommerfeld 	/*
34385d3b8cb7SBill Sommerfeld 	 * sensitivity label handling code:
34395d3b8cb7SBill Sommerfeld 	 * Convert sens + bitmap into cred_t, and associate it
34405d3b8cb7SBill Sommerfeld 	 * with the new SA.
34415d3b8cb7SBill Sommerfeld 	 */
34427c478bd9Sstevel@tonic-gate 	if (sens != NULL) {
34437c478bd9Sstevel@tonic-gate 		uint64_t *bitmap = (uint64_t *)(sens + 1);
34447c478bd9Sstevel@tonic-gate 
3445bd670b35SErik Nordmark 		newbie->ipsa_tsl = sadb_label_from_sens(sens, bitmap);
34465d3b8cb7SBill Sommerfeld 	}
34475d3b8cb7SBill Sommerfeld 
34485d3b8cb7SBill Sommerfeld 	/*
34495d3b8cb7SBill Sommerfeld 	 * Likewise for outer sensitivity.
34505d3b8cb7SBill Sommerfeld 	 */
34515d3b8cb7SBill Sommerfeld 	if (osens != NULL) {
34525d3b8cb7SBill Sommerfeld 		uint64_t *bitmap = (uint64_t *)(osens + 1);
3453bd670b35SErik Nordmark 		ts_label_t *tsl, *effective_tsl;
34545d3b8cb7SBill Sommerfeld 		uint32_t *peer_addr_ptr;
3455bd670b35SErik Nordmark 		zoneid_t zoneid = GLOBAL_ZONEID;
3456bd670b35SErik Nordmark 		zone_t *zone;
34575d3b8cb7SBill Sommerfeld 
34585d3b8cb7SBill Sommerfeld 		peer_addr_ptr = is_inbound ? src_addr_ptr : dst_addr_ptr;
34595d3b8cb7SBill Sommerfeld 
3460bd670b35SErik Nordmark 		tsl = sadb_label_from_sens(osens, bitmap);
34615d3b8cb7SBill Sommerfeld 		newbie->ipsa_mac_exempt = CONN_MAC_DEFAULT;
34625d3b8cb7SBill Sommerfeld 
34635d3b8cb7SBill Sommerfeld 		if (osens->sadb_x_sens_flags & SADB_X_SENS_IMPLICIT) {
34645d3b8cb7SBill Sommerfeld 			newbie->ipsa_mac_exempt = CONN_MAC_IMPLICIT;
34655d3b8cb7SBill Sommerfeld 		}
34665d3b8cb7SBill Sommerfeld 
3467bd670b35SErik Nordmark 		error = tsol_check_dest(tsl, peer_addr_ptr,
34685d3b8cb7SBill Sommerfeld 		    (af == AF_INET6)?IPV6_VERSION:IPV4_VERSION,
3469bd670b35SErik Nordmark 		    newbie->ipsa_mac_exempt, B_TRUE, &effective_tsl);
34705d3b8cb7SBill Sommerfeld 		if (error != 0) {
3471bd670b35SErik Nordmark 			label_rele(tsl);
34727c478bd9Sstevel@tonic-gate 			mutex_exit(&newbie->ipsa_lock);
34737c478bd9Sstevel@tonic-gate 			goto error;
34747c478bd9Sstevel@tonic-gate 		}
34755d3b8cb7SBill Sommerfeld 
3476bd670b35SErik Nordmark 		if (effective_tsl != NULL) {
3477bd670b35SErik Nordmark 			label_rele(tsl);
3478bd670b35SErik Nordmark 			tsl = effective_tsl;
34797c478bd9Sstevel@tonic-gate 		}
34805d3b8cb7SBill Sommerfeld 
3481bd670b35SErik Nordmark 		newbie->ipsa_otsl = tsl;
3482bd670b35SErik Nordmark 
3483bd670b35SErik Nordmark 		zone = zone_find_by_label(tsl);
3484bd670b35SErik Nordmark 		if (zone != NULL) {
3485bd670b35SErik Nordmark 			zoneid = zone->zone_id;
3486bd670b35SErik Nordmark 			zone_rele(zone);
3487bd670b35SErik Nordmark 		}
3488bd670b35SErik Nordmark 		/*
3489bd670b35SErik Nordmark 		 * For exclusive stacks we set the zoneid to zero to operate
3490bd670b35SErik Nordmark 		 * as if in the global zone for tsol_compute_label_v4/v6
3491bd670b35SErik Nordmark 		 */
3492bd670b35SErik Nordmark 		if (ipst->ips_netstack->netstack_stackid != GLOBAL_NETSTACKID)
3493bd670b35SErik Nordmark 			zoneid = GLOBAL_ZONEID;
34945d3b8cb7SBill Sommerfeld 
34955d3b8cb7SBill Sommerfeld 		if (af == AF_INET6) {
3496bd670b35SErik Nordmark 			error = tsol_compute_label_v6(tsl, zoneid,
3497eb035775SBill Sommerfeld 			    (in6_addr_t *)peer_addr_ptr,
34985d3b8cb7SBill Sommerfeld 			    newbie->ipsa_opt_storage, ipst);
34995d3b8cb7SBill Sommerfeld 		} else {
3500bd670b35SErik Nordmark 			error = tsol_compute_label_v4(tsl, zoneid,
3501bd670b35SErik Nordmark 			    *peer_addr_ptr, newbie->ipsa_opt_storage, ipst);
35027c478bd9Sstevel@tonic-gate 		}
3503eb035775SBill Sommerfeld 		if (error != 0) {
3504eb035775SBill Sommerfeld 			mutex_exit(&newbie->ipsa_lock);
3505eb035775SBill Sommerfeld 			goto error;
3506eb035775SBill Sommerfeld 		}
35077c478bd9Sstevel@tonic-gate 	}
35087c478bd9Sstevel@tonic-gate 
35097c478bd9Sstevel@tonic-gate 
35109c2c14abSThejaswini Singarajipura 	if (replayext != NULL) {
35119c2c14abSThejaswini Singarajipura 		if ((replayext->sadb_x_rc_replay32 == 0) &&
35129c2c14abSThejaswini Singarajipura 		    (replayext->sadb_x_rc_replay64 != 0)) {
35139c2c14abSThejaswini Singarajipura 			error = EOPNOTSUPP;
3514a1ba8781SMark Fenwick 			*diagnostic = SADB_X_DIAGNOSTIC_INVALID_REPLAY;
35159c2c14abSThejaswini Singarajipura 			mutex_exit(&newbie->ipsa_lock);
35169c2c14abSThejaswini Singarajipura 			goto error;
35179c2c14abSThejaswini Singarajipura 		}
35189c2c14abSThejaswini Singarajipura 		newbie->ipsa_replay = replayext->sadb_x_rc_replay32;
35199c2c14abSThejaswini Singarajipura 	}
35209c2c14abSThejaswini Singarajipura 
35217c478bd9Sstevel@tonic-gate 	/* now that the SA has been updated, set its new state */
35227c478bd9Sstevel@tonic-gate 	newbie->ipsa_state = assoc->sadb_sa_state;
35237c478bd9Sstevel@tonic-gate 
352438d95a78Smarkfen 	if (clone) {
352538d95a78Smarkfen 		newbie->ipsa_haspeer = B_TRUE;
352638d95a78Smarkfen 	} else {
352738d95a78Smarkfen 		if (!is_inbound) {
352838d95a78Smarkfen 			lifetime_fuzz(newbie);
352938d95a78Smarkfen 		}
353038d95a78Smarkfen 	}
35317c478bd9Sstevel@tonic-gate 	/*
35327c478bd9Sstevel@tonic-gate 	 * The less locks I hold when doing an insertion and possible cloning,
35337c478bd9Sstevel@tonic-gate 	 * the better!
35347c478bd9Sstevel@tonic-gate 	 */
35357c478bd9Sstevel@tonic-gate 	mutex_exit(&newbie->ipsa_lock);
35367c478bd9Sstevel@tonic-gate 
35377c478bd9Sstevel@tonic-gate 	if (clone) {
35387c478bd9Sstevel@tonic-gate 		newbie_clone = sadb_cloneassoc(newbie);
35397c478bd9Sstevel@tonic-gate 
35407c478bd9Sstevel@tonic-gate 		if (newbie_clone == NULL) {
35417c478bd9Sstevel@tonic-gate 			error = ENOMEM;
35427c478bd9Sstevel@tonic-gate 			goto error;
35437c478bd9Sstevel@tonic-gate 		}
35447c478bd9Sstevel@tonic-gate 	}
35457c478bd9Sstevel@tonic-gate 
35467c478bd9Sstevel@tonic-gate 	/*
35477c478bd9Sstevel@tonic-gate 	 * Enter the bucket locks.  The order of entry is outbound,
35487c478bd9Sstevel@tonic-gate 	 * inbound.  We map "primary" and "secondary" into outbound and inbound
35497c478bd9Sstevel@tonic-gate 	 * based on the destination address type.  If the destination address
35507c478bd9Sstevel@tonic-gate 	 * type is for a node that isn't mine (or potentially mine), the
35517c478bd9Sstevel@tonic-gate 	 * "primary" bucket is the outbound one.
35527c478bd9Sstevel@tonic-gate 	 */
355338d95a78Smarkfen 	if (!is_inbound) {
35547c478bd9Sstevel@tonic-gate 		/* primary == outbound */
35557c478bd9Sstevel@tonic-gate 		mutex_enter(&primary->isaf_lock);
35567c478bd9Sstevel@tonic-gate 		mutex_enter(&secondary->isaf_lock);
35577c478bd9Sstevel@tonic-gate 	} else {
35587c478bd9Sstevel@tonic-gate 		/* primary == inbound */
35597c478bd9Sstevel@tonic-gate 		mutex_enter(&secondary->isaf_lock);
35607c478bd9Sstevel@tonic-gate 		mutex_enter(&primary->isaf_lock);
35617c478bd9Sstevel@tonic-gate 	}
35627c478bd9Sstevel@tonic-gate 
35637c478bd9Sstevel@tonic-gate 	/*
35647c478bd9Sstevel@tonic-gate 	 * sadb_insertassoc() doesn't increment the reference
35657c478bd9Sstevel@tonic-gate 	 * count.  We therefore have to increment the
35667c478bd9Sstevel@tonic-gate 	 * reference count one more time to reflect the
35677c478bd9Sstevel@tonic-gate 	 * pointers of the table that reference this SA.
35687c478bd9Sstevel@tonic-gate 	 */
35697c478bd9Sstevel@tonic-gate 	IPSA_REFHOLD(newbie);
35707c478bd9Sstevel@tonic-gate 
35717c478bd9Sstevel@tonic-gate 	if (isupdate) {
35727c478bd9Sstevel@tonic-gate 		/*
35737c478bd9Sstevel@tonic-gate 		 * Unlink from larval holding cell in the "inbound" fanout.
35747c478bd9Sstevel@tonic-gate 		 */
35757c478bd9Sstevel@tonic-gate 		ASSERT(newbie->ipsa_linklock == &primary->isaf_lock ||
35767c478bd9Sstevel@tonic-gate 		    newbie->ipsa_linklock == &secondary->isaf_lock);
35777c478bd9Sstevel@tonic-gate 		sadb_unlinkassoc(newbie);
35787c478bd9Sstevel@tonic-gate 	}
35797c478bd9Sstevel@tonic-gate 
35807c478bd9Sstevel@tonic-gate 	mutex_enter(&newbie->ipsa_lock);
35817c478bd9Sstevel@tonic-gate 	error = sadb_insertassoc(newbie, primary);
35827c478bd9Sstevel@tonic-gate 	mutex_exit(&newbie->ipsa_lock);
35837c478bd9Sstevel@tonic-gate 
35847c478bd9Sstevel@tonic-gate 	if (error != 0) {
35857c478bd9Sstevel@tonic-gate 		/*
35867c478bd9Sstevel@tonic-gate 		 * Since sadb_insertassoc() failed, we must decrement the
35877c478bd9Sstevel@tonic-gate 		 * refcount again so the cleanup code will actually free
35887c478bd9Sstevel@tonic-gate 		 * the offending SA.
35897c478bd9Sstevel@tonic-gate 		 */
35907c478bd9Sstevel@tonic-gate 		IPSA_REFRELE(newbie);
35917c478bd9Sstevel@tonic-gate 		goto error_unlock;
35927c478bd9Sstevel@tonic-gate 	}
35937c478bd9Sstevel@tonic-gate 
35947c478bd9Sstevel@tonic-gate 	if (newbie_clone != NULL) {
35957c478bd9Sstevel@tonic-gate 		mutex_enter(&newbie_clone->ipsa_lock);
35967c478bd9Sstevel@tonic-gate 		error = sadb_insertassoc(newbie_clone, secondary);
35977c478bd9Sstevel@tonic-gate 		mutex_exit(&newbie_clone->ipsa_lock);
35987c478bd9Sstevel@tonic-gate 		if (error != 0) {
35997c478bd9Sstevel@tonic-gate 			/* Collision in secondary table. */
36007c478bd9Sstevel@tonic-gate 			sadb_unlinkassoc(newbie);  /* This does REFRELE. */
36017c478bd9Sstevel@tonic-gate 			goto error_unlock;
36027c478bd9Sstevel@tonic-gate 		}
36037c478bd9Sstevel@tonic-gate 		IPSA_REFHOLD(newbie_clone);
36047c478bd9Sstevel@tonic-gate 	} else {
36057c478bd9Sstevel@tonic-gate 		ASSERT(primary != secondary);
36067c478bd9Sstevel@tonic-gate 		scratch = ipsec_getassocbyspi(secondary, newbie->ipsa_spi,
36077c478bd9Sstevel@tonic-gate 		    ALL_ZEROES_PTR, newbie->ipsa_dstaddr, af);
36087c478bd9Sstevel@tonic-gate 		if (scratch != NULL) {
36097c478bd9Sstevel@tonic-gate 			/* Collision in secondary table. */
36107c478bd9Sstevel@tonic-gate 			sadb_unlinkassoc(newbie);  /* This does REFRELE. */
36117c478bd9Sstevel@tonic-gate 			/* Set the error, since ipsec_getassocbyspi() can't. */
36127c478bd9Sstevel@tonic-gate 			error = EEXIST;
36137c478bd9Sstevel@tonic-gate 			goto error_unlock;
36147c478bd9Sstevel@tonic-gate 		}
36157c478bd9Sstevel@tonic-gate 	}
36167c478bd9Sstevel@tonic-gate 
36177c478bd9Sstevel@tonic-gate 	/* OKAY!  So let's do some reality check assertions. */
36187c478bd9Sstevel@tonic-gate 
36190c0328cdSBill Sommerfeld 	ASSERT(MUTEX_NOT_HELD(&newbie->ipsa_lock));
36200c0328cdSBill Sommerfeld 	ASSERT(newbie_clone == NULL ||
36210c0328cdSBill Sommerfeld 	    (MUTEX_NOT_HELD(&newbie_clone->ipsa_lock)));
36227c478bd9Sstevel@tonic-gate 
36237c478bd9Sstevel@tonic-gate error_unlock:
36247c478bd9Sstevel@tonic-gate 
36257c478bd9Sstevel@tonic-gate 	/*
36267c478bd9Sstevel@tonic-gate 	 * We can exit the locks in any order.	Only entrance needs to
36277c478bd9Sstevel@tonic-gate 	 * follow any protocol.
36287c478bd9Sstevel@tonic-gate 	 */
36297c478bd9Sstevel@tonic-gate 	mutex_exit(&secondary->isaf_lock);
36307c478bd9Sstevel@tonic-gate 	mutex_exit(&primary->isaf_lock);
36317c478bd9Sstevel@tonic-gate 
363238d95a78Smarkfen 	if (pair_ext != NULL && error == 0) {
363338d95a78Smarkfen 		/* update pair_spi if it exists. */
36345d3b8cb7SBill Sommerfeld 		ipsa_query_t sq;
36355d3b8cb7SBill Sommerfeld 
36365d3b8cb7SBill Sommerfeld 		sq.spp = spp;		/* XXX param */
36375d3b8cb7SBill Sommerfeld 		error = sadb_form_query(ksi, IPSA_Q_DST, IPSA_Q_SRC|IPSA_Q_DST|
36385d3b8cb7SBill Sommerfeld 		    IPSA_Q_SA|IPSA_Q_INBOUND|IPSA_Q_OUTBOUND, &sq, diagnostic);
36395d3b8cb7SBill Sommerfeld 		if (error)
36405d3b8cb7SBill Sommerfeld 			return (error);
36415d3b8cb7SBill Sommerfeld 
36425d3b8cb7SBill Sommerfeld 		error = get_ipsa_pair(&sq, &ipsapp, diagnostic);
36435d3b8cb7SBill Sommerfeld 
36445d3b8cb7SBill Sommerfeld 		if (error != 0)
36455d3b8cb7SBill Sommerfeld 			goto error;
36465d3b8cb7SBill Sommerfeld 
36475d3b8cb7SBill Sommerfeld 		if (ipsapp.ipsap_psa_ptr != NULL) {
364838d95a78Smarkfen 			*diagnostic = SADB_X_DIAGNOSTIC_PAIR_ALREADY;
364938d95a78Smarkfen 			error = EINVAL;
365038d95a78Smarkfen 		} else {
365138d95a78Smarkfen 			/* update_pairing() sets diagnostic */
36525d3b8cb7SBill Sommerfeld 			error = update_pairing(&ipsapp, &sq, ksi, diagnostic);
365338d95a78Smarkfen 		}
365438d95a78Smarkfen 	}
36557c478bd9Sstevel@tonic-gate 	/* Common error point for this routine. */
36567c478bd9Sstevel@tonic-gate error:
36577c478bd9Sstevel@tonic-gate 	if (newbie != NULL) {
365838d95a78Smarkfen 		if (error != 0) {
365938d95a78Smarkfen 			/* This SA is broken, let the reaper clean up. */
366038d95a78Smarkfen 			mutex_enter(&newbie->ipsa_lock);
366138d95a78Smarkfen 			newbie->ipsa_state = IPSA_STATE_DEAD;
366238d95a78Smarkfen 			newbie->ipsa_hardexpiretime = 1;
366338d95a78Smarkfen 			mutex_exit(&newbie->ipsa_lock);
366438d95a78Smarkfen 		}
36657c478bd9Sstevel@tonic-gate 		IPSA_REFRELE(newbie);
36667c478bd9Sstevel@tonic-gate 	}
36677c478bd9Sstevel@tonic-gate 	if (newbie_clone != NULL) {
36687c478bd9Sstevel@tonic-gate 		IPSA_REFRELE(newbie_clone);
36697c478bd9Sstevel@tonic-gate 	}
36707c478bd9Sstevel@tonic-gate 
36717c478bd9Sstevel@tonic-gate 	if (error == 0) {
36727c478bd9Sstevel@tonic-gate 		/*
36737c478bd9Sstevel@tonic-gate 		 * Construct favorable PF_KEY return message and send to
367438d95a78Smarkfen 		 * keysock. Update the flags in the original keysock message
367538d95a78Smarkfen 		 * to reflect the actual flags in the new SA.
367638d95a78Smarkfen 		 *  (Q:  Do I need to pass "newbie"?  If I do,
36777c478bd9Sstevel@tonic-gate 		 * make sure to REFHOLD, call, then REFRELE.)
36787c478bd9Sstevel@tonic-gate 		 */
367938d95a78Smarkfen 		assoc->sadb_sa_flags = newbie->ipsa_flags;
36807c478bd9Sstevel@tonic-gate 		sadb_pfkey_echo(pfkey_q, mp, samsg, ksi, NULL);
36817c478bd9Sstevel@tonic-gate 	}
36827c478bd9Sstevel@tonic-gate 
36835d3b8cb7SBill Sommerfeld 	destroy_ipsa_pair(&ipsapp);
36847c478bd9Sstevel@tonic-gate 	return (error);
36857c478bd9Sstevel@tonic-gate }
36867c478bd9Sstevel@tonic-gate 
36877c478bd9Sstevel@tonic-gate /*
36887c478bd9Sstevel@tonic-gate  * Set the time of first use for a security association.  Update any
36897c478bd9Sstevel@tonic-gate  * expiration times as a result.
36907c478bd9Sstevel@tonic-gate  */
36917c478bd9Sstevel@tonic-gate void
36927c478bd9Sstevel@tonic-gate sadb_set_usetime(ipsa_t *assoc)
36937c478bd9Sstevel@tonic-gate {
3694437220cdSdanmcd 	time_t snapshot = gethrestime_sec();
3695437220cdSdanmcd 
36967c478bd9Sstevel@tonic-gate 	mutex_enter(&assoc->ipsa_lock);
3697437220cdSdanmcd 	assoc->ipsa_lastuse = snapshot;
36989c2c14abSThejaswini Singarajipura 	assoc->ipsa_idleexpiretime = snapshot + assoc->ipsa_idletime;
36999c2c14abSThejaswini Singarajipura 
37007c478bd9Sstevel@tonic-gate 	/*
37017c478bd9Sstevel@tonic-gate 	 * Caller does check usetime before calling me usually, and
37027c478bd9Sstevel@tonic-gate 	 * double-checking is better than a mutex_enter/exit hit.
37037c478bd9Sstevel@tonic-gate 	 */
37047c478bd9Sstevel@tonic-gate 	if (assoc->ipsa_usetime == 0) {
37057c478bd9Sstevel@tonic-gate 		/*
37067c478bd9Sstevel@tonic-gate 		 * This is redundant for outbound SA's, as
37077c478bd9Sstevel@tonic-gate 		 * ipsec_getassocbyconn() sets the IPSA_F_USED flag already.
37087c478bd9Sstevel@tonic-gate 		 * Inbound SAs, however, have no such protection.
37097c478bd9Sstevel@tonic-gate 		 */
37107c478bd9Sstevel@tonic-gate 		assoc->ipsa_flags |= IPSA_F_USED;
3711437220cdSdanmcd 		assoc->ipsa_usetime = snapshot;
37127c478bd9Sstevel@tonic-gate 
37137c478bd9Sstevel@tonic-gate 		/*
37147c478bd9Sstevel@tonic-gate 		 * After setting the use time, see if we have a use lifetime
37157c478bd9Sstevel@tonic-gate 		 * that would cause the actual SA expiration time to shorten.
37167c478bd9Sstevel@tonic-gate 		 */
37177c478bd9Sstevel@tonic-gate 		UPDATE_EXPIRE(assoc, softuselt, softexpiretime);
37187c478bd9Sstevel@tonic-gate 		UPDATE_EXPIRE(assoc, harduselt, hardexpiretime);
37197c478bd9Sstevel@tonic-gate 	}
37207c478bd9Sstevel@tonic-gate 	mutex_exit(&assoc->ipsa_lock);
37217c478bd9Sstevel@tonic-gate }
37227c478bd9Sstevel@tonic-gate 
37237c478bd9Sstevel@tonic-gate /*
37247c478bd9Sstevel@tonic-gate  * Send up a PF_KEY expire message for this association.
37257c478bd9Sstevel@tonic-gate  */
37267c478bd9Sstevel@tonic-gate static void
37277c478bd9Sstevel@tonic-gate sadb_expire_assoc(queue_t *pfkey_q, ipsa_t *assoc)
37287c478bd9Sstevel@tonic-gate {
37297c478bd9Sstevel@tonic-gate 	mblk_t *mp, *mp1;
37307c478bd9Sstevel@tonic-gate 	int alloclen, af;
37317c478bd9Sstevel@tonic-gate 	sadb_msg_t *samsg;
37327c478bd9Sstevel@tonic-gate 	sadb_lifetime_t *current, *expire;
37337c478bd9Sstevel@tonic-gate 	sadb_sa_t *saext;
37347c478bd9Sstevel@tonic-gate 	uint8_t *end;
37358810c16bSdanmcd 	boolean_t tunnel_mode;
37367c478bd9Sstevel@tonic-gate 
37377c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&assoc->ipsa_lock));
37387c478bd9Sstevel@tonic-gate 
37397c478bd9Sstevel@tonic-gate 	/* Don't bother sending if there's no queue. */
37407c478bd9Sstevel@tonic-gate 	if (pfkey_q == NULL)
37417c478bd9Sstevel@tonic-gate 		return;
37427c478bd9Sstevel@tonic-gate 
37437c478bd9Sstevel@tonic-gate 	mp = sadb_keysock_out(0);
37447c478bd9Sstevel@tonic-gate 	if (mp == NULL) {
37457c478bd9Sstevel@tonic-gate 		/* cmn_err(CE_WARN, */
37467c478bd9Sstevel@tonic-gate 		/*	"sadb_expire_assoc: Can't allocate KEYSOCK_OUT.\n"); */
37477c478bd9Sstevel@tonic-gate 		return;
37487c478bd9Sstevel@tonic-gate 	}
37497c478bd9Sstevel@tonic-gate 
37507c478bd9Sstevel@tonic-gate 	alloclen = sizeof (*samsg) + sizeof (*current) + sizeof (*expire) +
37517c478bd9Sstevel@tonic-gate 	    2 * sizeof (sadb_address_t) + sizeof (*saext);
37527c478bd9Sstevel@tonic-gate 
37537c478bd9Sstevel@tonic-gate 	af = assoc->ipsa_addrfam;
37547c478bd9Sstevel@tonic-gate 	switch (af) {
37557c478bd9Sstevel@tonic-gate 	case AF_INET:
37567c478bd9Sstevel@tonic-gate 		alloclen += 2 * sizeof (struct sockaddr_in);
37577c478bd9Sstevel@tonic-gate 		break;
37587c478bd9Sstevel@tonic-gate 	case AF_INET6:
37597c478bd9Sstevel@tonic-gate 		alloclen += 2 * sizeof (struct sockaddr_in6);
37607c478bd9Sstevel@tonic-gate 		break;
37617c478bd9Sstevel@tonic-gate 	default:
37627c478bd9Sstevel@tonic-gate 		/* Won't happen unless there's a kernel bug. */
37637c478bd9Sstevel@tonic-gate 		freeb(mp);
37647c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN,
37657c478bd9Sstevel@tonic-gate 		    "sadb_expire_assoc: Unknown address length.\n");
37667c478bd9Sstevel@tonic-gate 		return;
37677c478bd9Sstevel@tonic-gate 	}
37687c478bd9Sstevel@tonic-gate 
37698810c16bSdanmcd 	tunnel_mode = (assoc->ipsa_flags & IPSA_F_TUNNEL);
37708810c16bSdanmcd 	if (tunnel_mode) {
37718810c16bSdanmcd 		alloclen += 2 * sizeof (sadb_address_t);
37728810c16bSdanmcd 		switch (assoc->ipsa_innerfam) {
37738810c16bSdanmcd 		case AF_INET:
37748810c16bSdanmcd 			alloclen += 2 * sizeof (struct sockaddr_in);
37758810c16bSdanmcd 			break;
37768810c16bSdanmcd 		case AF_INET6:
37778810c16bSdanmcd 			alloclen += 2 * sizeof (struct sockaddr_in6);
37788810c16bSdanmcd 			break;
37798810c16bSdanmcd 		default:
37808810c16bSdanmcd 			/* Won't happen unless there's a kernel bug. */
37818810c16bSdanmcd 			freeb(mp);
37828810c16bSdanmcd 			cmn_err(CE_WARN, "sadb_expire_assoc: "
37838810c16bSdanmcd 			    "Unknown inner address length.\n");
37848810c16bSdanmcd 			return;
37858810c16bSdanmcd 		}
37868810c16bSdanmcd 	}
37878810c16bSdanmcd 
37887c478bd9Sstevel@tonic-gate 	mp->b_cont = allocb(alloclen, BPRI_HI);
37897c478bd9Sstevel@tonic-gate 	if (mp->b_cont == NULL) {
37907c478bd9Sstevel@tonic-gate 		freeb(mp);
37917c478bd9Sstevel@tonic-gate 		/* cmn_err(CE_WARN, */
37927c478bd9Sstevel@tonic-gate 		/*	"sadb_expire_assoc: Can't allocate message.\n"); */
37937c478bd9Sstevel@tonic-gate 		return;
37947c478bd9Sstevel@tonic-gate 	}
37957c478bd9Sstevel@tonic-gate 
37967c478bd9Sstevel@tonic-gate 	mp1 = mp;
37977c478bd9Sstevel@tonic-gate 	mp = mp->b_cont;
37987c478bd9Sstevel@tonic-gate 	end = mp->b_wptr + alloclen;
37997c478bd9Sstevel@tonic-gate 
38007c478bd9Sstevel@tonic-gate 	samsg = (sadb_msg_t *)mp->b_wptr;
38017c478bd9Sstevel@tonic-gate 	mp->b_wptr += sizeof (*samsg);
38027c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_version = PF_KEY_V2;
38037c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_type = SADB_EXPIRE;
38047c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_errno = 0;
38057c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_satype = assoc->ipsa_type;
38067c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_len = SADB_8TO64(alloclen);
38077c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_reserved = 0;
38087c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_seq = 0;
38097c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_pid = 0;
38107c478bd9Sstevel@tonic-gate 
38117c478bd9Sstevel@tonic-gate 	saext = (sadb_sa_t *)mp->b_wptr;
38127c478bd9Sstevel@tonic-gate 	mp->b_wptr += sizeof (*saext);
38137c478bd9Sstevel@tonic-gate 	saext->sadb_sa_len = SADB_8TO64(sizeof (*saext));
38147c478bd9Sstevel@tonic-gate 	saext->sadb_sa_exttype = SADB_EXT_SA;
38157c478bd9Sstevel@tonic-gate 	saext->sadb_sa_spi = assoc->ipsa_spi;
38167c478bd9Sstevel@tonic-gate 	saext->sadb_sa_replay = assoc->ipsa_replay_wsize;
38177c478bd9Sstevel@tonic-gate 	saext->sadb_sa_state = assoc->ipsa_state;
38187c478bd9Sstevel@tonic-gate 	saext->sadb_sa_auth = assoc->ipsa_auth_alg;
38197c478bd9Sstevel@tonic-gate 	saext->sadb_sa_encrypt = assoc->ipsa_encr_alg;
38207c478bd9Sstevel@tonic-gate 	saext->sadb_sa_flags = assoc->ipsa_flags;
38217c478bd9Sstevel@tonic-gate 
38227c478bd9Sstevel@tonic-gate 	current = (sadb_lifetime_t *)mp->b_wptr;
38237c478bd9Sstevel@tonic-gate 	mp->b_wptr += sizeof (sadb_lifetime_t);
38247c478bd9Sstevel@tonic-gate 	current->sadb_lifetime_len = SADB_8TO64(sizeof (*current));
38257c478bd9Sstevel@tonic-gate 	current->sadb_lifetime_exttype = SADB_EXT_LIFETIME_CURRENT;
3826437220cdSdanmcd 	/* We do not support the concept. */
3827437220cdSdanmcd 	current->sadb_lifetime_allocations = 0;
38287c478bd9Sstevel@tonic-gate 	current->sadb_lifetime_bytes = assoc->ipsa_bytes;
38297c478bd9Sstevel@tonic-gate 	current->sadb_lifetime_addtime = assoc->ipsa_addtime;
38307c478bd9Sstevel@tonic-gate 	current->sadb_lifetime_usetime = assoc->ipsa_usetime;
38317c478bd9Sstevel@tonic-gate 
38327c478bd9Sstevel@tonic-gate 	expire = (sadb_lifetime_t *)mp->b_wptr;
38337c478bd9Sstevel@tonic-gate 	mp->b_wptr += sizeof (*expire);
38347c478bd9Sstevel@tonic-gate 	expire->sadb_lifetime_len = SADB_8TO64(sizeof (*expire));
38357c478bd9Sstevel@tonic-gate 
38367c478bd9Sstevel@tonic-gate 	if (assoc->ipsa_state == IPSA_STATE_DEAD) {
38377c478bd9Sstevel@tonic-gate 		expire->sadb_lifetime_exttype = SADB_EXT_LIFETIME_HARD;
38387c478bd9Sstevel@tonic-gate 		expire->sadb_lifetime_allocations = assoc->ipsa_hardalloc;
38397c478bd9Sstevel@tonic-gate 		expire->sadb_lifetime_bytes = assoc->ipsa_hardbyteslt;
38407c478bd9Sstevel@tonic-gate 		expire->sadb_lifetime_addtime = assoc->ipsa_hardaddlt;
38417c478bd9Sstevel@tonic-gate 		expire->sadb_lifetime_usetime = assoc->ipsa_harduselt;
38429c2c14abSThejaswini Singarajipura 	} else if (assoc->ipsa_state == IPSA_STATE_DYING) {
38437c478bd9Sstevel@tonic-gate 		expire->sadb_lifetime_exttype = SADB_EXT_LIFETIME_SOFT;
38447c478bd9Sstevel@tonic-gate 		expire->sadb_lifetime_allocations = assoc->ipsa_softalloc;
38457c478bd9Sstevel@tonic-gate 		expire->sadb_lifetime_bytes = assoc->ipsa_softbyteslt;
38467c478bd9Sstevel@tonic-gate 		expire->sadb_lifetime_addtime = assoc->ipsa_softaddlt;
38477c478bd9Sstevel@tonic-gate 		expire->sadb_lifetime_usetime = assoc->ipsa_softuselt;
38489c2c14abSThejaswini Singarajipura 	} else {
38499c2c14abSThejaswini Singarajipura 		ASSERT(assoc->ipsa_state == IPSA_STATE_MATURE);
38509c2c14abSThejaswini Singarajipura 		expire->sadb_lifetime_exttype = SADB_X_EXT_LIFETIME_IDLE;
38519c2c14abSThejaswini Singarajipura 		expire->sadb_lifetime_allocations = 0;
38529c2c14abSThejaswini Singarajipura 		expire->sadb_lifetime_bytes = 0;
38539c2c14abSThejaswini Singarajipura 		expire->sadb_lifetime_addtime = assoc->ipsa_idleaddlt;
38549c2c14abSThejaswini Singarajipura 		expire->sadb_lifetime_usetime = assoc->ipsa_idleuselt;
38557c478bd9Sstevel@tonic-gate 	}
38567c478bd9Sstevel@tonic-gate 
38577c478bd9Sstevel@tonic-gate 	mp->b_wptr = sadb_make_addr_ext(mp->b_wptr, end, SADB_EXT_ADDRESS_SRC,
38588810c16bSdanmcd 	    af, assoc->ipsa_srcaddr, tunnel_mode ? 0 : SA_SRCPORT(assoc),
38598810c16bSdanmcd 	    SA_PROTO(assoc), 0);
38607c478bd9Sstevel@tonic-gate 	ASSERT(mp->b_wptr != NULL);
38617c478bd9Sstevel@tonic-gate 
38627c478bd9Sstevel@tonic-gate 	mp->b_wptr = sadb_make_addr_ext(mp->b_wptr, end, SADB_EXT_ADDRESS_DST,
38638810c16bSdanmcd 	    af, assoc->ipsa_dstaddr, tunnel_mode ? 0 : SA_DSTPORT(assoc),
38648810c16bSdanmcd 	    SA_PROTO(assoc), 0);
38657c478bd9Sstevel@tonic-gate 	ASSERT(mp->b_wptr != NULL);
38667c478bd9Sstevel@tonic-gate 
38678810c16bSdanmcd 	if (tunnel_mode) {
38688810c16bSdanmcd 		mp->b_wptr = sadb_make_addr_ext(mp->b_wptr, end,
38698810c16bSdanmcd 		    SADB_X_EXT_ADDRESS_INNER_SRC, assoc->ipsa_innerfam,
38708810c16bSdanmcd 		    assoc->ipsa_innersrc, SA_SRCPORT(assoc), SA_IPROTO(assoc),
38718810c16bSdanmcd 		    assoc->ipsa_innersrcpfx);
38728810c16bSdanmcd 		ASSERT(mp->b_wptr != NULL);
38738810c16bSdanmcd 		mp->b_wptr = sadb_make_addr_ext(mp->b_wptr, end,
38748810c16bSdanmcd 		    SADB_X_EXT_ADDRESS_INNER_DST, assoc->ipsa_innerfam,
38758810c16bSdanmcd 		    assoc->ipsa_innerdst, SA_DSTPORT(assoc), SA_IPROTO(assoc),
38768810c16bSdanmcd 		    assoc->ipsa_innerdstpfx);
38778810c16bSdanmcd 		ASSERT(mp->b_wptr != NULL);
38788810c16bSdanmcd 	}
38798810c16bSdanmcd 
38807c478bd9Sstevel@tonic-gate 	/* Can just putnext, we're ready to go! */
38817c478bd9Sstevel@tonic-gate 	putnext(pfkey_q, mp1);
38827c478bd9Sstevel@tonic-gate }
38837c478bd9Sstevel@tonic-gate 
38847c478bd9Sstevel@tonic-gate /*
38857c478bd9Sstevel@tonic-gate  * "Age" the SA with the number of bytes that was used to protect traffic.
38867c478bd9Sstevel@tonic-gate  * Send an SADB_EXPIRE message if appropriate.	Return B_TRUE if there was
38877c478bd9Sstevel@tonic-gate  * enough "charge" left in the SA to protect the data.	Return B_FALSE
38887c478bd9Sstevel@tonic-gate  * otherwise.  (If B_FALSE is returned, the association either was, or became
38897c478bd9Sstevel@tonic-gate  * DEAD.)
38907c478bd9Sstevel@tonic-gate  */
38917c478bd9Sstevel@tonic-gate boolean_t
38927c478bd9Sstevel@tonic-gate sadb_age_bytes(queue_t *pfkey_q, ipsa_t *assoc, uint64_t bytes,
38937c478bd9Sstevel@tonic-gate     boolean_t sendmsg)
38947c478bd9Sstevel@tonic-gate {
38957c478bd9Sstevel@tonic-gate 	boolean_t rc = B_TRUE;
38967c478bd9Sstevel@tonic-gate 	uint64_t newtotal;
38977c478bd9Sstevel@tonic-gate 
38987c478bd9Sstevel@tonic-gate 	mutex_enter(&assoc->ipsa_lock);
38997c478bd9Sstevel@tonic-gate 	newtotal = assoc->ipsa_bytes + bytes;
39007c478bd9Sstevel@tonic-gate 	if (assoc->ipsa_hardbyteslt != 0 &&
39017c478bd9Sstevel@tonic-gate 	    newtotal >= assoc->ipsa_hardbyteslt) {
39029c2c14abSThejaswini Singarajipura 		if (assoc->ipsa_state != IPSA_STATE_DEAD) {
39039c2c14abSThejaswini Singarajipura 			sadb_delete_cluster(assoc);
39047c478bd9Sstevel@tonic-gate 			/*
39057c478bd9Sstevel@tonic-gate 			 * Send EXPIRE message to PF_KEY.  May wish to pawn
39067c478bd9Sstevel@tonic-gate 			 * this off on another non-interrupt thread.  Also
39077c478bd9Sstevel@tonic-gate 			 * unlink this SA immediately.
39087c478bd9Sstevel@tonic-gate 			 */
39097c478bd9Sstevel@tonic-gate 			assoc->ipsa_state = IPSA_STATE_DEAD;
39107c478bd9Sstevel@tonic-gate 			if (sendmsg)
39117c478bd9Sstevel@tonic-gate 				sadb_expire_assoc(pfkey_q, assoc);
39127c478bd9Sstevel@tonic-gate 			/*
39137c478bd9Sstevel@tonic-gate 			 * Set non-zero expiration time so sadb_age_assoc()
39147c478bd9Sstevel@tonic-gate 			 * will work when reaping.
39157c478bd9Sstevel@tonic-gate 			 */
39167c478bd9Sstevel@tonic-gate 			assoc->ipsa_hardexpiretime = (time_t)1;
39177c478bd9Sstevel@tonic-gate 		} /* Else someone beat me to it! */
39187c478bd9Sstevel@tonic-gate 		rc = B_FALSE;
39197c478bd9Sstevel@tonic-gate 	} else if (assoc->ipsa_softbyteslt != 0 &&
39207c478bd9Sstevel@tonic-gate 	    (newtotal >= assoc->ipsa_softbyteslt)) {
39217c478bd9Sstevel@tonic-gate 		if (assoc->ipsa_state < IPSA_STATE_DYING) {
39227c478bd9Sstevel@tonic-gate 			/*
39237c478bd9Sstevel@tonic-gate 			 * Send EXPIRE message to PF_KEY.  May wish to pawn
39247c478bd9Sstevel@tonic-gate 			 * this off on another non-interrupt thread.
39257c478bd9Sstevel@tonic-gate 			 */
39267c478bd9Sstevel@tonic-gate 			assoc->ipsa_state = IPSA_STATE_DYING;
392750283218Smarkfen 			assoc->ipsa_bytes = newtotal;
39287c478bd9Sstevel@tonic-gate 			if (sendmsg)
39297c478bd9Sstevel@tonic-gate 				sadb_expire_assoc(pfkey_q, assoc);
39307c478bd9Sstevel@tonic-gate 		} /* Else someone beat me to it! */
39317c478bd9Sstevel@tonic-gate 	}
39327c478bd9Sstevel@tonic-gate 	if (rc == B_TRUE)
39337c478bd9Sstevel@tonic-gate 		assoc->ipsa_bytes = newtotal;
39347c478bd9Sstevel@tonic-gate 	mutex_exit(&assoc->ipsa_lock);
39357c478bd9Sstevel@tonic-gate 	return (rc);
39367c478bd9Sstevel@tonic-gate }
39377c478bd9Sstevel@tonic-gate 
39387c478bd9Sstevel@tonic-gate /*
39397c478bd9Sstevel@tonic-gate  * "Torch" an individual SA.  Returns NULL, so it can be tail-called from
39407c478bd9Sstevel@tonic-gate  *     sadb_age_assoc().
39417c478bd9Sstevel@tonic-gate  */
39427c478bd9Sstevel@tonic-gate static ipsa_t *
3943bd670b35SErik Nordmark sadb_torch_assoc(isaf_t *head, ipsa_t *sa)
39447c478bd9Sstevel@tonic-gate {
39457c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&head->isaf_lock));
39467c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&sa->ipsa_lock));
39477c478bd9Sstevel@tonic-gate 	ASSERT(sa->ipsa_state == IPSA_STATE_DEAD);
39487c478bd9Sstevel@tonic-gate 
39497c478bd9Sstevel@tonic-gate 	/*
39507c478bd9Sstevel@tonic-gate 	 * Force cached SAs to be revalidated..
39517c478bd9Sstevel@tonic-gate 	 */
39527c478bd9Sstevel@tonic-gate 	head->isaf_gen++;
39537c478bd9Sstevel@tonic-gate 
39547c478bd9Sstevel@tonic-gate 	mutex_exit(&sa->ipsa_lock);
39557c478bd9Sstevel@tonic-gate 	sadb_unlinkassoc(sa);
39567c478bd9Sstevel@tonic-gate 
39577c478bd9Sstevel@tonic-gate 	return (NULL);
39587c478bd9Sstevel@tonic-gate }
39597c478bd9Sstevel@tonic-gate 
39607c478bd9Sstevel@tonic-gate /*
3961437220cdSdanmcd  * Do various SA-is-idle activities depending on delta (the number of idle
3962437220cdSdanmcd  * seconds on the SA) and/or other properties of the SA.
3963437220cdSdanmcd  *
3964437220cdSdanmcd  * Return B_TRUE if I've sent a packet, because I have to drop the
3965437220cdSdanmcd  * association's mutex before sending a packet out the wire.
3966437220cdSdanmcd  */
3967437220cdSdanmcd /* ARGSUSED */
3968437220cdSdanmcd static boolean_t
3969437220cdSdanmcd sadb_idle_activities(ipsa_t *assoc, time_t delta, boolean_t inbound)
3970437220cdSdanmcd {
3971437220cdSdanmcd 	ipsecesp_stack_t *espstack = assoc->ipsa_netstack->netstack_ipsecesp;
3972437220cdSdanmcd 	int nat_t_interval = espstack->ipsecesp_nat_keepalive_interval;
3973437220cdSdanmcd 
3974437220cdSdanmcd 	ASSERT(MUTEX_HELD(&assoc->ipsa_lock));
3975437220cdSdanmcd 
3976437220cdSdanmcd 	if (!inbound && (assoc->ipsa_flags & IPSA_F_NATT_LOC) &&
3977437220cdSdanmcd 	    delta >= nat_t_interval &&
3978437220cdSdanmcd 	    gethrestime_sec() - assoc->ipsa_last_nat_t_ka >= nat_t_interval) {
3979437220cdSdanmcd 		ASSERT(assoc->ipsa_type == SADB_SATYPE_ESP);
3980437220cdSdanmcd 		assoc->ipsa_last_nat_t_ka = gethrestime_sec();
3981437220cdSdanmcd 		mutex_exit(&assoc->ipsa_lock);
3982437220cdSdanmcd 		ipsecesp_send_keepalive(assoc);
3983437220cdSdanmcd 		return (B_TRUE);
3984437220cdSdanmcd 	}
3985437220cdSdanmcd 	return (B_FALSE);
3986437220cdSdanmcd }
3987437220cdSdanmcd 
3988437220cdSdanmcd /*
398938d95a78Smarkfen  * Return "assoc" if haspeer is true and I send an expire.  This allows
39907c478bd9Sstevel@tonic-gate  * the consumers' aging functions to tidy up an expired SA's peer.
39917c478bd9Sstevel@tonic-gate  */
39927c478bd9Sstevel@tonic-gate static ipsa_t *
39937c478bd9Sstevel@tonic-gate sadb_age_assoc(isaf_t *head, queue_t *pfkey_q, ipsa_t *assoc,
3994bd670b35SErik Nordmark     time_t current, int reap_delay, boolean_t inbound)
39957c478bd9Sstevel@tonic-gate {
39967c478bd9Sstevel@tonic-gate 	ipsa_t *retval = NULL;
3997437220cdSdanmcd 	boolean_t dropped_mutex = B_FALSE;
39987c478bd9Sstevel@tonic-gate 
39997c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&head->isaf_lock));
40007c478bd9Sstevel@tonic-gate 
40017c478bd9Sstevel@tonic-gate 	mutex_enter(&assoc->ipsa_lock);
40027c478bd9Sstevel@tonic-gate 
40039c2c14abSThejaswini Singarajipura 	if (((assoc->ipsa_state == IPSA_STATE_LARVAL) ||
40049c2c14abSThejaswini Singarajipura 	    ((assoc->ipsa_state == IPSA_STATE_IDLE) ||
40059c2c14abSThejaswini Singarajipura 	    (assoc->ipsa_state == IPSA_STATE_ACTIVE_ELSEWHERE) &&
40069c2c14abSThejaswini Singarajipura 	    (assoc->ipsa_hardexpiretime != 0))) &&
40077c478bd9Sstevel@tonic-gate 	    (assoc->ipsa_hardexpiretime <= current)) {
40087c478bd9Sstevel@tonic-gate 		assoc->ipsa_state = IPSA_STATE_DEAD;
4009bd670b35SErik Nordmark 		return (sadb_torch_assoc(head, assoc));
40107c478bd9Sstevel@tonic-gate 	}
40117c478bd9Sstevel@tonic-gate 
40127c478bd9Sstevel@tonic-gate 	/*
40137c478bd9Sstevel@tonic-gate 	 * Check lifetimes.  Fortunately, SA setup is done
40147c478bd9Sstevel@tonic-gate 	 * such that there are only two times to look at,
40157c478bd9Sstevel@tonic-gate 	 * softexpiretime, and hardexpiretime.
40167c478bd9Sstevel@tonic-gate 	 *
40177c478bd9Sstevel@tonic-gate 	 * Check hard first.
40187c478bd9Sstevel@tonic-gate 	 */
40197c478bd9Sstevel@tonic-gate 
40207c478bd9Sstevel@tonic-gate 	if (assoc->ipsa_hardexpiretime != 0 &&
40217c478bd9Sstevel@tonic-gate 	    assoc->ipsa_hardexpiretime <= current) {
40227c478bd9Sstevel@tonic-gate 		if (assoc->ipsa_state == IPSA_STATE_DEAD)
4023bd670b35SErik Nordmark 			return (sadb_torch_assoc(head, assoc));
40247c478bd9Sstevel@tonic-gate 
40259c2c14abSThejaswini Singarajipura 		if (inbound) {
40269c2c14abSThejaswini Singarajipura 			sadb_delete_cluster(assoc);
40279c2c14abSThejaswini Singarajipura 		}
40289c2c14abSThejaswini Singarajipura 
40297c478bd9Sstevel@tonic-gate 		/*
40307c478bd9Sstevel@tonic-gate 		 * Send SADB_EXPIRE with hard lifetime, delay for unlinking.
40317c478bd9Sstevel@tonic-gate 		 */
40327c478bd9Sstevel@tonic-gate 		assoc->ipsa_state = IPSA_STATE_DEAD;
403338d95a78Smarkfen 		if (assoc->ipsa_haspeer || assoc->ipsa_otherspi != 0) {
40347c478bd9Sstevel@tonic-gate 			/*
403538d95a78Smarkfen 			 * If the SA is paired or peered with another, put
403638d95a78Smarkfen 			 * a copy on a list which can be processed later, the
403738d95a78Smarkfen 			 * pair/peer SA needs to be updated so the both die
403838d95a78Smarkfen 			 * at the same time.
403938d95a78Smarkfen 			 *
404038d95a78Smarkfen 			 * If I return assoc, I have to bump up its reference
404138d95a78Smarkfen 			 * count to keep with the ipsa_t reference count
404238d95a78Smarkfen 			 * semantics.
40437c478bd9Sstevel@tonic-gate 			 */
40447c478bd9Sstevel@tonic-gate 			IPSA_REFHOLD(assoc);
40457c478bd9Sstevel@tonic-gate 			retval = assoc;
40467c478bd9Sstevel@tonic-gate 		}
40477c478bd9Sstevel@tonic-gate 		sadb_expire_assoc(pfkey_q, assoc);
40487c478bd9Sstevel@tonic-gate 		assoc->ipsa_hardexpiretime = current + reap_delay;
40497c478bd9Sstevel@tonic-gate 	} else if (assoc->ipsa_softexpiretime != 0 &&
40507c478bd9Sstevel@tonic-gate 	    assoc->ipsa_softexpiretime <= current &&
40517c478bd9Sstevel@tonic-gate 	    assoc->ipsa_state < IPSA_STATE_DYING) {
40527c478bd9Sstevel@tonic-gate 		/*
40537c478bd9Sstevel@tonic-gate 		 * Send EXPIRE message to PF_KEY.  May wish to pawn
40547c478bd9Sstevel@tonic-gate 		 * this off on another non-interrupt thread.
40557c478bd9Sstevel@tonic-gate 		 */
40567c478bd9Sstevel@tonic-gate 		assoc->ipsa_state = IPSA_STATE_DYING;
40577c478bd9Sstevel@tonic-gate 		if (assoc->ipsa_haspeer) {
40587c478bd9Sstevel@tonic-gate 			/*
405938d95a78Smarkfen 			 * If the SA has a peer, update the peer's state
406038d95a78Smarkfen 			 * on SOFT_EXPIRE, this is mostly to prevent two
406138d95a78Smarkfen 			 * expire messages from effectively the same SA.
406238d95a78Smarkfen 			 *
406338d95a78Smarkfen 			 * Don't care about paired SA's, then can (and should)
406438d95a78Smarkfen 			 * be able to soft expire at different times.
406538d95a78Smarkfen 			 *
40667c478bd9Sstevel@tonic-gate 			 * If I return assoc, I have to bump up its
40677c478bd9Sstevel@tonic-gate 			 * reference count to keep with the ipsa_t reference
40687c478bd9Sstevel@tonic-gate 			 * count semantics.
40697c478bd9Sstevel@tonic-gate 			 */
40707c478bd9Sstevel@tonic-gate 			IPSA_REFHOLD(assoc);
40717c478bd9Sstevel@tonic-gate 			retval = assoc;
40727c478bd9Sstevel@tonic-gate 		}
40737c478bd9Sstevel@tonic-gate 		sadb_expire_assoc(pfkey_q, assoc);
40749c2c14abSThejaswini Singarajipura 	} else if (assoc->ipsa_idletime != 0 &&
40759c2c14abSThejaswini Singarajipura 	    assoc->ipsa_idleexpiretime <= current) {
40769c2c14abSThejaswini Singarajipura 		if (assoc->ipsa_state == IPSA_STATE_ACTIVE_ELSEWHERE) {
40779c2c14abSThejaswini Singarajipura 			assoc->ipsa_state = IPSA_STATE_IDLE;
40789c2c14abSThejaswini Singarajipura 		}
40799c2c14abSThejaswini Singarajipura 
40809c2c14abSThejaswini Singarajipura 		/*
40819c2c14abSThejaswini Singarajipura 		 * Need to handle Mature case
40829c2c14abSThejaswini Singarajipura 		 */
4083a14de6c8SDan McDonald 		if (assoc->ipsa_state == IPSA_STATE_MATURE) {
40849c2c14abSThejaswini Singarajipura 			sadb_expire_assoc(pfkey_q, assoc);
40859c2c14abSThejaswini Singarajipura 		}
4086437220cdSdanmcd 	} else {
4087437220cdSdanmcd 		/* Check idle time activities. */
4088437220cdSdanmcd 		dropped_mutex = sadb_idle_activities(assoc,
4089437220cdSdanmcd 		    current - assoc->ipsa_lastuse, inbound);
40907c478bd9Sstevel@tonic-gate 	}
40917c478bd9Sstevel@tonic-gate 
4092437220cdSdanmcd 	if (!dropped_mutex)
40937c478bd9Sstevel@tonic-gate 		mutex_exit(&assoc->ipsa_lock);
40947c478bd9Sstevel@tonic-gate 	return (retval);
40957c478bd9Sstevel@tonic-gate }
40967c478bd9Sstevel@tonic-gate 
40977c478bd9Sstevel@tonic-gate /*
40987c478bd9Sstevel@tonic-gate  * Called by a consumer protocol to do ther dirty work of reaping dead
40997c478bd9Sstevel@tonic-gate  * Security Associations.
410038d95a78Smarkfen  *
410138d95a78Smarkfen  * NOTE: sadb_age_assoc() marks expired SA's as DEAD but only removed
410238d95a78Smarkfen  * SA's that are already marked DEAD, so expired SA's are only reaped
410338d95a78Smarkfen  * the second time sadb_ager() runs.
41047c478bd9Sstevel@tonic-gate  */
41057c478bd9Sstevel@tonic-gate void
4106bd670b35SErik Nordmark sadb_ager(sadb_t *sp, queue_t *pfkey_q, int reap_delay, netstack_t *ns)
41077c478bd9Sstevel@tonic-gate {
41087c478bd9Sstevel@tonic-gate 	int i;
41097c478bd9Sstevel@tonic-gate 	isaf_t *bucket;
41107c478bd9Sstevel@tonic-gate 	ipsa_t *assoc, *spare;
41117c478bd9Sstevel@tonic-gate 	iacqf_t *acqlist;
41127c478bd9Sstevel@tonic-gate 	ipsacq_t *acqrec, *spareacq;
411338d95a78Smarkfen 	templist_t *haspeerlist, *newbie;
4114437220cdSdanmcd 	/* Snapshot current time now. */
4115437220cdSdanmcd 	time_t current = gethrestime_sec();
411638d95a78Smarkfen 	haspeerlist = NULL;
41177c478bd9Sstevel@tonic-gate 
41187c478bd9Sstevel@tonic-gate 	/*
41197c478bd9Sstevel@tonic-gate 	 * Do my dirty work.  This includes aging real entries, aging
41207c478bd9Sstevel@tonic-gate 	 * larvals, and aging outstanding ACQUIREs.
41217c478bd9Sstevel@tonic-gate 	 *
41227c478bd9Sstevel@tonic-gate 	 * I hope I don't tie up resources for too long.
41237c478bd9Sstevel@tonic-gate 	 */
41247c478bd9Sstevel@tonic-gate 
41257c478bd9Sstevel@tonic-gate 	/* Age acquires. */
41267c478bd9Sstevel@tonic-gate 
4127fb87b5d2Ssommerfe 	for (i = 0; i < sp->sdb_hashsize; i++) {
41287c478bd9Sstevel@tonic-gate 		acqlist = &sp->sdb_acq[i];
41297c478bd9Sstevel@tonic-gate 		mutex_enter(&acqlist->iacqf_lock);
41307c478bd9Sstevel@tonic-gate 		for (acqrec = acqlist->iacqf_ipsacq; acqrec != NULL;
41317c478bd9Sstevel@tonic-gate 		    acqrec = spareacq) {
41327c478bd9Sstevel@tonic-gate 			spareacq = acqrec->ipsacq_next;
41337c478bd9Sstevel@tonic-gate 			if (current > acqrec->ipsacq_expire)
4134f4b3ec61Sdh155122 				sadb_destroy_acquire(acqrec, ns);
41357c478bd9Sstevel@tonic-gate 		}
41367c478bd9Sstevel@tonic-gate 		mutex_exit(&acqlist->iacqf_lock);
41377c478bd9Sstevel@tonic-gate 	}
41387c478bd9Sstevel@tonic-gate 
41397c478bd9Sstevel@tonic-gate 	/* Age inbound associations. */
4140fb87b5d2Ssommerfe 	for (i = 0; i < sp->sdb_hashsize; i++) {
41417c478bd9Sstevel@tonic-gate 		bucket = &(sp->sdb_if[i]);
41427c478bd9Sstevel@tonic-gate 		mutex_enter(&bucket->isaf_lock);
41437c478bd9Sstevel@tonic-gate 		for (assoc = bucket->isaf_ipsa; assoc != NULL;
41447c478bd9Sstevel@tonic-gate 		    assoc = spare) {
41457c478bd9Sstevel@tonic-gate 			spare = assoc->ipsa_next;
41467c478bd9Sstevel@tonic-gate 			if (sadb_age_assoc(bucket, pfkey_q, assoc, current,
4147bd670b35SErik Nordmark 			    reap_delay, B_TRUE) != NULL) {
41487c478bd9Sstevel@tonic-gate 				/*
414938d95a78Smarkfen 				 * Put SA's which have a peer or SA's which
415038d95a78Smarkfen 				 * are paired on a list for processing after
415138d95a78Smarkfen 				 * all the hash tables have been walked.
415238d95a78Smarkfen 				 *
41537c478bd9Sstevel@tonic-gate 				 * sadb_age_assoc() increments the refcnt,
41547c478bd9Sstevel@tonic-gate 				 * effectively doing an IPSA_REFHOLD().
41557c478bd9Sstevel@tonic-gate 				 */
41567c478bd9Sstevel@tonic-gate 				newbie = kmem_alloc(sizeof (*newbie),
41577c478bd9Sstevel@tonic-gate 				    KM_NOSLEEP);
41587c478bd9Sstevel@tonic-gate 				if (newbie == NULL) {
41597c478bd9Sstevel@tonic-gate 					/*
41607c478bd9Sstevel@tonic-gate 					 * Don't forget to REFRELE().
41617c478bd9Sstevel@tonic-gate 					 */
41627c478bd9Sstevel@tonic-gate 					IPSA_REFRELE(assoc);
41637c478bd9Sstevel@tonic-gate 					continue;	/* for loop... */
41647c478bd9Sstevel@tonic-gate 				}
41657c478bd9Sstevel@tonic-gate 				newbie->next = haspeerlist;
41667c478bd9Sstevel@tonic-gate 				newbie->ipsa = assoc;
41677c478bd9Sstevel@tonic-gate 				haspeerlist = newbie;
41687c478bd9Sstevel@tonic-gate 			}
41697c478bd9Sstevel@tonic-gate 		}
41707c478bd9Sstevel@tonic-gate 		mutex_exit(&bucket->isaf_lock);
41717c478bd9Sstevel@tonic-gate 	}
41727c478bd9Sstevel@tonic-gate 
417338d95a78Smarkfen 	age_pair_peer_list(haspeerlist, sp, B_FALSE);
417438d95a78Smarkfen 	haspeerlist = NULL;
41757c478bd9Sstevel@tonic-gate 
41767c478bd9Sstevel@tonic-gate 	/* Age outbound associations. */
4177fb87b5d2Ssommerfe 	for (i = 0; i < sp->sdb_hashsize; i++) {
41787c478bd9Sstevel@tonic-gate 		bucket = &(sp->sdb_of[i]);
41797c478bd9Sstevel@tonic-gate 		mutex_enter(&bucket->isaf_lock);
41807c478bd9Sstevel@tonic-gate 		for (assoc = bucket->isaf_ipsa; assoc != NULL;
41817c478bd9Sstevel@tonic-gate 		    assoc = spare) {
41827c478bd9Sstevel@tonic-gate 			spare = assoc->ipsa_next;
41837c478bd9Sstevel@tonic-gate 			if (sadb_age_assoc(bucket, pfkey_q, assoc, current,
4184bd670b35SErik Nordmark 			    reap_delay, B_FALSE) != NULL) {
41857c478bd9Sstevel@tonic-gate 				/*
41867c478bd9Sstevel@tonic-gate 				 * sadb_age_assoc() increments the refcnt,
41877c478bd9Sstevel@tonic-gate 				 * effectively doing an IPSA_REFHOLD().
41887c478bd9Sstevel@tonic-gate 				 */
41897c478bd9Sstevel@tonic-gate 				newbie = kmem_alloc(sizeof (*newbie),
41907c478bd9Sstevel@tonic-gate 				    KM_NOSLEEP);
41917c478bd9Sstevel@tonic-gate 				if (newbie == NULL) {
41927c478bd9Sstevel@tonic-gate 					/*
41937c478bd9Sstevel@tonic-gate 					 * Don't forget to REFRELE().
41947c478bd9Sstevel@tonic-gate 					 */
41957c478bd9Sstevel@tonic-gate 					IPSA_REFRELE(assoc);
41967c478bd9Sstevel@tonic-gate 					continue;	/* for loop... */
41977c478bd9Sstevel@tonic-gate 				}
41987c478bd9Sstevel@tonic-gate 				newbie->next = haspeerlist;
41997c478bd9Sstevel@tonic-gate 				newbie->ipsa = assoc;
42007c478bd9Sstevel@tonic-gate 				haspeerlist = newbie;
42017c478bd9Sstevel@tonic-gate 			}
42027c478bd9Sstevel@tonic-gate 		}
42037c478bd9Sstevel@tonic-gate 		mutex_exit(&bucket->isaf_lock);
42047c478bd9Sstevel@tonic-gate 	}
420538d95a78Smarkfen 
420638d95a78Smarkfen 	age_pair_peer_list(haspeerlist, sp, B_TRUE);
420738d95a78Smarkfen 
42087c478bd9Sstevel@tonic-gate 	/*
42097c478bd9Sstevel@tonic-gate 	 * Run a GC pass to clean out dead identities.
42107c478bd9Sstevel@tonic-gate 	 */
4211f4b3ec61Sdh155122 	ipsid_gc(ns);
42127c478bd9Sstevel@tonic-gate }
42137c478bd9Sstevel@tonic-gate 
42147c478bd9Sstevel@tonic-gate /*
42157c478bd9Sstevel@tonic-gate  * Figure out when to reschedule the ager.
42167c478bd9Sstevel@tonic-gate  */
42177c478bd9Sstevel@tonic-gate timeout_id_t
42187c478bd9Sstevel@tonic-gate sadb_retimeout(hrtime_t begin, queue_t *pfkey_q, void (*ager)(void *),
4219f4b3ec61Sdh155122     void *agerarg, uint_t *intp, uint_t intmax, short mid)
42207c478bd9Sstevel@tonic-gate {
42217c478bd9Sstevel@tonic-gate 	hrtime_t end = gethrtime();
4222cf0ee00aSDan McDonald 	uint_t interval = *intp;	/* "interval" is in ms. */
42237c478bd9Sstevel@tonic-gate 
42247c478bd9Sstevel@tonic-gate 	/*
42257c478bd9Sstevel@tonic-gate 	 * See how long this took.  If it took too long, increase the
42267c478bd9Sstevel@tonic-gate 	 * aging interval.
42277c478bd9Sstevel@tonic-gate 	 */
4228cf0ee00aSDan McDonald 	if ((end - begin) > MSEC2NSEC(interval)) {
42297c478bd9Sstevel@tonic-gate 		if (interval >= intmax) {
42307c478bd9Sstevel@tonic-gate 			/* XXX Rate limit this?  Or recommend flush? */
42317c478bd9Sstevel@tonic-gate 			(void) strlog(mid, 0, 0, SL_ERROR | SL_WARN,
42327c478bd9Sstevel@tonic-gate 			    "Too many SA's to age out in %d msec.\n",
42337c478bd9Sstevel@tonic-gate 			    intmax);
42347c478bd9Sstevel@tonic-gate 		} else {
42357c478bd9Sstevel@tonic-gate 			/* Double by shifting by one bit. */
42367c478bd9Sstevel@tonic-gate 			interval <<= 1;
42377c478bd9Sstevel@tonic-gate 			interval = min(interval, intmax);
42387c478bd9Sstevel@tonic-gate 		}
4239cf0ee00aSDan McDonald 	} else if ((end - begin) <= (MSEC2NSEC(interval) / 2) &&
42407c478bd9Sstevel@tonic-gate 	    interval > SADB_AGE_INTERVAL_DEFAULT) {
42417c478bd9Sstevel@tonic-gate 		/*
42427c478bd9Sstevel@tonic-gate 		 * If I took less than half of the interval, then I should
42437c478bd9Sstevel@tonic-gate 		 * ratchet the interval back down.  Never automatically
42447c478bd9Sstevel@tonic-gate 		 * shift below the default aging interval.
42457c478bd9Sstevel@tonic-gate 		 *
42467c478bd9Sstevel@tonic-gate 		 * NOTE:This even overrides manual setting of the age
42470e9b5742SDan McDonald 		 *	interval using NDD to lower the setting past the
42480e9b5742SDan McDonald 		 *	default.  In other words, if you set the interval
42490e9b5742SDan McDonald 		 *	lower than the default, and your SADB gets too big,
42500e9b5742SDan McDonald 		 *	the interval will only self-lower back to the default.
42517c478bd9Sstevel@tonic-gate 		 */
42527c478bd9Sstevel@tonic-gate 		/* Halve by shifting one bit. */
42537c478bd9Sstevel@tonic-gate 		interval >>= 1;
42547c478bd9Sstevel@tonic-gate 		interval = max(interval, SADB_AGE_INTERVAL_DEFAULT);
42557c478bd9Sstevel@tonic-gate 	}
42567c478bd9Sstevel@tonic-gate 	*intp = interval;
4257f4b3ec61Sdh155122 	return (qtimeout(pfkey_q, ager, agerarg,
4258cf0ee00aSDan McDonald 	    drv_usectohz(interval * (MICROSEC / MILLISEC))));
42597c478bd9Sstevel@tonic-gate }
42607c478bd9Sstevel@tonic-gate 
42617c478bd9Sstevel@tonic-gate 
42627c478bd9Sstevel@tonic-gate /*
42637c478bd9Sstevel@tonic-gate  * Update the lifetime values of an SA.	 This is the path an SADB_UPDATE
42647c478bd9Sstevel@tonic-gate  * message takes when updating a MATURE or DYING SA.
42657c478bd9Sstevel@tonic-gate  */
42667c478bd9Sstevel@tonic-gate static void
42677c478bd9Sstevel@tonic-gate sadb_update_lifetimes(ipsa_t *assoc, sadb_lifetime_t *hard,
42689c2c14abSThejaswini Singarajipura     sadb_lifetime_t *soft, sadb_lifetime_t *idle, boolean_t outbound)
42697c478bd9Sstevel@tonic-gate {
42707c478bd9Sstevel@tonic-gate 	mutex_enter(&assoc->ipsa_lock);
42717c478bd9Sstevel@tonic-gate 
42727c478bd9Sstevel@tonic-gate 	/*
42737c478bd9Sstevel@tonic-gate 	 * XXX RFC 2367 mentions how an SADB_EXT_LIFETIME_CURRENT can be
42747c478bd9Sstevel@tonic-gate 	 * passed in during an update message.	We currently don't handle
42757c478bd9Sstevel@tonic-gate 	 * these.
42767c478bd9Sstevel@tonic-gate 	 */
42777c478bd9Sstevel@tonic-gate 
42787c478bd9Sstevel@tonic-gate 	if (hard != NULL) {
42797c478bd9Sstevel@tonic-gate 		if (hard->sadb_lifetime_bytes != 0)
42807c478bd9Sstevel@tonic-gate 			assoc->ipsa_hardbyteslt = hard->sadb_lifetime_bytes;
42817c478bd9Sstevel@tonic-gate 		if (hard->sadb_lifetime_usetime != 0)
42827c478bd9Sstevel@tonic-gate 			assoc->ipsa_harduselt = hard->sadb_lifetime_usetime;
42837c478bd9Sstevel@tonic-gate 		if (hard->sadb_lifetime_addtime != 0)
42847c478bd9Sstevel@tonic-gate 			assoc->ipsa_hardaddlt = hard->sadb_lifetime_addtime;
42857c478bd9Sstevel@tonic-gate 		if (assoc->ipsa_hardaddlt != 0) {
42867c478bd9Sstevel@tonic-gate 			assoc->ipsa_hardexpiretime =
42877c478bd9Sstevel@tonic-gate 			    assoc->ipsa_addtime + assoc->ipsa_hardaddlt;
42887c478bd9Sstevel@tonic-gate 		}
428938d95a78Smarkfen 		if (assoc->ipsa_harduselt != 0 &&
429038d95a78Smarkfen 		    assoc->ipsa_flags & IPSA_F_USED) {
429138d95a78Smarkfen 			UPDATE_EXPIRE(assoc, harduselt, hardexpiretime);
42927c478bd9Sstevel@tonic-gate 		}
42937c478bd9Sstevel@tonic-gate 		if (hard->sadb_lifetime_allocations != 0)
42947c478bd9Sstevel@tonic-gate 			assoc->ipsa_hardalloc = hard->sadb_lifetime_allocations;
42957c478bd9Sstevel@tonic-gate 	}
42967c478bd9Sstevel@tonic-gate 
42977c478bd9Sstevel@tonic-gate 	if (soft != NULL) {
429838d95a78Smarkfen 		if (soft->sadb_lifetime_bytes != 0) {
429938d95a78Smarkfen 			if (soft->sadb_lifetime_bytes >
430038d95a78Smarkfen 			    assoc->ipsa_hardbyteslt) {
430138d95a78Smarkfen 				assoc->ipsa_softbyteslt =
430238d95a78Smarkfen 				    assoc->ipsa_hardbyteslt;
430338d95a78Smarkfen 			} else {
430438d95a78Smarkfen 				assoc->ipsa_softbyteslt =
430538d95a78Smarkfen 				    soft->sadb_lifetime_bytes;
430638d95a78Smarkfen 			}
430738d95a78Smarkfen 		}
430838d95a78Smarkfen 		if (soft->sadb_lifetime_usetime != 0) {
430938d95a78Smarkfen 			if (soft->sadb_lifetime_usetime >
431038d95a78Smarkfen 			    assoc->ipsa_harduselt) {
431138d95a78Smarkfen 				assoc->ipsa_softuselt =
431238d95a78Smarkfen 				    assoc->ipsa_harduselt;
431338d95a78Smarkfen 			} else {
431438d95a78Smarkfen 				assoc->ipsa_softuselt =
431538d95a78Smarkfen 				    soft->sadb_lifetime_usetime;
431638d95a78Smarkfen 			}
431738d95a78Smarkfen 		}
431838d95a78Smarkfen 		if (soft->sadb_lifetime_addtime != 0) {
431938d95a78Smarkfen 			if (soft->sadb_lifetime_addtime >
432038d95a78Smarkfen 			    assoc->ipsa_hardexpiretime) {
432138d95a78Smarkfen 				assoc->ipsa_softexpiretime =
432238d95a78Smarkfen 				    assoc->ipsa_hardexpiretime;
432338d95a78Smarkfen 			} else {
432438d95a78Smarkfen 				assoc->ipsa_softaddlt =
432538d95a78Smarkfen 				    soft->sadb_lifetime_addtime;
432638d95a78Smarkfen 			}
432738d95a78Smarkfen 		}
43287c478bd9Sstevel@tonic-gate 		if (assoc->ipsa_softaddlt != 0) {
43297c478bd9Sstevel@tonic-gate 			assoc->ipsa_softexpiretime =
43307c478bd9Sstevel@tonic-gate 			    assoc->ipsa_addtime + assoc->ipsa_softaddlt;
43317c478bd9Sstevel@tonic-gate 		}
433238d95a78Smarkfen 		if (assoc->ipsa_softuselt != 0 &&
433338d95a78Smarkfen 		    assoc->ipsa_flags & IPSA_F_USED) {
433438d95a78Smarkfen 			UPDATE_EXPIRE(assoc, softuselt, softexpiretime);
43357c478bd9Sstevel@tonic-gate 		}
433638d95a78Smarkfen 		if (outbound && assoc->ipsa_softexpiretime != 0) {
433738d95a78Smarkfen 			if (assoc->ipsa_state == IPSA_STATE_MATURE)
433838d95a78Smarkfen 				lifetime_fuzz(assoc);
43397c478bd9Sstevel@tonic-gate 		}
43407c478bd9Sstevel@tonic-gate 
43417c478bd9Sstevel@tonic-gate 		if (soft->sadb_lifetime_allocations != 0)
43427c478bd9Sstevel@tonic-gate 			assoc->ipsa_softalloc = soft->sadb_lifetime_allocations;
43437c478bd9Sstevel@tonic-gate 	}
43449c2c14abSThejaswini Singarajipura 
43459c2c14abSThejaswini Singarajipura 	if (idle != NULL) {
43469c2c14abSThejaswini Singarajipura 		time_t current = gethrestime_sec();
43479c2c14abSThejaswini Singarajipura 		if ((assoc->ipsa_idleexpiretime <= current) &&
43489c2c14abSThejaswini Singarajipura 		    (assoc->ipsa_idleaddlt == idle->sadb_lifetime_addtime)) {
43499c2c14abSThejaswini Singarajipura 			assoc->ipsa_idleexpiretime =
43509c2c14abSThejaswini Singarajipura 			    current + assoc->ipsa_idleaddlt;
43519c2c14abSThejaswini Singarajipura 		}
43529c2c14abSThejaswini Singarajipura 		if (idle->sadb_lifetime_addtime != 0)
43539c2c14abSThejaswini Singarajipura 			assoc->ipsa_idleaddlt = idle->sadb_lifetime_addtime;
43549c2c14abSThejaswini Singarajipura 		if (idle->sadb_lifetime_usetime != 0)
43559c2c14abSThejaswini Singarajipura 			assoc->ipsa_idleuselt = idle->sadb_lifetime_usetime;
43569c2c14abSThejaswini Singarajipura 		if (assoc->ipsa_idleaddlt != 0) {
43579c2c14abSThejaswini Singarajipura 			assoc->ipsa_idleexpiretime =
43589c2c14abSThejaswini Singarajipura 			    current + idle->sadb_lifetime_addtime;
43599c2c14abSThejaswini Singarajipura 			assoc->ipsa_idletime = idle->sadb_lifetime_addtime;
43609c2c14abSThejaswini Singarajipura 		}
43619c2c14abSThejaswini Singarajipura 		if (assoc->ipsa_idleuselt != 0) {
43629c2c14abSThejaswini Singarajipura 			if (assoc->ipsa_idletime != 0) {
43639c2c14abSThejaswini Singarajipura 				assoc->ipsa_idletime = min(assoc->ipsa_idletime,
43649c2c14abSThejaswini Singarajipura 				    assoc->ipsa_idleuselt);
43659c2c14abSThejaswini Singarajipura 			assoc->ipsa_idleexpiretime =
43669c2c14abSThejaswini Singarajipura 			    current + assoc->ipsa_idletime;
43679c2c14abSThejaswini Singarajipura 			} else {
43689c2c14abSThejaswini Singarajipura 				assoc->ipsa_idleexpiretime =
43699c2c14abSThejaswini Singarajipura 				    current + assoc->ipsa_idleuselt;
43709c2c14abSThejaswini Singarajipura 				assoc->ipsa_idletime = assoc->ipsa_idleuselt;
43719c2c14abSThejaswini Singarajipura 			}
43729c2c14abSThejaswini Singarajipura 		}
43739c2c14abSThejaswini Singarajipura 	}
43747c478bd9Sstevel@tonic-gate 	mutex_exit(&assoc->ipsa_lock);
43757c478bd9Sstevel@tonic-gate }
43767c478bd9Sstevel@tonic-gate 
43779c2c14abSThejaswini Singarajipura static int
43789c2c14abSThejaswini Singarajipura sadb_update_state(ipsa_t *assoc, uint_t new_state, mblk_t **ipkt_lst)
43799c2c14abSThejaswini Singarajipura {
43809c2c14abSThejaswini Singarajipura 	int rcode = 0;
43819c2c14abSThejaswini Singarajipura 	time_t current = gethrestime_sec();
43829c2c14abSThejaswini Singarajipura 
43839c2c14abSThejaswini Singarajipura 	mutex_enter(&assoc->ipsa_lock);
43849c2c14abSThejaswini Singarajipura 
43859c2c14abSThejaswini Singarajipura 	switch (new_state) {
43869c2c14abSThejaswini Singarajipura 	case SADB_X_SASTATE_ACTIVE_ELSEWHERE:
43879c2c14abSThejaswini Singarajipura 		if (assoc->ipsa_state == SADB_X_SASTATE_IDLE) {
43889c2c14abSThejaswini Singarajipura 			assoc->ipsa_state = IPSA_STATE_ACTIVE_ELSEWHERE;
43899c2c14abSThejaswini Singarajipura 			assoc->ipsa_idleexpiretime =
43909c2c14abSThejaswini Singarajipura 			    current + assoc->ipsa_idletime;
43919c2c14abSThejaswini Singarajipura 		}
43929c2c14abSThejaswini Singarajipura 		break;
43939c2c14abSThejaswini Singarajipura 	case SADB_X_SASTATE_IDLE:
43949c2c14abSThejaswini Singarajipura 		if (assoc->ipsa_state == SADB_X_SASTATE_ACTIVE_ELSEWHERE) {
43959c2c14abSThejaswini Singarajipura 			assoc->ipsa_state = IPSA_STATE_IDLE;
43969c2c14abSThejaswini Singarajipura 			assoc->ipsa_idleexpiretime =
43979c2c14abSThejaswini Singarajipura 			    current + assoc->ipsa_idletime;
43989c2c14abSThejaswini Singarajipura 		} else {
43999c2c14abSThejaswini Singarajipura 			rcode = EINVAL;
44009c2c14abSThejaswini Singarajipura 		}
44019c2c14abSThejaswini Singarajipura 		break;
44029c2c14abSThejaswini Singarajipura 
44039c2c14abSThejaswini Singarajipura 	case SADB_X_SASTATE_ACTIVE:
44049c2c14abSThejaswini Singarajipura 		if (assoc->ipsa_state != SADB_X_SASTATE_IDLE) {
44059c2c14abSThejaswini Singarajipura 			rcode = EINVAL;
44069c2c14abSThejaswini Singarajipura 			break;
44079c2c14abSThejaswini Singarajipura 		}
44089c2c14abSThejaswini Singarajipura 		assoc->ipsa_state = IPSA_STATE_MATURE;
44099c2c14abSThejaswini Singarajipura 		assoc->ipsa_idleexpiretime = current + assoc->ipsa_idletime;
44109c2c14abSThejaswini Singarajipura 
44119c2c14abSThejaswini Singarajipura 		if (ipkt_lst == NULL) {
44129c2c14abSThejaswini Singarajipura 			break;
44139c2c14abSThejaswini Singarajipura 		}
44149c2c14abSThejaswini Singarajipura 
44159c2c14abSThejaswini Singarajipura 		if (assoc->ipsa_bpkt_head != NULL) {
44169c2c14abSThejaswini Singarajipura 			*ipkt_lst = assoc->ipsa_bpkt_head;
44179c2c14abSThejaswini Singarajipura 			assoc->ipsa_bpkt_head = assoc->ipsa_bpkt_tail = NULL;
44189c2c14abSThejaswini Singarajipura 			assoc->ipsa_mblkcnt = 0;
44199c2c14abSThejaswini Singarajipura 		} else {
44209c2c14abSThejaswini Singarajipura 			*ipkt_lst = NULL;
44219c2c14abSThejaswini Singarajipura 		}
44229c2c14abSThejaswini Singarajipura 		break;
44239c2c14abSThejaswini Singarajipura 	default:
44249c2c14abSThejaswini Singarajipura 		rcode = EINVAL;
44259c2c14abSThejaswini Singarajipura 		break;
44269c2c14abSThejaswini Singarajipura 	}
44279c2c14abSThejaswini Singarajipura 
44289c2c14abSThejaswini Singarajipura 	mutex_exit(&assoc->ipsa_lock);
44299c2c14abSThejaswini Singarajipura 	return (rcode);
44309c2c14abSThejaswini Singarajipura }
44319c2c14abSThejaswini Singarajipura 
44327c478bd9Sstevel@tonic-gate /*
44335d3b8cb7SBill Sommerfeld  * Check a proposed KMC update for sanity.
44345d3b8cb7SBill Sommerfeld  */
44355d3b8cb7SBill Sommerfeld static int
44365d3b8cb7SBill Sommerfeld sadb_check_kmc(ipsa_query_t *sq, ipsa_t *sa, int *diagnostic)
44375d3b8cb7SBill Sommerfeld {
44385d3b8cb7SBill Sommerfeld 	uint32_t kmp = sq->kmp;
44395d3b8cb7SBill Sommerfeld 	uint32_t kmc = sq->kmc;
44405d3b8cb7SBill Sommerfeld 
44415d3b8cb7SBill Sommerfeld 	if (sa == NULL)
44425d3b8cb7SBill Sommerfeld 		return (0);
44435d3b8cb7SBill Sommerfeld 
44445d3b8cb7SBill Sommerfeld 	if (sa->ipsa_state == IPSA_STATE_DEAD)
44455d3b8cb7SBill Sommerfeld 		return (ESRCH);	/* DEAD == Not there, in this case. */
44465d3b8cb7SBill Sommerfeld 
44475d3b8cb7SBill Sommerfeld 	if ((kmp != 0) && ((sa->ipsa_kmp != 0) || (sa->ipsa_kmp != kmp))) {
44485d3b8cb7SBill Sommerfeld 		*diagnostic = SADB_X_DIAGNOSTIC_DUPLICATE_KMP;
44495d3b8cb7SBill Sommerfeld 		return (EINVAL);
44505d3b8cb7SBill Sommerfeld 	}
44515d3b8cb7SBill Sommerfeld 
44525d3b8cb7SBill Sommerfeld 	if ((kmc != 0) && ((sa->ipsa_kmc != 0) || (sa->ipsa_kmc != kmc))) {
44535d3b8cb7SBill Sommerfeld 		*diagnostic = SADB_X_DIAGNOSTIC_DUPLICATE_KMC;
44545d3b8cb7SBill Sommerfeld 		return (EINVAL);
44555d3b8cb7SBill Sommerfeld 	}
44565d3b8cb7SBill Sommerfeld 
44575d3b8cb7SBill Sommerfeld 	return (0);
44585d3b8cb7SBill Sommerfeld }
44595d3b8cb7SBill Sommerfeld 
44605d3b8cb7SBill Sommerfeld /*
44615d3b8cb7SBill Sommerfeld  * Actually update the KMC info.
44625d3b8cb7SBill Sommerfeld  */
44635d3b8cb7SBill Sommerfeld static void
44645d3b8cb7SBill Sommerfeld sadb_update_kmc(ipsa_query_t *sq, ipsa_t *sa)
44655d3b8cb7SBill Sommerfeld {
44665d3b8cb7SBill Sommerfeld 	uint32_t kmp = sq->kmp;
44675d3b8cb7SBill Sommerfeld 	uint32_t kmc = sq->kmc;
44685d3b8cb7SBill Sommerfeld 
44695d3b8cb7SBill Sommerfeld 	if (kmp != 0)
44705d3b8cb7SBill Sommerfeld 		sa->ipsa_kmp = kmp;
44715d3b8cb7SBill Sommerfeld 	if (kmc != 0)
44725d3b8cb7SBill Sommerfeld 		sa->ipsa_kmc = kmc;
44735d3b8cb7SBill Sommerfeld }
44745d3b8cb7SBill Sommerfeld 
44755d3b8cb7SBill Sommerfeld /*
44767c478bd9Sstevel@tonic-gate  * Common code to update an SA.
44777c478bd9Sstevel@tonic-gate  */
44787c478bd9Sstevel@tonic-gate 
44797c478bd9Sstevel@tonic-gate int
44809c2c14abSThejaswini Singarajipura sadb_update_sa(mblk_t *mp, keysock_in_t *ksi, mblk_t **ipkt_lst,
448138d95a78Smarkfen     sadbp_t *spp, int *diagnostic, queue_t *pfkey_q,
4482f4b3ec61Sdh155122     int (*add_sa_func)(mblk_t *, keysock_in_t *, int *, netstack_t *),
448338d95a78Smarkfen     netstack_t *ns, uint8_t sadb_msg_type)
44847c478bd9Sstevel@tonic-gate {
44857c478bd9Sstevel@tonic-gate 	sadb_key_t *akey = (sadb_key_t *)ksi->ks_in_extv[SADB_EXT_KEY_AUTH];
44867c478bd9Sstevel@tonic-gate 	sadb_key_t *ekey = (sadb_key_t *)ksi->ks_in_extv[SADB_EXT_KEY_ENCRYPT];
44879c2c14abSThejaswini Singarajipura 	sadb_x_replay_ctr_t *replext =
44889c2c14abSThejaswini Singarajipura 	    (sadb_x_replay_ctr_t *)ksi->ks_in_extv[SADB_X_EXT_REPLAY_VALUE];
44897c478bd9Sstevel@tonic-gate 	sadb_lifetime_t *soft =
44907c478bd9Sstevel@tonic-gate 	    (sadb_lifetime_t *)ksi->ks_in_extv[SADB_EXT_LIFETIME_SOFT];
44917c478bd9Sstevel@tonic-gate 	sadb_lifetime_t *hard =
44927c478bd9Sstevel@tonic-gate 	    (sadb_lifetime_t *)ksi->ks_in_extv[SADB_EXT_LIFETIME_HARD];
44939c2c14abSThejaswini Singarajipura 	sadb_lifetime_t *idle =
44949c2c14abSThejaswini Singarajipura 	    (sadb_lifetime_t *)ksi->ks_in_extv[SADB_X_EXT_LIFETIME_IDLE];
449538d95a78Smarkfen 	sadb_x_pair_t *pair_ext =
449638d95a78Smarkfen 	    (sadb_x_pair_t *)ksi->ks_in_extv[SADB_X_EXT_PAIR];
449738d95a78Smarkfen 	ipsa_t *echo_target = NULL;
44985d3b8cb7SBill Sommerfeld 	ipsap_t ipsapp;
44995d3b8cb7SBill Sommerfeld 	ipsa_query_t sq;
45009c2c14abSThejaswini Singarajipura 	time_t current = gethrestime_sec();
45017c478bd9Sstevel@tonic-gate 
45025d3b8cb7SBill Sommerfeld 	sq.spp = spp;		/* XXX param */
45035d3b8cb7SBill Sommerfeld 	int error = sadb_form_query(ksi, IPSA_Q_SRC|IPSA_Q_DST|IPSA_Q_SA,
45045d3b8cb7SBill Sommerfeld 	    IPSA_Q_SRC|IPSA_Q_DST|IPSA_Q_SA|IPSA_Q_INBOUND|IPSA_Q_OUTBOUND,
45055d3b8cb7SBill Sommerfeld 	    &sq, diagnostic);
450638d95a78Smarkfen 
45075d3b8cb7SBill Sommerfeld 	if (error != 0)
45085d3b8cb7SBill Sommerfeld 		return (error);
45097c478bd9Sstevel@tonic-gate 
45105d3b8cb7SBill Sommerfeld 	error = get_ipsa_pair(&sq, &ipsapp, diagnostic);
45115d3b8cb7SBill Sommerfeld 	if (error != 0)
45125d3b8cb7SBill Sommerfeld 		return (error);
45137c478bd9Sstevel@tonic-gate 
45145d3b8cb7SBill Sommerfeld 	if (ipsapp.ipsap_psa_ptr == NULL && ipsapp.ipsap_sa_ptr != NULL) {
45155d3b8cb7SBill Sommerfeld 		if (ipsapp.ipsap_sa_ptr->ipsa_state == IPSA_STATE_LARVAL) {
45167c478bd9Sstevel@tonic-gate 			/*
45177c478bd9Sstevel@tonic-gate 			 * REFRELE the target and let the add_sa_func()
45187c478bd9Sstevel@tonic-gate 			 * deal with updating a larval SA.
45197c478bd9Sstevel@tonic-gate 			 */
45205d3b8cb7SBill Sommerfeld 			destroy_ipsa_pair(&ipsapp);
4521f4b3ec61Sdh155122 			return (add_sa_func(mp, ksi, diagnostic, ns));
45227c478bd9Sstevel@tonic-gate 		}
45237c478bd9Sstevel@tonic-gate 	}
45247c478bd9Sstevel@tonic-gate 
4525a1ba8781SMark Fenwick 	/*
4526a1ba8781SMark Fenwick 	 * At this point we have an UPDATE to a MATURE SA. There should
4527a1ba8781SMark Fenwick 	 * not be any keying material present.
4528a1ba8781SMark Fenwick 	 */
4529a1ba8781SMark Fenwick 	if (akey != NULL) {
4530a1ba8781SMark Fenwick 		*diagnostic = SADB_X_DIAGNOSTIC_AKEY_PRESENT;
4531a1ba8781SMark Fenwick 		error = EINVAL;
4532a1ba8781SMark Fenwick 		goto bail;
4533a1ba8781SMark Fenwick 	}
4534a1ba8781SMark Fenwick 	if (ekey != NULL) {
4535a1ba8781SMark Fenwick 		*diagnostic = SADB_X_DIAGNOSTIC_EKEY_PRESENT;
4536a1ba8781SMark Fenwick 		error = EINVAL;
4537a1ba8781SMark Fenwick 		goto bail;
4538a1ba8781SMark Fenwick 	}
4539a1ba8781SMark Fenwick 
45405d3b8cb7SBill Sommerfeld 	if (sq.assoc->sadb_sa_state == SADB_X_SASTATE_ACTIVE_ELSEWHERE) {
45415d3b8cb7SBill Sommerfeld 		if (ipsapp.ipsap_sa_ptr != NULL &&
45425d3b8cb7SBill Sommerfeld 		    ipsapp.ipsap_sa_ptr->ipsa_state == IPSA_STATE_IDLE) {
45435d3b8cb7SBill Sommerfeld 			if ((error = sadb_update_state(ipsapp.ipsap_sa_ptr,
45445d3b8cb7SBill Sommerfeld 			    sq.assoc->sadb_sa_state, NULL)) != 0) {
45459c2c14abSThejaswini Singarajipura 				*diagnostic = SADB_X_DIAGNOSTIC_BAD_SASTATE;
45469c2c14abSThejaswini Singarajipura 				goto bail;
45479c2c14abSThejaswini Singarajipura 			}
45489c2c14abSThejaswini Singarajipura 		}
45495d3b8cb7SBill Sommerfeld 		if (ipsapp.ipsap_psa_ptr != NULL &&
45505d3b8cb7SBill Sommerfeld 		    ipsapp.ipsap_psa_ptr->ipsa_state == IPSA_STATE_IDLE) {
45515d3b8cb7SBill Sommerfeld 			if ((error = sadb_update_state(ipsapp.ipsap_psa_ptr,
45525d3b8cb7SBill Sommerfeld 			    sq.assoc->sadb_sa_state, NULL)) != 0) {
45539c2c14abSThejaswini Singarajipura 				*diagnostic = SADB_X_DIAGNOSTIC_BAD_SASTATE;
45549c2c14abSThejaswini Singarajipura 				goto bail;
45559c2c14abSThejaswini Singarajipura 			}
45569c2c14abSThejaswini Singarajipura 		}
45579c2c14abSThejaswini Singarajipura 	}
45585d3b8cb7SBill Sommerfeld 	if (sq.assoc->sadb_sa_state == SADB_X_SASTATE_ACTIVE) {
45595d3b8cb7SBill Sommerfeld 		if (ipsapp.ipsap_sa_ptr != NULL) {
45605d3b8cb7SBill Sommerfeld 			error = sadb_update_state(ipsapp.ipsap_sa_ptr,
45615d3b8cb7SBill Sommerfeld 			    sq.assoc->sadb_sa_state,
45625d3b8cb7SBill Sommerfeld 			    (ipsapp.ipsap_sa_ptr->ipsa_flags &
45639c2c14abSThejaswini Singarajipura 			    IPSA_F_INBOUND) ? ipkt_lst : NULL);
45649c2c14abSThejaswini Singarajipura 			if (error) {
45659c2c14abSThejaswini Singarajipura 				*diagnostic = SADB_X_DIAGNOSTIC_BAD_SASTATE;
45669c2c14abSThejaswini Singarajipura 				goto bail;
45679c2c14abSThejaswini Singarajipura 			}
45689c2c14abSThejaswini Singarajipura 		}
45695d3b8cb7SBill Sommerfeld 		if (ipsapp.ipsap_psa_ptr != NULL) {
45705d3b8cb7SBill Sommerfeld 			error = sadb_update_state(ipsapp.ipsap_psa_ptr,
45715d3b8cb7SBill Sommerfeld 			    sq.assoc->sadb_sa_state,
45725d3b8cb7SBill Sommerfeld 			    (ipsapp.ipsap_psa_ptr->ipsa_flags &
45739c2c14abSThejaswini Singarajipura 			    IPSA_F_INBOUND) ? ipkt_lst : NULL);
45749c2c14abSThejaswini Singarajipura 			if (error) {
45759c2c14abSThejaswini Singarajipura 				*diagnostic = SADB_X_DIAGNOSTIC_BAD_SASTATE;
45769c2c14abSThejaswini Singarajipura 				goto bail;
45779c2c14abSThejaswini Singarajipura 			}
45789c2c14abSThejaswini Singarajipura 		}
45799c2c14abSThejaswini Singarajipura 		sadb_pfkey_echo(pfkey_q, mp, (sadb_msg_t *)mp->b_cont->b_rptr,
45809c2c14abSThejaswini Singarajipura 		    ksi, echo_target);
45819c2c14abSThejaswini Singarajipura 		goto bail;
45829c2c14abSThejaswini Singarajipura 	}
45839c2c14abSThejaswini Singarajipura 
45847c478bd9Sstevel@tonic-gate 	/*
45857c478bd9Sstevel@tonic-gate 	 * Reality checks for updates of active associations.
45867c478bd9Sstevel@tonic-gate 	 * Sundry first-pass UPDATE-specific reality checks.
45877c478bd9Sstevel@tonic-gate 	 * Have to do the checks here, because it's after the add_sa code.
45887c478bd9Sstevel@tonic-gate 	 * XXX STATS : logging/stats here?
45897c478bd9Sstevel@tonic-gate 	 */
45907c478bd9Sstevel@tonic-gate 
45915d3b8cb7SBill Sommerfeld 	if (!((sq.assoc->sadb_sa_state == SADB_SASTATE_MATURE) ||
45925d3b8cb7SBill Sommerfeld 	    (sq.assoc->sadb_sa_state == SADB_X_SASTATE_ACTIVE_ELSEWHERE))) {
45937c478bd9Sstevel@tonic-gate 		*diagnostic = SADB_X_DIAGNOSTIC_BAD_SASTATE;
45947c478bd9Sstevel@tonic-gate 		error = EINVAL;
45957c478bd9Sstevel@tonic-gate 		goto bail;
45967c478bd9Sstevel@tonic-gate 	}
45975d3b8cb7SBill Sommerfeld 	if (sq.assoc->sadb_sa_flags & ~spp->s_updateflags) {
45987c478bd9Sstevel@tonic-gate 		*diagnostic = SADB_X_DIAGNOSTIC_BAD_SAFLAGS;
45997c478bd9Sstevel@tonic-gate 		error = EINVAL;
46007c478bd9Sstevel@tonic-gate 		goto bail;
46017c478bd9Sstevel@tonic-gate 	}
46027c478bd9Sstevel@tonic-gate 	if (ksi->ks_in_extv[SADB_EXT_LIFETIME_CURRENT] != NULL) {
4603a1ba8781SMark Fenwick 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_LIFETIME;
46047c478bd9Sstevel@tonic-gate 		error = EOPNOTSUPP;
46057c478bd9Sstevel@tonic-gate 		goto bail;
46067c478bd9Sstevel@tonic-gate 	}
46079c2c14abSThejaswini Singarajipura 
46089c2c14abSThejaswini Singarajipura 	if ((*diagnostic = sadb_hardsoftchk(hard, soft, idle)) != 0) {
46097c478bd9Sstevel@tonic-gate 		error = EINVAL;
46107c478bd9Sstevel@tonic-gate 		goto bail;
46117c478bd9Sstevel@tonic-gate 	}
46127c478bd9Sstevel@tonic-gate 
46135d3b8cb7SBill Sommerfeld 	if ((*diagnostic = sadb_labelchk(ksi)) != 0)
46145d3b8cb7SBill Sommerfeld 		return (EINVAL);
46155d3b8cb7SBill Sommerfeld 
46165d3b8cb7SBill Sommerfeld 	error = sadb_check_kmc(&sq, ipsapp.ipsap_sa_ptr, diagnostic);
46175d3b8cb7SBill Sommerfeld 	if (error != 0)
46187c478bd9Sstevel@tonic-gate 		goto bail;
46195d3b8cb7SBill Sommerfeld 
46205d3b8cb7SBill Sommerfeld 	error = sadb_check_kmc(&sq, ipsapp.ipsap_psa_ptr, diagnostic);
46215d3b8cb7SBill Sommerfeld 	if (error != 0)
46227c478bd9Sstevel@tonic-gate 		goto bail;
46235d3b8cb7SBill Sommerfeld 
46245d3b8cb7SBill Sommerfeld 
46255d3b8cb7SBill Sommerfeld 	if (ipsapp.ipsap_sa_ptr != NULL) {
46269c2c14abSThejaswini Singarajipura 		/*
46279c2c14abSThejaswini Singarajipura 		 * Do not allow replay value change for MATURE or LARVAL SA.
46289c2c14abSThejaswini Singarajipura 		 */
46299c2c14abSThejaswini Singarajipura 
46309c2c14abSThejaswini Singarajipura 		if ((replext != NULL) &&
46315d3b8cb7SBill Sommerfeld 		    ((ipsapp.ipsap_sa_ptr->ipsa_state == IPSA_STATE_LARVAL) ||
46325d3b8cb7SBill Sommerfeld 		    (ipsapp.ipsap_sa_ptr->ipsa_state == IPSA_STATE_MATURE))) {
46339c2c14abSThejaswini Singarajipura 			*diagnostic = SADB_X_DIAGNOSTIC_BAD_SASTATE;
46349c2c14abSThejaswini Singarajipura 			error = EINVAL;
46359c2c14abSThejaswini Singarajipura 			goto bail;
46369c2c14abSThejaswini Singarajipura 		}
46377c478bd9Sstevel@tonic-gate 	}
46387c478bd9Sstevel@tonic-gate 
46397c478bd9Sstevel@tonic-gate 
46405d3b8cb7SBill Sommerfeld 	if (ipsapp.ipsap_sa_ptr != NULL) {
46415d3b8cb7SBill Sommerfeld 		sadb_update_lifetimes(ipsapp.ipsap_sa_ptr, hard, soft,
46429c2c14abSThejaswini Singarajipura 		    idle, B_TRUE);
46435d3b8cb7SBill Sommerfeld 		sadb_update_kmc(&sq, ipsapp.ipsap_sa_ptr);
46449c2c14abSThejaswini Singarajipura 		if ((replext != NULL) &&
46455d3b8cb7SBill Sommerfeld 		    (ipsapp.ipsap_sa_ptr->ipsa_replay_wsize != 0)) {
46469c2c14abSThejaswini Singarajipura 			/*
46479c2c14abSThejaswini Singarajipura 			 * If an inbound SA, update the replay counter
46489c2c14abSThejaswini Singarajipura 			 * and check off all the other sequence number
46499c2c14abSThejaswini Singarajipura 			 */
46509c2c14abSThejaswini Singarajipura 			if (ksi->ks_in_dsttype == KS_IN_ADDR_ME) {
46515d3b8cb7SBill Sommerfeld 				if (!sadb_replay_check(ipsapp.ipsap_sa_ptr,
46529c2c14abSThejaswini Singarajipura 				    replext->sadb_x_rc_replay32)) {
4653a1ba8781SMark Fenwick 					*diagnostic =
4654a1ba8781SMark Fenwick 					    SADB_X_DIAGNOSTIC_INVALID_REPLAY;
46559c2c14abSThejaswini Singarajipura 					error = EINVAL;
46569c2c14abSThejaswini Singarajipura 					goto bail;
46579c2c14abSThejaswini Singarajipura 				}
46585d3b8cb7SBill Sommerfeld 				mutex_enter(&ipsapp.ipsap_sa_ptr->ipsa_lock);
46595d3b8cb7SBill Sommerfeld 				ipsapp.ipsap_sa_ptr->ipsa_idleexpiretime =
46609c2c14abSThejaswini Singarajipura 				    current +
46615d3b8cb7SBill Sommerfeld 				    ipsapp.ipsap_sa_ptr->ipsa_idletime;
46625d3b8cb7SBill Sommerfeld 				mutex_exit(&ipsapp.ipsap_sa_ptr->ipsa_lock);
46639c2c14abSThejaswini Singarajipura 			} else {
46645d3b8cb7SBill Sommerfeld 				mutex_enter(&ipsapp.ipsap_sa_ptr->ipsa_lock);
46655d3b8cb7SBill Sommerfeld 				ipsapp.ipsap_sa_ptr->ipsa_replay =
46669c2c14abSThejaswini Singarajipura 				    replext->sadb_x_rc_replay32;
46675d3b8cb7SBill Sommerfeld 				ipsapp.ipsap_sa_ptr->ipsa_idleexpiretime =
46689c2c14abSThejaswini Singarajipura 				    current +
46695d3b8cb7SBill Sommerfeld 				    ipsapp.ipsap_sa_ptr->ipsa_idletime;
46705d3b8cb7SBill Sommerfeld 				mutex_exit(&ipsapp.ipsap_sa_ptr->ipsa_lock);
46719c2c14abSThejaswini Singarajipura 			}
46729c2c14abSThejaswini Singarajipura 		}
46737c478bd9Sstevel@tonic-gate 	}
46747c478bd9Sstevel@tonic-gate 
467538d95a78Smarkfen 	if (sadb_msg_type == SADB_X_UPDATEPAIR) {
46765d3b8cb7SBill Sommerfeld 		if (ipsapp.ipsap_psa_ptr != NULL) {
46775d3b8cb7SBill Sommerfeld 			sadb_update_lifetimes(ipsapp.ipsap_psa_ptr, hard, soft,
46789c2c14abSThejaswini Singarajipura 			    idle, B_FALSE);
46795d3b8cb7SBill Sommerfeld 			sadb_update_kmc(&sq, ipsapp.ipsap_psa_ptr);
468038d95a78Smarkfen 		} else {
468138d95a78Smarkfen 			*diagnostic = SADB_X_DIAGNOSTIC_PAIR_SA_NOTFOUND;
468238d95a78Smarkfen 			error = ESRCH;
468338d95a78Smarkfen 			goto bail;
468438d95a78Smarkfen 		}
46857c478bd9Sstevel@tonic-gate 	}
46867c478bd9Sstevel@tonic-gate 
468738d95a78Smarkfen 	if (pair_ext != NULL)
46885d3b8cb7SBill Sommerfeld 		error = update_pairing(&ipsapp, &sq, ksi, diagnostic);
468938d95a78Smarkfen 
469038d95a78Smarkfen 	if (error == 0)
46917c478bd9Sstevel@tonic-gate 		sadb_pfkey_echo(pfkey_q, mp, (sadb_msg_t *)mp->b_cont->b_rptr,
469238d95a78Smarkfen 		    ksi, echo_target);
46937c478bd9Sstevel@tonic-gate bail:
469438d95a78Smarkfen 
46955d3b8cb7SBill Sommerfeld 	destroy_ipsa_pair(&ipsapp);
469638d95a78Smarkfen 
469738d95a78Smarkfen 	return (error);
46987c478bd9Sstevel@tonic-gate }
46997c478bd9Sstevel@tonic-gate 
470038d95a78Smarkfen 
47015d3b8cb7SBill Sommerfeld static int
47025d3b8cb7SBill Sommerfeld update_pairing(ipsap_t *ipsapp, ipsa_query_t *sq, keysock_in_t *ksi,
47035d3b8cb7SBill Sommerfeld     int *diagnostic)
470438d95a78Smarkfen {
470538d95a78Smarkfen 	sadb_sa_t *assoc = (sadb_sa_t *)ksi->ks_in_extv[SADB_EXT_SA];
470638d95a78Smarkfen 	sadb_x_pair_t *pair_ext =
470738d95a78Smarkfen 	    (sadb_x_pair_t *)ksi->ks_in_extv[SADB_X_EXT_PAIR];
470838d95a78Smarkfen 	int error = 0;
47095d3b8cb7SBill Sommerfeld 	ipsap_t oipsapp;
471038d95a78Smarkfen 	boolean_t undo_pair = B_FALSE;
471138d95a78Smarkfen 	uint32_t ipsa_flags;
471238d95a78Smarkfen 
471338d95a78Smarkfen 	if (pair_ext->sadb_x_pair_spi == 0 || pair_ext->sadb_x_pair_spi ==
471438d95a78Smarkfen 	    assoc->sadb_sa_spi) {
471538d95a78Smarkfen 		*diagnostic = SADB_X_DIAGNOSTIC_PAIR_INAPPROPRIATE;
471638d95a78Smarkfen 		return (EINVAL);
471738d95a78Smarkfen 	}
471838d95a78Smarkfen 
471938d95a78Smarkfen 	/*
472038d95a78Smarkfen 	 * Assume for now that the spi value provided in the SADB_UPDATE
472138d95a78Smarkfen 	 * message was valid, update the SA with its pair spi value.
472238d95a78Smarkfen 	 * If the spi turns out to be bogus or the SA no longer exists
472338d95a78Smarkfen 	 * then this will be detected when the reverse update is made
472438d95a78Smarkfen 	 * below.
472538d95a78Smarkfen 	 */
472638d95a78Smarkfen 	mutex_enter(&ipsapp->ipsap_sa_ptr->ipsa_lock);
472738d95a78Smarkfen 	ipsapp->ipsap_sa_ptr->ipsa_flags |= IPSA_F_PAIRED;
472838d95a78Smarkfen 	ipsapp->ipsap_sa_ptr->ipsa_otherspi = pair_ext->sadb_x_pair_spi;
472938d95a78Smarkfen 	mutex_exit(&ipsapp->ipsap_sa_ptr->ipsa_lock);
473038d95a78Smarkfen 
473138d95a78Smarkfen 	/*
473238d95a78Smarkfen 	 * After updating the ipsa_otherspi element of the SA, get_ipsa_pair()
473338d95a78Smarkfen 	 * should now return pointers to the SA *AND* its pair, if this is not
473438d95a78Smarkfen 	 * the case, the "otherspi" either did not exist or was deleted. Also
473538d95a78Smarkfen 	 * check that "otherspi" is not already paired. If everything looks
473638d95a78Smarkfen 	 * good, complete the update. IPSA_REFRELE the first pair_pointer
473738d95a78Smarkfen 	 * after this update to ensure its not deleted until we are done.
473838d95a78Smarkfen 	 */
47395d3b8cb7SBill Sommerfeld 	error = get_ipsa_pair(sq, &oipsapp, diagnostic);
47405d3b8cb7SBill Sommerfeld 	if (error != 0) {
474138d95a78Smarkfen 		/*
474238d95a78Smarkfen 		 * This should never happen, calling function still has
474338d95a78Smarkfen 		 * IPSA_REFHELD on the SA we just updated.
474438d95a78Smarkfen 		 */
47455d3b8cb7SBill Sommerfeld 		return (error);	/* XXX EINVAL instead of ESRCH? */
474638d95a78Smarkfen 	}
474738d95a78Smarkfen 
47485d3b8cb7SBill Sommerfeld 	if (oipsapp.ipsap_psa_ptr == NULL) {
474938d95a78Smarkfen 		*diagnostic = SADB_X_DIAGNOSTIC_PAIR_INAPPROPRIATE;
4750a1ba8781SMark Fenwick 		error = EINVAL;
475138d95a78Smarkfen 		undo_pair = B_TRUE;
475238d95a78Smarkfen 	} else {
47535d3b8cb7SBill Sommerfeld 		ipsa_flags = oipsapp.ipsap_psa_ptr->ipsa_flags;
47545d3b8cb7SBill Sommerfeld 		if ((oipsapp.ipsap_psa_ptr->ipsa_state == IPSA_STATE_DEAD) ||
47555d3b8cb7SBill Sommerfeld 		    (oipsapp.ipsap_psa_ptr->ipsa_state == IPSA_STATE_DYING)) {
475638d95a78Smarkfen 			/* Its dead Jim! */
475738d95a78Smarkfen 			*diagnostic = SADB_X_DIAGNOSTIC_PAIR_INAPPROPRIATE;
475838d95a78Smarkfen 			undo_pair = B_TRUE;
475938d95a78Smarkfen 		} else if ((ipsa_flags & (IPSA_F_OUTBOUND | IPSA_F_INBOUND)) ==
476038d95a78Smarkfen 		    (IPSA_F_OUTBOUND | IPSA_F_INBOUND)) {
476138d95a78Smarkfen 			/* This SA is in both hashtables. */
476238d95a78Smarkfen 			*diagnostic = SADB_X_DIAGNOSTIC_PAIR_INAPPROPRIATE;
476338d95a78Smarkfen 			undo_pair = B_TRUE;
476438d95a78Smarkfen 		} else if (ipsa_flags & IPSA_F_PAIRED) {
476538d95a78Smarkfen 			/* This SA is already paired with another. */
476638d95a78Smarkfen 			*diagnostic = SADB_X_DIAGNOSTIC_PAIR_ALREADY;
476738d95a78Smarkfen 			undo_pair = B_TRUE;
476838d95a78Smarkfen 		}
476938d95a78Smarkfen 	}
477038d95a78Smarkfen 
477138d95a78Smarkfen 	if (undo_pair) {
477238d95a78Smarkfen 		/* The pair SA does not exist. */
477338d95a78Smarkfen 		mutex_enter(&ipsapp->ipsap_sa_ptr->ipsa_lock);
477438d95a78Smarkfen 		ipsapp->ipsap_sa_ptr->ipsa_flags &= ~IPSA_F_PAIRED;
477538d95a78Smarkfen 		ipsapp->ipsap_sa_ptr->ipsa_otherspi = 0;
477638d95a78Smarkfen 		mutex_exit(&ipsapp->ipsap_sa_ptr->ipsa_lock);
477738d95a78Smarkfen 	} else {
47785d3b8cb7SBill Sommerfeld 		mutex_enter(&oipsapp.ipsap_psa_ptr->ipsa_lock);
47795d3b8cb7SBill Sommerfeld 		oipsapp.ipsap_psa_ptr->ipsa_otherspi = assoc->sadb_sa_spi;
47805d3b8cb7SBill Sommerfeld 		oipsapp.ipsap_psa_ptr->ipsa_flags |= IPSA_F_PAIRED;
47815d3b8cb7SBill Sommerfeld 		mutex_exit(&oipsapp.ipsap_psa_ptr->ipsa_lock);
478238d95a78Smarkfen 	}
478338d95a78Smarkfen 
47845d3b8cb7SBill Sommerfeld 	destroy_ipsa_pair(&oipsapp);
47857c478bd9Sstevel@tonic-gate 	return (error);
47867c478bd9Sstevel@tonic-gate }
47877c478bd9Sstevel@tonic-gate 
47887c478bd9Sstevel@tonic-gate /*
47897c478bd9Sstevel@tonic-gate  * The following functions deal with ACQUIRE LISTS.  An ACQUIRE list is
47907c478bd9Sstevel@tonic-gate  * a list of outstanding SADB_ACQUIRE messages.	 If ipsec_getassocbyconn() fails
47917c478bd9Sstevel@tonic-gate  * for an outbound datagram, that datagram is queued up on an ACQUIRE record,
47927c478bd9Sstevel@tonic-gate  * and an SADB_ACQUIRE message is sent up.  Presumably, a user-space key
47937c478bd9Sstevel@tonic-gate  * management daemon will process the ACQUIRE, use a SADB_GETSPI to reserve
47947c478bd9Sstevel@tonic-gate  * an SPI value and a larval SA, then SADB_UPDATE the larval SA, and ADD the
47957c478bd9Sstevel@tonic-gate  * other direction's SA.
47967c478bd9Sstevel@tonic-gate  */
47977c478bd9Sstevel@tonic-gate 
47987c478bd9Sstevel@tonic-gate /*
47997c478bd9Sstevel@tonic-gate  * Check the ACQUIRE lists.  If there's an existing ACQUIRE record,
48007c478bd9Sstevel@tonic-gate  * grab it, lock it, and return it.  Otherwise return NULL.
48015d3b8cb7SBill Sommerfeld  *
48025d3b8cb7SBill Sommerfeld  * XXX MLS number of arguments getting unwieldy here
48037c478bd9Sstevel@tonic-gate  */
48047c478bd9Sstevel@tonic-gate static ipsacq_t *
48057c478bd9Sstevel@tonic-gate sadb_checkacquire(iacqf_t *bucket, ipsec_action_t *ap, ipsec_policy_t *pp,
48068810c16bSdanmcd     uint32_t *src, uint32_t *dst, uint32_t *isrc, uint32_t *idst,
4807bd670b35SErik Nordmark     uint64_t unique_id, ts_label_t *tsl)
48087c478bd9Sstevel@tonic-gate {
48097c478bd9Sstevel@tonic-gate 	ipsacq_t *walker;
48107c478bd9Sstevel@tonic-gate 	sa_family_t fam;
48118810c16bSdanmcd 	uint32_t blank_address[4] = {0, 0, 0, 0};
48128810c16bSdanmcd 
48138810c16bSdanmcd 	if (isrc == NULL) {
48148810c16bSdanmcd 		ASSERT(idst == NULL);
48158810c16bSdanmcd 		isrc = idst = blank_address;
48168810c16bSdanmcd 	}
48177c478bd9Sstevel@tonic-gate 
48187c478bd9Sstevel@tonic-gate 	/*
48197c478bd9Sstevel@tonic-gate 	 * Scan list for duplicates.  Check for UNIQUE, src/dest, policy.
48207c478bd9Sstevel@tonic-gate 	 *
48217c478bd9Sstevel@tonic-gate 	 * XXX May need search for duplicates based on other things too!
48227c478bd9Sstevel@tonic-gate 	 */
48237c478bd9Sstevel@tonic-gate 	for (walker = bucket->iacqf_ipsacq; walker != NULL;
48247c478bd9Sstevel@tonic-gate 	    walker = walker->ipsacq_next) {
48257c478bd9Sstevel@tonic-gate 		mutex_enter(&walker->ipsacq_lock);
48267c478bd9Sstevel@tonic-gate 		fam = walker->ipsacq_addrfam;
48277c478bd9Sstevel@tonic-gate 		if (IPSA_ARE_ADDR_EQUAL(dst, walker->ipsacq_dstaddr, fam) &&
48287c478bd9Sstevel@tonic-gate 		    IPSA_ARE_ADDR_EQUAL(src, walker->ipsacq_srcaddr, fam) &&
48298810c16bSdanmcd 		    ip_addr_match((uint8_t *)isrc, walker->ipsacq_innersrcpfx,
48308810c16bSdanmcd 		    (in6_addr_t *)walker->ipsacq_innersrc) &&
48318810c16bSdanmcd 		    ip_addr_match((uint8_t *)idst, walker->ipsacq_innerdstpfx,
48328810c16bSdanmcd 		    (in6_addr_t *)walker->ipsacq_innerdst) &&
48337c478bd9Sstevel@tonic-gate 		    (ap == walker->ipsacq_act) &&
48347c478bd9Sstevel@tonic-gate 		    (pp == walker->ipsacq_policy) &&
48357c478bd9Sstevel@tonic-gate 		    /* XXX do deep compares of ap/pp? */
48365d3b8cb7SBill Sommerfeld 		    (unique_id == walker->ipsacq_unique_id) &&
4837bd670b35SErik Nordmark 		    (ipsec_label_match(tsl, walker->ipsacq_tsl)))
48387c478bd9Sstevel@tonic-gate 			break;			/* everything matched */
48397c478bd9Sstevel@tonic-gate 		mutex_exit(&walker->ipsacq_lock);
48407c478bd9Sstevel@tonic-gate 	}
48417c478bd9Sstevel@tonic-gate 
48427c478bd9Sstevel@tonic-gate 	return (walker);
48437c478bd9Sstevel@tonic-gate }
48447c478bd9Sstevel@tonic-gate 
48457c478bd9Sstevel@tonic-gate /*
48467c478bd9Sstevel@tonic-gate  * For this mblk, insert a new acquire record.  Assume bucket contains addrs
48477c478bd9Sstevel@tonic-gate  * of all of the same length.  Give up (and drop) if memory
48487c478bd9Sstevel@tonic-gate  * cannot be allocated for a new one; otherwise, invoke callback to
48497c478bd9Sstevel@tonic-gate  * send the acquire up..
48507c478bd9Sstevel@tonic-gate  *
48517c478bd9Sstevel@tonic-gate  * In cases where we need both AH and ESP, add the SA to the ESP ACQUIRE
4852bd670b35SErik Nordmark  * list.  The ah_add_sa_finish() routines can look at the packet's attached
4853bd670b35SErik Nordmark  * attributes and handle this case specially.
48547c478bd9Sstevel@tonic-gate  */
48557c478bd9Sstevel@tonic-gate void
4856bd670b35SErik Nordmark sadb_acquire(mblk_t *datamp, ip_xmit_attr_t *ixa, boolean_t need_ah,
4857bd670b35SErik Nordmark     boolean_t need_esp)
48587c478bd9Sstevel@tonic-gate {
4859bd670b35SErik Nordmark 	mblk_t	*asyncmp;
48607c478bd9Sstevel@tonic-gate 	sadbp_t *spp;
48617c478bd9Sstevel@tonic-gate 	sadb_t *sp;
48627c478bd9Sstevel@tonic-gate 	ipsacq_t *newbie;
48637c478bd9Sstevel@tonic-gate 	iacqf_t *bucket;
48647c478bd9Sstevel@tonic-gate 	mblk_t *extended;
48657c478bd9Sstevel@tonic-gate 	ipha_t *ipha = (ipha_t *)datamp->b_rptr;
48667c478bd9Sstevel@tonic-gate 	ip6_t *ip6h = (ip6_t *)datamp->b_rptr;
48678810c16bSdanmcd 	uint32_t *src, *dst, *isrc, *idst;
4868bd670b35SErik Nordmark 	ipsec_policy_t *pp = ixa->ixa_ipsec_policy;
4869bd670b35SErik Nordmark 	ipsec_action_t *ap = ixa->ixa_ipsec_action;
48707c478bd9Sstevel@tonic-gate 	sa_family_t af;
48717c478bd9Sstevel@tonic-gate 	int hashoffset;
48727c478bd9Sstevel@tonic-gate 	uint32_t seq;
48737c478bd9Sstevel@tonic-gate 	uint64_t unique_id = 0;
48747c478bd9Sstevel@tonic-gate 	ipsec_selector_t sel;
4875bd670b35SErik Nordmark 	boolean_t tunnel_mode = (ixa->ixa_flags & IXAF_IPSEC_TUNNEL) != 0;
4876bd670b35SErik Nordmark 	ts_label_t 	*tsl = NULL;
4877bd670b35SErik Nordmark 	netstack_t	*ns = ixa->ixa_ipst->ips_netstack;
4878f4b3ec61Sdh155122 	ipsec_stack_t	*ipss = ns->netstack_ipsec;
48795d3b8cb7SBill Sommerfeld 	sadb_sens_t 	*sens = NULL;
48805d3b8cb7SBill Sommerfeld 	int 		sens_len;
48817c478bd9Sstevel@tonic-gate 
48827c478bd9Sstevel@tonic-gate 	ASSERT((pp != NULL) || (ap != NULL));
48837c478bd9Sstevel@tonic-gate 
48847c478bd9Sstevel@tonic-gate 	ASSERT(need_ah != NULL || need_esp != NULL);
48855d3b8cb7SBill Sommerfeld 
48867c478bd9Sstevel@tonic-gate 	/* Assign sadb pointers */
4887f4b3ec61Sdh155122 	if (need_esp) { /* ESP for AH+ESP */
4888f4b3ec61Sdh155122 		ipsecesp_stack_t *espstack = ns->netstack_ipsecesp;
4889f4b3ec61Sdh155122 
4890f4b3ec61Sdh155122 		spp = &espstack->esp_sadb;
4891f4b3ec61Sdh155122 	} else {
4892f4b3ec61Sdh155122 		ipsecah_stack_t	*ahstack = ns->netstack_ipsecah;
4893f4b3ec61Sdh155122 
4894f4b3ec61Sdh155122 		spp = &ahstack->ah_sadb;
4895f4b3ec61Sdh155122 	}
4896bd670b35SErik Nordmark 	sp = (ixa->ixa_flags & IXAF_IS_IPV4) ? &spp->s_v4 : &spp->s_v6;
48975d3b8cb7SBill Sommerfeld 
48985d3b8cb7SBill Sommerfeld 	if (is_system_labeled())
4899bd670b35SErik Nordmark 		tsl = ixa->ixa_tsl;
49005d3b8cb7SBill Sommerfeld 
49017c478bd9Sstevel@tonic-gate 	if (ap == NULL)
49027c478bd9Sstevel@tonic-gate 		ap = pp->ipsp_act;
49037c478bd9Sstevel@tonic-gate 
49047c478bd9Sstevel@tonic-gate 	ASSERT(ap != NULL);
49057c478bd9Sstevel@tonic-gate 
49068810c16bSdanmcd 	if (ap->ipa_act.ipa_apply.ipp_use_unique || tunnel_mode)
4907bd670b35SErik Nordmark 		unique_id = SA_FORM_UNIQUE_ID(ixa);
49087c478bd9Sstevel@tonic-gate 
49097c478bd9Sstevel@tonic-gate 	/*
49107c478bd9Sstevel@tonic-gate 	 * Set up an ACQUIRE record.
49117c478bd9Sstevel@tonic-gate 	 *
49127c478bd9Sstevel@tonic-gate 	 * Immediately, make sure the ACQUIRE sequence number doesn't slip
49137c478bd9Sstevel@tonic-gate 	 * below the lowest point allowed in the kernel.  (In other words,
49147c478bd9Sstevel@tonic-gate 	 * make sure the high bit on the sequence number is set.)
49157c478bd9Sstevel@tonic-gate 	 */
49167c478bd9Sstevel@tonic-gate 
4917f4b3ec61Sdh155122 	seq = keysock_next_seq(ns) | IACQF_LOWEST_SEQ;
49187c478bd9Sstevel@tonic-gate 
49197c478bd9Sstevel@tonic-gate 	if (IPH_HDR_VERSION(ipha) == IP_VERSION) {
49207c478bd9Sstevel@tonic-gate 		src = (uint32_t *)&ipha->ipha_src;
49217c478bd9Sstevel@tonic-gate 		dst = (uint32_t *)&ipha->ipha_dst;
49227c478bd9Sstevel@tonic-gate 		af = AF_INET;
4923fb87b5d2Ssommerfe 		hashoffset = OUTBOUND_HASH_V4(sp, ipha->ipha_dst);
4924bd670b35SErik Nordmark 		ASSERT(ixa->ixa_flags & IXAF_IS_IPV4);
49257c478bd9Sstevel@tonic-gate 	} else {
49267c478bd9Sstevel@tonic-gate 		ASSERT(IPH_HDR_VERSION(ipha) == IPV6_VERSION);
49277c478bd9Sstevel@tonic-gate 		src = (uint32_t *)&ip6h->ip6_src;
49287c478bd9Sstevel@tonic-gate 		dst = (uint32_t *)&ip6h->ip6_dst;
49297c478bd9Sstevel@tonic-gate 		af = AF_INET6;
4930fb87b5d2Ssommerfe 		hashoffset = OUTBOUND_HASH_V6(sp, ip6h->ip6_dst);
4931bd670b35SErik Nordmark 		ASSERT(!(ixa->ixa_flags & IXAF_IS_IPV4));
49327c478bd9Sstevel@tonic-gate 	}
49337c478bd9Sstevel@tonic-gate 
49348810c16bSdanmcd 	if (tunnel_mode) {
49357807385bSDan McDonald 		if (pp == NULL) {
49367807385bSDan McDonald 			/*
49377807385bSDan McDonald 			 * Tunnel mode with no policy pointer means this is a
49387807385bSDan McDonald 			 * reflected ICMP (like a ECHO REQUEST) that came in
49397807385bSDan McDonald 			 * with self-encapsulated protection.  Until we better
49407807385bSDan McDonald 			 * support this, drop the packet.
49417807385bSDan McDonald 			 */
4942bd670b35SErik Nordmark 			ip_drop_packet(datamp, B_FALSE, NULL,
49437807385bSDan McDonald 			    DROPPER(ipss, ipds_spd_got_selfencap),
49447807385bSDan McDonald 			    &ipss->ipsec_spd_dropper);
49457807385bSDan McDonald 			return;
49467807385bSDan McDonald 		}
49478810c16bSdanmcd 		/* Snag inner addresses. */
4948bd670b35SErik Nordmark 		isrc = ixa->ixa_ipsec_insrc;
4949bd670b35SErik Nordmark 		idst = ixa->ixa_ipsec_indst;
49508810c16bSdanmcd 	} else {
49518810c16bSdanmcd 		isrc = idst = NULL;
49528810c16bSdanmcd 	}
49538810c16bSdanmcd 
49547c478bd9Sstevel@tonic-gate 	/*
49557c478bd9Sstevel@tonic-gate 	 * Check buckets to see if there is an existing entry.  If so,
49567c478bd9Sstevel@tonic-gate 	 * grab it.  sadb_checkacquire locks newbie if found.
49577c478bd9Sstevel@tonic-gate 	 */
49587c478bd9Sstevel@tonic-gate 	bucket = &(sp->sdb_acq[hashoffset]);
49597c478bd9Sstevel@tonic-gate 	mutex_enter(&bucket->iacqf_lock);
49608810c16bSdanmcd 	newbie = sadb_checkacquire(bucket, ap, pp, src, dst, isrc, idst,
4961bd670b35SErik Nordmark 	    unique_id, tsl);
49627c478bd9Sstevel@tonic-gate 
49637c478bd9Sstevel@tonic-gate 	if (newbie == NULL) {
49647c478bd9Sstevel@tonic-gate 		/*
49657c478bd9Sstevel@tonic-gate 		 * Otherwise, allocate a new one.
49667c478bd9Sstevel@tonic-gate 		 */
49677c478bd9Sstevel@tonic-gate 		newbie = kmem_zalloc(sizeof (*newbie), KM_NOSLEEP);
49687c478bd9Sstevel@tonic-gate 		if (newbie == NULL) {
49697c478bd9Sstevel@tonic-gate 			mutex_exit(&bucket->iacqf_lock);
4970bd670b35SErik Nordmark 			ip_drop_packet(datamp, B_FALSE, NULL,
4971f4b3ec61Sdh155122 			    DROPPER(ipss, ipds_sadb_acquire_nomem),
4972f4b3ec61Sdh155122 			    &ipss->ipsec_sadb_dropper);
49737c478bd9Sstevel@tonic-gate 			return;
49747c478bd9Sstevel@tonic-gate 		}
49757c478bd9Sstevel@tonic-gate 		newbie->ipsacq_policy = pp;
49767c478bd9Sstevel@tonic-gate 		if (pp != NULL) {
49777c478bd9Sstevel@tonic-gate 			IPPOL_REFHOLD(pp);
49787c478bd9Sstevel@tonic-gate 		}
49797c478bd9Sstevel@tonic-gate 		IPACT_REFHOLD(ap);
49807c478bd9Sstevel@tonic-gate 		newbie->ipsacq_act = ap;
49817c478bd9Sstevel@tonic-gate 		newbie->ipsacq_linklock = &bucket->iacqf_lock;
49827c478bd9Sstevel@tonic-gate 		newbie->ipsacq_next = bucket->iacqf_ipsacq;
49837c478bd9Sstevel@tonic-gate 		newbie->ipsacq_ptpn = &bucket->iacqf_ipsacq;
49847c478bd9Sstevel@tonic-gate 		if (newbie->ipsacq_next != NULL)
49857c478bd9Sstevel@tonic-gate 			newbie->ipsacq_next->ipsacq_ptpn = &newbie->ipsacq_next;
49865d3b8cb7SBill Sommerfeld 
49877c478bd9Sstevel@tonic-gate 		bucket->iacqf_ipsacq = newbie;
49887c478bd9Sstevel@tonic-gate 		mutex_init(&newbie->ipsacq_lock, NULL, MUTEX_DEFAULT, NULL);
49897c478bd9Sstevel@tonic-gate 		mutex_enter(&newbie->ipsacq_lock);
49907c478bd9Sstevel@tonic-gate 	}
49917c478bd9Sstevel@tonic-gate 
49925d3b8cb7SBill Sommerfeld 	/*
49935d3b8cb7SBill Sommerfeld 	 * XXX MLS does it actually help us to drop the bucket lock here?
49945d3b8cb7SBill Sommerfeld 	 * we have inserted a half-built, locked acquire record into the
49955d3b8cb7SBill Sommerfeld 	 * bucket.  any competing thread will now be able to lock the bucket
49965d3b8cb7SBill Sommerfeld 	 * to scan it, but will immediately pile up on the new acquire
49975d3b8cb7SBill Sommerfeld 	 * record's lock; I don't think we gain anything here other than to
49985d3b8cb7SBill Sommerfeld 	 * disperse blame for lock contention.
49995d3b8cb7SBill Sommerfeld 	 *
50005d3b8cb7SBill Sommerfeld 	 * we might be able to dispense with acquire record locks entirely..
50015d3b8cb7SBill Sommerfeld 	 * just use the bucket locks..
50025d3b8cb7SBill Sommerfeld 	 */
50035d3b8cb7SBill Sommerfeld 
50047c478bd9Sstevel@tonic-gate 	mutex_exit(&bucket->iacqf_lock);
50057c478bd9Sstevel@tonic-gate 
50067c478bd9Sstevel@tonic-gate 	/*
50077c478bd9Sstevel@tonic-gate 	 * This assert looks silly for now, but we may need to enter newbie's
50087c478bd9Sstevel@tonic-gate 	 * mutex during a search.
50097c478bd9Sstevel@tonic-gate 	 */
50107c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&newbie->ipsacq_lock));
50117c478bd9Sstevel@tonic-gate 
5012bd670b35SErik Nordmark 	/*
5013bd670b35SErik Nordmark 	 * Make the ip_xmit_attr_t into something we can queue.
5014bd670b35SErik Nordmark 	 * If no memory it frees datamp.
5015bd670b35SErik Nordmark 	 */
5016bd670b35SErik Nordmark 	asyncmp = ip_xmit_attr_to_mblk(ixa);
5017bd670b35SErik Nordmark 	if (asyncmp != NULL)
5018bd670b35SErik Nordmark 		linkb(asyncmp, datamp);
5019bd670b35SErik Nordmark 
50207c478bd9Sstevel@tonic-gate 	/* Queue up packet.  Use b_next. */
5021bd670b35SErik Nordmark 
5022bd670b35SErik Nordmark 	if (asyncmp == NULL) {
5023bd670b35SErik Nordmark 		/* Statistics for allocation failure */
5024bd670b35SErik Nordmark 		if (ixa->ixa_flags & IXAF_IS_IPV4) {
5025bd670b35SErik Nordmark 			BUMP_MIB(&ixa->ixa_ipst->ips_ip_mib,
5026bd670b35SErik Nordmark 			    ipIfStatsOutDiscards);
5027bd670b35SErik Nordmark 		} else {
5028bd670b35SErik Nordmark 			BUMP_MIB(&ixa->ixa_ipst->ips_ip6_mib,
5029bd670b35SErik Nordmark 			    ipIfStatsOutDiscards);
5030bd670b35SErik Nordmark 		}
5031bd670b35SErik Nordmark 		ip_drop_output("No memory for asyncmp", datamp, NULL);
5032bd670b35SErik Nordmark 		freemsg(datamp);
5033bd670b35SErik Nordmark 	} else if (newbie->ipsacq_numpackets == 0) {
50347c478bd9Sstevel@tonic-gate 		/* First one. */
5035bd670b35SErik Nordmark 		newbie->ipsacq_mp = asyncmp;
50367c478bd9Sstevel@tonic-gate 		newbie->ipsacq_numpackets = 1;
5037437220cdSdanmcd 		newbie->ipsacq_expire = gethrestime_sec();
50387c478bd9Sstevel@tonic-gate 		/*
50397c478bd9Sstevel@tonic-gate 		 * Extended ACQUIRE with both AH+ESP will use ESP's timeout
50407c478bd9Sstevel@tonic-gate 		 * value.
50417c478bd9Sstevel@tonic-gate 		 */
50427c478bd9Sstevel@tonic-gate 		newbie->ipsacq_expire += *spp->s_acquire_timeout;
50437c478bd9Sstevel@tonic-gate 		newbie->ipsacq_seq = seq;
50447c478bd9Sstevel@tonic-gate 		newbie->ipsacq_addrfam = af;
50457c478bd9Sstevel@tonic-gate 
5046bd670b35SErik Nordmark 		newbie->ipsacq_srcport = ixa->ixa_ipsec_src_port;
5047bd670b35SErik Nordmark 		newbie->ipsacq_dstport = ixa->ixa_ipsec_dst_port;
5048bd670b35SErik Nordmark 		newbie->ipsacq_icmp_type = ixa->ixa_ipsec_icmp_type;
5049bd670b35SErik Nordmark 		newbie->ipsacq_icmp_code = ixa->ixa_ipsec_icmp_code;
50508810c16bSdanmcd 		if (tunnel_mode) {
5051bd670b35SErik Nordmark 			newbie->ipsacq_inneraddrfam = ixa->ixa_ipsec_inaf;
5052bd670b35SErik Nordmark 			newbie->ipsacq_proto = ixa->ixa_ipsec_inaf == AF_INET6 ?
50538810c16bSdanmcd 			    IPPROTO_IPV6 : IPPROTO_ENCAP;
5054bd670b35SErik Nordmark 			newbie->ipsacq_innersrcpfx = ixa->ixa_ipsec_insrcpfx;
5055bd670b35SErik Nordmark 			newbie->ipsacq_innerdstpfx = ixa->ixa_ipsec_indstpfx;
50568810c16bSdanmcd 			IPSA_COPY_ADDR(newbie->ipsacq_innersrc,
5057bd670b35SErik Nordmark 			    ixa->ixa_ipsec_insrc, ixa->ixa_ipsec_inaf);
50588810c16bSdanmcd 			IPSA_COPY_ADDR(newbie->ipsacq_innerdst,
5059bd670b35SErik Nordmark 			    ixa->ixa_ipsec_indst, ixa->ixa_ipsec_inaf);
50608810c16bSdanmcd 		} else {
5061bd670b35SErik Nordmark 			newbie->ipsacq_proto = ixa->ixa_ipsec_proto;
50628810c16bSdanmcd 		}
50637c478bd9Sstevel@tonic-gate 		newbie->ipsacq_unique_id = unique_id;
50645d3b8cb7SBill Sommerfeld 
5065bd670b35SErik Nordmark 		if (ixa->ixa_tsl != NULL) {
5066bd670b35SErik Nordmark 			label_hold(ixa->ixa_tsl);
5067bd670b35SErik Nordmark 			newbie->ipsacq_tsl = ixa->ixa_tsl;
50685d3b8cb7SBill Sommerfeld 		}
50697c478bd9Sstevel@tonic-gate 	} else {
50707c478bd9Sstevel@tonic-gate 		/* Scan to the end of the list & insert. */
50717c478bd9Sstevel@tonic-gate 		mblk_t *lastone = newbie->ipsacq_mp;
50727c478bd9Sstevel@tonic-gate 
50737c478bd9Sstevel@tonic-gate 		while (lastone->b_next != NULL)
50747c478bd9Sstevel@tonic-gate 			lastone = lastone->b_next;
5075bd670b35SErik Nordmark 		lastone->b_next = asyncmp;
5076bffb04cfSmarkfen 		if (newbie->ipsacq_numpackets++ == ipsacq_maxpackets) {
5077bffb04cfSmarkfen 			newbie->ipsacq_numpackets = ipsacq_maxpackets;
50787c478bd9Sstevel@tonic-gate 			lastone = newbie->ipsacq_mp;
50797c478bd9Sstevel@tonic-gate 			newbie->ipsacq_mp = lastone->b_next;
50807c478bd9Sstevel@tonic-gate 			lastone->b_next = NULL;
5081bd670b35SErik Nordmark 
5082bd670b35SErik Nordmark 			/* Freeing the async message */
5083bd670b35SErik Nordmark 			lastone = ip_xmit_attr_free_mblk(lastone);
5084bd670b35SErik Nordmark 			ip_drop_packet(lastone, B_FALSE, NULL,
5085f4b3ec61Sdh155122 			    DROPPER(ipss, ipds_sadb_acquire_toofull),
5086f4b3ec61Sdh155122 			    &ipss->ipsec_sadb_dropper);
5087bffb04cfSmarkfen 		} else {
5088f4b3ec61Sdh155122 			IP_ACQUIRE_STAT(ipss, qhiwater,
5089f4b3ec61Sdh155122 			    newbie->ipsacq_numpackets);
50907c478bd9Sstevel@tonic-gate 		}
50917c478bd9Sstevel@tonic-gate 	}
50927c478bd9Sstevel@tonic-gate 
50937c478bd9Sstevel@tonic-gate 	/*
50947c478bd9Sstevel@tonic-gate 	 * Reset addresses.  Set them to the most recently added mblk chain,
50957c478bd9Sstevel@tonic-gate 	 * so that the address pointers in the acquire record will point
50967c478bd9Sstevel@tonic-gate 	 * at an mblk still attached to the acquire list.
50977c478bd9Sstevel@tonic-gate 	 */
50987c478bd9Sstevel@tonic-gate 
50997c478bd9Sstevel@tonic-gate 	newbie->ipsacq_srcaddr = src;
51007c478bd9Sstevel@tonic-gate 	newbie->ipsacq_dstaddr = dst;
51017c478bd9Sstevel@tonic-gate 
51027c478bd9Sstevel@tonic-gate 	/*
51037c478bd9Sstevel@tonic-gate 	 * If the acquire record has more than one queued packet, we've
51047c478bd9Sstevel@tonic-gate 	 * already sent an ACQUIRE, and don't need to repeat ourself.
51057c478bd9Sstevel@tonic-gate 	 */
51067c478bd9Sstevel@tonic-gate 	if (newbie->ipsacq_seq != seq || newbie->ipsacq_numpackets > 1) {
51077c478bd9Sstevel@tonic-gate 		/* I have an acquire outstanding already! */
51087c478bd9Sstevel@tonic-gate 		mutex_exit(&newbie->ipsacq_lock);
51097c478bd9Sstevel@tonic-gate 		return;
51107c478bd9Sstevel@tonic-gate 	}
51117c478bd9Sstevel@tonic-gate 
51125d3b8cb7SBill Sommerfeld 	if (!keysock_extended_reg(ns))
51135d3b8cb7SBill Sommerfeld 		goto punt_extended;
51147c478bd9Sstevel@tonic-gate 	/*
51157c478bd9Sstevel@tonic-gate 	 * Construct an extended ACQUIRE.  There are logging
51167c478bd9Sstevel@tonic-gate 	 * opportunities here in failure cases.
51177c478bd9Sstevel@tonic-gate 	 */
5118188e1664SErik Nordmark 	bzero(&sel, sizeof (sel));
5119bd670b35SErik Nordmark 	sel.ips_isv4 = (ixa->ixa_flags & IXAF_IS_IPV4) != 0;
51208810c16bSdanmcd 	if (tunnel_mode) {
5121bd670b35SErik Nordmark 		sel.ips_protocol = (ixa->ixa_ipsec_inaf == AF_INET) ?
51228810c16bSdanmcd 		    IPPROTO_ENCAP : IPPROTO_IPV6;
51238810c16bSdanmcd 	} else {
5124bd670b35SErik Nordmark 		sel.ips_protocol = ixa->ixa_ipsec_proto;
5125bd670b35SErik Nordmark 		sel.ips_local_port = ixa->ixa_ipsec_src_port;
5126bd670b35SErik Nordmark 		sel.ips_remote_port = ixa->ixa_ipsec_dst_port;
51278810c16bSdanmcd 	}
5128bd670b35SErik Nordmark 	sel.ips_icmp_type = ixa->ixa_ipsec_icmp_type;
5129bd670b35SErik Nordmark 	sel.ips_icmp_code = ixa->ixa_ipsec_icmp_code;
51308810c16bSdanmcd 	sel.ips_is_icmp_inv_acq = 0;
51318810c16bSdanmcd 	if (af == AF_INET) {
51328810c16bSdanmcd 		sel.ips_local_addr_v4 = ipha->ipha_src;
51338810c16bSdanmcd 		sel.ips_remote_addr_v4 = ipha->ipha_dst;
51348810c16bSdanmcd 	} else {
51358810c16bSdanmcd 		sel.ips_local_addr_v6 = ip6h->ip6_src;
51368810c16bSdanmcd 		sel.ips_remote_addr_v6 = ip6h->ip6_dst;
51378810c16bSdanmcd 	}
51388810c16bSdanmcd 
51397c478bd9Sstevel@tonic-gate 	extended = sadb_keysock_out(0);
51405d3b8cb7SBill Sommerfeld 	if (extended == NULL)
51415d3b8cb7SBill Sommerfeld 		goto punt_extended;
51425d3b8cb7SBill Sommerfeld 
5143bd670b35SErik Nordmark 	if (ixa->ixa_tsl != NULL) {
51445d3b8cb7SBill Sommerfeld 		/*
51455d3b8cb7SBill Sommerfeld 		 * XXX MLS correct condition here?
51465d3b8cb7SBill Sommerfeld 		 * XXX MLS other credential attributes in acquire?
51475d3b8cb7SBill Sommerfeld 		 * XXX malloc failure?  don't fall back to original?
51485d3b8cb7SBill Sommerfeld 		 */
5149bd670b35SErik Nordmark 		sens = sadb_make_sens_ext(ixa->ixa_tsl, &sens_len);
51505d3b8cb7SBill Sommerfeld 
51515d3b8cb7SBill Sommerfeld 		if (sens == NULL) {
51525d3b8cb7SBill Sommerfeld 			freeb(extended);
51535d3b8cb7SBill Sommerfeld 			goto punt_extended;
51545d3b8cb7SBill Sommerfeld 		}
51555d3b8cb7SBill Sommerfeld 	}
51565d3b8cb7SBill Sommerfeld 
51575d3b8cb7SBill Sommerfeld 	extended->b_cont = sadb_extended_acquire(&sel, pp, ap, tunnel_mode,
51585d3b8cb7SBill Sommerfeld 	    seq, 0, sens, ns);
51595d3b8cb7SBill Sommerfeld 
51605d3b8cb7SBill Sommerfeld 	if (sens != NULL)
51615d3b8cb7SBill Sommerfeld 		kmem_free(sens, sens_len);
51625d3b8cb7SBill Sommerfeld 
51637c478bd9Sstevel@tonic-gate 	if (extended->b_cont == NULL) {
51647c478bd9Sstevel@tonic-gate 		freeb(extended);
51655d3b8cb7SBill Sommerfeld 		goto punt_extended;
51667c478bd9Sstevel@tonic-gate 	}
51677c478bd9Sstevel@tonic-gate 
51687c478bd9Sstevel@tonic-gate 	/*
51697c478bd9Sstevel@tonic-gate 	 * Send an ACQUIRE message (and possible an extended ACQUIRE) based on
51707c478bd9Sstevel@tonic-gate 	 * this new record.  The send-acquire callback assumes that acqrec is
51717c478bd9Sstevel@tonic-gate 	 * already locked.
51727c478bd9Sstevel@tonic-gate 	 */
5173f4b3ec61Sdh155122 	(*spp->s_acqfn)(newbie, extended, ns);
51745d3b8cb7SBill Sommerfeld 	return;
51755d3b8cb7SBill Sommerfeld 
51765d3b8cb7SBill Sommerfeld punt_extended:
51775d3b8cb7SBill Sommerfeld 	(*spp->s_acqfn)(newbie, NULL, ns);
51787c478bd9Sstevel@tonic-gate }
51797c478bd9Sstevel@tonic-gate 
51807c478bd9Sstevel@tonic-gate /*
51817c478bd9Sstevel@tonic-gate  * Unlink and free an acquire record.
51827c478bd9Sstevel@tonic-gate  */
51837c478bd9Sstevel@tonic-gate void
5184f4b3ec61Sdh155122 sadb_destroy_acquire(ipsacq_t *acqrec, netstack_t *ns)
51857c478bd9Sstevel@tonic-gate {
51867c478bd9Sstevel@tonic-gate 	mblk_t		*mp;
5187f4b3ec61Sdh155122 	ipsec_stack_t	*ipss = ns->netstack_ipsec;
51887c478bd9Sstevel@tonic-gate 
51897c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(acqrec->ipsacq_linklock));
51907c478bd9Sstevel@tonic-gate 
51917c478bd9Sstevel@tonic-gate 	if (acqrec->ipsacq_policy != NULL) {
5192bd670b35SErik Nordmark 		IPPOL_REFRELE(acqrec->ipsacq_policy);
51937c478bd9Sstevel@tonic-gate 	}
51947c478bd9Sstevel@tonic-gate 	if (acqrec->ipsacq_act != NULL) {
51957c478bd9Sstevel@tonic-gate 		IPACT_REFRELE(acqrec->ipsacq_act);
51967c478bd9Sstevel@tonic-gate 	}
51977c478bd9Sstevel@tonic-gate 
51987c478bd9Sstevel@tonic-gate 	/* Unlink */
51997c478bd9Sstevel@tonic-gate 	*(acqrec->ipsacq_ptpn) = acqrec->ipsacq_next;
52007c478bd9Sstevel@tonic-gate 	if (acqrec->ipsacq_next != NULL)
52017c478bd9Sstevel@tonic-gate 		acqrec->ipsacq_next->ipsacq_ptpn = acqrec->ipsacq_ptpn;
52027c478bd9Sstevel@tonic-gate 
5203bd670b35SErik Nordmark 	if (acqrec->ipsacq_tsl != NULL) {
5204bd670b35SErik Nordmark 		label_rele(acqrec->ipsacq_tsl);
5205bd670b35SErik Nordmark 		acqrec->ipsacq_tsl = NULL;
52065d3b8cb7SBill Sommerfeld 	}
52075d3b8cb7SBill Sommerfeld 
52087c478bd9Sstevel@tonic-gate 	/*
52097c478bd9Sstevel@tonic-gate 	 * Free hanging mp's.
52107c478bd9Sstevel@tonic-gate 	 *
52117c478bd9Sstevel@tonic-gate 	 * XXX Instead of freemsg(), perhaps use IPSEC_REQ_FAILED.
52127c478bd9Sstevel@tonic-gate 	 */
52137c478bd9Sstevel@tonic-gate 
52147c478bd9Sstevel@tonic-gate 	mutex_enter(&acqrec->ipsacq_lock);
52157c478bd9Sstevel@tonic-gate 	while (acqrec->ipsacq_mp != NULL) {
52167c478bd9Sstevel@tonic-gate 		mp = acqrec->ipsacq_mp;
52177c478bd9Sstevel@tonic-gate 		acqrec->ipsacq_mp = mp->b_next;
52187c478bd9Sstevel@tonic-gate 		mp->b_next = NULL;
5219bd670b35SErik Nordmark 		/* Freeing the async message */
5220bd670b35SErik Nordmark 		mp = ip_xmit_attr_free_mblk(mp);
5221bd670b35SErik Nordmark 		ip_drop_packet(mp, B_FALSE, NULL,
5222f4b3ec61Sdh155122 		    DROPPER(ipss, ipds_sadb_acquire_timeout),
5223f4b3ec61Sdh155122 		    &ipss->ipsec_sadb_dropper);
52247c478bd9Sstevel@tonic-gate 	}
52257c478bd9Sstevel@tonic-gate 	mutex_exit(&acqrec->ipsacq_lock);
52267c478bd9Sstevel@tonic-gate 
52277c478bd9Sstevel@tonic-gate 	/* Free */
52287c478bd9Sstevel@tonic-gate 	mutex_destroy(&acqrec->ipsacq_lock);
52297c478bd9Sstevel@tonic-gate 	kmem_free(acqrec, sizeof (*acqrec));
52307c478bd9Sstevel@tonic-gate }
52317c478bd9Sstevel@tonic-gate 
52327c478bd9Sstevel@tonic-gate /*
52337c478bd9Sstevel@tonic-gate  * Destroy an acquire list fanout.
52347c478bd9Sstevel@tonic-gate  */
5235fb87b5d2Ssommerfe static void
5236f4b3ec61Sdh155122 sadb_destroy_acqlist(iacqf_t **listp, uint_t numentries, boolean_t forever,
5237f4b3ec61Sdh155122     netstack_t *ns)
52387c478bd9Sstevel@tonic-gate {
52397c478bd9Sstevel@tonic-gate 	int i;
5240fb87b5d2Ssommerfe 	iacqf_t *list = *listp;
5241fb87b5d2Ssommerfe 
5242fb87b5d2Ssommerfe 	if (list == NULL)
5243fb87b5d2Ssommerfe 		return;
52447c478bd9Sstevel@tonic-gate 
52457c478bd9Sstevel@tonic-gate 	for (i = 0; i < numentries; i++) {
52467c478bd9Sstevel@tonic-gate 		mutex_enter(&(list[i].iacqf_lock));
52477c478bd9Sstevel@tonic-gate 		while (list[i].iacqf_ipsacq != NULL)
5248f4b3ec61Sdh155122 			sadb_destroy_acquire(list[i].iacqf_ipsacq, ns);
52497c478bd9Sstevel@tonic-gate 		mutex_exit(&(list[i].iacqf_lock));
52507c478bd9Sstevel@tonic-gate 		if (forever)
52517c478bd9Sstevel@tonic-gate 			mutex_destroy(&(list[i].iacqf_lock));
52527c478bd9Sstevel@tonic-gate 	}
52537c478bd9Sstevel@tonic-gate 
5254fb87b5d2Ssommerfe 	if (forever) {
5255fb87b5d2Ssommerfe 		*listp = NULL;
52567c478bd9Sstevel@tonic-gate 		kmem_free(list, numentries * sizeof (*list));
52577c478bd9Sstevel@tonic-gate 	}
5258fb87b5d2Ssommerfe }
52597c478bd9Sstevel@tonic-gate 
52607845d282Sdanmcd /*
52617845d282Sdanmcd  * Create an algorithm descriptor for an extended ACQUIRE.  Filter crypto
52627845d282Sdanmcd  * framework's view of reality vs. IPsec's.  EF's wins, BTW.
52637845d282Sdanmcd  */
52647c478bd9Sstevel@tonic-gate static uint8_t *
52657c478bd9Sstevel@tonic-gate sadb_new_algdesc(uint8_t *start, uint8_t *limit,
52667c478bd9Sstevel@tonic-gate     sadb_x_ecomb_t *ecomb, uint8_t satype, uint8_t algtype,
5267f4b3ec61Sdh155122     uint8_t alg, uint16_t minbits, uint16_t maxbits, ipsec_stack_t *ipss)
52687c478bd9Sstevel@tonic-gate {
52697c478bd9Sstevel@tonic-gate 	uint8_t *cur = start;
52707845d282Sdanmcd 	ipsec_alginfo_t *algp;
52717c478bd9Sstevel@tonic-gate 	sadb_x_algdesc_t *algdesc = (sadb_x_algdesc_t *)cur;
52727845d282Sdanmcd 
52737c478bd9Sstevel@tonic-gate 	cur += sizeof (*algdesc);
52747c478bd9Sstevel@tonic-gate 	if (cur >= limit)
52757c478bd9Sstevel@tonic-gate 		return (NULL);
52767c478bd9Sstevel@tonic-gate 
52777c478bd9Sstevel@tonic-gate 	ecomb->sadb_x_ecomb_numalgs++;
52787c478bd9Sstevel@tonic-gate 
52797845d282Sdanmcd 	/*
52807845d282Sdanmcd 	 * Normalize vs. crypto framework's limits.  This way, you can specify
52817845d282Sdanmcd 	 * a stronger policy, and when the framework loads a stronger version,
52827845d282Sdanmcd 	 * you can just keep plowing w/o rewhacking your SPD.
52837845d282Sdanmcd 	 */
5284f4b3ec61Sdh155122 	mutex_enter(&ipss->ipsec_alg_lock);
5285f4b3ec61Sdh155122 	algp = ipss->ipsec_alglists[(algtype == SADB_X_ALGTYPE_AUTH) ?
52867845d282Sdanmcd 	    IPSEC_ALG_AUTH : IPSEC_ALG_ENCR][alg];
528710b3fbf5Sdanmcd 	if (algp == NULL) {
528810b3fbf5Sdanmcd 		mutex_exit(&ipss->ipsec_alg_lock);
528910b3fbf5Sdanmcd 		return (NULL);	/* Algorithm doesn't exist.  Fail gracefully. */
529010b3fbf5Sdanmcd 	}
52917845d282Sdanmcd 	if (minbits < algp->alg_ef_minbits)
52927845d282Sdanmcd 		minbits = algp->alg_ef_minbits;
52937845d282Sdanmcd 	if (maxbits > algp->alg_ef_maxbits)
52947845d282Sdanmcd 		maxbits = algp->alg_ef_maxbits;
5295f4b3ec61Sdh155122 	mutex_exit(&ipss->ipsec_alg_lock);
52967845d282Sdanmcd 
5297628b0c67SMark Fenwick 	algdesc->sadb_x_algdesc_reserved = SADB_8TO1(algp->alg_saltlen);
52987c478bd9Sstevel@tonic-gate 	algdesc->sadb_x_algdesc_satype = satype;
52997c478bd9Sstevel@tonic-gate 	algdesc->sadb_x_algdesc_algtype = algtype;
53007c478bd9Sstevel@tonic-gate 	algdesc->sadb_x_algdesc_alg = alg;
53017c478bd9Sstevel@tonic-gate 	algdesc->sadb_x_algdesc_minbits = minbits;
53027c478bd9Sstevel@tonic-gate 	algdesc->sadb_x_algdesc_maxbits = maxbits;
5303628b0c67SMark Fenwick 
53047c478bd9Sstevel@tonic-gate 	return (cur);
53057c478bd9Sstevel@tonic-gate }
53067c478bd9Sstevel@tonic-gate 
53077c478bd9Sstevel@tonic-gate /*
53087c478bd9Sstevel@tonic-gate  * Convert the given ipsec_action_t into an ecomb starting at *ecomb
53097c478bd9Sstevel@tonic-gate  * which must fit before *limit
53107c478bd9Sstevel@tonic-gate  *
53117c478bd9Sstevel@tonic-gate  * return NULL if we ran out of room or a pointer to the end of the ecomb.
53127c478bd9Sstevel@tonic-gate  */
53137c478bd9Sstevel@tonic-gate static uint8_t *
5314f4b3ec61Sdh155122 sadb_action_to_ecomb(uint8_t *start, uint8_t *limit, ipsec_action_t *act,
5315f4b3ec61Sdh155122     netstack_t *ns)
53167c478bd9Sstevel@tonic-gate {
53177c478bd9Sstevel@tonic-gate 	uint8_t *cur = start;
53187c478bd9Sstevel@tonic-gate 	sadb_x_ecomb_t *ecomb = (sadb_x_ecomb_t *)cur;
53197c478bd9Sstevel@tonic-gate 	ipsec_prot_t *ipp;
5320f4b3ec61Sdh155122 	ipsec_stack_t *ipss = ns->netstack_ipsec;
53217c478bd9Sstevel@tonic-gate 
53227c478bd9Sstevel@tonic-gate 	cur += sizeof (*ecomb);
53237c478bd9Sstevel@tonic-gate 	if (cur >= limit)
53247c478bd9Sstevel@tonic-gate 		return (NULL);
53257c478bd9Sstevel@tonic-gate 
53267c478bd9Sstevel@tonic-gate 	ASSERT(act->ipa_act.ipa_type == IPSEC_ACT_APPLY);
53277c478bd9Sstevel@tonic-gate 
53287c478bd9Sstevel@tonic-gate 	ipp = &act->ipa_act.ipa_apply;
53297c478bd9Sstevel@tonic-gate 
53307c478bd9Sstevel@tonic-gate 	ecomb->sadb_x_ecomb_numalgs = 0;
53317c478bd9Sstevel@tonic-gate 	ecomb->sadb_x_ecomb_reserved = 0;
53327c478bd9Sstevel@tonic-gate 	ecomb->sadb_x_ecomb_reserved2 = 0;
53337c478bd9Sstevel@tonic-gate 	/*
53347c478bd9Sstevel@tonic-gate 	 * No limits on allocations, since we really don't support that
53357c478bd9Sstevel@tonic-gate 	 * concept currently.
53367c478bd9Sstevel@tonic-gate 	 */
53377c478bd9Sstevel@tonic-gate 	ecomb->sadb_x_ecomb_soft_allocations = 0;
53387c478bd9Sstevel@tonic-gate 	ecomb->sadb_x_ecomb_hard_allocations = 0;
53397c478bd9Sstevel@tonic-gate 
53407c478bd9Sstevel@tonic-gate 	/*
53417c478bd9Sstevel@tonic-gate 	 * XXX TBD: Policy or global parameters will eventually be
53427c478bd9Sstevel@tonic-gate 	 * able to fill in some of these.
53437c478bd9Sstevel@tonic-gate 	 */
53447c478bd9Sstevel@tonic-gate 	ecomb->sadb_x_ecomb_flags = 0;
53457c478bd9Sstevel@tonic-gate 	ecomb->sadb_x_ecomb_soft_bytes = 0;
53467c478bd9Sstevel@tonic-gate 	ecomb->sadb_x_ecomb_hard_bytes = 0;
53477c478bd9Sstevel@tonic-gate 	ecomb->sadb_x_ecomb_soft_addtime = 0;
53487c478bd9Sstevel@tonic-gate 	ecomb->sadb_x_ecomb_hard_addtime = 0;
53497c478bd9Sstevel@tonic-gate 	ecomb->sadb_x_ecomb_soft_usetime = 0;
53507c478bd9Sstevel@tonic-gate 	ecomb->sadb_x_ecomb_hard_usetime = 0;
53517c478bd9Sstevel@tonic-gate 
53527c478bd9Sstevel@tonic-gate 	if (ipp->ipp_use_ah) {
53537c478bd9Sstevel@tonic-gate 		cur = sadb_new_algdesc(cur, limit, ecomb,
53547c478bd9Sstevel@tonic-gate 		    SADB_SATYPE_AH, SADB_X_ALGTYPE_AUTH, ipp->ipp_auth_alg,
5355f4b3ec61Sdh155122 		    ipp->ipp_ah_minbits, ipp->ipp_ah_maxbits, ipss);
53567c478bd9Sstevel@tonic-gate 		if (cur == NULL)
53577c478bd9Sstevel@tonic-gate 			return (NULL);
5358f4b3ec61Sdh155122 		ipsecah_fill_defs(ecomb, ns);
53597c478bd9Sstevel@tonic-gate 	}
53607c478bd9Sstevel@tonic-gate 
53617c478bd9Sstevel@tonic-gate 	if (ipp->ipp_use_esp) {
53627c478bd9Sstevel@tonic-gate 		if (ipp->ipp_use_espa) {
53637c478bd9Sstevel@tonic-gate 			cur = sadb_new_algdesc(cur, limit, ecomb,
53647c478bd9Sstevel@tonic-gate 			    SADB_SATYPE_ESP, SADB_X_ALGTYPE_AUTH,
53657c478bd9Sstevel@tonic-gate 			    ipp->ipp_esp_auth_alg,
53667c478bd9Sstevel@tonic-gate 			    ipp->ipp_espa_minbits,
5367f4b3ec61Sdh155122 			    ipp->ipp_espa_maxbits, ipss);
53687c478bd9Sstevel@tonic-gate 			if (cur == NULL)
53697c478bd9Sstevel@tonic-gate 				return (NULL);
53707c478bd9Sstevel@tonic-gate 		}
53717c478bd9Sstevel@tonic-gate 
53727c478bd9Sstevel@tonic-gate 		cur = sadb_new_algdesc(cur, limit, ecomb,
53737c478bd9Sstevel@tonic-gate 		    SADB_SATYPE_ESP, SADB_X_ALGTYPE_CRYPT,
53747c478bd9Sstevel@tonic-gate 		    ipp->ipp_encr_alg,
53757c478bd9Sstevel@tonic-gate 		    ipp->ipp_espe_minbits,
5376f4b3ec61Sdh155122 		    ipp->ipp_espe_maxbits, ipss);
53777c478bd9Sstevel@tonic-gate 		if (cur == NULL)
53787c478bd9Sstevel@tonic-gate 			return (NULL);
53797c478bd9Sstevel@tonic-gate 		/* Fill in lifetimes if and only if AH didn't already... */
53807c478bd9Sstevel@tonic-gate 		if (!ipp->ipp_use_ah)
5381f4b3ec61Sdh155122 			ipsecesp_fill_defs(ecomb, ns);
53827c478bd9Sstevel@tonic-gate 	}
53837c478bd9Sstevel@tonic-gate 
53847c478bd9Sstevel@tonic-gate 	return (cur);
53857c478bd9Sstevel@tonic-gate }
53867c478bd9Sstevel@tonic-gate 
53875d3b8cb7SBill Sommerfeld #include <sys/tsol/label_macro.h> /* XXX should not need this */
53885d3b8cb7SBill Sommerfeld 
53895d3b8cb7SBill Sommerfeld /*
53905d3b8cb7SBill Sommerfeld  * From a cred_t, construct a sensitivity label extension
53915d3b8cb7SBill Sommerfeld  *
53925d3b8cb7SBill Sommerfeld  * We send up a fixed-size sensitivity label bitmap, and are perhaps
53935d3b8cb7SBill Sommerfeld  * overly chummy with the underlying data structures here.
53945d3b8cb7SBill Sommerfeld  */
53955d3b8cb7SBill Sommerfeld 
53965d3b8cb7SBill Sommerfeld /* ARGSUSED */
53975d3b8cb7SBill Sommerfeld int
5398bd670b35SErik Nordmark sadb_sens_len_from_label(ts_label_t *tsl)
53995d3b8cb7SBill Sommerfeld {
54005d3b8cb7SBill Sommerfeld 	int baselen = sizeof (sadb_sens_t) + _C_LEN * 4;
54015d3b8cb7SBill Sommerfeld 	return (roundup(baselen, sizeof (uint64_t)));
54025d3b8cb7SBill Sommerfeld }
54035d3b8cb7SBill Sommerfeld 
54045d3b8cb7SBill Sommerfeld void
5405bd670b35SErik Nordmark sadb_sens_from_label(sadb_sens_t *sens, int exttype, ts_label_t *tsl,
5406bd670b35SErik Nordmark     int senslen)
54075d3b8cb7SBill Sommerfeld {
54085d3b8cb7SBill Sommerfeld 	uint8_t *bitmap;
54095d3b8cb7SBill Sommerfeld 	bslabel_t *sl;
54105d3b8cb7SBill Sommerfeld 
54115d3b8cb7SBill Sommerfeld 	/* LINTED */
54125d3b8cb7SBill Sommerfeld 	ASSERT((_C_LEN & 1) == 0);
54135d3b8cb7SBill Sommerfeld 	ASSERT((senslen & 7) == 0);
54145d3b8cb7SBill Sommerfeld 
54155d3b8cb7SBill Sommerfeld 	sl = label2bslabel(tsl);
54165d3b8cb7SBill Sommerfeld 
54175d3b8cb7SBill Sommerfeld 	sens->sadb_sens_exttype = exttype;
54185d3b8cb7SBill Sommerfeld 	sens->sadb_sens_len = SADB_8TO64(senslen);
54195d3b8cb7SBill Sommerfeld 
54205d3b8cb7SBill Sommerfeld 	sens->sadb_sens_dpd = tsl->tsl_doi;
54215d3b8cb7SBill Sommerfeld 	sens->sadb_sens_sens_level = LCLASS(sl);
54225d3b8cb7SBill Sommerfeld 	sens->sadb_sens_integ_level = 0; /* TBD */
54235d3b8cb7SBill Sommerfeld 	sens->sadb_sens_sens_len = _C_LEN >> 1;
54245d3b8cb7SBill Sommerfeld 	sens->sadb_sens_integ_len = 0; /* TBD */
54255d3b8cb7SBill Sommerfeld 	sens->sadb_x_sens_flags = 0;
54265d3b8cb7SBill Sommerfeld 
54275d3b8cb7SBill Sommerfeld 	bitmap = (uint8_t *)(sens + 1);
54285d3b8cb7SBill Sommerfeld 	bcopy(&(((_bslabel_impl_t *)sl)->compartments), bitmap, _C_LEN * 4);
54295d3b8cb7SBill Sommerfeld }
54305d3b8cb7SBill Sommerfeld 
54315d3b8cb7SBill Sommerfeld static sadb_sens_t *
5432bd670b35SErik Nordmark sadb_make_sens_ext(ts_label_t *tsl, int *len)
54335d3b8cb7SBill Sommerfeld {
54345d3b8cb7SBill Sommerfeld 	/* XXX allocation failure? */
5435bd670b35SErik Nordmark 	int sens_len = sadb_sens_len_from_label(tsl);
54365d3b8cb7SBill Sommerfeld 
54375d3b8cb7SBill Sommerfeld 	sadb_sens_t *sens = kmem_alloc(sens_len, KM_SLEEP);
54385d3b8cb7SBill Sommerfeld 
5439bd670b35SErik Nordmark 	sadb_sens_from_label(sens, SADB_EXT_SENSITIVITY, tsl, sens_len);
54405d3b8cb7SBill Sommerfeld 
54415d3b8cb7SBill Sommerfeld 	*len = sens_len;
54425d3b8cb7SBill Sommerfeld 
54435d3b8cb7SBill Sommerfeld 	return (sens);
54445d3b8cb7SBill Sommerfeld }
54455d3b8cb7SBill Sommerfeld 
54465d3b8cb7SBill Sommerfeld /*
54475d3b8cb7SBill Sommerfeld  * Okay, how do we report errors/invalid labels from this?
54485d3b8cb7SBill Sommerfeld  * With a special designated "not a label" cred_t ?
54495d3b8cb7SBill Sommerfeld  */
54505d3b8cb7SBill Sommerfeld /* ARGSUSED */
5451bd670b35SErik Nordmark ts_label_t *
5452bd670b35SErik Nordmark sadb_label_from_sens(sadb_sens_t *sens, uint64_t *bitmap)
54535d3b8cb7SBill Sommerfeld {
54545d3b8cb7SBill Sommerfeld 	int bitmap_len = SADB_64TO8(sens->sadb_sens_sens_len);
54555d3b8cb7SBill Sommerfeld 	bslabel_t sl;
5456bd670b35SErik Nordmark 	ts_label_t *tsl;
54575d3b8cb7SBill Sommerfeld 
54585d3b8cb7SBill Sommerfeld 	if (sens->sadb_sens_integ_level != 0)
54595d3b8cb7SBill Sommerfeld 		return (NULL);
54605d3b8cb7SBill Sommerfeld 	if (sens->sadb_sens_integ_len != 0)
54615d3b8cb7SBill Sommerfeld 		return (NULL);
54625d3b8cb7SBill Sommerfeld 	if (bitmap_len > _C_LEN * 4)
54635d3b8cb7SBill Sommerfeld 		return (NULL);
54645d3b8cb7SBill Sommerfeld 
54655d3b8cb7SBill Sommerfeld 	bsllow(&sl);
54665d3b8cb7SBill Sommerfeld 	LCLASS_SET((_bslabel_impl_t *)&sl, sens->sadb_sens_sens_level);
54675d3b8cb7SBill Sommerfeld 	bcopy(bitmap, &((_bslabel_impl_t *)&sl)->compartments,
54685d3b8cb7SBill Sommerfeld 	    bitmap_len);
54695d3b8cb7SBill Sommerfeld 
5470bd670b35SErik Nordmark 	tsl = labelalloc(&sl, sens->sadb_sens_dpd, KM_NOSLEEP);
5471bd670b35SErik Nordmark 	if (tsl == NULL)
5472bd670b35SErik Nordmark 		return (NULL);
54735d3b8cb7SBill Sommerfeld 
54745d3b8cb7SBill Sommerfeld 	if (sens->sadb_x_sens_flags & SADB_X_SENS_UNLABELED)
5475bd670b35SErik Nordmark 		tsl->tsl_flags |= TSLF_UNLABELED;
5476bd670b35SErik Nordmark 	return (tsl);
54775d3b8cb7SBill Sommerfeld }
54785d3b8cb7SBill Sommerfeld 
54795d3b8cb7SBill Sommerfeld /* End XXX label-library-leakage */
54805d3b8cb7SBill Sommerfeld 
54817c478bd9Sstevel@tonic-gate /*
54827c478bd9Sstevel@tonic-gate  * Construct an extended ACQUIRE message based on a selector and the resulting
54837c478bd9Sstevel@tonic-gate  * IPsec action.
54847c478bd9Sstevel@tonic-gate  *
54857c478bd9Sstevel@tonic-gate  * NOTE: This is used by both inverse ACQUIRE and actual ACQUIRE
54867c478bd9Sstevel@tonic-gate  * generation. As a consequence, expect this function to evolve
54877c478bd9Sstevel@tonic-gate  * rapidly.
54887c478bd9Sstevel@tonic-gate  */
54897c478bd9Sstevel@tonic-gate static mblk_t *
54907c478bd9Sstevel@tonic-gate sadb_extended_acquire(ipsec_selector_t *sel, ipsec_policy_t *pol,
5491f4b3ec61Sdh155122     ipsec_action_t *act, boolean_t tunnel_mode, uint32_t seq, uint32_t pid,
54925d3b8cb7SBill Sommerfeld     sadb_sens_t *sens, netstack_t *ns)
54937c478bd9Sstevel@tonic-gate {
54947c478bd9Sstevel@tonic-gate 	mblk_t *mp;
54957c478bd9Sstevel@tonic-gate 	sadb_msg_t *samsg;
54967c478bd9Sstevel@tonic-gate 	uint8_t *start, *cur, *end;
54977c478bd9Sstevel@tonic-gate 	uint32_t *saddrptr, *daddrptr;
54987c478bd9Sstevel@tonic-gate 	sa_family_t af;
54997c478bd9Sstevel@tonic-gate 	sadb_prop_t *eprop;
55007c478bd9Sstevel@tonic-gate 	ipsec_action_t *ap, *an;
55018810c16bSdanmcd 	ipsec_selkey_t *ipsl;
55028810c16bSdanmcd 	uint8_t proto, pfxlen;
55037c478bd9Sstevel@tonic-gate 	uint16_t lport, rport;
55047c478bd9Sstevel@tonic-gate 	uint32_t kmp, kmc;
55057c478bd9Sstevel@tonic-gate 
55067c478bd9Sstevel@tonic-gate 	/*
55077c478bd9Sstevel@tonic-gate 	 * Find the action we want sooner rather than later..
55087c478bd9Sstevel@tonic-gate 	 */
55097c478bd9Sstevel@tonic-gate 	an = NULL;
55107c478bd9Sstevel@tonic-gate 	if (pol == NULL) {
55117c478bd9Sstevel@tonic-gate 		ap = act;
55127c478bd9Sstevel@tonic-gate 	} else {
55137c478bd9Sstevel@tonic-gate 		ap = pol->ipsp_act;
55147c478bd9Sstevel@tonic-gate 
55157c478bd9Sstevel@tonic-gate 		if (ap != NULL)
55167c478bd9Sstevel@tonic-gate 			an = ap->ipa_next;
55177c478bd9Sstevel@tonic-gate 	}
55187c478bd9Sstevel@tonic-gate 
55197c478bd9Sstevel@tonic-gate 	/*
55207c478bd9Sstevel@tonic-gate 	 * Just take a swag for the allocation for now.	 We can always
55217c478bd9Sstevel@tonic-gate 	 * alter it later.
55227c478bd9Sstevel@tonic-gate 	 */
55230358d3a6Sdanmcd #define	SADB_EXTENDED_ACQUIRE_SIZE	4096
55247c478bd9Sstevel@tonic-gate 	mp = allocb(SADB_EXTENDED_ACQUIRE_SIZE, BPRI_HI);
55257c478bd9Sstevel@tonic-gate 	if (mp == NULL)
55267c478bd9Sstevel@tonic-gate 		return (NULL);
55277c478bd9Sstevel@tonic-gate 
55287c478bd9Sstevel@tonic-gate 	start = mp->b_rptr;
55297c478bd9Sstevel@tonic-gate 	end = start + SADB_EXTENDED_ACQUIRE_SIZE;
55307c478bd9Sstevel@tonic-gate 
55317c478bd9Sstevel@tonic-gate 	cur = start;
55327c478bd9Sstevel@tonic-gate 
55337c478bd9Sstevel@tonic-gate 	samsg = (sadb_msg_t *)cur;
55347c478bd9Sstevel@tonic-gate 	cur += sizeof (*samsg);
55357c478bd9Sstevel@tonic-gate 
55367c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_version = PF_KEY_V2;
55377c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_type = SADB_ACQUIRE;
55387c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_errno = 0;
55397c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_reserved = 0;
55407c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_satype = 0;
55417c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_seq = seq;
55427c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_pid = pid;
55437c478bd9Sstevel@tonic-gate 
55448810c16bSdanmcd 	if (tunnel_mode) {
55457c478bd9Sstevel@tonic-gate 		/*
55468810c16bSdanmcd 		 * Form inner address extensions based NOT on the inner
55478810c16bSdanmcd 		 * selectors (i.e. the packet data), but on the policy's
55488810c16bSdanmcd 		 * selector key (i.e. the policy's selector information).
55498810c16bSdanmcd 		 *
55508810c16bSdanmcd 		 * NOTE:  The position of IPv4 and IPv6 addresses is the
55518810c16bSdanmcd 		 * same in ipsec_selkey_t (unless the compiler does very
55528810c16bSdanmcd 		 * strange things with unions, consult your local C language
55538810c16bSdanmcd 		 * lawyer for details).
55547c478bd9Sstevel@tonic-gate 		 */
55557807385bSDan McDonald 		ASSERT(pol != NULL);
55567807385bSDan McDonald 
55578810c16bSdanmcd 		ipsl = &(pol->ipsp_sel->ipsl_key);
55588810c16bSdanmcd 		if (ipsl->ipsl_valid & IPSL_IPV4) {
55598810c16bSdanmcd 			af = AF_INET;
55608810c16bSdanmcd 			ASSERT(sel->ips_protocol == IPPROTO_ENCAP);
55618810c16bSdanmcd 			ASSERT(!(ipsl->ipsl_valid & IPSL_IPV6));
55628810c16bSdanmcd 		} else {
55638810c16bSdanmcd 			af = AF_INET6;
55648810c16bSdanmcd 			ASSERT(sel->ips_protocol == IPPROTO_IPV6);
55658810c16bSdanmcd 			ASSERT(ipsl->ipsl_valid & IPSL_IPV6);
55668810c16bSdanmcd 		}
55677c478bd9Sstevel@tonic-gate 
55688810c16bSdanmcd 		if (ipsl->ipsl_valid & IPSL_LOCAL_ADDR) {
55698810c16bSdanmcd 			saddrptr = (uint32_t *)(&ipsl->ipsl_local);
55708810c16bSdanmcd 			pfxlen = ipsl->ipsl_local_pfxlen;
55718810c16bSdanmcd 		} else {
55728810c16bSdanmcd 			saddrptr = (uint32_t *)(&ipv6_all_zeros);
55738810c16bSdanmcd 			pfxlen = 0;
55748810c16bSdanmcd 		}
55758810c16bSdanmcd 		/* XXX What about ICMP type/code? */
55768810c16bSdanmcd 		lport = (ipsl->ipsl_valid & IPSL_LOCAL_PORT) ?
55778810c16bSdanmcd 		    ipsl->ipsl_lport : 0;
55788810c16bSdanmcd 		proto = (ipsl->ipsl_valid & IPSL_PROTOCOL) ?
55798810c16bSdanmcd 		    ipsl->ipsl_proto : 0;
55808810c16bSdanmcd 
55818810c16bSdanmcd 		cur = sadb_make_addr_ext(cur, end, SADB_X_EXT_ADDRESS_INNER_SRC,
55828810c16bSdanmcd 		    af, saddrptr, lport, proto, pfxlen);
55838810c16bSdanmcd 		if (cur == NULL) {
55848810c16bSdanmcd 			freeb(mp);
55858810c16bSdanmcd 			return (NULL);
55868810c16bSdanmcd 		}
55878810c16bSdanmcd 
55888810c16bSdanmcd 		if (ipsl->ipsl_valid & IPSL_REMOTE_ADDR) {
55898810c16bSdanmcd 			daddrptr = (uint32_t *)(&ipsl->ipsl_remote);
55908810c16bSdanmcd 			pfxlen = ipsl->ipsl_remote_pfxlen;
55918810c16bSdanmcd 		} else {
55928810c16bSdanmcd 			daddrptr = (uint32_t *)(&ipv6_all_zeros);
55938810c16bSdanmcd 			pfxlen = 0;
55948810c16bSdanmcd 		}
55958810c16bSdanmcd 		/* XXX What about ICMP type/code? */
55968810c16bSdanmcd 		rport = (ipsl->ipsl_valid & IPSL_REMOTE_PORT) ?
55978810c16bSdanmcd 		    ipsl->ipsl_rport : 0;
55988810c16bSdanmcd 
55998810c16bSdanmcd 		cur = sadb_make_addr_ext(cur, end, SADB_X_EXT_ADDRESS_INNER_DST,
56008810c16bSdanmcd 		    af, daddrptr, rport, proto, pfxlen);
56018810c16bSdanmcd 		if (cur == NULL) {
56028810c16bSdanmcd 			freeb(mp);
56038810c16bSdanmcd 			return (NULL);
56048810c16bSdanmcd 		}
56058810c16bSdanmcd 		/*
56068810c16bSdanmcd 		 * TODO  - if we go to 3408's dream of transport mode IP-in-IP
56078810c16bSdanmcd 		 * _with_ inner-packet address selectors, we'll need to further
56088810c16bSdanmcd 		 * distinguish tunnel mode here.  For now, having inner
56098810c16bSdanmcd 		 * addresses and/or ports is sufficient.
56108810c16bSdanmcd 		 *
56118810c16bSdanmcd 		 * Meanwhile, whack proto/ports to reflect IP-in-IP for the
56128810c16bSdanmcd 		 * outer addresses.
56138810c16bSdanmcd 		 */
56148810c16bSdanmcd 		proto = sel->ips_protocol;	/* Either _ENCAP or _IPV6 */
56158810c16bSdanmcd 		lport = rport = 0;
56168810c16bSdanmcd 	} else if ((ap != NULL) && (!ap->ipa_want_unique)) {
56177c478bd9Sstevel@tonic-gate 		proto = 0;
56187c478bd9Sstevel@tonic-gate 		lport = 0;
56197c478bd9Sstevel@tonic-gate 		rport = 0;
56207c478bd9Sstevel@tonic-gate 		if (pol != NULL) {
56218810c16bSdanmcd 			ipsl = &(pol->ipsp_sel->ipsl_key);
56228810c16bSdanmcd 			if (ipsl->ipsl_valid & IPSL_PROTOCOL)
56238810c16bSdanmcd 				proto = ipsl->ipsl_proto;
56248810c16bSdanmcd 			if (ipsl->ipsl_valid & IPSL_REMOTE_PORT)
56258810c16bSdanmcd 				rport = ipsl->ipsl_rport;
56268810c16bSdanmcd 			if (ipsl->ipsl_valid & IPSL_LOCAL_PORT)
56278810c16bSdanmcd 				lport = ipsl->ipsl_lport;
56287c478bd9Sstevel@tonic-gate 		}
56298810c16bSdanmcd 	} else {
56308810c16bSdanmcd 		proto = sel->ips_protocol;
56318810c16bSdanmcd 		lport = sel->ips_local_port;
56328810c16bSdanmcd 		rport = sel->ips_remote_port;
56337c478bd9Sstevel@tonic-gate 	}
56347c478bd9Sstevel@tonic-gate 
56358810c16bSdanmcd 	af = sel->ips_isv4 ? AF_INET : AF_INET6;
56368810c16bSdanmcd 
56378810c16bSdanmcd 	/*
56388810c16bSdanmcd 	 * NOTE:  The position of IPv4 and IPv6 addresses is the same in
56398810c16bSdanmcd 	 * ipsec_selector_t.
56408810c16bSdanmcd 	 */
56417c478bd9Sstevel@tonic-gate 	cur = sadb_make_addr_ext(cur, end, SADB_EXT_ADDRESS_SRC, af,
56428810c16bSdanmcd 	    (uint32_t *)(&sel->ips_local_addr_v6), lport, proto, 0);
56437c478bd9Sstevel@tonic-gate 
56447c478bd9Sstevel@tonic-gate 	if (cur == NULL) {
56457c478bd9Sstevel@tonic-gate 		freeb(mp);
56467c478bd9Sstevel@tonic-gate 		return (NULL);
56477c478bd9Sstevel@tonic-gate 	}
56487c478bd9Sstevel@tonic-gate 
56497c478bd9Sstevel@tonic-gate 	cur = sadb_make_addr_ext(cur, end, SADB_EXT_ADDRESS_DST, af,
56508810c16bSdanmcd 	    (uint32_t *)(&sel->ips_remote_addr_v6), rport, proto, 0);
56517c478bd9Sstevel@tonic-gate 
56527c478bd9Sstevel@tonic-gate 	if (cur == NULL) {
56537c478bd9Sstevel@tonic-gate 		freeb(mp);
56547c478bd9Sstevel@tonic-gate 		return (NULL);
56557c478bd9Sstevel@tonic-gate 	}
56567c478bd9Sstevel@tonic-gate 
56575d3b8cb7SBill Sommerfeld 	if (sens != NULL) {
56585d3b8cb7SBill Sommerfeld 		uint8_t *sensext = cur;
56595d3b8cb7SBill Sommerfeld 		int senslen = SADB_64TO8(sens->sadb_sens_len);
56605d3b8cb7SBill Sommerfeld 
56615d3b8cb7SBill Sommerfeld 		cur += senslen;
56625d3b8cb7SBill Sommerfeld 		if (cur > end) {
56635d3b8cb7SBill Sommerfeld 			freeb(mp);
56645d3b8cb7SBill Sommerfeld 			return (NULL);
56655d3b8cb7SBill Sommerfeld 		}
56665d3b8cb7SBill Sommerfeld 		bcopy(sens, sensext, senslen);
56675d3b8cb7SBill Sommerfeld 	}
56685d3b8cb7SBill Sommerfeld 
56697c478bd9Sstevel@tonic-gate 	/*
56707c478bd9Sstevel@tonic-gate 	 * This section will change a lot as policy evolves.
56717c478bd9Sstevel@tonic-gate 	 * For now, it'll be relatively simple.
56727c478bd9Sstevel@tonic-gate 	 */
56737c478bd9Sstevel@tonic-gate 	eprop = (sadb_prop_t *)cur;
56747c478bd9Sstevel@tonic-gate 	cur += sizeof (*eprop);
56757c478bd9Sstevel@tonic-gate 	if (cur > end) {
56767c478bd9Sstevel@tonic-gate 		/* no space left */
56777c478bd9Sstevel@tonic-gate 		freeb(mp);
56787c478bd9Sstevel@tonic-gate 		return (NULL);
56797c478bd9Sstevel@tonic-gate 	}
56807c478bd9Sstevel@tonic-gate 
56817c478bd9Sstevel@tonic-gate 	eprop->sadb_prop_exttype = SADB_X_EXT_EPROP;
56827c478bd9Sstevel@tonic-gate 	eprop->sadb_x_prop_ereserved = 0;
56837c478bd9Sstevel@tonic-gate 	eprop->sadb_x_prop_numecombs = 0;
56847c478bd9Sstevel@tonic-gate 	eprop->sadb_prop_replay = 32;	/* default */
56857c478bd9Sstevel@tonic-gate 
56867c478bd9Sstevel@tonic-gate 	kmc = kmp = 0;
56877c478bd9Sstevel@tonic-gate 
56887c478bd9Sstevel@tonic-gate 	for (; ap != NULL; ap = an) {
56897c478bd9Sstevel@tonic-gate 		an = (pol != NULL) ? ap->ipa_next : NULL;
56907c478bd9Sstevel@tonic-gate 
56917c478bd9Sstevel@tonic-gate 		/*
56927c478bd9Sstevel@tonic-gate 		 * Skip non-IPsec policies
56937c478bd9Sstevel@tonic-gate 		 */
56947c478bd9Sstevel@tonic-gate 		if (ap->ipa_act.ipa_type != IPSEC_ACT_APPLY)
56957c478bd9Sstevel@tonic-gate 			continue;
56967c478bd9Sstevel@tonic-gate 
56977c478bd9Sstevel@tonic-gate 		if (ap->ipa_act.ipa_apply.ipp_km_proto)
56987c478bd9Sstevel@tonic-gate 			kmp = ap->ipa_act.ipa_apply.ipp_km_proto;
56997c478bd9Sstevel@tonic-gate 		if (ap->ipa_act.ipa_apply.ipp_km_cookie)
57007c478bd9Sstevel@tonic-gate 			kmc = ap->ipa_act.ipa_apply.ipp_km_cookie;
57017c478bd9Sstevel@tonic-gate 		if (ap->ipa_act.ipa_apply.ipp_replay_depth) {
57027c478bd9Sstevel@tonic-gate 			eprop->sadb_prop_replay =
57037c478bd9Sstevel@tonic-gate 			    ap->ipa_act.ipa_apply.ipp_replay_depth;
57047c478bd9Sstevel@tonic-gate 		}
57057c478bd9Sstevel@tonic-gate 
5706f4b3ec61Sdh155122 		cur = sadb_action_to_ecomb(cur, end, ap, ns);
57077c478bd9Sstevel@tonic-gate 		if (cur == NULL) { /* no space */
57087c478bd9Sstevel@tonic-gate 			freeb(mp);
57097c478bd9Sstevel@tonic-gate 			return (NULL);
57107c478bd9Sstevel@tonic-gate 		}
57117c478bd9Sstevel@tonic-gate 		eprop->sadb_x_prop_numecombs++;
57127c478bd9Sstevel@tonic-gate 	}
57137c478bd9Sstevel@tonic-gate 
57147c478bd9Sstevel@tonic-gate 	if (eprop->sadb_x_prop_numecombs == 0) {
57157c478bd9Sstevel@tonic-gate 		/*
57167c478bd9Sstevel@tonic-gate 		 * This will happen if we fail to find a policy
57177c478bd9Sstevel@tonic-gate 		 * allowing for IPsec processing.
57187c478bd9Sstevel@tonic-gate 		 * Construct an error message.
57197c478bd9Sstevel@tonic-gate 		 */
57207c478bd9Sstevel@tonic-gate 		samsg->sadb_msg_len = SADB_8TO64(sizeof (*samsg));
57217c478bd9Sstevel@tonic-gate 		samsg->sadb_msg_errno = ENOENT;
57227c478bd9Sstevel@tonic-gate 		samsg->sadb_x_msg_diagnostic = 0;
57237c478bd9Sstevel@tonic-gate 		return (mp);
57247c478bd9Sstevel@tonic-gate 	}
57257c478bd9Sstevel@tonic-gate 
57267c478bd9Sstevel@tonic-gate 	if ((kmp != 0) || (kmc != 0)) {
57277c478bd9Sstevel@tonic-gate 		cur = sadb_make_kmc_ext(cur, end, kmp, kmc);
57287c478bd9Sstevel@tonic-gate 		if (cur == NULL) {
57297c478bd9Sstevel@tonic-gate 			freeb(mp);
57307c478bd9Sstevel@tonic-gate 			return (NULL);
57317c478bd9Sstevel@tonic-gate 		}
57327c478bd9Sstevel@tonic-gate 	}
57337c478bd9Sstevel@tonic-gate 
57347c478bd9Sstevel@tonic-gate 	eprop->sadb_prop_len = SADB_8TO64(cur - (uint8_t *)eprop);
57357c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_len = SADB_8TO64(cur - start);
57367c478bd9Sstevel@tonic-gate 	mp->b_wptr = cur;
57377c478bd9Sstevel@tonic-gate 
57387c478bd9Sstevel@tonic-gate 	return (mp);
57397c478bd9Sstevel@tonic-gate }
57407c478bd9Sstevel@tonic-gate 
57417c478bd9Sstevel@tonic-gate /*
57428810c16bSdanmcd  * Generic setup of an RFC 2367 ACQUIRE message.  Caller sets satype.
57438810c16bSdanmcd  *
57448810c16bSdanmcd  * NOTE: This function acquires alg_lock as a side-effect if-and-only-if we
57458810c16bSdanmcd  * succeed (i.e. return non-NULL).  Caller MUST release it.  This is to
57468810c16bSdanmcd  * maximize code consolidation while preventing algorithm changes from messing
57478810c16bSdanmcd  * with the callers finishing touches on the ACQUIRE itself.
57487c478bd9Sstevel@tonic-gate  */
57498810c16bSdanmcd mblk_t *
5750f4b3ec61Sdh155122 sadb_setup_acquire(ipsacq_t *acqrec, uint8_t satype, ipsec_stack_t *ipss)
57517c478bd9Sstevel@tonic-gate {
57528810c16bSdanmcd 	uint_t allocsize;
57538810c16bSdanmcd 	mblk_t *pfkeymp, *msgmp;
57547c478bd9Sstevel@tonic-gate 	sa_family_t af;
57558810c16bSdanmcd 	uint8_t *cur, *end;
57568810c16bSdanmcd 	sadb_msg_t *samsg;
57577c478bd9Sstevel@tonic-gate 	uint16_t sport_typecode;
57587c478bd9Sstevel@tonic-gate 	uint16_t dport_typecode;
57597c478bd9Sstevel@tonic-gate 	uint8_t check_proto;
57608810c16bSdanmcd 	boolean_t tunnel_mode = (acqrec->ipsacq_inneraddrfam != 0);
57617c478bd9Sstevel@tonic-gate 
57628810c16bSdanmcd 	ASSERT(MUTEX_HELD(&acqrec->ipsacq_lock));
57638810c16bSdanmcd 
57648810c16bSdanmcd 	pfkeymp = sadb_keysock_out(0);
57658810c16bSdanmcd 	if (pfkeymp == NULL)
57667c478bd9Sstevel@tonic-gate 		return (NULL);
57677c478bd9Sstevel@tonic-gate 
57688810c16bSdanmcd 	/*
57698810c16bSdanmcd 	 * First, allocate a basic ACQUIRE message
57708810c16bSdanmcd 	 */
57718810c16bSdanmcd 	allocsize = sizeof (sadb_msg_t) + sizeof (sadb_address_t) +
57728810c16bSdanmcd 	    sizeof (sadb_address_t) + sizeof (sadb_prop_t);
57738810c16bSdanmcd 
57748810c16bSdanmcd 	/* Make sure there's enough to cover both AF_INET and AF_INET6. */
57758810c16bSdanmcd 	allocsize += 2 * sizeof (struct sockaddr_in6);
57768810c16bSdanmcd 
5777f4b3ec61Sdh155122 	mutex_enter(&ipss->ipsec_alg_lock);
57788810c16bSdanmcd 	/* NOTE:  The lock is now held through to this function's return. */
5779f4b3ec61Sdh155122 	allocsize += ipss->ipsec_nalgs[IPSEC_ALG_AUTH] *
5780f4b3ec61Sdh155122 	    ipss->ipsec_nalgs[IPSEC_ALG_ENCR] * sizeof (sadb_comb_t);
57818810c16bSdanmcd 
57828810c16bSdanmcd 	if (tunnel_mode) {
57838810c16bSdanmcd 		/* Tunnel mode! */
57848810c16bSdanmcd 		allocsize += 2 * sizeof (sadb_address_t);
57858810c16bSdanmcd 		/* Enough to cover both AF_INET and AF_INET6. */
57868810c16bSdanmcd 		allocsize += 2 * sizeof (struct sockaddr_in6);
57878810c16bSdanmcd 	}
57888810c16bSdanmcd 
57898810c16bSdanmcd 	msgmp = allocb(allocsize, BPRI_HI);
57908810c16bSdanmcd 	if (msgmp == NULL) {
57918810c16bSdanmcd 		freeb(pfkeymp);
5792f4b3ec61Sdh155122 		mutex_exit(&ipss->ipsec_alg_lock);
57938810c16bSdanmcd 		return (NULL);
57948810c16bSdanmcd 	}
57958810c16bSdanmcd 
57968810c16bSdanmcd 	pfkeymp->b_cont = msgmp;
57978810c16bSdanmcd 	cur = msgmp->b_rptr;
57988810c16bSdanmcd 	end = cur + allocsize;
57998810c16bSdanmcd 	samsg = (sadb_msg_t *)cur;
58008810c16bSdanmcd 	cur += sizeof (sadb_msg_t);
58018810c16bSdanmcd 
58027c478bd9Sstevel@tonic-gate 	af = acqrec->ipsacq_addrfam;
58037c478bd9Sstevel@tonic-gate 	switch (af) {
58047c478bd9Sstevel@tonic-gate 	case AF_INET:
58057c478bd9Sstevel@tonic-gate 		check_proto = IPPROTO_ICMP;
58067c478bd9Sstevel@tonic-gate 		break;
58077c478bd9Sstevel@tonic-gate 	case AF_INET6:
58087c478bd9Sstevel@tonic-gate 		check_proto = IPPROTO_ICMPV6;
58097c478bd9Sstevel@tonic-gate 		break;
58107c478bd9Sstevel@tonic-gate 	default:
58117c478bd9Sstevel@tonic-gate 		/* This should never happen unless we have kernel bugs. */
58127c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN,
58137c478bd9Sstevel@tonic-gate 		    "sadb_setup_acquire:  corrupt ACQUIRE record.\n");
58147c478bd9Sstevel@tonic-gate 		ASSERT(0);
5815f4b3ec61Sdh155122 		mutex_exit(&ipss->ipsec_alg_lock);
58167c478bd9Sstevel@tonic-gate 		return (NULL);
58177c478bd9Sstevel@tonic-gate 	}
58187c478bd9Sstevel@tonic-gate 
58197c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_version = PF_KEY_V2;
58207c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_type = SADB_ACQUIRE;
58218810c16bSdanmcd 	samsg->sadb_msg_satype = satype;
58227c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_errno = 0;
58237c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_pid = 0;
58247c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_reserved = 0;
58257c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_seq = acqrec->ipsacq_seq;
58267c478bd9Sstevel@tonic-gate 
58277c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&acqrec->ipsacq_lock));
58287c478bd9Sstevel@tonic-gate 
58298810c16bSdanmcd 	if ((acqrec->ipsacq_proto == check_proto) || tunnel_mode) {
58307c478bd9Sstevel@tonic-gate 		sport_typecode = dport_typecode = 0;
58317c478bd9Sstevel@tonic-gate 	} else {
58327c478bd9Sstevel@tonic-gate 		sport_typecode = acqrec->ipsacq_srcport;
58337c478bd9Sstevel@tonic-gate 		dport_typecode = acqrec->ipsacq_dstport;
58347c478bd9Sstevel@tonic-gate 	}
58357c478bd9Sstevel@tonic-gate 
58367c478bd9Sstevel@tonic-gate 	cur = sadb_make_addr_ext(cur, end, SADB_EXT_ADDRESS_SRC, af,
58378810c16bSdanmcd 	    acqrec->ipsacq_srcaddr, sport_typecode, acqrec->ipsacq_proto, 0);
58387c478bd9Sstevel@tonic-gate 
58397c478bd9Sstevel@tonic-gate 	cur = sadb_make_addr_ext(cur, end, SADB_EXT_ADDRESS_DST, af,
58408810c16bSdanmcd 	    acqrec->ipsacq_dstaddr, dport_typecode, acqrec->ipsacq_proto, 0);
58418810c16bSdanmcd 
58428810c16bSdanmcd 	if (tunnel_mode) {
58438810c16bSdanmcd 		sport_typecode = acqrec->ipsacq_srcport;
58448810c16bSdanmcd 		dport_typecode = acqrec->ipsacq_dstport;
58458810c16bSdanmcd 		cur = sadb_make_addr_ext(cur, end, SADB_X_EXT_ADDRESS_INNER_SRC,
58468810c16bSdanmcd 		    acqrec->ipsacq_inneraddrfam, acqrec->ipsacq_innersrc,
58478810c16bSdanmcd 		    sport_typecode, acqrec->ipsacq_inner_proto,
58488810c16bSdanmcd 		    acqrec->ipsacq_innersrcpfx);
58498810c16bSdanmcd 		cur = sadb_make_addr_ext(cur, end, SADB_X_EXT_ADDRESS_INNER_DST,
58508810c16bSdanmcd 		    acqrec->ipsacq_inneraddrfam, acqrec->ipsacq_innerdst,
58518810c16bSdanmcd 		    dport_typecode, acqrec->ipsacq_inner_proto,
58528810c16bSdanmcd 		    acqrec->ipsacq_innerdstpfx);
58538810c16bSdanmcd 	}
58548810c16bSdanmcd 
58558810c16bSdanmcd 	/* XXX Insert identity information here. */
58568810c16bSdanmcd 
58578810c16bSdanmcd 	/* XXXMLS Insert sensitivity information here. */
58587c478bd9Sstevel@tonic-gate 
58597c478bd9Sstevel@tonic-gate 	if (cur != NULL)
58608810c16bSdanmcd 		samsg->sadb_msg_len = SADB_8TO64(cur - msgmp->b_rptr);
58618810c16bSdanmcd 	else
5862f4b3ec61Sdh155122 		mutex_exit(&ipss->ipsec_alg_lock);
58637c478bd9Sstevel@tonic-gate 
58648810c16bSdanmcd 	return (pfkeymp);
58657c478bd9Sstevel@tonic-gate }
58667c478bd9Sstevel@tonic-gate 
58677c478bd9Sstevel@tonic-gate /*
58687c478bd9Sstevel@tonic-gate  * Given an SADB_GETSPI message, find an appropriately ranged SA and
58697c478bd9Sstevel@tonic-gate  * allocate an SA.  If there are message improprieties, return (ipsa_t *)-1.
58707c478bd9Sstevel@tonic-gate  * If there was a memory allocation error, return NULL.	 (Assume NULL !=
58717c478bd9Sstevel@tonic-gate  * (ipsa_t *)-1).
58727c478bd9Sstevel@tonic-gate  *
58737c478bd9Sstevel@tonic-gate  * master_spi is passed in host order.
58747c478bd9Sstevel@tonic-gate  */
58757c478bd9Sstevel@tonic-gate ipsa_t *
5876f4b3ec61Sdh155122 sadb_getspi(keysock_in_t *ksi, uint32_t master_spi, int *diagnostic,
58779c2c14abSThejaswini Singarajipura     netstack_t *ns, uint_t sa_type)
58787c478bd9Sstevel@tonic-gate {
58797c478bd9Sstevel@tonic-gate 	sadb_address_t *src =
58807c478bd9Sstevel@tonic-gate 	    (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_SRC],
58817c478bd9Sstevel@tonic-gate 	    *dst = (sadb_address_t *)ksi->ks_in_extv[SADB_EXT_ADDRESS_DST];
58827c478bd9Sstevel@tonic-gate 	sadb_spirange_t *range =
58837c478bd9Sstevel@tonic-gate 	    (sadb_spirange_t *)ksi->ks_in_extv[SADB_EXT_SPIRANGE];
58847c478bd9Sstevel@tonic-gate 	struct sockaddr_in *ssa, *dsa;
58857c478bd9Sstevel@tonic-gate 	struct sockaddr_in6 *ssa6, *dsa6;
58867c478bd9Sstevel@tonic-gate 	uint32_t *srcaddr, *dstaddr;
58877c478bd9Sstevel@tonic-gate 	sa_family_t af;
58887c478bd9Sstevel@tonic-gate 	uint32_t add, min, max;
58899c2c14abSThejaswini Singarajipura 	uint8_t protocol =
58909c2c14abSThejaswini Singarajipura 	    (sa_type == SADB_SATYPE_AH) ? IPPROTO_AH : IPPROTO_ESP;
58917c478bd9Sstevel@tonic-gate 
58927c478bd9Sstevel@tonic-gate 	if (src == NULL) {
58937c478bd9Sstevel@tonic-gate 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_SRC;
58947c478bd9Sstevel@tonic-gate 		return ((ipsa_t *)-1);
58957c478bd9Sstevel@tonic-gate 	}
58967c478bd9Sstevel@tonic-gate 	if (dst == NULL) {
58977c478bd9Sstevel@tonic-gate 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_DST;
58987c478bd9Sstevel@tonic-gate 		return ((ipsa_t *)-1);
58997c478bd9Sstevel@tonic-gate 	}
59007c478bd9Sstevel@tonic-gate 	if (range == NULL) {
59017c478bd9Sstevel@tonic-gate 		*diagnostic = SADB_X_DIAGNOSTIC_MISSING_RANGE;
59027c478bd9Sstevel@tonic-gate 		return ((ipsa_t *)-1);
59037c478bd9Sstevel@tonic-gate 	}
59047c478bd9Sstevel@tonic-gate 
59057c478bd9Sstevel@tonic-gate 	min = ntohl(range->sadb_spirange_min);
59067c478bd9Sstevel@tonic-gate 	max = ntohl(range->sadb_spirange_max);
59077c478bd9Sstevel@tonic-gate 	dsa = (struct sockaddr_in *)(dst + 1);
59087c478bd9Sstevel@tonic-gate 	dsa6 = (struct sockaddr_in6 *)dsa;
59097c478bd9Sstevel@tonic-gate 
59107c478bd9Sstevel@tonic-gate 	ssa = (struct sockaddr_in *)(src + 1);
59117c478bd9Sstevel@tonic-gate 	ssa6 = (struct sockaddr_in6 *)ssa;
59128810c16bSdanmcd 	ASSERT(dsa->sin_family == ssa->sin_family);
59137c478bd9Sstevel@tonic-gate 
59147c478bd9Sstevel@tonic-gate 	srcaddr = ALL_ZEROES_PTR;
59157c478bd9Sstevel@tonic-gate 	af = dsa->sin_family;
59167c478bd9Sstevel@tonic-gate 	switch (af) {
59177c478bd9Sstevel@tonic-gate 	case AF_INET:
59187c478bd9Sstevel@tonic-gate 		if (src != NULL)
59197c478bd9Sstevel@tonic-gate 			srcaddr = (uint32_t *)(&ssa->sin_addr);
59207c478bd9Sstevel@tonic-gate 		dstaddr = (uint32_t *)(&dsa->sin_addr);
59217c478bd9Sstevel@tonic-gate 		break;
59227c478bd9Sstevel@tonic-gate 	case AF_INET6:
59237c478bd9Sstevel@tonic-gate 		if (src != NULL)
59247c478bd9Sstevel@tonic-gate 			srcaddr = (uint32_t *)(&ssa6->sin6_addr);
59257c478bd9Sstevel@tonic-gate 		dstaddr = (uint32_t *)(&dsa6->sin6_addr);
59267c478bd9Sstevel@tonic-gate 		break;
59277c478bd9Sstevel@tonic-gate 	default:
59287c478bd9Sstevel@tonic-gate 		*diagnostic = SADB_X_DIAGNOSTIC_BAD_DST_AF;
59297c478bd9Sstevel@tonic-gate 		return ((ipsa_t *)-1);
59307c478bd9Sstevel@tonic-gate 	}
59317c478bd9Sstevel@tonic-gate 
59327c478bd9Sstevel@tonic-gate 	if (master_spi < min || master_spi > max) {
59337c478bd9Sstevel@tonic-gate 		/* Return a random value in the range. */
59349c2c14abSThejaswini Singarajipura 		if (cl_inet_getspi) {
59358e4b770fSLu Huafeng 			cl_inet_getspi(ns->netstack_stackid, protocol,
59368e4b770fSLu Huafeng 			    (uint8_t *)&add, sizeof (add), NULL);
59379c2c14abSThejaswini Singarajipura 		} else {
59389c2c14abSThejaswini Singarajipura 			(void) random_get_pseudo_bytes((uint8_t *)&add,
59399c2c14abSThejaswini Singarajipura 			    sizeof (add));
59409c2c14abSThejaswini Singarajipura 		}
59417c478bd9Sstevel@tonic-gate 		master_spi = min + (add % (max - min + 1));
59427c478bd9Sstevel@tonic-gate 	}
59437c478bd9Sstevel@tonic-gate 
59447c478bd9Sstevel@tonic-gate 	/*
59457c478bd9Sstevel@tonic-gate 	 * Since master_spi is passed in host order, we need to htonl() it
59467c478bd9Sstevel@tonic-gate 	 * for the purposes of creating a new SA.
59477c478bd9Sstevel@tonic-gate 	 */
5948f4b3ec61Sdh155122 	return (sadb_makelarvalassoc(htonl(master_spi), srcaddr, dstaddr, af,
5949f4b3ec61Sdh155122 	    ns));
59507c478bd9Sstevel@tonic-gate }
59517c478bd9Sstevel@tonic-gate 
59527c478bd9Sstevel@tonic-gate /*
59537c478bd9Sstevel@tonic-gate  *
59547c478bd9Sstevel@tonic-gate  * Locate an ACQUIRE and nuke it.  If I have an samsg that's larger than the
59557c478bd9Sstevel@tonic-gate  * base header, just ignore it.	 Otherwise, lock down the whole ACQUIRE list
59567c478bd9Sstevel@tonic-gate  * and scan for the sequence number in question.  I may wish to accept an
59577c478bd9Sstevel@tonic-gate  * address pair with it, for easier searching.
59587c478bd9Sstevel@tonic-gate  *
59597c478bd9Sstevel@tonic-gate  * Caller frees the message, so we don't have to here.
59607c478bd9Sstevel@tonic-gate  *
5961bd670b35SErik Nordmark  * NOTE:	The pfkey_q parameter may be used in the future for ACQUIRE
59627c478bd9Sstevel@tonic-gate  *		failures.
59637c478bd9Sstevel@tonic-gate  */
59647c478bd9Sstevel@tonic-gate /* ARGSUSED */
59657c478bd9Sstevel@tonic-gate void
5966bd670b35SErik Nordmark sadb_in_acquire(sadb_msg_t *samsg, sadbp_t *sp, queue_t *pfkey_q,
5967bd670b35SErik Nordmark     netstack_t *ns)
59687c478bd9Sstevel@tonic-gate {
59697c478bd9Sstevel@tonic-gate 	int i;
59707c478bd9Sstevel@tonic-gate 	ipsacq_t *acqrec;
59717c478bd9Sstevel@tonic-gate 	iacqf_t *bucket;
59727c478bd9Sstevel@tonic-gate 
59737c478bd9Sstevel@tonic-gate 	/*
59747c478bd9Sstevel@tonic-gate 	 * I only accept the base header for this!
59757c478bd9Sstevel@tonic-gate 	 * Though to be honest, requiring the dst address would help
59767c478bd9Sstevel@tonic-gate 	 * immensely.
59777c478bd9Sstevel@tonic-gate 	 *
59787c478bd9Sstevel@tonic-gate 	 * XXX	There are already cases where I can get the dst address.
59797c478bd9Sstevel@tonic-gate 	 */
59807c478bd9Sstevel@tonic-gate 	if (samsg->sadb_msg_len > SADB_8TO64(sizeof (*samsg)))
59817c478bd9Sstevel@tonic-gate 		return;
59827c478bd9Sstevel@tonic-gate 
59837c478bd9Sstevel@tonic-gate 	/*
59847c478bd9Sstevel@tonic-gate 	 * Using the samsg->sadb_msg_seq, find the ACQUIRE record, delete it,
59857c478bd9Sstevel@tonic-gate 	 * (and in the future send a message to IP with the appropriate error
59867c478bd9Sstevel@tonic-gate 	 * number).
59877c478bd9Sstevel@tonic-gate 	 *
59887c478bd9Sstevel@tonic-gate 	 * Q: Do I want to reject if pid != 0?
59897c478bd9Sstevel@tonic-gate 	 */
59907c478bd9Sstevel@tonic-gate 
5991fb87b5d2Ssommerfe 	for (i = 0; i < sp->s_v4.sdb_hashsize; i++) {
59927c478bd9Sstevel@tonic-gate 		bucket = &sp->s_v4.sdb_acq[i];
59937c478bd9Sstevel@tonic-gate 		mutex_enter(&bucket->iacqf_lock);
59947c478bd9Sstevel@tonic-gate 		for (acqrec = bucket->iacqf_ipsacq; acqrec != NULL;
59957c478bd9Sstevel@tonic-gate 		    acqrec = acqrec->ipsacq_next) {
59967c478bd9Sstevel@tonic-gate 			if (samsg->sadb_msg_seq == acqrec->ipsacq_seq)
59977c478bd9Sstevel@tonic-gate 				break;	/* for acqrec... loop. */
59987c478bd9Sstevel@tonic-gate 		}
59997c478bd9Sstevel@tonic-gate 		if (acqrec != NULL)
60007c478bd9Sstevel@tonic-gate 			break;	/* for i = 0... loop. */
60017c478bd9Sstevel@tonic-gate 
60027c478bd9Sstevel@tonic-gate 		mutex_exit(&bucket->iacqf_lock);
6003fb87b5d2Ssommerfe 	}
60047c478bd9Sstevel@tonic-gate 
6005fb87b5d2Ssommerfe 	if (acqrec == NULL) {
6006fb87b5d2Ssommerfe 		for (i = 0; i < sp->s_v6.sdb_hashsize; i++) {
60077c478bd9Sstevel@tonic-gate 			bucket = &sp->s_v6.sdb_acq[i];
60087c478bd9Sstevel@tonic-gate 			mutex_enter(&bucket->iacqf_lock);
60097c478bd9Sstevel@tonic-gate 			for (acqrec = bucket->iacqf_ipsacq; acqrec != NULL;
60107c478bd9Sstevel@tonic-gate 			    acqrec = acqrec->ipsacq_next) {
60117c478bd9Sstevel@tonic-gate 				if (samsg->sadb_msg_seq == acqrec->ipsacq_seq)
60127c478bd9Sstevel@tonic-gate 					break;	/* for acqrec... loop. */
60137c478bd9Sstevel@tonic-gate 			}
60147c478bd9Sstevel@tonic-gate 			if (acqrec != NULL)
60157c478bd9Sstevel@tonic-gate 				break;	/* for i = 0... loop. */
60167c478bd9Sstevel@tonic-gate 
60177c478bd9Sstevel@tonic-gate 			mutex_exit(&bucket->iacqf_lock);
60187c478bd9Sstevel@tonic-gate 		}
6019fb87b5d2Ssommerfe 	}
6020fb87b5d2Ssommerfe 
60217c478bd9Sstevel@tonic-gate 
60227c478bd9Sstevel@tonic-gate 	if (acqrec == NULL)
60237c478bd9Sstevel@tonic-gate 		return;
60247c478bd9Sstevel@tonic-gate 
60257c478bd9Sstevel@tonic-gate 	/*
60267c478bd9Sstevel@tonic-gate 	 * What do I do with the errno and IP?	I may need mp's services a
60277c478bd9Sstevel@tonic-gate 	 * little more.	 See sadb_destroy_acquire() for future directions
60287c478bd9Sstevel@tonic-gate 	 * beyond free the mblk chain on the acquire record.
60297c478bd9Sstevel@tonic-gate 	 */
60307c478bd9Sstevel@tonic-gate 
60317c478bd9Sstevel@tonic-gate 	ASSERT(&bucket->iacqf_lock == acqrec->ipsacq_linklock);
6032f4b3ec61Sdh155122 	sadb_destroy_acquire(acqrec, ns);
60337c478bd9Sstevel@tonic-gate 	/* Have to exit mutex here, because of breaking out of for loop. */
60347c478bd9Sstevel@tonic-gate 	mutex_exit(&bucket->iacqf_lock);
60357c478bd9Sstevel@tonic-gate }
60367c478bd9Sstevel@tonic-gate 
60377c478bd9Sstevel@tonic-gate /*
60387c478bd9Sstevel@tonic-gate  * The following functions work with the replay windows of an SA.  They assume
60397c478bd9Sstevel@tonic-gate  * the ipsa->ipsa_replay_arr is an array of uint64_t, and that the bit vector
60407c478bd9Sstevel@tonic-gate  * represents the highest sequence number packet received, and back
60417c478bd9Sstevel@tonic-gate  * (ipsa->ipsa_replay_wsize) packets.
60427c478bd9Sstevel@tonic-gate  */
60437c478bd9Sstevel@tonic-gate 
60447c478bd9Sstevel@tonic-gate /*
60457c478bd9Sstevel@tonic-gate  * Is the replay bit set?
60467c478bd9Sstevel@tonic-gate  */
60477c478bd9Sstevel@tonic-gate static boolean_t
60487c478bd9Sstevel@tonic-gate ipsa_is_replay_set(ipsa_t *ipsa, uint32_t offset)
60497c478bd9Sstevel@tonic-gate {
60507c478bd9Sstevel@tonic-gate 	uint64_t bit = (uint64_t)1 << (uint64_t)(offset & 63);
60517c478bd9Sstevel@tonic-gate 
60527c478bd9Sstevel@tonic-gate 	return ((bit & ipsa->ipsa_replay_arr[offset >> 6]) ? B_TRUE : B_FALSE);
60537c478bd9Sstevel@tonic-gate }
60547c478bd9Sstevel@tonic-gate 
60557c478bd9Sstevel@tonic-gate /*
60567c478bd9Sstevel@tonic-gate  * Shift the bits of the replay window over.
60577c478bd9Sstevel@tonic-gate  */
60587c478bd9Sstevel@tonic-gate static void
60597c478bd9Sstevel@tonic-gate ipsa_shift_replay(ipsa_t *ipsa, uint32_t shift)
60607c478bd9Sstevel@tonic-gate {
60617c478bd9Sstevel@tonic-gate 	int i;
60627c478bd9Sstevel@tonic-gate 	int jump = ((shift - 1) >> 6) + 1;
60637c478bd9Sstevel@tonic-gate 
60647c478bd9Sstevel@tonic-gate 	if (shift == 0)
60657c478bd9Sstevel@tonic-gate 		return;
60667c478bd9Sstevel@tonic-gate 
60677c478bd9Sstevel@tonic-gate 	for (i = (ipsa->ipsa_replay_wsize - 1) >> 6; i >= 0; i--) {
60687c478bd9Sstevel@tonic-gate 		if (i + jump <= (ipsa->ipsa_replay_wsize - 1) >> 6) {
60697c478bd9Sstevel@tonic-gate 			ipsa->ipsa_replay_arr[i + jump] |=
60707c478bd9Sstevel@tonic-gate 			    ipsa->ipsa_replay_arr[i] >> (64 - (shift & 63));
60717c478bd9Sstevel@tonic-gate 		}
60727c478bd9Sstevel@tonic-gate 		ipsa->ipsa_replay_arr[i] <<= shift;
60737c478bd9Sstevel@tonic-gate 	}
60747c478bd9Sstevel@tonic-gate }
60757c478bd9Sstevel@tonic-gate 
60767c478bd9Sstevel@tonic-gate /*
60777c478bd9Sstevel@tonic-gate  * Set a bit in the bit vector.
60787c478bd9Sstevel@tonic-gate  */
60797c478bd9Sstevel@tonic-gate static void
60807c478bd9Sstevel@tonic-gate ipsa_set_replay(ipsa_t *ipsa, uint32_t offset)
60817c478bd9Sstevel@tonic-gate {
60827c478bd9Sstevel@tonic-gate 	uint64_t bit = (uint64_t)1 << (uint64_t)(offset & 63);
60837c478bd9Sstevel@tonic-gate 
60847c478bd9Sstevel@tonic-gate 	ipsa->ipsa_replay_arr[offset >> 6] |= bit;
60857c478bd9Sstevel@tonic-gate }
60867c478bd9Sstevel@tonic-gate 
60877c478bd9Sstevel@tonic-gate #define	SADB_MAX_REPLAY_VALUE 0xffffffff
60887c478bd9Sstevel@tonic-gate 
60897c478bd9Sstevel@tonic-gate /*
60907c478bd9Sstevel@tonic-gate  * Assume caller has NOT done ntohl() already on seq.  Check to see
60917c478bd9Sstevel@tonic-gate  * if replay sequence number "seq" has been seen already.
60927c478bd9Sstevel@tonic-gate  */
60937c478bd9Sstevel@tonic-gate boolean_t
60947c478bd9Sstevel@tonic-gate sadb_replay_check(ipsa_t *ipsa, uint32_t seq)
60957c478bd9Sstevel@tonic-gate {
60967c478bd9Sstevel@tonic-gate 	boolean_t rc;
60977c478bd9Sstevel@tonic-gate 	uint32_t diff;
60987c478bd9Sstevel@tonic-gate 
60997c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_replay_wsize == 0)
61007c478bd9Sstevel@tonic-gate 		return (B_TRUE);
61017c478bd9Sstevel@tonic-gate 
61027c478bd9Sstevel@tonic-gate 	/*
61037c478bd9Sstevel@tonic-gate 	 * NOTE:  I've already checked for 0 on the wire in sadb_replay_peek().
61047c478bd9Sstevel@tonic-gate 	 */
61057c478bd9Sstevel@tonic-gate 
61067c478bd9Sstevel@tonic-gate 	/* Convert sequence number into host order before holding the mutex. */
61077c478bd9Sstevel@tonic-gate 	seq = ntohl(seq);
61087c478bd9Sstevel@tonic-gate 
61097c478bd9Sstevel@tonic-gate 	mutex_enter(&ipsa->ipsa_lock);
61107c478bd9Sstevel@tonic-gate 
61117c478bd9Sstevel@tonic-gate 	/* Initialize inbound SA's ipsa_replay field to last one received. */
61127c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_replay == 0)
61137c478bd9Sstevel@tonic-gate 		ipsa->ipsa_replay = 1;
61147c478bd9Sstevel@tonic-gate 
61157c478bd9Sstevel@tonic-gate 	if (seq > ipsa->ipsa_replay) {
61167c478bd9Sstevel@tonic-gate 		/*
61177c478bd9Sstevel@tonic-gate 		 * I have received a new "highest value received".  Shift
61187c478bd9Sstevel@tonic-gate 		 * the replay window over.
61197c478bd9Sstevel@tonic-gate 		 */
61207c478bd9Sstevel@tonic-gate 		diff = seq - ipsa->ipsa_replay;
61217c478bd9Sstevel@tonic-gate 		if (diff < ipsa->ipsa_replay_wsize) {
61227c478bd9Sstevel@tonic-gate 			/* In replay window, shift bits over. */
61237c478bd9Sstevel@tonic-gate 			ipsa_shift_replay(ipsa, diff);
61247c478bd9Sstevel@tonic-gate 		} else {
61257c478bd9Sstevel@tonic-gate 			/* WAY FAR AHEAD, clear bits and start again. */
61267c478bd9Sstevel@tonic-gate 			bzero(ipsa->ipsa_replay_arr,
61277c478bd9Sstevel@tonic-gate 			    sizeof (ipsa->ipsa_replay_arr));
61287c478bd9Sstevel@tonic-gate 		}
61297c478bd9Sstevel@tonic-gate 		ipsa_set_replay(ipsa, 0);
61307c478bd9Sstevel@tonic-gate 		ipsa->ipsa_replay = seq;
61317c478bd9Sstevel@tonic-gate 		rc = B_TRUE;
61327c478bd9Sstevel@tonic-gate 		goto done;
61337c478bd9Sstevel@tonic-gate 	}
61347c478bd9Sstevel@tonic-gate 	diff = ipsa->ipsa_replay - seq;
61357c478bd9Sstevel@tonic-gate 	if (diff >= ipsa->ipsa_replay_wsize || ipsa_is_replay_set(ipsa, diff)) {
61367c478bd9Sstevel@tonic-gate 		rc = B_FALSE;
61377c478bd9Sstevel@tonic-gate 		goto done;
61387c478bd9Sstevel@tonic-gate 	}
61397c478bd9Sstevel@tonic-gate 	/* Set this packet as seen. */
61407c478bd9Sstevel@tonic-gate 	ipsa_set_replay(ipsa, diff);
61417c478bd9Sstevel@tonic-gate 
61427c478bd9Sstevel@tonic-gate 	rc = B_TRUE;
61437c478bd9Sstevel@tonic-gate done:
61447c478bd9Sstevel@tonic-gate 	mutex_exit(&ipsa->ipsa_lock);
61457c478bd9Sstevel@tonic-gate 	return (rc);
61467c478bd9Sstevel@tonic-gate }
61477c478bd9Sstevel@tonic-gate 
61487c478bd9Sstevel@tonic-gate /*
61497c478bd9Sstevel@tonic-gate  * "Peek" and see if we should even bother going through the effort of
61507c478bd9Sstevel@tonic-gate  * running an authentication check on the sequence number passed in.
61517c478bd9Sstevel@tonic-gate  * this takes into account packets that are below the replay window,
61527c478bd9Sstevel@tonic-gate  * and collisions with already replayed packets.  Return B_TRUE if it
61538810c16bSdanmcd  * is okay to proceed, B_FALSE if this packet should be dropped immediately.
61547c478bd9Sstevel@tonic-gate  * Assume same byte-ordering as sadb_replay_check.
61557c478bd9Sstevel@tonic-gate  */
61567c478bd9Sstevel@tonic-gate boolean_t
61577c478bd9Sstevel@tonic-gate sadb_replay_peek(ipsa_t *ipsa, uint32_t seq)
61587c478bd9Sstevel@tonic-gate {
61597c478bd9Sstevel@tonic-gate 	boolean_t rc = B_FALSE;
61607c478bd9Sstevel@tonic-gate 	uint32_t diff;
61617c478bd9Sstevel@tonic-gate 
61627c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_replay_wsize == 0)
61637c478bd9Sstevel@tonic-gate 		return (B_TRUE);
61647c478bd9Sstevel@tonic-gate 
61657c478bd9Sstevel@tonic-gate 	/*
61667c478bd9Sstevel@tonic-gate 	 * 0 is 0, regardless of byte order... :)
61677c478bd9Sstevel@tonic-gate 	 *
61687c478bd9Sstevel@tonic-gate 	 * If I get 0 on the wire (and there is a replay window) then the
61697c478bd9Sstevel@tonic-gate 	 * sender most likely wrapped.	This ipsa may need to be marked or
61707c478bd9Sstevel@tonic-gate 	 * something.
61717c478bd9Sstevel@tonic-gate 	 */
61727c478bd9Sstevel@tonic-gate 	if (seq == 0)
61737c478bd9Sstevel@tonic-gate 		return (B_FALSE);
61747c478bd9Sstevel@tonic-gate 
61757c478bd9Sstevel@tonic-gate 	seq = ntohl(seq);
61767c478bd9Sstevel@tonic-gate 	mutex_enter(&ipsa->ipsa_lock);
61777c478bd9Sstevel@tonic-gate 	if (seq < ipsa->ipsa_replay - ipsa->ipsa_replay_wsize &&
61787c478bd9Sstevel@tonic-gate 	    ipsa->ipsa_replay >= ipsa->ipsa_replay_wsize)
61797c478bd9Sstevel@tonic-gate 		goto done;
61807c478bd9Sstevel@tonic-gate 
61817c478bd9Sstevel@tonic-gate 	/*
61827c478bd9Sstevel@tonic-gate 	 * If I've hit 0xffffffff, then quite honestly, I don't need to
61837c478bd9Sstevel@tonic-gate 	 * bother with formalities.  I'm not accepting any more packets
61847c478bd9Sstevel@tonic-gate 	 * on this SA.
61857c478bd9Sstevel@tonic-gate 	 */
61867c478bd9Sstevel@tonic-gate 	if (ipsa->ipsa_replay == SADB_MAX_REPLAY_VALUE) {
61877c478bd9Sstevel@tonic-gate 		/*
61887c478bd9Sstevel@tonic-gate 		 * Since we're already holding the lock, update the
61897c478bd9Sstevel@tonic-gate 		 * expire time ala. sadb_replay_delete() and return.
61907c478bd9Sstevel@tonic-gate 		 */
61917c478bd9Sstevel@tonic-gate 		ipsa->ipsa_hardexpiretime = (time_t)1;
61927c478bd9Sstevel@tonic-gate 		goto done;
61937c478bd9Sstevel@tonic-gate 	}
61947c478bd9Sstevel@tonic-gate 
61957c478bd9Sstevel@tonic-gate 	if (seq <= ipsa->ipsa_replay) {
61967c478bd9Sstevel@tonic-gate 		/*
61977c478bd9Sstevel@tonic-gate 		 * This seq is in the replay window.  I'm not below it,
61987c478bd9Sstevel@tonic-gate 		 * because I already checked for that above!
61997c478bd9Sstevel@tonic-gate 		 */
62007c478bd9Sstevel@tonic-gate 		diff = ipsa->ipsa_replay - seq;
62017c478bd9Sstevel@tonic-gate 		if (ipsa_is_replay_set(ipsa, diff))
62027c478bd9Sstevel@tonic-gate 			goto done;
62037c478bd9Sstevel@tonic-gate 	}
62047c478bd9Sstevel@tonic-gate 	/* Else return B_TRUE, I'm going to advance the window. */
62057c478bd9Sstevel@tonic-gate 
62067c478bd9Sstevel@tonic-gate 	rc = B_TRUE;
62077c478bd9Sstevel@tonic-gate done:
62087c478bd9Sstevel@tonic-gate 	mutex_exit(&ipsa->ipsa_lock);
62097c478bd9Sstevel@tonic-gate 	return (rc);
62107c478bd9Sstevel@tonic-gate }
62117c478bd9Sstevel@tonic-gate 
62127c478bd9Sstevel@tonic-gate /*
62137c478bd9Sstevel@tonic-gate  * Delete a single SA.
62147c478bd9Sstevel@tonic-gate  *
62157c478bd9Sstevel@tonic-gate  * For now, use the quick-and-dirty trick of making the association's
62167c478bd9Sstevel@tonic-gate  * hard-expire lifetime (time_t)1, ensuring deletion by the *_ager().
62177c478bd9Sstevel@tonic-gate  */
62187c478bd9Sstevel@tonic-gate void
62197c478bd9Sstevel@tonic-gate sadb_replay_delete(ipsa_t *assoc)
62207c478bd9Sstevel@tonic-gate {
62217c478bd9Sstevel@tonic-gate 	mutex_enter(&assoc->ipsa_lock);
62227c478bd9Sstevel@tonic-gate 	assoc->ipsa_hardexpiretime = (time_t)1;
62237c478bd9Sstevel@tonic-gate 	mutex_exit(&assoc->ipsa_lock);
62247c478bd9Sstevel@tonic-gate }
62257c478bd9Sstevel@tonic-gate 
62267c478bd9Sstevel@tonic-gate /*
62277c478bd9Sstevel@tonic-gate  * Special front-end to ipsec_rl_strlog() dealing with SA failure.
62287c478bd9Sstevel@tonic-gate  * this is designed to take only a format string with "* %x * %s *", so
62297c478bd9Sstevel@tonic-gate  * that "spi" is printed first, then "addr" is converted using inet_pton().
62307c478bd9Sstevel@tonic-gate  *
62317c478bd9Sstevel@tonic-gate  * This is abstracted out to save the stack space for only when inet_pton()
62327c478bd9Sstevel@tonic-gate  * is called.  Make sure "spi" is in network order; it usually is when this
62337c478bd9Sstevel@tonic-gate  * would get called.
62347c478bd9Sstevel@tonic-gate  */
62357c478bd9Sstevel@tonic-gate void
62367c478bd9Sstevel@tonic-gate ipsec_assocfailure(short mid, short sid, char level, ushort_t sl, char *fmt,
6237f4b3ec61Sdh155122     uint32_t spi, void *addr, int af, netstack_t *ns)
62387c478bd9Sstevel@tonic-gate {
62397c478bd9Sstevel@tonic-gate 	char buf[INET6_ADDRSTRLEN];
62407c478bd9Sstevel@tonic-gate 
62417c478bd9Sstevel@tonic-gate 	ASSERT(af == AF_INET6 || af == AF_INET);
62427c478bd9Sstevel@tonic-gate 
6243f4b3ec61Sdh155122 	ipsec_rl_strlog(ns, mid, sid, level, sl, fmt, ntohl(spi),
62447c478bd9Sstevel@tonic-gate 	    inet_ntop(af, addr, buf, sizeof (buf)));
62457c478bd9Sstevel@tonic-gate }
62467c478bd9Sstevel@tonic-gate 
62477c478bd9Sstevel@tonic-gate /*
62487c478bd9Sstevel@tonic-gate  * Fills in a reference to the policy, if any, from the conn, in *ppp
62497c478bd9Sstevel@tonic-gate  */
62507c478bd9Sstevel@tonic-gate static void
62518810c16bSdanmcd ipsec_conn_pol(ipsec_selector_t *sel, conn_t *connp, ipsec_policy_t **ppp)
62527c478bd9Sstevel@tonic-gate {
62537c478bd9Sstevel@tonic-gate 	ipsec_policy_t	*pp;
62547c478bd9Sstevel@tonic-gate 	ipsec_latch_t	*ipl = connp->conn_latch;
62557c478bd9Sstevel@tonic-gate 
6256bd670b35SErik Nordmark 	if ((ipl != NULL) && (connp->conn_ixa->ixa_ipsec_policy != NULL)) {
6257bd670b35SErik Nordmark 		pp = connp->conn_ixa->ixa_ipsec_policy;
62587c478bd9Sstevel@tonic-gate 		IPPOL_REFHOLD(pp);
62597c478bd9Sstevel@tonic-gate 	} else {
6260bd670b35SErik Nordmark 		pp = ipsec_find_policy(IPSEC_TYPE_OUTBOUND, connp, sel,
6261f4b3ec61Sdh155122 		    connp->conn_netstack);
62627c478bd9Sstevel@tonic-gate 	}
62637c478bd9Sstevel@tonic-gate 	*ppp = pp;
62647c478bd9Sstevel@tonic-gate }
62657c478bd9Sstevel@tonic-gate 
62667c478bd9Sstevel@tonic-gate /*
62677c478bd9Sstevel@tonic-gate  * The following functions scan through active conn_t structures
62687c478bd9Sstevel@tonic-gate  * and return a reference to the best-matching policy it can find.
62697c478bd9Sstevel@tonic-gate  * Caller must release the reference.
62707c478bd9Sstevel@tonic-gate  */
62717c478bd9Sstevel@tonic-gate static void
6272f4b3ec61Sdh155122 ipsec_udp_pol(ipsec_selector_t *sel, ipsec_policy_t **ppp, ip_stack_t *ipst)
62737c478bd9Sstevel@tonic-gate {
62747c478bd9Sstevel@tonic-gate 	connf_t *connfp;
62757c478bd9Sstevel@tonic-gate 	conn_t *connp = NULL;
62767c478bd9Sstevel@tonic-gate 	ipsec_selector_t portonly;
62777c478bd9Sstevel@tonic-gate 
62787c478bd9Sstevel@tonic-gate 	bzero((void *)&portonly, sizeof (portonly));
62797c478bd9Sstevel@tonic-gate 
62807c478bd9Sstevel@tonic-gate 	if (sel->ips_local_port == 0)
62817c478bd9Sstevel@tonic-gate 		return;
62827c478bd9Sstevel@tonic-gate 
6283f4b3ec61Sdh155122 	connfp = &ipst->ips_ipcl_udp_fanout[IPCL_UDP_HASH(sel->ips_local_port,
6284f4b3ec61Sdh155122 	    ipst)];
62857c478bd9Sstevel@tonic-gate 	mutex_enter(&connfp->connf_lock);
62867c478bd9Sstevel@tonic-gate 
62877c478bd9Sstevel@tonic-gate 	if (sel->ips_isv4) {
62887c478bd9Sstevel@tonic-gate 		connp = connfp->connf_head;
62897c478bd9Sstevel@tonic-gate 		while (connp != NULL) {
62907c478bd9Sstevel@tonic-gate 			if (IPCL_UDP_MATCH(connp, sel->ips_local_port,
62917c478bd9Sstevel@tonic-gate 			    sel->ips_local_addr_v4, sel->ips_remote_port,
62927c478bd9Sstevel@tonic-gate 			    sel->ips_remote_addr_v4))
62937c478bd9Sstevel@tonic-gate 				break;
62947c478bd9Sstevel@tonic-gate 			connp = connp->conn_next;
62957c478bd9Sstevel@tonic-gate 		}
62967c478bd9Sstevel@tonic-gate 
62977c478bd9Sstevel@tonic-gate 		if (connp == NULL) {
62987c478bd9Sstevel@tonic-gate 			/* Try port-only match in IPv6. */
62997c478bd9Sstevel@tonic-gate 			portonly.ips_local_port = sel->ips_local_port;
63007c478bd9Sstevel@tonic-gate 			sel = &portonly;
63017c478bd9Sstevel@tonic-gate 		}
63027c478bd9Sstevel@tonic-gate 	}
63037c478bd9Sstevel@tonic-gate 
63047c478bd9Sstevel@tonic-gate 	if (connp == NULL) {
63057c478bd9Sstevel@tonic-gate 		connp = connfp->connf_head;
63067c478bd9Sstevel@tonic-gate 		while (connp != NULL) {
63077c478bd9Sstevel@tonic-gate 			if (IPCL_UDP_MATCH_V6(connp, sel->ips_local_port,
63087c478bd9Sstevel@tonic-gate 			    sel->ips_local_addr_v6, sel->ips_remote_port,
63097c478bd9Sstevel@tonic-gate 			    sel->ips_remote_addr_v6))
63107c478bd9Sstevel@tonic-gate 				break;
63117c478bd9Sstevel@tonic-gate 			connp = connp->conn_next;
63127c478bd9Sstevel@tonic-gate 		}
63137c478bd9Sstevel@tonic-gate 
63147c478bd9Sstevel@tonic-gate 		if (connp == NULL) {
63157c478bd9Sstevel@tonic-gate 			mutex_exit(&connfp->connf_lock);
63167c478bd9Sstevel@tonic-gate 			return;
63177c478bd9Sstevel@tonic-gate 		}
63187c478bd9Sstevel@tonic-gate 	}
63197c478bd9Sstevel@tonic-gate 
63207c478bd9Sstevel@tonic-gate 	CONN_INC_REF(connp);
63217c478bd9Sstevel@tonic-gate 	mutex_exit(&connfp->connf_lock);
63227c478bd9Sstevel@tonic-gate 
63238810c16bSdanmcd 	ipsec_conn_pol(sel, connp, ppp);
6324bd670b35SErik Nordmark 	CONN_DEC_REF(connp);
63257c478bd9Sstevel@tonic-gate }
63267c478bd9Sstevel@tonic-gate 
63277c478bd9Sstevel@tonic-gate static conn_t *
6328f4b3ec61Sdh155122 ipsec_find_listen_conn(uint16_t *pptr, ipsec_selector_t *sel, ip_stack_t *ipst)
63297c478bd9Sstevel@tonic-gate {
63307c478bd9Sstevel@tonic-gate 	connf_t *connfp;
63317c478bd9Sstevel@tonic-gate 	conn_t *connp = NULL;
63327c478bd9Sstevel@tonic-gate 	const in6_addr_t *v6addrmatch = &sel->ips_local_addr_v6;
63337c478bd9Sstevel@tonic-gate 
63347c478bd9Sstevel@tonic-gate 	if (sel->ips_local_port == 0)
63357c478bd9Sstevel@tonic-gate 		return (NULL);
63367c478bd9Sstevel@tonic-gate 
6337437220cdSdanmcd 	connfp = &ipst->ips_ipcl_bind_fanout[
6338437220cdSdanmcd 	    IPCL_BIND_HASH(sel->ips_local_port, ipst)];
63397c478bd9Sstevel@tonic-gate 	mutex_enter(&connfp->connf_lock);
63407c478bd9Sstevel@tonic-gate 
63417c478bd9Sstevel@tonic-gate 	if (sel->ips_isv4) {
63427c478bd9Sstevel@tonic-gate 		connp = connfp->connf_head;
63437c478bd9Sstevel@tonic-gate 		while (connp != NULL) {
63447c478bd9Sstevel@tonic-gate 			if (IPCL_BIND_MATCH(connp, IPPROTO_TCP,
63457c478bd9Sstevel@tonic-gate 			    sel->ips_local_addr_v4, pptr[1]))
63467c478bd9Sstevel@tonic-gate 				break;
63477c478bd9Sstevel@tonic-gate 			connp = connp->conn_next;
63487c478bd9Sstevel@tonic-gate 		}
63497c478bd9Sstevel@tonic-gate 
63507c478bd9Sstevel@tonic-gate 		if (connp == NULL) {
63517c478bd9Sstevel@tonic-gate 			/* Match to all-zeroes. */
63527c478bd9Sstevel@tonic-gate 			v6addrmatch = &ipv6_all_zeros;
63537c478bd9Sstevel@tonic-gate 		}
63547c478bd9Sstevel@tonic-gate 	}
63557c478bd9Sstevel@tonic-gate 
63567c478bd9Sstevel@tonic-gate 	if (connp == NULL) {
63577c478bd9Sstevel@tonic-gate 		connp = connfp->connf_head;
63587c478bd9Sstevel@tonic-gate 		while (connp != NULL) {
63597c478bd9Sstevel@tonic-gate 			if (IPCL_BIND_MATCH_V6(connp, IPPROTO_TCP,
63607c478bd9Sstevel@tonic-gate 			    *v6addrmatch, pptr[1]))
63617c478bd9Sstevel@tonic-gate 				break;
63627c478bd9Sstevel@tonic-gate 			connp = connp->conn_next;
63637c478bd9Sstevel@tonic-gate 		}
63647c478bd9Sstevel@tonic-gate 
63657c478bd9Sstevel@tonic-gate 		if (connp == NULL) {
63667c478bd9Sstevel@tonic-gate 			mutex_exit(&connfp->connf_lock);
63677c478bd9Sstevel@tonic-gate 			return (NULL);
63687c478bd9Sstevel@tonic-gate 		}
63697c478bd9Sstevel@tonic-gate 	}
63707c478bd9Sstevel@tonic-gate 
63717c478bd9Sstevel@tonic-gate 	CONN_INC_REF(connp);
63727c478bd9Sstevel@tonic-gate 	mutex_exit(&connfp->connf_lock);
63737c478bd9Sstevel@tonic-gate 	return (connp);
63747c478bd9Sstevel@tonic-gate }
63757c478bd9Sstevel@tonic-gate 
63767c478bd9Sstevel@tonic-gate static void
6377f4b3ec61Sdh155122 ipsec_tcp_pol(ipsec_selector_t *sel, ipsec_policy_t **ppp, ip_stack_t *ipst)
63787c478bd9Sstevel@tonic-gate {
63797c478bd9Sstevel@tonic-gate 	connf_t 	*connfp;
63807c478bd9Sstevel@tonic-gate 	conn_t		*connp;
63817c478bd9Sstevel@tonic-gate 	uint32_t	ports;
63827c478bd9Sstevel@tonic-gate 	uint16_t	*pptr = (uint16_t *)&ports;
63837c478bd9Sstevel@tonic-gate 
63847c478bd9Sstevel@tonic-gate 	/*
63857c478bd9Sstevel@tonic-gate 	 * Find TCP state in the following order:
63867c478bd9Sstevel@tonic-gate 	 * 1.) Connected conns.
63877c478bd9Sstevel@tonic-gate 	 * 2.) Listeners.
63887c478bd9Sstevel@tonic-gate 	 *
63897c478bd9Sstevel@tonic-gate 	 * Even though #2 will be the common case for inbound traffic, only
63907c478bd9Sstevel@tonic-gate 	 * following this order insures correctness.
63917c478bd9Sstevel@tonic-gate 	 */
63927c478bd9Sstevel@tonic-gate 
63937c478bd9Sstevel@tonic-gate 	if (sel->ips_local_port == 0)
63947c478bd9Sstevel@tonic-gate 		return;
63957c478bd9Sstevel@tonic-gate 
63967c478bd9Sstevel@tonic-gate 	/*
63977c478bd9Sstevel@tonic-gate 	 * 0 should be fport, 1 should be lport.  SRC is the local one here.
63987c478bd9Sstevel@tonic-gate 	 * See ipsec_construct_inverse_acquire() for details.
63997c478bd9Sstevel@tonic-gate 	 */
64007c478bd9Sstevel@tonic-gate 	pptr[0] = sel->ips_remote_port;
64017c478bd9Sstevel@tonic-gate 	pptr[1] = sel->ips_local_port;
64027c478bd9Sstevel@tonic-gate 
6403f4b3ec61Sdh155122 	connfp = &ipst->ips_ipcl_conn_fanout[
6404f4b3ec61Sdh155122 	    IPCL_CONN_HASH(sel->ips_remote_addr_v4, ports, ipst)];
64057c478bd9Sstevel@tonic-gate 	mutex_enter(&connfp->connf_lock);
64067c478bd9Sstevel@tonic-gate 	connp = connfp->connf_head;
64077c478bd9Sstevel@tonic-gate 
64087c478bd9Sstevel@tonic-gate 	if (sel->ips_isv4) {
64097c478bd9Sstevel@tonic-gate 		while (connp != NULL) {
64107c478bd9Sstevel@tonic-gate 			if (IPCL_CONN_MATCH(connp, IPPROTO_TCP,
64117c478bd9Sstevel@tonic-gate 			    sel->ips_remote_addr_v4, sel->ips_local_addr_v4,
64127c478bd9Sstevel@tonic-gate 			    ports))
64137c478bd9Sstevel@tonic-gate 				break;
64147c478bd9Sstevel@tonic-gate 			connp = connp->conn_next;
64157c478bd9Sstevel@tonic-gate 		}
64167c478bd9Sstevel@tonic-gate 	} else {
64177c478bd9Sstevel@tonic-gate 		while (connp != NULL) {
64187c478bd9Sstevel@tonic-gate 			if (IPCL_CONN_MATCH_V6(connp, IPPROTO_TCP,
64197c478bd9Sstevel@tonic-gate 			    sel->ips_remote_addr_v6, sel->ips_local_addr_v6,
64207c478bd9Sstevel@tonic-gate 			    ports))
64217c478bd9Sstevel@tonic-gate 				break;
64227c478bd9Sstevel@tonic-gate 			connp = connp->conn_next;
64237c478bd9Sstevel@tonic-gate 		}
64247c478bd9Sstevel@tonic-gate 	}
64257c478bd9Sstevel@tonic-gate 
64267c478bd9Sstevel@tonic-gate 	if (connp != NULL) {
64277c478bd9Sstevel@tonic-gate 		CONN_INC_REF(connp);
64287c478bd9Sstevel@tonic-gate 		mutex_exit(&connfp->connf_lock);
64297c478bd9Sstevel@tonic-gate 	} else {
64307c478bd9Sstevel@tonic-gate 		mutex_exit(&connfp->connf_lock);
64317c478bd9Sstevel@tonic-gate 
64327c478bd9Sstevel@tonic-gate 		/* Try the listen hash. */
6433f4b3ec61Sdh155122 		if ((connp = ipsec_find_listen_conn(pptr, sel, ipst)) == NULL)
64347c478bd9Sstevel@tonic-gate 			return;
64357c478bd9Sstevel@tonic-gate 	}
64367c478bd9Sstevel@tonic-gate 
64378810c16bSdanmcd 	ipsec_conn_pol(sel, connp, ppp);
6438bd670b35SErik Nordmark 	CONN_DEC_REF(connp);
64397c478bd9Sstevel@tonic-gate }
64407c478bd9Sstevel@tonic-gate 
64417c478bd9Sstevel@tonic-gate static void
6442f4b3ec61Sdh155122 ipsec_sctp_pol(ipsec_selector_t *sel, ipsec_policy_t **ppp,
6443f4b3ec61Sdh155122     ip_stack_t *ipst)
64447c478bd9Sstevel@tonic-gate {
64457c478bd9Sstevel@tonic-gate 	conn_t		*connp;
64467c478bd9Sstevel@tonic-gate 	uint32_t	ports;
64477c478bd9Sstevel@tonic-gate 	uint16_t	*pptr = (uint16_t *)&ports;
64487c478bd9Sstevel@tonic-gate 
64497c478bd9Sstevel@tonic-gate 	/*
64507c478bd9Sstevel@tonic-gate 	 * Find SCP state in the following order:
64517c478bd9Sstevel@tonic-gate 	 * 1.) Connected conns.
64527c478bd9Sstevel@tonic-gate 	 * 2.) Listeners.
64537c478bd9Sstevel@tonic-gate 	 *
64547c478bd9Sstevel@tonic-gate 	 * Even though #2 will be the common case for inbound traffic, only
64557c478bd9Sstevel@tonic-gate 	 * following this order insures correctness.
64567c478bd9Sstevel@tonic-gate 	 */
64577c478bd9Sstevel@tonic-gate 
64587c478bd9Sstevel@tonic-gate 	if (sel->ips_local_port == 0)
64597c478bd9Sstevel@tonic-gate 		return;
64607c478bd9Sstevel@tonic-gate 
64617c478bd9Sstevel@tonic-gate 	/*
64627c478bd9Sstevel@tonic-gate 	 * 0 should be fport, 1 should be lport.  SRC is the local one here.
64637c478bd9Sstevel@tonic-gate 	 * See ipsec_construct_inverse_acquire() for details.
64647c478bd9Sstevel@tonic-gate 	 */
64657c478bd9Sstevel@tonic-gate 	pptr[0] = sel->ips_remote_port;
64667c478bd9Sstevel@tonic-gate 	pptr[1] = sel->ips_local_port;
64677c478bd9Sstevel@tonic-gate 
6468bd670b35SErik Nordmark 	/*
6469bd670b35SErik Nordmark 	 * For labeled systems, there's no need to check the
6470bd670b35SErik Nordmark 	 * label here.  It's known to be good as we checked
6471bd670b35SErik Nordmark 	 * before allowing the connection to become bound.
6472bd670b35SErik Nordmark 	 */
64737c478bd9Sstevel@tonic-gate 	if (sel->ips_isv4) {
64747c478bd9Sstevel@tonic-gate 		in6_addr_t	src, dst;
64757c478bd9Sstevel@tonic-gate 
64767c478bd9Sstevel@tonic-gate 		IN6_IPADDR_TO_V4MAPPED(sel->ips_remote_addr_v4, &dst);
64777c478bd9Sstevel@tonic-gate 		IN6_IPADDR_TO_V4MAPPED(sel->ips_local_addr_v4, &src);
6478e35d2278Svi117747 		connp = sctp_find_conn(&dst, &src, ports, ALL_ZONES,
6479bd670b35SErik Nordmark 		    0, ipst->ips_netstack->netstack_sctp);
64807c478bd9Sstevel@tonic-gate 	} else {
64817c478bd9Sstevel@tonic-gate 		connp = sctp_find_conn(&sel->ips_remote_addr_v6,
6482e35d2278Svi117747 		    &sel->ips_local_addr_v6, ports, ALL_ZONES,
6483bd670b35SErik Nordmark 		    0, ipst->ips_netstack->netstack_sctp);
64847c478bd9Sstevel@tonic-gate 	}
64857c478bd9Sstevel@tonic-gate 	if (connp == NULL)
64867c478bd9Sstevel@tonic-gate 		return;
64878810c16bSdanmcd 	ipsec_conn_pol(sel, connp, ppp);
6488bd670b35SErik Nordmark 	CONN_DEC_REF(connp);
64898810c16bSdanmcd }
64908810c16bSdanmcd 
64918810c16bSdanmcd /*
64928810c16bSdanmcd  * Fill in a query for the SPD (in "sel") using two PF_KEY address extensions.
64938810c16bSdanmcd  * Returns 0 or errno, and always sets *diagnostic to something appropriate
64948810c16bSdanmcd  * to PF_KEY.
64958810c16bSdanmcd  *
64968810c16bSdanmcd  * NOTE:  For right now, this function (and ipsec_selector_t for that matter),
64978810c16bSdanmcd  * ignore prefix lengths in the address extension.  Since we match on first-
64988810c16bSdanmcd  * entered policies, this shouldn't matter.  Also, since we normalize prefix-
64998810c16bSdanmcd  * set addresses to mask out the lower bits, we should get a suitable search
65008810c16bSdanmcd  * key for the SPD anyway.  This is the function to change if the assumption
65018810c16bSdanmcd  * about suitable search keys is wrong.
65028810c16bSdanmcd  */
65038810c16bSdanmcd static int
65048810c16bSdanmcd ipsec_get_inverse_acquire_sel(ipsec_selector_t *sel, sadb_address_t *srcext,
65058810c16bSdanmcd     sadb_address_t *dstext, int *diagnostic)
65068810c16bSdanmcd {
65078810c16bSdanmcd 	struct sockaddr_in *src, *dst;
65088810c16bSdanmcd 	struct sockaddr_in6 *src6, *dst6;
65098810c16bSdanmcd 
65108810c16bSdanmcd 	*diagnostic = 0;
65118810c16bSdanmcd 
65128810c16bSdanmcd 	bzero(sel, sizeof (*sel));
65138810c16bSdanmcd 	sel->ips_protocol = srcext->sadb_address_proto;
65148810c16bSdanmcd 	dst = (struct sockaddr_in *)(dstext + 1);
65158810c16bSdanmcd 	if (dst->sin_family == AF_INET6) {
65168810c16bSdanmcd 		dst6 = (struct sockaddr_in6 *)dst;
65178810c16bSdanmcd 		src6 = (struct sockaddr_in6 *)(srcext + 1);
65188810c16bSdanmcd 		if (src6->sin6_family != AF_INET6) {
65198810c16bSdanmcd 			*diagnostic = SADB_X_DIAGNOSTIC_AF_MISMATCH;
65208810c16bSdanmcd 			return (EINVAL);
65218810c16bSdanmcd 		}
65228810c16bSdanmcd 		sel->ips_remote_addr_v6 = dst6->sin6_addr;
65238810c16bSdanmcd 		sel->ips_local_addr_v6 = src6->sin6_addr;
65248810c16bSdanmcd 		if (sel->ips_protocol == IPPROTO_ICMPV6) {
65258810c16bSdanmcd 			sel->ips_is_icmp_inv_acq = 1;
65268810c16bSdanmcd 		} else {
65278810c16bSdanmcd 			sel->ips_remote_port = dst6->sin6_port;
65288810c16bSdanmcd 			sel->ips_local_port = src6->sin6_port;
65298810c16bSdanmcd 		}
65308810c16bSdanmcd 		sel->ips_isv4 = B_FALSE;
65318810c16bSdanmcd 	} else {
65328810c16bSdanmcd 		src = (struct sockaddr_in *)(srcext + 1);
65338810c16bSdanmcd 		if (src->sin_family != AF_INET) {
65348810c16bSdanmcd 			*diagnostic = SADB_X_DIAGNOSTIC_AF_MISMATCH;
65358810c16bSdanmcd 			return (EINVAL);
65368810c16bSdanmcd 		}
65378810c16bSdanmcd 		sel->ips_remote_addr_v4 = dst->sin_addr.s_addr;
65388810c16bSdanmcd 		sel->ips_local_addr_v4 = src->sin_addr.s_addr;
65398810c16bSdanmcd 		if (sel->ips_protocol == IPPROTO_ICMP) {
65408810c16bSdanmcd 			sel->ips_is_icmp_inv_acq = 1;
65418810c16bSdanmcd 		} else {
65428810c16bSdanmcd 			sel->ips_remote_port = dst->sin_port;
65438810c16bSdanmcd 			sel->ips_local_port = src->sin_port;
65448810c16bSdanmcd 		}
65458810c16bSdanmcd 		sel->ips_isv4 = B_TRUE;
65468810c16bSdanmcd 	}
65478810c16bSdanmcd 	return (0);
65488810c16bSdanmcd }
65498810c16bSdanmcd 
65508810c16bSdanmcd /*
65518810c16bSdanmcd  * We have encapsulation.
65528810c16bSdanmcd  * - Lookup tun_t by address and look for an associated
65538810c16bSdanmcd  *   tunnel policy
65548810c16bSdanmcd  * - If there are inner selectors
65558810c16bSdanmcd  *   - check ITPF_P_TUNNEL and ITPF_P_ACTIVE
65568810c16bSdanmcd  *   - Look up tunnel policy based on selectors
65578810c16bSdanmcd  * - Else
65588810c16bSdanmcd  *   - Sanity check the negotation
65598810c16bSdanmcd  *   - If appropriate, fall through to global policy
65608810c16bSdanmcd  */
65618810c16bSdanmcd static int
65628810c16bSdanmcd ipsec_tun_pol(ipsec_selector_t *sel, ipsec_policy_t **ppp,
65638810c16bSdanmcd     sadb_address_t *innsrcext, sadb_address_t *inndstext, ipsec_tun_pol_t *itp,
6564bd670b35SErik Nordmark     int *diagnostic)
65658810c16bSdanmcd {
65668810c16bSdanmcd 	int err;
65678810c16bSdanmcd 	ipsec_policy_head_t *polhead;
65688810c16bSdanmcd 
6569051c7f8aSDan McDonald 	*diagnostic = 0;
6570051c7f8aSDan McDonald 
65718810c16bSdanmcd 	/* Check for inner selectors and act appropriately */
65728810c16bSdanmcd 
65738810c16bSdanmcd 	if (innsrcext != NULL) {
65748810c16bSdanmcd 		/* Inner selectors present */
65758810c16bSdanmcd 		ASSERT(inndstext != NULL);
65768810c16bSdanmcd 		if ((itp == NULL) ||
65778810c16bSdanmcd 		    (itp->itp_flags & (ITPF_P_ACTIVE | ITPF_P_TUNNEL)) !=
65788810c16bSdanmcd 		    (ITPF_P_ACTIVE | ITPF_P_TUNNEL)) {
65798810c16bSdanmcd 			/*
65808810c16bSdanmcd 			 * If inner packet selectors, we must have negotiate
65818810c16bSdanmcd 			 * tunnel and active policy.  If the tunnel has
65828810c16bSdanmcd 			 * transport-mode policy set on it, or has no policy,
65838810c16bSdanmcd 			 * fail.
65848810c16bSdanmcd 			 */
65858810c16bSdanmcd 			return (ENOENT);
65868810c16bSdanmcd 		} else {
65878810c16bSdanmcd 			/*
65888810c16bSdanmcd 			 * Reset "sel" to indicate inner selectors.  Pass
65898810c16bSdanmcd 			 * inner PF_KEY address extensions for this to happen.
65908810c16bSdanmcd 			 */
65912b24ab6bSSebastien Roy 			if ((err = ipsec_get_inverse_acquire_sel(sel,
65922b24ab6bSSebastien Roy 			    innsrcext, inndstext, diagnostic)) != 0)
65938810c16bSdanmcd 				return (err);
65948810c16bSdanmcd 			/*
65958810c16bSdanmcd 			 * Now look for a tunnel policy based on those inner
65968810c16bSdanmcd 			 * selectors.  (Common code is below.)
65978810c16bSdanmcd 			 */
65988810c16bSdanmcd 		}
65998810c16bSdanmcd 	} else {
66008810c16bSdanmcd 		/* No inner selectors present */
66018810c16bSdanmcd 		if ((itp == NULL) || !(itp->itp_flags & ITPF_P_ACTIVE)) {
66028810c16bSdanmcd 			/*
66038810c16bSdanmcd 			 * Transport mode negotiation with no tunnel policy
66048810c16bSdanmcd 			 * configured - return to indicate a global policy
66058810c16bSdanmcd 			 * check is needed.
66068810c16bSdanmcd 			 */
66078810c16bSdanmcd 			return (0);
66088810c16bSdanmcd 		} else if (itp->itp_flags & ITPF_P_TUNNEL) {
66098810c16bSdanmcd 			/* Tunnel mode set with no inner selectors. */
66108810c16bSdanmcd 			return (ENOENT);
66118810c16bSdanmcd 		}
66128810c16bSdanmcd 		/*
66138810c16bSdanmcd 		 * Else, this is a tunnel policy configured with ifconfig(1m)
66148810c16bSdanmcd 		 * or "negotiate transport" with ipsecconf(1m).  We have an
66158810c16bSdanmcd 		 * itp with policy set based on any match, so don't bother
66168810c16bSdanmcd 		 * changing fields in "sel".
66178810c16bSdanmcd 		 */
66188810c16bSdanmcd 	}
66198810c16bSdanmcd 
66208810c16bSdanmcd 	ASSERT(itp != NULL);
66218810c16bSdanmcd 	polhead = itp->itp_policy;
66228810c16bSdanmcd 	ASSERT(polhead != NULL);
66238810c16bSdanmcd 	rw_enter(&polhead->iph_lock, RW_READER);
6624bd670b35SErik Nordmark 	*ppp = ipsec_find_policy_head(NULL, polhead, IPSEC_TYPE_INBOUND, sel);
66258810c16bSdanmcd 	rw_exit(&polhead->iph_lock);
66268810c16bSdanmcd 
66278810c16bSdanmcd 	/*
66288810c16bSdanmcd 	 * Don't default to global if we didn't find a matching policy entry.
66298810c16bSdanmcd 	 * Instead, send ENOENT, just like if we hit a transport-mode tunnel.
66308810c16bSdanmcd 	 */
66318810c16bSdanmcd 	if (*ppp == NULL)
66328810c16bSdanmcd 		return (ENOENT);
66338810c16bSdanmcd 
66348810c16bSdanmcd 	return (0);
66357c478bd9Sstevel@tonic-gate }
66367c478bd9Sstevel@tonic-gate 
6637bd670b35SErik Nordmark /*
6638bd670b35SErik Nordmark  * For sctp conn_faddr is the primary address, hence this is of limited
6639bd670b35SErik Nordmark  * use for sctp.
6640bd670b35SErik Nordmark  */
66417c478bd9Sstevel@tonic-gate static void
6642f4b3ec61Sdh155122 ipsec_oth_pol(ipsec_selector_t *sel, ipsec_policy_t **ppp,
6643f4b3ec61Sdh155122     ip_stack_t *ipst)
66447c478bd9Sstevel@tonic-gate {
66457c478bd9Sstevel@tonic-gate 	boolean_t	isv4 = sel->ips_isv4;
66467c478bd9Sstevel@tonic-gate 	connf_t		*connfp;
66477c478bd9Sstevel@tonic-gate 	conn_t		*connp;
66487c478bd9Sstevel@tonic-gate 
66497c478bd9Sstevel@tonic-gate 	if (isv4) {
6650bd670b35SErik Nordmark 		connfp = &ipst->ips_ipcl_proto_fanout_v4[sel->ips_protocol];
66517c478bd9Sstevel@tonic-gate 	} else {
6652f4b3ec61Sdh155122 		connfp = &ipst->ips_ipcl_proto_fanout_v6[sel->ips_protocol];
66537c478bd9Sstevel@tonic-gate 	}
66547c478bd9Sstevel@tonic-gate 
66557c478bd9Sstevel@tonic-gate 	mutex_enter(&connfp->connf_lock);
66567c478bd9Sstevel@tonic-gate 	for (connp = connfp->connf_head; connp != NULL;
66577c478bd9Sstevel@tonic-gate 	    connp = connp->conn_next) {
6658bd670b35SErik Nordmark 		if (isv4) {
6659bd670b35SErik Nordmark 			if ((connp->conn_laddr_v4 == INADDR_ANY ||
6660bd670b35SErik Nordmark 			    connp->conn_laddr_v4 == sel->ips_local_addr_v4) &&
6661bd670b35SErik Nordmark 			    (connp->conn_faddr_v4 == INADDR_ANY ||
6662bd670b35SErik Nordmark 			    connp->conn_faddr_v4 == sel->ips_remote_addr_v4))
6663bd670b35SErik Nordmark 				break;
6664bd670b35SErik Nordmark 		} else {
6665bd670b35SErik Nordmark 			if ((IN6_IS_ADDR_UNSPECIFIED(&connp->conn_laddr_v6) ||
6666bd670b35SErik Nordmark 			    IN6_ARE_ADDR_EQUAL(&connp->conn_laddr_v6,
66677c478bd9Sstevel@tonic-gate 			    &sel->ips_local_addr_v6)) &&
6668bd670b35SErik Nordmark 			    (IN6_IS_ADDR_UNSPECIFIED(&connp->conn_faddr_v6) ||
6669bd670b35SErik Nordmark 			    IN6_ARE_ADDR_EQUAL(&connp->conn_faddr_v6,
6670bd670b35SErik Nordmark 			    &sel->ips_remote_addr_v6)))
66717c478bd9Sstevel@tonic-gate 				break;
66727c478bd9Sstevel@tonic-gate 		}
66737c478bd9Sstevel@tonic-gate 	}
66747c478bd9Sstevel@tonic-gate 	if (connp == NULL) {
66757c478bd9Sstevel@tonic-gate 		mutex_exit(&connfp->connf_lock);
66767c478bd9Sstevel@tonic-gate 		return;
66777c478bd9Sstevel@tonic-gate 	}
66787c478bd9Sstevel@tonic-gate 
66797c478bd9Sstevel@tonic-gate 	CONN_INC_REF(connp);
66807c478bd9Sstevel@tonic-gate 	mutex_exit(&connfp->connf_lock);
66817c478bd9Sstevel@tonic-gate 
66828810c16bSdanmcd 	ipsec_conn_pol(sel, connp, ppp);
6683bd670b35SErik Nordmark 	CONN_DEC_REF(connp);
66847c478bd9Sstevel@tonic-gate }
66857c478bd9Sstevel@tonic-gate 
66867c478bd9Sstevel@tonic-gate /*
66877c478bd9Sstevel@tonic-gate  * Construct an inverse ACQUIRE reply based on:
66887c478bd9Sstevel@tonic-gate  *
66897c478bd9Sstevel@tonic-gate  * 1.) Current global policy.
66907c478bd9Sstevel@tonic-gate  * 2.) An conn_t match depending on what all was passed in the extv[].
66918810c16bSdanmcd  * 3.) A tunnel's policy head.
66927c478bd9Sstevel@tonic-gate  * ...
66937c478bd9Sstevel@tonic-gate  * N.) Other stuff TBD (e.g. identities)
66947c478bd9Sstevel@tonic-gate  *
66957c478bd9Sstevel@tonic-gate  * If there is an error, set sadb_msg_errno and sadb_x_msg_diagnostic
66967c478bd9Sstevel@tonic-gate  * in this function so the caller can extract them where appropriately.
66977c478bd9Sstevel@tonic-gate  *
66987c478bd9Sstevel@tonic-gate  * The SRC address is the local one - just like an outbound ACQUIRE message.
66995d3b8cb7SBill Sommerfeld  *
67005d3b8cb7SBill Sommerfeld  * XXX MLS: key management supplies a label which we just reflect back up
67015d3b8cb7SBill Sommerfeld  * again.  clearly we need to involve the label in the rest of the checks.
67027c478bd9Sstevel@tonic-gate  */
67037c478bd9Sstevel@tonic-gate mblk_t *
6704f4b3ec61Sdh155122 ipsec_construct_inverse_acquire(sadb_msg_t *samsg, sadb_ext_t *extv[],
6705f4b3ec61Sdh155122     netstack_t *ns)
67067c478bd9Sstevel@tonic-gate {
67077c478bd9Sstevel@tonic-gate 	int err;
67087c478bd9Sstevel@tonic-gate 	int diagnostic;
67097c478bd9Sstevel@tonic-gate 	sadb_address_t *srcext = (sadb_address_t *)extv[SADB_EXT_ADDRESS_SRC],
67108810c16bSdanmcd 	    *dstext = (sadb_address_t *)extv[SADB_EXT_ADDRESS_DST],
67118810c16bSdanmcd 	    *innsrcext = (sadb_address_t *)extv[SADB_X_EXT_ADDRESS_INNER_SRC],
67128810c16bSdanmcd 	    *inndstext = (sadb_address_t *)extv[SADB_X_EXT_ADDRESS_INNER_DST];
67135d3b8cb7SBill Sommerfeld 	sadb_sens_t *sens = (sadb_sens_t *)extv[SADB_EXT_SENSITIVITY];
67148810c16bSdanmcd 	struct sockaddr_in6 *src, *dst;
67158810c16bSdanmcd 	struct sockaddr_in6 *isrc, *idst;
67168810c16bSdanmcd 	ipsec_tun_pol_t *itp = NULL;
67178810c16bSdanmcd 	ipsec_policy_t *pp = NULL;
67188810c16bSdanmcd 	ipsec_selector_t sel, isel;
6719051c7f8aSDan McDonald 	mblk_t *retmp = NULL;
6720f4b3ec61Sdh155122 	ip_stack_t	*ipst = ns->netstack_ip;
67217c478bd9Sstevel@tonic-gate 
67225d3b8cb7SBill Sommerfeld 
67238810c16bSdanmcd 	/* Normalize addresses */
6724f4b3ec61Sdh155122 	if (sadb_addrcheck(NULL, (mblk_t *)samsg, (sadb_ext_t *)srcext, 0, ns)
6725f4b3ec61Sdh155122 	    == KS_IN_ADDR_UNKNOWN) {
67267c478bd9Sstevel@tonic-gate 		err = EINVAL;
67278810c16bSdanmcd 		diagnostic = SADB_X_DIAGNOSTIC_BAD_SRC;
67287c478bd9Sstevel@tonic-gate 		goto bail;
67297c478bd9Sstevel@tonic-gate 	}
67308810c16bSdanmcd 	src = (struct sockaddr_in6 *)(srcext + 1);
6731f4b3ec61Sdh155122 	if (sadb_addrcheck(NULL, (mblk_t *)samsg, (sadb_ext_t *)dstext, 0, ns)
6732f4b3ec61Sdh155122 	    == KS_IN_ADDR_UNKNOWN) {
67337c478bd9Sstevel@tonic-gate 		err = EINVAL;
67348810c16bSdanmcd 		diagnostic = SADB_X_DIAGNOSTIC_BAD_DST;
67357c478bd9Sstevel@tonic-gate 		goto bail;
67367c478bd9Sstevel@tonic-gate 	}
67378810c16bSdanmcd 	dst = (struct sockaddr_in6 *)(dstext + 1);
67388810c16bSdanmcd 	if (src->sin6_family != dst->sin6_family) {
67398810c16bSdanmcd 		err = EINVAL;
67408810c16bSdanmcd 		diagnostic = SADB_X_DIAGNOSTIC_AF_MISMATCH;
67418810c16bSdanmcd 		goto bail;
67427c478bd9Sstevel@tonic-gate 	}
67438810c16bSdanmcd 
67448810c16bSdanmcd 	/* Check for tunnel mode and act appropriately */
67458810c16bSdanmcd 	if (innsrcext != NULL) {
67468810c16bSdanmcd 		if (inndstext == NULL) {
67478810c16bSdanmcd 			err = EINVAL;
67488810c16bSdanmcd 			diagnostic = SADB_X_DIAGNOSTIC_MISSING_INNER_DST;
67498810c16bSdanmcd 			goto bail;
67508810c16bSdanmcd 		}
67518810c16bSdanmcd 		if (sadb_addrcheck(NULL, (mblk_t *)samsg,
6752f4b3ec61Sdh155122 		    (sadb_ext_t *)innsrcext, 0, ns) == KS_IN_ADDR_UNKNOWN) {
67538810c16bSdanmcd 			err = EINVAL;
67548810c16bSdanmcd 			diagnostic = SADB_X_DIAGNOSTIC_MALFORMED_INNER_SRC;
67558810c16bSdanmcd 			goto bail;
67568810c16bSdanmcd 		}
67578810c16bSdanmcd 		isrc = (struct sockaddr_in6 *)(innsrcext + 1);
67588810c16bSdanmcd 		if (sadb_addrcheck(NULL, (mblk_t *)samsg,
6759f4b3ec61Sdh155122 		    (sadb_ext_t *)inndstext, 0, ns) == KS_IN_ADDR_UNKNOWN) {
67608810c16bSdanmcd 			err = EINVAL;
67618810c16bSdanmcd 			diagnostic = SADB_X_DIAGNOSTIC_MALFORMED_INNER_DST;
67628810c16bSdanmcd 			goto bail;
67638810c16bSdanmcd 		}
67648810c16bSdanmcd 		idst = (struct sockaddr_in6 *)(inndstext + 1);
67658810c16bSdanmcd 		if (isrc->sin6_family != idst->sin6_family) {
67668810c16bSdanmcd 			err = EINVAL;
67678810c16bSdanmcd 			diagnostic = SADB_X_DIAGNOSTIC_INNER_AF_MISMATCH;
67688810c16bSdanmcd 			goto bail;
67698810c16bSdanmcd 		}
67708810c16bSdanmcd 		if (isrc->sin6_family != AF_INET &&
67718810c16bSdanmcd 		    isrc->sin6_family != AF_INET6) {
67728810c16bSdanmcd 			err = EINVAL;
67738810c16bSdanmcd 			diagnostic = SADB_X_DIAGNOSTIC_BAD_INNER_SRC_AF;
67748810c16bSdanmcd 			goto bail;
67758810c16bSdanmcd 		}
67768810c16bSdanmcd 	} else if (inndstext != NULL) {
67778810c16bSdanmcd 		err = EINVAL;
67788810c16bSdanmcd 		diagnostic = SADB_X_DIAGNOSTIC_MISSING_INNER_SRC;
67798810c16bSdanmcd 		goto bail;
67808810c16bSdanmcd 	}
67818810c16bSdanmcd 
67828810c16bSdanmcd 	/* Get selectors first, based on outer addresses */
67838810c16bSdanmcd 	err = ipsec_get_inverse_acquire_sel(&sel, srcext, dstext, &diagnostic);
67848810c16bSdanmcd 	if (err != 0)
67858810c16bSdanmcd 		goto bail;
67868810c16bSdanmcd 
67878810c16bSdanmcd 	/* Check for tunnel mode mismatches. */
67888810c16bSdanmcd 	if (innsrcext != NULL &&
67898810c16bSdanmcd 	    ((isrc->sin6_family == AF_INET &&
67908810c16bSdanmcd 	    sel.ips_protocol != IPPROTO_ENCAP && sel.ips_protocol != 0) ||
67918810c16bSdanmcd 	    (isrc->sin6_family == AF_INET6 &&
6792437220cdSdanmcd 	    sel.ips_protocol != IPPROTO_IPV6 && sel.ips_protocol != 0))) {
67938810c16bSdanmcd 		err = EPROTOTYPE;
67948810c16bSdanmcd 		goto bail;
67957c478bd9Sstevel@tonic-gate 	}
67967c478bd9Sstevel@tonic-gate 
67977c478bd9Sstevel@tonic-gate 	/*
67987c478bd9Sstevel@tonic-gate 	 * Okay, we have the addresses and other selector information.
67997c478bd9Sstevel@tonic-gate 	 * Let's first find a conn...
68007c478bd9Sstevel@tonic-gate 	 */
68018810c16bSdanmcd 	pp = NULL;
68027c478bd9Sstevel@tonic-gate 	switch (sel.ips_protocol) {
68037c478bd9Sstevel@tonic-gate 	case IPPROTO_TCP:
6804f4b3ec61Sdh155122 		ipsec_tcp_pol(&sel, &pp, ipst);
68057c478bd9Sstevel@tonic-gate 		break;
68067c478bd9Sstevel@tonic-gate 	case IPPROTO_UDP:
6807f4b3ec61Sdh155122 		ipsec_udp_pol(&sel, &pp, ipst);
68087c478bd9Sstevel@tonic-gate 		break;
68097c478bd9Sstevel@tonic-gate 	case IPPROTO_SCTP:
6810f4b3ec61Sdh155122 		ipsec_sctp_pol(&sel, &pp, ipst);
68118810c16bSdanmcd 		break;
68128810c16bSdanmcd 	case IPPROTO_ENCAP:
68138810c16bSdanmcd 	case IPPROTO_IPV6:
68148810c16bSdanmcd 		/*
68158810c16bSdanmcd 		 * Assume sel.ips_remote_addr_* has the right address at
68168810c16bSdanmcd 		 * that exact position.
68178810c16bSdanmcd 		 */
68182b24ab6bSSebastien Roy 		itp = itp_get_byaddr((uint32_t *)(&sel.ips_local_addr_v6),
68192b24ab6bSSebastien Roy 		    (uint32_t *)(&sel.ips_remote_addr_v6), src->sin6_family,
68202b24ab6bSSebastien Roy 		    ipst);
68212b24ab6bSSebastien Roy 
68228810c16bSdanmcd 		if (innsrcext == NULL) {
68238810c16bSdanmcd 			/*
68248810c16bSdanmcd 			 * Transport-mode tunnel, make sure we fake out isel
68258810c16bSdanmcd 			 * to contain something based on the outer protocol.
68268810c16bSdanmcd 			 */
68278810c16bSdanmcd 			bzero(&isel, sizeof (isel));
68288810c16bSdanmcd 			isel.ips_isv4 = (sel.ips_protocol == IPPROTO_ENCAP);
68298810c16bSdanmcd 		} /* Else isel is initialized by ipsec_tun_pol(). */
68308810c16bSdanmcd 		err = ipsec_tun_pol(&isel, &pp, innsrcext, inndstext, itp,
6831bd670b35SErik Nordmark 		    &diagnostic);
68328810c16bSdanmcd 		/*
68338810c16bSdanmcd 		 * NOTE:  isel isn't used for now, but in RFC 430x IPsec, it
68348810c16bSdanmcd 		 * may be.
68358810c16bSdanmcd 		 */
68368810c16bSdanmcd 		if (err != 0)
68378810c16bSdanmcd 			goto bail;
68387c478bd9Sstevel@tonic-gate 		break;
68397c478bd9Sstevel@tonic-gate 	default:
6840f4b3ec61Sdh155122 		ipsec_oth_pol(&sel, &pp, ipst);
68417c478bd9Sstevel@tonic-gate 		break;
68427c478bd9Sstevel@tonic-gate 	}
68437c478bd9Sstevel@tonic-gate 
68447c478bd9Sstevel@tonic-gate 	/*
68458810c16bSdanmcd 	 * If we didn't find a matching conn_t or other policy head, take a
68468810c16bSdanmcd 	 * look in the global policy.
68477c478bd9Sstevel@tonic-gate 	 */
68488810c16bSdanmcd 	if (pp == NULL) {
6849bd670b35SErik Nordmark 		pp = ipsec_find_policy(IPSEC_TYPE_OUTBOUND, NULL, &sel, ns);
68507c478bd9Sstevel@tonic-gate 		if (pp == NULL) {
68517c478bd9Sstevel@tonic-gate 			/* There's no global policy. */
68527c478bd9Sstevel@tonic-gate 			err = ENOENT;
68537c478bd9Sstevel@tonic-gate 			diagnostic = 0;
68547c478bd9Sstevel@tonic-gate 			goto bail;
68557c478bd9Sstevel@tonic-gate 		}
68567c478bd9Sstevel@tonic-gate 	}
68577c478bd9Sstevel@tonic-gate 
68587c478bd9Sstevel@tonic-gate 	/*
68597c478bd9Sstevel@tonic-gate 	 * Now that we have a policy entry/widget, construct an ACQUIRE
68607c478bd9Sstevel@tonic-gate 	 * message based on that, fix fields where appropriate,
68617c478bd9Sstevel@tonic-gate 	 * and return the message.
68627c478bd9Sstevel@tonic-gate 	 */
68638810c16bSdanmcd 	retmp = sadb_extended_acquire(&sel, pp, NULL,
68648810c16bSdanmcd 	    (itp != NULL && (itp->itp_flags & ITPF_P_TUNNEL)),
68655d3b8cb7SBill Sommerfeld 	    samsg->sadb_msg_seq, samsg->sadb_msg_pid, sens, ns);
68667c478bd9Sstevel@tonic-gate 	if (pp != NULL) {
6867bd670b35SErik Nordmark 		IPPOL_REFRELE(pp);
68687c478bd9Sstevel@tonic-gate 	}
6869051c7f8aSDan McDonald 	ASSERT(err == 0 && diagnostic == 0);
6870051c7f8aSDan McDonald 	if (retmp == NULL)
6871051c7f8aSDan McDonald 		err = ENOMEM;
6872051c7f8aSDan McDonald bail:
68732b24ab6bSSebastien Roy 	if (itp != NULL) {
68742b24ab6bSSebastien Roy 		ITP_REFRELE(itp, ns);
68752b24ab6bSSebastien Roy 	}
68767c478bd9Sstevel@tonic-gate 	samsg->sadb_msg_errno = (uint8_t)err;
68777c478bd9Sstevel@tonic-gate 	samsg->sadb_x_msg_diagnostic = (uint16_t)diagnostic;
6878051c7f8aSDan McDonald 	return (retmp);
68797c478bd9Sstevel@tonic-gate }
68807c478bd9Sstevel@tonic-gate 
68817c478bd9Sstevel@tonic-gate /*
6882a14de6c8SDan McDonald  * ipsa_lpkt is a one-element queue, only manipulated by the next two
6883a14de6c8SDan McDonald  * functions.  They have to hold the ipsa_lock because of potential races
6884a14de6c8SDan McDonald  * between key management using SADB_UPDATE, and inbound packets that may
6885a14de6c8SDan McDonald  * queue up on the larval SA (hence the 'l' in "lpkt").
68867c478bd9Sstevel@tonic-gate  */
68877c478bd9Sstevel@tonic-gate 
68887c478bd9Sstevel@tonic-gate /*
6889930af642SDan McDonald  * sadb_set_lpkt:
6890930af642SDan McDonald  *
6891930af642SDan McDonald  * Returns the passed-in packet if the SA is no longer larval.
6892930af642SDan McDonald  *
6893930af642SDan McDonald  * Returns NULL if the SA is larval, and needs to be swapped into the SA for
6894930af642SDan McDonald  * processing after an SADB_UPDATE.
68957c478bd9Sstevel@tonic-gate  */
6896930af642SDan McDonald mblk_t *
6897bd670b35SErik Nordmark sadb_set_lpkt(ipsa_t *ipsa, mblk_t *npkt, ip_recv_attr_t *ira)
68987c478bd9Sstevel@tonic-gate {
68997c478bd9Sstevel@tonic-gate 	mblk_t		*opkt;
69007c478bd9Sstevel@tonic-gate 
6901a14de6c8SDan McDonald 	mutex_enter(&ipsa->ipsa_lock);
6902930af642SDan McDonald 	opkt = ipsa->ipsa_lpkt;
6903930af642SDan McDonald 	if (ipsa->ipsa_state == IPSA_STATE_LARVAL) {
6904930af642SDan McDonald 		/*
6905930af642SDan McDonald 		 * Consume npkt and place it in the LARVAL SA's inbound
6906930af642SDan McDonald 		 * packet slot.
6907930af642SDan McDonald 		 */
6908bd670b35SErik Nordmark 		mblk_t	*attrmp;
6909bd670b35SErik Nordmark 
6910bd670b35SErik Nordmark 		attrmp = ip_recv_attr_to_mblk(ira);
6911bd670b35SErik Nordmark 		if (attrmp == NULL) {
6912bd670b35SErik Nordmark 			ill_t *ill = ira->ira_ill;
6913bd670b35SErik Nordmark 
6914bd670b35SErik Nordmark 			BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
6915bd670b35SErik Nordmark 			ip_drop_input("ipIfStatsInDiscards", npkt, ill);
6916bd670b35SErik Nordmark 			freemsg(npkt);
6917bd670b35SErik Nordmark 			opkt = NULL;
6918bd670b35SErik Nordmark 		} else {
6919bd670b35SErik Nordmark 			ASSERT(attrmp->b_cont == NULL);
6920bd670b35SErik Nordmark 			attrmp->b_cont = npkt;
6921930af642SDan McDonald 			ipsa->ipsa_lpkt = attrmp;
6922bd670b35SErik Nordmark 		}
6923930af642SDan McDonald 		npkt = NULL;
6924a14de6c8SDan McDonald 	} else {
6925930af642SDan McDonald 		/*
6926930af642SDan McDonald 		 * If not larval, we lost the race.  NOTE: ipsa_lpkt may still
6927930af642SDan McDonald 		 * have been non-NULL in the non-larval case, because of
6928930af642SDan McDonald 		 * inbound packets arriving prior to sadb_common_add()
6929930af642SDan McDonald 		 * transferring the SA completely out of larval state, but
6930930af642SDan McDonald 		 * after lpkt was grabbed by the AH/ESP-specific add routines.
6931930af642SDan McDonald 		 * We should clear the old ipsa_lpkt in this case to make sure
6932930af642SDan McDonald 		 * that it doesn't linger on the now-MATURE IPsec SA, or get
6933930af642SDan McDonald 		 * picked up as an out-of-order packet.
6934930af642SDan McDonald 		 */
6935930af642SDan McDonald 		ipsa->ipsa_lpkt = NULL;
6936a14de6c8SDan McDonald 	}
6937a14de6c8SDan McDonald 	mutex_exit(&ipsa->ipsa_lock);
69387c478bd9Sstevel@tonic-gate 
6939bd670b35SErik Nordmark 	if (opkt != NULL) {
6940930af642SDan McDonald 		ipsec_stack_t	*ipss;
6941930af642SDan McDonald 
6942930af642SDan McDonald 		ipss = ira->ira_ill->ill_ipst->ips_netstack->netstack_ipsec;
6943bd670b35SErik Nordmark 		opkt = ip_recv_attr_free_mblk(opkt);
6944bd670b35SErik Nordmark 		ip_drop_packet(opkt, B_TRUE, ira->ira_ill,
6945f4b3ec61Sdh155122 		    DROPPER(ipss, ipds_sadb_inlarval_replace),
6946f4b3ec61Sdh155122 		    &ipss->ipsec_sadb_dropper);
6947bd670b35SErik Nordmark 	}
6948930af642SDan McDonald 	return (npkt);
69497c478bd9Sstevel@tonic-gate }
69507c478bd9Sstevel@tonic-gate 
69517c478bd9Sstevel@tonic-gate /*
69527c478bd9Sstevel@tonic-gate  * sadb_clear_lpkt: Atomically clear ipsa->ipsa_lpkt and return the
69537c478bd9Sstevel@tonic-gate  * previous value.
69547c478bd9Sstevel@tonic-gate  */
69557c478bd9Sstevel@tonic-gate mblk_t *
69567c478bd9Sstevel@tonic-gate sadb_clear_lpkt(ipsa_t *ipsa)
69577c478bd9Sstevel@tonic-gate {
69587c478bd9Sstevel@tonic-gate 	mblk_t *opkt;
69597c478bd9Sstevel@tonic-gate 
6960a14de6c8SDan McDonald 	mutex_enter(&ipsa->ipsa_lock);
69617c478bd9Sstevel@tonic-gate 	opkt = ipsa->ipsa_lpkt;
6962a14de6c8SDan McDonald 	ipsa->ipsa_lpkt = NULL;
6963a14de6c8SDan McDonald 	mutex_exit(&ipsa->ipsa_lock);
69647c478bd9Sstevel@tonic-gate 	return (opkt);
69657c478bd9Sstevel@tonic-gate }
69667c478bd9Sstevel@tonic-gate 
6967d74f5ecaSDan McDonald /*
6968d74f5ecaSDan McDonald  * Buffer a packet that's in IDLE state as set by Solaris Clustering.
6969d74f5ecaSDan McDonald  */
69709c2c14abSThejaswini Singarajipura void
6971bd670b35SErik Nordmark sadb_buf_pkt(ipsa_t *ipsa, mblk_t *bpkt, ip_recv_attr_t *ira)
69729c2c14abSThejaswini Singarajipura {
6973bd670b35SErik Nordmark 	netstack_t	*ns = ira->ira_ill->ill_ipst->ips_netstack;
69749c2c14abSThejaswini Singarajipura 	ipsec_stack_t   *ipss = ns->netstack_ipsec;
6975d74f5ecaSDan McDonald 	in6_addr_t *srcaddr = (in6_addr_t *)(&ipsa->ipsa_srcaddr);
6976d74f5ecaSDan McDonald 	in6_addr_t *dstaddr = (in6_addr_t *)(&ipsa->ipsa_dstaddr);
6977bd670b35SErik Nordmark 	mblk_t		*mp;
69789c2c14abSThejaswini Singarajipura 
69799c2c14abSThejaswini Singarajipura 	ASSERT(ipsa->ipsa_state == IPSA_STATE_IDLE);
6980d74f5ecaSDan McDonald 
6981d74f5ecaSDan McDonald 	if (cl_inet_idlesa == NULL) {
6982bd670b35SErik Nordmark 		ip_drop_packet(bpkt, B_TRUE, ira->ira_ill,
6983d74f5ecaSDan McDonald 		    DROPPER(ipss, ipds_sadb_inidle_overflow),
6984d74f5ecaSDan McDonald 		    &ipss->ipsec_sadb_dropper);
6985d74f5ecaSDan McDonald 		return;
6986d74f5ecaSDan McDonald 	}
6987d74f5ecaSDan McDonald 
69888e4b770fSLu Huafeng 	cl_inet_idlesa(ns->netstack_stackid,
69898e4b770fSLu Huafeng 	    (ipsa->ipsa_type == SADB_SATYPE_AH) ? IPPROTO_AH : IPPROTO_ESP,
69908e4b770fSLu Huafeng 	    ipsa->ipsa_spi, ipsa->ipsa_addrfam, *srcaddr, *dstaddr, NULL);
6991d74f5ecaSDan McDonald 
6992bd670b35SErik Nordmark 	mp = ip_recv_attr_to_mblk(ira);
6993bd670b35SErik Nordmark 	if (mp == NULL) {
6994bd670b35SErik Nordmark 		ip_drop_packet(bpkt, B_TRUE, ira->ira_ill,
6995bd670b35SErik Nordmark 		    DROPPER(ipss, ipds_sadb_inidle_overflow),
6996bd670b35SErik Nordmark 		    &ipss->ipsec_sadb_dropper);
6997bd670b35SErik Nordmark 		return;
6998bd670b35SErik Nordmark 	}
6999bd670b35SErik Nordmark 	linkb(mp, bpkt);
700073184bc7SDan McDonald 
70019c2c14abSThejaswini Singarajipura 	mutex_enter(&ipsa->ipsa_lock);
70029c2c14abSThejaswini Singarajipura 	ipsa->ipsa_mblkcnt++;
70039c2c14abSThejaswini Singarajipura 	if (ipsa->ipsa_bpkt_head == NULL) {
70049c2c14abSThejaswini Singarajipura 		ipsa->ipsa_bpkt_head = ipsa->ipsa_bpkt_tail = bpkt;
70059c2c14abSThejaswini Singarajipura 	} else {
70069c2c14abSThejaswini Singarajipura 		ipsa->ipsa_bpkt_tail->b_next = bpkt;
70079c2c14abSThejaswini Singarajipura 		ipsa->ipsa_bpkt_tail = bpkt;
70089c2c14abSThejaswini Singarajipura 		if (ipsa->ipsa_mblkcnt > SADB_MAX_IDLEPKTS) {
70099c2c14abSThejaswini Singarajipura 			mblk_t *tmp;
7010bd670b35SErik Nordmark 
70119c2c14abSThejaswini Singarajipura 			tmp = ipsa->ipsa_bpkt_head;
70129c2c14abSThejaswini Singarajipura 			ipsa->ipsa_bpkt_head = ipsa->ipsa_bpkt_head->b_next;
7013bd670b35SErik Nordmark 			tmp = ip_recv_attr_free_mblk(tmp);
7014bd670b35SErik Nordmark 			ip_drop_packet(tmp, B_TRUE, NULL,
70159c2c14abSThejaswini Singarajipura 			    DROPPER(ipss, ipds_sadb_inidle_overflow),
70169c2c14abSThejaswini Singarajipura 			    &ipss->ipsec_sadb_dropper);
70179c2c14abSThejaswini Singarajipura 			ipsa->ipsa_mblkcnt --;
70189c2c14abSThejaswini Singarajipura 		}
70199c2c14abSThejaswini Singarajipura 	}
70209c2c14abSThejaswini Singarajipura 	mutex_exit(&ipsa->ipsa_lock);
70219c2c14abSThejaswini Singarajipura }
70229c2c14abSThejaswini Singarajipura 
70239c2c14abSThejaswini Singarajipura /*
70249c2c14abSThejaswini Singarajipura  * Stub function that taskq_dispatch() invokes to take the mblk (in arg)
70259c2c14abSThejaswini Singarajipura  * and put into STREAMS again.
70269c2c14abSThejaswini Singarajipura  */
70279c2c14abSThejaswini Singarajipura void
70289c2c14abSThejaswini Singarajipura sadb_clear_buf_pkt(void *ipkt)
70299c2c14abSThejaswini Singarajipura {
70309c2c14abSThejaswini Singarajipura 	mblk_t	*tmp, *buf_pkt;
7031bd670b35SErik Nordmark 	ip_recv_attr_t	iras;
70329c2c14abSThejaswini Singarajipura 
70339c2c14abSThejaswini Singarajipura 	buf_pkt = (mblk_t *)ipkt;
70349c2c14abSThejaswini Singarajipura 
70359c2c14abSThejaswini Singarajipura 	while (buf_pkt != NULL) {
7036bd670b35SErik Nordmark 		mblk_t *data_mp;
7037bd670b35SErik Nordmark 
70389c2c14abSThejaswini Singarajipura 		tmp = buf_pkt->b_next;
70399c2c14abSThejaswini Singarajipura 		buf_pkt->b_next = NULL;
7040bd670b35SErik Nordmark 
7041bd670b35SErik Nordmark 		data_mp = buf_pkt->b_cont;
7042bd670b35SErik Nordmark 		buf_pkt->b_cont = NULL;
7043bd670b35SErik Nordmark 		if (!ip_recv_attr_from_mblk(buf_pkt, &iras)) {
7044bd670b35SErik Nordmark 			/* The ill or ip_stack_t disappeared on us. */
7045bd670b35SErik Nordmark 			ip_drop_input("ip_recv_attr_from_mblk", data_mp, NULL);
7046bd670b35SErik Nordmark 			freemsg(data_mp);
7047bd670b35SErik Nordmark 		} else {
7048bd670b35SErik Nordmark 			ip_input_post_ipsec(data_mp, &iras);
7049bd670b35SErik Nordmark 		}
7050bd670b35SErik Nordmark 		ira_cleanup(&iras, B_TRUE);
70519c2c14abSThejaswini Singarajipura 		buf_pkt = tmp;
70529c2c14abSThejaswini Singarajipura 	}
70539c2c14abSThejaswini Singarajipura }
70547c478bd9Sstevel@tonic-gate /*
70557c478bd9Sstevel@tonic-gate  * Walker callback used by sadb_alg_update() to free/create crypto
70567c478bd9Sstevel@tonic-gate  * context template when a crypto software provider is removed or
70577c478bd9Sstevel@tonic-gate  * added.
70587c478bd9Sstevel@tonic-gate  */
70597c478bd9Sstevel@tonic-gate 
70607c478bd9Sstevel@tonic-gate struct sadb_update_alg_state {
70617c478bd9Sstevel@tonic-gate 	ipsec_algtype_t alg_type;
70627c478bd9Sstevel@tonic-gate 	uint8_t alg_id;
70637c478bd9Sstevel@tonic-gate 	boolean_t is_added;
7064bd670b35SErik Nordmark 	boolean_t async_auth;
7065bd670b35SErik Nordmark 	boolean_t async_encr;
70667c478bd9Sstevel@tonic-gate };
70677c478bd9Sstevel@tonic-gate 
70687c478bd9Sstevel@tonic-gate static void
70697c478bd9Sstevel@tonic-gate sadb_alg_update_cb(isaf_t *head, ipsa_t *entry, void *cookie)
70707c478bd9Sstevel@tonic-gate {
70717c478bd9Sstevel@tonic-gate 	struct sadb_update_alg_state *update_state =
70727c478bd9Sstevel@tonic-gate 	    (struct sadb_update_alg_state *)cookie;
70737c478bd9Sstevel@tonic-gate 	crypto_ctx_template_t *ctx_tmpl = NULL;
70747c478bd9Sstevel@tonic-gate 
70757c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&head->isaf_lock));
70767c478bd9Sstevel@tonic-gate 
70777c478bd9Sstevel@tonic-gate 	if (entry->ipsa_state == IPSA_STATE_LARVAL)
70787c478bd9Sstevel@tonic-gate 		return;
70797c478bd9Sstevel@tonic-gate 
70807c478bd9Sstevel@tonic-gate 	mutex_enter(&entry->ipsa_lock);
70817c478bd9Sstevel@tonic-gate 
7082bd670b35SErik Nordmark 	if ((entry->ipsa_encr_alg != SADB_EALG_NONE && entry->ipsa_encr_alg !=
7083bd670b35SErik Nordmark 	    SADB_EALG_NULL && update_state->async_encr) ||
7084bd670b35SErik Nordmark 	    (entry->ipsa_auth_alg != SADB_AALG_NONE &&
7085bd670b35SErik Nordmark 	    update_state->async_auth)) {
7086bd670b35SErik Nordmark 		entry->ipsa_flags |= IPSA_F_ASYNC;
7087bd670b35SErik Nordmark 	} else {
7088bd670b35SErik Nordmark 		entry->ipsa_flags &= ~IPSA_F_ASYNC;
7089bd670b35SErik Nordmark 	}
7090bd670b35SErik Nordmark 
70917c478bd9Sstevel@tonic-gate 	switch (update_state->alg_type) {
70927c478bd9Sstevel@tonic-gate 	case IPSEC_ALG_AUTH:
70937c478bd9Sstevel@tonic-gate 		if (entry->ipsa_auth_alg == update_state->alg_id)
70947c478bd9Sstevel@tonic-gate 			ctx_tmpl = &entry->ipsa_authtmpl;
70957c478bd9Sstevel@tonic-gate 		break;
70967c478bd9Sstevel@tonic-gate 	case IPSEC_ALG_ENCR:
70977c478bd9Sstevel@tonic-gate 		if (entry->ipsa_encr_alg == update_state->alg_id)
70987c478bd9Sstevel@tonic-gate 			ctx_tmpl = &entry->ipsa_encrtmpl;
70997c478bd9Sstevel@tonic-gate 		break;
71007c478bd9Sstevel@tonic-gate 	default:
71017c478bd9Sstevel@tonic-gate 		ctx_tmpl = NULL;
71027c478bd9Sstevel@tonic-gate 	}
71037c478bd9Sstevel@tonic-gate 
71047c478bd9Sstevel@tonic-gate 	if (ctx_tmpl == NULL) {
71057c478bd9Sstevel@tonic-gate 		mutex_exit(&entry->ipsa_lock);
71067c478bd9Sstevel@tonic-gate 		return;
71077c478bd9Sstevel@tonic-gate 	}
71087c478bd9Sstevel@tonic-gate 
71097c478bd9Sstevel@tonic-gate 	/*
71107c478bd9Sstevel@tonic-gate 	 * The context template of the SA may be affected by the change
71117c478bd9Sstevel@tonic-gate 	 * of crypto provider.
71127c478bd9Sstevel@tonic-gate 	 */
71137c478bd9Sstevel@tonic-gate 	if (update_state->is_added) {
71147c478bd9Sstevel@tonic-gate 		/* create the context template if not already done */
71157c478bd9Sstevel@tonic-gate 		if (*ctx_tmpl == NULL) {
71167c478bd9Sstevel@tonic-gate 			(void) ipsec_create_ctx_tmpl(entry,
71177c478bd9Sstevel@tonic-gate 			    update_state->alg_type);
71187c478bd9Sstevel@tonic-gate 		}
71197c478bd9Sstevel@tonic-gate 	} else {
71207c478bd9Sstevel@tonic-gate 		/*
71217c478bd9Sstevel@tonic-gate 		 * The crypto provider was removed. If the context template
71227c478bd9Sstevel@tonic-gate 		 * exists but it is no longer valid, free it.
71237c478bd9Sstevel@tonic-gate 		 */
71247c478bd9Sstevel@tonic-gate 		if (*ctx_tmpl != NULL)
71257c478bd9Sstevel@tonic-gate 			ipsec_destroy_ctx_tmpl(entry, update_state->alg_type);
71267c478bd9Sstevel@tonic-gate 	}
71277c478bd9Sstevel@tonic-gate 
71287c478bd9Sstevel@tonic-gate 	mutex_exit(&entry->ipsa_lock);
71297c478bd9Sstevel@tonic-gate }
71307c478bd9Sstevel@tonic-gate 
71317c478bd9Sstevel@tonic-gate /*
7132bd670b35SErik Nordmark  * Invoked by IP when an software crypto provider has been updated, or if
7133bd670b35SErik Nordmark  * the crypto synchrony changes.  The type and id of the corresponding
7134bd670b35SErik Nordmark  * algorithm is passed as argument.  The type is set to ALL in the case of
7135bd670b35SErik Nordmark  * a synchrony change.
7136bd670b35SErik Nordmark  *
71377c478bd9Sstevel@tonic-gate  * is_added is B_TRUE if the provider was added, B_FALSE if it was
71387c478bd9Sstevel@tonic-gate  * removed. The function updates the SADB and free/creates the
71397c478bd9Sstevel@tonic-gate  * context templates associated with SAs if needed.
71407c478bd9Sstevel@tonic-gate  */
71417c478bd9Sstevel@tonic-gate 
7142fb87b5d2Ssommerfe #define	SADB_ALG_UPDATE_WALK(sadb, table) \
7143fb87b5d2Ssommerfe     sadb_walker((sadb).table, (sadb).sdb_hashsize, sadb_alg_update_cb, \
7144fb87b5d2Ssommerfe 	&update_state)
71457c478bd9Sstevel@tonic-gate 
71467c478bd9Sstevel@tonic-gate void
7147f4b3ec61Sdh155122 sadb_alg_update(ipsec_algtype_t alg_type, uint8_t alg_id, boolean_t is_added,
7148f4b3ec61Sdh155122     netstack_t *ns)
71497c478bd9Sstevel@tonic-gate {
71507c478bd9Sstevel@tonic-gate 	struct sadb_update_alg_state update_state;
7151f4b3ec61Sdh155122 	ipsecah_stack_t	*ahstack = ns->netstack_ipsecah;
7152f4b3ec61Sdh155122 	ipsecesp_stack_t	*espstack = ns->netstack_ipsecesp;
7153bd670b35SErik Nordmark 	ipsec_stack_t *ipss = ns->netstack_ipsec;
71547c478bd9Sstevel@tonic-gate 
71557c478bd9Sstevel@tonic-gate 	update_state.alg_type = alg_type;
71567c478bd9Sstevel@tonic-gate 	update_state.alg_id = alg_id;
71577c478bd9Sstevel@tonic-gate 	update_state.is_added = is_added;
7158bd670b35SErik Nordmark 	update_state.async_auth = ipss->ipsec_algs_exec_mode[IPSEC_ALG_AUTH] ==
7159bd670b35SErik Nordmark 	    IPSEC_ALGS_EXEC_ASYNC;
7160bd670b35SErik Nordmark 	update_state.async_encr = ipss->ipsec_algs_exec_mode[IPSEC_ALG_ENCR] ==
7161bd670b35SErik Nordmark 	    IPSEC_ALGS_EXEC_ASYNC;
71627c478bd9Sstevel@tonic-gate 
7163bd670b35SErik Nordmark 	if (alg_type == IPSEC_ALG_AUTH || alg_type == IPSEC_ALG_ALL) {
71647c478bd9Sstevel@tonic-gate 		/* walk the AH tables only for auth. algorithm changes */
7165f4b3ec61Sdh155122 		SADB_ALG_UPDATE_WALK(ahstack->ah_sadb.s_v4, sdb_of);
7166f4b3ec61Sdh155122 		SADB_ALG_UPDATE_WALK(ahstack->ah_sadb.s_v4, sdb_if);
7167f4b3ec61Sdh155122 		SADB_ALG_UPDATE_WALK(ahstack->ah_sadb.s_v6, sdb_of);
7168f4b3ec61Sdh155122 		SADB_ALG_UPDATE_WALK(ahstack->ah_sadb.s_v6, sdb_if);
71697c478bd9Sstevel@tonic-gate 	}
71707c478bd9Sstevel@tonic-gate 
71717c478bd9Sstevel@tonic-gate 	/* walk the ESP tables */
7172f4b3ec61Sdh155122 	SADB_ALG_UPDATE_WALK(espstack->esp_sadb.s_v4, sdb_of);
7173f4b3ec61Sdh155122 	SADB_ALG_UPDATE_WALK(espstack->esp_sadb.s_v4, sdb_if);
7174f4b3ec61Sdh155122 	SADB_ALG_UPDATE_WALK(espstack->esp_sadb.s_v6, sdb_of);
7175f4b3ec61Sdh155122 	SADB_ALG_UPDATE_WALK(espstack->esp_sadb.s_v6, sdb_if);
71767c478bd9Sstevel@tonic-gate }
71777c478bd9Sstevel@tonic-gate 
71787c478bd9Sstevel@tonic-gate /*
71797c478bd9Sstevel@tonic-gate  * Creates a context template for the specified SA. This function
71807c478bd9Sstevel@tonic-gate  * is called when an SA is created and when a context template needs
71817c478bd9Sstevel@tonic-gate  * to be created due to a change of software provider.
71827c478bd9Sstevel@tonic-gate  */
71837c478bd9Sstevel@tonic-gate int
71847c478bd9Sstevel@tonic-gate ipsec_create_ctx_tmpl(ipsa_t *sa, ipsec_algtype_t alg_type)
71857c478bd9Sstevel@tonic-gate {
71867c478bd9Sstevel@tonic-gate 	ipsec_alginfo_t *alg;
71877c478bd9Sstevel@tonic-gate 	crypto_mechanism_t mech;
71887c478bd9Sstevel@tonic-gate 	crypto_key_t *key;
71897c478bd9Sstevel@tonic-gate 	crypto_ctx_template_t *sa_tmpl;
71907c478bd9Sstevel@tonic-gate 	int rv;
7191f4b3ec61Sdh155122 	ipsec_stack_t	*ipss = sa->ipsa_netstack->netstack_ipsec;
71927c478bd9Sstevel@tonic-gate 
7193f4b3ec61Sdh155122 	ASSERT(MUTEX_HELD(&ipss->ipsec_alg_lock));
71947c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&sa->ipsa_lock));
71957c478bd9Sstevel@tonic-gate 
71967c478bd9Sstevel@tonic-gate 	/* get pointers to the algorithm info, context template, and key */
71977c478bd9Sstevel@tonic-gate 	switch (alg_type) {
71987c478bd9Sstevel@tonic-gate 	case IPSEC_ALG_AUTH:
71997c478bd9Sstevel@tonic-gate 		key = &sa->ipsa_kcfauthkey;
72007c478bd9Sstevel@tonic-gate 		sa_tmpl = &sa->ipsa_authtmpl;
7201f4b3ec61Sdh155122 		alg = ipss->ipsec_alglists[alg_type][sa->ipsa_auth_alg];
72027c478bd9Sstevel@tonic-gate 		break;
72037c478bd9Sstevel@tonic-gate 	case IPSEC_ALG_ENCR:
72047c478bd9Sstevel@tonic-gate 		key = &sa->ipsa_kcfencrkey;
72057c478bd9Sstevel@tonic-gate 		sa_tmpl = &sa->ipsa_encrtmpl;
7206f4b3ec61Sdh155122 		alg = ipss->ipsec_alglists[alg_type][sa->ipsa_encr_alg];
72077c478bd9Sstevel@tonic-gate 		break;
72087c478bd9Sstevel@tonic-gate 	default:
72097c478bd9Sstevel@tonic-gate 		alg = NULL;
72107c478bd9Sstevel@tonic-gate 	}
72117c478bd9Sstevel@tonic-gate 
72127c478bd9Sstevel@tonic-gate 	if (alg == NULL || !ALG_VALID(alg))
72137c478bd9Sstevel@tonic-gate 		return (EINVAL);
72147c478bd9Sstevel@tonic-gate 
72157c478bd9Sstevel@tonic-gate 	/* initialize the mech info structure for the framework */
72167c478bd9Sstevel@tonic-gate 	ASSERT(alg->alg_mech_type != CRYPTO_MECHANISM_INVALID);
72177c478bd9Sstevel@tonic-gate 	mech.cm_type = alg->alg_mech_type;
72187c478bd9Sstevel@tonic-gate 	mech.cm_param = NULL;
72197c478bd9Sstevel@tonic-gate 	mech.cm_param_len = 0;
72207c478bd9Sstevel@tonic-gate 
72217c478bd9Sstevel@tonic-gate 	/* create a new context template */
72227c478bd9Sstevel@tonic-gate 	rv = crypto_create_ctx_template(&mech, key, sa_tmpl, KM_NOSLEEP);
72237c478bd9Sstevel@tonic-gate 
72247c478bd9Sstevel@tonic-gate 	/*
72257c478bd9Sstevel@tonic-gate 	 * CRYPTO_MECH_NOT_SUPPORTED can be returned if only hardware
72267c478bd9Sstevel@tonic-gate 	 * providers are available for that mechanism. In that case
72277c478bd9Sstevel@tonic-gate 	 * we don't fail, and will generate the context template from
72287c478bd9Sstevel@tonic-gate 	 * the framework callback when a software provider for that
72297c478bd9Sstevel@tonic-gate 	 * mechanism registers.
72307c478bd9Sstevel@tonic-gate 	 *
72317c478bd9Sstevel@tonic-gate 	 * The context template is assigned the special value
72327c478bd9Sstevel@tonic-gate 	 * IPSEC_CTX_TMPL_ALLOC if the allocation failed due to a
72337c478bd9Sstevel@tonic-gate 	 * lack of memory. No attempt will be made to use
72347c478bd9Sstevel@tonic-gate 	 * the context template if it is set to this value.
72357c478bd9Sstevel@tonic-gate 	 */
72367c478bd9Sstevel@tonic-gate 	if (rv == CRYPTO_HOST_MEMORY) {
72377c478bd9Sstevel@tonic-gate 		*sa_tmpl = IPSEC_CTX_TMPL_ALLOC;
72387c478bd9Sstevel@tonic-gate 	} else if (rv != CRYPTO_SUCCESS) {
72397c478bd9Sstevel@tonic-gate 		*sa_tmpl = NULL;
72407c478bd9Sstevel@tonic-gate 		if (rv != CRYPTO_MECH_NOT_SUPPORTED)
72417c478bd9Sstevel@tonic-gate 			return (EINVAL);
72427c478bd9Sstevel@tonic-gate 	}
72437c478bd9Sstevel@tonic-gate 
72447c478bd9Sstevel@tonic-gate 	return (0);
72457c478bd9Sstevel@tonic-gate }
72467c478bd9Sstevel@tonic-gate 
72477c478bd9Sstevel@tonic-gate /*
72487c478bd9Sstevel@tonic-gate  * Destroy the context template of the specified algorithm type
72497c478bd9Sstevel@tonic-gate  * of the specified SA. Must be called while holding the SA lock.
72507c478bd9Sstevel@tonic-gate  */
72517c478bd9Sstevel@tonic-gate void
72527c478bd9Sstevel@tonic-gate ipsec_destroy_ctx_tmpl(ipsa_t *sa, ipsec_algtype_t alg_type)
72537c478bd9Sstevel@tonic-gate {
72547c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&sa->ipsa_lock));
72557c478bd9Sstevel@tonic-gate 
72567c478bd9Sstevel@tonic-gate 	if (alg_type == IPSEC_ALG_AUTH) {
72577c478bd9Sstevel@tonic-gate 		if (sa->ipsa_authtmpl == IPSEC_CTX_TMPL_ALLOC)
72587c478bd9Sstevel@tonic-gate 			sa->ipsa_authtmpl = NULL;
72597c478bd9Sstevel@tonic-gate 		else if (sa->ipsa_authtmpl != NULL) {
72607c478bd9Sstevel@tonic-gate 			crypto_destroy_ctx_template(sa->ipsa_authtmpl);
72617c478bd9Sstevel@tonic-gate 			sa->ipsa_authtmpl = NULL;
72627c478bd9Sstevel@tonic-gate 		}
72637c478bd9Sstevel@tonic-gate 	} else {
72647c478bd9Sstevel@tonic-gate 		ASSERT(alg_type == IPSEC_ALG_ENCR);
72657c478bd9Sstevel@tonic-gate 		if (sa->ipsa_encrtmpl == IPSEC_CTX_TMPL_ALLOC)
72667c478bd9Sstevel@tonic-gate 			sa->ipsa_encrtmpl = NULL;
72677c478bd9Sstevel@tonic-gate 		else if (sa->ipsa_encrtmpl != NULL) {
72687c478bd9Sstevel@tonic-gate 			crypto_destroy_ctx_template(sa->ipsa_encrtmpl);
72697c478bd9Sstevel@tonic-gate 			sa->ipsa_encrtmpl = NULL;
72707c478bd9Sstevel@tonic-gate 		}
72717c478bd9Sstevel@tonic-gate 	}
72727c478bd9Sstevel@tonic-gate }
72737c478bd9Sstevel@tonic-gate 
72747c478bd9Sstevel@tonic-gate /*
72757c478bd9Sstevel@tonic-gate  * Use the kernel crypto framework to check the validity of a key received
72767c478bd9Sstevel@tonic-gate  * via keysock. Returns 0 if the key is OK, -1 otherwise.
72777c478bd9Sstevel@tonic-gate  */
72787c478bd9Sstevel@tonic-gate int
72797c478bd9Sstevel@tonic-gate ipsec_check_key(crypto_mech_type_t mech_type, sadb_key_t *sadb_key,
72807c478bd9Sstevel@tonic-gate     boolean_t is_auth, int *diag)
72817c478bd9Sstevel@tonic-gate {
72827c478bd9Sstevel@tonic-gate 	crypto_mechanism_t mech;
72837c478bd9Sstevel@tonic-gate 	crypto_key_t crypto_key;
72847c478bd9Sstevel@tonic-gate 	int crypto_rc;
72857c478bd9Sstevel@tonic-gate 
72867c478bd9Sstevel@tonic-gate 	mech.cm_type = mech_type;
72877c478bd9Sstevel@tonic-gate 	mech.cm_param = NULL;
72887c478bd9Sstevel@tonic-gate 	mech.cm_param_len = 0;
72897c478bd9Sstevel@tonic-gate 
72907c478bd9Sstevel@tonic-gate 	crypto_key.ck_format = CRYPTO_KEY_RAW;
72917c478bd9Sstevel@tonic-gate 	crypto_key.ck_data = sadb_key + 1;
72927c478bd9Sstevel@tonic-gate 	crypto_key.ck_length = sadb_key->sadb_key_bits;
72937c478bd9Sstevel@tonic-gate 
72947c478bd9Sstevel@tonic-gate 	crypto_rc = crypto_key_check(&mech, &crypto_key);
72957c478bd9Sstevel@tonic-gate 
72967c478bd9Sstevel@tonic-gate 	switch (crypto_rc) {
72977c478bd9Sstevel@tonic-gate 	case CRYPTO_SUCCESS:
72987c478bd9Sstevel@tonic-gate 		return (0);
72997c478bd9Sstevel@tonic-gate 	case CRYPTO_MECHANISM_INVALID:
73007c478bd9Sstevel@tonic-gate 	case CRYPTO_MECH_NOT_SUPPORTED:
73017c478bd9Sstevel@tonic-gate 		*diag = is_auth ? SADB_X_DIAGNOSTIC_BAD_AALG :
73027c478bd9Sstevel@tonic-gate 		    SADB_X_DIAGNOSTIC_BAD_EALG;
73037c478bd9Sstevel@tonic-gate 		break;
73047c478bd9Sstevel@tonic-gate 	case CRYPTO_KEY_SIZE_RANGE:
73057c478bd9Sstevel@tonic-gate 		*diag = is_auth ? SADB_X_DIAGNOSTIC_BAD_AKEYBITS :
73067c478bd9Sstevel@tonic-gate 		    SADB_X_DIAGNOSTIC_BAD_EKEYBITS;
73077c478bd9Sstevel@tonic-gate 		break;
73087c478bd9Sstevel@tonic-gate 	case CRYPTO_WEAK_KEY:
73097c478bd9Sstevel@tonic-gate 		*diag = is_auth ? SADB_X_DIAGNOSTIC_WEAK_AKEY :
73107c478bd9Sstevel@tonic-gate 		    SADB_X_DIAGNOSTIC_WEAK_EKEY;
73117c478bd9Sstevel@tonic-gate 		break;
73127c478bd9Sstevel@tonic-gate 	}
73137c478bd9Sstevel@tonic-gate 
73147c478bd9Sstevel@tonic-gate 	return (-1);
73157c478bd9Sstevel@tonic-gate }
73165d3b8cb7SBill Sommerfeld 
73175d3b8cb7SBill Sommerfeld /*
73185d3b8cb7SBill Sommerfeld  * Whack options in the outer IP header when ipsec changes the outer label
73195d3b8cb7SBill Sommerfeld  *
73205d3b8cb7SBill Sommerfeld  * This is inelegant and really could use refactoring.
73215d3b8cb7SBill Sommerfeld  */
7322bd670b35SErik Nordmark mblk_t *
7323bd670b35SErik Nordmark sadb_whack_label_v4(mblk_t *mp, ipsa_t *assoc, kstat_named_t *counter,
7324bd670b35SErik Nordmark     ipdropper_t *dropper)
73255d3b8cb7SBill Sommerfeld {
73265d3b8cb7SBill Sommerfeld 	int delta;
73275d3b8cb7SBill Sommerfeld 	int plen;
73285d3b8cb7SBill Sommerfeld 	dblk_t *db;
73295d3b8cb7SBill Sommerfeld 	int hlen;
73305d3b8cb7SBill Sommerfeld 	uint8_t *opt_storage = assoc->ipsa_opt_storage;
73315d3b8cb7SBill Sommerfeld 	ipha_t *ipha = (ipha_t *)mp->b_rptr;
73325d3b8cb7SBill Sommerfeld 
73335d3b8cb7SBill Sommerfeld 	plen = ntohs(ipha->ipha_length);
73345d3b8cb7SBill Sommerfeld 
73355d3b8cb7SBill Sommerfeld 	delta = tsol_remove_secopt(ipha, MBLKL(mp));
73365d3b8cb7SBill Sommerfeld 	mp->b_wptr += delta;
73375d3b8cb7SBill Sommerfeld 	plen += delta;
73385d3b8cb7SBill Sommerfeld 
73395d3b8cb7SBill Sommerfeld 	/* XXX XXX code copied from tsol_check_label */
73405d3b8cb7SBill Sommerfeld 
73415d3b8cb7SBill Sommerfeld 	/* Make sure we have room for the worst-case addition */
73425d3b8cb7SBill Sommerfeld 	hlen = IPH_HDR_LENGTH(ipha) + opt_storage[IPOPT_OLEN];
73435d3b8cb7SBill Sommerfeld 	hlen = (hlen + 3) & ~3;
73445d3b8cb7SBill Sommerfeld 	if (hlen > IP_MAX_HDR_LENGTH)
73455d3b8cb7SBill Sommerfeld 		hlen = IP_MAX_HDR_LENGTH;
73465d3b8cb7SBill Sommerfeld 	hlen -= IPH_HDR_LENGTH(ipha);
73475d3b8cb7SBill Sommerfeld 
73485d3b8cb7SBill Sommerfeld 	db = mp->b_datap;
73495d3b8cb7SBill Sommerfeld 	if ((db->db_ref != 1) || (mp->b_wptr + hlen > db->db_lim)) {
73505d3b8cb7SBill Sommerfeld 		int copylen;
73515d3b8cb7SBill Sommerfeld 		mblk_t *new_mp;
73525d3b8cb7SBill Sommerfeld 
73535d3b8cb7SBill Sommerfeld 		/* allocate enough to be meaningful, but not *too* much */
73545d3b8cb7SBill Sommerfeld 		copylen = MBLKL(mp);
73555d3b8cb7SBill Sommerfeld 		if (copylen > 256)
73565d3b8cb7SBill Sommerfeld 			copylen = 256;
73575d3b8cb7SBill Sommerfeld 		new_mp = allocb_tmpl(hlen + copylen +
73585d3b8cb7SBill Sommerfeld 		    (mp->b_rptr - mp->b_datap->db_base), mp);
73595d3b8cb7SBill Sommerfeld 
7360bd670b35SErik Nordmark 		if (new_mp == NULL) {
7361bd670b35SErik Nordmark 			ip_drop_packet(mp, B_FALSE, NULL, counter,  dropper);
7362bd670b35SErik Nordmark 			return (NULL);
7363bd670b35SErik Nordmark 		}
73645d3b8cb7SBill Sommerfeld 
73655d3b8cb7SBill Sommerfeld 		/* keep the bias */
73665d3b8cb7SBill Sommerfeld 		new_mp->b_rptr += mp->b_rptr - mp->b_datap->db_base;
73675d3b8cb7SBill Sommerfeld 		new_mp->b_wptr = new_mp->b_rptr + copylen;
73685d3b8cb7SBill Sommerfeld 		bcopy(mp->b_rptr, new_mp->b_rptr, copylen);
73695d3b8cb7SBill Sommerfeld 		new_mp->b_cont = mp;
73705d3b8cb7SBill Sommerfeld 		if ((mp->b_rptr += copylen) >= mp->b_wptr) {
73715d3b8cb7SBill Sommerfeld 			new_mp->b_cont = mp->b_cont;
73725d3b8cb7SBill Sommerfeld 			freeb(mp);
73735d3b8cb7SBill Sommerfeld 		}
7374bd670b35SErik Nordmark 		mp = new_mp;
73755d3b8cb7SBill Sommerfeld 		ipha = (ipha_t *)mp->b_rptr;
73765d3b8cb7SBill Sommerfeld 	}
73775d3b8cb7SBill Sommerfeld 
73785d3b8cb7SBill Sommerfeld 	delta = tsol_prepend_option(assoc->ipsa_opt_storage, ipha, MBLKL(mp));
73795d3b8cb7SBill Sommerfeld 
73805d3b8cb7SBill Sommerfeld 	ASSERT(delta != -1);
73815d3b8cb7SBill Sommerfeld 
73825d3b8cb7SBill Sommerfeld 	plen += delta;
73835d3b8cb7SBill Sommerfeld 	mp->b_wptr += delta;
73845d3b8cb7SBill Sommerfeld 
73855d3b8cb7SBill Sommerfeld 	/*
73865d3b8cb7SBill Sommerfeld 	 * Paranoia
73875d3b8cb7SBill Sommerfeld 	 */
73885d3b8cb7SBill Sommerfeld 	db = mp->b_datap;
73895d3b8cb7SBill Sommerfeld 
73905d3b8cb7SBill Sommerfeld 	ASSERT3P(mp->b_wptr, <=, db->db_lim);
73915d3b8cb7SBill Sommerfeld 	ASSERT3P(mp->b_rptr, <=, db->db_lim);
73925d3b8cb7SBill Sommerfeld 
73935d3b8cb7SBill Sommerfeld 	ASSERT3P(mp->b_wptr, >=, db->db_base);
73945d3b8cb7SBill Sommerfeld 	ASSERT3P(mp->b_rptr, >=, db->db_base);
73955d3b8cb7SBill Sommerfeld 	/* End paranoia */
73965d3b8cb7SBill Sommerfeld 
73975d3b8cb7SBill Sommerfeld 	ipha->ipha_length = htons(plen);
73985d3b8cb7SBill Sommerfeld 
7399bd670b35SErik Nordmark 	return (mp);
74005d3b8cb7SBill Sommerfeld }
74015d3b8cb7SBill Sommerfeld 
7402bd670b35SErik Nordmark mblk_t *
7403bd670b35SErik Nordmark sadb_whack_label_v6(mblk_t *mp, ipsa_t *assoc, kstat_named_t *counter,
7404bd670b35SErik Nordmark     ipdropper_t *dropper)
74055d3b8cb7SBill Sommerfeld {
74065d3b8cb7SBill Sommerfeld 	int delta;
74075d3b8cb7SBill Sommerfeld 	int plen;
74085d3b8cb7SBill Sommerfeld 	dblk_t *db;
74095d3b8cb7SBill Sommerfeld 	int hlen;
74105d3b8cb7SBill Sommerfeld 	uint8_t *opt_storage = assoc->ipsa_opt_storage;
74115d3b8cb7SBill Sommerfeld 	uint_t sec_opt_len; /* label option length not including type, len */
74125d3b8cb7SBill Sommerfeld 	ip6_t *ip6h = (ip6_t *)mp->b_rptr;
74135d3b8cb7SBill Sommerfeld 
74145d3b8cb7SBill Sommerfeld 	plen = ntohs(ip6h->ip6_plen);
74155d3b8cb7SBill Sommerfeld 
74165d3b8cb7SBill Sommerfeld 	delta = tsol_remove_secopt_v6(ip6h, MBLKL(mp));
74175d3b8cb7SBill Sommerfeld 	mp->b_wptr += delta;
74185d3b8cb7SBill Sommerfeld 	plen += delta;
74195d3b8cb7SBill Sommerfeld 
74205d3b8cb7SBill Sommerfeld 	/* XXX XXX code copied from tsol_check_label_v6 */
74215d3b8cb7SBill Sommerfeld 	/*
74225d3b8cb7SBill Sommerfeld 	 * Make sure we have room for the worst-case addition. Add 2 bytes for
74235d3b8cb7SBill Sommerfeld 	 * the hop-by-hop ext header's next header and length fields. Add
74245d3b8cb7SBill Sommerfeld 	 * another 2 bytes for the label option type, len and then round
74255d3b8cb7SBill Sommerfeld 	 * up to the next 8-byte multiple.
74265d3b8cb7SBill Sommerfeld 	 */
74275d3b8cb7SBill Sommerfeld 	sec_opt_len = opt_storage[1];
74285d3b8cb7SBill Sommerfeld 
74295d3b8cb7SBill Sommerfeld 	db = mp->b_datap;
74305d3b8cb7SBill Sommerfeld 	hlen = (4 + sec_opt_len + 7) & ~7;
74315d3b8cb7SBill Sommerfeld 
74325d3b8cb7SBill Sommerfeld 	if ((db->db_ref != 1) || (mp->b_wptr + hlen > db->db_lim)) {
74335d3b8cb7SBill Sommerfeld 		int copylen;
74345d3b8cb7SBill Sommerfeld 		mblk_t *new_mp;
74355d3b8cb7SBill Sommerfeld 		uint16_t hdr_len;
74365d3b8cb7SBill Sommerfeld 
74375d3b8cb7SBill Sommerfeld 		hdr_len = ip_hdr_length_v6(mp, ip6h);
74385d3b8cb7SBill Sommerfeld 		/*
74395d3b8cb7SBill Sommerfeld 		 * Allocate enough to be meaningful, but not *too* much.
74405d3b8cb7SBill Sommerfeld 		 * Also all the IPv6 extension headers must be in the same mblk
74415d3b8cb7SBill Sommerfeld 		 */
74425d3b8cb7SBill Sommerfeld 		copylen = MBLKL(mp);
74435d3b8cb7SBill Sommerfeld 		if (copylen > 256)
74445d3b8cb7SBill Sommerfeld 			copylen = 256;
74455d3b8cb7SBill Sommerfeld 		if (copylen < hdr_len)
74465d3b8cb7SBill Sommerfeld 			copylen = hdr_len;
74475d3b8cb7SBill Sommerfeld 		new_mp = allocb_tmpl(hlen + copylen +
74485d3b8cb7SBill Sommerfeld 		    (mp->b_rptr - mp->b_datap->db_base), mp);
7449bd670b35SErik Nordmark 		if (new_mp == NULL) {
7450bd670b35SErik Nordmark 			ip_drop_packet(mp, B_FALSE, NULL, counter,  dropper);
7451bd670b35SErik Nordmark 			return (NULL);
7452bd670b35SErik Nordmark 		}
74535d3b8cb7SBill Sommerfeld 
74545d3b8cb7SBill Sommerfeld 		/* keep the bias */
74555d3b8cb7SBill Sommerfeld 		new_mp->b_rptr += mp->b_rptr - mp->b_datap->db_base;
74565d3b8cb7SBill Sommerfeld 		new_mp->b_wptr = new_mp->b_rptr + copylen;
74575d3b8cb7SBill Sommerfeld 		bcopy(mp->b_rptr, new_mp->b_rptr, copylen);
74585d3b8cb7SBill Sommerfeld 		new_mp->b_cont = mp;
74595d3b8cb7SBill Sommerfeld 		if ((mp->b_rptr += copylen) >= mp->b_wptr) {
74605d3b8cb7SBill Sommerfeld 			new_mp->b_cont = mp->b_cont;
74615d3b8cb7SBill Sommerfeld 			freeb(mp);
74625d3b8cb7SBill Sommerfeld 		}
7463bd670b35SErik Nordmark 		mp = new_mp;
74645d3b8cb7SBill Sommerfeld 		ip6h = (ip6_t *)mp->b_rptr;
74655d3b8cb7SBill Sommerfeld 	}
74665d3b8cb7SBill Sommerfeld 
74675d3b8cb7SBill Sommerfeld 	delta = tsol_prepend_option_v6(assoc->ipsa_opt_storage,
74685d3b8cb7SBill Sommerfeld 	    ip6h, MBLKL(mp));
74695d3b8cb7SBill Sommerfeld 
74705d3b8cb7SBill Sommerfeld 	ASSERT(delta != -1);
74715d3b8cb7SBill Sommerfeld 
74725d3b8cb7SBill Sommerfeld 	plen += delta;
74735d3b8cb7SBill Sommerfeld 	mp->b_wptr += delta;
74745d3b8cb7SBill Sommerfeld 
74755d3b8cb7SBill Sommerfeld 	/*
74765d3b8cb7SBill Sommerfeld 	 * Paranoia
74775d3b8cb7SBill Sommerfeld 	 */
74785d3b8cb7SBill Sommerfeld 	db = mp->b_datap;
74795d3b8cb7SBill Sommerfeld 
74805d3b8cb7SBill Sommerfeld 	ASSERT3P(mp->b_wptr, <=, db->db_lim);
74815d3b8cb7SBill Sommerfeld 	ASSERT3P(mp->b_rptr, <=, db->db_lim);
74825d3b8cb7SBill Sommerfeld 
74835d3b8cb7SBill Sommerfeld 	ASSERT3P(mp->b_wptr, >=, db->db_base);
74845d3b8cb7SBill Sommerfeld 	ASSERT3P(mp->b_rptr, >=, db->db_base);
74855d3b8cb7SBill Sommerfeld 	/* End paranoia */
74865d3b8cb7SBill Sommerfeld 
74875d3b8cb7SBill Sommerfeld 	ip6h->ip6_plen = htons(plen);
74885d3b8cb7SBill Sommerfeld 
7489bd670b35SErik Nordmark 	return (mp);
74905d3b8cb7SBill Sommerfeld }
74915d3b8cb7SBill Sommerfeld 
7492bd670b35SErik Nordmark /* Whack the labels and update ip_xmit_attr_t as needed */
7493bd670b35SErik Nordmark mblk_t *
7494bd670b35SErik Nordmark sadb_whack_label(mblk_t *mp, ipsa_t *assoc, ip_xmit_attr_t *ixa,
7495bd670b35SErik Nordmark     kstat_named_t *counter, ipdropper_t *dropper)
7496bd670b35SErik Nordmark {
7497bd670b35SErik Nordmark 	int adjust;
7498bd670b35SErik Nordmark 	int iplen;
74995d3b8cb7SBill Sommerfeld 
7500bd670b35SErik Nordmark 	if (ixa->ixa_flags & IXAF_IS_IPV4) {
7501bd670b35SErik Nordmark 		ipha_t		*ipha = (ipha_t *)mp->b_rptr;
7502bd670b35SErik Nordmark 
7503bd670b35SErik Nordmark 		ASSERT(IPH_HDR_VERSION(ipha) == IPV4_VERSION);
7504bd670b35SErik Nordmark 		iplen = ntohs(ipha->ipha_length);
7505bd670b35SErik Nordmark 		mp = sadb_whack_label_v4(mp, assoc, counter, dropper);
7506bd670b35SErik Nordmark 		if (mp == NULL)
7507bd670b35SErik Nordmark 			return (NULL);
7508bd670b35SErik Nordmark 
7509bd670b35SErik Nordmark 		ipha = (ipha_t *)mp->b_rptr;
7510bd670b35SErik Nordmark 		ASSERT(IPH_HDR_VERSION(ipha) == IPV4_VERSION);
7511bd670b35SErik Nordmark 		adjust = (int)ntohs(ipha->ipha_length) - iplen;
7512bd670b35SErik Nordmark 	} else {
7513bd670b35SErik Nordmark 		ip6_t		*ip6h = (ip6_t *)mp->b_rptr;
7514bd670b35SErik Nordmark 
7515bd670b35SErik Nordmark 		ASSERT(IPH_HDR_VERSION(ip6h) == IPV6_VERSION);
7516bd670b35SErik Nordmark 		iplen = ntohs(ip6h->ip6_plen);
7517bd670b35SErik Nordmark 		mp = sadb_whack_label_v6(mp, assoc, counter, dropper);
7518bd670b35SErik Nordmark 		if (mp == NULL)
7519bd670b35SErik Nordmark 			return (NULL);
7520bd670b35SErik Nordmark 
7521bd670b35SErik Nordmark 		ip6h = (ip6_t *)mp->b_rptr;
7522bd670b35SErik Nordmark 		ASSERT(IPH_HDR_VERSION(ip6h) == IPV6_VERSION);
7523bd670b35SErik Nordmark 		adjust = (int)ntohs(ip6h->ip6_plen) - iplen;
7524bd670b35SErik Nordmark 	}
7525bd670b35SErik Nordmark 	ixa->ixa_pktlen += adjust;
7526bd670b35SErik Nordmark 	ixa->ixa_ip_hdr_length += adjust;
7527bd670b35SErik Nordmark 	return (mp);
7528bd670b35SErik Nordmark }
75295d3b8cb7SBill Sommerfeld 
753038d95a78Smarkfen /*
753138d95a78Smarkfen  * If this is an outgoing SA then add some fuzz to the
753238d95a78Smarkfen  * SOFT EXPIRE time. The reason for this is to stop
753338d95a78Smarkfen  * peers trying to renegotiate SOFT expiring SA's at
753438d95a78Smarkfen  * the same time. The amount of fuzz needs to be at
75350e9b5742SDan McDonald  * least 8 seconds which is the typical interval
753638d95a78Smarkfen  * sadb_ager(), although this is only a guide as it
753738d95a78Smarkfen  * selftunes.
753838d95a78Smarkfen  */
75395d3b8cb7SBill Sommerfeld static void
754038d95a78Smarkfen lifetime_fuzz(ipsa_t *assoc)
754138d95a78Smarkfen {
754238d95a78Smarkfen 	uint8_t rnd;
754338d95a78Smarkfen 
754438d95a78Smarkfen 	if (assoc->ipsa_softaddlt == 0)
754538d95a78Smarkfen 		return;
754638d95a78Smarkfen 
754738d95a78Smarkfen 	(void) random_get_pseudo_bytes(&rnd, sizeof (rnd));
75480e9b5742SDan McDonald 	rnd = (rnd & 0xF) + 8;
754938d95a78Smarkfen 	assoc->ipsa_softexpiretime -= rnd;
755038d95a78Smarkfen 	assoc->ipsa_softaddlt -= rnd;
755138d95a78Smarkfen }
75525d3b8cb7SBill Sommerfeld 
75535d3b8cb7SBill Sommerfeld static void
755438d95a78Smarkfen destroy_ipsa_pair(ipsap_t *ipsapp)
755538d95a78Smarkfen {
755638d95a78Smarkfen 	/*
755738d95a78Smarkfen 	 * Because of the multi-line macro nature of IPSA_REFRELE, keep
755838d95a78Smarkfen 	 * them in { }.
755938d95a78Smarkfen 	 */
756038d95a78Smarkfen 	if (ipsapp->ipsap_sa_ptr != NULL) {
756138d95a78Smarkfen 		IPSA_REFRELE(ipsapp->ipsap_sa_ptr);
756238d95a78Smarkfen 	}
756338d95a78Smarkfen 	if (ipsapp->ipsap_psa_ptr != NULL) {
756438d95a78Smarkfen 		IPSA_REFRELE(ipsapp->ipsap_psa_ptr);
756538d95a78Smarkfen 	}
75665d3b8cb7SBill Sommerfeld 	init_ipsa_pair(ipsapp);
75675d3b8cb7SBill Sommerfeld }
756838d95a78Smarkfen 
75695d3b8cb7SBill Sommerfeld static void
75705d3b8cb7SBill Sommerfeld init_ipsa_pair(ipsap_t *ipsapp)
75715d3b8cb7SBill Sommerfeld {
75725d3b8cb7SBill Sommerfeld 	ipsapp->ipsap_bucket = NULL;
75735d3b8cb7SBill Sommerfeld 	ipsapp->ipsap_sa_ptr = NULL;
75745d3b8cb7SBill Sommerfeld 	ipsapp->ipsap_pbucket = NULL;
75755d3b8cb7SBill Sommerfeld 	ipsapp->ipsap_psa_ptr = NULL;
757638d95a78Smarkfen }
757738d95a78Smarkfen 
757838d95a78Smarkfen /*
757938d95a78Smarkfen  * The sadb_ager() function walks through the hash tables of SA's and ages
758038d95a78Smarkfen  * them, if the SA expires as a result, its marked as DEAD and will be reaped
758138d95a78Smarkfen  * the next time sadb_ager() runs. SA's which are paired or have a peer (same
758238d95a78Smarkfen  * SA appears in both the inbound and outbound tables because its not possible
758338d95a78Smarkfen  * to determine its direction) are placed on a list when they expire. This is
758438d95a78Smarkfen  * to ensure that pair/peer SA's are reaped at the same time, even if they
758538d95a78Smarkfen  * expire at different times.
758638d95a78Smarkfen  *
758738d95a78Smarkfen  * This function is called twice by sadb_ager(), one after processing the
758838d95a78Smarkfen  * inbound table, then again after processing the outbound table.
758938d95a78Smarkfen  */
759038d95a78Smarkfen void
759138d95a78Smarkfen age_pair_peer_list(templist_t *haspeerlist, sadb_t *sp, boolean_t outbound)
759238d95a78Smarkfen {
759338d95a78Smarkfen 	templist_t *listptr;
759438d95a78Smarkfen 	int outhash;
759538d95a78Smarkfen 	isaf_t *bucket;
759638d95a78Smarkfen 	boolean_t haspeer;
759738d95a78Smarkfen 	ipsa_t *peer_assoc, *dying;
759838d95a78Smarkfen 	/*
759938d95a78Smarkfen 	 * Haspeer cases will contain both IPv4 and IPv6.  This code
760038d95a78Smarkfen 	 * is address independent.
760138d95a78Smarkfen 	 */
760238d95a78Smarkfen 	while (haspeerlist != NULL) {
760338d95a78Smarkfen 		/* "dying" contains the SA that has a peer. */
760438d95a78Smarkfen 		dying = haspeerlist->ipsa;
760538d95a78Smarkfen 		haspeer = (dying->ipsa_haspeer);
760638d95a78Smarkfen 		listptr = haspeerlist;
760738d95a78Smarkfen 		haspeerlist = listptr->next;
760838d95a78Smarkfen 		kmem_free(listptr, sizeof (*listptr));
760938d95a78Smarkfen 		/*
761038d95a78Smarkfen 		 * Pick peer bucket based on addrfam.
761138d95a78Smarkfen 		 */
761238d95a78Smarkfen 		if (outbound) {
761338d95a78Smarkfen 			if (haspeer)
761438d95a78Smarkfen 				bucket = INBOUND_BUCKET(sp, dying->ipsa_spi);
761538d95a78Smarkfen 			else
761638d95a78Smarkfen 				bucket = INBOUND_BUCKET(sp,
761738d95a78Smarkfen 				    dying->ipsa_otherspi);
761838d95a78Smarkfen 		} else { /* inbound */
761938d95a78Smarkfen 			if (haspeer) {
762038d95a78Smarkfen 				if (dying->ipsa_addrfam == AF_INET6) {
762138d95a78Smarkfen 					outhash = OUTBOUND_HASH_V6(sp,
762238d95a78Smarkfen 					    *((in6_addr_t *)&dying->
762338d95a78Smarkfen 					    ipsa_dstaddr));
762438d95a78Smarkfen 				} else {
762538d95a78Smarkfen 					outhash = OUTBOUND_HASH_V4(sp,
762638d95a78Smarkfen 					    *((ipaddr_t *)&dying->
762738d95a78Smarkfen 					    ipsa_dstaddr));
762838d95a78Smarkfen 				}
762938d95a78Smarkfen 			} else if (dying->ipsa_addrfam == AF_INET6) {
763038d95a78Smarkfen 				outhash = OUTBOUND_HASH_V6(sp,
763138d95a78Smarkfen 				    *((in6_addr_t *)&dying->
763238d95a78Smarkfen 				    ipsa_srcaddr));
763338d95a78Smarkfen 			} else {
763438d95a78Smarkfen 				outhash = OUTBOUND_HASH_V4(sp,
763538d95a78Smarkfen 				    *((ipaddr_t *)&dying->
763638d95a78Smarkfen 				    ipsa_srcaddr));
763738d95a78Smarkfen 			}
763838d95a78Smarkfen 			bucket = &(sp->sdb_of[outhash]);
763938d95a78Smarkfen 		}
764038d95a78Smarkfen 
764138d95a78Smarkfen 		mutex_enter(&bucket->isaf_lock);
764238d95a78Smarkfen 		/*
764338d95a78Smarkfen 		 * "haspeer" SA's have the same src/dst address ordering,
764438d95a78Smarkfen 		 * "paired" SA's have the src/dst addresses reversed.
764538d95a78Smarkfen 		 */
764638d95a78Smarkfen 		if (haspeer) {
764738d95a78Smarkfen 			peer_assoc = ipsec_getassocbyspi(bucket,
764838d95a78Smarkfen 			    dying->ipsa_spi, dying->ipsa_srcaddr,
764938d95a78Smarkfen 			    dying->ipsa_dstaddr, dying->ipsa_addrfam);
765038d95a78Smarkfen 		} else {
765138d95a78Smarkfen 			peer_assoc = ipsec_getassocbyspi(bucket,
765238d95a78Smarkfen 			    dying->ipsa_otherspi, dying->ipsa_dstaddr,
765338d95a78Smarkfen 			    dying->ipsa_srcaddr, dying->ipsa_addrfam);
765438d95a78Smarkfen 		}
765538d95a78Smarkfen 
765638d95a78Smarkfen 		mutex_exit(&bucket->isaf_lock);
765738d95a78Smarkfen 		if (peer_assoc != NULL) {
765838d95a78Smarkfen 			mutex_enter(&peer_assoc->ipsa_lock);
765938d95a78Smarkfen 			mutex_enter(&dying->ipsa_lock);
766038d95a78Smarkfen 			if (!haspeer) {
766138d95a78Smarkfen 				/*
766238d95a78Smarkfen 				 * Only SA's which have a "peer" or are
766338d95a78Smarkfen 				 * "paired" end up on this list, so this
766438d95a78Smarkfen 				 * must be a "paired" SA, update the flags
766538d95a78Smarkfen 				 * to break the pair.
766638d95a78Smarkfen 				 */
766738d95a78Smarkfen 				peer_assoc->ipsa_otherspi = 0;
766838d95a78Smarkfen 				peer_assoc->ipsa_flags &= ~IPSA_F_PAIRED;
766938d95a78Smarkfen 				dying->ipsa_otherspi = 0;
767038d95a78Smarkfen 				dying->ipsa_flags &= ~IPSA_F_PAIRED;
767138d95a78Smarkfen 			}
767238d95a78Smarkfen 			if (haspeer || outbound) {
767338d95a78Smarkfen 				/*
767438d95a78Smarkfen 				 * Update the state of the "inbound" SA when
767538d95a78Smarkfen 				 * the "outbound" SA has expired. Don't update
767638d95a78Smarkfen 				 * the "outbound" SA when the "inbound" SA
767738d95a78Smarkfen 				 * SA expires because setting the hard_addtime
767838d95a78Smarkfen 				 * below will cause this to happen.
767938d95a78Smarkfen 				 */
768038d95a78Smarkfen 				peer_assoc->ipsa_state = dying->ipsa_state;
768138d95a78Smarkfen 			}
768238d95a78Smarkfen 			if (dying->ipsa_state == IPSA_STATE_DEAD)
768338d95a78Smarkfen 				peer_assoc->ipsa_hardexpiretime = 1;
768438d95a78Smarkfen 
768538d95a78Smarkfen 			mutex_exit(&dying->ipsa_lock);
768638d95a78Smarkfen 			mutex_exit(&peer_assoc->ipsa_lock);
768738d95a78Smarkfen 			IPSA_REFRELE(peer_assoc);
768838d95a78Smarkfen 		}
768938d95a78Smarkfen 		IPSA_REFRELE(dying);
769038d95a78Smarkfen 	}
769138d95a78Smarkfen }
7692628b0c67SMark Fenwick 
7693628b0c67SMark Fenwick /*
7694628b0c67SMark Fenwick  * Ensure that the IV used for CCM mode never repeats. The IV should
7695628b0c67SMark Fenwick  * only be updated by this function. Also check to see if the IV
7696628b0c67SMark Fenwick  * is about to wrap and generate a SOFT Expire. This function is only
7697628b0c67SMark Fenwick  * called for outgoing packets, the IV for incomming packets is taken
7698628b0c67SMark Fenwick  * from the wire. If the outgoing SA needs to be expired, update
7699628b0c67SMark Fenwick  * the matching incomming SA.
7700628b0c67SMark Fenwick  */
7701628b0c67SMark Fenwick boolean_t
7702628b0c67SMark Fenwick update_iv(uint8_t *iv_ptr, queue_t *pfkey_q, ipsa_t *assoc,
7703628b0c67SMark Fenwick     ipsecesp_stack_t *espstack)
7704628b0c67SMark Fenwick {
7705628b0c67SMark Fenwick 	boolean_t rc = B_TRUE;
7706628b0c67SMark Fenwick 	isaf_t *inbound_bucket;
7707628b0c67SMark Fenwick 	sadb_t *sp;
7708628b0c67SMark Fenwick 	ipsa_t *pair_sa = NULL;
7709628b0c67SMark Fenwick 	int sa_new_state = 0;
7710628b0c67SMark Fenwick 
7711628b0c67SMark Fenwick 	/* For non counter modes, the IV is random data. */
7712628b0c67SMark Fenwick 	if (!(assoc->ipsa_flags & IPSA_F_COUNTERMODE)) {
7713628b0c67SMark Fenwick 		(void) random_get_pseudo_bytes(iv_ptr, assoc->ipsa_iv_len);
7714628b0c67SMark Fenwick 		return (rc);
7715628b0c67SMark Fenwick 	}
7716628b0c67SMark Fenwick 
7717628b0c67SMark Fenwick 	mutex_enter(&assoc->ipsa_lock);
7718628b0c67SMark Fenwick 
7719628b0c67SMark Fenwick 	(*assoc->ipsa_iv)++;
7720628b0c67SMark Fenwick 
7721628b0c67SMark Fenwick 	if (*assoc->ipsa_iv == assoc->ipsa_iv_hardexpire) {
7722628b0c67SMark Fenwick 		sa_new_state = IPSA_STATE_DEAD;
7723628b0c67SMark Fenwick 		rc = B_FALSE;
7724628b0c67SMark Fenwick 	} else if (*assoc->ipsa_iv == assoc->ipsa_iv_softexpire) {
7725628b0c67SMark Fenwick 		if (assoc->ipsa_state != IPSA_STATE_DYING) {
7726628b0c67SMark Fenwick 			/*
7727628b0c67SMark Fenwick 			 * This SA may have already been expired when its
7728628b0c67SMark Fenwick 			 * PAIR_SA expired.
7729628b0c67SMark Fenwick 			 */
7730628b0c67SMark Fenwick 			sa_new_state = IPSA_STATE_DYING;
7731628b0c67SMark Fenwick 		}
7732628b0c67SMark Fenwick 	}
7733628b0c67SMark Fenwick 	if (sa_new_state) {
7734628b0c67SMark Fenwick 		/*
7735628b0c67SMark Fenwick 		 * If there is a state change, we need to update this SA
7736628b0c67SMark Fenwick 		 * and its "pair", we can find the bucket for the "pair" SA
7737628b0c67SMark Fenwick 		 * while holding the ipsa_t mutex, but we won't actually
7738628b0c67SMark Fenwick 		 * update anything untill the ipsa_t mutex has been released
7739628b0c67SMark Fenwick 		 * for _this_ SA.
7740628b0c67SMark Fenwick 		 */
7741628b0c67SMark Fenwick 		assoc->ipsa_state = sa_new_state;
7742628b0c67SMark Fenwick 		if (assoc->ipsa_addrfam == AF_INET6) {
7743628b0c67SMark Fenwick 			sp = &espstack->esp_sadb.s_v6;
7744628b0c67SMark Fenwick 		} else {
7745628b0c67SMark Fenwick 			sp = &espstack->esp_sadb.s_v4;
7746628b0c67SMark Fenwick 		}
7747628b0c67SMark Fenwick 		inbound_bucket = INBOUND_BUCKET(sp, assoc->ipsa_otherspi);
7748628b0c67SMark Fenwick 		sadb_expire_assoc(pfkey_q, assoc);
7749628b0c67SMark Fenwick 	}
7750628b0c67SMark Fenwick 	if (rc == B_TRUE)
7751628b0c67SMark Fenwick 		bcopy(assoc->ipsa_iv, iv_ptr, assoc->ipsa_iv_len);
7752628b0c67SMark Fenwick 
7753628b0c67SMark Fenwick 	mutex_exit(&assoc->ipsa_lock);
7754628b0c67SMark Fenwick 
7755628b0c67SMark Fenwick 	if (sa_new_state) {
7756628b0c67SMark Fenwick 		/* Find the inbound SA, need to lock hash bucket. */
7757628b0c67SMark Fenwick 		mutex_enter(&inbound_bucket->isaf_lock);
7758628b0c67SMark Fenwick 		pair_sa = ipsec_getassocbyspi(inbound_bucket,
7759628b0c67SMark Fenwick 		    assoc->ipsa_otherspi, assoc->ipsa_dstaddr,
7760628b0c67SMark Fenwick 		    assoc->ipsa_srcaddr, assoc->ipsa_addrfam);
7761628b0c67SMark Fenwick 		mutex_exit(&inbound_bucket->isaf_lock);
7762628b0c67SMark Fenwick 		if (pair_sa != NULL) {
7763628b0c67SMark Fenwick 			mutex_enter(&pair_sa->ipsa_lock);
7764628b0c67SMark Fenwick 			pair_sa->ipsa_state = sa_new_state;
7765628b0c67SMark Fenwick 			mutex_exit(&pair_sa->ipsa_lock);
7766628b0c67SMark Fenwick 			IPSA_REFRELE(pair_sa);
7767628b0c67SMark Fenwick 		}
7768628b0c67SMark Fenwick 	}
7769628b0c67SMark Fenwick 
7770628b0c67SMark Fenwick 	return (rc);
7771628b0c67SMark Fenwick }
7772628b0c67SMark Fenwick 
7773628b0c67SMark Fenwick void
7774628b0c67SMark Fenwick ccm_params_init(ipsa_t *assoc, uchar_t *esph, uint_t data_len, uchar_t *iv_ptr,
7775628b0c67SMark Fenwick     ipsa_cm_mech_t *cm_mech, crypto_data_t *crypto_data)
7776628b0c67SMark Fenwick {
7777628b0c67SMark Fenwick 	uchar_t *nonce;
7778628b0c67SMark Fenwick 	crypto_mechanism_t *combined_mech;
7779628b0c67SMark Fenwick 	CK_AES_CCM_PARAMS *params;
7780628b0c67SMark Fenwick 
7781628b0c67SMark Fenwick 	combined_mech = (crypto_mechanism_t *)cm_mech;
7782628b0c67SMark Fenwick 	params = (CK_AES_CCM_PARAMS *)(combined_mech + 1);
7783628b0c67SMark Fenwick 	nonce = (uchar_t *)(params + 1);
7784628b0c67SMark Fenwick 	params->ulMACSize = assoc->ipsa_mac_len;
7785628b0c67SMark Fenwick 	params->ulNonceSize = assoc->ipsa_nonce_len;
7786628b0c67SMark Fenwick 	params->ulAuthDataSize = sizeof (esph_t);
7787628b0c67SMark Fenwick 	params->ulDataSize = data_len;
7788628b0c67SMark Fenwick 	params->nonce = nonce;
7789628b0c67SMark Fenwick 	params->authData = esph;
7790628b0c67SMark Fenwick 
7791628b0c67SMark Fenwick 	cm_mech->combined_mech.cm_type = assoc->ipsa_emech.cm_type;
7792628b0c67SMark Fenwick 	cm_mech->combined_mech.cm_param_len = sizeof (CK_AES_CCM_PARAMS);
7793628b0c67SMark Fenwick 	cm_mech->combined_mech.cm_param = (caddr_t)params;
7794628b0c67SMark Fenwick 	/* See gcm_params_init() for comments. */
7795628b0c67SMark Fenwick 	bcopy(assoc->ipsa_nonce, nonce, assoc->ipsa_saltlen);
7796628b0c67SMark Fenwick 	nonce += assoc->ipsa_saltlen;
7797628b0c67SMark Fenwick 	bcopy(iv_ptr, nonce, assoc->ipsa_iv_len);
7798628b0c67SMark Fenwick 	crypto_data->cd_miscdata = NULL;
7799628b0c67SMark Fenwick }
7800628b0c67SMark Fenwick 
7801628b0c67SMark Fenwick /* ARGSUSED */
7802628b0c67SMark Fenwick void
7803628b0c67SMark Fenwick cbc_params_init(ipsa_t *assoc, uchar_t *esph, uint_t data_len, uchar_t *iv_ptr,
7804628b0c67SMark Fenwick     ipsa_cm_mech_t *cm_mech, crypto_data_t *crypto_data)
7805628b0c67SMark Fenwick {
7806628b0c67SMark Fenwick 	cm_mech->combined_mech.cm_type = assoc->ipsa_emech.cm_type;
7807628b0c67SMark Fenwick 	cm_mech->combined_mech.cm_param_len = 0;
7808628b0c67SMark Fenwick 	cm_mech->combined_mech.cm_param = NULL;
7809628b0c67SMark Fenwick 	crypto_data->cd_miscdata = (char *)iv_ptr;
7810628b0c67SMark Fenwick }
7811628b0c67SMark Fenwick 
7812628b0c67SMark Fenwick /* ARGSUSED */
7813628b0c67SMark Fenwick void
7814628b0c67SMark Fenwick gcm_params_init(ipsa_t *assoc, uchar_t *esph, uint_t data_len, uchar_t *iv_ptr,
7815628b0c67SMark Fenwick     ipsa_cm_mech_t *cm_mech, crypto_data_t *crypto_data)
7816628b0c67SMark Fenwick {
7817628b0c67SMark Fenwick 	uchar_t *nonce;
7818628b0c67SMark Fenwick 	crypto_mechanism_t *combined_mech;
7819628b0c67SMark Fenwick 	CK_AES_GCM_PARAMS *params;
7820628b0c67SMark Fenwick 
7821628b0c67SMark Fenwick 	combined_mech = (crypto_mechanism_t *)cm_mech;
7822628b0c67SMark Fenwick 	params = (CK_AES_GCM_PARAMS *)(combined_mech + 1);
7823628b0c67SMark Fenwick 	nonce = (uchar_t *)(params + 1);
7824628b0c67SMark Fenwick 
7825628b0c67SMark Fenwick 	params->pIv = nonce;
7826628b0c67SMark Fenwick 	params->ulIvLen = assoc->ipsa_nonce_len;
7827628b0c67SMark Fenwick 	params->ulIvBits = SADB_8TO1(assoc->ipsa_nonce_len);
7828628b0c67SMark Fenwick 	params->pAAD = esph;
7829628b0c67SMark Fenwick 	params->ulAADLen = sizeof (esph_t);
7830628b0c67SMark Fenwick 	params->ulTagBits = SADB_8TO1(assoc->ipsa_mac_len);
7831628b0c67SMark Fenwick 
7832628b0c67SMark Fenwick 	cm_mech->combined_mech.cm_type = assoc->ipsa_emech.cm_type;
7833628b0c67SMark Fenwick 	cm_mech->combined_mech.cm_param_len = sizeof (CK_AES_GCM_PARAMS);
7834628b0c67SMark Fenwick 	cm_mech->combined_mech.cm_param = (caddr_t)params;
7835628b0c67SMark Fenwick 	/*
7836628b0c67SMark Fenwick 	 * Create the nonce, which is made up of the salt and the IV.
7837628b0c67SMark Fenwick 	 * Copy the salt from the SA and the IV from the packet.
7838628b0c67SMark Fenwick 	 * For inbound packets we copy the IV from the packet because it
7839628b0c67SMark Fenwick 	 * was set by the sending system, for outbound packets we copy the IV
7840628b0c67SMark Fenwick 	 * from the packet because the IV in the SA may be changed by another
7841628b0c67SMark Fenwick 	 * thread, the IV in the packet was created while holding a mutex.
7842628b0c67SMark Fenwick 	 */
7843628b0c67SMark Fenwick 	bcopy(assoc->ipsa_nonce, nonce, assoc->ipsa_saltlen);
7844628b0c67SMark Fenwick 	nonce += assoc->ipsa_saltlen;
7845628b0c67SMark Fenwick 	bcopy(iv_ptr, nonce, assoc->ipsa_iv_len);
7846628b0c67SMark Fenwick 	crypto_data->cd_miscdata = NULL;
7847628b0c67SMark Fenwick }
7848