OpenBSD-4.6/sbin/isakmpd/pf_key_v2.c

Compare this file to the similar file:
Show the results in this format:

/* $OpenBSD: pf_key_v2.c,v 1.185 2009/01/28 17:57:15 hshoexer Exp $  */
/* $EOM: pf_key_v2.c,v 1.79 2000/12/12 00:33:19 niklas Exp $	 */

/*
 * Copyright (c) 1999, 2000, 2001 Niklas Hallqvist.  All rights reserved.
 * Copyright (c) 1999, 2000, 2001 Angelos D. Keromytis.  All rights reserved.
 * Copyright (c) 2001 Håkan Olsson.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * This code was written under funding by Ericsson Radio Systems.
 */

#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/queue.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/uio.h>

#include <net/pfkeyv2.h>
#include <netinet/in.h>
#include <netinet/ip_ipsp.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pwd.h>
#include <errno.h>
#include <bitstring.h>

#include "cert.h"
#include "conf.h"
#include "connection.h"
#include "exchange.h"
#include "ipsec.h"
#include "ipsec_num.h"
#include "key.h"
#include "log.h"
#include "pf_key_v2.h"
#include "sa.h"
#include "timer.h"
#include "transport.h"
#include "ui.h"
#include "util.h"

#include "policy.h"

#include "udp_encap.h"

#define IN6_IS_ADDR_FULL(a)						\
	((*(u_int32_t *)(void *)(&(a)->s6_addr[0]) == 0xffffffff) &&	\
	(*(u_int32_t *)(void *)(&(a)->s6_addr[4]) == 0xffffffff) &&	\
	(*(u_int32_t *)(void *)(&(a)->s6_addr[8]) == 0xffffffff) &&	\
	(*(u_int32_t *)(void *)(&(a)->s6_addr[12]) == 0xffffffff))

#define ADDRESS_MAX sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"

/*
 * PF_KEY v2 always work with 64-bit entities and aligns on 64-bit boundaries.
 */
#define PF_KEY_V2_CHUNK 8
#define PF_KEY_V2_ROUND(x)						\
	(((x) + PF_KEY_V2_CHUNK - 1) & ~(PF_KEY_V2_CHUNK - 1))

/* How many microseconds we will wait for a reply from the PF_KEY socket.  */
#define PF_KEY_REPLY_TIMEOUT 1000

struct pf_key_v2_node {
	TAILQ_ENTRY(pf_key_v2_node) link;
	void           *seg;
	size_t		sz;
	int		cnt;
	u_int16_t       type;
	u_int8_t	flags;
};

TAILQ_HEAD(pf_key_v2_msg, pf_key_v2_node);

#define PF_KEY_V2_NODE_MALLOCED 1
#define PF_KEY_V2_NODE_MARK 2

/* Used to derive "unique" connection identifiers. */
int		connection_seq = 0;

static u_int8_t *pf_key_v2_convert_id(u_int8_t *, int, size_t *, int *);
static struct pf_key_v2_msg *pf_key_v2_call(struct pf_key_v2_msg *);
static struct pf_key_v2_node *pf_key_v2_find_ext(struct pf_key_v2_msg *,
		    u_int16_t);
static void     pf_key_v2_notify(struct pf_key_v2_msg *);
static struct pf_key_v2_msg *pf_key_v2_read(u_int32_t);
static u_int32_t pf_key_v2_seq(void);
static u_int32_t pf_key_v2_write(struct pf_key_v2_msg *);
static int      pf_key_v2_remove_conf(char *);
static int      pf_key_v2_conf_refhandle(int, char *);

static int      pf_key_v2_conf_refinc(int, char *);

/* The socket to use for PF_KEY interactions.  */
int      pf_key_v2_socket;

static struct pf_key_v2_msg *
pf_key_v2_msg_new(struct sadb_msg *msg, int flags)
{
	struct pf_key_v2_node *node;
	struct pf_key_v2_msg *ret;

	node = malloc(sizeof *node);
	if (!node)
		goto cleanup;
	ret = malloc(sizeof *ret);
	if (!ret)
		goto cleanup;
	TAILQ_INIT(ret);
	node->seg = msg;
	node->sz = sizeof *msg;
	node->type = 0;
	node->cnt = 1;
	node->flags = flags;
	TAILQ_INSERT_HEAD(ret, node, link);
	return ret;

cleanup:
	free(node);
	return 0;
}

/* Add a SZ sized segment SEG to the PF_KEY message MSG.  */
static int
pf_key_v2_msg_add(struct pf_key_v2_msg *msg, struct sadb_ext *ext, int flags)
{
	struct pf_key_v2_node *node;

	node = malloc(sizeof *node);
	if (!node)
		return -1;
	node->seg = ext;
	node->sz = ext->sadb_ext_len * PF_KEY_V2_CHUNK;
	node->type = ext->sadb_ext_type;
	node->flags = flags;
	TAILQ_FIRST(msg)->cnt++;
	TAILQ_INSERT_TAIL(msg, node, link);
	return 0;
}

/* Deallocate the PF_KEY message MSG.  */
static void
pf_key_v2_msg_free(struct pf_key_v2_msg *msg)
{
	struct pf_key_v2_node *np;

	np = TAILQ_FIRST(msg);
	while (np) {
		TAILQ_REMOVE(msg, np, link);
		if (np->flags & PF_KEY_V2_NODE_MALLOCED)
			free(np->seg);
		free(np);
		np = TAILQ_FIRST(msg);
	}
	free(msg);
}

/* Just return a new sequence number.  */
static u_int32_t
pf_key_v2_seq(void)
{
	static u_int32_t seq = 0;

	return ++seq;
}

/*
 * Read a PF_KEY packet with SEQ as the sequence number, looping if necessary.
 * If SEQ is zero just read the first message we see, otherwise we queue
 * messages up until both the PID and the sequence number match.
 */
static struct pf_key_v2_msg *
pf_key_v2_read(u_int32_t seq)
{
	ssize_t		n;
	u_int8_t       *buf = 0;
	struct pf_key_v2_msg *ret = 0;
	struct sadb_msg *msg;
	struct sadb_msg hdr;
	struct sadb_ext *ext;
	struct timeval  tv;
	fd_set         *fds;

	while (1) {
		/*
		 * If this is a read of a reply we should actually expect the
		 * reply to get lost as PF_KEY is an unreliable service per
		 * the specs. Currently we do this by setting a short timeout,
		 * and if it is not readable in that time, we fail the read.
		 */
		if (seq) {
			fds = calloc(howmany(pf_key_v2_socket + 1, NFDBITS),
			    sizeof(fd_mask));
			if (!fds) {
				log_error("pf_key_v2_read: "
				    "calloc (%lu, %lu) failed",
				    (unsigned long) howmany(pf_key_v2_socket + 1,
					NFDBITS),
				    (unsigned long) sizeof(fd_mask));
				goto cleanup;
			}
			FD_SET(pf_key_v2_socket, fds);
			tv.tv_sec = 0;
			tv.tv_usec = PF_KEY_REPLY_TIMEOUT;
			n = select(pf_key_v2_socket + 1, fds, 0, 0, &tv);
			free(fds);
			if (n == -1) {
				log_error("pf_key_v2_read: "
				    "select (%d, fds, 0, 0, &tv) failed",
				    pf_key_v2_socket + 1);
				goto cleanup;
			}
			if (!n) {
				log_print("pf_key_v2_read: "
				    "no reply from PF_KEY");
				goto cleanup;
			}
		}
		n = recv(pf_key_v2_socket, &hdr, sizeof hdr, MSG_PEEK);
		if (n == -1) {
			log_error("pf_key_v2_read: recv (%d, ...) failed",
			    pf_key_v2_socket);
			goto cleanup;
		}
		if (n != sizeof hdr) {
			log_error("pf_key_v2_read: recv (%d, ...) "
			    "returned short packet (%lu bytes)",
			    pf_key_v2_socket, (unsigned long) n);
			goto cleanup;
		}
		n = hdr.sadb_msg_len * PF_KEY_V2_CHUNK;
		buf = malloc(n);
		if (!buf) {
			log_error("pf_key_v2_read: malloc (%lu) failed",
			    (unsigned long) n);
			goto cleanup;
		}
		n = read(pf_key_v2_socket, buf, n);
		if (n == -1) {
			log_error("pf_key_v2_read: read (%d, ...) failed",
				  pf_key_v2_socket);
			goto cleanup;
		}
		if (n != hdr.sadb_msg_len * PF_KEY_V2_CHUNK) {
			log_print("pf_key_v2_read: read (%d, ...) "
			    "returned short packet (%lu bytes)",
			    pf_key_v2_socket, (unsigned long) n);
			goto cleanup;
		}
		LOG_DBG_BUF((LOG_SYSDEP, 80, "pf_key_v2_read: msg", buf, n));

		/* We drop all messages that is not what we expect.  */
		msg = (struct sadb_msg *) buf;
		if (msg->sadb_msg_version != PF_KEY_V2 ||
		    (msg->sadb_msg_pid != 0 &&
		    msg->sadb_msg_pid != (u_int32_t) getpid())) {
			if (seq) {
				free(buf);
				buf = 0;
				continue;
			} else {
				LOG_DBG((LOG_SYSDEP, 90, "pf_key_v2_read:"
				    "bad version (%d) or PID (%d, mine is "
				    "%ld), ignored", msg->sadb_msg_version,
				    msg->sadb_msg_pid, (long) getpid()));
				goto cleanup;
			}
		}
		/* Parse the message.  */
		ret = pf_key_v2_msg_new(msg, PF_KEY_V2_NODE_MALLOCED);
		if (!ret)
			goto cleanup;
		buf = 0;
		for (ext = (struct sadb_ext *) (msg + 1);
		    (u_int8_t *) ext - (u_int8_t *) msg <
		    msg->sadb_msg_len * PF_KEY_V2_CHUNK;
		    ext = (struct sadb_ext *) ((u_int8_t *) ext +
		    ext->sadb_ext_len * PF_KEY_V2_CHUNK))
			pf_key_v2_msg_add(ret, ext, 0);

		/*
		 * If the message is not the one we are waiting for, queue it
		 * up.
		 */
		if (seq && (msg->sadb_msg_pid != (u_int32_t) getpid() ||
		    msg->sadb_msg_seq != seq)) {
			gettimeofday(&tv, 0);
			timer_add_event("pf_key_v2_notify",
			    (void (*) (void *)) pf_key_v2_notify, ret, &tv);
			ret = 0;
			continue;
		}
		return ret;
	}

cleanup:
	free(buf);
	if (ret)
		pf_key_v2_msg_free(ret);
	return 0;
}

/* Write the message in PMSG to the PF_KEY socket.  */
u_int32_t
pf_key_v2_write(struct pf_key_v2_msg *pmsg)
{
	struct iovec   *iov = 0;
	ssize_t		n;
	size_t		len;
	int		i, cnt = TAILQ_FIRST(pmsg)->cnt;
	char		header[80];
	struct sadb_msg *msg = TAILQ_FIRST(pmsg)->seg;
	struct pf_key_v2_node *np = TAILQ_FIRST(pmsg);

	iov = (struct iovec *) calloc(cnt, sizeof *iov);
	if (!iov) {
		log_error("pf_key_v2_write: malloc (%lu) failed",
		    cnt * (unsigned long) sizeof *iov);
		return 0;
	}
	msg->sadb_msg_version = PF_KEY_V2;
	msg->sadb_msg_errno = 0;
	msg->sadb_msg_reserved = 0;
	msg->sadb_msg_pid = getpid();
	if (!msg->sadb_msg_seq)
		msg->sadb_msg_seq = pf_key_v2_seq();

	/* Compute the iovec segments as well as the message length.  */
	len = 0;
	for (i = 0; i < cnt; i++) {
		iov[i].iov_base = np->seg;
		len += iov[i].iov_len = np->sz;

		/*
		 * XXX One can envision setting specific extension fields,
		 * like *_reserved ones here.  For now we require them to be
		 * set by the caller.
		 */

		np = TAILQ_NEXT(np, link);
	}
	msg->sadb_msg_len = len / PF_KEY_V2_CHUNK;

	for (i = 0; i < cnt; i++) {
		snprintf(header, sizeof header, "pf_key_v2_write: iov[%d]", i);
		LOG_DBG_BUF((LOG_SYSDEP, 80, header,
		    (u_int8_t *) iov[i].iov_base, iov[i].iov_len));
	}

	do {
		n = writev(pf_key_v2_socket, iov, cnt);
	} while (n == -1 && (errno == EAGAIN || errno == EINTR));
	if (n == -1) {
		log_error("pf_key_v2_write: writev (%d, %p, %d) failed",
		    pf_key_v2_socket, iov, cnt);
		goto cleanup;
	}
	if ((size_t) n != len) {
		log_error("pf_key_v2_write: "
		    "writev (%d, ...) returned prematurely (%lu)",
		    pf_key_v2_socket, (unsigned long) n);
		goto cleanup;
	}
	free(iov);
	return msg->sadb_msg_seq;

cleanup:
	free(iov);
	return 0;
}

/*
 * Do a PF_KEY "call", i.e. write a message MSG, read the reply and return
 * it to the caller.
 */
static struct pf_key_v2_msg *
pf_key_v2_call(struct pf_key_v2_msg *msg)
{
	u_int32_t       seq;

	seq = pf_key_v2_write(msg);
	if (!seq)
		return 0;
	return pf_key_v2_read(seq);
}

/* Find the TYPE extension in MSG.  Return zero if none found.  */
static struct pf_key_v2_node *
pf_key_v2_find_ext(struct pf_key_v2_msg *msg, u_int16_t type)
{
	struct pf_key_v2_node *ext;

	for (ext = TAILQ_NEXT(TAILQ_FIRST(msg), link); ext;
	    ext = TAILQ_NEXT(ext, link))
		if (ext->type == type)
			return ext;
	return 0;
}

/*
 * Open the PF_KEYv2 sockets and return the descriptor used for notifies.
 * Return -1 for failure and -2 if no notifies will show up.
 */
int
pf_key_v2_open(void)
{
	int		fd = -1, err;
	struct sadb_msg msg;
	struct pf_key_v2_msg *regmsg = 0, *ret = 0;

	/* Open the socket we use to speak to IPsec. */
	pf_key_v2_socket = -1;
	fd = socket(PF_KEY, SOCK_RAW, PF_KEY_V2);
	if (fd == -1) {
		log_error("pf_key_v2_open: "
		    "socket (PF_KEY, SOCK_RAW, PF_KEY_V2) failed");
		goto cleanup;
	}
	pf_key_v2_socket = fd;

	/* Register it to get ESP and AH acquires from the kernel.  */
	msg.sadb_msg_seq = 0;
	msg.sadb_msg_type = SADB_REGISTER;
	msg.sadb_msg_satype = SADB_SATYPE_ESP;
	regmsg = pf_key_v2_msg_new(&msg, 0);
	if (!regmsg)
		goto cleanup;
	ret = pf_key_v2_call(regmsg);
	pf_key_v2_msg_free(regmsg);
	if (!ret)
		goto cleanup;
	err = ((struct sadb_msg *)TAILQ_FIRST(ret)->seg)->sadb_msg_errno;
	if (err) {
		log_print("pf_key_v2_open: REGISTER: %s", strerror(err));
		goto cleanup;
	}
	/* XXX Register the accepted transforms.  */

	pf_key_v2_msg_free(ret);
	ret = 0;

	msg.sadb_msg_seq = 0;
	msg.sadb_msg_type = SADB_REGISTER;
	msg.sadb_msg_satype = SADB_SATYPE_AH;
	regmsg = pf_key_v2_msg_new(&msg, 0);
	if (!regmsg)
		goto cleanup;
	ret = pf_key_v2_call(regmsg);
	pf_key_v2_msg_free(regmsg);
	if (!ret)
		goto cleanup;
	err = ((struct sadb_msg *)TAILQ_FIRST(ret)->seg)->sadb_msg_errno;
	if (err) {
		log_print("pf_key_v2_open: REGISTER: %s", strerror(err));
		goto cleanup;
	}
	/* XXX Register the accepted transforms.  */

	pf_key_v2_msg_free(ret);
	ret = 0;

	msg.sadb_msg_seq = 0;
	msg.sadb_msg_type = SADB_REGISTER;
	msg.sadb_msg_satype = SADB_X_SATYPE_IPCOMP;
	regmsg = pf_key_v2_msg_new(&msg, 0);
	if (!regmsg)
		goto cleanup;
	ret = pf_key_v2_call(regmsg);
	pf_key_v2_msg_free(regmsg);
	if (!ret)
		goto cleanup;
	err = ((struct sadb_msg *)TAILQ_FIRST(ret)->seg)->sadb_msg_errno;
	if (err) {
		log_print("pf_key_v2_open: REGISTER: %s", strerror(err));
		goto cleanup;
	}
	/* XXX Register the accepted transforms.  */

	pf_key_v2_msg_free(ret);

	return fd;

cleanup:
	if (pf_key_v2_socket != -1) {
		close(pf_key_v2_socket);
		pf_key_v2_socket = -1;
	}
	if (ret)
		pf_key_v2_msg_free(ret);
	return -1;
}

/*
 * Generate a SPI for protocol PROTO and the source/destination pair given by
 * SRC, SRCLEN, DST & DSTLEN.  Stash the SPI size in SZ.
 */
u_int8_t *
pf_key_v2_get_spi(size_t *sz, u_int8_t proto, struct sockaddr *src,
    struct sockaddr *dst, u_int32_t seq)
{
	struct sadb_msg msg;
	struct sadb_sa *sa;
	struct sadb_address *addr = 0;
	struct sadb_spirange spirange;
	struct pf_key_v2_msg *getspi = 0, *ret = 0;
	struct pf_key_v2_node *ext;
	u_int8_t       *spi = 0;
	int		len, err;

	msg.sadb_msg_type = SADB_GETSPI;
	switch (proto) {
	case IPSEC_PROTO_IPSEC_ESP:
		msg.sadb_msg_satype = SADB_SATYPE_ESP;
		break;
	case IPSEC_PROTO_IPSEC_AH:
		msg.sadb_msg_satype = SADB_SATYPE_AH;
		break;
	case IPSEC_PROTO_IPCOMP:
		msg.sadb_msg_satype = SADB_X_SATYPE_IPCOMP;
		break;
	default:
		log_print("pf_key_v2_get_spi: invalid proto %d", proto);
		goto cleanup;
	}

	/* Set the sequence number from the ACQUIRE message. */
	msg.sadb_msg_seq = seq;
	getspi = pf_key_v2_msg_new(&msg, 0);
	if (!getspi)
		goto cleanup;

	/* Setup the ADDRESS extensions.  */
	len =
	    sizeof(struct sadb_address) + PF_KEY_V2_ROUND(SA_LEN(src));
	addr = calloc(1, len);
	if (!addr)
		goto cleanup;
	addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
	addr->sadb_address_len = len / PF_KEY_V2_CHUNK;
	addr->sadb_address_reserved = 0;
	memcpy(addr + 1, src, SA_LEN(src));
	switch (((struct sockaddr *) (addr + 1))->sa_family) {
	case AF_INET:
		((struct sockaddr_in *) (addr + 1))->sin_port = 0;
		break;
	case AF_INET6:
		((struct sockaddr_in6 *) (addr + 1))->sin6_port = 0;
		break;
	}
	if (pf_key_v2_msg_add(getspi, (struct sadb_ext *) addr,
	    PF_KEY_V2_NODE_MALLOCED) == -1)
		goto cleanup;
	addr = 0;

	len = sizeof(struct sadb_address) + PF_KEY_V2_ROUND(SA_LEN(dst));
	addr = calloc(1, len);
	if (!addr)
		goto cleanup;
	addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
	addr->sadb_address_len = len / PF_KEY_V2_CHUNK;
	addr->sadb_address_reserved = 0;
	memcpy(addr + 1, dst, SA_LEN(dst));
	switch (((struct sockaddr *) (addr + 1))->sa_family) {
	case AF_INET:
		((struct sockaddr_in *) (addr + 1))->sin_port = 0;
		break;
	case AF_INET6:
		((struct sockaddr_in6 *) (addr + 1))->sin6_port = 0;
		break;
	}
	if (pf_key_v2_msg_add(getspi, (struct sadb_ext *) addr,
	    PF_KEY_V2_NODE_MALLOCED) == -1)
		goto cleanup;
	addr = 0;

	/* Setup the SPIRANGE extension.  */
	spirange.sadb_spirange_exttype = SADB_EXT_SPIRANGE;
	spirange.sadb_spirange_len = sizeof spirange / PF_KEY_V2_CHUNK;
	if (proto == IPSEC_PROTO_IPCOMP) {
		spirange.sadb_spirange_min = CPI_RESERVED_MAX + 1;
		spirange.sadb_spirange_max = CPI_PRIVATE_MIN - 1;
	} else {
		spirange.sadb_spirange_min = IPSEC_SPI_LOW;
		spirange.sadb_spirange_max = 0xffffffff;
	}
	spirange.sadb_spirange_reserved = 0;
	if (pf_key_v2_msg_add(getspi, (struct sadb_ext *)&spirange, 0) == -1)
		goto cleanup;

	ret = pf_key_v2_call(getspi);
	pf_key_v2_msg_free(getspi);
	getspi = 0;
	if (!ret)
		goto cleanup;
	err = ((struct sadb_msg *)TAILQ_FIRST(ret)->seg)->sadb_msg_errno;
	if (err) {
		log_print("pf_key_v2_get_spi: GETSPI: %s", strerror(err));
		goto cleanup;
	}
	ext = pf_key_v2_find_ext(ret, SADB_EXT_SA);
	if (!ext) {
		log_print("pf_key_v2_get_spi: no SA extension found");
		goto cleanup;
	}
	sa = ext->seg;

	/* IPCOMP CPIs are only 16 bits long.  */
	*sz = (proto == IPSEC_PROTO_IPCOMP) ? sizeof(u_int16_t)
		: sizeof sa->sadb_sa_spi;
	spi = malloc(*sz);
	if (!spi)
		goto cleanup;
	/* XXX This is ugly.  */
	if (proto == IPSEC_PROTO_IPCOMP) {
		u_int32_t       tspi = ntohl(sa->sadb_sa_spi);
		*(u_int16_t *) spi = htons((u_int16_t) tspi);
	} else
		memcpy(spi, &sa->sadb_sa_spi, *sz);

	pf_key_v2_msg_free(ret);

	LOG_DBG_BUF((LOG_SYSDEP, 50, "pf_key_v2_get_spi: spi", spi, *sz));
	return spi;

cleanup:
	free(spi);
	free(addr);
	if (getspi)
		pf_key_v2_msg_free(getspi);
	if (ret)
		pf_key_v2_msg_free(ret);
	return 0;
}

/* Fetch SA information from the kernel. XXX OpenBSD only?  */
struct sa_kinfo *
pf_key_v2_get_kernel_sa(u_int8_t *spi, size_t spi_sz, u_int8_t proto,
    struct sockaddr *dst)
{
	struct sadb_msg msg;
	struct sadb_sa *ssa;
	struct sadb_address *addr = 0;
	struct sockaddr *sa;
	struct sadb_lifetime *life;
	struct pf_key_v2_msg *gettdb = 0, *ret = 0;
	struct pf_key_v2_node *ext;
	static struct sa_kinfo ksa;
	struct sadb_x_udpencap *udpencap;
	int len, err;

	if (spi_sz != sizeof (ssa->sadb_sa_spi))
		return 0;

	msg.sadb_msg_type = SADB_GET;
	switch (proto) {
	case IPSEC_PROTO_IPSEC_ESP:
		msg.sadb_msg_satype = SADB_SATYPE_ESP;
		break;
	case IPSEC_PROTO_IPSEC_AH:
		msg.sadb_msg_satype = SADB_SATYPE_AH;
		break;
	case IPSEC_PROTO_IPCOMP:
		msg.sadb_msg_satype = SADB_X_SATYPE_IPCOMP;
		break;
	default:
		log_print("pf_key_v2_get_kernel_sa: invalid proto %d", proto);
		goto cleanup;
	}

	gettdb = pf_key_v2_msg_new(&msg, 0);
	if (!gettdb)
		goto cleanup;

	/* SPI */
	ssa = (struct sadb_sa *)calloc(1, sizeof *ssa);
	if (!ssa) {
		log_print("pf_key_v2_get_kernel_sa: calloc(1, %lu) failed",
		    (unsigned long)sizeof *ssa);
		goto cleanup;
	}

	ssa->sadb_sa_exttype = SADB_EXT_SA;
	ssa->sadb_sa_len = sizeof *ssa / PF_KEY_V2_CHUNK;
	memcpy(&ssa->sadb_sa_spi, spi, sizeof ssa->sadb_sa_spi);
	ssa->sadb_sa_state = SADB_SASTATE_MATURE;
	if (pf_key_v2_msg_add(gettdb, (struct sadb_ext *)ssa,
	    PF_KEY_V2_NODE_MALLOCED) == -1)
		goto cleanup;
	ssa = 0;

	/* Address */
	len =
	    sizeof(struct sadb_address) + PF_KEY_V2_ROUND(SA_LEN(dst));
	addr = calloc(1, len);
	if (!addr)
		goto cleanup;
	addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
	addr->sadb_address_len = len / PF_KEY_V2_CHUNK;
	addr->sadb_address_reserved = 0;
	memcpy(addr + 1, dst, SA_LEN(dst));
	switch (((struct sockaddr *) (addr + 1))->sa_family) {
	case AF_INET:
		((struct sockaddr_in *) (addr + 1))->sin_port = 0;
		break;
	case AF_INET6:
		((struct sockaddr_in6 *) (addr + 1))->sin6_port = 0;
		break;
	}
	if (pf_key_v2_msg_add(gettdb, (struct sadb_ext *)addr,
	    PF_KEY_V2_NODE_MALLOCED) == -1)
		goto cleanup;
	addr = 0;

	ret = pf_key_v2_call(gettdb);
	pf_key_v2_msg_free(gettdb);
	gettdb = 0;
	if (!ret)
		goto cleanup;
	err = ((struct sadb_msg *)TAILQ_FIRST(ret)->seg)->sadb_msg_errno;
	if (err) {
		log_print("pf_key_v2_get_kernel_sa: SADB_GET: %s",
		    strerror(err));
		goto cleanup;
	}

	/* Extract the data.  */
	bzero(&ksa, sizeof ksa);

	ext = pf_key_v2_find_ext(ret, SADB_EXT_SA);
	if (!ext)
		goto cleanup;

	ssa = (struct sadb_sa *)ext;
	ksa.spi = ssa->sadb_sa_spi;
	ksa.wnd = ssa->sadb_sa_replay;
	ksa.flags = ssa->sadb_sa_flags;

	ext = pf_key_v2_find_ext(ret, SADB_EXT_LIFETIME_CURRENT);
	if (ext) {
		life = (struct sadb_lifetime *)ext->seg;
		ksa.cur_allocations = life->sadb_lifetime_allocations;
		ksa.cur_bytes =	life->sadb_lifetime_bytes;
		ksa.first_use = life->sadb_lifetime_usetime;
		ksa.established = life->sadb_lifetime_addtime;
	}

	ext = pf_key_v2_find_ext(ret, SADB_EXT_LIFETIME_SOFT);
	if (ext) {
		life = (struct sadb_lifetime *)ext->seg;
		ksa.soft_allocations = life->sadb_lifetime_allocations;
		ksa.soft_bytes = life->sadb_lifetime_bytes;
		ksa.soft_timeout = life->sadb_lifetime_addtime;
		ksa.soft_first_use = life->sadb_lifetime_usetime;
	}

	ext = pf_key_v2_find_ext(ret, SADB_EXT_LIFETIME_HARD);
	if (ext) {
		life = (struct sadb_lifetime *)ext->seg;
		ksa.exp_allocations = life->sadb_lifetime_allocations;
		ksa.exp_bytes = life->sadb_lifetime_bytes;
		ksa.exp_timeout = life->sadb_lifetime_addtime;
		ksa.exp_first_use = life->sadb_lifetime_usetime;
	}

	ext = pf_key_v2_find_ext(ret, SADB_X_EXT_LIFETIME_LASTUSE);
	if (ext) {
		life = (struct sadb_lifetime *)ext->seg;
		ksa.last_used = life->sadb_lifetime_usetime;
	}

	ext = pf_key_v2_find_ext(ret, SADB_EXT_ADDRESS_SRC);
	if (ext) {
		sa = (struct sockaddr *)ext->seg;
		memcpy(&ksa.src, sa,
		    sa->sa_family == AF_INET ? sizeof(struct sockaddr_in) :
		    sizeof(struct sockaddr_in6));
	}

	ext = pf_key_v2_find_ext(ret, SADB_EXT_ADDRESS_DST);
	if (ext) {
		sa = (struct sockaddr *)ext->seg;
		memcpy(&ksa.dst, sa,
		    sa->sa_family == AF_INET ? sizeof(struct sockaddr_in) :
		    sizeof(struct sockaddr_in6));
	}

	ext = pf_key_v2_find_ext(ret, SADB_EXT_ADDRESS_PROXY);
	if (ext) {
		sa = (struct sockaddr *)ext->seg;
		memcpy(sa, &ksa.proxy,
		    sa->sa_family == AF_INET ? sizeof(struct sockaddr_in) :
		    sizeof(struct sockaddr_in6));
	}

	ext = pf_key_v2_find_ext(ret, SADB_X_EXT_UDPENCAP);
	if (ext) {
		udpencap = (struct sadb_x_udpencap *)ext->seg;
		ksa.udpencap_port = udpencap->sadb_x_udpencap_port;
	}

	pf_key_v2_msg_free(ret);

	LOG_DBG_BUF((LOG_SYSDEP, 50, "pf_key_v2_get_kernel_sa: spi", spi,
	    spi_sz));

	return &ksa;

  cleanup:
	if (addr)
		free (addr);
	if (gettdb)
		pf_key_v2_msg_free(gettdb);
	if (ret)
		pf_key_v2_msg_free(ret);
	return 0;
}

static void
pf_key_v2_setup_sockaddr(void *res, struct sockaddr *src,
    struct sockaddr *dst, in_port_t port, int ingress)
{
	struct sockaddr_in *ip4_sa;
	struct sockaddr_in6 *ip6_sa;
	u_int8_t       *p;

	switch (src->sa_family) {
	case AF_INET:
		ip4_sa = (struct sockaddr_in *) res;
		ip4_sa->sin_family = AF_INET;
		ip4_sa->sin_len = sizeof *ip4_sa;
		ip4_sa->sin_port = port;
		if (dst)
			p = (u_int8_t *) (ingress ?
			    &((struct sockaddr_in *)src)->sin_addr.s_addr :
			    &((struct sockaddr_in *)dst)->sin_addr.s_addr);
		else
			p = (u_int8_t *)&((struct sockaddr_in *)src)->sin_addr.s_addr;
		ip4_sa->sin_addr.s_addr = *((in_addr_t *) p);
		break;

	case AF_INET6:
		ip6_sa = (struct sockaddr_in6 *) res;
		ip6_sa->sin6_family = AF_INET6;
		ip6_sa->sin6_len = sizeof *ip6_sa;
		ip6_sa->sin6_port = port;
		if (dst)
			p = (u_int8_t *) (ingress ?
			    &((struct sockaddr_in6 *)src)->sin6_addr.s6_addr :
			    &((struct sockaddr_in6 *)dst)->sin6_addr.s6_addr);
		else
			p = (u_int8_t *)&((struct sockaddr_in6 *)src)->sin6_addr.s6_addr;
		memcpy(ip6_sa->sin6_addr.s6_addr, p, sizeof(struct in6_addr));
		break;

	default:
		log_print("pf_key_v2_setup_sockaddr: unknown family %d\n",
		    src->sa_family);
		break;
	}
}

/*
 * Store/update a PF_KEY_V2 security association with full information from the
 * IKE SA and PROTO into the kernel.  INCOMING is set if we are setting the
 * parameters for the incoming SA, and cleared otherwise.
 */
int
pf_key_v2_set_spi(struct sa *sa, struct proto *proto, int incoming,
    struct sa *isakmp_sa)
{
	struct sadb_msg msg;
	struct sadb_sa  ssa;
	struct sadb_x_tag *stag = NULL;
	struct sadb_lifetime *life = 0;
	struct sadb_address *addr = 0;
	struct sadb_key *key = 0;
	struct sadb_ident *sid = 0;
	struct sockaddr *src, *dst;
	struct pf_key_v2_msg *update = 0, *ret = 0;
	struct ipsec_proto *iproto = proto->data;
	size_t		len;
	int		keylen, hashlen, err;
	u_int8_t       *pp;
	int		idtype;
	struct ipsec_sa *isa = sa->data;
	struct sadb_x_cred *cred;
	struct sadb_protocol flowtype, tprotocol;
	struct sadb_x_udpencap udpencap;
	char           *addr_str, *s;

	msg.sadb_msg_type = incoming ? SADB_UPDATE : SADB_ADD;
	switch (proto->proto) {
	case IPSEC_PROTO_IPSEC_ESP:
		msg.sadb_msg_satype = SADB_SATYPE_ESP;
		keylen = ipsec_esp_enckeylength(proto);
		hashlen = ipsec_esp_authkeylength(proto);

		switch (proto->id) {
		case IPSEC_ESP_DES:
		case IPSEC_ESP_DES_IV32:
		case IPSEC_ESP_DES_IV64:
			ssa.sadb_sa_encrypt = SADB_EALG_DESCBC;
			break;

		case IPSEC_ESP_3DES:
			ssa.sadb_sa_encrypt = SADB_EALG_3DESCBC;
			break;

		case IPSEC_ESP_AES:
			ssa.sadb_sa_encrypt = SADB_X_EALG_AES;
			break;

		case IPSEC_ESP_AES_128_CTR:
			ssa.sadb_sa_encrypt = SADB_X_EALG_AESCTR;
			break;

		case IPSEC_ESP_CAST:
			ssa.sadb_sa_encrypt = SADB_X_EALG_CAST;
			break;

		case IPSEC_ESP_BLOWFISH:
			ssa.sadb_sa_encrypt = SADB_X_EALG_BLF;
			break;

		case IPSEC_ESP_NULL:
			ssa.sadb_sa_encrypt = SADB_EALG_NULL;
			break;

		default:
			LOG_DBG((LOG_SYSDEP, 50, "pf_key_v2_set_spi: "
			    "unknown encryption algorithm %d", proto->id));
			return -1;
		}

		switch (iproto->auth) {
		case IPSEC_AUTH_HMAC_MD5:
			ssa.sadb_sa_auth = SADB_AALG_MD5HMAC;
			break;

		case IPSEC_AUTH_HMAC_SHA:
			ssa.sadb_sa_auth = SADB_AALG_SHA1HMAC;
			break;

		case IPSEC_AUTH_HMAC_RIPEMD:
			ssa.sadb_sa_auth = SADB_X_AALG_RIPEMD160HMAC;
			break;

		case IPSEC_AUTH_HMAC_SHA2_256:
			ssa.sadb_sa_auth = SADB_X_AALG_SHA2_256;
			break;

		case IPSEC_AUTH_HMAC_SHA2_384:
			ssa.sadb_sa_auth = SADB_X_AALG_SHA2_384;
			break;

		case IPSEC_AUTH_HMAC_SHA2_512:
			ssa.sadb_sa_auth = SADB_X_AALG_SHA2_512;
			break;

		case IPSEC_AUTH_DES_MAC:
		case IPSEC_AUTH_KPDK:
			/* XXX We should be supporting KPDK */
			LOG_DBG((LOG_SYSDEP, 50, "pf_key_v2_set_spi: "
			    "unknown authentication algorithm %d",
			    iproto->auth));
			return -1;

		default:
			ssa.sadb_sa_auth = SADB_AALG_NONE;
		}
		break;

	case IPSEC_PROTO_IPSEC_AH:
		msg.sadb_msg_satype = SADB_SATYPE_AH;
		hashlen = ipsec_ah_keylength(proto);
		keylen = 0;

		ssa.sadb_sa_encrypt = SADB_EALG_NONE;
		switch (proto->id) {
		case IPSEC_AH_MD5:
			ssa.sadb_sa_auth = SADB_AALG_MD5HMAC;
			break;

		case IPSEC_AH_SHA:
			ssa.sadb_sa_auth = SADB_AALG_SHA1HMAC;
			break;

		case IPSEC_AH_RIPEMD:
			ssa.sadb_sa_auth = SADB_X_AALG_RIPEMD160HMAC;
			break;

		case IPSEC_AH_SHA2_256:
			ssa.sadb_sa_auth = SADB_X_AALG_SHA2_256;
			break;

		case IPSEC_AH_SHA2_384:
			ssa.sadb_sa_auth = SADB_X_AALG_SHA2_384;
			break;

		case IPSEC_AH_SHA2_512:
			ssa.sadb_sa_auth = SADB_X_AALG_SHA2_512;
			break;

		default:
			LOG_DBG((LOG_SYSDEP, 50, "pf_key_v2_set_spi: "
			    "unknown authentication algorithm %d", proto->id));
			goto cleanup;
		}
		break;

	case IPSEC_PROTO_IPCOMP:
		msg.sadb_msg_satype = SADB_X_SATYPE_IPCOMP;
		ssa.sadb_sa_auth = SADB_AALG_NONE;
		keylen = 0;
		hashlen = 0;

		/*
		 * Put compression algorithm type in the sadb_sa_encrypt
		 * field.
		 */
		switch (proto->id) {
		case IPSEC_IPCOMP_OUI:
			ssa.sadb_sa_encrypt = SADB_X_CALG_OUI;
			break;

		case IPSEC_IPCOMP_DEFLATE:
			ssa.sadb_sa_encrypt = SADB_X_CALG_DEFLATE;
			break;

		case IPSEC_IPCOMP_LZS:
			ssa.sadb_sa_encrypt = SADB_X_CALG_LZS;
			break;

		default:
			break;
		}
		break;

	default:
		log_print("pf_key_v2_set_spi: invalid proto %d", proto->proto);
		goto cleanup;
	}
	if (incoming)
		sa->transport->vtbl->get_src(sa->transport, &dst);
	else
		sa->transport->vtbl->get_dst(sa->transport, &dst);
	msg.sadb_msg_seq = sa->seq;
	update = pf_key_v2_msg_new(&msg, 0);
	if (!update)
		goto cleanup;

	/* Setup the rest of the SA extension.  */
	ssa.sadb_sa_exttype = SADB_EXT_SA;
	ssa.sadb_sa_len = sizeof ssa / PF_KEY_V2_CHUNK;
	if (proto->spi_sz[incoming] == 2)	/* IPCOMP uses 16bit CPIs.  */
		ssa.sadb_sa_spi = htonl(proto->spi[incoming][0] << 8 |
		    proto->spi[incoming][1]);
	else
		memcpy(&ssa.sadb_sa_spi, proto->spi[incoming],
		    sizeof ssa.sadb_sa_spi);
	ssa.sadb_sa_replay = conf_get_str("General", "Shared-SADB") ? 0 :
	    iproto->replay_window;
	ssa.sadb_sa_state = SADB_SASTATE_MATURE;
	ssa.sadb_sa_flags = 0;
	if (iproto->encap_mode == IPSEC_ENCAP_TUNNEL ||
	    iproto->encap_mode == IPSEC_ENCAP_UDP_ENCAP_TUNNEL ||
	    iproto->encap_mode == IPSEC_ENCAP_UDP_ENCAP_TUNNEL_DRAFT)
		ssa.sadb_sa_flags = SADB_X_SAFLAGS_TUNNEL;

	if (isakmp_sa->flags & SA_FLAG_NAT_T_ENABLE) {
		bzero(&udpencap, sizeof udpencap);
		ssa.sadb_sa_flags |= SADB_X_SAFLAGS_UDPENCAP;
		udpencap.sadb_x_udpencap_exttype = SADB_X_EXT_UDPENCAP;
		udpencap.sadb_x_udpencap_len =
		    sizeof udpencap / PF_KEY_V2_CHUNK;
		udpencap.sadb_x_udpencap_port = sockaddr_port(dst);
		if (pf_key_v2_msg_add(update, (struct sadb_ext *)&udpencap, 0)
		    == -1)
			goto cleanup;
	}

	if (pf_key_v2_msg_add(update, (struct sadb_ext *)&ssa, 0) == -1)
		goto cleanup;

	if (sa->seconds || sa->kilobytes) {
		/* Setup the hard limits.  */
		life = malloc(sizeof *life);
		if (!life)
			goto cleanup;
		life->sadb_lifetime_len = sizeof *life / PF_KEY_V2_CHUNK;
		life->sadb_lifetime_exttype = SADB_EXT_LIFETIME_HARD;
		life->sadb_lifetime_allocations = 0;
		life->sadb_lifetime_bytes = sa->kilobytes * 1024;
		/*
		 * XXX I am not sure which one is best in security respect.
		 * Maybe the RFCs actually mandate what a lifetime really is.
		 */
#if 0
		life->sadb_lifetime_addtime = 0;
		life->sadb_lifetime_usetime = sa->seconds;
#else
		life->sadb_lifetime_addtime = sa->seconds;
		life->sadb_lifetime_usetime = 0;
#endif
		if (pf_key_v2_msg_add(update, (struct sadb_ext *) life,
		    PF_KEY_V2_NODE_MALLOCED) == -1)
			goto cleanup;
		life = 0;

		/*
		 * Setup the soft limits, we use 90 % of the hard ones.
		 * XXX A configurable ratio would be better.
		 */
		life = malloc(sizeof *life);
		if (!life)
			goto cleanup;
		life->sadb_lifetime_len = sizeof *life / PF_KEY_V2_CHUNK;
		life->sadb_lifetime_exttype = SADB_EXT_LIFETIME_SOFT;
		life->sadb_lifetime_allocations = 0;
		life->sadb_lifetime_bytes = sa->kilobytes * 1024 * 9 / 10;
		/*
		 * XXX I am not sure which one is best in security respect.
		 * Maybe the RFCs actually mandate what a lifetime really is.
		 */
#if 0
		life->sadb_lifetime_addtime = 0;
		life->sadb_lifetime_usetime = sa->seconds * 9 / 10;
#else
		life->sadb_lifetime_addtime = sa->seconds * 9 / 10;
		life->sadb_lifetime_usetime = 0;
#endif
		if (pf_key_v2_msg_add(update, (struct sadb_ext *) life,
		    PF_KEY_V2_NODE_MALLOCED) == -1)
			goto cleanup;
		life = 0;
	}
	/*
	 * Setup the ADDRESS extensions.
	 */
	if (incoming)
		sa->transport->vtbl->get_dst(sa->transport, &src);
	else
		sa->transport->vtbl->get_src(sa->transport, &src);
	len = sizeof *addr + PF_KEY_V2_ROUND(SA_LEN(src));
	addr = calloc(1, len);
	if (!addr)
		goto cleanup;
	addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
	addr->sadb_address_len = len / PF_KEY_V2_CHUNK;
	addr->sadb_address_reserved = 0;
	memcpy(addr + 1, src, SA_LEN(src));
	switch (((struct sockaddr *) (addr + 1))->sa_family) {
	case AF_INET:
		((struct sockaddr_in *) (addr + 1))->sin_port = 0;
		break;
	case AF_INET6:
		((struct sockaddr_in6 *) (addr + 1))->sin6_port = 0;
		break;
	}
	if (pf_key_v2_msg_add(update, (struct sadb_ext *) addr,
	    PF_KEY_V2_NODE_MALLOCED) == -1)
		goto cleanup;
	addr = 0;

	len = sizeof *addr + PF_KEY_V2_ROUND(SA_LEN(dst));
	addr = calloc(1, len);
	if (!addr)
		goto cleanup;
	addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
	addr->sadb_address_len = len / PF_KEY_V2_CHUNK;
	addr->sadb_address_reserved = 0;
	memcpy(addr + 1, dst, SA_LEN(dst));
	switch (((struct sockaddr *) (addr + 1))->sa_family) {
	case AF_INET:
		((struct sockaddr_in *) (addr + 1))->sin_port = 0;
		break;
	case AF_INET6:
		((struct sockaddr_in6 *) (addr + 1))->sin6_port = 0;
		break;
	}
	if (pf_key_v2_msg_add(update, (struct sadb_ext *) addr,
	    PF_KEY_V2_NODE_MALLOCED) == -1)
		goto cleanup;
	addr = 0;

#if 0
	/* XXX I am not sure about what to do here just yet.  */
	if (iproto->encap_mode == IPSEC_ENCAP_TUNNEL) {
		len = sizeof *addr + PF_KEY_V2_ROUND(SA_LEN(dst));
		addr = calloc(1, len);
		if (!addr)
			goto cleanup;
		addr->sadb_address_exttype = SADB_EXT_ADDRESS_PROXY;
		addr->sadb_address_len = len / PF_KEY_V2_CHUNK;
		addr->sadb_address_reserved = 0;
		memcpy(addr + 1, dst, SA_LEN(dst));
		switch (((struct sockaddr *) (addr + 1))->sa_family) {
		case AF_INET:
			((struct sockaddr_in *) (addr + 1))->sin_port = 0;
			break;
		case AF_INET6:
			((struct sockaddr_in6 *) (addr + 1))->sin6_port = 0;
			break;
		}
		if (pf_key_v2_msg_add(update, (struct sadb_ext *) addr,
		    PF_KEY_V2_NODE_MALLOCED) == -1)
			goto cleanup;
		addr = 0;
#if 0
		msg->em_odst = msg->em_dst;
		msg->em_osrc = msg->em_src;
#endif
	}
#endif

	if (proto->proto != IPSEC_PROTO_IPCOMP) {
		/* Setup the KEY extensions.  */
		if (hashlen) {
			len = sizeof *key + PF_KEY_V2_ROUND(hashlen);
			key = malloc(len);
			if (!key)
				goto cleanup;
			key->sadb_key_exttype = SADB_EXT_KEY_AUTH;
			key->sadb_key_len = len / PF_KEY_V2_CHUNK;
			key->sadb_key_bits = hashlen * 8;
			key->sadb_key_reserved = 0;
			memcpy(key + 1,
			    iproto->keymat[incoming] +
			    (proto->proto ==
				IPSEC_PROTO_IPSEC_ESP ? keylen : 0),
			    hashlen);
			if (pf_key_v2_msg_add(update, (struct sadb_ext *) key,
			    PF_KEY_V2_NODE_MALLOCED) == -1)
				goto cleanup;
			key = 0;
		}
		if (keylen) {
			len = sizeof *key + PF_KEY_V2_ROUND(keylen);
			key = malloc(len);
			if (!key)
				goto cleanup;
			key->sadb_key_exttype = SADB_EXT_KEY_ENCRYPT;
			key->sadb_key_len = len / PF_KEY_V2_CHUNK;
			key->sadb_key_bits = keylen * 8;
			key->sadb_key_reserved = 0;
			memcpy(key + 1, iproto->keymat[incoming], keylen);
			if (pf_key_v2_msg_add(update, (struct sadb_ext *) key,
			    PF_KEY_V2_NODE_MALLOCED) == -1)
				goto cleanup;
			key = 0;
		}
	}
	/* Setup identity extensions. */
	if (isakmp_sa->id_i) {
		pp = pf_key_v2_convert_id(isakmp_sa->id_i, isakmp_sa->id_i_len,
		    &len, &idtype);
		if (!pp)
			goto nosid;

		sid = calloc(PF_KEY_V2_ROUND(len + 1) + sizeof *sid,
		    sizeof(u_int8_t));
		if (!sid) {
			free(pp);
			goto cleanup;
		}
		sid->sadb_ident_type = idtype;
		sid->sadb_ident_len = ((sizeof *sid) / PF_KEY_V2_CHUNK) +
		    PF_KEY_V2_ROUND(len + 1) / PF_KEY_V2_CHUNK;
		if ((isakmp_sa->initiator && !incoming) ||
		    (!isakmp_sa->initiator && incoming))
			sid->sadb_ident_exttype = SADB_EXT_IDENTITY_SRC;
		else
			sid->sadb_ident_exttype = SADB_EXT_IDENTITY_DST;

		memcpy(sid + 1, pp, len);
		free(pp);

		if (pf_key_v2_msg_add(update, (struct sadb_ext *) sid,
		    PF_KEY_V2_NODE_MALLOCED) == -1)
			goto cleanup;
		sid = 0;

nosid:
		free(sid);
		sid = 0;
	}
	if (isakmp_sa->id_r) {
		pp = pf_key_v2_convert_id(isakmp_sa->id_r, isakmp_sa->id_r_len,
		    &len, &idtype);
		if (!pp)
			goto nodid;

		sid = calloc(PF_KEY_V2_ROUND(len + 1) + sizeof *sid,
		    sizeof(u_int8_t));
		if (!sid) {
			free(pp);
			goto cleanup;
		}
		sid->sadb_ident_type = idtype;
		sid->sadb_ident_len = ((sizeof *sid) / PF_KEY_V2_CHUNK) +
		    PF_KEY_V2_ROUND(len + 1) / PF_KEY_V2_CHUNK;
		if ((isakmp_sa->initiator && !incoming) ||
		    (!isakmp_sa->initiator && incoming))
			sid->sadb_ident_exttype = SADB_EXT_IDENTITY_DST;
		else
			sid->sadb_ident_exttype = SADB_EXT_IDENTITY_SRC;

		memcpy(sid + 1, pp, len);
		free(pp);

		if (pf_key_v2_msg_add(update, (struct sadb_ext *) sid,
		    PF_KEY_V2_NODE_MALLOCED) == -1)
			goto cleanup;
		sid = 0;

nodid:
		free(sid);
		sid = 0;
	}

	/*
	 * Send received credentials to the kernel. We don't bother with
	 * our credentials, since the process either knows them (if it
	 * specified them with setsockopt()), or has no business looking at
	 * them (e.g., system wide certs).
	 */
	if (isakmp_sa->recv_cert) {
		switch (isakmp_sa->recv_certtype) {
		case ISAKMP_CERTENC_NONE:
			/* Nothing to be done here. */
			break;

		case ISAKMP_CERTENC_KEYNOTE:
			len = strlen(isakmp_sa->recv_cert);
			cred = calloc(PF_KEY_V2_ROUND(len) + sizeof *cred,
			    sizeof(u_int8_t));
			if (!cred)
				goto cleanup;

			cred->sadb_x_cred_len =
			    ((sizeof *cred) / PF_KEY_V2_CHUNK) +
			    PF_KEY_V2_ROUND(len) / PF_KEY_V2_CHUNK;
			cred->sadb_x_cred_exttype =
			    SADB_X_EXT_REMOTE_CREDENTIALS;
			cred->sadb_x_cred_type = SADB_X_CREDTYPE_KEYNOTE;
			memcpy(cred + 1, isakmp_sa->recv_cert, len);

			if (pf_key_v2_msg_add(update, (struct sadb_ext *) cred,
			    PF_KEY_V2_NODE_MALLOCED) == -1)
				goto cleanup;
			break;

		case ISAKMP_CERTENC_X509_SIG:
			{
				u_int8_t       *data;
				u_int32_t       datalen;
				struct cert_handler *handler;

				/* We do it this way to avoid weird includes.*/
				handler = cert_get(ISAKMP_CERTENC_X509_SIG);
				if (!handler)
					break;
				handler->cert_serialize(isakmp_sa->recv_cert,
				    &data, &datalen);
				if (!data)
					break;

				len = datalen;
				cred =
				    calloc(PF_KEY_V2_ROUND(len) + sizeof *cred,
					sizeof(u_int8_t));
				if (!cred) {
					free(data);
					goto cleanup;
				}
				cred->sadb_x_cred_len =
				    ((sizeof *cred) / PF_KEY_V2_CHUNK) +
				    PF_KEY_V2_ROUND(len) / PF_KEY_V2_CHUNK;
				cred->sadb_x_cred_exttype =
				    SADB_X_EXT_REMOTE_CREDENTIALS;
				cred->sadb_x_cred_type = SADB_X_CREDTYPE_X509;
				memcpy(cred + 1, data, len);
				free(data);

				if (pf_key_v2_msg_add(update,
				    (struct sadb_ext *) cred,
				    PF_KEY_V2_NODE_MALLOCED) == -1)
					goto cleanup;
			}
			break;
		}
	}

	/*
	 * Tell the kernel what the peer used to authenticate, unless it was a
	 * passphrase.
	 */
	if (isakmp_sa->recv_key) {
		u_int8_t       *data;

		/*
		 * If it's a private key, we shouldn't pass it to the kernel
		 * for processes to see; successful authentication of Phase 1
		 * implies that the process already knew the passphrase. On
		 * the other hand, we don't want to reveal to processes any
		 * system-wide passphrases used for authentication with remote
		 * systems. Same reason we don't send up the key (private or
		 * passphrase) we used to authenticate with the peer.
		 */
		if (isakmp_sa->recv_keytype == ISAKMP_KEY_PASSPHRASE)
			goto doneauth;

		key_serialize(isakmp_sa->recv_keytype, ISAKMP_KEYTYPE_PUBLIC,
		    isakmp_sa->recv_key, &data, &len);
		if (!data)
			goto cleanup;

		cred = calloc(PF_KEY_V2_ROUND(len) + sizeof *cred,
		    sizeof(u_int8_t));
		if (!cred) {
			free(data);
			goto cleanup;
		}
		cred->sadb_x_cred_len = ((sizeof *cred) / PF_KEY_V2_CHUNK) +
		    PF_KEY_V2_ROUND(len) / PF_KEY_V2_CHUNK;
		cred->sadb_x_cred_exttype = SADB_X_EXT_REMOTE_AUTH;
		memcpy(cred + 1, data, len);
		free(data);

		switch (isakmp_sa->recv_keytype) {
		case ISAKMP_KEY_RSA:
			cred->sadb_x_cred_type = SADB_X_AUTHTYPE_RSA;
			break;

		default:
			log_print("pf_key_v2_set_spi: "
			    "unknown received key type %d",
			    isakmp_sa->recv_keytype);
			free(cred);
			goto cleanup;
		}

		if (pf_key_v2_msg_add(update, (struct sadb_ext *) cred,
		    PF_KEY_V2_NODE_MALLOCED) == -1)
			goto cleanup;
	}
doneauth:

	/* Setup the flow type extension.  */
	bzero(&flowtype, sizeof flowtype);
	flowtype.sadb_protocol_exttype = SADB_X_EXT_FLOW_TYPE;
	flowtype.sadb_protocol_len = sizeof flowtype / PF_KEY_V2_CHUNK;
	flowtype.sadb_protocol_direction = incoming ?
	    IPSP_DIRECTION_IN : IPSP_DIRECTION_OUT;

	if (pf_key_v2_msg_add(update, (struct sadb_ext *)&flowtype, 0) == -1)
		goto cleanup;

	bzero(&tprotocol, sizeof tprotocol);
	tprotocol.sadb_protocol_exttype = SADB_X_EXT_PROTOCOL;
	tprotocol.sadb_protocol_len = sizeof tprotocol / PF_KEY_V2_CHUNK;
	tprotocol.sadb_protocol_proto = isa->tproto;

	if (pf_key_v2_msg_add(update, (struct sadb_ext *)&tprotocol,
	    0) == -1)
		goto cleanup;

	len = sizeof *addr + PF_KEY_V2_ROUND(SA_LEN(isa->src_net));
	addr = calloc(1, len);
	if (!addr)
		goto cleanup;
	addr->sadb_address_exttype = incoming ?
	    SADB_X_EXT_DST_FLOW : SADB_X_EXT_SRC_FLOW;
	addr->sadb_address_len = len / PF_KEY_V2_CHUNK;
	addr->sadb_address_reserved = 0;
	pf_key_v2_setup_sockaddr(addr + 1, isa->src_net, 0, isa->sport, 0);
	if (pf_key_v2_msg_add(update, (struct sadb_ext *) addr,
	    PF_KEY_V2_NODE_MALLOCED) == -1)
		goto cleanup;
	addr = 0;

	addr = calloc(1, len);
	if (!addr)
		goto cleanup;
	addr->sadb_address_exttype =
		incoming ? SADB_X_EXT_DST_MASK : SADB_X_EXT_SRC_MASK;
	addr->sadb_address_len = len / PF_KEY_V2_CHUNK;
	addr->sadb_address_reserved = 0;
	pf_key_v2_setup_sockaddr(addr + 1, isa->src_mask, 0,
	    isa->sport ? 0xffff : 0, 0);
	if (pf_key_v2_msg_add(update, (struct sadb_ext *) addr,
	    PF_KEY_V2_NODE_MALLOCED) == -1)
		goto cleanup;
	addr = 0;

	addr = calloc(1, len);
	if (!addr)
		goto cleanup;
	addr->sadb_address_exttype = incoming ?
	    SADB_X_EXT_SRC_FLOW : SADB_X_EXT_DST_FLOW;
	addr->sadb_address_len = len / PF_KEY_V2_CHUNK;
	addr->sadb_address_reserved = 0;
	pf_key_v2_setup_sockaddr(addr + 1, isa->dst_net, 0, isa->dport, 0);
	if (pf_key_v2_msg_add(update, (struct sadb_ext *) addr,
	    PF_KEY_V2_NODE_MALLOCED) == -1)
		goto cleanup;
	addr = 0;

	addr = calloc(1, len);
	if (!addr)
		goto cleanup;
	addr->sadb_address_exttype =
		incoming ? SADB_X_EXT_SRC_MASK : SADB_X_EXT_DST_MASK;
	addr->sadb_address_len = len / PF_KEY_V2_CHUNK;
	addr->sadb_address_reserved = 0;
	pf_key_v2_setup_sockaddr(addr + 1, isa->dst_mask, 0,
	    isa->dport ? 0xffff : 0, 0);
	if (pf_key_v2_msg_add(update, (struct sadb_ext *) addr,
	    PF_KEY_V2_NODE_MALLOCED) == -1)
		goto cleanup;
	addr = 0;

	/* Add a pf tag to matching packets of this SA. */
	if (sa->tag != NULL) {
		len = sizeof(*stag) + PF_KEY_V2_ROUND(strlen(sa->tag) + 1);
		if ((stag = (struct sadb_x_tag *)calloc(1, len)) == NULL)
			goto cleanup;
		stag->sadb_x_tag_exttype = SADB_X_EXT_TAG;
		stag->sadb_x_tag_len = len / PF_KEY_V2_CHUNK;
		stag->sadb_x_tag_taglen = strlen(sa->tag) + 1;
		s = (char *)(stag + 1);
		strlcpy(s, sa->tag, stag->sadb_x_tag_taglen);
		if (pf_key_v2_msg_add(update, (struct sadb_ext *)stag,
		    PF_KEY_V2_NODE_MALLOCED) == -1)
			goto cleanup;
	}

	/* XXX Here can sensitivity extensions be setup.  */

	if (sockaddr2text(dst, &addr_str, 0))
		addr_str = 0;

	LOG_DBG((LOG_SYSDEP, 10, "pf_key_v2_set_spi: "
	    "satype %d dst %s SPI 0x%x%s%s", msg.sadb_msg_satype,
	    addr_str ? addr_str : "unknown",
	    ntohl(ssa.sadb_sa_spi), sa->tag ? " tag " : "",
	    sa->tag ? sa->tag : ""));

	free(addr_str);

	/*
	 * Although PF_KEY knows about expirations, it is unreliable per the
	 * specs thus we need to do them inside isakmpd as well.
	 */
	if (sa->seconds)
		if (sa_setup_expirations(sa))
			goto cleanup;

	ret = pf_key_v2_call(update);
	pf_key_v2_msg_free(update);
	update = 0;
	if (!ret)
		goto cleanup;
	err = ((struct sadb_msg *)TAILQ_FIRST(ret)->seg)->sadb_msg_errno;
	pf_key_v2_msg_free(ret);
	ret = 0;

	/*
	 * If we are doing an addition into an SADB shared with our peer,
	 * errors here are to be expected as the peer will already have
	 * created the SA, and can thus be ignored.
	 */
	if (err && !(msg.sadb_msg_type == SADB_ADD &&
	    conf_get_str("General", "Shared-SADB"))) {
		log_print("pf_key_v2_set_spi: %s: %s",
		    msg.sadb_msg_type == SADB_ADD ? "ADD" : "UPDATE",
		    strerror(err));
		goto cleanup;
	}
	LOG_DBG((LOG_SYSDEP, 50, "pf_key_v2_set_spi: done"));

	return 0;

cleanup:
	free(sid);
	free(addr);
	free(life);
	free(key);
	if (update)
		pf_key_v2_msg_free(update);
	if (ret)
		pf_key_v2_msg_free(ret);
	return -1;
}

static __inline__ int
pf_key_v2_mask_to_bits(u_int32_t mask)
{
	u_int32_t       hmask = ntohl(mask);

	return (33 - ffs(~hmask + 1)) % 33;
}

static int
pf_key_v2_mask6_to_bits(u_int8_t *mask)
{
	int		n;

	bit_ffc(mask, 128, &n);
	return n == -1 ? 128 : n;
}

/*
 * Enable/disable a flow.
 * XXX Assumes OpenBSD {ADD,DEL}FLOW extensions.
 */
static int
pf_key_v2_flow(struct sockaddr *laddr, struct sockaddr *lmask,
    struct sockaddr *raddr, struct sockaddr *rmask,
    u_int8_t tproto, u_int16_t sport, u_int16_t dport,
    u_int8_t *spi, u_int8_t proto, struct sockaddr *dst,
    struct sockaddr *src, int delete, int ingress,
    u_int8_t srcid_type, u_int8_t *srcid, int srcid_len,
    u_int8_t dstid_type, u_int8_t *dstid, int dstid_len,
    struct ipsec_proto *iproto)
{
	char           *laddr_str, *lmask_str, *raddr_str, *rmask_str;

	struct sadb_msg msg;
	struct sadb_protocol flowtype;
	struct sadb_ident *sid = 0;
	struct sadb_address *addr = 0;
	struct sadb_protocol tprotocol;
	struct pf_key_v2_msg *flow = 0, *ret = 0;
	size_t		len;
	int		err;

	msg.sadb_msg_type = delete ? SADB_X_DELFLOW : SADB_X_ADDFLOW;
	switch (proto) {
	case IPSEC_PROTO_IPSEC_ESP:
		msg.sadb_msg_satype = SADB_SATYPE_ESP;
		break;
	case IPSEC_PROTO_IPSEC_AH:
		msg.sadb_msg_satype = SADB_SATYPE_AH;
		break;
	case IPSEC_PROTO_IPCOMP:
		msg.sadb_msg_satype = SADB_X_SATYPE_IPCOMP;
		break;
	default:
		log_print("pf_key_v2_flow: invalid proto %d", proto);
		goto cleanup;
	}
	msg.sadb_msg_seq = 0;
	flow = pf_key_v2_msg_new(&msg, 0);
	if (!flow)
		goto cleanup;

	if (!delete) {
		/* Setup the source ID, if provided. */
		if (srcid) {
			sid = calloc(
			    PF_KEY_V2_ROUND(srcid_len + 1) + sizeof *sid,
			    sizeof(u_int8_t));
			if (!sid)
				goto cleanup;

			sid->sadb_ident_len = ((sizeof *sid) / PF_KEY_V2_CHUNK)
			    + PF_KEY_V2_ROUND(srcid_len + 1) / PF_KEY_V2_CHUNK;
			sid->sadb_ident_exttype = SADB_EXT_IDENTITY_SRC;
			sid->sadb_ident_type = srcid_type;

			memcpy(sid + 1, srcid, srcid_len);

			if (pf_key_v2_msg_add(flow, (struct sadb_ext *) sid,
			    PF_KEY_V2_NODE_MALLOCED) == -1)
				goto cleanup;

			sid = 0;
		}
		/* Setup the destination ID, if provided. */
		if (dstid) {
			sid = calloc(
			    PF_KEY_V2_ROUND(dstid_len + 1) + sizeof *sid,
			    sizeof(u_int8_t));
			if (!sid)
				goto cleanup;

			sid->sadb_ident_len = ((sizeof *sid) / PF_KEY_V2_CHUNK)
			    + PF_KEY_V2_ROUND(dstid_len + 1) / PF_KEY_V2_CHUNK;
			sid->sadb_ident_exttype = SADB_EXT_IDENTITY_DST;
			sid->sadb_ident_type = dstid_type;

			memcpy(sid + 1, dstid, dstid_len);

			if (pf_key_v2_msg_add(flow, (struct sadb_ext *) sid,
			    PF_KEY_V2_NODE_MALLOCED) == -1)
				goto cleanup;

			sid = 0;
		}
	}
	/* Setup the flow type extension.  */
	bzero(&flowtype, sizeof flowtype);
	flowtype.sadb_protocol_exttype = SADB_X_EXT_FLOW_TYPE;
	flowtype.sadb_protocol_len = sizeof flowtype / PF_KEY_V2_CHUNK;
	flowtype.sadb_protocol_direction =
	    ingress ? IPSP_DIRECTION_IN : IPSP_DIRECTION_OUT;
	flowtype.sadb_protocol_proto =
	    ingress ? SADB_X_FLOW_TYPE_USE : SADB_X_FLOW_TYPE_REQUIRE;

	if (pf_key_v2_msg_add(flow, (struct sadb_ext *)&flowtype, 0) == -1)
		goto cleanup;

	/*
	 * Setup the ADDRESS extensions.
	 */
	len = sizeof *addr + PF_KEY_V2_ROUND(SA_LEN(src));
	if (!delete)
	{
		addr = calloc(1, len);
		if (!addr)
			goto cleanup;
		addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
		addr->sadb_address_len = len / PF_KEY_V2_CHUNK;
		addr->sadb_address_reserved = 0;
		pf_key_v2_setup_sockaddr(addr + 1, src, dst, 0, ingress);
		if (pf_key_v2_msg_add(flow, (struct sadb_ext *) addr,
		    PF_KEY_V2_NODE_MALLOCED) == -1)
			goto cleanup;
		addr = 0;
	}
	len = sizeof *addr + PF_KEY_V2_ROUND(SA_LEN(laddr));
	addr = calloc(1, len);
	if (!addr)
		goto cleanup;
	addr->sadb_address_exttype = SADB_X_EXT_SRC_FLOW;
	addr->sadb_address_len = len / PF_KEY_V2_CHUNK;
	addr->sadb_address_reserved = 0;
	pf_key_v2_setup_sockaddr(addr + 1, laddr, 0, sport, 0);
	if (pf_key_v2_msg_add(flow, (struct sadb_ext *) addr,
	    PF_KEY_V2_NODE_MALLOCED) == -1)
		goto cleanup;
	addr = 0;

	addr = calloc(1, len);
	if (!addr)
		goto cleanup;
	addr->sadb_address_exttype = SADB_X_EXT_SRC_MASK;
	addr->sadb_address_len = len / PF_KEY_V2_CHUNK;
	addr->sadb_address_reserved = 0;
	pf_key_v2_setup_sockaddr(addr + 1, lmask, 0, sport ? 0xffff : 0, 0);
	if (pf_key_v2_msg_add(flow, (struct sadb_ext *) addr,
	    PF_KEY_V2_NODE_MALLOCED) == -1)
		goto cleanup;
	addr = 0;

	addr = calloc(1, len);
	if (!addr)
		goto cleanup;
	addr->sadb_address_exttype = SADB_X_EXT_DST_FLOW;
	addr->sadb_address_len = len / PF_KEY_V2_CHUNK;
	addr->sadb_address_reserved = 0;
	pf_key_v2_setup_sockaddr(addr + 1, raddr, 0, dport, 0);
	if (pf_key_v2_msg_add(flow, (struct sadb_ext *) addr,
	    PF_KEY_V2_NODE_MALLOCED) == -1)
		goto cleanup;
	addr = 0;

	addr = calloc(1, len);
	if (!addr)
		goto cleanup;
	addr->sadb_address_exttype = SADB_X_EXT_DST_MASK;
	addr->sadb_address_len = len / PF_KEY_V2_CHUNK;
	addr->sadb_address_reserved = 0;
	pf_key_v2_setup_sockaddr(addr + 1, rmask, 0, dport ? 0xffff : 0, 0);
	if (pf_key_v2_msg_add(flow, (struct sadb_ext *) addr,
	    PF_KEY_V2_NODE_MALLOCED) == -1)
		goto cleanup;
	addr = 0;

	/* Setup the protocol extension.  */
	bzero(&tprotocol, sizeof tprotocol);
	tprotocol.sadb_protocol_exttype = SADB_X_EXT_PROTOCOL;
	tprotocol.sadb_protocol_len = sizeof tprotocol / PF_KEY_V2_CHUNK;
	tprotocol.sadb_protocol_proto = tproto;

	if (pf_key_v2_msg_add(flow, (struct sadb_ext *)&tprotocol, 0) == -1)
		goto cleanup;

	if (sockaddr2text(laddr, &laddr_str, 0))
		laddr_str = 0;
	if (sockaddr2text(lmask, &lmask_str, 0))
		lmask_str = 0;
	if (sockaddr2text(raddr, &raddr_str, 0))
		raddr_str = 0;
	if (sockaddr2text(rmask, &rmask_str, 0))
		rmask_str = 0;

	LOG_DBG((LOG_SYSDEP, 50,
	   "pf_key_v2_flow: src %s %s dst %s %s proto %u sport %u dport %u",
	 laddr_str ? laddr_str : "<??\?>", lmask_str ? lmask_str : "<??\?>",
	 raddr_str ? raddr_str : "<??\?>", rmask_str ? rmask_str : "<??\?>",
		 tproto, ntohs(sport), ntohs(dport)));

	free(laddr_str);
	free(lmask_str);
	free(raddr_str);
	free(rmask_str);

	ret = pf_key_v2_call(flow);
	pf_key_v2_msg_free(flow);
	flow = 0;
	if (!ret)
		goto cleanup;
	err = ((struct sadb_msg *)TAILQ_FIRST(ret)->seg)->sadb_msg_errno;
	if (err) {
		if (err == ESRCH)	/* These are common and usually
					 * harmless.  */
			LOG_DBG((LOG_SYSDEP, 10, "pf_key_v2_flow: %sFLOW: %s",
				 delete ? "DEL" : "ADD", strerror(err)));
		else
			log_print("pf_key_v2_flow: %sFLOW: %s",
			    delete ? "DEL" : "ADD", strerror(err));
		goto cleanup;
	}
	pf_key_v2_msg_free(ret);

	LOG_DBG((LOG_MISC, 50, "pf_key_v2_flow: %sFLOW: done",
		 delete ? "DEL" : "ADD"));

	return 0;

cleanup:
	free(sid);
	free(addr);
	if (flow)
		pf_key_v2_msg_free(flow);
	if (ret)
		pf_key_v2_msg_free(ret);
	return -1;
}

static u_int8_t *
pf_key_v2_convert_id(u_int8_t *id, int idlen, size_t *reslen, int *idtype)
{
	u_int8_t       *addr, *res = 0;
	char		addrbuf[ADDRESS_MAX + 5];

	switch (id[0]) {
	case IPSEC_ID_FQDN:
		res = calloc(idlen - ISAKMP_ID_DATA_OFF + ISAKMP_GEN_SZ,
		    sizeof(u_int8_t));
		if (!res)
			return 0;

		*reslen = idlen - ISAKMP_ID_DATA_OFF + ISAKMP_GEN_SZ;
		memcpy(res, id + ISAKMP_ID_DATA_OFF - ISAKMP_GEN_SZ, *reslen);
		*idtype = SADB_IDENTTYPE_FQDN;
		LOG_DBG((LOG_SYSDEP, 40, "pf_key_v2_convert_id: FQDN %.*s",
		    (int) *reslen, res));
		return res;

	case IPSEC_ID_USER_FQDN:
		res = calloc(idlen - ISAKMP_ID_DATA_OFF + ISAKMP_GEN_SZ,
		    sizeof(u_int8_t));
		if (!res)
			return 0;

		*reslen = idlen - ISAKMP_ID_DATA_OFF + ISAKMP_GEN_SZ;
		memcpy(res, id + ISAKMP_ID_DATA_OFF - ISAKMP_GEN_SZ, *reslen);
		*idtype = SADB_IDENTTYPE_USERFQDN;
		LOG_DBG((LOG_SYSDEP, 40, "pf_key_v2_convert_id: UFQDN %.*s",
		    (int) *reslen, res));
		return res;

	case IPSEC_ID_IPV4_ADDR:	/* XXX CONNECTION ? */
		if (inet_ntop(AF_INET, id + ISAKMP_ID_DATA_OFF - ISAKMP_GEN_SZ,
		    addrbuf, ADDRESS_MAX) == NULL)
			return 0;
		*reslen = strlen(addrbuf) + 3;
		strlcat(addrbuf, "/32", ADDRESS_MAX + 5);
		res = (u_int8_t *) strdup(addrbuf);
		if (!res)
			return 0;
		*idtype = SADB_IDENTTYPE_PREFIX;
		LOG_DBG((LOG_SYSDEP, 40, "pf_key_v2_convert_id: "
		    "IPv4 address %s", res));
		return res;

	case IPSEC_ID_IPV6_ADDR:	/* XXX CONNECTION ? */
		if (inet_ntop(AF_INET6,
		    id + ISAKMP_ID_DATA_OFF - ISAKMP_GEN_SZ,
		    addrbuf, ADDRESS_MAX) == NULL)
			return 0;
		*reslen = strlen(addrbuf) + 4;
		strlcat(addrbuf, "/128", ADDRESS_MAX + 5);
		res = (u_int8_t *) strdup(addrbuf);
		if (!res)
			return 0;
		LOG_DBG((LOG_SYSDEP, 40, "pf_key_v2_convert_id: "
		    "IPv6 address %s", res));
		*idtype = SADB_IDENTTYPE_PREFIX;
		return res;

	case IPSEC_ID_IPV4_ADDR_SUBNET:	/* XXX PREFIX */
		addr = id + ISAKMP_ID_DATA_OFF - ISAKMP_GEN_SZ;
		if (inet_ntop(AF_INET, addr, addrbuf, ADDRESS_MAX) == NULL)
			return 0;
		snprintf(addrbuf + strlen(addrbuf),
		    ADDRESS_MAX - strlen(addrbuf), "/%d",
		    pf_key_v2_mask_to_bits(*(u_int32_t *)(addr +
			sizeof(struct in_addr))));
		*reslen = strlen(addrbuf);
		res = (u_int8_t *) strdup(addrbuf);
		if (!res)
			return 0;
		*idtype = SADB_IDENTTYPE_PREFIX;
		LOG_DBG((LOG_SYSDEP, 40, "pf_key_v2_convert_id: "
		    "IPv4 subnet %s", res));
		return res;

	case IPSEC_ID_IPV6_ADDR_SUBNET:	/* XXX PREFIX */
		addr = id + ISAKMP_ID_DATA_OFF - ISAKMP_GEN_SZ;
		if (inet_ntop(AF_INET6, addr, addrbuf, ADDRESS_MAX) == NULL)
			return 0;
		snprintf(addrbuf + strlen(addrbuf),
		    ADDRESS_MAX - strlen(addrbuf), "/%d",
		    pf_key_v2_mask6_to_bits(addr +
			sizeof(struct in6_addr)));
		*reslen = strlen(addrbuf);
		res = (u_int8_t *) strdup(addrbuf);
		if (!res)
			return 0;
		LOG_DBG((LOG_SYSDEP, 40, "pf_key_v2_convert_id: "
		    "IPv6 subnet %s", res));
		*idtype = SADB_IDENTTYPE_PREFIX;
		return res;

	case IPSEC_ID_IPV4_RANGE:
	case IPSEC_ID_IPV6_RANGE:
	case IPSEC_ID_DER_ASN1_DN:
	case IPSEC_ID_DER_ASN1_GN:
	case IPSEC_ID_KEY_ID:
		/* XXX Not implemented yet.  */
		return 0;
	}

	return 0;
}

/* Enable a flow given an SA.  */
int
pf_key_v2_enable_sa(struct sa *sa, struct sa *isakmp_sa)
{
	struct ipsec_sa *isa = sa->data;
	struct sockaddr *dst, *src;
	int		error;
	struct proto   *proto = TAILQ_FIRST(&sa->protos);
	int		sidtype = 0, didtype = 0;
	size_t		sidlen = 0, didlen = 0;
	u_int8_t       *sid = 0, *did = 0;

	sa->transport->vtbl->get_dst(sa->transport, &dst);
	sa->transport->vtbl->get_src(sa->transport, &src);

	if (isakmp_sa->id_i) {
		if (isakmp_sa->initiator)
			sid = pf_key_v2_convert_id(isakmp_sa->id_i,
			    isakmp_sa->id_i_len, &sidlen, &sidtype);
		else
			did = pf_key_v2_convert_id(isakmp_sa->id_i,
			    isakmp_sa->id_i_len, &didlen, &didtype);
	}
	if (isakmp_sa->id_r) {
		if (isakmp_sa->initiator)
			did = pf_key_v2_convert_id(isakmp_sa->id_r,
			    isakmp_sa->id_r_len, &didlen, &didtype);
		else
			sid = pf_key_v2_convert_id(isakmp_sa->id_r,
			    isakmp_sa->id_r_len, &sidlen, &sidtype);
	}

	error = pf_key_v2_flow(isa->src_net, isa->src_mask, isa->dst_net,
	    isa->dst_mask, isa->tproto, isa->sport, isa->dport, proto->spi[0],
	    proto->proto, dst, src, 0, 0, sidtype, sid, sidlen, didtype, did,
	    didlen, proto->data);
	if (error)
		goto cleanup;

	error = pf_key_v2_flow(isa->dst_net, isa->dst_mask, isa->src_net,
	    isa->src_mask, isa->tproto, isa->dport, isa->sport, proto->spi[1],
	    proto->proto, src, dst, 0, 1, sidtype, sid, sidlen, didtype, did,
	    didlen, proto->data);

cleanup:
	free(sid);
	free(did);

	return error;
}

/* Increase reference count of refcounted sections. */
static int
pf_key_v2_conf_refinc(int af, char *section)
{
	char		conn[22];
	int		num;

	if (!section)
		return 0;

	num = conf_get_num(section, "Refcount", 0);
	if (num == 0)
		return 0;

	snprintf(conn, sizeof conn, "%d", num + 1);
	conf_set(af, section, "Refcount", conn, 1, 0);
	return 0;
}

/*
 * Return 0 if the section didn't exist or was removed, non-zero otherwise.
 * Don't touch non-refcounted (statically defined) sections.
 */
static int
pf_key_v2_conf_refhandle(int af, char *section)
{
	char		conn[22];
	int		num;

	if (!section)
		return 0;

	num = conf_get_num(section, "Refcount", 0);
	if (num == 1) {
		conf_remove_section(af, section);
		num--;
	} else if (num != 0) {
		snprintf(conn, sizeof conn, "%d", num - 1);
		conf_set(af, section, "Refcount", conn, 1, 0);
	}
	return num;
}

/* Remove all dynamically-established configuration entries.  */
static int
pf_key_v2_remove_conf(char *section)
{
	char           *ikepeer, *localid, *remoteid, *configname;
	struct conf_list_node *attr;
	struct conf_list *attrs;
	int		af;

	if (!section)
		return 0;

	if (!conf_get_str(section, "Phase"))
		return 0;

	/* Only remove dynamically-established entries. */
	attrs = conf_get_list(section, "Flags");
	if (attrs) {
		for (attr = TAILQ_FIRST(&attrs->fields); attr;
		    attr = TAILQ_NEXT(attr, link))
			if (!strcasecmp(attr->field, "__ondemand"))
				goto passed;

		conf_free_list(attrs);
	}
	return 0;

passed:
	conf_free_list(attrs);

	af = conf_begin();

	configname = conf_get_str(section, "Configuration");
	conf_remove_section(af, configname);

	/* These are the Phase 2 Local/Remote IDs. */
	localid = conf_get_str(section, "Local-ID");
	pf_key_v2_conf_refhandle(af, localid);

	remoteid = conf_get_str(section, "Remote-ID");
	pf_key_v2_conf_refhandle(af, remoteid);

	ikepeer = conf_get_str(section, "ISAKMP-peer");

	pf_key_v2_conf_refhandle(af, section);

	if (ikepeer) {
		remoteid = conf_get_str(ikepeer, "Remote-ID");
		localid = conf_get_str(ikepeer, "ID");
		configname = conf_get_str(ikepeer, "Configuration");

		pf_key_v2_conf_refhandle(af, ikepeer);
		pf_key_v2_conf_refhandle(af, configname);

		/* Phase 1 IDs */
		pf_key_v2_conf_refhandle(af, localid);
		pf_key_v2_conf_refhandle(af, remoteid);
	}
	conf_end(af, 1);
	return 0;
}

/* Disable a flow given a SA.  */
int
pf_key_v2_disable_sa(struct sa *sa, int incoming)
{
	struct ipsec_sa *isa = sa->data;
	struct sockaddr *dst, *src;
	struct proto   *proto = TAILQ_FIRST(&sa->protos);

	sa->transport->vtbl->get_dst(sa->transport, &dst);
	sa->transport->vtbl->get_src(sa->transport, &src);

	if (!incoming)
		return pf_key_v2_flow(isa->src_net, isa->src_mask,
		    isa->dst_net, isa->dst_mask, isa->tproto, isa->sport,
		    isa->dport, proto->spi[0], proto->proto, src, dst, 1, 0,
		    0, 0, 0, 0, 0, 0, proto->data);
	else {
		return pf_key_v2_flow(isa->dst_net, isa->dst_mask,
		    isa->src_net, isa->src_mask, isa->tproto, isa->dport,
		    isa->sport, proto->spi[1], proto->proto, src, dst, 1, 1,
		    0, 0, 0, 0, 0, 0, proto->data);
	}
}

/*
 * Delete the IPsec SA represented by the INCOMING direction in protocol PROTO
 * of the IKE security association SA.  Also delete potential flows tied to it.
 */
int
pf_key_v2_delete_spi(struct sa *sa, struct proto *proto, int incoming)
{
	struct sadb_msg msg;
	struct sadb_sa  ssa;
	struct sadb_address *addr = 0;
	struct sockaddr *saddr;
	int		len, err;
	struct pf_key_v2_msg *delete = 0, *ret = 0;

	/* If it's not an established SA, don't proceed. */
	if (!(sa->flags & SA_FLAG_READY))
		return 0;

	if (sa->name && !(sa->flags & SA_FLAG_REPLACED)) {
		LOG_DBG((LOG_SYSDEP, 50,
			 "pf_key_v2_delete_spi: removing configuration %s",
			 sa->name));
		pf_key_v2_remove_conf(sa->name);
	}
	msg.sadb_msg_type = SADB_DELETE;
	switch (proto->proto) {
	case IPSEC_PROTO_IPSEC_ESP:
		msg.sadb_msg_satype = SADB_SATYPE_ESP;
		break;
	case IPSEC_PROTO_IPSEC_AH:
		msg.sadb_msg_satype = SADB_SATYPE_AH;
		break;
	case IPSEC_PROTO_IPCOMP:
		msg.sadb_msg_satype = SADB_X_SATYPE_IPCOMP;
		break;
	default:
		log_print("pf_key_v2_delete_spi: invalid proto %d",
		    proto->proto);
		goto cleanup;
	}
	msg.sadb_msg_seq = 0;
	delete = pf_key_v2_msg_new(&msg, 0);
	if (!delete)
		goto cleanup;

	/* Setup the SA extension.  */
	ssa.sadb_sa_exttype = SADB_EXT_SA;
	ssa.sadb_sa_len = sizeof ssa / PF_KEY_V2_CHUNK;
	memcpy(&ssa.sadb_sa_spi, proto->spi[incoming], sizeof ssa.sadb_sa_spi);
	ssa.sadb_sa_replay = 0;
	ssa.sadb_sa_state = 0;
	ssa.sadb_sa_auth = 0;
	ssa.sadb_sa_encrypt = 0;
	ssa.sadb_sa_flags = 0;
	if (pf_key_v2_msg_add(delete, (struct sadb_ext *)&ssa, 0) == -1)
		goto cleanup;

	/*
	 * Setup the ADDRESS extensions.
	 */
	if (incoming)
		sa->transport->vtbl->get_dst(sa->transport, &saddr);
	else
		sa->transport->vtbl->get_src(sa->transport, &saddr);
	len = sizeof *addr + PF_KEY_V2_ROUND(SA_LEN(saddr));
	addr = calloc(1, len);
	if (!addr)
		goto cleanup;
	addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
	addr->sadb_address_len = len / PF_KEY_V2_CHUNK;
	addr->sadb_address_reserved = 0;
	memcpy(addr + 1, saddr, SA_LEN(saddr));
	switch (saddr->sa_family) {
	case AF_INET:
		((struct sockaddr_in *) (addr + 1))->sin_port = 0;
		break;
	case AF_INET6:
		((struct sockaddr_in6 *) (addr + 1))->sin6_port = 0;
		break;
	}
	if (pf_key_v2_msg_add(delete, (struct sadb_ext *) addr,
	    PF_KEY_V2_NODE_MALLOCED) == -1)
		goto cleanup;
	addr = 0;

	if (incoming)
		sa->transport->vtbl->get_src(sa->transport, &saddr);
	else
		sa->transport->vtbl->get_dst(sa->transport, &saddr);
	len = sizeof *addr + PF_KEY_V2_ROUND(SA_LEN(saddr));
	addr = calloc(1, len);
	if (!addr)
		goto cleanup;
	addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
	addr->sadb_address_len = len / PF_KEY_V2_CHUNK;
	addr->sadb_address_reserved = 0;
	memcpy(addr + 1, saddr, SA_LEN(saddr));
	switch (saddr->sa_family) {
	case AF_INET:
		((struct sockaddr_in *) (addr + 1))->sin_port = 0;
		break;
	case AF_INET6:
		((struct sockaddr_in6 *) (addr + 1))->sin6_port = 0;
		break;
	}
	if (pf_key_v2_msg_add(delete, (struct sadb_ext *) addr,
	    PF_KEY_V2_NODE_MALLOCED) == -1)
		goto cleanup;
	addr = 0;

	ret = pf_key_v2_call(delete);
	pf_key_v2_msg_free(delete);
	delete = 0;
	if (!ret)
		goto cleanup;
	err = ((struct sadb_msg *)TAILQ_FIRST(ret)->seg)->sadb_msg_errno;
	if (err) {
		LOG_DBG((LOG_SYSDEP, 10, "pf_key_v2_delete_spi: DELETE: %s",
			 strerror(err)));
		goto cleanup;
	}
	pf_key_v2_msg_free(ret);

	LOG_DBG((LOG_SYSDEP, 50, "pf_key_v2_delete_spi: done"));

	return 0;

cleanup:
	free(addr);
	if (delete)
		pf_key_v2_msg_free(delete);
	if (ret)
		pf_key_v2_msg_free(ret);
	return -1;
}

static void
pf_key_v2_stayalive(struct exchange *exchange, void *vconn, int fail)
{
	char           *conn = vconn;
	struct sa      *sa;

	/* XXX What if it is phase 1 ? */
	sa = sa_lookup_by_name(conn, 2);
	if (sa)
		sa->flags |= SA_FLAG_STAYALIVE;

	/*
	 * Remove failed configuration entry -- call twice because it is
	 * created with a Refcount of 2.
	 */
	if (fail && (!exchange || exchange->name)) {
		pf_key_v2_remove_conf(conn);
		pf_key_v2_remove_conf(conn);
	}
}

/* Check if a connection CONN exists, otherwise establish it.  */
void
pf_key_v2_connection_check(char *conn)
{
	if (!sa_lookup_by_name(conn, 2)) {
		LOG_DBG((LOG_SYSDEP, 70,
		    "pf_key_v2_connection_check: SA for %s missing", conn));
		exchange_establish(conn, pf_key_v2_stayalive, conn, 0);
	} else
		LOG_DBG((LOG_SYSDEP, 70, "pf_key_v2_connection_check: "
		    "SA for %s exists", conn));
}

/* Handle a PF_KEY lifetime expiration message PMSG.  */
static void
pf_key_v2_expire(struct pf_key_v2_msg *pmsg)
{
	struct sadb_msg *msg;
	struct sadb_sa *ssa;
	struct sadb_address *dst;
	struct sockaddr *dstaddr;
	struct sadb_lifetime *life, *lifecurrent;
	struct sa      *sa;
	struct pf_key_v2_node *lifenode, *ext;
	char           *dst_str;

	msg = (struct sadb_msg *)TAILQ_FIRST(pmsg)->seg;
	ext = pf_key_v2_find_ext(pmsg, SADB_EXT_SA);
	if (!ext) {
		log_print("pf_key_v2_expire: no SA extension found");
		return;
	}
	ssa = ext->seg;
	ext = pf_key_v2_find_ext(pmsg, SADB_EXT_ADDRESS_DST);
	if (!ext) {
		log_print("pf_key_v2_expire: "
		    "no destination address extension found");
		return;
	}
	dst = ext->seg;
	dstaddr = (struct sockaddr *) (dst + 1);
	lifenode = pf_key_v2_find_ext(pmsg, SADB_EXT_LIFETIME_HARD);
	if (!lifenode)
		lifenode = pf_key_v2_find_ext(pmsg, SADB_EXT_LIFETIME_SOFT);
	if (!lifenode) {
		log_print("pf_key_v2_expire: no lifetime extension found");
		return;
	}
	life = lifenode->seg;

	lifenode = pf_key_v2_find_ext(pmsg, SADB_EXT_LIFETIME_CURRENT);
	if (!lifenode) {
		log_print("pf_key_v2_expire: "
		    "no current lifetime extension found");
		return;
	}
	lifecurrent = lifenode->seg;

	if (sockaddr2text(dstaddr, &dst_str, 0))
		dst_str = 0;

	LOG_DBG((LOG_SYSDEP, 20, "pf_key_v2_expire: "
	    "%s dst %s SPI %x sproto %d",
	    life->sadb_lifetime_exttype == SADB_EXT_LIFETIME_SOFT ? "SOFT"
	    : "HARD", dst_str ? dst_str : "<unknown>",
	    ntohl(ssa->sadb_sa_spi), msg->sadb_msg_satype));

	free(dst_str);

	/*
	 * Find the IPsec SA.  The IPsec stack has two SAs for every IKE SA,
	 * one outgoing and one incoming, we regard expirations for any of
	 * them as an expiration of the full IKE SA.  Likewise, in
	 * protection suites consisting of more than one protocol, any
	 * expired individual IPsec stack SA will be seen as an expiration
	 * of the full suite.
	 */
	switch (msg->sadb_msg_satype) {
	case SADB_SATYPE_ESP:
		sa = ipsec_sa_lookup(dstaddr, ssa->sadb_sa_spi,
		    IPSEC_PROTO_IPSEC_ESP);
		break;

	case SADB_SATYPE_AH:
		sa = ipsec_sa_lookup(dstaddr, ssa->sadb_sa_spi,
		    IPSEC_PROTO_IPSEC_AH);
		break;

	case SADB_X_SATYPE_IPCOMP:
		sa = ipsec_sa_lookup(dstaddr, ssa->sadb_sa_spi,
		    IPSEC_PROTO_IPCOMP);
		break;

	default:
		/* XXX Log? */
		sa = 0;
		break;
	}

	/* If the SA is already gone, don't do anything.  */
	if (!sa)
		return;

	/*
	 * If we got a notification, try to renegotiate the SA -- unless of
	 * course it has already been replaced by another.
	 * Also, ignore SAs that were not dynamically established, or that
	 * did not see any use.
	 */
	if (!(sa->flags & SA_FLAG_REPLACED) &&
	    (sa->flags & SA_FLAG_ONDEMAND) &&
	    lifecurrent->sadb_lifetime_bytes)
		exchange_establish(sa->name, 0, 0, 0);

	if (life->sadb_lifetime_exttype == SADB_EXT_LIFETIME_HARD) {
		/* Remove the old SA, it isn't useful anymore.  */
		sa_free(sa);
	}
}

static int
mask4len(const struct sockaddr_in *mask)
{
	int len;
	u_int32_t m;

	len = 0;
	for (m = 0x80000000; m & ntohl(mask->sin_addr.s_addr); m >>= 1)
		len++;
	if (len == 32)
		len = -1;
	return len;
}

#ifndef s6_addr8
#define s6_addr8 __u6_addr.__u6_addr8
#endif

static int
mask6len(const struct sockaddr_in6 *mask)
{
	int i, len;
	u_int8_t m;

	len = 0;
	for (i = 0, m = 0; i < 16 && !m; i++)
		for (m = 0x80; m & mask->sin6_addr.s6_addr8[i]; m >>= 1)
			len++;
	if (len == 128)
		len = -1;
	return len;
}

static int
phase2id(char *str, size_t size, const char *side, const char *sflow,
    int masklen, u_int8_t proto, u_int16_t port)
{
	char smasklen[10], sproto[10], sport[10];

	smasklen[0] = sproto[0] = sport[0] = 0;
	if (masklen != -1)
		snprintf(smasklen, sizeof smasklen, "/%d", masklen);
	if (proto)
		snprintf(sproto, sizeof sproto, "=%u", proto);
	if (port)
		snprintf(sport, sizeof sport, ":%u", ntohs(port));

	return snprintf(str, size, "%s-%s%s%s%s", side, sflow, smasklen,
	    sproto, sport);
}

/* Handle a PF_KEY SA ACQUIRE message PMSG.  */
static void
pf_key_v2_acquire(struct pf_key_v2_msg *pmsg)
{
	struct sadb_msg *msg, askpolicy_msg;
	struct pf_key_v2_msg *askpolicy = 0, *ret = 0;
	struct sadb_x_policy policy;
	struct sadb_address *dst = 0, *src = 0;
	struct sockaddr *dstaddr, *srcaddr = 0;
	struct sadb_comb *scmb = 0;
	struct sadb_prop *sprp = 0;
	struct sadb_ident *srcident = 0, *dstident = 0;
	char		dstbuf[ADDRESS_MAX], srcbuf[ADDRESS_MAX], *peer = 0;
	char		confname[120], *conn = 0;
	char           *srcid = 0, *dstid = 0, *prefstring = 0;
	int		slen, af, afamily, masklen;
	struct sockaddr *smask, *sflow, *dmask, *dflow;
	struct sadb_protocol *sproto;
	char		ssflow[ADDRESS_MAX], sdflow[ADDRESS_MAX];
	char		sdmask[ADDRESS_MAX], ssmask[ADDRESS_MAX];
	int		dmasklen, smasklen;
	char           *sidtype = 0, *didtype = 0;
	char		lname[100], dname[100], configname[200];
	int		shostflag = 0, dhostflag = 0;
	struct pf_key_v2_node *ext;
	struct passwd  *pwd = 0;
	u_int16_t       sport = 0, dport = 0;
	u_int8_t	tproto = 0;
	char		tmbuf[sizeof sport * 3 + 1], *xform;
	int		connlen;
	struct sadb_x_cred *cred = 0, *sauth = 0;

	/* This needs to be dynamically allocated. */
	connlen = 22;
	conn = malloc(connlen);
	if (!conn) {
		log_error("pf_key_v2_acquire: malloc (%d) failed", connlen);
		return;
	}
	msg = (struct sadb_msg *)TAILQ_FIRST(pmsg)->seg;

	ext = pf_key_v2_find_ext(pmsg, SADB_EXT_ADDRESS_DST);
	if (!ext) {
		log_print("pf_key_v2_acquire: "
		    "no destination address specified");
		free(conn);
		return;
	}
	dst = ext->seg;

	ext = pf_key_v2_find_ext(pmsg, SADB_EXT_ADDRESS_SRC);
	if (ext)
		src = ext->seg;

	ext = pf_key_v2_find_ext(pmsg, SADB_EXT_PROPOSAL);
	if (ext) {
		sprp = ext->seg;
		scmb = (struct sadb_comb *) (sprp + 1);
	}
	ext = pf_key_v2_find_ext(pmsg, SADB_EXT_IDENTITY_SRC);
	if (ext)
		srcident = ext->seg;

	ext = pf_key_v2_find_ext(pmsg, SADB_EXT_IDENTITY_DST);
	if (ext)
		dstident = ext->seg;

	/* Ask the kernel for the matching policy. */
	bzero(&askpolicy_msg, sizeof askpolicy_msg);
	askpolicy_msg.sadb_msg_type = SADB_X_ASKPOLICY;
	askpolicy = pf_key_v2_msg_new(&askpolicy_msg, 0);
	if (!askpolicy)
		goto fail;

	policy.sadb_x_policy_exttype = SADB_X_EXT_POLICY;
	policy.sadb_x_policy_len = sizeof policy / PF_KEY_V2_CHUNK;
	policy.sadb_x_policy_seq = msg->sadb_msg_seq;
	if (pf_key_v2_msg_add(askpolicy, (struct sadb_ext *)&policy, 0) == -1)
		goto fail;

	ret = pf_key_v2_call(askpolicy);
	if (!ret)
		goto fail;

	/* Now we have all the information needed. */

	ext = pf_key_v2_find_ext(ret, SADB_X_EXT_SRC_FLOW);
	if (!ext) {
		log_print("pf_key_v2_acquire: no source flow extension found");
		goto fail;
	}
	sflow = (struct sockaddr *) (((struct sadb_address *) ext->seg) + 1);

	ext = pf_key_v2_find_ext(ret, SADB_X_EXT_DST_FLOW);
	if (!ext) {
		log_print("pf_key_v2_acquire: "
		    "no destination flow extension found");
		goto fail;
	}
	dflow = (struct sockaddr *) (((struct sadb_address *) ext->seg) + 1);
	ext = pf_key_v2_find_ext(ret, SADB_X_EXT_SRC_MASK);
	if (!ext) {
		log_print("pf_key_v2_acquire: no source mask extension found");
		goto fail;
	}
	smask = (struct sockaddr *) (((struct sadb_address *) ext->seg) + 1);

	ext = pf_key_v2_find_ext(ret, SADB_X_EXT_DST_MASK);
	if (!ext) {
		log_print("pf_key_v2_acquire: "
		    "no destination mask extension found");
		goto fail;
	}
	dmask = (struct sockaddr *) (((struct sadb_address *) ext->seg) + 1);

	ext = pf_key_v2_find_ext(ret, SADB_X_EXT_FLOW_TYPE);
	if (!ext) {
		log_print("pf_key_v2_acquire: no flow type extension found");
		goto fail;
	}
	sproto = ext->seg;
	tproto = sproto->sadb_protocol_proto;

	ext = pf_key_v2_find_ext(pmsg, SADB_X_EXT_LOCAL_CREDENTIALS);
	if (ext)
		cred = (struct sadb_x_cred *) ext->seg;
	else
		cred = 0;

	ext = pf_key_v2_find_ext(pmsg, SADB_X_EXT_LOCAL_AUTH);
	if (ext)
		sauth = (struct sadb_x_cred *) ext->seg;
	else
		sauth = 0;

	bzero(ssflow, sizeof ssflow);
	bzero(sdflow, sizeof sdflow);
	bzero(ssmask, sizeof ssmask);
	bzero(sdmask, sizeof sdmask);
	smasklen = dmasklen = -1;

	sidtype = didtype = "IPV4_ADDR_SUBNET";	/* default */

	switch (sflow->sa_family) {
	case AF_INET:
		if (inet_ntop(AF_INET,
		    &((struct sockaddr_in *) sflow)->sin_addr, ssflow,
		    ADDRESS_MAX) == NULL) {
			log_print("pf_key_v2_acquire: inet_ntop failed");
			goto fail;
		}
		sport = ((struct sockaddr_in *) sflow)->sin_port;
		if (inet_ntop(AF_INET,
		    &((struct sockaddr_in *) dflow)->sin_addr, sdflow,
		    ADDRESS_MAX) == NULL) {
			log_print("pf_key_v2_acquire: inet_ntop failed");
			goto fail;
		}
		dport = ((struct sockaddr_in *) dflow)->sin_port;
		if (inet_ntop(AF_INET,
		    &((struct sockaddr_in *) smask)->sin_addr, ssmask,
		    ADDRESS_MAX) == NULL) {
			log_print("pf_key_v2_acquire: inet_ntop failed");
			goto fail;
		}
		if (inet_ntop(AF_INET,
		    &((struct sockaddr_in *) dmask)->sin_addr, sdmask,
		    ADDRESS_MAX) == NULL) {
			log_print("pf_key_v2_acquire: inet_ntop failed");
			goto fail;
		}
		smasklen = mask4len((struct sockaddr_in *) smask);
		dmasklen = mask4len((struct sockaddr_in *) dmask);
		if (((struct sockaddr_in *) smask)->sin_addr.s_addr ==
		    INADDR_BROADCAST) {
			shostflag = 1;
			sidtype = "IPV4_ADDR";
		}
		if (((struct sockaddr_in *) dmask)->sin_addr.s_addr ==
		    INADDR_BROADCAST) {
			dhostflag = 1;
			didtype = "IPV4_ADDR";
		}
		break;

	case AF_INET6:
		if (inet_ntop(AF_INET6,
		    &((struct sockaddr_in6 *) sflow)->sin6_addr,
		    ssflow, ADDRESS_MAX) == NULL) {
			log_print("pf_key_v2_acquire: inet_ntop failed");
			goto fail;
		}
		sport = ((struct sockaddr_in6 *) sflow)->sin6_port;
		if (inet_ntop(AF_INET6,
		    &((struct sockaddr_in6 *) dflow)->sin6_addr,
		    sdflow, ADDRESS_MAX) == NULL) {
			log_print("pf_key_v2_acquire: inet_ntop failed");
			goto fail;
		}
		dport = ((struct sockaddr_in6 *) dflow)->sin6_port;
		if (inet_ntop(AF_INET6,
		    &((struct sockaddr_in6 *) smask)->sin6_addr,
		    ssmask, ADDRESS_MAX) == NULL) {
			log_print("pf_key_v2_acquire: inet_ntop failed");
			goto fail;
		}
		if (inet_ntop(AF_INET6,
		    &((struct sockaddr_in6 *) dmask)->sin6_addr,
		    sdmask, ADDRESS_MAX) == NULL) {
			log_print("pf_key_v2_acquire: inet_ntop failed");
			goto fail;
		}
		smasklen = mask6len((struct sockaddr_in6 *) smask);
		dmasklen = mask6len((struct sockaddr_in6 *) dmask);
		sidtype = didtype = "IPV6_ADDR_SUBNET";
		if (IN6_IS_ADDR_FULL(&((struct sockaddr_in6 *)smask)->sin6_addr)) {
			shostflag = 1;
			sidtype = "IPV6_ADDR";
		}
		if (IN6_IS_ADDR_FULL(&((struct sockaddr_in6 *)dmask)->sin6_addr)) {
			dhostflag = 1;
			didtype = "IPV6_ADDR";
		}
		break;
	}

	dstaddr = (struct sockaddr *)(dst + 1);
	bzero(dstbuf, sizeof dstbuf);
	bzero(srcbuf, sizeof srcbuf);

	if (dstaddr->sa_family == 0) {
		/*
		 * Destination was not specified in the flow -- can we derive
		 * it?
		 */
		if (dhostflag == 0) {
			log_print("pf_key_v2_acquire: "
			    "Cannot determine precise destination");
			goto fail;
		}
		dstaddr = dflow;
	}
	switch (dstaddr->sa_family) {
	case AF_INET:
		if (inet_ntop(AF_INET,
		    &((struct sockaddr_in *) dstaddr)->sin_addr,
		    dstbuf, ADDRESS_MAX) == NULL) {
			log_print("pf_key_v2_acquire: inet_ntop failed");
			goto fail;
		}
		LOG_DBG((LOG_SYSDEP, 20,
		    "pf_key_v2_acquire: dst=%s sproto %d", dstbuf,
		    msg->sadb_msg_satype));
		break;

	case AF_INET6:
		if (inet_ntop(AF_INET6,
		    &((struct sockaddr_in6 *) dstaddr)->sin6_addr,
		    dstbuf, ADDRESS_MAX) == NULL) {
			log_print("pf_key_v2_acquire: inet_ntop failed");
			goto fail;
		}
		LOG_DBG((LOG_SYSDEP, 20,
		    "pf_key_v2_acquire: dst=%s sproto %d", dstbuf,
		    msg->sadb_msg_satype));
		break;
	}

	if (src) {
		srcaddr = (struct sockaddr *) (src + 1);

		switch (srcaddr->sa_family) {
		case AF_INET:
			if (inet_ntop(AF_INET,
			    &((struct sockaddr_in *) srcaddr)->sin_addr,
			    srcbuf, ADDRESS_MAX) == NULL) {
				log_print("pf_key_v2_acquire: "
				    "inet_ntop failed");
				goto fail;
			}
			break;

		case AF_INET6:
			if (inet_ntop(AF_INET6,
			    &((struct sockaddr_in6 *)srcaddr)->sin6_addr,
			    srcbuf, ADDRESS_MAX) == NULL) {
				log_print("pf_key_v2_acquire: "
				    "inet_ntop failed");
				goto fail;
			}
			break;

		default:
			/*
			 * The kernel will pass an all '0' EXT_ADDRESS_SRC if
			 * it wasn't specified for the flow. In that case, do
			 * NOT specify the srcaddr in the Peer-name below
			 */
			srcbuf[0] = 0;
			srcaddr = NULL;
			break;
		}
	}
	/* Insert source ID. */
	if (srcident) {
		slen = (srcident->sadb_ident_len * sizeof(u_int64_t))
			- sizeof(struct sadb_ident);
		if (((unsigned char *) (srcident + 1))[slen - 1] != '\0') {
			log_print("pf_key_v2_acquire: "
			    "source identity not NUL-terminated");
			goto fail;
		}
		/* Check for valid type. */
		switch (srcident->sadb_ident_type) {
		case SADB_X_IDENTTYPE_CONNECTION:
			/* XXX */
			break;

		case SADB_IDENTTYPE_PREFIX:
			/* Determine what the address family is. */
			srcid = memchr(srcident + 1, ':', slen);
			if (srcid)
				afamily = AF_INET6;
			else
				afamily = AF_INET;

			srcid = memchr(srcident + 1, '/', slen);
			if (!srcid) {
				log_print("pf_key_v2_acquire: "
				    "badly formatted PREFIX identity");
				goto fail;
			}
			masklen = atoi(srcid + 1);

			/* XXX We only support host addresses. */
			if ((afamily == AF_INET6 && masklen != 128) ||
			    (afamily == AF_INET && masklen != 32)) {
				log_print("pf_key_v2_acquire: "
				    "non-host address specified in source "
				    "identity (mask length %d), ignoring "
				    "request", masklen);
				goto fail;
			}
			/*
			 * NUL-terminate the PREFIX string at the separator,
			 * then dup.
			 */
			*srcid = '\0';
			if (asprintf(&srcid, "id-%s",
			    (char *) (srcident + 1)) == -1) {
				log_error("pf_key_v2_acquire: asprintf() failed");
				goto fail;
			}

			/* Set the section if it doesn't already exist. */
			af = conf_begin();
			if (!conf_get_str(srcid, "ID-type")) {
				if (conf_set(af, srcid, "ID-type",
				    afamily == AF_INET ? "IPV4_ADDR" :
				    "IPV6_ADDR", 1, 0) ||
				    conf_set(af, srcid, "Refcount", "1", 1, 0) ||
				    conf_set(af, srcid, "Address",
					(char *) (srcident + 1), 1, 0)) {
					conf_end(af, 0);
					goto fail;
				}
			} else
				pf_key_v2_conf_refinc(af, srcid);
			conf_end(af, 1);
			break;

		case SADB_IDENTTYPE_FQDN:
			prefstring = "FQDN";
			/*FALLTHROUGH*/
		case SADB_IDENTTYPE_USERFQDN:
			if (!prefstring) {
				prefstring = "USER_FQDN";

				/*
				 * Check whether there is a string following
				 * the header; if no, that there is a user ID
				 * (and acquire the login name). If there is
				 * both a string and a user ID, check that
				 * they match.
				 */
				if ((slen == 0) &&
				    (srcident->sadb_ident_id == 0)) {
					log_print("pf_key_v2_acquire: "
					    "no user FQDN or ID provided");
					goto fail;
				}
				if (srcident->sadb_ident_id) {
					pwd =
					    getpwuid(srcident->sadb_ident_id);
					if (!pwd) {
						log_error("pf_key_v2_acquire: "
						    "could not acquire "
						    "username from provided "
						    "ID %llu",
						    srcident->sadb_ident_id);
						goto fail;
					}
					if (slen != 0)
						if (strcmp(pwd->pw_name,
						    (char *) (srcident + 1))
						    != 0) {
							log_print("pf_key_v2_acquire: "
							    "provided user "
							    "name and ID do "
							    "not match (%s != "
							    "%s)",
							    (char *) (srcident + 1),
							    pwd->pw_name);
							/*
							 * String has
							 * precedence, per
							 * RFC 2367.
							 */
						}
				}
			}
			if (asprintf(&srcid, "id-%s",
			    slen ? (char *) (srcident + 1) : pwd->pw_name) == -1) {
				log_error("pf_key_v2_acquire: asprintf() failed");
				goto fail;
			}
			pwd = 0;

			/* Set the section if it doesn't already exist. */
			af = conf_begin();
			if (!conf_get_str(srcid, "ID-type")) {
				if (conf_set(af, srcid, "ID-type", prefstring,
				    1, 0) ||
				    conf_set(af, srcid, "Refcount", "1", 1, 0) ||
				    conf_set(af, srcid, "Name",
					srcid + 3, 1, 0)) {
					conf_end(af, 0);
					goto fail;
				}
			} else
				pf_key_v2_conf_refinc(af, srcid);
			conf_end(af, 1);
			break;

		default:
			LOG_DBG((LOG_SYSDEP, 20,
			    "pf_key_v2_acquire: invalid source ID type %d",
			    srcident->sadb_ident_type));
			goto fail;
		}

		LOG_DBG((LOG_SYSDEP, 50,
		    "pf_key_v2_acquire: constructed source ID \"%s\"", srcid));
		prefstring = 0;
	}
	/* Insert destination ID. */
	if (dstident) {
		slen = (dstident->sadb_ident_len * sizeof(u_int64_t))
			- sizeof(struct sadb_ident);

		/* Check for valid type. */
		switch (dstident->sadb_ident_type) {
		case SADB_X_IDENTTYPE_CONNECTION:
			/* XXX */
			break;

		case SADB_IDENTTYPE_PREFIX:
			/* Determine what the address family is. */
			dstid = memchr(dstident + 1, ':', slen);
			if (dstid)
				afamily = AF_INET6;
			else
				afamily = AF_INET;

			dstid = memchr(dstident + 1, '/', slen);
			if (!dstid) {
				log_print("pf_key_v2_acquire: "
				    "badly formatted PREFIX identity");
				goto fail;
			}
			masklen = atoi(dstid + 1);

			/* XXX We only support host addresses. */
			if ((afamily == AF_INET6 && masklen != 128) ||
			    (afamily == AF_INET && masklen != 32)) {
				log_print("pf_key_v2_acquire: "
				    "non-host address specified in "
				    "destination identity (mask length %d), "
				    "ignoring request", masklen);
				goto fail;
			}
			/*
			 * NUL-terminate the PREFIX string at the separator,
			 * then dup.
			 */
			*dstid = '\0';
			if (asprintf(&dstid, "id-%s",
			    (char *) (dstident + 1)) == -1) {
				log_error("pf_key_v2_acquire: asprintf() failed");
				goto fail;
			}

			/* Set the section if it doesn't already exist. */
			af = conf_begin();
			if (!conf_get_str(dstid, "ID-type")) {
				if (conf_set(af, dstid, "ID-type",
				    afamily == AF_INET ? "IPV4_ADDR" :
				    "IPV6_ADDR", 1, 0) ||
				    conf_set(af, dstid, "Refcount", "1", 1, 0) ||
				    conf_set(af, dstid, "Address",
					(char *) (dstident + 1), 1, 0)) {
					conf_end(af, 0);
					goto fail;
				}
			} else
				pf_key_v2_conf_refinc(af, dstid);
			conf_end(af, 1);
			break;

		case SADB_IDENTTYPE_FQDN:
			prefstring = "FQDN";
			/*FALLTHROUGH*/
		case SADB_IDENTTYPE_USERFQDN:
			if (!prefstring) {
				prefstring = "USER_FQDN";

				/*
				 * Check whether there is a string following
				 * the header; if no, that there is a user ID
				 * (and acquire the login name). If there is
				 * both a string and a user ID, check that
				 * they match.
				 */
				if (slen == 0 &&
				    dstident->sadb_ident_id == 0) {
					log_print("pf_key_v2_acquire: "
					    "no user FQDN or ID provided");
					goto fail;
				}
				if (dstident->sadb_ident_id) {
					pwd = getpwuid(dstident->sadb_ident_id);
					if (!pwd) {
						log_error("pf_key_v2_acquire: "
						    "could not acquire "
						    "username from provided "
						    "ID %llu",
						    dstident->sadb_ident_id);
						goto fail;
					}
					if (slen != 0)
						if (strcmp(pwd->pw_name,
						    (char *) (dstident + 1))
						    != 0) {
							log_print("pf_key_v2_acquire: "
							    "provided user "
							    "name and ID do "
							    "not match (%s != "
							    "%s)",
							    (char *) (dstident + 1),
							    pwd->pw_name);
							/*
							 * String has
							 * precedence, per RF
							 * 2367.
							 */
						}
				}
			}
			if (asprintf(&dstid, "id-%s",
			    slen ? (char *) (dstident + 1) : pwd->pw_name) == -1) {
				log_error("pf_key_v2_acquire: asprintf() failed");
				goto fail;
			}
			pwd = 0;

			/* Set the section if it doesn't already exist. */
			af = conf_begin();
			if (!conf_get_str(dstid, "ID-type")) {
				if (conf_set(af, dstid, "ID-type", prefstring,
				    1, 0) ||
				    conf_set(af, dstid, "Refcount", "1", 1, 0) ||
				    conf_set(af, dstid, "Name",
					dstid + 3, 1, 0)) {
					conf_end(af, 0);
					goto fail;
				}
			} else
				pf_key_v2_conf_refinc(af, dstid);
			conf_end(af, 1);
			break;

		default:
			LOG_DBG((LOG_SYSDEP, 20, "pf_key_v2_acquire: "
			    "invalid destination ID type %d",
			    dstident->sadb_ident_type));
			goto fail;
		}

		LOG_DBG((LOG_SYSDEP, 50,
		    "pf_key_v2_acquire: constructed destination ID \"%s\"",
		    dstid));
	}
	/* Now we've placed the necessary IDs in the configuration space. */

	/* Get a new connection sequence number. */
	for (;; connection_seq++) {
		snprintf(conn, connlen, "Connection-%u", connection_seq);

		/* Does it exist ? */
		if (!conf_get_str(conn, "Phase"))
			break;
	}

	/*
	 * Set the IPsec connection entry. In particular, the following fields:
	 * - Phase
	 * - ISAKMP-peer
	 * - Local-ID/Remote-ID (if provided)
	 * - Acquire-ID (sequence number of kernel message, e.g., PF_KEYv2)
	 * - Configuration
	 *
	 * Also set the following section:
	 *    [peer-dstaddr(-local-srcaddr)]
	 * with these fields:
	 * - Phase
	 * - ID (if provided)
	 * - Remote-ID (if provided)
	 * - Local-address (if provided)
	 * - Address
	 * - Configuration (if an entry phase1-dstaddr-srcadd)
	 *                  exists -- otherwise use the defaults)
	 */

	/*
	 * The various cases:
	 * - peer-dstaddr
	 * - peer-dstaddr-local-srcaddr
	 */
	if (asprintf(&peer, "peer-%s%s%s", dstbuf, srcaddr ? "-local-" : "",
	    srcaddr ? srcbuf : "") == -1)
		goto fail;

	/*
	 * Set the IPsec connection section. Refcount is set to 2, because
	 * it will be linked both to the incoming and the outgoing SA.
	 */
	af = conf_begin();
	if (conf_set(af, conn, "Phase", "2", 0, 0) ||
	    conf_set(af, conn, "Flags", "__ondemand", 0, 0) ||
	    conf_set(af, conn, "Refcount", "2", 0, 0) ||
	    conf_set(af, conn, "ISAKMP-peer", peer, 0, 0)) {
		conf_end(af, 0);
		goto fail;
	}
	/* Set the sequence number. */
	snprintf(lname, sizeof lname, "%u", msg->sadb_msg_seq);
	if (conf_set(af, conn, "Acquire-ID", lname, 0, 0)) {
		conf_end(af, 0);
		goto fail;
	}
	/*
	 * Set Phase 2 IDs -- this is the Local-ID section.
	 * - from-address
	 * - from-address=proto
	 * - from-address=proto:port
	 * - from-network/masklen
	 * - from-network/masklen=proto
	 * - from-network/masklen=proto:port
	 */
	phase2id(lname, sizeof lname, "from", ssflow, smasklen, tproto, sport);
	if (conf_set(af, conn, "Local-ID", lname, 0, 0)) {
		conf_end(af, 0);
		goto fail;
	}
	if (!conf_get_str(lname, "ID-type")) {
		if (conf_set(af, lname, "Refcount", "1", 0, 0)) {
			conf_end(af, 0);
			goto fail;
		}
		if (shostflag) {
			if (conf_set(af, lname, "ID-type", sidtype, 0, 0) ||
			    conf_set(af, lname, "Address", ssflow, 0, 0)) {
				conf_end(af, 0);
				goto fail;
			}
		} else {
			if (conf_set(af, lname, "ID-type", sidtype, 0, 0) ||
			    conf_set(af, lname, "Network", ssflow, 0, 0) ||
			    conf_set(af, lname, "Netmask", ssmask, 0, 0)) {
				conf_end(af, 0);
				goto fail;
			}
		}
		if (tproto) {
			snprintf(tmbuf, sizeof sport * 3 + 1, "%u", tproto);
			if (conf_set(af, lname, "Protocol", tmbuf, 0, 0)) {
				conf_end(af, 0);
				goto fail;
			}
			if (sport) {
				snprintf(tmbuf, sizeof sport * 3 + 1, "%u",
				    ntohs(sport));
				if (conf_set(af, lname, "Port", tmbuf, 0, 0)) {
					conf_end(af, 0);
					goto fail;
				}
			}
		}
	} else
		pf_key_v2_conf_refinc(af, lname);

	/*
	 * Set Remote-ID section.
	 * to-address
	 * to-address=proto
	 * to-address=proto:port
	 * to-network/masklen
	 * to-network/masklen=proto
	 * to-network/masklen=proto:port
	 */
	phase2id(dname, sizeof dname, "to", sdflow, dmasklen, tproto, dport);
	if (conf_set(af, conn, "Remote-ID", dname, 0, 0)) {
		conf_end(af, 0);
		goto fail;
	}
	if (!conf_get_str(dname, "ID-type")) {
		if (conf_set(af, dname, "Refcount", "1", 0, 0)) {
			conf_end(af, 0);
			goto fail;
		}
		if (dhostflag) {
			if (conf_set(af, dname, "ID-type", didtype, 0, 0) ||
			    conf_set(af, dname, "Address", sdflow, 0, 0)) {
				conf_end(af, 0);
				goto fail;
			}
		} else {
			if (conf_set(af, dname, "ID-type", didtype, 0, 0) ||
			    conf_set(af, dname, "Network", sdflow, 0, 0) ||
			    conf_set(af, dname, "Netmask", sdmask, 0, 0)) {
				conf_end(af, 0);
				goto fail;
			}
		}

		if (tproto) {
			snprintf(tmbuf, sizeof dport * 3 + 1, "%u", tproto);
			if (conf_set(af, dname, "Protocol", tmbuf, 0, 0)) {
				conf_end(af, 0);
				goto fail;
			}
			if (dport) {
				snprintf(tmbuf, sizeof dport * 3 + 1, "%u",
				    ntohs(dport));
				if (conf_set(af, dname, "Port", tmbuf, 0, 0)) {
					conf_end(af, 0);
					goto fail;
				}
			}
		}
	} else
		pf_key_v2_conf_refinc(af, dname);

	/*
	 * XXX
	 * We should be using information from the proposal to set this up.
	 * At least, we should make this selectable.
	 */

	/*
	 * Phase 2 configuration.
	 * - phase2-from-address-to-address
	 * - ...
	 * - phase2-from-net/len=proto:port-to-net/len=proto:port
	 */
	snprintf(configname, sizeof configname, "phase2-%s-%s", lname, dname);
	if (conf_set(af, conn, "Configuration", configname, 0, 0)) {
		conf_end(af, 0);
		goto fail;
	}
	if (!conf_get_str(configname, "Exchange_type")) {
		if (conf_set(af, configname, "Exchange_type", "Quick_mode",
		    0, 0) ||
		    conf_set(af, configname, "DOI", "IPSEC", 0, 0)) {
			conf_end(af, 0);
			goto fail;
		}
		if (conf_get_str("General", "Default-phase-2-suites")) {
			if (conf_set(af, configname, "Suites",
			    conf_get_str("General", "Default-phase-2-suites"),
			    0, 0)) {
				conf_end(af, 0);
				goto fail;
			}
		} else {
			if (conf_set(af, configname, "Suites",
			    "QM-ESP-3DES-SHA-PFS-SUITE", 0, 0)) {
				conf_end(af, 0);
				goto fail;
			}
		}
	}

	/* Set the ISAKMP-peer section. */
	if (!conf_get_str(peer, "Phase")) {
		if (conf_set(af, peer, "Phase", "1", 0, 0) ||
		    conf_set(af, peer, "Refcount", "1", 0, 0) ||
		    conf_set(af, peer, "Address", dstbuf, 0, 0)) {
			conf_end(af, 0);
			goto fail;
		}
		if (srcaddr && conf_set(af, peer, "Local-address", srcbuf, 0,
		    0)) {
			conf_end(af, 0);
			goto fail;
		}
		snprintf(confname, sizeof confname, "phase1-%s", peer);
		if (conf_set(af, peer, "Configuration", confname, 0, 0)) {
			conf_end(af, 0);
			goto fail;
		}
		/* Store any credentials passed to us. */
		if (cred) {
			struct cert_handler *handler = 0;
			void           *cert;
			char		num[12], *certprint;

			/* Convert to bytes in-place. */
			cred->sadb_x_cred_len *= PF_KEY_V2_CHUNK;

			if (cred->sadb_x_cred_len <= sizeof *cred) {
				log_print("pf_key_v2_acquire: "
				    "zero-length credentials, aborting SA "
				    "acquisition");
				conf_end(af, 0);
				goto fail;
			}
			switch (cred->sadb_x_cred_type) {
			case SADB_X_CREDTYPE_X509:
				snprintf(num, sizeof num, "%d",
				    ISAKMP_CERTENC_X509_SIG);
				handler = cert_get(ISAKMP_CERTENC_X509_SIG);
				break;
			case SADB_X_CREDTYPE_KEYNOTE:
				snprintf(num, sizeof num, "%d",
				    ISAKMP_CERTENC_KEYNOTE);
				handler = cert_get(ISAKMP_CERTENC_KEYNOTE);
				break;
			default:
				log_print("pf_key_v2_acquire: "
				    "unknown credential type %d",
				    cred->sadb_x_cred_type);
				conf_end(af, 0);
				goto fail;
			}

			if (!handler) {
				log_print("pf_key_v2_acquire: "
				    "cert_get (%s) failed", num);
				conf_end(af, 0);
				goto fail;
			}
			/* Set the credential type as a number. */
			if (conf_set(af, peer, "Credential_type", num, 0, 0)) {
				conf_end(af, 0);
				goto fail;
			}
			/* Get the certificate. */
			cert = handler->cert_get((u_int8_t *) (cred + 1),
			    cred->sadb_x_cred_len - sizeof *cred);

			/* Now convert to printable format. */
			certprint = handler->cert_printable(cert);
			handler->cert_free(cert);
			if (!certprint ||
			    conf_set(af, peer, "Credentials", certprint, 0,
				0)) {
				free(certprint);
				conf_end(af, 0);
				goto fail;
			}
			free(certprint);
		}

		/* Phase 1 configuration. */
		if (!conf_get_str(confname, "exchange_type")) {
			/*
			 * We may have been provided with authentication
			 * material.
			 */
			if (sauth) {
				char           *authm;

				/* Convert to bytes in-place. */
				sauth->sadb_x_cred_len *= PF_KEY_V2_CHUNK;

				switch (sauth->sadb_x_cred_type) {
				case SADB_X_AUTHTYPE_PASSPHRASE:
					if (conf_set(af, confname,
					    "Transforms", "3DES-SHA", 0, 0)) {
						conf_end(af, 0);
						goto fail;
					}
					if (sauth->sadb_x_cred_len <=
					    sizeof *sauth) {
						log_print("pf_key_v2_acquire: "
						    "zero-length passphrase, "
						    "aborting SA acquisition");
						conf_end(af, 0);
						goto fail;
					}
					authm = malloc(sauth->sadb_x_cred_len -
					    sizeof *sauth + 1);
					if (!authm) {
						log_error("pf_key_v2_acquire: "
						    "malloc (%lu) failed",
						    sauth->sadb_x_cred_len -
						    (unsigned long) sizeof *sauth + 1);
						conf_end(af, 0);
						goto fail;
					}
					memcpy(authm, sauth + 1,
					    sauth->sadb_x_cred_len -
					    sizeof *sauth + 1);

					/* Set the passphrase in the peer. */
					if (conf_set(af, peer,
					    "Authentication", authm, 0, 0)) {
						free(authm);
						conf_end(af, 0);
						goto fail;
					}
					free(authm);
					break;

				case SADB_X_AUTHTYPE_RSA:
					if (conf_set(af, confname,
					    "Transforms", "3DES-SHA-RSA_SIG",
					    0, 0)) {
						conf_end(af, 0);
						goto fail;
					}
					if (sauth->sadb_x_cred_len <=
					    sizeof *sauth) {
						log_print("pf_key_v2_acquire: "
						    "zero-length RSA key, "
						    "aborting SA acquisition");
						conf_end(af, 0);
						goto fail;
					}
					authm = key_printable(ISAKMP_KEY_RSA,
					    ISAKMP_KEYTYPE_PRIVATE,
					    (u_int8_t *)(sauth + 1),
					    sauth->sadb_x_cred_len -
					    sizeof *sauth);
					if (!authm) {
						log_print("pf_key_v2_acquire: "
						    "failed to convert "
						    "private key to printable "
						    "format (size %lu)",
						    sauth->sadb_x_cred_len -
						    (unsigned long) sizeof *sauth);
						conf_end(af, 0);
						goto fail;
					}
					/*
					 * Set the key in the peer. We don't
					 * use "Authentication" to avoid
					 * potential conflicts with file-based
					 * configurations that use public key
					 * authentication but still specify
					 * an "Authentication" tag (typically
					 * as a remnant of passphrase-based
					 * testing).
					 */
					if (conf_set(af, peer,
					    "PKAuthentication", authm, 0, 0)) {
						free(authm);
						conf_end(af, 0);
						goto fail;
					}
					free(authm);
					break;

				default:
					log_print("pf_key_v2_acquire: "
					    "unknown authentication "
					    "material type %d received from "
					    "kernel", sauth->sadb_x_cred_type);
					conf_end(af, 0);
					goto fail;
				}
			} else {
				xform = conf_get_str(
				    "Default-phase-1-configuration",
				    "Transforms");
				if (conf_set(af, confname, "Transforms",
				    xform ? xform : "3DES-SHA-RSA_SIG", 0,
				    0)) {
					conf_end(af, 0);
					goto fail;
				}
			}

			if (conf_set(af, confname, "Exchange_Type", "ID_PROT",
			    0, 0) ||
			    conf_set(af, confname, "DOI", "IPSEC", 0, 0) ||
			    conf_set(af, confname, "Refcount", "1", 0, 0)) {
				conf_end(af, 0);
				goto fail;
			}
		} else
			pf_key_v2_conf_refinc(af, confname);

		/* The ID we should use in Phase 1. */
		if (srcid && conf_set(af, peer, "ID", srcid, 0, 0)) {
			conf_end(af, 0);
			goto fail;
		}
		/* The ID the other side should use in Phase 1. */
		if (dstid && conf_set(af, peer, "Remote-ID", dstid, 0, 0)) {
			conf_end(af, 0);
			goto fail;
		}
	} else
		pf_key_v2_conf_refinc(af, peer);

	/* All done. */
	conf_end(af, 1);

	/* Let's rock 'n roll. */
	pf_key_v2_connection_check(conn);
	connection_record_passive(conn);
	conn = 0;

	/* Fall-through to cleanup. */
fail:
	if (ret)
		pf_key_v2_msg_free(ret);
	if (askpolicy)
		pf_key_v2_msg_free(askpolicy);
	free(srcid);
	free(dstid);
	free(peer);
	free(conn);
	return;
}

static void
pf_key_v2_notify(struct pf_key_v2_msg *msg)
{
	switch (((struct sadb_msg *)TAILQ_FIRST(msg)->seg)->sadb_msg_type) {
	case SADB_EXPIRE:
		pf_key_v2_expire(msg);
		break;

	case SADB_ACQUIRE:
		if (!ui_daemon_passive)
			pf_key_v2_acquire(msg);
		break;

	default:
		log_print("pf_key_v2_notify: unexpected message type (%d)",
		    ((struct sadb_msg *)TAILQ_FIRST(msg)->seg)->sadb_msg_type);
	}
	pf_key_v2_msg_free(msg);
}

void
pf_key_v2_handler(int fd)
{
	struct pf_key_v2_msg *msg;
	int		n;

	/*
	 * As synchronous read/writes to the socket can have taken place
	 * between the select(2) call of the main loop and this handler, we
	 * need to recheck the readability.
	 */
	if (ioctl(pf_key_v2_socket, FIONREAD, &n) == -1) {
		log_error("pf_key_v2_handler: ioctl (%d, FIONREAD, &n) failed",
		    pf_key_v2_socket);
		return;
	}
	if (!n)
		return;

	msg = pf_key_v2_read(0);
	if (msg)
		pf_key_v2_notify(msg);
}

/*
 * Group 2 IPsec SAs given by the PROTO1 and PROTO2 protocols of the SA IKE
 * security association in a chain.
 * XXX Assumes OpenBSD GRPSPIS extension.
 */
int
pf_key_v2_group_spis(struct sa *sa, struct proto *proto1,
    struct proto *proto2, int incoming)
{
	struct sadb_msg msg;
	struct sadb_sa  sa1, sa2;
	struct sadb_address *addr = 0;
	struct sadb_protocol protocol;
	struct pf_key_v2_msg *grpspis = 0, *ret = 0;
	struct sockaddr *saddr;
	int		err;
	size_t		len;

	msg.sadb_msg_type = SADB_X_GRPSPIS;
	switch (proto1->proto) {
	case IPSEC_PROTO_IPSEC_ESP:
		msg.sadb_msg_satype = SADB_SATYPE_ESP;
		break;
	case IPSEC_PROTO_IPSEC_AH:
		msg.sadb_msg_satype = SADB_SATYPE_AH;
		break;
	case IPSEC_PROTO_IPCOMP:
		msg.sadb_msg_satype = SADB_X_SATYPE_IPCOMP;
		break;
	default:
		log_print("pf_key_v2_group_spis: invalid proto %d",
		    proto1->proto);
		goto cleanup;
	}
	msg.sadb_msg_seq = 0;
	grpspis = pf_key_v2_msg_new(&msg, 0);
	if (!grpspis)
		goto cleanup;

	/* Setup the SA extensions.  */
	sa1.sadb_sa_exttype = SADB_EXT_SA;
	sa1.sadb_sa_len = sizeof sa1 / PF_KEY_V2_CHUNK;
	memcpy(&sa1.sadb_sa_spi, proto1->spi[incoming],
	    sizeof sa1.sadb_sa_spi);
	sa1.sadb_sa_replay = 0;
	sa1.sadb_sa_state = 0;
	sa1.sadb_sa_auth = 0;
	sa1.sadb_sa_encrypt = 0;
	sa1.sadb_sa_flags = 0;
	if (pf_key_v2_msg_add(grpspis, (struct sadb_ext *)&sa1, 0) == -1)
		goto cleanup;

	sa2.sadb_sa_exttype = SADB_X_EXT_SA2;
	sa2.sadb_sa_len = sizeof sa2 / PF_KEY_V2_CHUNK;
	memcpy(&sa2.sadb_sa_spi, proto2->spi[incoming],
	    sizeof sa2.sadb_sa_spi);
	sa2.sadb_sa_replay = 0;
	sa2.sadb_sa_state = 0;
	sa2.sadb_sa_auth = 0;
	sa2.sadb_sa_encrypt = 0;
	sa2.sadb_sa_flags = 0;
	if (pf_key_v2_msg_add(grpspis, (struct sadb_ext *)&sa2, 0) == -1)
		goto cleanup;

	/*
	 * Setup the ADDRESS extensions.
	 */
	if (incoming)
		sa->transport->vtbl->get_src(sa->transport, &saddr);
	else
		sa->transport->vtbl->get_dst(sa->transport, &saddr);
	len = sizeof *addr + PF_KEY_V2_ROUND(SA_LEN(saddr));
	addr = calloc(1, len);
	if (!addr)
		goto cleanup;
	addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
	addr->sadb_address_len = len / PF_KEY_V2_CHUNK;
	addr->sadb_address_reserved = 0;
	memcpy(addr + 1, saddr, SA_LEN(saddr));
	((struct sockaddr_in *) (addr + 1))->sin_port = 0;
	if (pf_key_v2_msg_add(grpspis, (struct sadb_ext *) addr,
	    PF_KEY_V2_NODE_MALLOCED) == -1)
		goto cleanup;
	addr = 0;

	addr = calloc(1, len);
	if (!addr)
		goto cleanup;
	addr->sadb_address_exttype = SADB_X_EXT_DST2;
	addr->sadb_address_len = len / PF_KEY_V2_CHUNK;
	addr->sadb_address_reserved = 0;
	memcpy(addr + 1, saddr, SA_LEN(saddr));
	((struct sockaddr_in *) (addr + 1))->sin_port = 0;
	if (pf_key_v2_msg_add(grpspis, (struct sadb_ext *) addr,
	    PF_KEY_V2_NODE_MALLOCED) == -1)
		goto cleanup;
	addr = 0;

	/* Setup the PROTOCOL extension.  */
	protocol.sadb_protocol_exttype = SADB_X_EXT_PROTOCOL;
	protocol.sadb_protocol_len = sizeof protocol / PF_KEY_V2_CHUNK;
	switch (proto2->proto) {
	case IPSEC_PROTO_IPSEC_ESP:
		protocol.sadb_protocol_proto = SADB_SATYPE_ESP;
		break;
	case IPSEC_PROTO_IPSEC_AH:
		protocol.sadb_protocol_proto = SADB_SATYPE_AH;
		break;
	case IPSEC_PROTO_IPCOMP:
		protocol.sadb_protocol_proto = SADB_X_SATYPE_IPCOMP;
		break;
	default:
		log_print("pf_key_v2_group_spis: invalid proto %d",
		    proto2->proto);
		goto cleanup;
	}
	protocol.sadb_protocol_reserved2 = 0;
	if (pf_key_v2_msg_add(grpspis,
	    (struct sadb_ext *)&protocol, 0) == -1)
		goto cleanup;

	ret = pf_key_v2_call(grpspis);
	pf_key_v2_msg_free(grpspis);
	grpspis = 0;
	if (!ret)
		goto cleanup;
	err = ((struct sadb_msg *)TAILQ_FIRST(ret)->seg)->sadb_msg_errno;
	if (err) {
		log_print("pf_key_v2_group_spis: GRPSPIS: %s", strerror(err));
		goto cleanup;
	}
	pf_key_v2_msg_free(ret);

	LOG_DBG((LOG_SYSDEP, 50, "pf_key_v2_group_spis: done"));

	return 0;

cleanup:
	free(addr);
	if (grpspis)
		pf_key_v2_msg_free(grpspis);
	if (ret)
		pf_key_v2_msg_free(ret);
	return -1;
}