--- old/usr/src/pkg/manifests/system-test-ostest.mf	Wed Jul 26 12:09:29 2017
+++ new/usr/src/pkg/manifests/system-test-ostest.mf	Wed Jul 26 12:09:29 2017
@@ -12,7 +12,7 @@
 #
 # Copyright (c) 2012, 2016 by Delphix. All rights reserved.
 # Copyright 2014, OmniTI Computer Consulting, Inc. All rights reserved.
-# Copyright 2016, Joyent, Inc.
+# Copyright 2017 Joyent, Inc.
 #
 
 set name=pkg.fmri value=pkg:/system/test/ostest@$(PKGVERS)
@@ -26,6 +26,7 @@
 dir path=opt/os-tests/runfiles
 dir path=opt/os-tests/tests
 dir path=opt/os-tests/tests/file-locking
+dir path=opt/os-tests/tests/pf_key
 dir path=opt/os-tests/tests/sdevfs
 dir path=opt/os-tests/tests/secflags
 dir path=opt/os-tests/tests/sigqueue
@@ -39,6 +40,9 @@
 file path=opt/os-tests/tests/file-locking/acquire-lock.64 mode=0555
 file path=opt/os-tests/tests/file-locking/runtests.32 mode=0555
 file path=opt/os-tests/tests/file-locking/runtests.64 mode=0555
+file path=opt/os-tests/tests/pf_key/acquire-compare mode=0555
+file path=opt/os-tests/tests/pf_key/acquire-spray mode=0555
+file path=opt/os-tests/tests/pf_key/eacq-enabler mode=0555
 file path=opt/os-tests/tests/poll_test mode=0555
 file path=opt/os-tests/tests/sdevfs/sdevfs_eisdir mode=0555
 file path=opt/os-tests/tests/secflags/addrs-32 mode=0555
--- old/usr/src/test/os-tests/runfiles/default.run	Wed Jul 26 12:09:29 2017
+++ new/usr/src/test/os-tests/runfiles/default.run	Wed Jul 26 12:09:29 2017
@@ -11,7 +11,7 @@
 
 #
 # Copyright (c) 2012 by Delphix. All rights reserved.
-# Copyright 2016 Joyent, Inc.
+# Copyright 2017 Joyent, Inc.
 #
 
 [DEFAULT]
@@ -58,3 +58,7 @@
 [/opt/os-tests/tests/sockfs]
 user = root
 tests = ['conn', 'dgram', 'drop_priv', 'nosignal', 'sockpair']
+
+[/opt/os-tests/tests/pf_key]
+user = root
+tests = ['acquire-compare', 'acquire-spray']
--- old/usr/src/test/os-tests/tests/Makefile	Wed Jul 26 12:09:29 2017
+++ new/usr/src/test/os-tests/tests/Makefile	Wed Jul 26 12:09:29 2017
@@ -11,8 +11,10 @@
 
 #
 # Copyright (c) 2012, 2016 by Delphix. All rights reserved.
+# Copyright 2017 Joyent, Inc.
 #
 
-SUBDIRS = poll secflags sigqueue spoof-ras sdevfs sockfs stress file-locking
+SUBDIRS = poll secflags sigqueue spoof-ras sdevfs sockfs stress file-locking \
+	pf_key
 
 include $(SRC)/test/Makefile.com
--- /dev/null	Wed Jul 26 12:09:29 2017
+++ new/usr/src/test/os-tests/tests/pf_key/Makefile	Wed Jul 26 12:09:29 2017
@@ -0,0 +1,55 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source.  A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2017 Joyent, Inc.
+#
+
+include $(SRC)/cmd/Makefile.cmd
+include $(SRC)/test/Makefile.com
+
+# Shell scripts...
+PROG =	acquire-spray \
+	acquire-compare
+
+PROG += eacq-enabler
+
+ROOTOPTPKG = $(ROOT)/opt/os-tests
+TESTDIR = $(ROOTOPTPKG)/tests/pf_key
+
+CMDS = $(PROG:%=$(TESTDIR)/%)
+$(CMDS) := FILEMODE = 0555
+
+LDLIBS += -lsocket
+
+eacq-enabler:
+	$(LINK.c) eacq-enabler.c -o $@ $(LDLIBS)
+	$(POST_PROCESS)
+
+all: $(PROG)
+
+install: all $(CMDS)
+
+lint:
+
+clobber: clean
+	-$(RM) $(PROG)
+
+clean:
+	-$(RM) $(CLEANFILES)
+
+$(CMDS): $(TESTDIR) $(PROG)
+
+$(TESTDIR):
+	$(INS.dir)
+
+$(TESTDIR)/%: %
+	$(INS.file)
--- /dev/null	Wed Jul 26 12:09:29 2017
+++ new/usr/src/test/os-tests/tests/pf_key/acquire-compare.sh	Wed Jul 26 12:09:29 2017
@@ -0,0 +1,170 @@
+#!/usr/bin/ksh
+
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source.  A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2017 Joyent, Inc.
+#
+
+if [ `id -u` -ne 0 ]; then
+	echo "Need to be root or have effective UID of root."
+	exit 255
+fi
+
+# NOTE: If multihomed, this may fail in interesting ways...
+MY_IP=`netstat -in -f inet | egrep -v "Name|lo0" | awk '{print $4}' | head -1`
+TEST_REMOTE_DST1=10.90.1.25
+TEST_REMOTE_DST2=10.19.84.2
+TEST_REMOTE_DST3=10.19.84.3
+TEST_REMOTE_DST4=10.19.84.4
+
+T1_SRC=10.21.12.4
+T1_DST=10.21.12.5
+T1_PREFIX=10.21.12.0/24
+T2_SRC=10.51.50.4
+T2_DST=10.51.50.5
+T2_PREFIX=10.51.50.0/24
+
+MONITOR_LOG=/tmp/ipseckey-monitor.$$
+
+EACQ_PROG=/opt/os-tests/tests/pf_key/eacq-enabler
+
+$EACQ_PROG &
+eapid=$!
+
+echo "Warning, this trashes IPsec policy."
+ipsecconf -Fq
+
+# Setup the IPsec policy...
+ipsecconf -qa - << EOF
+# Global policy...
+# Remote-port-based policy.  Use different algorithms...
+{ raddr $TEST_REMOTE_DST3 rport 23 ulp tcp } ipsec { encr_algs aes encr_auth_algs sha512 }
+
+# Unique policy...
+{ raddr $TEST_REMOTE_DST4 rport 23 ulp tcp } ipsec { encr_algs aes encr_auth_algs sha256 sa unique }
+
+# Simple IP address policy.  Use an AH + ESP for it.
+{ raddr $TEST_REMOTE_DST1 } ipsec { auth_algs sha512 encr_algs aes(256) }
+{ raddr $TEST_REMOTE_DST2 } ipsec { auth_algs sha384 encr_algs aes(256) }
+
+# Tunnel policy...
+{ tunnel rush0 raddr $T1_PREFIX negotiate tunnel } ipsec { encr_algs aes-gcm(256) }
+# NULL-encryption...
+{ tunnel vh0 raddr $T2_PREFIX negotiate tunnel } ipsec {encr_auth_algs hmac-sha384 }
+EOF
+
+# Plumb the tunnels
+dladm create-iptun -t -T ipv4 -a local=$MY_IP -a remote=$TEST_REMOTE_DST1 rush0
+dladm create-iptun -t -T ipv4 -a local=$MY_IP -a remote=$TEST_REMOTE_DST2 vh0
+ipadm create-addr -t -T static -a local=$T1_SRC,remote=$T1_DST rush0/v4
+ipadm create-addr -t -T static -a local=$T2_SRC,remote=$T2_DST vh0/v4
+route add $T1_PREFIX $T1_DST
+route add $T2_PREFIX $T2_DST
+
+ipseckey flush
+ipseckey -np monitor > $MONITOR_LOG &
+IPSECKEY_PID=$!
+
+# Launch pings and telnets to different addresses (each requiring an ACQUIRE).
+ping -svn $TEST_REMOTE_DST1 1024 1 2>&1 > /dev/null &
+p1=$!
+ping -svn $TEST_REMOTE_DST2 1024 1 2>&1 > /dev/null &
+p2=$!
+ping -svn $T1_DST 1024 1 2>&1 > /dev/null &
+p3=$!
+ping -svn $T2_DST 1024 1 2>&1 > /dev/null &
+p4=$!
+
+echo "Waiting for pings..."
+pwait $p1 $p2 $p3 $p4
+
+# Now try some telnets to trigger port and unique policy.
+# port-only for DST3
+telnet $TEST_REMOTE_DST3 &
+tpid=$!
+t1port=`pfiles $tpid | grep sockname | awk '{print $5}'`
+echo "First local port == $t1port"
+sleep 10 ; kill $tpid
+# unique for DST4
+telnet $TEST_REMOTE_DST4 &
+tpid=$!
+t2port=`pfiles $tpid | grep sockname | awk '{print $5}'`
+echo "Second local port == $t2port"
+sleep 10 ; kill $tpid
+# Nothing specced for DST1
+telnet $TEST_REMOTE_DST1 &
+tpid=$!
+t3port=`pfiles $tpid | grep sockname | awk '{print $5}'`
+echo "Third local port == $t3port"
+sleep 10 ; kill $tpid
+
+# Clean up.
+kill $IPSECKEY_PID
+kill $eapid
+# Unplumb the tunnels
+route delete $T2_PREFIX $T2_DST
+route delete $T1_PREFIX $T1_DST
+ipadm delete-addr vh0/v4
+ipadm delete-addr rush0/v4
+ipadm delete-if vh0
+ipadm delete-if rush0
+dladm delete-iptun vh0
+dladm delete-iptun rush0
+# Flush policy
+ipsecconf -Fq
+# Use SMF to restore anything that may have been there.  "restart" on
+# a disabled service is a NOP, but an enabled one will get
+# /etc/inet/ipsecinit.conf reloaded.
+svcadm restart ipsec/policy
+
+# Process MONITOR_LOG's output...
+echo "Checking for unique local port only in one ACQUIRE case."
+egrep "$t1port|$t2port|$t3port" $MONITOR_LOG > /tmp/egrep.$$
+grep $t2port $MONITOR_LOG > /tmp/grep.$$
+diff /tmp/grep.$$ /tmp/egrep.$$
+if [[ $? != 0 ]]; then
+    echo "More than just the one unique port, $tport2, found in monitor output."
+    /bin/rm -f /tmp/grep.$$ /tmp/egrep.$$ $MONITOR_LOG
+    exit 1
+fi
+
+# Split out extended (file.0) and regular (file.1) ACQUIREs.
+# NOTE: "+7" is dependent on "ipseckey monitor"'s first output where it gets
+# the "PROMISC" reply.
+
+mkdir /tmp/raw.$$
+savedir=$PWD
+cd /tmp/raw.$$
+tail +7 $MONITOR_LOG | \
+    awk 'BEGIN { out=0; } /Read/ {out++;} { print >> (out % 2) }'
+cd $savedir
+
+# Pluck out the address extension from the two ACQUIRE types.
+# NOTE: Add any new in-ACQUIRE address types here if more arrive.
+egrep "DST:|SRC:|INS:|IND:" /tmp/raw.$$/0 > /tmp/extended-addresses.$$
+egrep "DST:|SRC:|INS:|IND:" /tmp/raw.$$/1 > /tmp/regular-addresses.$$
+
+# There should be NO differences between address fields from regular vs.
+# extended ACQUIREs. If there are, it's a bug (or an older version of illumos).
+diff /tmp/extended-addresses.$$ /tmp/regular-addresses.$$
+if [[ $? != 0 ]]; then
+    echo "Address fields in ACQUIRE differ."
+    rc=1
+else
+    rc=0
+fi
+
+/bin/rm -rf /tmp/*-addresses.$$ /tmp/raw.$$
+/bin/rm -f /tmp/grep.$$ /tmp/egrep.$$ /tmp/addrs.$$ $MONITOR_LOG
+
+exit $rc
--- /dev/null	Wed Jul 26 12:09:29 2017
+++ new/usr/src/test/os-tests/tests/pf_key/acquire-spray.sh	Wed Jul 26 12:09:29 2017
@@ -0,0 +1,99 @@
+#!/usr/bin/ksh
+
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source.  A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2017 Joyent, Inc.
+#
+
+if [ `id -u` -ne 0 ]; then
+	echo "Need to be root or have effective UID of root."
+	exit 255
+fi
+
+if [[ `zonename` != "global" ]]; then
+	echo "Need to be the in the global zone for lock detection."
+	exit 254
+fi
+
+# This test sprays many concurrent ACQUIRE messages.  The idea originally
+# was to view lock contention on the global netstack's IPsec algorithm lock.
+# It is also useful for having multiple ACQUIRE records.
+
+PREFIX=10.21.12.0/24
+MONITOR_LOG=/var/run/ipseckey-monitor.$$
+
+# The program that sends an extended REGISTER to enable extended ACQUIREs.
+EACQ_PROG=/opt/os-tests/tests/pf_key/eacq-enabler
+
+$EACQ_PROG &
+eapid=$!
+
+# Find the ipsec_alg_lock to monitor with lockstat (below).
+GLOBAL_NETSTACK=`echo ::netstack | mdb -k | grep -w 0 | awk '{print $1}'`
+GLOBAL_IPSEC=`echo $GLOBAL_NETSTACK::print netstack_t | mdb -k | grep -w nu_ipsec | awk '{print $3}'`
+IPSEC_ALG_LOCK=`echo $GLOBAL_IPSEC::print -a ipsec_stack_t ipsec_alg_lock | mdb -k | head -1 | awk '{print $1}'`
+
+#echo "WARNING -- this test flushes out IPsec policy..."
+#echo "GLOBAL_NETSTACK = $GLOBAL_NETSTACK"
+#echo "GLOBAL_IPSEC = $GLOBAL_IPSEC"
+#echo "IPSEC_ALG_LOCK = $IPSEC_ALG_LOCK"
+
+# Tunnels will be preserved by using -f instead of -F.
+ipsecconf -qf
+
+# Simple one-type-of-ESP setup...
+echo "{ raddr $PREFIX } ipsec { encr_algs aes encr_auth_algs sha512 }" | \
+	ipsecconf -qa -
+# ipsecconf -ln
+
+# Get monitoring PF_KEY for at least regular ACQUIREs.
+ipseckey -n monitor > $MONITOR_LOG &
+IPSECKEY_PID=$!
+
+# Flush out the SADB to make damned sure we don't have straggler acquire
+# records internally.
+ipseckey flush
+
+# Launch 254 pings to different addresses (each requiring an ACQUIRE).
+i=1
+while [ $i -le 254 ]; do
+	truss -Topen -o /dev/null ping -svn 10.21.12.$i 1024 1 2>&1 > /dev/null &
+	i=$(($i + 1))
+done
+
+# Unleash the pings in 10 seconds, Smithers.
+( sleep 10 ; prun `pgrep ping` ) &
+
+# Get the lockstats going now.
+echo "Running:     lockstat -A -l 0x$IPSEC_ALG_LOCK,8 sleep 30"
+lockstat -A -l 0x$IPSEC_ALG_LOCK,8 sleep 30
+kill $IPSECKEY_PID
+kill $eapid
+# Use SMF to restore anything that may have been there.  "restart" on
+# a disabled service is a NOP, but an enabled one will get
+# /etc/inet/ipsecinit.conf reloaded.
+svcadm restart ipsec/policy
+
+# See if we have decent results.
+
+numacq=`grep ACQUIRE $MONITOR_LOG | wc -l | awk '{print $1}`
+#rm -f $MONITOR_LOG
+# Pardon the hardcoding again.
+if [[ $numacq != 508 ]]; then
+    echo "Got $numacq ACQUIREs instead of 508"
+    exit 1
+else
+    echo "Saw expected $numacq ACQUIREs."
+fi
+
+exit 0
--- /dev/null	Wed Jul 26 12:09:29 2017
+++ new/usr/src/test/os-tests/tests/pf_key/eacq-enabler.c	Wed Jul 26 12:09:29 2017
@@ -0,0 +1,111 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright (c) 2017 Joyent, Inc.
+ */
+
+/*
+ * Designed to be backgrounded and just killed. Open a PF_KEY socket, do
+ * an extended-REGISTER so the kernel will send extended-ACQUIRE messages,
+ * and then read-and-discard everything off the socket.
+ */
+
+#include <sys/socket.h>
+#include <net/pfkeyv2.h>
+#include <stdio.h>
+#include <errno.h>
+#include <err.h>
+#include <unistd.h>
+
+/* ARGSUSED */
+int
+main(int argc, char *argv[])
+{
+	int s, rc;
+	uint64_t buf[1024];	/* PF_KEY likes 64-bit alignment. */
+	sadb_msg_t *samsg;
+	sadb_x_ereg_t *ereg;
+	boolean_t ah_ack, esp_ack;
+	pid_t pid = getpid();
+
+	s = socket(PF_KEY, SOCK_RAW, PF_KEY_V2);
+	if (s == -1)
+		err(-1, "socket(PF_KEY)");
+
+	/* Base message. */
+	samsg = (sadb_msg_t *)buf;
+	ereg = (sadb_x_ereg_t *)(samsg + 1);
+	samsg->sadb_msg_version = PF_KEY_V2;
+	samsg->sadb_msg_type = SADB_REGISTER;
+	samsg->sadb_msg_errno = 0;
+	samsg->sadb_msg_satype = SADB_SATYPE_UNSPEC;
+	samsg->sadb_msg_reserved = 0;
+	samsg->sadb_msg_seq = 1;
+	samsg->sadb_msg_pid = pid;
+	samsg->sadb_msg_len = SADB_8TO64(sizeof (*samsg) + sizeof (*ereg));
+
+	/* extended REGISTER so we can listen for extended ACQUIREs. */
+	ereg->sadb_x_ereg_len = SADB_8TO64(sizeof (*ereg));
+	ereg->sadb_x_ereg_exttype = SADB_X_EXT_EREG;
+	ereg->sadb_x_ereg_satypes[0] = SADB_SATYPE_ESP;
+	ereg->sadb_x_ereg_satypes[1] = SADB_SATYPE_AH;
+	ereg->sadb_x_ereg_satypes[2] = SADB_SATYPE_UNSPEC;
+
+	rc = write(s, buf, sizeof (*samsg) + sizeof (*ereg));
+	if (rc == -1)
+		err(-1, "Extended register write error");
+
+	/*
+	 * Extended REGISTER expects a regular REGISTER reply for EACH protocol
+	 * requested.  In our case, AH and ESP.
+	 */
+	do {
+
+		do {
+			rc = read(s, buf, sizeof (buf));
+			if (rc == -1)
+				err(-1, "Extended register read error");
+
+		} while (samsg->sadb_msg_seq != 1 ||
+		    samsg->sadb_msg_pid != pid ||
+		    samsg->sadb_msg_type != SADB_REGISTER);
+
+		if (samsg->sadb_msg_errno != 0) {
+			if (samsg->sadb_msg_errno == EPROTONOSUPPORT) {
+				warn("Protocol %d not supported.",
+				    samsg->sadb_msg_satype);
+			} else {
+				errno = samsg->sadb_msg_errno;
+				err(-1, "Extended REGISTER returned");
+			}
+		}
+
+		switch (samsg->sadb_msg_satype) {
+		case SADB_SATYPE_ESP:
+			esp_ack = B_TRUE;
+			break;
+		case SADB_SATYPE_AH:
+			ah_ack = B_TRUE;
+			break;
+		default:
+			err(-1, "Bad satype in extended register ACK %d.",
+			    samsg->sadb_msg_satype);
+		}
+	} while (!esp_ack || !ah_ack);
+
+	/* Expect this loop to never end. This program ends via signal. */
+	do {
+		rc = read(s, buf, sizeof (buf));
+	} while (rc != -1);
+
+	err(-1, "PF_KEY read error");
+}
--- old/usr/src/uts/common/inet/ip/ipsecah.c	Wed Jul 26 12:09:29 2017
+++ new/usr/src/uts/common/inet/ip/ipsecah.c	Wed Jul 26 12:09:29 2017
@@ -96,20 +96,6 @@
 	{ 0,	0xffffffffU,		0,	"ipsecah_default_hard_usetime"},
 	{ 0,	1,			0,	"ipsecah_log_unknown_spi"},
 };
-#define	ipsecah_debug			ipsecah_params[0].ipsecah_param_value
-#define	ipsecah_age_interval		ipsecah_params[1].ipsecah_param_value
-#define	ipsecah_age_int_max		ipsecah_params[1].ipsecah_param_max
-#define	ipsecah_reap_delay		ipsecah_params[2].ipsecah_param_value
-#define	ipsecah_replay_size		ipsecah_params[3].ipsecah_param_value
-#define	ipsecah_acquire_timeout		ipsecah_params[4].ipsecah_param_value
-#define	ipsecah_larval_timeout		ipsecah_params[5].ipsecah_param_value
-#define	ipsecah_default_soft_bytes	ipsecah_params[6].ipsecah_param_value
-#define	ipsecah_default_hard_bytes	ipsecah_params[7].ipsecah_param_value
-#define	ipsecah_default_soft_addtime	ipsecah_params[8].ipsecah_param_value
-#define	ipsecah_default_hard_addtime	ipsecah_params[9].ipsecah_param_value
-#define	ipsecah_default_soft_usetime	ipsecah_params[10].ipsecah_param_value
-#define	ipsecah_default_hard_usetime	ipsecah_params[11].ipsecah_param_value
-#define	ipsecah_log_unknown_spi		ipsecah_params[12].ipsecah_param_value
 
 #define	ah0dbg(a)	printf a
 /* NOTE:  != 0 instead of > 0 so lint doesn't complain. */
@@ -149,7 +135,6 @@
 static int ipsecah_open(queue_t *, dev_t *, int, int, cred_t *);
 static int ipsecah_close(queue_t *);
 static void ipsecah_wput(queue_t *, mblk_t *);
-static void ah_send_acquire(ipsacq_t *, mblk_t *, netstack_t *);
 static boolean_t ah_register_out(uint32_t, uint32_t, uint_t, ipsecah_stack_t *,
     cred_t *);
 static void	*ipsecah_stack_init(netstackid_t stackid, netstack_t *ns);
@@ -420,7 +405,6 @@
 	(void) ah_kstat_init(ahstack, stackid);
 
 	ahstack->ah_sadb.s_acquire_timeout = &ahstack->ipsecah_acquire_timeout;
-	ahstack->ah_sadb.s_acqfn = ah_send_acquire;
 	sadbp_init("AH", &ahstack->ah_sadb, SADB_SATYPE_AH, ah_hash_size,
 	    ahstack->ipsecah_netstack);
 
@@ -451,7 +435,6 @@
 	if (ahstack->ah_pfkey_q != NULL) {
 		(void) quntimeout(ahstack->ah_pfkey_q, ahstack->ah_event);
 	}
-	ahstack->ah_sadb.s_acqfn = NULL;
 	ahstack->ah_sadb.s_acquire_timeout = NULL;
 	sadbp_destroy(&ahstack->ah_sadb, ahstack->ipsecah_netstack);
 	ip_drop_unregister(&ahstack->ah_dropper);
@@ -1736,160 +1719,6 @@
 	return (inrc && outrc);
 }
 
-/*
- * Perform the really difficult work of inserting the proposed situation.
- * Called while holding the algorithm lock.
- */
-static void
-ah_insert_prop(sadb_prop_t *prop, ipsacq_t *acqrec, uint_t combs,
-    netstack_t *ns)
-{
-	sadb_comb_t *comb = (sadb_comb_t *)(prop + 1);
-	ipsec_action_t *ap;
-	ipsec_prot_t *prot;
-	ipsecah_stack_t	*ahstack = ns->netstack_ipsecah;
-	ipsec_stack_t	*ipss = ns->netstack_ipsec;
-
-	ASSERT(RW_READ_HELD(&ipss->ipsec_alg_lock));
-
-	prop->sadb_prop_exttype = SADB_EXT_PROPOSAL;
-	prop->sadb_prop_len = SADB_8TO64(sizeof (sadb_prop_t));
-	*(uint32_t *)(&prop->sadb_prop_replay) = 0;	/* Quick zero-out! */
-
-	prop->sadb_prop_replay = ahstack->ipsecah_replay_size;
-
-	/*
-	 * Based upon algorithm properties, and what-not, prioritize a
-	 * proposal, based on the ordering of the AH algorithms in the
-	 * alternatives in the policy rule or socket that was placed
-	 * in the acquire record.
-	 */
-
-	for (ap = acqrec->ipsacq_act; ap != NULL;
-	    ap = ap->ipa_next) {
-		ipsec_alginfo_t *aalg;
-
-		if ((ap->ipa_act.ipa_type != IPSEC_POLICY_APPLY) ||
-		    (!ap->ipa_act.ipa_apply.ipp_use_ah))
-			continue;
-
-		prot = &ap->ipa_act.ipa_apply;
-
-		ASSERT(prot->ipp_auth_alg > 0);
-
-		aalg = ipss->ipsec_alglists[IPSEC_ALG_AUTH]
-		    [prot->ipp_auth_alg];
-		if (aalg == NULL || !ALG_VALID(aalg))
-			continue;
-
-		/* XXX check aalg for duplicates??.. */
-
-		comb->sadb_comb_flags = 0;
-		comb->sadb_comb_reserved = 0;
-		comb->sadb_comb_encrypt = 0;
-		comb->sadb_comb_encrypt_minbits = 0;
-		comb->sadb_comb_encrypt_maxbits = 0;
-
-		comb->sadb_comb_auth = aalg->alg_id;
-		comb->sadb_comb_auth_minbits =
-		    MAX(prot->ipp_ah_minbits, aalg->alg_ef_minbits);
-		comb->sadb_comb_auth_maxbits =
-		    MIN(prot->ipp_ah_maxbits, aalg->alg_ef_maxbits);
-
-		/*
-		 * The following may be based on algorithm
-		 * properties, but in the meantime, we just pick
-		 * some good, sensible numbers.  Key mgmt. can
-		 * (and perhaps should) be the place to finalize
-		 * such decisions.
-		 */
-
-		/*
-		 * No limits on allocations, since we really don't
-		 * support that concept currently.
-		 */
-		comb->sadb_comb_soft_allocations = 0;
-		comb->sadb_comb_hard_allocations = 0;
-
-		/*
-		 * These may want to come from policy rule..
-		 */
-		comb->sadb_comb_soft_bytes =
-		    ahstack->ipsecah_default_soft_bytes;
-		comb->sadb_comb_hard_bytes =
-		    ahstack->ipsecah_default_hard_bytes;
-		comb->sadb_comb_soft_addtime =
-		    ahstack->ipsecah_default_soft_addtime;
-		comb->sadb_comb_hard_addtime =
-		    ahstack->ipsecah_default_hard_addtime;
-		comb->sadb_comb_soft_usetime =
-		    ahstack->ipsecah_default_soft_usetime;
-		comb->sadb_comb_hard_usetime =
-		    ahstack->ipsecah_default_hard_usetime;
-
-		prop->sadb_prop_len += SADB_8TO64(sizeof (*comb));
-		if (--combs == 0)
-			return;	/* out of space.. */
-		comb++;
-	}
-}
-
-/*
- * Prepare and actually send the SADB_ACQUIRE message to PF_KEY.
- */
-static void
-ah_send_acquire(ipsacq_t *acqrec, mblk_t *extended, netstack_t *ns)
-{
-	uint_t combs;
-	sadb_msg_t *samsg;
-	sadb_prop_t *prop;
-	mblk_t *pfkeymp, *msgmp;
-	ipsecah_stack_t	*ahstack = ns->netstack_ipsecah;
-	ipsec_stack_t	*ipss = ns->netstack_ipsec;
-
-	AH_BUMP_STAT(ahstack, acquire_requests);
-
-	if (ahstack->ah_pfkey_q == NULL) {
-		mutex_exit(&acqrec->ipsacq_lock);
-		return;
-	}
-
-	/* Set up ACQUIRE. */
-	pfkeymp = sadb_setup_acquire(acqrec, SADB_SATYPE_AH,
-	    ns->netstack_ipsec);
-	if (pfkeymp == NULL) {
-		ah0dbg(("sadb_setup_acquire failed.\n"));
-		mutex_exit(&acqrec->ipsacq_lock);
-		return;
-	}
-	ASSERT(RW_READ_HELD(&ipss->ipsec_alg_lock));
-	combs = ipss->ipsec_nalgs[IPSEC_ALG_AUTH];
-	msgmp = pfkeymp->b_cont;
-	samsg = (sadb_msg_t *)(msgmp->b_rptr);
-
-	/* Insert proposal here. */
-
-	prop = (sadb_prop_t *)(((uint64_t *)samsg) + samsg->sadb_msg_len);
-	ah_insert_prop(prop, acqrec, combs, ns);
-	samsg->sadb_msg_len += prop->sadb_prop_len;
-	msgmp->b_wptr += SADB_64TO8(samsg->sadb_msg_len);
-
-	rw_exit(&ipss->ipsec_alg_lock);
-
-	/*
-	 * Must mutex_exit() before sending PF_KEY message up, in
-	 * order to avoid recursive mutex_enter() if there are no registered
-	 * listeners.
-	 *
-	 * Once I've sent the message, I'm cool anyway.
-	 */
-	mutex_exit(&acqrec->ipsacq_lock);
-	if (extended != NULL) {
-		putnext(ahstack->ah_pfkey_q, extended);
-	}
-	putnext(ahstack->ah_pfkey_q, pfkeymp);
-}
-
 /* Refactor me */
 /*
  * Handle the SADB_GETSPI message.  Create a larval SA.
--- old/usr/src/uts/common/inet/ip/ipsecesp.c	Wed Jul 26 12:09:29 2017
+++ new/usr/src/uts/common/inet/ip/ipsecesp.c	Wed Jul 26 12:09:29 2017
@@ -21,6 +21,8 @@
 /*
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright (c) 2012 Nexenta Systems, Inc. All rights reserved.
+ * Copyright (c) 2017 Joyent, Inc.
  */
 
 #include <sys/types.h>
@@ -95,31 +97,6 @@
 	{ 0,	2,		1,	"ipsecesp_padding_check"},
 	{ 0,	600,		20,	"ipsecesp_nat_keepalive_interval"},
 };
-#define	ipsecesp_debug	ipsecesp_params[0].ipsecesp_param_value
-#define	ipsecesp_age_interval ipsecesp_params[1].ipsecesp_param_value
-#define	ipsecesp_age_int_max	ipsecesp_params[1].ipsecesp_param_max
-#define	ipsecesp_reap_delay	ipsecesp_params[2].ipsecesp_param_value
-#define	ipsecesp_replay_size	ipsecesp_params[3].ipsecesp_param_value
-#define	ipsecesp_acquire_timeout	\
-	ipsecesp_params[4].ipsecesp_param_value
-#define	ipsecesp_larval_timeout	\
-	ipsecesp_params[5].ipsecesp_param_value
-#define	ipsecesp_default_soft_bytes	\
-	ipsecesp_params[6].ipsecesp_param_value
-#define	ipsecesp_default_hard_bytes	\
-	ipsecesp_params[7].ipsecesp_param_value
-#define	ipsecesp_default_soft_addtime	\
-	ipsecesp_params[8].ipsecesp_param_value
-#define	ipsecesp_default_hard_addtime	\
-	ipsecesp_params[9].ipsecesp_param_value
-#define	ipsecesp_default_soft_usetime	\
-	ipsecesp_params[10].ipsecesp_param_value
-#define	ipsecesp_default_hard_usetime	\
-	ipsecesp_params[11].ipsecesp_param_value
-#define	ipsecesp_log_unknown_spi	\
-	ipsecesp_params[12].ipsecesp_param_value
-#define	ipsecesp_padding_check	\
-	ipsecesp_params[13].ipsecesp_param_value
 /* For ipsecesp_nat_keepalive_interval, see ipsecesp.h. */
 
 #define	esp0dbg(a)	printf a
@@ -133,7 +110,6 @@
 static void ipsecesp_wput(queue_t *, mblk_t *);
 static void	*ipsecesp_stack_init(netstackid_t stackid, netstack_t *ns);
 static void	ipsecesp_stack_fini(netstackid_t stackid, void *arg);
-static void esp_send_acquire(ipsacq_t *, mblk_t *, netstack_t *);
 
 static void esp_prepare_udp(netstack_t *, mblk_t *, ipha_t *);
 static void esp_outbound_finish(mblk_t *, ip_xmit_attr_t *);
@@ -181,52 +157,6 @@
  *		IPPROTO_ESP
  */
 
-/*
- * Stats.  This may eventually become a full-blown SNMP MIB once that spec
- * stabilizes.
- */
-
-typedef struct esp_kstats_s {
-	kstat_named_t esp_stat_num_aalgs;
-	kstat_named_t esp_stat_good_auth;
-	kstat_named_t esp_stat_bad_auth;
-	kstat_named_t esp_stat_bad_padding;
-	kstat_named_t esp_stat_replay_failures;
-	kstat_named_t esp_stat_replay_early_failures;
-	kstat_named_t esp_stat_keysock_in;
-	kstat_named_t esp_stat_out_requests;
-	kstat_named_t esp_stat_acquire_requests;
-	kstat_named_t esp_stat_bytes_expired;
-	kstat_named_t esp_stat_out_discards;
-	kstat_named_t esp_stat_crypto_sync;
-	kstat_named_t esp_stat_crypto_async;
-	kstat_named_t esp_stat_crypto_failures;
-	kstat_named_t esp_stat_num_ealgs;
-	kstat_named_t esp_stat_bad_decrypt;
-	kstat_named_t esp_stat_sa_port_renumbers;
-} esp_kstats_t;
-
-/*
- * espstack->esp_kstats is equal to espstack->esp_ksp->ks_data if
- * kstat_create_netstack for espstack->esp_ksp succeeds, but when it
- * fails, it will be NULL. Note this is done for all stack instances,
- * so it *could* fail. hence a non-NULL checking is done for
- * ESP_BUMP_STAT and ESP_DEBUMP_STAT
- */
-#define	ESP_BUMP_STAT(espstack, x)					\
-do {									\
-	if (espstack->esp_kstats != NULL)				\
-		(espstack->esp_kstats->esp_stat_ ## x).value.ui64++;	\
-_NOTE(CONSTCOND)							\
-} while (0)
-
-#define	ESP_DEBUMP_STAT(espstack, x)					\
-do {									\
-	if (espstack->esp_kstats != NULL)				\
-		(espstack->esp_kstats->esp_stat_ ## x).value.ui64--;	\
-_NOTE(CONSTCOND)							\
-} while (0)
-
 static int	esp_kstat_update(kstat_t *, int);
 
 static boolean_t
@@ -503,6 +433,7 @@
 	}
 	return (B_TRUE);
 }
+
 /*
  * Initialize things for ESP for each stack instance
  */
@@ -527,7 +458,6 @@
 
 	espstack->esp_sadb.s_acquire_timeout =
 	    &espstack->ipsecesp_acquire_timeout;
-	espstack->esp_sadb.s_acqfn = esp_send_acquire;
 	sadbp_init("ESP", &espstack->esp_sadb, SADB_SATYPE_ESP, esp_hash_size,
 	    espstack->ipsecesp_netstack);
 
@@ -558,7 +488,6 @@
 	if (espstack->esp_pfkey_q != NULL) {
 		(void) quntimeout(espstack->esp_pfkey_q, espstack->esp_event);
 	}
-	espstack->esp_sadb.s_acqfn = NULL;
 	espstack->esp_sadb.s_acquire_timeout = NULL;
 	sadbp_destroy(&espstack->esp_sadb, espstack->ipsecesp_netstack);
 	ip_drop_unregister(&espstack->esp_dropper);
@@ -1181,179 +1110,6 @@
 	    (uint8_t *)esph - data_mp->b_rptr));
 }
 
-/*
- * Perform the really difficult work of inserting the proposed situation.
- * Called while holding the algorithm lock.
- */
-static void
-esp_insert_prop(sadb_prop_t *prop, ipsacq_t *acqrec, uint_t combs,
-    netstack_t *ns)
-{
-	sadb_comb_t *comb = (sadb_comb_t *)(prop + 1);
-	ipsec_action_t *ap;
-	ipsec_prot_t *prot;
-	ipsecesp_stack_t *espstack = ns->netstack_ipsecesp;
-	ipsec_stack_t	*ipss = ns->netstack_ipsec;
-
-	ASSERT(RW_READ_HELD(&ipss->ipsec_alg_lock));
-
-	prop->sadb_prop_exttype = SADB_EXT_PROPOSAL;
-	prop->sadb_prop_len = SADB_8TO64(sizeof (sadb_prop_t));
-	*(uint32_t *)(&prop->sadb_prop_replay) = 0;	/* Quick zero-out! */
-
-	prop->sadb_prop_replay = espstack->ipsecesp_replay_size;
-
-	/*
-	 * Based upon algorithm properties, and what-not, prioritize a
-	 * proposal, based on the ordering of the ESP algorithms in the
-	 * alternatives in the policy rule or socket that was placed
-	 * in the acquire record.
-	 *
-	 * For each action in policy list
-	 *   Add combination.  If I've hit limit, return.
-	 */
-
-	for (ap = acqrec->ipsacq_act; ap != NULL;
-	    ap = ap->ipa_next) {
-		ipsec_alginfo_t *ealg = NULL;
-		ipsec_alginfo_t *aalg = NULL;
-
-		if (ap->ipa_act.ipa_type != IPSEC_POLICY_APPLY)
-			continue;
-
-		prot = &ap->ipa_act.ipa_apply;
-
-		if (!(prot->ipp_use_esp))
-			continue;
-
-		if (prot->ipp_esp_auth_alg != 0) {
-			aalg = ipss->ipsec_alglists[IPSEC_ALG_AUTH]
-			    [prot->ipp_esp_auth_alg];
-			if (aalg == NULL || !ALG_VALID(aalg))
-				continue;
-		}
-
-		ASSERT(prot->ipp_encr_alg > 0);
-		ealg = ipss->ipsec_alglists[IPSEC_ALG_ENCR]
-		    [prot->ipp_encr_alg];
-		if (ealg == NULL || !ALG_VALID(ealg))
-			continue;
-
-		comb->sadb_comb_flags = 0;
-		comb->sadb_comb_reserved = 0;
-		comb->sadb_comb_encrypt = ealg->alg_id;
-		comb->sadb_comb_encrypt_minbits =
-		    MAX(prot->ipp_espe_minbits, ealg->alg_ef_minbits);
-		comb->sadb_comb_encrypt_maxbits =
-		    MIN(prot->ipp_espe_maxbits, ealg->alg_ef_maxbits);
-
-		if (aalg == NULL) {
-			comb->sadb_comb_auth = 0;
-			comb->sadb_comb_auth_minbits = 0;
-			comb->sadb_comb_auth_maxbits = 0;
-		} else {
-			comb->sadb_comb_auth = aalg->alg_id;
-			comb->sadb_comb_auth_minbits =
-			    MAX(prot->ipp_espa_minbits, aalg->alg_ef_minbits);
-			comb->sadb_comb_auth_maxbits =
-			    MIN(prot->ipp_espa_maxbits, aalg->alg_ef_maxbits);
-		}
-
-		/*
-		 * The following may be based on algorithm
-		 * properties, but in the meantime, we just pick
-		 * some good, sensible numbers.  Key mgmt. can
-		 * (and perhaps should) be the place to finalize
-		 * such decisions.
-		 */
-
-		/*
-		 * No limits on allocations, since we really don't
-		 * support that concept currently.
-		 */
-		comb->sadb_comb_soft_allocations = 0;
-		comb->sadb_comb_hard_allocations = 0;
-
-		/*
-		 * These may want to come from policy rule..
-		 */
-		comb->sadb_comb_soft_bytes =
-		    espstack->ipsecesp_default_soft_bytes;
-		comb->sadb_comb_hard_bytes =
-		    espstack->ipsecesp_default_hard_bytes;
-		comb->sadb_comb_soft_addtime =
-		    espstack->ipsecesp_default_soft_addtime;
-		comb->sadb_comb_hard_addtime =
-		    espstack->ipsecesp_default_hard_addtime;
-		comb->sadb_comb_soft_usetime =
-		    espstack->ipsecesp_default_soft_usetime;
-		comb->sadb_comb_hard_usetime =
-		    espstack->ipsecesp_default_hard_usetime;
-
-		prop->sadb_prop_len += SADB_8TO64(sizeof (*comb));
-		if (--combs == 0)
-			break;	/* out of space.. */
-		comb++;
-	}
-}
-
-/*
- * Prepare and actually send the SADB_ACQUIRE message to PF_KEY.
- */
-static void
-esp_send_acquire(ipsacq_t *acqrec, mblk_t *extended, netstack_t *ns)
-{
-	uint_t combs;
-	sadb_msg_t *samsg;
-	sadb_prop_t *prop;
-	mblk_t *pfkeymp, *msgmp;
-	ipsecesp_stack_t *espstack = ns->netstack_ipsecesp;
-	ipsec_stack_t	*ipss = ns->netstack_ipsec;
-
-	ESP_BUMP_STAT(espstack, acquire_requests);
-
-	if (espstack->esp_pfkey_q == NULL) {
-		mutex_exit(&acqrec->ipsacq_lock);
-		return;
-	}
-
-	/* Set up ACQUIRE. */
-	pfkeymp = sadb_setup_acquire(acqrec, SADB_SATYPE_ESP,
-	    ns->netstack_ipsec);
-	if (pfkeymp == NULL) {
-		esp0dbg(("sadb_setup_acquire failed.\n"));
-		mutex_exit(&acqrec->ipsacq_lock);
-		return;
-	}
-	ASSERT(RW_READ_HELD(&ipss->ipsec_alg_lock));
-	combs = ipss->ipsec_nalgs[IPSEC_ALG_AUTH] *
-	    ipss->ipsec_nalgs[IPSEC_ALG_ENCR];
-	msgmp = pfkeymp->b_cont;
-	samsg = (sadb_msg_t *)(msgmp->b_rptr);
-
-	/* Insert proposal here. */
-
-	prop = (sadb_prop_t *)(((uint64_t *)samsg) + samsg->sadb_msg_len);
-	esp_insert_prop(prop, acqrec, combs, ns);
-	samsg->sadb_msg_len += prop->sadb_prop_len;
-	msgmp->b_wptr += SADB_64TO8(samsg->sadb_msg_len);
-
-	rw_exit(&ipss->ipsec_alg_lock);
-
-	/*
-	 * Must mutex_exit() before sending PF_KEY message up, in
-	 * order to avoid recursive mutex_enter() if there are no registered
-	 * listeners.
-	 *
-	 * Once I've sent the message, I'm cool anyway.
-	 */
-	mutex_exit(&acqrec->ipsacq_lock);
-	if (extended != NULL) {
-		putnext(espstack->esp_pfkey_q, extended);
-	}
-	putnext(espstack->esp_pfkey_q, pfkeymp);
-}
-
 /* XXX refactor me */
 /*
  * Handle the SADB_GETSPI message.  Create a larval SA.
@@ -1811,11 +1567,9 @@
 /*
  * Invoked for outbound packets after ESP processing. If the packet
  * also requires AH, performs the AH SA selection and AH processing.
- * Returns B_TRUE if the AH processing was not needed or if it was
- * performed successfully. Returns B_FALSE and consumes the passed mblk
- * if AH processing was required but could not be performed.
  *
- * Returns data_mp unless data_mp was consumed/queued.
+ * Returns data_mp (possibly with AH added) unless data_mp was consumed
+ * due to an error, or queued due to async. crypto or an ACQUIRE trigger.
  */
 static mblk_t *
 esp_do_outbound_ah(mblk_t *data_mp, ip_xmit_attr_t *ixa)
--- old/usr/src/uts/common/inet/ip/sadb.c	Wed Jul 26 12:09:30 2017
+++ new/usr/src/uts/common/inet/ip/sadb.c	Wed Jul 26 12:09:30 2017
@@ -22,6 +22,7 @@
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  * Copyright (c) 2012 Nexenta Systems, Inc. All rights reserved.
+ * Copyright (c) 2017 Joyent, Inc.
  */
 
 #include <sys/types.h>
@@ -74,8 +75,7 @@
  * of falling under export control, it was safe to link it in there.
  */
 
-static mblk_t *sadb_extended_acquire(ipsec_selector_t *, ipsec_policy_t *,
-    ipsec_action_t *, boolean_t, uint32_t, uint32_t, sadb_sens_t *,
+static uint8_t *sadb_action_to_ecomb(uint8_t *, uint8_t *, ipsec_action_t *,
     netstack_t *);
 static ipsa_t *sadb_torch_assoc(isaf_t *, ipsa_t *);
 static void sadb_destroy_acqlist(iacqf_t **, uint_t, boolean_t,
@@ -83,7 +83,6 @@
 static void sadb_destroy(sadb_t *, netstack_t *);
 static mblk_t *sadb_sa2msg(ipsa_t *, sadb_msg_t *);
 static ts_label_t *sadb_label_from_sens(sadb_sens_t *, uint64_t *);
-static sadb_sens_t *sadb_make_sens_ext(ts_label_t *tsl, int *len);
 
 static time_t sadb_add_time(time_t, uint64_t);
 static void lifetime_fuzz(ipsa_t *);
@@ -4844,6 +4843,555 @@
 }
 
 /*
+ * Generate an SADB_ACQUIRE base message mblk, including KEYSOCK_OUT metadata.
+ * In other words, this will return, upon success, a two-mblk chain.
+ */
+static inline mblk_t *
+sadb_acquire_msg_base(minor_t serial, uint8_t satype, uint32_t seq, pid_t pid)
+{
+	mblk_t *mp;
+	sadb_msg_t *samsg;
+
+	mp = sadb_keysock_out(serial);
+	if (mp == NULL)
+		return (NULL);
+	mp->b_cont = allocb(sizeof (sadb_msg_t), BPRI_HI);
+	if (mp->b_cont == NULL) {
+		freeb(mp);
+		return (NULL);
+	}
+
+	samsg = (sadb_msg_t *)mp->b_cont->b_rptr;
+	mp->b_cont->b_wptr += sizeof (*samsg);
+	samsg->sadb_msg_version = PF_KEY_V2;
+	samsg->sadb_msg_type = SADB_ACQUIRE;
+	samsg->sadb_msg_errno = 0;
+	samsg->sadb_msg_reserved = 0;
+	samsg->sadb_msg_satype = satype;
+	samsg->sadb_msg_seq = seq;
+	samsg->sadb_msg_pid = pid;
+
+	return (mp);
+}
+
+/*
+ * Generate address and TX/MLS sensitivity label PF_KEY extensions that are
+ * common to both regular and extended ACQUIREs.
+ */
+static mblk_t *
+sadb_acquire_msg_common(ipsec_selector_t *sel, ipsec_policy_t *pp,
+    ipsec_action_t *ap, boolean_t tunnel_mode, ts_label_t *tsl,
+    sadb_sens_t *sens)
+{
+	size_t len;
+	mblk_t *mp;
+	uint8_t *start, *cur, *end;
+	uint32_t *saddrptr, *daddrptr;
+	sa_family_t af;
+	ipsec_action_t *oldap;
+	ipsec_selkey_t *ipsl;
+	uint8_t proto, pfxlen;
+	uint16_t lport, rport;
+	int senslen = 0;
+
+	/*
+	 * Get action pointer set if it isn't already.
+	 */
+	oldap = ap;
+	if (pp != NULL) {
+		ap = pp->ipsp_act;
+		if (ap == NULL)
+			ap = oldap;
+	}
+
+	/*
+	 * Biggest-case scenario:
+	 * 4x (sadb_address_t + struct sockaddr_in6)
+	 *	(src, dst, isrc, idst)
+	 *	(COMING SOON, 6x, because of triggering-packet contents.)
+	 * sadb_x_kmc_t
+	 * sadb_sens_t
+	 * And wiggle room for label bitvectors.  Luckily there are
+	 * programmatic ways to find it.
+	 */
+	len = 4 * (sizeof (sadb_address_t) + sizeof (struct sockaddr_in6));
+
+	/* Figure out full and proper length of sensitivity labels. */
+	if (sens != NULL) {
+		ASSERT(tsl == NULL);
+		senslen = SADB_64TO8(sens->sadb_sens_len);
+	} else if (tsl != NULL) {
+		senslen = sadb_sens_len_from_label(tsl);
+	}
+#ifdef DEBUG
+	else {
+		ASSERT(senslen == 0);
+	}
+#endif /* DEBUG */
+	len += senslen;
+
+	mp = allocb(len, BPRI_HI);
+	if (mp == NULL)
+		return (NULL);
+
+	start = mp->b_rptr;
+	end = start + len;
+	cur = start;
+
+	/*
+	 * Address extensions first, from most-recently-defined to least.
+	 * (This should immediately trigger surprise or verify robustness on
+	 * older apps, like in.iked.)
+	 */
+	if (tunnel_mode) {
+		/*
+		 * Form inner address extensions based NOT on the inner
+		 * selectors (i.e. the packet data), but on the policy's
+		 * selector key (i.e. the policy's selector information).
+		 *
+		 * NOTE:  The position of IPv4 and IPv6 addresses is the
+		 * same in ipsec_selkey_t (unless the compiler does very
+		 * strange things with unions, consult your local C language
+		 * lawyer for details).
+		 */
+		ASSERT(pp != NULL);
+
+		ipsl = &(pp->ipsp_sel->ipsl_key);
+		if (ipsl->ipsl_valid & IPSL_IPV4) {
+			af = AF_INET;
+			ASSERT(sel->ips_protocol == IPPROTO_ENCAP);
+			ASSERT(!(ipsl->ipsl_valid & IPSL_IPV6));
+		} else {
+			af = AF_INET6;
+			ASSERT(sel->ips_protocol == IPPROTO_IPV6);
+			ASSERT(ipsl->ipsl_valid & IPSL_IPV6);
+		}
+
+		if (ipsl->ipsl_valid & IPSL_LOCAL_ADDR) {
+			saddrptr = (uint32_t *)(&ipsl->ipsl_local);
+			pfxlen = ipsl->ipsl_local_pfxlen;
+		} else {
+			saddrptr = (uint32_t *)(&ipv6_all_zeros);
+			pfxlen = 0;
+		}
+		/* XXX What about ICMP type/code? */
+		lport = (ipsl->ipsl_valid & IPSL_LOCAL_PORT) ?
+		    ipsl->ipsl_lport : 0;
+		proto = (ipsl->ipsl_valid & IPSL_PROTOCOL) ?
+		    ipsl->ipsl_proto : 0;
+
+		cur = sadb_make_addr_ext(cur, end, SADB_X_EXT_ADDRESS_INNER_SRC,
+		    af, saddrptr, lport, proto, pfxlen);
+		if (cur == NULL) {
+			freeb(mp);
+			return (NULL);
+		}
+
+		if (ipsl->ipsl_valid & IPSL_REMOTE_ADDR) {
+			daddrptr = (uint32_t *)(&ipsl->ipsl_remote);
+			pfxlen = ipsl->ipsl_remote_pfxlen;
+		} else {
+			daddrptr = (uint32_t *)(&ipv6_all_zeros);
+			pfxlen = 0;
+		}
+		/* XXX What about ICMP type/code? */
+		rport = (ipsl->ipsl_valid & IPSL_REMOTE_PORT) ?
+		    ipsl->ipsl_rport : 0;
+
+		cur = sadb_make_addr_ext(cur, end, SADB_X_EXT_ADDRESS_INNER_DST,
+		    af, daddrptr, rport, proto, pfxlen);
+		if (cur == NULL) {
+			freeb(mp);
+			return (NULL);
+		}
+		/*
+		 * TODO  - if we go to 3884's dream of transport mode IP-in-IP
+		 * _with_ inner-packet address selectors, we'll need to further
+		 * distinguish tunnel mode here.  For now, having inner
+		 * addresses and/or ports is sufficient.
+		 *
+		 * Meanwhile, whack proto/ports to reflect IP-in-IP for the
+		 * outer addresses.
+		 */
+		proto = sel->ips_protocol;	/* Either _ENCAP or _IPV6 */
+		lport = rport = 0;
+	} else if ((ap != NULL) && (!ap->ipa_want_unique)) {
+		/*
+		 * For cases when the policy calls out specific ports (or not).
+		 */
+		proto = 0;
+		lport = 0;
+		rport = 0;
+		if (pp != NULL) {
+			ipsl = &(pp->ipsp_sel->ipsl_key);
+			if (ipsl->ipsl_valid & IPSL_PROTOCOL)
+				proto = ipsl->ipsl_proto;
+			if (ipsl->ipsl_valid & IPSL_REMOTE_PORT)
+				rport = ipsl->ipsl_rport;
+			if (ipsl->ipsl_valid & IPSL_LOCAL_PORT)
+				lport = ipsl->ipsl_lport;
+		}
+	} else {
+		/*
+		 * For require-unique-SA policies.
+		 */
+		proto = sel->ips_protocol;
+		lport = sel->ips_local_port;
+		rport = sel->ips_remote_port;
+	}
+
+	/*
+	 * Regular addresses.  These are outer-packet ones for tunnel mode.
+	 * Or for transport mode, the regulard address & port information.
+	 */
+	af = sel->ips_isv4 ? AF_INET : AF_INET6;
+
+	/*
+	 * NOTE:  The position of IPv4 and IPv6 addresses is the same in
+	 * ipsec_selector_t.
+	 */
+	cur = sadb_make_addr_ext(cur, end, SADB_EXT_ADDRESS_SRC, af,
+	    (uint32_t *)(&sel->ips_local_addr_v6), lport, proto, 0);
+	if (cur == NULL) {
+		freeb(mp);
+		return (NULL);
+	}
+
+	cur = sadb_make_addr_ext(cur, end, SADB_EXT_ADDRESS_DST, af,
+	    (uint32_t *)(&sel->ips_remote_addr_v6), rport, proto, 0);
+	if (cur == NULL) {
+		freeb(mp);
+		return (NULL);
+	}
+
+	/*
+	 * If present, generate a sensitivity label.
+	 */
+	if (cur + senslen > end) {
+		freeb(mp);
+		return (NULL);
+	}
+	if (sens != NULL) {
+		/* Explicit sadb_sens_t, usually from inverse-ACQUIRE. */
+		bcopy(sens, cur, senslen);
+	} else if (tsl != NULL) {
+		/* Generate sadb_sens_t from ACQUIRE source. */
+		sadb_sens_from_label((sadb_sens_t *)cur, SADB_EXT_SENSITIVITY,
+		    tsl, senslen);
+	}
+#ifdef DEBUG
+	else {
+		ASSERT(senslen == 0);
+	}
+#endif /* DEBUG */
+	cur += senslen;
+	mp->b_wptr = cur;
+
+	return (mp);
+}
+
+/*
+ * Generate a regular ACQUIRE's proposal extension and KMC information..
+ */
+static mblk_t *
+sadb_acquire_prop(ipsec_action_t *ap, netstack_t *ns, boolean_t do_esp)
+{
+	ipsec_stack_t *ipss = ns->netstack_ipsec;
+	ipsecesp_stack_t *espstack = ns->netstack_ipsecesp;
+	ipsecah_stack_t *ahstack = ns->netstack_ipsecah;
+	mblk_t *mp = NULL;
+	sadb_prop_t *prop;
+	sadb_comb_t *comb;
+	ipsec_action_t *walker;
+	int ncombs, allocsize, ealgid, aalgid, aminbits, amaxbits, eminbits,
+	    emaxbits, replay;
+	uint64_t softbytes, hardbytes, softaddtime, hardaddtime, softusetime,
+	    hardusetime;
+	uint32_t kmc = 0, kmp = 0;
+
+	/*
+	 * Since it's an rwlock read, AND writing to the IPsec algorithms is
+	 * rare, just acquire it once up top, and drop it upon return.
+	 */
+	rw_enter(&ipss->ipsec_alg_lock, RW_READER);
+	if (do_esp) {
+		uint64_t num_aalgs, num_ealgs;
+
+		if (espstack->esp_kstats == NULL)
+			goto bail;
+
+		num_aalgs = ipss->ipsec_nalgs[IPSEC_ALG_AUTH];
+		num_ealgs = ipss->ipsec_nalgs[IPSEC_ALG_ENCR];
+		if (num_ealgs == 0)
+			goto bail;	/* IPsec not loaded yet, apparently. */
+		num_aalgs++;	/* No-auth or self-auth-crypto ESP. */
+
+		/* Use netstack's maximum loaded algorithms... */
+		ncombs = num_ealgs * num_aalgs;
+		replay =  espstack->ipsecesp_replay_size;
+	} else {
+		if (ahstack->ah_kstats == NULL)
+			goto bail;
+
+		ncombs = ipss->ipsec_nalgs[IPSEC_ALG_AUTH];
+
+		if (ncombs == 0)
+			goto bail;	/* IPsec not loaded yet, apparently. */
+		replay =  ahstack->ipsecah_replay_size;
+	}
+
+	allocsize = sizeof (*prop) + ncombs * sizeof (*comb) +
+	    sizeof (sadb_x_kmc_t);
+	mp = allocb(allocsize, BPRI_HI);
+	if (mp == NULL)
+		goto bail;
+	prop = (sadb_prop_t *)mp->b_rptr;
+	mp->b_wptr += sizeof (*prop);
+	comb = (sadb_comb_t *)mp->b_wptr;
+	/* Decrement allocsize, if it goes to or below 0, stop. */
+	allocsize -= sizeof (*prop);
+	prop->sadb_prop_exttype = SADB_EXT_PROPOSAL;
+	prop->sadb_prop_len = SADB_8TO64(sizeof (*prop));
+	*(uint32_t *)(&prop->sadb_prop_replay) = 0;	/* Quick zero-out! */
+	prop->sadb_prop_replay = replay;
+
+	/*
+	 * Based upon algorithm properties, and what-not, prioritize a
+	 * proposal, based on the ordering of the ESP algorithms in the
+	 * alternatives in the policy rule or socket that was placed
+	 * in the acquire record.
+	 *
+	 * For each action in policy list
+	 *   Add combination.
+	 *   I should not hit it, but if I've hit limit, return.
+	 */
+
+	for (walker = ap; walker != NULL; walker = walker->ipa_next) {
+		ipsec_alginfo_t *ealg, *aalg;
+		ipsec_prot_t *prot;
+
+		if (walker->ipa_act.ipa_type != IPSEC_POLICY_APPLY)
+			continue;
+
+		prot = &walker->ipa_act.ipa_apply;
+		if (walker->ipa_act.ipa_apply.ipp_km_proto != 0)
+			kmp = walker->ipa_act.ipa_apply.ipp_km_proto;
+		if (walker->ipa_act.ipa_apply.ipp_km_cookie != 0)
+			kmc = walker->ipa_act.ipa_apply.ipp_km_cookie;
+		if (walker->ipa_act.ipa_apply.ipp_replay_depth) {
+			prop->sadb_prop_replay =
+			    walker->ipa_act.ipa_apply.ipp_replay_depth;
+		}
+
+		if (do_esp) {
+			if (!prot->ipp_use_esp)
+				continue;
+
+			if (prot->ipp_esp_auth_alg != 0) {
+				aalg = ipss->ipsec_alglists[IPSEC_ALG_AUTH]
+				    [prot->ipp_esp_auth_alg];
+				if (aalg == NULL || !ALG_VALID(aalg))
+					continue;
+			} else
+				aalg = NULL;
+
+			ASSERT(prot->ipp_encr_alg > 0);
+			ealg = ipss->ipsec_alglists[IPSEC_ALG_ENCR]
+			    [prot->ipp_encr_alg];
+			if (ealg == NULL || !ALG_VALID(ealg))
+				continue;
+
+			/*
+			 * These may want to come from policy rule..
+			 */
+			softbytes = espstack->ipsecesp_default_soft_bytes;
+			hardbytes = espstack->ipsecesp_default_hard_bytes;
+			softaddtime = espstack->ipsecesp_default_soft_addtime;
+			hardaddtime = espstack->ipsecesp_default_hard_addtime;
+			softusetime = espstack->ipsecesp_default_soft_usetime;
+			hardusetime = espstack->ipsecesp_default_hard_usetime;
+		} else {
+			if (!prot->ipp_use_ah)
+				continue;
+			ealg = NULL;
+			aalg = ipss->ipsec_alglists[IPSEC_ALG_AUTH]
+			    [prot->ipp_auth_alg];
+			if (aalg == NULL || !ALG_VALID(aalg))
+				continue;
+
+			/*
+			 * These may want to come from policy rule..
+			 */
+			softbytes = ahstack->ipsecah_default_soft_bytes;
+			hardbytes = ahstack->ipsecah_default_hard_bytes;
+			softaddtime = ahstack->ipsecah_default_soft_addtime;
+			hardaddtime = ahstack->ipsecah_default_hard_addtime;
+			softusetime = ahstack->ipsecah_default_soft_usetime;
+			hardusetime = ahstack->ipsecah_default_hard_usetime;
+		}
+
+		if (ealg == NULL) {
+			ealgid = eminbits = emaxbits = 0;
+		} else {
+			ealgid = ealg->alg_id;
+			eminbits =
+			    MAX(prot->ipp_espe_minbits, ealg->alg_ef_minbits);
+			emaxbits =
+			    MIN(prot->ipp_espe_maxbits, ealg->alg_ef_maxbits);
+		}
+
+		if (aalg == NULL) {
+			aalgid = aminbits = amaxbits = 0;
+		} else {
+			aalgid = aalg->alg_id;
+			aminbits = MAX(prot->ipp_espa_minbits,
+			    aalg->alg_ef_minbits);
+			amaxbits = MIN(prot->ipp_espa_maxbits,
+			    aalg->alg_ef_maxbits);
+		}
+
+		comb->sadb_comb_flags = 0;
+		comb->sadb_comb_reserved = 0;
+		comb->sadb_comb_encrypt = ealgid;
+		comb->sadb_comb_encrypt_minbits = eminbits;
+		comb->sadb_comb_encrypt_maxbits = emaxbits;
+		comb->sadb_comb_auth = aalgid;
+		comb->sadb_comb_auth_minbits = aminbits;
+		comb->sadb_comb_auth_maxbits = amaxbits;
+		comb->sadb_comb_soft_allocations = 0;
+		comb->sadb_comb_hard_allocations = 0;
+		comb->sadb_comb_soft_bytes = softbytes;
+		comb->sadb_comb_hard_bytes = hardbytes;
+		comb->sadb_comb_soft_addtime = softaddtime;
+		comb->sadb_comb_hard_addtime = hardaddtime;
+		comb->sadb_comb_soft_usetime = softusetime;
+		comb->sadb_comb_hard_usetime = hardusetime;
+
+		prop->sadb_prop_len += SADB_8TO64(sizeof (*comb));
+		mp->b_wptr += sizeof (*comb);
+		allocsize -= sizeof (*comb);
+		/* Should never dip BELOW sizeof (KM cookie extension). */
+		ASSERT3S(allocsize, >=, sizeof (sadb_x_kmc_t));
+		if (allocsize <= sizeof (sadb_x_kmc_t))
+			break;	/* out of space.. */
+		comb++;
+	}
+
+	/* Don't include KMC extension if there's no room. */
+	if (((kmp != 0) || (kmc != 0)) && allocsize >= sizeof (sadb_x_kmc_t)) {
+		if (sadb_make_kmc_ext(mp->b_wptr,
+		    mp->b_wptr + sizeof (sadb_x_kmc_t), kmp, kmc) == NULL) {
+			freeb(mp);
+			mp = NULL;
+			goto bail;
+		}
+		mp->b_wptr += sizeof (sadb_x_kmc_t);
+		prop->sadb_prop_len += SADB_8TO64(sizeof (sadb_x_kmc_t));
+	}
+
+bail:
+	rw_exit(&ipss->ipsec_alg_lock);
+	return (mp);
+}
+
+/*
+ * Generate an extended ACQUIRE's extended-proposal extension.
+ */
+/* ARGSUSED */
+static mblk_t *
+sadb_acquire_extended_prop(ipsec_action_t *ap, netstack_t *ns)
+{
+	sadb_prop_t *eprop;
+	uint8_t *cur, *end;
+	mblk_t *mp;
+	int allocsize, numecombs = 0, numalgdescs = 0;
+	uint32_t kmc = 0, kmp = 0, replay = 0;
+	ipsec_action_t *walker;
+
+	allocsize = sizeof (*eprop);
+
+	/*
+	 * Going to walk through the action list twice.  Once for allocation
+	 * measurement, and once for actual construction.
+	 */
+	for (walker = ap; walker != NULL; walker = walker->ipa_next) {
+		ipsec_prot_t *ipp;
+
+		/*
+		 * Skip non-IPsec policies
+		 */
+		if (walker->ipa_act.ipa_type != IPSEC_ACT_APPLY)
+			continue;
+
+		ipp = &walker->ipa_act.ipa_apply;
+
+		if (walker->ipa_act.ipa_apply.ipp_km_proto)
+			kmp = ipp->ipp_km_proto;
+		if (walker->ipa_act.ipa_apply.ipp_km_cookie)
+			kmc = ipp->ipp_km_cookie;
+		if (walker->ipa_act.ipa_apply.ipp_replay_depth)
+			replay = ipp->ipp_replay_depth;
+
+		if (ipp->ipp_use_ah)
+			numalgdescs++;
+		if (ipp->ipp_use_esp) {
+			numalgdescs++;
+			if (ipp->ipp_use_espa)
+				numalgdescs++;
+		}
+
+		numecombs++;
+	}
+	ASSERT(numecombs > 0);
+
+	allocsize += numecombs * sizeof (sadb_x_ecomb_t) +
+	    numalgdescs * sizeof (sadb_x_algdesc_t) + sizeof (sadb_x_kmc_t);
+	mp = allocb(allocsize, BPRI_HI);
+	if (mp == NULL)
+		return (NULL);
+	eprop = (sadb_prop_t *)mp->b_rptr;
+	end = mp->b_rptr + allocsize;
+	cur = mp->b_rptr + sizeof (*eprop);
+
+	eprop->sadb_prop_exttype = SADB_X_EXT_EPROP;
+	eprop->sadb_x_prop_ereserved = 0;
+	eprop->sadb_x_prop_numecombs = 0;
+	*(uint32_t *)(&eprop->sadb_prop_replay) = 0;	/* Quick zero-out! */
+	/* Pick ESP's replay default if need be. */
+	eprop->sadb_prop_replay = (replay == 0) ?
+	    ns->netstack_ipsecesp->ipsecesp_replay_size : replay;
+
+	/* This time, walk through and actually allocate. */
+	for (walker = ap; walker != NULL; walker = walker->ipa_next) {
+		/*
+		 * Skip non-IPsec policies
+		 */
+		if (walker->ipa_act.ipa_type != IPSEC_ACT_APPLY)
+			continue;
+		cur = sadb_action_to_ecomb(cur, end, walker, ns);
+		if (cur == NULL) {
+			/* NOTE: inverse-ACQUIRE should note this as ENOMEM. */
+			freeb(mp);
+			return (NULL);
+		}
+		eprop->sadb_x_prop_numecombs++;
+	}
+
+	ASSERT(end - cur >= sizeof (sadb_x_kmc_t));
+	if ((kmp != 0) || (kmc != 0)) {
+		cur = sadb_make_kmc_ext(cur, end, kmp, kmc);
+		if (cur == NULL) {
+			freeb(mp);
+			return (NULL);
+		}
+	}
+	mp->b_wptr = cur;
+	eprop->sadb_prop_len = SADB_8TO64(cur - mp->b_rptr);
+
+	return (mp);
+}
+
+/*
  * For this mblk, insert a new acquire record.  Assume bucket contains addrs
  * of all of the same length.  Give up (and drop) if memory
  * cannot be allocated for a new one; otherwise, invoke callback to
@@ -4857,12 +5405,11 @@
 sadb_acquire(mblk_t *datamp, ip_xmit_attr_t *ixa, boolean_t need_ah,
     boolean_t need_esp)
 {
-	mblk_t	*asyncmp;
+	mblk_t	*asyncmp, *regular, *extended, *common, *prop, *eprop;
 	sadbp_t *spp;
 	sadb_t *sp;
 	ipsacq_t *newbie;
 	iacqf_t *bucket;
-	mblk_t *extended;
 	ipha_t *ipha = (ipha_t *)datamp->b_rptr;
 	ip6_t *ip6h = (ip6_t *)datamp->b_rptr;
 	uint32_t *src, *dst, *isrc, *idst;
@@ -4872,26 +5419,26 @@
 	int hashoffset;
 	uint32_t seq;
 	uint64_t unique_id = 0;
-	ipsec_selector_t sel;
 	boolean_t tunnel_mode = (ixa->ixa_flags & IXAF_IPSEC_TUNNEL) != 0;
-	ts_label_t 	*tsl = NULL;
+	ts_label_t 	*tsl;
 	netstack_t	*ns = ixa->ixa_ipst->ips_netstack;
 	ipsec_stack_t	*ipss = ns->netstack_ipsec;
-	sadb_sens_t 	*sens = NULL;
-	int 		sens_len;
+	ipsecesp_stack_t *espstack = ns->netstack_ipsecesp;
+	ipsecah_stack_t	*ahstack = ns->netstack_ipsecah;
+	ipsec_selector_t sel;
+	queue_t *q;
 
 	ASSERT((pp != NULL) || (ap != NULL));
 
-	ASSERT(need_ah != NULL || need_esp != NULL);
+	ASSERT(need_ah || need_esp);
 
 	/* Assign sadb pointers */
-	if (need_esp) { /* ESP for AH+ESP */
-		ipsecesp_stack_t *espstack = ns->netstack_ipsecesp;
-
+	if (need_esp) {
+		/*
+		 * ESP happens first if we need both AH and ESP.
+		 */
 		spp = &espstack->esp_sadb;
 	} else {
-		ipsecah_stack_t	*ahstack = ns->netstack_ipsecah;
-
 		spp = &ahstack->ah_sadb;
 	}
 	sp = (ixa->ixa_flags & IXAF_IS_IPV4) ? &spp->s_v4 : &spp->s_v6;
@@ -4898,10 +5445,11 @@
 
 	if (is_system_labeled())
 		tsl = ixa->ixa_tsl;
+	else
+		tsl = NULL;
 
 	if (ap == NULL)
 		ap = pp->ipsp_act;
-
 	ASSERT(ap != NULL);
 
 	if (ap->ipa_act.ipa_apply.ipp_use_unique || tunnel_mode)
@@ -5031,6 +5579,13 @@
 		}
 		ip_drop_output("No memory for asyncmp", datamp, NULL);
 		freemsg(datamp);
+		/*
+		 * The acquire record will be freed quickly if it's new
+		 * (ipsacq_expire == 0), and will proceed as if no packet
+		 * showed up if not.
+		 */
+		mutex_exit(&newbie->ipsacq_lock);
+		return;
 	} else if (newbie->ipsacq_numpackets == 0) {
 		/* First one. */
 		newbie->ipsacq_mp = asyncmp;
@@ -5063,9 +5618,9 @@
 		}
 		newbie->ipsacq_unique_id = unique_id;
 
-		if (ixa->ixa_tsl != NULL) {
-			label_hold(ixa->ixa_tsl);
-			newbie->ipsacq_tsl = ixa->ixa_tsl;
+		if (tsl != NULL) {
+			label_hold(tsl);
+			newbie->ipsacq_tsl = tsl;
 		}
 	} else {
 		/* Scan to the end of the list & insert. */
@@ -5110,11 +5665,25 @@
 		return;
 	}
 
-	if (!keysock_extended_reg(ns))
-		goto punt_extended;
+	if (need_esp) {
+		ESP_BUMP_STAT(espstack, acquire_requests);
+		q = espstack->esp_pfkey_q;
+	} else {
+		/*
+		 * Two cases get us here:
+		 * 1.) AH-only policy.
+		 *
+		 * 2.) A continuation of an AH+ESP policy, and this is the
+		 * post-ESP, AH-needs-to-send-a-regular-ACQUIRE case.
+		 * (i.e. called from esp_do_outbound_ah().)
+		 */
+		AH_BUMP_STAT(ahstack, acquire_requests);
+		q = ahstack->ah_pfkey_q;
+	}
+
 	/*
-	 * Construct an extended ACQUIRE.  There are logging
-	 * opportunities here in failure cases.
+	 * Get selectors and other policy-expression bits needed for an
+	 * ACQUIRE.
 	 */
 	bzero(&sel, sizeof (sel));
 	sel.ips_isv4 = (ixa->ixa_flags & IXAF_IS_IPV4) != 0;
@@ -5137,45 +5706,99 @@
 		sel.ips_remote_addr_v6 = ip6h->ip6_dst;
 	}
 
-	extended = sadb_keysock_out(0);
-	if (extended == NULL)
-		goto punt_extended;
 
-	if (ixa->ixa_tsl != NULL) {
-		/*
-		 * XXX MLS correct condition here?
-		 * XXX MLS other credential attributes in acquire?
-		 * XXX malloc failure?  don't fall back to original?
-		 */
-		sens = sadb_make_sens_ext(ixa->ixa_tsl, &sens_len);
+	/*
+	 * 1. Generate addresses, kmc, and sensitivity.  These are "common"
+	 * and should be an mblk pointed to by common. TBD -- eventually it
+	 * will include triggering packet contents as more address extensions.
+	 *
+	 * 2. Generate ACQUIRE & KEYSOCK_OUT and single-protocol proposal.
+	 * These are "regular" and "prop".  String regular->b_cont->b_cont =
+	 * common, common->b_cont = prop.
+	 *
+	 * 3. If extended register got turned on, generate EXT_ACQUIRE &
+	 * KEYSOCK_OUT and multi-protocol eprop. These are "extended" and
+	 * "eprop".  String extended->b_cont->b_cont = dupb(common) and
+	 * extended->b_cont->b_cont->b_cont = prop.
+	 *
+	 * 4. Deliver:  putnext(q, regular) and if there, putnext(q, extended).
+	 */
 
-		if (sens == NULL) {
-			freeb(extended);
-			goto punt_extended;
-		}
-	}
+	regular = extended = prop = eprop = NULL;
 
-	extended->b_cont = sadb_extended_acquire(&sel, pp, ap, tunnel_mode,
-	    seq, 0, sens, ns);
+	common = sadb_acquire_msg_common(&sel, pp, ap, tunnel_mode, tsl, NULL);
+	if (common == NULL)
+		goto bail;
 
-	if (sens != NULL)
-		kmem_free(sens, sens_len);
+	regular = sadb_acquire_msg_base(0, (need_esp ?
+	    SADB_SATYPE_ESP : SADB_SATYPE_AH), newbie->ipsacq_seq, 0);
+	if (regular == NULL)
+		goto bail;
 
-	if (extended->b_cont == NULL) {
-		freeb(extended);
-		goto punt_extended;
-	}
+	/*
+	 * Pardon the boolean cleverness. At least one of need_* must be true.
+	 * If they are equal, it's an AH & ESP policy and ESP needs to go
+	 * first.  If they aren't, just check the contents of need_esp.
+	 */
+	prop = sadb_acquire_prop(ap, ns, need_esp);
+	if (prop == NULL)
+		goto bail;
 
+	/* Link the parts together. */
+	regular->b_cont->b_cont = common;
+	common->b_cont = prop;
 	/*
-	 * Send an ACQUIRE message (and possible an extended ACQUIRE) based on
-	 * this new record.  The send-acquire callback assumes that acqrec is
-	 * already locked.
+	 * Prop is now linked, so don't freemsg() it if the extended
+	 * construction goes off the rails.
 	 */
-	(*spp->s_acqfn)(newbie, extended, ns);
+	prop = NULL;
+
+	((sadb_msg_t *)(regular->b_cont->b_rptr))->sadb_msg_len =
+	    SADB_8TO64(msgsize(regular->b_cont));
+
+	/*
+	 * If we need an extended ACQUIRE, build it here.
+	 */
+	if (keysock_extended_reg(ns)) {
+		/* NOTE: "common" still points to what we need. */
+		extended = sadb_acquire_msg_base(0, 0, newbie->ipsacq_seq, 0);
+		if (extended == NULL) {
+			common = NULL;
+			goto bail;
+		}
+
+		extended->b_cont->b_cont = dupb(common);
+		common = NULL;
+		if (extended->b_cont->b_cont == NULL)
+			goto bail;
+
+		eprop = sadb_acquire_extended_prop(ap, ns);
+		if (eprop == NULL)
+			goto bail;
+		extended->b_cont->b_cont->b_cont = eprop;
+
+		((sadb_msg_t *)(extended->b_cont->b_rptr))->sadb_msg_len =
+		    SADB_8TO64(msgsize(extended->b_cont));
+	}
+
+	/* So we don't hold a lock across putnext()... */
+	mutex_exit(&newbie->ipsacq_lock);
+
+	if (extended != NULL)
+		putnext(q, extended);
+	ASSERT(regular != NULL);
+	putnext(q, regular);
 	return;
 
-punt_extended:
-	(*spp->s_acqfn)(newbie, NULL, ns);
+bail:
+	/* Make this acquire record go away quickly... */
+	newbie->ipsacq_expire = 0;
+	/* Exploit freemsg(NULL) being legal for fun & profit. */
+	freemsg(common);
+	freemsg(prop);
+	freemsg(extended);
+	freemsg(regular);
+	mutex_exit(&newbie->ipsacq_lock);
 }
 
 /*
@@ -5429,21 +6052,6 @@
 	bcopy(&(((_bslabel_impl_t *)sl)->compartments), bitmap, _C_LEN * 4);
 }
 
-static sadb_sens_t *
-sadb_make_sens_ext(ts_label_t *tsl, int *len)
-{
-	/* XXX allocation failure? */
-	int sens_len = sadb_sens_len_from_label(tsl);
-
-	sadb_sens_t *sens = kmem_alloc(sens_len, KM_SLEEP);
-
-	sadb_sens_from_label(sens, SADB_EXT_SENSITIVITY, tsl, sens_len);
-
-	*len = sens_len;
-
-	return (sens);
-}
-
 /*
  * Okay, how do we report errors/invalid labels from this?
  * With a special designated "not a label" cred_t ?
@@ -5480,392 +6088,6 @@
 /* End XXX label-library-leakage */
 
 /*
- * Construct an extended ACQUIRE message based on a selector and the resulting
- * IPsec action.
- *
- * NOTE: This is used by both inverse ACQUIRE and actual ACQUIRE
- * generation. As a consequence, expect this function to evolve
- * rapidly.
- */
-static mblk_t *
-sadb_extended_acquire(ipsec_selector_t *sel, ipsec_policy_t *pol,
-    ipsec_action_t *act, boolean_t tunnel_mode, uint32_t seq, uint32_t pid,
-    sadb_sens_t *sens, netstack_t *ns)
-{
-	mblk_t *mp;
-	sadb_msg_t *samsg;
-	uint8_t *start, *cur, *end;
-	uint32_t *saddrptr, *daddrptr;
-	sa_family_t af;
-	sadb_prop_t *eprop;
-	ipsec_action_t *ap, *an;
-	ipsec_selkey_t *ipsl;
-	uint8_t proto, pfxlen;
-	uint16_t lport, rport;
-	uint32_t kmp, kmc;
-
-	/*
-	 * Find the action we want sooner rather than later..
-	 */
-	an = NULL;
-	if (pol == NULL) {
-		ap = act;
-	} else {
-		ap = pol->ipsp_act;
-
-		if (ap != NULL)
-			an = ap->ipa_next;
-	}
-
-	/*
-	 * Just take a swag for the allocation for now.	 We can always
-	 * alter it later.
-	 */
-#define	SADB_EXTENDED_ACQUIRE_SIZE	4096
-	mp = allocb(SADB_EXTENDED_ACQUIRE_SIZE, BPRI_HI);
-	if (mp == NULL)
-		return (NULL);
-
-	start = mp->b_rptr;
-	end = start + SADB_EXTENDED_ACQUIRE_SIZE;
-
-	cur = start;
-
-	samsg = (sadb_msg_t *)cur;
-	cur += sizeof (*samsg);
-
-	samsg->sadb_msg_version = PF_KEY_V2;
-	samsg->sadb_msg_type = SADB_ACQUIRE;
-	samsg->sadb_msg_errno = 0;
-	samsg->sadb_msg_reserved = 0;
-	samsg->sadb_msg_satype = 0;
-	samsg->sadb_msg_seq = seq;
-	samsg->sadb_msg_pid = pid;
-
-	if (tunnel_mode) {
-		/*
-		 * Form inner address extensions based NOT on the inner
-		 * selectors (i.e. the packet data), but on the policy's
-		 * selector key (i.e. the policy's selector information).
-		 *
-		 * NOTE:  The position of IPv4 and IPv6 addresses is the
-		 * same in ipsec_selkey_t (unless the compiler does very
-		 * strange things with unions, consult your local C language
-		 * lawyer for details).
-		 */
-		ASSERT(pol != NULL);
-
-		ipsl = &(pol->ipsp_sel->ipsl_key);
-		if (ipsl->ipsl_valid & IPSL_IPV4) {
-			af = AF_INET;
-			ASSERT(sel->ips_protocol == IPPROTO_ENCAP);
-			ASSERT(!(ipsl->ipsl_valid & IPSL_IPV6));
-		} else {
-			af = AF_INET6;
-			ASSERT(sel->ips_protocol == IPPROTO_IPV6);
-			ASSERT(ipsl->ipsl_valid & IPSL_IPV6);
-		}
-
-		if (ipsl->ipsl_valid & IPSL_LOCAL_ADDR) {
-			saddrptr = (uint32_t *)(&ipsl->ipsl_local);
-			pfxlen = ipsl->ipsl_local_pfxlen;
-		} else {
-			saddrptr = (uint32_t *)(&ipv6_all_zeros);
-			pfxlen = 0;
-		}
-		/* XXX What about ICMP type/code? */
-		lport = (ipsl->ipsl_valid & IPSL_LOCAL_PORT) ?
-		    ipsl->ipsl_lport : 0;
-		proto = (ipsl->ipsl_valid & IPSL_PROTOCOL) ?
-		    ipsl->ipsl_proto : 0;
-
-		cur = sadb_make_addr_ext(cur, end, SADB_X_EXT_ADDRESS_INNER_SRC,
-		    af, saddrptr, lport, proto, pfxlen);
-		if (cur == NULL) {
-			freeb(mp);
-			return (NULL);
-		}
-
-		if (ipsl->ipsl_valid & IPSL_REMOTE_ADDR) {
-			daddrptr = (uint32_t *)(&ipsl->ipsl_remote);
-			pfxlen = ipsl->ipsl_remote_pfxlen;
-		} else {
-			daddrptr = (uint32_t *)(&ipv6_all_zeros);
-			pfxlen = 0;
-		}
-		/* XXX What about ICMP type/code? */
-		rport = (ipsl->ipsl_valid & IPSL_REMOTE_PORT) ?
-		    ipsl->ipsl_rport : 0;
-
-		cur = sadb_make_addr_ext(cur, end, SADB_X_EXT_ADDRESS_INNER_DST,
-		    af, daddrptr, rport, proto, pfxlen);
-		if (cur == NULL) {
-			freeb(mp);
-			return (NULL);
-		}
-		/*
-		 * TODO  - if we go to 3408's dream of transport mode IP-in-IP
-		 * _with_ inner-packet address selectors, we'll need to further
-		 * distinguish tunnel mode here.  For now, having inner
-		 * addresses and/or ports is sufficient.
-		 *
-		 * Meanwhile, whack proto/ports to reflect IP-in-IP for the
-		 * outer addresses.
-		 */
-		proto = sel->ips_protocol;	/* Either _ENCAP or _IPV6 */
-		lport = rport = 0;
-	} else if ((ap != NULL) && (!ap->ipa_want_unique)) {
-		proto = 0;
-		lport = 0;
-		rport = 0;
-		if (pol != NULL) {
-			ipsl = &(pol->ipsp_sel->ipsl_key);
-			if (ipsl->ipsl_valid & IPSL_PROTOCOL)
-				proto = ipsl->ipsl_proto;
-			if (ipsl->ipsl_valid & IPSL_REMOTE_PORT)
-				rport = ipsl->ipsl_rport;
-			if (ipsl->ipsl_valid & IPSL_LOCAL_PORT)
-				lport = ipsl->ipsl_lport;
-		}
-	} else {
-		proto = sel->ips_protocol;
-		lport = sel->ips_local_port;
-		rport = sel->ips_remote_port;
-	}
-
-	af = sel->ips_isv4 ? AF_INET : AF_INET6;
-
-	/*
-	 * NOTE:  The position of IPv4 and IPv6 addresses is the same in
-	 * ipsec_selector_t.
-	 */
-	cur = sadb_make_addr_ext(cur, end, SADB_EXT_ADDRESS_SRC, af,
-	    (uint32_t *)(&sel->ips_local_addr_v6), lport, proto, 0);
-
-	if (cur == NULL) {
-		freeb(mp);
-		return (NULL);
-	}
-
-	cur = sadb_make_addr_ext(cur, end, SADB_EXT_ADDRESS_DST, af,
-	    (uint32_t *)(&sel->ips_remote_addr_v6), rport, proto, 0);
-
-	if (cur == NULL) {
-		freeb(mp);
-		return (NULL);
-	}
-
-	if (sens != NULL) {
-		uint8_t *sensext = cur;
-		int senslen = SADB_64TO8(sens->sadb_sens_len);
-
-		cur += senslen;
-		if (cur > end) {
-			freeb(mp);
-			return (NULL);
-		}
-		bcopy(sens, sensext, senslen);
-	}
-
-	/*
-	 * This section will change a lot as policy evolves.
-	 * For now, it'll be relatively simple.
-	 */
-	eprop = (sadb_prop_t *)cur;
-	cur += sizeof (*eprop);
-	if (cur > end) {
-		/* no space left */
-		freeb(mp);
-		return (NULL);
-	}
-
-	eprop->sadb_prop_exttype = SADB_X_EXT_EPROP;
-	eprop->sadb_x_prop_ereserved = 0;
-	eprop->sadb_x_prop_numecombs = 0;
-	eprop->sadb_prop_replay = 32;	/* default */
-
-	kmc = kmp = 0;
-
-	for (; ap != NULL; ap = an) {
-		an = (pol != NULL) ? ap->ipa_next : NULL;
-
-		/*
-		 * Skip non-IPsec policies
-		 */
-		if (ap->ipa_act.ipa_type != IPSEC_ACT_APPLY)
-			continue;
-
-		if (ap->ipa_act.ipa_apply.ipp_km_proto)
-			kmp = ap->ipa_act.ipa_apply.ipp_km_proto;
-		if (ap->ipa_act.ipa_apply.ipp_km_cookie)
-			kmc = ap->ipa_act.ipa_apply.ipp_km_cookie;
-		if (ap->ipa_act.ipa_apply.ipp_replay_depth) {
-			eprop->sadb_prop_replay =
-			    ap->ipa_act.ipa_apply.ipp_replay_depth;
-		}
-
-		cur = sadb_action_to_ecomb(cur, end, ap, ns);
-		if (cur == NULL) { /* no space */
-			freeb(mp);
-			return (NULL);
-		}
-		eprop->sadb_x_prop_numecombs++;
-	}
-
-	if (eprop->sadb_x_prop_numecombs == 0) {
-		/*
-		 * This will happen if we fail to find a policy
-		 * allowing for IPsec processing.
-		 * Construct an error message.
-		 */
-		samsg->sadb_msg_len = SADB_8TO64(sizeof (*samsg));
-		samsg->sadb_msg_errno = ENOENT;
-		samsg->sadb_x_msg_diagnostic = 0;
-		return (mp);
-	}
-
-	if ((kmp != 0) || (kmc != 0)) {
-		cur = sadb_make_kmc_ext(cur, end, kmp, kmc);
-		if (cur == NULL) {
-			freeb(mp);
-			return (NULL);
-		}
-	}
-
-	eprop->sadb_prop_len = SADB_8TO64(cur - (uint8_t *)eprop);
-	samsg->sadb_msg_len = SADB_8TO64(cur - start);
-	mp->b_wptr = cur;
-
-	return (mp);
-}
-
-/*
- * Generic setup of an RFC 2367 ACQUIRE message.  Caller sets satype.
- *
- * NOTE: This function acquires alg_lock as a side-effect if-and-only-if we
- * succeed (i.e. return non-NULL).  Caller MUST release it.  This is to
- * maximize code consolidation while preventing algorithm changes from messing
- * with the callers finishing touches on the ACQUIRE itself.
- */
-mblk_t *
-sadb_setup_acquire(ipsacq_t *acqrec, uint8_t satype, ipsec_stack_t *ipss)
-{
-	uint_t allocsize;
-	mblk_t *pfkeymp, *msgmp;
-	sa_family_t af;
-	uint8_t *cur, *end;
-	sadb_msg_t *samsg;
-	uint16_t sport_typecode;
-	uint16_t dport_typecode;
-	uint8_t check_proto;
-	boolean_t tunnel_mode = (acqrec->ipsacq_inneraddrfam != 0);
-
-	ASSERT(MUTEX_HELD(&acqrec->ipsacq_lock));
-
-	pfkeymp = sadb_keysock_out(0);
-	if (pfkeymp == NULL)
-		return (NULL);
-
-	/*
-	 * First, allocate a basic ACQUIRE message
-	 */
-	allocsize = sizeof (sadb_msg_t) + sizeof (sadb_address_t) +
-	    sizeof (sadb_address_t) + sizeof (sadb_prop_t);
-
-	/* Make sure there's enough to cover both AF_INET and AF_INET6. */
-	allocsize += 2 * sizeof (struct sockaddr_in6);
-
-	rw_enter(&ipss->ipsec_alg_lock, RW_READER);
-	/* NOTE:  The lock is now held through to this function's return. */
-	allocsize += ipss->ipsec_nalgs[IPSEC_ALG_AUTH] *
-	    ipss->ipsec_nalgs[IPSEC_ALG_ENCR] * sizeof (sadb_comb_t);
-
-	if (tunnel_mode) {
-		/* Tunnel mode! */
-		allocsize += 2 * sizeof (sadb_address_t);
-		/* Enough to cover both AF_INET and AF_INET6. */
-		allocsize += 2 * sizeof (struct sockaddr_in6);
-	}
-
-	msgmp = allocb(allocsize, BPRI_HI);
-	if (msgmp == NULL) {
-		freeb(pfkeymp);
-		rw_exit(&ipss->ipsec_alg_lock);
-		return (NULL);
-	}
-
-	pfkeymp->b_cont = msgmp;
-	cur = msgmp->b_rptr;
-	end = cur + allocsize;
-	samsg = (sadb_msg_t *)cur;
-	cur += sizeof (sadb_msg_t);
-
-	af = acqrec->ipsacq_addrfam;
-	switch (af) {
-	case AF_INET:
-		check_proto = IPPROTO_ICMP;
-		break;
-	case AF_INET6:
-		check_proto = IPPROTO_ICMPV6;
-		break;
-	default:
-		/* This should never happen unless we have kernel bugs. */
-		cmn_err(CE_WARN,
-		    "sadb_setup_acquire:  corrupt ACQUIRE record.\n");
-		ASSERT(0);
-		rw_exit(&ipss->ipsec_alg_lock);
-		return (NULL);
-	}
-
-	samsg->sadb_msg_version = PF_KEY_V2;
-	samsg->sadb_msg_type = SADB_ACQUIRE;
-	samsg->sadb_msg_satype = satype;
-	samsg->sadb_msg_errno = 0;
-	samsg->sadb_msg_pid = 0;
-	samsg->sadb_msg_reserved = 0;
-	samsg->sadb_msg_seq = acqrec->ipsacq_seq;
-
-	ASSERT(MUTEX_HELD(&acqrec->ipsacq_lock));
-
-	if ((acqrec->ipsacq_proto == check_proto) || tunnel_mode) {
-		sport_typecode = dport_typecode = 0;
-	} else {
-		sport_typecode = acqrec->ipsacq_srcport;
-		dport_typecode = acqrec->ipsacq_dstport;
-	}
-
-	cur = sadb_make_addr_ext(cur, end, SADB_EXT_ADDRESS_SRC, af,
-	    acqrec->ipsacq_srcaddr, sport_typecode, acqrec->ipsacq_proto, 0);
-
-	cur = sadb_make_addr_ext(cur, end, SADB_EXT_ADDRESS_DST, af,
-	    acqrec->ipsacq_dstaddr, dport_typecode, acqrec->ipsacq_proto, 0);
-
-	if (tunnel_mode) {
-		sport_typecode = acqrec->ipsacq_srcport;
-		dport_typecode = acqrec->ipsacq_dstport;
-		cur = sadb_make_addr_ext(cur, end, SADB_X_EXT_ADDRESS_INNER_SRC,
-		    acqrec->ipsacq_inneraddrfam, acqrec->ipsacq_innersrc,
-		    sport_typecode, acqrec->ipsacq_inner_proto,
-		    acqrec->ipsacq_innersrcpfx);
-		cur = sadb_make_addr_ext(cur, end, SADB_X_EXT_ADDRESS_INNER_DST,
-		    acqrec->ipsacq_inneraddrfam, acqrec->ipsacq_innerdst,
-		    dport_typecode, acqrec->ipsacq_inner_proto,
-		    acqrec->ipsacq_innerdstpfx);
-	}
-
-	/* XXX Insert identity information here. */
-
-	/* XXXMLS Insert sensitivity information here. */
-
-	if (cur != NULL)
-		samsg->sadb_msg_len = SADB_8TO64(cur - msgmp->b_rptr);
-	else
-		rw_exit(&ipss->ipsec_alg_lock);
-
-	return (pfkeymp);
-}
-
-/*
  * Given an SADB_GETSPI message, find an appropriately ranged SA and
  * allocate an SA.  If there are message improprieties, return (ipsa_t *)-1.
  * If there was a memory allocation error, return NULL.	 (Assume NULL !=
@@ -6856,14 +7078,34 @@
 		}
 	}
 
-	/*
-	 * Now that we have a policy entry/widget, construct an ACQUIRE
-	 * message based on that, fix fields where appropriate,
-	 * and return the message.
-	 */
-	retmp = sadb_extended_acquire(&sel, pp, NULL,
-	    (itp != NULL && (itp->itp_flags & ITPF_P_TUNNEL)),
-	    samsg->sadb_msg_seq, samsg->sadb_msg_pid, sens, ns);
+	ASSERT(pp != NULL);
+	retmp = sadb_acquire_msg_base(0, 0, samsg->sadb_msg_seq,
+	    samsg->sadb_msg_pid);
+	if (retmp != NULL) {
+		/* Remove KEYSOCK_OUT, because caller constructs it instead. */
+		mblk_t *kso = retmp;
+
+		retmp = retmp->b_cont;
+		freeb(kso);
+		/* Append addresses... */
+		retmp->b_cont = sadb_acquire_msg_common(&sel, pp, NULL,
+		    (itp != NULL && (itp->itp_flags & ITPF_P_TUNNEL)), NULL,
+		    sens);
+		if (retmp->b_cont == NULL) {
+			freemsg(retmp);
+			retmp = NULL;
+		}
+		/* And the policy result. */
+		retmp->b_cont->b_cont =
+		    sadb_acquire_extended_prop(pp->ipsp_act, ns);
+		if (retmp->b_cont->b_cont == NULL) {
+			freemsg(retmp);
+			retmp = NULL;
+		}
+		((sadb_msg_t *)retmp->b_rptr)->sadb_msg_len =
+		    SADB_8TO64(msgsize(retmp));
+	}
+
 	if (pp != NULL) {
 		IPPOL_REFRELE(pp);
 	}
--- old/usr/src/uts/common/inet/ip/spd.c	Wed Jul 26 12:09:30 2017
+++ new/usr/src/uts/common/inet/ip/spd.c	Wed Jul 26 12:09:30 2017
@@ -23,6 +23,7 @@
  * Use is subject to license terms.
  * Copyright (c) 2012 Nexenta Systems, Inc. All rights reserved.
  * Copyright (c) 2016 by Delphix. All rights reserved.
+ * Copyright (c) 2017, Joyent, Inc.
  */
 
 /*
@@ -4688,8 +4689,9 @@
 }
 
 /*
- * Update the minimum and maximum supported key sizes for the
- * specified algorithm. Must be called while holding the algorithms lock.
+ * Update the minimum and maximum supported key sizes for the specified
+ * algorithm, which is either a member of a netstack alg array or about to be,
+ * and therefore must be called holding ipsec_alg_lock for write.
  */
 void
 ipsec_alg_fix_min_max(ipsec_alginfo_t *alg, ipsec_algtype_t alg_type,
--- old/usr/src/uts/common/inet/ipsecah.h	Wed Jul 26 12:09:31 2017
+++ new/usr/src/uts/common/inet/ipsecah.h	Wed Jul 26 12:09:31 2017
@@ -21,6 +21,7 @@
 /*
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright (c) 2012 Nexenta Systems, Inc. All rights reserved.
  */
 
 #ifndef	_INET_IPSECAH_H
@@ -114,6 +115,21 @@
 };
 typedef struct ipsecah_stack ipsecah_stack_t;
 
+#define	ipsecah_debug			ipsecah_params[0].ipsecah_param_value
+#define	ipsecah_age_interval		ipsecah_params[1].ipsecah_param_value
+#define	ipsecah_age_int_max		ipsecah_params[1].ipsecah_param_max
+#define	ipsecah_reap_delay		ipsecah_params[2].ipsecah_param_value
+#define	ipsecah_replay_size		ipsecah_params[3].ipsecah_param_value
+#define	ipsecah_acquire_timeout		ipsecah_params[4].ipsecah_param_value
+#define	ipsecah_larval_timeout		ipsecah_params[5].ipsecah_param_value
+#define	ipsecah_default_soft_bytes	ipsecah_params[6].ipsecah_param_value
+#define	ipsecah_default_hard_bytes	ipsecah_params[7].ipsecah_param_value
+#define	ipsecah_default_soft_addtime	ipsecah_params[8].ipsecah_param_value
+#define	ipsecah_default_hard_addtime	ipsecah_params[9].ipsecah_param_value
+#define	ipsecah_default_soft_usetime	ipsecah_params[10].ipsecah_param_value
+#define	ipsecah_default_hard_usetime	ipsecah_params[11].ipsecah_param_value
+#define	ipsecah_log_unknown_spi		ipsecah_params[12].ipsecah_param_value
+
 #endif	/* _KERNEL */
 
 /*
--- old/usr/src/uts/common/inet/ipsecesp.h	Wed Jul 26 12:09:31 2017
+++ new/usr/src/uts/common/inet/ipsecesp.h	Wed Jul 26 12:09:31 2017
@@ -21,6 +21,7 @@
 /*
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright (c) 2012 Nexenta Systems, Inc. All rights reserved.
  */
 
 #ifndef	_INET_IPSECESP_H
@@ -44,6 +45,52 @@
 } ipsecespparam_t;
 
 /*
+ * Stats.  This may eventually become a full-blown SNMP MIB once that spec
+ * stabilizes.
+ */
+
+typedef struct esp_kstats_s {
+	kstat_named_t esp_stat_num_aalgs;
+	kstat_named_t esp_stat_good_auth;
+	kstat_named_t esp_stat_bad_auth;
+	kstat_named_t esp_stat_bad_padding;
+	kstat_named_t esp_stat_replay_failures;
+	kstat_named_t esp_stat_replay_early_failures;
+	kstat_named_t esp_stat_keysock_in;
+	kstat_named_t esp_stat_out_requests;
+	kstat_named_t esp_stat_acquire_requests;
+	kstat_named_t esp_stat_bytes_expired;
+	kstat_named_t esp_stat_out_discards;
+	kstat_named_t esp_stat_crypto_sync;
+	kstat_named_t esp_stat_crypto_async;
+	kstat_named_t esp_stat_crypto_failures;
+	kstat_named_t esp_stat_num_ealgs;
+	kstat_named_t esp_stat_bad_decrypt;
+	kstat_named_t esp_stat_sa_port_renumbers;
+} esp_kstats_t;
+
+/*
+ * espstack->esp_kstats is equal to espstack->esp_ksp->ks_data if
+ * kstat_create_netstack for espstack->esp_ksp succeeds, but when it
+ * fails, it will be NULL. Note this is done for all stack instances,
+ * so it *could* fail. hence a non-NULL checking is done for
+ * ESP_BUMP_STAT and ESP_DEBUMP_STAT
+ */
+#define	ESP_BUMP_STAT(espstack, x)					\
+do {									\
+	if (espstack->esp_kstats != NULL)				\
+		(espstack->esp_kstats->esp_stat_ ## x).value.ui64++;	\
+_NOTE(CONSTCOND)							\
+} while (0)
+
+#define	ESP_DEBUMP_STAT(espstack, x)					\
+do {									\
+	if (espstack->esp_kstats != NULL)				\
+		(espstack->esp_kstats->esp_stat_ ## x).value.ui64--;	\
+_NOTE(CONSTCOND)							\
+} while (0)
+
+/*
  * IPSECESP stack instances
  */
 struct ipsecesp_stack {
@@ -72,7 +119,31 @@
 };
 typedef struct ipsecesp_stack ipsecesp_stack_t;
 
-/* Define *this* NDD variable here because we use it outside ESP proper. */
+#define	ipsecesp_debug	ipsecesp_params[0].ipsecesp_param_value
+#define	ipsecesp_age_interval ipsecesp_params[1].ipsecesp_param_value
+#define	ipsecesp_age_int_max	ipsecesp_params[1].ipsecesp_param_max
+#define	ipsecesp_reap_delay	ipsecesp_params[2].ipsecesp_param_value
+#define	ipsecesp_replay_size	ipsecesp_params[3].ipsecesp_param_value
+#define	ipsecesp_acquire_timeout	\
+	ipsecesp_params[4].ipsecesp_param_value
+#define	ipsecesp_larval_timeout	\
+	ipsecesp_params[5].ipsecesp_param_value
+#define	ipsecesp_default_soft_bytes	\
+	ipsecesp_params[6].ipsecesp_param_value
+#define	ipsecesp_default_hard_bytes	\
+	ipsecesp_params[7].ipsecesp_param_value
+#define	ipsecesp_default_soft_addtime	\
+	ipsecesp_params[8].ipsecesp_param_value
+#define	ipsecesp_default_hard_addtime	\
+	ipsecesp_params[9].ipsecesp_param_value
+#define	ipsecesp_default_soft_usetime	\
+	ipsecesp_params[10].ipsecesp_param_value
+#define	ipsecesp_default_hard_usetime	\
+	ipsecesp_params[11].ipsecesp_param_value
+#define	ipsecesp_log_unknown_spi	\
+	ipsecesp_params[12].ipsecesp_param_value
+#define	ipsecesp_padding_check	\
+	ipsecesp_params[13].ipsecesp_param_value
 #define	ipsecesp_nat_keepalive_interval	\
 	ipsecesp_params[14].ipsecesp_param_value
 
--- old/usr/src/uts/common/inet/sadb.h	Wed Jul 26 12:09:31 2017
+++ new/usr/src/uts/common/inet/sadb.h	Wed Jul 26 12:09:31 2017
@@ -568,8 +568,7 @@
 } sadb_t;
 
 /*
- * A pair of SADB's (one for v4, one for v6), and related state (including
- * acquire callbacks).
+ * A pair of SADB's (one for v4, one for v6), and related state.
  */
 
 typedef struct sadbp_s
@@ -576,7 +575,6 @@
 {
 	uint32_t	s_satype;
 	uint32_t	*s_acquire_timeout;
-	void 		(*s_acqfn)(ipsacq_t *, mblk_t *, netstack_t *);
 	sadb_t		s_v4;
 	sadb_t		s_v6;
 	uint32_t	s_addflags;
@@ -773,7 +771,6 @@
 
 void sadb_destroy_acquire(ipsacq_t *, netstack_t *);
 struct ipsec_stack;
-mblk_t *sadb_setup_acquire(ipsacq_t *, uint8_t, struct ipsec_stack *);
 ipsa_t *sadb_getspi(keysock_in_t *, uint32_t, int *, netstack_t *, uint_t);
 void sadb_in_acquire(sadb_msg_t *, sadbp_t *, queue_t *, netstack_t *);
 boolean_t sadb_replay_check(ipsa_t *, uint32_t);
