#include <machine/rtems-bsd-user-space.h>
#ifdef __rtems__
#include <machine/rtems-bsd-program.h>
#include "rtems-bsd-racoon-namespace.h"
#endif /* __rtems__ */

/*	$NetBSD: pfkey.c,v 1.57 2011/03/15 13:20:14 vanhu Exp $	*/

/* $Id: pfkey.c,v 1.57 2011/03/15 13:20:14 vanhu Exp $ */

/*
 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
 * 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.
 * 3. Neither the name of the project nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``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 PROJECT OR CONTRIBUTORS 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.
 */

#include "config.h"

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <netdb.h>
#include <errno.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#ifdef ENABLE_NATT
# ifdef __linux__
#  include <linux/udp.h>
# endif
# if defined(__NetBSD__) || defined(__FreeBSD__) ||	\
  (defined(__APPLE__) && defined(__MACH__))
#  include <netinet/udp.h>
# endif
#endif

#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/queue.h>
#include <sys/sysctl.h>

#include <net/route.h>
#include <net/pfkeyv2.h>

#include <netinet/in.h>
#include PATH_IPSEC_H
#include <fcntl.h>

#include "libpfkey.h"

#include "var.h"
#include "misc.h"
#include "vmbuf.h"
#include "plog.h"
#include "sockmisc.h"
#include "session.h"
#include "debug.h"

#include "schedule.h"
#include "localconf.h"
#include "remoteconf.h"
#include "handler.h"
#include "policy.h"
#include "proposal.h"
#include "isakmp_var.h"
#include "isakmp.h"
#include "isakmp_inf.h"
#include "ipsec_doi.h"
#include "oakley.h"
#include "pfkey.h"
#include "algorithm.h"
#include "sainfo.h"
#include "admin.h"
#include "evt.h"
#include "privsep.h"
#include "strnames.h"
#include "backupsa.h"
#include "gcmalloc.h"
#include "nattraversal.h"
#include "crypto_openssl.h"
#include "grabmyaddr.h"

#if defined(SADB_X_EALG_RIJNDAELCBC) && !defined(SADB_X_EALG_AESCBC)
#define SADB_X_EALG_AESCBC  SADB_X_EALG_RIJNDAELCBC
#endif

/* prototype */
static u_int ipsecdoi2pfkey_aalg __P((u_int));
static u_int ipsecdoi2pfkey_ealg __P((u_int));
static u_int ipsecdoi2pfkey_calg __P((u_int));
static u_int ipsecdoi2pfkey_alg __P((u_int, u_int));
static u_int keylen_aalg __P((u_int));
static u_int keylen_ealg __P((u_int, int));

static int pk_recvgetspi __P((caddr_t *));
static int pk_recvupdate __P((caddr_t *));
static int pk_recvadd __P((caddr_t *));
static int pk_recvdelete __P((caddr_t *));
static int pk_recvacquire __P((caddr_t *));
static int pk_recvexpire __P((caddr_t *));
static int pk_recvflush __P((caddr_t *));
static int getsadbpolicy __P((caddr_t *, int *, int, struct ph2handle *));
static int pk_recvspdupdate __P((caddr_t *));
static int pk_recvspdadd __P((caddr_t *));
static int pk_recvspddelete __P((caddr_t *));
static int pk_recvspdexpire __P((caddr_t *));
static int pk_recvspdget __P((caddr_t *));
static int pk_recvspddump __P((caddr_t *));
static int pk_recvspdflush __P((caddr_t *));
#if defined(SADB_X_MIGRATE) && defined(SADB_X_EXT_KMADDRESS)
static int pk_recvmigrate __P((caddr_t *));
#endif
static struct sadb_msg *pk_recv __P((int, int *));

static int (*pkrecvf[]) __P((caddr_t *)) = {
NULL,
pk_recvgetspi,
pk_recvupdate,
pk_recvadd,
pk_recvdelete,
NULL,	/* SADB_GET */
pk_recvacquire,
NULL,	/* SABD_REGISTER */
pk_recvexpire,
pk_recvflush,
NULL,	/* SADB_DUMP */
NULL,	/* SADB_X_PROMISC */
NULL,	/* SADB_X_PCHANGE */
pk_recvspdupdate,
pk_recvspdadd,
pk_recvspddelete,
pk_recvspdget,
NULL,	/* SADB_X_SPDACQUIRE */
pk_recvspddump,
pk_recvspdflush,
NULL,	/* SADB_X_SPDSETIDX */
pk_recvspdexpire,
NULL,	/* SADB_X_SPDDELETE2 */
NULL,	/* SADB_X_NAT_T_NEW_MAPPING */
#if defined(SADB_X_MIGRATE) && defined(SADB_X_EXT_KMADDRESS)
pk_recvmigrate,
#else
NULL,	/* SADB_X_MIGRATE */
#endif
#if (SADB_MAX > 24)
#error "SADB extra message?"
#endif
};

static int addnewsp __P((caddr_t *, struct sockaddr *, struct sockaddr *));

/* cope with old kame headers - ugly */
#ifndef SADB_X_AALG_MD5
#define SADB_X_AALG_MD5		SADB_AALG_MD5
#endif
#ifndef SADB_X_AALG_SHA
#define SADB_X_AALG_SHA		SADB_AALG_SHA
#endif
#ifndef SADB_X_AALG_NULL
#define SADB_X_AALG_NULL	SADB_AALG_NULL
#endif

#ifndef SADB_X_EALG_BLOWFISHCBC
#define SADB_X_EALG_BLOWFISHCBC	SADB_EALG_BLOWFISHCBC
#endif
#ifndef SADB_X_EALG_CAST128CBC
#define SADB_X_EALG_CAST128CBC	SADB_EALG_CAST128CBC
#endif
#ifndef SADB_X_EALG_RC5CBC
#ifdef SADB_EALG_RC5CBC
#define SADB_X_EALG_RC5CBC	SADB_EALG_RC5CBC
#endif
#endif

/*
 * PF_KEY packet handler
 *	0: success
 *	-1: fail
 */
static int
pfkey_handler(ctx, fd)
	void *ctx;
	int fd;
{
	struct sadb_msg *msg;
	int len;
	caddr_t mhp[SADB_EXT_MAX + 1];
	int error = -1;

	/* receive pfkey message. */
	len = 0;
	msg = (struct sadb_msg *) pk_recv(fd, &len);
	if (msg == NULL) {
		if (len < 0) {
		        /* do not report EAGAIN as error; well get
		         * called from main loop later. and it's normal
		         * when spd dump is received during reload and
		         * this function is called in loop. */
		        if (errno == EAGAIN)
		                goto end;

			plog(LLV_ERROR, LOCATION, NULL,
				"failed to recv from pfkey (%s)\n",
				strerror(errno));
			goto end;
		} else {
			/* short message - msg not ready */
			return 0;
		}
	}

	plog(LLV_DEBUG, LOCATION, NULL, "got pfkey %s message\n",
		s_pfkey_type(msg->sadb_msg_type));
	plogdump(LLV_DEBUG2, msg, msg->sadb_msg_len << 3);

	/* validity check */
	if (msg->sadb_msg_errno) {
		int pri;

		/* when SPD is empty, treat the state as no error. */
		if (msg->sadb_msg_type == SADB_X_SPDDUMP &&
		    msg->sadb_msg_errno == ENOENT)
			pri = LLV_DEBUG;
		else
			pri = LLV_ERROR;

		plog(pri, LOCATION, NULL,
			"pfkey %s failed: %s\n",
			s_pfkey_type(msg->sadb_msg_type),
			strerror(msg->sadb_msg_errno));

		goto end;
	}

	/* check pfkey message. */
	if (pfkey_align(msg, mhp)) {
		plog(LLV_ERROR, LOCATION, NULL,
			"libipsec failed pfkey align (%s)\n",
			ipsec_strerror());
		goto end;
	}
	if (pfkey_check(mhp)) {
		plog(LLV_ERROR, LOCATION, NULL,
			"libipsec failed pfkey check (%s)\n",
			ipsec_strerror());
		goto end;
	}
	msg = (struct sadb_msg *)mhp[0];

	/* safety check */
	if (msg->sadb_msg_type >= ARRAYLEN(pkrecvf)) {
		plog(LLV_ERROR, LOCATION, NULL,
			"unknown PF_KEY message type=%u\n",
			msg->sadb_msg_type);
		goto end;
	}

	if (pkrecvf[msg->sadb_msg_type] == NULL) {
		plog(LLV_INFO, LOCATION, NULL,
			"unsupported PF_KEY message %s\n",
			s_pfkey_type(msg->sadb_msg_type));
		goto end;
	}

	if ((pkrecvf[msg->sadb_msg_type])(mhp) < 0)
		goto end;

	error = 1;
end:
	if (msg)
		racoon_free(msg);
	return(error);
}

/*
 * dump SADB
 */
vchar_t *
pfkey_dump_sadb(satype)
	int satype;
{
	int s;
	vchar_t *buf = NULL;
	pid_t pid = getpid();
	struct sadb_msg *msg = NULL;
	size_t bl, ml;
	int len;
	int bufsiz;

	if ((s = privsep_socket(PF_KEY, SOCK_RAW, PF_KEY_V2)) < 0) {
		plog(LLV_ERROR, LOCATION, NULL,
			"libipsec failed pfkey open: %s\n",
			ipsec_strerror());
		return NULL;
	}

	if ((bufsiz = pfkey_set_buffer_size(s, lcconf->pfkey_buffer_size)) < 0) {
		plog(LLV_ERROR, LOCATION, NULL,
		     "libipsec failed pfkey set buffer size to %d: %s\n",
		     lcconf->pfkey_buffer_size, ipsec_strerror());
		return NULL;
	} else if (bufsiz < lcconf->pfkey_buffer_size) {
		plog(LLV_WARNING, LOCATION, NULL,
		     "pfkey socket receive buffer set to %dKB, instead of %d\n",
		     bufsiz, lcconf->pfkey_buffer_size);
	}

	plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_dump\n");
	if (pfkey_send_dump(s, satype) < 0) {
		plog(LLV_ERROR, LOCATION, NULL,
			"libipsec failed dump: %s\n", ipsec_strerror());
		goto fail;
	}

	while (1) {
		if (msg)
			racoon_free(msg);
		msg = pk_recv(s, &len);
		if (msg == NULL) {
			if (len < 0)
				goto done;
			else
				continue;
		}

		if (msg->sadb_msg_type != SADB_DUMP || msg->sadb_msg_pid != pid)
		{
		    plog(LLV_DEBUG, LOCATION, NULL,
			 "discarding non-sadb dump msg %p, our pid=%i\n", msg, pid);
		    plog(LLV_DEBUG, LOCATION, NULL,
			 "type %i, pid %i\n", msg->sadb_msg_type, msg->sadb_msg_pid);
		    continue;
		}


		ml = msg->sadb_msg_len << 3;
		bl = buf ? buf->l : 0;
		buf = vrealloc(buf, bl + ml);
		if (buf == NULL) {
			plog(LLV_ERROR, LOCATION, NULL,
				"failed to reallocate buffer to dump.\n");
			goto fail;
		}
		memcpy(buf->v + bl, msg, ml);

		if (msg->sadb_msg_seq == 0)
			break;
	}
	goto done;

fail:
	if (buf)
		vfree(buf);
	buf = NULL;
done:
	if (msg)
		racoon_free(msg);
	close(s);
	return buf;
}

#ifdef ENABLE_ADMINPORT
/*
 * flush SADB
 */
void
pfkey_flush_sadb(proto)
	u_int proto;
{
	int satype;

	/* convert to SADB_SATYPE */
	if ((satype = admin2pfkey_proto(proto)) < 0)
		return;

	plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_flush\n");
	if (pfkey_send_flush(lcconf->sock_pfkey, satype) < 0) {
		plog(LLV_ERROR, LOCATION, NULL,
			"libipsec failed send flush (%s)\n", ipsec_strerror());
		return;
	}

	return;
}
#endif

/*
 * These are the SATYPEs that we manage.  We register to get
 * PF_KEY messages related to these SATYPEs, and we also use
 * this list to determine which SATYPEs to delete SAs for when
 * we receive an INITIAL-CONTACT.
 */
const struct pfkey_satype pfkey_satypes[] = {
	{ SADB_SATYPE_AH,	"AH" },
	{ SADB_SATYPE_ESP,	"ESP" },
	{ SADB_X_SATYPE_IPCOMP,	"IPCOMP" },
};
const int pfkey_nsatypes =
    sizeof(pfkey_satypes) / sizeof(pfkey_satypes[0]);

/*
 * PF_KEY initialization
 */
int
pfkey_init()
{
	int i, reg_fail;
	int bufsiz;

	if ((lcconf->sock_pfkey = pfkey_open()) < 0) {
		plog(LLV_ERROR, LOCATION, NULL,
			"libipsec failed pfkey open (%s)\n", ipsec_strerror());
		return -1;
	}
	if ((bufsiz = pfkey_set_buffer_size(lcconf->sock_pfkey,
					    lcconf->pfkey_buffer_size)) < 0) {
		plog(LLV_ERROR, LOCATION, NULL,
		     "libipsec failed to set pfkey buffer size to %d (%s)\n",
		     lcconf->pfkey_buffer_size, ipsec_strerror());
		return -1;
	} else if (bufsiz < lcconf->pfkey_buffer_size) {
		plog(LLV_WARNING, LOCATION, NULL,
		     "pfkey socket receive buffer set to %dKB, instead of %d\n",
		     bufsiz, lcconf->pfkey_buffer_size);
	}

	if (fcntl(lcconf->sock_pfkey, F_SETFL, O_NONBLOCK) == -1)
		plog(LLV_WARNING, LOCATION, NULL,
		    "failed to set the pfkey socket to NONBLOCK\n");

	for (i = 0, reg_fail = 0; i < pfkey_nsatypes; i++) {
		plog(LLV_DEBUG, LOCATION, NULL,
		    "call pfkey_send_register for %s\n",
		    pfkey_satypes[i].ps_name);
		if (pfkey_send_register(lcconf->sock_pfkey,
					pfkey_satypes[i].ps_satype) < 0 ||
		    pfkey_recv_register(lcconf->sock_pfkey) < 0) {
			plog(LLV_WARNING, LOCATION, NULL,
			    "failed to register %s (%s)\n",
			    pfkey_satypes[i].ps_name,
			    ipsec_strerror());
			reg_fail++;
		}
	}

	if (reg_fail == pfkey_nsatypes) {
		plog(LLV_ERROR, LOCATION, NULL,
			"failed to regist any protocol.\n");
		pfkey_close(lcconf->sock_pfkey);
		return -1;
	}

	initsp();

	if (pfkey_send_spddump(lcconf->sock_pfkey) < 0) {
		plog(LLV_ERROR, LOCATION, NULL,
			"libipsec sending spddump failed: %s\n",
			ipsec_strerror());
		pfkey_close(lcconf->sock_pfkey);
		return -1;
	}
#if 0
	if (pfkey_promisc_toggle(1) < 0) {
		pfkey_close(lcconf->sock_pfkey);
		return -1;
	}
#endif
	monitor_fd(lcconf->sock_pfkey, pfkey_handler, NULL, 0);
	return 0;
}

int
pfkey_reload()
{
	flushsp();

	if (pfkey_send_spddump(lcconf->sock_pfkey) < 0) {
		plog(LLV_ERROR, LOCATION, NULL,
			"libipsec sending spddump failed: %s\n",
			ipsec_strerror());
		return -1;
	}

	while (pfkey_handler(NULL, lcconf->sock_pfkey) > 0)
		continue;

	return 0;
}

/* %%% for conversion */
/* IPSECDOI_ATTR_AUTH -> SADB_AALG */
static u_int
ipsecdoi2pfkey_aalg(hashtype)
	u_int hashtype;
{
	switch (hashtype) {
	case IPSECDOI_ATTR_AUTH_HMAC_MD5:
		return SADB_AALG_MD5HMAC;
	case IPSECDOI_ATTR_AUTH_HMAC_SHA1:
		return SADB_AALG_SHA1HMAC;
	case IPSECDOI_ATTR_AUTH_HMAC_SHA2_256:
#if (defined SADB_X_AALG_SHA2_256) && !defined(SADB_X_AALG_SHA2_256HMAC)
		return SADB_X_AALG_SHA2_256;
#else
		return SADB_X_AALG_SHA2_256HMAC;
#endif
	case IPSECDOI_ATTR_AUTH_HMAC_SHA2_384:
#if (defined SADB_X_AALG_SHA2_384) && !defined(SADB_X_AALG_SHA2_384HMAC)
		return SADB_X_AALG_SHA2_384;
#else
		return SADB_X_AALG_SHA2_384HMAC;
#endif
	case IPSECDOI_ATTR_AUTH_HMAC_SHA2_512:
#if (defined SADB_X_AALG_SHA2_512) && !defined(SADB_X_AALG_SHA2_512HMAC)
		return SADB_X_AALG_SHA2_512;
#else
		return SADB_X_AALG_SHA2_512HMAC;
#endif
	case IPSECDOI_ATTR_AUTH_KPDK:		/* need special care */
		return SADB_AALG_NONE;

	/* not supported */
	case IPSECDOI_ATTR_AUTH_DES_MAC:
		plog(LLV_ERROR, LOCATION, NULL,
			"Not supported hash type: %u\n", hashtype);
		return ~0;

	case 0: /* reserved */
	default:
		return SADB_AALG_NONE;

		plog(LLV_ERROR, LOCATION, NULL,
			"Invalid hash type: %u\n", hashtype);
		return ~0;
	}
	/*NOTREACHED*/
}

/* IPSECDOI_ESP -> SADB_EALG */
static u_int
ipsecdoi2pfkey_ealg(t_id)
	u_int t_id;
{
	switch (t_id) {
	case IPSECDOI_ESP_DES_IV64:		/* sa_flags |= SADB_X_EXT_OLD */
		return SADB_EALG_DESCBC;
	case IPSECDOI_ESP_DES:
		return SADB_EALG_DESCBC;
	case IPSECDOI_ESP_3DES:
		return SADB_EALG_3DESCBC;
#ifdef SADB_X_EALG_RC5CBC
	case IPSECDOI_ESP_RC5:
		return SADB_X_EALG_RC5CBC;
#endif
	case IPSECDOI_ESP_CAST:
		return SADB_X_EALG_CAST128CBC;
	case IPSECDOI_ESP_BLOWFISH:
		return SADB_X_EALG_BLOWFISHCBC;
	case IPSECDOI_ESP_DES_IV32:	/* flags |= (SADB_X_EXT_OLD|
							SADB_X_EXT_IV4B)*/
		return SADB_EALG_DESCBC;
	case IPSECDOI_ESP_NULL:
		return SADB_EALG_NULL;
#ifdef SADB_X_EALG_AESCBC
	case IPSECDOI_ESP_AES:
		return SADB_X_EALG_AESCBC;
#endif
#ifdef SADB_X_EALG_TWOFISHCBC
	case IPSECDOI_ESP_TWOFISH:
		return SADB_X_EALG_TWOFISHCBC;
#endif
#ifdef SADB_X_EALG_CAMELLIACBC
	case IPSECDOI_ESP_CAMELLIA:
		return SADB_X_EALG_CAMELLIACBC;
#endif

	/* not supported */
	case IPSECDOI_ESP_3IDEA:
	case IPSECDOI_ESP_IDEA:
	case IPSECDOI_ESP_RC4:
		plog(LLV_ERROR, LOCATION, NULL,
			"Not supported transform: %u\n", t_id);
		return ~0;

	case 0: /* reserved */
	default:
		plog(LLV_ERROR, LOCATION, NULL,
			"Invalid transform id: %u\n", t_id);
		return ~0;
	}
	/*NOTREACHED*/
}

/* IPCOMP -> SADB_CALG */
static u_int
ipsecdoi2pfkey_calg(t_id)
	u_int t_id;
{
	switch (t_id) {
	case IPSECDOI_IPCOMP_OUI:
		return SADB_X_CALG_OUI;
	case IPSECDOI_IPCOMP_DEFLATE:
		return SADB_X_CALG_DEFLATE;
	case IPSECDOI_IPCOMP_LZS:
		return SADB_X_CALG_LZS;

	case 0: /* reserved */
	default:
		plog(LLV_ERROR, LOCATION, NULL,
			"Invalid transform id: %u\n", t_id);
		return ~0;
	}
	/*NOTREACHED*/
}

/* IPSECDOI_PROTO -> SADB_SATYPE */
u_int
ipsecdoi2pfkey_proto(proto)
	u_int proto;
{
	switch (proto) {
	case IPSECDOI_PROTO_IPSEC_AH:
		return SADB_SATYPE_AH;
	case IPSECDOI_PROTO_IPSEC_ESP:
		return SADB_SATYPE_ESP;
	case IPSECDOI_PROTO_IPCOMP:
		return SADB_X_SATYPE_IPCOMP;

	default:
		plog(LLV_ERROR, LOCATION, NULL,
			"Invalid ipsec_doi proto: %u\n", proto);
		return ~0;
	}
	/*NOTREACHED*/
}

static u_int
ipsecdoi2pfkey_alg(algclass, type)
	u_int algclass, type;
{
	switch (algclass) {
	case IPSECDOI_ATTR_AUTH:
		return ipsecdoi2pfkey_aalg(type);
	case IPSECDOI_PROTO_IPSEC_ESP:
		return ipsecdoi2pfkey_ealg(type);
	case IPSECDOI_PROTO_IPCOMP:
		return ipsecdoi2pfkey_calg(type);
	default:
		plog(LLV_ERROR, LOCATION, NULL,
			"Invalid ipsec_doi algclass: %u\n", algclass);
		return ~0;
	}
	/*NOTREACHED*/
}

/* SADB_SATYPE -> IPSECDOI_PROTO */
u_int
pfkey2ipsecdoi_proto(satype)
	u_int satype;
{
	switch (satype) {
	case SADB_SATYPE_AH:
		return IPSECDOI_PROTO_IPSEC_AH;
	case SADB_SATYPE_ESP:
		return IPSECDOI_PROTO_IPSEC_ESP;
	case SADB_X_SATYPE_IPCOMP:
		return IPSECDOI_PROTO_IPCOMP;

	default:
		plog(LLV_ERROR, LOCATION, NULL,
			"Invalid pfkey proto: %u\n", satype);
		return ~0;
	}
	/*NOTREACHED*/
}

/* IPSECDOI_ATTR_ENC_MODE -> IPSEC_MODE */
u_int
ipsecdoi2pfkey_mode(mode)
	u_int mode;
{
	switch (mode) {
	case IPSECDOI_ATTR_ENC_MODE_TUNNEL:
#ifdef ENABLE_NATT
	case IPSECDOI_ATTR_ENC_MODE_UDPTUNNEL_RFC:
	case IPSECDOI_ATTR_ENC_MODE_UDPTUNNEL_DRAFT:
#endif
		return IPSEC_MODE_TUNNEL;
	case IPSECDOI_ATTR_ENC_MODE_TRNS:
#ifdef ENABLE_NATT
	case IPSECDOI_ATTR_ENC_MODE_UDPTRNS_RFC:
	case IPSECDOI_ATTR_ENC_MODE_UDPTRNS_DRAFT:
#endif
		return IPSEC_MODE_TRANSPORT;
	default:
		plog(LLV_ERROR, LOCATION, NULL, "Invalid mode type: %u\n", mode);
		return ~0;
	}
	/*NOTREACHED*/
}

/* IPSECDOI_ATTR_ENC_MODE -> IPSEC_MODE */
u_int
pfkey2ipsecdoi_mode(mode)
	u_int mode;
{
	switch (mode) {
	case IPSEC_MODE_TUNNEL:
		return IPSECDOI_ATTR_ENC_MODE_TUNNEL;
	case IPSEC_MODE_TRANSPORT:
		return IPSECDOI_ATTR_ENC_MODE_TRNS;
	case IPSEC_MODE_ANY:
		return IPSECDOI_ATTR_ENC_MODE_ANY;
	default:
		plog(LLV_ERROR, LOCATION, NULL, "Invalid mode type: %u\n", mode);
		return ~0;
	}
	/*NOTREACHED*/
}

/* default key length for encryption algorithm */
static u_int
keylen_aalg(hashtype)
	u_int hashtype;
{
	int res;

	if (hashtype == 0)
		return SADB_AALG_NONE;

	res = alg_ipsec_hmacdef_hashlen(hashtype);
	if (res == -1) {
		plog(LLV_ERROR, LOCATION, NULL,
			"invalid hmac algorithm %u.\n", hashtype);
		return ~0;
	}
	return res;
}

/* default key length for encryption algorithm */
static u_int
keylen_ealg(enctype, encklen)
	u_int enctype;
	int encklen;
{
	int res;

	res = alg_ipsec_encdef_keylen(enctype, encklen);
	if (res == -1) {
		plog(LLV_ERROR, LOCATION, NULL,
			"invalid encryption algorithm %u.\n", enctype);
		return ~0;
	}
	return res;
}

void
pk_fixup_sa_addresses(mhp)
	caddr_t *mhp;
{
	struct sockaddr *src, *dst;

	src = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]);
	dst = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]);
	set_port(src, PORT_ISAKMP);
	set_port(dst, PORT_ISAKMP);

#ifdef ENABLE_NATT
	if (PFKEY_ADDR_X_NATTYPE(mhp[SADB_X_EXT_NAT_T_TYPE])) {
		/* NAT-T is enabled for this SADB entry; copy
		 * the ports from NAT-T extensions */
		if(mhp[SADB_X_EXT_NAT_T_SPORT] != NULL)
			set_port(src, PFKEY_ADDR_X_PORT(mhp[SADB_X_EXT_NAT_T_SPORT]));
		if(mhp[SADB_X_EXT_NAT_T_DPORT] != NULL)
			set_port(dst, PFKEY_ADDR_X_PORT(mhp[SADB_X_EXT_NAT_T_DPORT]));
	}
#endif
}

int
pfkey_convertfromipsecdoi(proto_id, t_id, hashtype,
		e_type, e_keylen, a_type, a_keylen, flags)
	u_int proto_id;
	u_int t_id;
	u_int hashtype;
	u_int *e_type;
	u_int *e_keylen;
	u_int *a_type;
	u_int *a_keylen;
	u_int *flags;
{
	*flags = 0;
	switch (proto_id) {
	case IPSECDOI_PROTO_IPSEC_ESP:
		if ((*e_type = ipsecdoi2pfkey_ealg(t_id)) == ~0)
			goto bad;
		if ((*e_keylen = keylen_ealg(t_id, *e_keylen)) == ~0)
			goto bad;
		*e_keylen >>= 3;

		if ((*a_type = ipsecdoi2pfkey_aalg(hashtype)) == ~0)
			goto bad;
		if ((*a_keylen = keylen_aalg(hashtype)) == ~0)
			goto bad;
		*a_keylen >>= 3;

		if (*e_type == SADB_EALG_NONE) {
			plog(LLV_ERROR, LOCATION, NULL, "no ESP algorithm.\n");
			goto bad;
		}
		break;

	case IPSECDOI_PROTO_IPSEC_AH:
		if ((*a_type = ipsecdoi2pfkey_aalg(hashtype)) == ~0)
			goto bad;
		if ((*a_keylen = keylen_aalg(hashtype)) == ~0)
			goto bad;
		*a_keylen >>= 3;

		if (t_id == IPSECDOI_ATTR_AUTH_HMAC_MD5
		 && hashtype == IPSECDOI_ATTR_AUTH_KPDK) {
			/* AH_MD5 + Auth(KPDK) = RFC1826 keyed-MD5 */
			*a_type = SADB_X_AALG_MD5;
			*flags |= SADB_X_EXT_OLD;
		}
		*e_type = SADB_EALG_NONE;
		*e_keylen = 0;
		if (*a_type == SADB_AALG_NONE) {
			plog(LLV_ERROR, LOCATION, NULL, "no AH algorithm.\n");
			goto bad;
		}
		break;

	case IPSECDOI_PROTO_IPCOMP:
		if ((*e_type = ipsecdoi2pfkey_calg(t_id)) == ~0)
			goto bad;
		*e_keylen = 0;

		*flags = SADB_X_EXT_RAWCPI;

		*a_type = SADB_AALG_NONE;
		*a_keylen = 0;
		if (*e_type == SADB_X_CALG_NONE) {
			plog(LLV_ERROR, LOCATION, NULL, "no IPCOMP algorithm.\n");
			goto bad;
		}
		break;

	default:
		plog(LLV_ERROR, LOCATION, NULL, "unknown IPsec protocol.\n");
		goto bad;
	}

	return 0;

    bad:
	errno = EINVAL;
	return -1;
}

/*%%%*/
/* send getspi message per ipsec protocol per remote address */
/*
 * the local address and remote address in ph1handle are dealed
 * with destination address and source address respectively.
 * Because SPI is decided by responder.
 */
int
pk_sendgetspi(iph2)
	struct ph2handle *iph2;
{
	struct sockaddr *src = NULL, *dst = NULL;
	u_int satype, mode;
	struct saprop *pp;
	struct saproto *pr;
	u_int32_t minspi, maxspi;
	u_int8_t natt_type = 0;
	u_int16_t sport = 0, dport = 0;

	if (iph2->side == INITIATOR)
		pp = iph2->proposal;
	else
		pp = iph2->approval;

	if (iph2->sa_src && iph2->sa_dst) {
		/* MIPv6: Use SA addresses, not IKE ones */
		src = dupsaddr(iph2->sa_src);
		dst = dupsaddr(iph2->sa_dst);
	} else {
		/* Common case: SA addresses and IKE ones are the same */
		src = dupsaddr(iph2->src);
		dst = dupsaddr(iph2->dst);
	}

	if (src == NULL || dst == NULL) {
		racoon_free(src);
		racoon_free(dst);
		return -1;
	}

	for (pr = pp->head; pr != NULL; pr = pr->next) {

		/* validity check */
		satype = ipsecdoi2pfkey_proto(pr->proto_id);
		if (satype == ~0) {
			plog(LLV_ERROR, LOCATION, NULL,
				"invalid proto_id %d\n", pr->proto_id);
			racoon_free(src);
			racoon_free(dst);
			return -1;
		}
		/* this works around a bug in Linux kernel where it allocates 4 byte
		   spi's for IPCOMP */
		else if (satype == SADB_X_SATYPE_IPCOMP) {
			minspi = 0x100;
			maxspi = 0xffff;
		}
		else {
			minspi = 0;
			maxspi = 0;
		}
		mode = ipsecdoi2pfkey_mode(pr->encmode);
		if (mode == ~0) {
			plog(LLV_ERROR, LOCATION, NULL,
				"invalid encmode %d\n", pr->encmode);
			racoon_free(src);
			racoon_free(dst);
			return -1;
		}

#ifdef ENABLE_NATT
		if (pr->udp_encap) {
			natt_type = iph2->ph1->natt_options->encaps_type;
			sport=extract_port(src);
			dport=extract_port(dst);
		}
#endif

		plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_getspi\n");
		if (pfkey_send_getspi_nat(
				lcconf->sock_pfkey,
				satype,
				mode,
				dst,			/* src of SA */
				src,			/* dst of SA */
				natt_type,
				dport,
				sport,
				minspi, maxspi,
				pr->reqid_in, iph2->seq) < 0) {
			plog(LLV_ERROR, LOCATION, NULL,
				"ipseclib failed send getspi (%s)\n",
				ipsec_strerror());
			racoon_free(src);
			racoon_free(dst);
			return -1;
		}
		plog(LLV_DEBUG, LOCATION, NULL,
			"pfkey GETSPI sent: %s\n",
			sadbsecas2str(dst, src, satype, 0, mode));
	}

	racoon_free(src);
	racoon_free(dst);
	return 0;
}

/*
 * receive GETSPI from kernel.
 */
static int
pk_recvgetspi(mhp)
	caddr_t *mhp;
{
	struct sadb_msg *msg;
	struct sadb_sa *sa;
	struct ph2handle *iph2;
	struct sockaddr *src, *dst;
	int proto_id;
	int allspiok, notfound;
	struct saprop *pp;
	struct saproto *pr;

	/* validity check */
	if (mhp[SADB_EXT_SA] == NULL
	 || mhp[SADB_EXT_ADDRESS_DST] == NULL
	 || mhp[SADB_EXT_ADDRESS_SRC] == NULL) {
		plog(LLV_ERROR, LOCATION, NULL,
			"inappropriate sadb getspi message passed.\n");
		return -1;
	}
	msg = (struct sadb_msg *)mhp[0];
	sa = (struct sadb_sa *)mhp[SADB_EXT_SA];
	pk_fixup_sa_addresses(mhp);
	dst = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]); /* note SA dir */
	src = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]);

	/* the message has to be processed or not ? */
	if (msg->sadb_msg_pid != getpid()) {
		plog(LLV_DEBUG, LOCATION, NULL,
			"%s message is not interesting "
			"because pid %d is not mine.\n",
			s_pfkey_type(msg->sadb_msg_type),
			msg->sadb_msg_pid);
		return -1;
	}

	iph2 = getph2byseq(msg->sadb_msg_seq);
	if (iph2 == NULL) {
		plog(LLV_DEBUG, LOCATION, NULL,
			"seq %d of %s message not interesting.\n",
			msg->sadb_msg_seq,
			s_pfkey_type(msg->sadb_msg_type));
		return -1;
	}

	if (iph2->status != PHASE2ST_GETSPISENT) {
		plog(LLV_ERROR, LOCATION, NULL,
			"status mismatch (db:%d msg:%d)\n",
			iph2->status, PHASE2ST_GETSPISENT);
		return -1;
	}

	/* set SPI, and check to get all spi whether or not */
	allspiok = 1;
	notfound = 1;
	proto_id = pfkey2ipsecdoi_proto(msg->sadb_msg_satype);
	pp = iph2->side == INITIATOR ? iph2->proposal : iph2->approval;

	for (pr = pp->head; pr != NULL; pr = pr->next) {
		if (pr->proto_id == proto_id && pr->spi == 0) {
			pr->spi = sa->sadb_sa_spi;
			notfound = 0;
			plog(LLV_DEBUG, LOCATION, NULL,
				"pfkey GETSPI succeeded: %s\n",
				sadbsecas2str(dst, src,
				    msg->sadb_msg_satype,
				    sa->sadb_sa_spi,
				    ipsecdoi2pfkey_mode(pr->encmode)));
		}
		if (pr->spi == 0)
			allspiok = 0;	/* not get all spi */
	}

	if (notfound) {
		plog(LLV_ERROR, LOCATION, NULL,
			"get spi for unknown address %s\n",
			saddrwop2str(dst));
		return -1;
	}

	if (allspiok) {
		/* update status */
		iph2->status = PHASE2ST_GETSPIDONE;
		if (isakmp_post_getspi(iph2) < 0) {
			plog(LLV_ERROR, LOCATION, NULL,
				"failed to start post getspi.\n");
			remph2(iph2);
			delph2(iph2);
			iph2 = NULL;
			return -1;
		}
	}

	return 0;
}

/*
 * set inbound SA
 */
int
pk_sendupdate(iph2)
	struct ph2handle *iph2;
{
	struct saproto *pr;
	struct pfkey_send_sa_args sa_args;

	/* sanity check */
	if (iph2->approval == NULL) {
		plog(LLV_ERROR, LOCATION, NULL,
			"no approvaled SAs found.\n");
		return -1;
	}

	/* fill in some needed for pfkey_send_update2 */
	memset (&sa_args, 0, sizeof (sa_args));
	sa_args.so = lcconf->sock_pfkey;
	if (iph2->lifetime_secs)
		sa_args.l_addtime = iph2->lifetime_secs;
	else
		sa_args.l_addtime = iph2->approval->lifetime;
	sa_args.seq = iph2->seq;
	sa_args.wsize = 4;

	if (iph2->sa_src && iph2->sa_dst) {
		/* MIPv6: Use SA addresses, not IKE ones */
		sa_args.dst = dupsaddr(iph2->sa_src);
		sa_args.src = dupsaddr(iph2->sa_dst);
	} else {
		/* Common case: SA addresses and IKE ones are the same */
		sa_args.dst = dupsaddr(iph2->src);
		sa_args.src = dupsaddr(iph2->dst);
	}

	if (sa_args.src == NULL || sa_args.dst == NULL) {
		racoon_free(sa_args.src);
		racoon_free(sa_args.dst);
		return -1;
	}

	for (pr = iph2->approval->head; pr != NULL; pr = pr->next) {
		/* validity check */
		sa_args.satype = ipsecdoi2pfkey_proto(pr->proto_id);
		if (sa_args.satype == ~0) {
			plog(LLV_ERROR, LOCATION, NULL,
				"invalid proto_id %d\n", pr->proto_id);
			racoon_free(sa_args.src);
			racoon_free(sa_args.dst);
			return -1;
		}
		else if (sa_args.satype == SADB_X_SATYPE_IPCOMP) {
			/* IPCOMP has no replay window */
			sa_args.wsize = 0;
		}
#ifdef ENABLE_SAMODE_UNSPECIFIED
		sa_args.mode = IPSEC_MODE_ANY;
#else
		sa_args.mode = ipsecdoi2pfkey_mode(pr->encmode);
		if (sa_args.mode == ~0) {
			plog(LLV_ERROR, LOCATION, NULL,
				"invalid encmode %d\n", pr->encmode);
			racoon_free(sa_args.src);
			racoon_free(sa_args.dst);
			return -1;
		}
#endif
		/* set algorithm type and key length */
		sa_args.e_keylen = pr->head->encklen;
		if (pfkey_convertfromipsecdoi(
				pr->proto_id,
				pr->head->trns_id,
				pr->head->authtype,
				&sa_args.e_type, &sa_args.e_keylen,
				&sa_args.a_type, &sa_args.a_keylen,
				&sa_args.flags) < 0){
			racoon_free(sa_args.src);
			racoon_free(sa_args.dst);
			return -1;
		}

#if 0
		sa_args.l_bytes = iph2->approval->lifebyte * 1024,
#else
		sa_args.l_bytes = 0;
#endif

#ifdef HAVE_SECCTX
		if (*iph2->approval->sctx.ctx_str) {
			sa_args.ctxdoi = iph2->approval->sctx.ctx_doi;
			sa_args.ctxalg = iph2->approval->sctx.ctx_alg;
			sa_args.ctxstrlen = iph2->approval->sctx.ctx_strlen;
			sa_args.ctxstr = iph2->approval->sctx.ctx_str;
		}
#endif /* HAVE_SECCTX */

#ifdef ENABLE_NATT
		if (pr->udp_encap) {
			sa_args.l_natt_type = iph2->ph1->natt_options->encaps_type;
			sa_args.l_natt_sport = extract_port(iph2->ph1->remote);
			sa_args.l_natt_dport = extract_port(iph2->ph1->local);
			/* if (iph2->ph1->natt_flags & NAT_DETECTED_PEER) */
				sa_args.l_natt_oai = iph2->natoa_dst;
			/* if (iph2->ph1->natt_flags & NAT_DETECTED_ME) */
				sa_args.l_natt_oar = iph2->natoa_src;
#ifdef SADB_X_EXT_NAT_T_FRAG
			sa_args.l_natt_frag = iph2->ph1->rmconf->esp_frag;
#endif
		}
#endif

		/* more info to fill in */
		sa_args.spi = pr->spi;
		sa_args.reqid = pr->reqid_in;
		sa_args.keymat = pr->keymat->v;

		plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_update2\n");
		if (pfkey_send_update2(&sa_args) < 0) {
			plog(LLV_ERROR, LOCATION, NULL,
				"libipsec failed send update (%s)\n",
				ipsec_strerror());
			racoon_free(sa_args.src);
			racoon_free(sa_args.dst);
			return -1;
		}

		if (!lcconf->pathinfo[LC_PATHTYPE_BACKUPSA])
			continue;

		/*
		 * It maybe good idea to call backupsa_to_file() after
		 * racoon will receive the sadb_update messages.
		 * But it is impossible because there is not key in the
		 * information from the kernel.
		 */

		/* change some things before backing up */
		sa_args.wsize = 4;
		sa_args.l_bytes = iph2->approval->lifebyte * 1024;

		if (backupsa_to_file(&sa_args) < 0) {
			plog(LLV_ERROR, LOCATION, NULL,
				"backuped SA failed: %s\n",
				sadbsecas2str(sa_args.src, sa_args.dst,
				sa_args.satype, sa_args.spi, sa_args.mode));
		}
		plog(LLV_DEBUG, LOCATION, NULL,
			"backuped SA: %s\n",
			sadbsecas2str(sa_args.src, sa_args.dst,
			sa_args.satype, sa_args.spi, sa_args.mode));
	}

	racoon_free(sa_args.src);
	racoon_free(sa_args.dst);
	return 0;
}

static int
pk_recvupdate(mhp)
	caddr_t *mhp;
{
	struct sadb_msg *msg;
	struct sadb_sa *sa;
	struct sockaddr *src, *dst;
	struct ph2handle *iph2;
	u_int proto_id, encmode, sa_mode;
	int incomplete = 0;
	struct saproto *pr;

	/* ignore this message because of local test mode. */
	if (f_local)
		return 0;

	/* sanity check */
	if (mhp[0] == NULL
	 || mhp[SADB_EXT_SA] == NULL
	 || mhp[SADB_EXT_ADDRESS_SRC] == NULL
	 || mhp[SADB_EXT_ADDRESS_DST] == NULL) {
		plog(LLV_ERROR, LOCATION, NULL,
			"inappropriate sadb update message passed.\n");
		return -1;
	}
	msg = (struct sadb_msg *)mhp[0];
	pk_fixup_sa_addresses(mhp);
	src = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]);
	dst = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]);
	sa = (struct sadb_sa *)mhp[SADB_EXT_SA];

	sa_mode = mhp[SADB_X_EXT_SA2] == NULL
		? IPSEC_MODE_ANY
		: ((struct sadb_x_sa2 *)mhp[SADB_X_EXT_SA2])->sadb_x_sa2_mode;

	/* the message has to be processed or not ? */
	if (msg->sadb_msg_pid != getpid()) {
		plog(LLV_DEBUG, LOCATION, NULL,
			"%s message is not interesting "
			"because pid %d is not mine.\n",
			s_pfkey_type(msg->sadb_msg_type),
			msg->sadb_msg_pid);
		return -1;
	}

	iph2 = getph2byseq(msg->sadb_msg_seq);
	if (iph2 == NULL) {
		plog(LLV_DEBUG, LOCATION, NULL,
			"seq %d of %s message not interesting.\n",
			msg->sadb_msg_seq,
			s_pfkey_type(msg->sadb_msg_type));
		return -1;
	}

	if (iph2->status != PHASE2ST_ADDSA) {
		plog(LLV_ERROR, LOCATION, NULL,
			"status mismatch (db:%d msg:%d)\n",
			iph2->status, PHASE2ST_ADDSA);
		return -1;
	}

	/* check to complete all keys ? */
	for (pr = iph2->approval->head; pr != NULL; pr = pr->next) {
		proto_id = pfkey2ipsecdoi_proto(msg->sadb_msg_satype);
		if (proto_id == ~0) {
			plog(LLV_ERROR, LOCATION, NULL,
				"invalid proto_id %d\n", msg->sadb_msg_satype);
			return -1;
		}
		encmode = pfkey2ipsecdoi_mode(sa_mode);
		if (encmode == ~0) {
			plog(LLV_ERROR, LOCATION, NULL,
				"invalid encmode %d\n", sa_mode);
			return -1;
		}

		if (pr->proto_id == proto_id
		 && pr->spi == sa->sadb_sa_spi) {
			pr->ok = 1;
			plog(LLV_DEBUG, LOCATION, NULL,
				"pfkey UPDATE succeeded: %s\n",
				sadbsecas2str(dst, src,
				    msg->sadb_msg_satype,
				    sa->sadb_sa_spi,
				    sa_mode));

			plog(LLV_INFO, LOCATION, NULL,
				"IPsec-SA established: %s\n",
				sadbsecas2str(dst, src,
					msg->sadb_msg_satype, sa->sadb_sa_spi,
					sa_mode));
		}

		if (pr->ok == 0)
			incomplete = 1;
	}

	if (incomplete)
		return 0;

	/* turn off the timer for calling pfkey_timeover() */
	sched_cancel(&iph2->sce);

	/* update status */
	iph2->status = PHASE2ST_ESTABLISHED;
	evt_phase2(iph2, EVT_PHASE2_UP, NULL);

#ifdef ENABLE_STATS
	gettimeofday(&iph2->end, NULL);
	syslog(LOG_NOTICE, "%s(%s): %8.6f",
		"phase2", "quick", timedelta(&iph2->start, &iph2->end));
#endif

	/* turn off schedule */
	sched_cancel(&iph2->scr);

	/*
	 * since we are going to reuse the phase2 handler, we need to
	 * remain it and refresh all the references between ph1 and ph2 to use.
	 */
	sched_schedule(&iph2->sce, iph2->approval->lifetime,
		       isakmp_ph2expire_stub);

	plog(LLV_DEBUG, LOCATION, NULL, "===\n");
	return 0;
}

/*
 * set outbound SA
 */
int
pk_sendadd(iph2)
	struct ph2handle *iph2;
{
	struct saproto *pr;
	struct pfkey_send_sa_args sa_args;

	/* sanity check */
	if (iph2->approval == NULL) {
		plog(LLV_ERROR, LOCATION, NULL,
			"no approvaled SAs found.\n");
		return -1;
	}

	/* fill in some needed for pfkey_send_update2 */
	memset (&sa_args, 0, sizeof (sa_args));
	sa_args.so = lcconf->sock_pfkey;
	if (iph2->lifetime_secs)
		sa_args.l_addtime = iph2->lifetime_secs;
	else
		sa_args.l_addtime = iph2->approval->lifetime;
	sa_args.seq = iph2->seq;
	sa_args.wsize = 4;

	if (iph2->sa_src && iph2->sa_dst) {
		/* MIPv6: Use SA addresses, not IKE ones */
		sa_args.src = dupsaddr(iph2->sa_src);
		sa_args.dst = dupsaddr(iph2->sa_dst);
	} else {
		/* Common case: SA addresses and IKE ones are the same */
		sa_args.src = dupsaddr(iph2->src);
		sa_args.dst = dupsaddr(iph2->dst);
	}

	if (sa_args.src == NULL || sa_args.dst == NULL) {
		racoon_free(sa_args.src);
		racoon_free(sa_args.dst);
		return -1;
	}

	for (pr = iph2->approval->head; pr != NULL; pr = pr->next) {
		/* validity check */
		sa_args.satype = ipsecdoi2pfkey_proto(pr->proto_id);
		if (sa_args.satype == ~0) {
			plog(LLV_ERROR, LOCATION, NULL,
				"invalid proto_id %d\n", pr->proto_id);
			racoon_free(sa_args.src);
			racoon_free(sa_args.dst);
			return -1;
		}
		else if (sa_args.satype == SADB_X_SATYPE_IPCOMP) {
			/* no replay window for IPCOMP */
			sa_args.wsize = 0;
		}
#ifdef ENABLE_SAMODE_UNSPECIFIED
		sa_args.mode = IPSEC_MODE_ANY;
#else
		sa_args.mode = ipsecdoi2pfkey_mode(pr->encmode);
		if (sa_args.mode == ~0) {
			plog(LLV_ERROR, LOCATION, NULL,
				"invalid encmode %d\n", pr->encmode);
			racoon_free(sa_args.src);
			racoon_free(sa_args.dst);
			return -1;
		}
#endif

		/* set algorithm type and key length */
		sa_args.e_keylen = pr->head->encklen;
		if (pfkey_convertfromipsecdoi(
				pr->proto_id,
				pr->head->trns_id,
				pr->head->authtype,
				&sa_args.e_type, &sa_args.e_keylen,
				&sa_args.a_type, &sa_args.a_keylen,
				&sa_args.flags) < 0){
			racoon_free(sa_args.src);
			racoon_free(sa_args.dst);
			return -1;
		}

#if 0
		sa_args.l_bytes = iph2->approval->lifebyte * 1024,
#else
		sa_args.l_bytes = 0;
#endif

#ifdef HAVE_SECCTX
		if (*iph2->approval->sctx.ctx_str) {
			sa_args.ctxdoi = iph2->approval->sctx.ctx_doi;
			sa_args.ctxalg = iph2->approval->sctx.ctx_alg;
			sa_args.ctxstrlen = iph2->approval->sctx.ctx_strlen;
			sa_args.ctxstr = iph2->approval->sctx.ctx_str;
		}
#endif /* HAVE_SECCTX */

#ifdef ENABLE_NATT
		plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_add2 "
		    "(NAT flavor)\n");

		if (pr->udp_encap) {
			sa_args.l_natt_type = UDP_ENCAP_ESPINUDP;
			sa_args.l_natt_sport = extract_port(iph2->ph1->local);
			sa_args.l_natt_dport = extract_port(iph2->ph1->remote);
#ifdef SADB_X_EXT_NAT_T_FRAG
			sa_args.l_natt_frag = iph2->ph1->rmconf->esp_frag;
#endif
		}
#endif
		/* more info to fill in */
		sa_args.spi = pr->spi_p;
		sa_args.reqid = pr->reqid_out;
		sa_args.keymat = pr->keymat_p->v;

		plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_add2\n");
		if (pfkey_send_add2(&sa_args) < 0) {
			plog(LLV_ERROR, LOCATION, NULL,
				"libipsec failed send add (%s)\n",
				ipsec_strerror());
			racoon_free(sa_args.src);
			racoon_free(sa_args.dst);
			return -1;
		}

		if (!lcconf->pathinfo[LC_PATHTYPE_BACKUPSA])
			continue;

		/*
		 * It maybe good idea to call backupsa_to_file() after
		 * racoon will receive the sadb_update messages.
		 * But it is impossible because there is not key in the
		 * information from the kernel.
		 */
		if (backupsa_to_file(&sa_args) < 0) {
			plog(LLV_ERROR, LOCATION, NULL,
				"backuped SA failed: %s\n",
				sadbsecas2str(sa_args.src, sa_args.dst,
				sa_args.satype, sa_args.spi, sa_args.mode));
		}
		plog(LLV_DEBUG, LOCATION, NULL,
			"backuped SA: %s\n",
			sadbsecas2str(sa_args.src, sa_args.dst,
			sa_args.satype, sa_args.spi, sa_args.mode));
	}
	racoon_free(sa_args.src);
	racoon_free(sa_args.dst);
	return 0;
}

static int
pk_recvadd(mhp)
	caddr_t *mhp;
{
	struct sadb_msg *msg;
	struct sadb_sa *sa;
	struct sockaddr *src, *dst;
	struct ph2handle *iph2;
	u_int sa_mode;

	/* ignore this message because of local test mode. */
	if (f_local)
		return 0;

	/* sanity check */
	if (mhp[0] == NULL
	 || mhp[SADB_EXT_SA] == NULL
	 || mhp[SADB_EXT_ADDRESS_SRC] == NULL
	 || mhp[SADB_EXT_ADDRESS_DST] == NULL) {
		plog(LLV_ERROR, LOCATION, NULL,
			"inappropriate sadb add message passed.\n");
		return -1;
	}
	msg = (struct sadb_msg *)mhp[0];
	pk_fixup_sa_addresses(mhp);
	src = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]);
	dst = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]);
	sa = (struct sadb_sa *)mhp[SADB_EXT_SA];

	sa_mode = mhp[SADB_X_EXT_SA2] == NULL
		? IPSEC_MODE_ANY
		: ((struct sadb_x_sa2 *)mhp[SADB_X_EXT_SA2])->sadb_x_sa2_mode;

	/* the message has to be processed or not ? */
	if (msg->sadb_msg_pid != getpid()) {
		plog(LLV_DEBUG, LOCATION, NULL,
			"%s message is not interesting "
			"because pid %d is not mine.\n",
			s_pfkey_type(msg->sadb_msg_type),
			msg->sadb_msg_pid);
		return -1;
	}

	iph2 = getph2byseq(msg->sadb_msg_seq);
	if (iph2 == NULL) {
		plog(LLV_DEBUG, LOCATION, NULL,
			"seq %d of %s message not interesting.\n",
			msg->sadb_msg_seq,
			s_pfkey_type(msg->sadb_msg_type));
		return -1;
	}

	/*
	 * NOTE don't update any status of phase2 handle
	 * because they must be updated by SADB_UPDATE message
	 */

	plog(LLV_INFO, LOCATION, NULL,
		"IPsec-SA established: %s\n",
		sadbsecas2str(src, dst,
			msg->sadb_msg_satype, sa->sadb_sa_spi, sa_mode));

	plog(LLV_DEBUG, LOCATION, NULL, "===\n");
	return 0;
}

static int
pk_recvexpire(mhp)
	caddr_t *mhp;
{
	struct sadb_msg *msg;
	struct sadb_sa *sa;
	struct sockaddr *src, *dst;
	struct ph2handle *iph2;
	u_int proto_id, sa_mode;

	/* sanity check */
	if (mhp[0] == NULL
	 || mhp[SADB_EXT_SA] == NULL
	 || mhp[SADB_EXT_ADDRESS_SRC] == NULL
	 || mhp[SADB_EXT_ADDRESS_DST] == NULL
	 || (mhp[SADB_EXT_LIFETIME_HARD] != NULL
	  && mhp[SADB_EXT_LIFETIME_SOFT] != NULL)) {
		plog(LLV_ERROR, LOCATION, NULL,
			"inappropriate sadb expire message passed.\n");
		return -1;
	}
	msg = (struct sadb_msg *)mhp[0];
	sa = (struct sadb_sa *)mhp[SADB_EXT_SA];
	pk_fixup_sa_addresses(mhp);
	src = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]);
	dst = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]);

	sa_mode = mhp[SADB_X_EXT_SA2] == NULL
		? IPSEC_MODE_ANY
		: ((struct sadb_x_sa2 *)mhp[SADB_X_EXT_SA2])->sadb_x_sa2_mode;

	proto_id = pfkey2ipsecdoi_proto(msg->sadb_msg_satype);
	if (proto_id == ~0) {
		plog(LLV_ERROR, LOCATION, NULL,
			"invalid proto_id %d\n", msg->sadb_msg_satype);
		return -1;
	}

	plog(LLV_INFO, LOCATION, NULL,
		"IPsec-SA expired: %s\n",
		sadbsecas2str(src, dst,
			msg->sadb_msg_satype, sa->sadb_sa_spi, sa_mode));

	iph2 = getph2bysaidx(src, dst, proto_id, sa->sadb_sa_spi);
	if (iph2 == NULL) {
		/*
		 * Ignore it because two expire messages are come up.
		 * phase2 handler has been deleted already when 2nd message
		 * is received.
		 */
		plog(LLV_DEBUG, LOCATION, NULL,
			"no such a SA found: %s\n",
			sadbsecas2str(src, dst,
			    msg->sadb_msg_satype, sa->sadb_sa_spi,
			    sa_mode));
		return 0;
	}

	/* resent expiry message? */
	if (iph2->status > PHASE2ST_ESTABLISHED)
		return 0;

	/* still negotiating? */
	if (iph2->status < PHASE2ST_ESTABLISHED) {
		/* not a hard timeout? */
		if (mhp[SADB_EXT_LIFETIME_HARD] == NULL)
			return 0;

		/*
		 * We were negotiating for that SA (w/o much success
		 * from current status) and kernel has decided our time
		 * is over trying (xfrm_larval_drop controls that and
		 * is enabled by default on Linux >= 2.6.28 kernels).
		 */
		plog(LLV_WARNING, LOCATION, NULL,
		     "PF_KEY EXPIRE message received from kernel for SA"
		     " being negotiated. Stopping negotiation.\n");
	}

	/* turn off the timer for calling isakmp_ph2expire() */
	sched_cancel(&iph2->sce);

	if (iph2->status == PHASE2ST_ESTABLISHED &&
	    iph2->side == INITIATOR) {
		struct ph1handle *iph1hint;
		/*
		 * Active phase 2 expired and we were initiator.
		 * Begin new phase 2 exchange, so we can keep on sending
		 * traffic.
		 */

		/* update status for re-use */
		iph1hint = iph2->ph1;
		initph2(iph2);
		iph2->status = PHASE2ST_STATUS2;

		/* start quick exchange */
		if (isakmp_post_acquire(iph2, iph1hint, FALSE) < 0) {
			plog(LLV_ERROR, LOCATION, iph2->dst,
				"failed to begin ipsec sa "
				"re-negotication.\n");
			remph2(iph2);
			delph2(iph2);
			return -1;
		}

		return 0;
	}

	/*
	 * We are responder or the phase 2 was not established.
	 * Just remove the ph2handle to reflect SADB.
	 */
	iph2->status = PHASE2ST_EXPIRED;
	remph2(iph2);
	delph2(iph2);

	return 0;
}

static int
pk_recvacquire(mhp)
	caddr_t *mhp;
{
	struct sadb_msg *msg;
	struct sadb_x_policy *xpl;
	struct secpolicy *sp_out = NULL, *sp_in = NULL;
	struct ph2handle *iph2;
	struct sockaddr *src, *dst;     /* IKE addresses (for exchanges) */
	struct sockaddr *sp_src, *sp_dst;   /* SP addresses (selectors). */
	struct sockaddr *sa_src = NULL, *sa_dst = NULL ; /* SA addresses */
#ifdef HAVE_SECCTX
	struct sadb_x_sec_ctx *m_sec_ctx;
#endif /* HAVE_SECCTX */
	struct policyindex spidx;

	/* ignore this message because of local test mode. */
	if (f_local)
		return 0;

	/* sanity check */
	if (mhp[0] == NULL
	 || mhp[SADB_EXT_ADDRESS_SRC] == NULL
	 || mhp[SADB_EXT_ADDRESS_DST] == NULL
	 || mhp[SADB_X_EXT_POLICY] == NULL) {
		plog(LLV_ERROR, LOCATION, NULL,
			"inappropriate sadb acquire message passed.\n");
		return -1;
	}
	msg = (struct sadb_msg *)mhp[0];
	xpl = (struct sadb_x_policy *)mhp[SADB_X_EXT_POLICY];
	/* acquire does not have nat-t ports; so do not bother setting
	 * the default port 500; just use the port zero for wildcard
	 * matching the get a valid natted destination */
	sp_src = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]);
	sp_dst = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]);

#ifdef HAVE_SECCTX
	m_sec_ctx = (struct sadb_x_sec_ctx *)mhp[SADB_X_EXT_SEC_CTX];

	if (m_sec_ctx != NULL) {
		plog(LLV_INFO, LOCATION, NULL, "security context doi: %u\n",
		     m_sec_ctx->sadb_x_ctx_doi);
		plog(LLV_INFO, LOCATION, NULL,
		     "security context algorithm: %u\n",
		     m_sec_ctx->sadb_x_ctx_alg);
		plog(LLV_INFO, LOCATION, NULL, "security context length: %u\n",
		     m_sec_ctx->sadb_x_ctx_len);
		plog(LLV_INFO, LOCATION, NULL, "security context: %s\n",
		     ((char *)m_sec_ctx + sizeof(struct sadb_x_sec_ctx)));
	}
#endif /* HAVE_SECCTX */

	/* ignore if type is not IPSEC_POLICY_IPSEC */
	if (xpl->sadb_x_policy_type != IPSEC_POLICY_IPSEC) {
		plog(LLV_DEBUG, LOCATION, NULL,
			"ignore ACQUIRE message. type is not IPsec.\n");
		return 0;
	}

	/* ignore it if src or dst are multicast addresses. */
	if ((sp_dst->sa_family == AF_INET
	  && IN_MULTICAST(ntohl(((struct sockaddr_in *)sp_dst)->sin_addr.s_addr)))
#ifdef INET6
	 || (sp_dst->sa_family == AF_INET6
	  && IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6 *)sp_dst)->sin6_addr))
#endif
	) {
		plog(LLV_DEBUG, LOCATION, NULL,
			"ignore due to multicast destination address: %s.\n",
			saddrwop2str(sp_dst));
		return 0;
	}

	if ((sp_src->sa_family == AF_INET
	  && IN_MULTICAST(ntohl(((struct sockaddr_in *)sp_src)->sin_addr.s_addr)))
#ifdef INET6
	 || (sp_src->sa_family == AF_INET6
	  && IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6 *)sp_src)->sin6_addr))
#endif
	) {
		plog(LLV_DEBUG, LOCATION, NULL,
			"ignore due to multicast source address: %s.\n",
			saddrwop2str(sp_src));
		return 0;
	}

	/* search for proper policyindex */
	sp_out = getspbyspid(xpl->sadb_x_policy_id);
	if (sp_out == NULL) {
		plog(LLV_ERROR, LOCATION, NULL, "no policy found: id:%d.\n",
			xpl->sadb_x_policy_id);
		return -1;
	}
	plog(LLV_DEBUG, LOCATION, NULL,
		"suitable outbound SP found: %s.\n", spidx2str(&sp_out->spidx));

	/* Before going further, let first get the source and destination
	 * address that would be used for IKE negotiation. The logic is:
	 * - if SP from SPD image contains local and remote hints, we
	 *   use them (provided by MIGRATE).
	 * - otherwise, we use the ones from the ipsecrequest, which means:
	 *   - the addresses from the request for transport mode
	 *   - the endpoints addresses for tunnel mode
	 *
	 * Note that:
	 * 1) racoon does not support negotiation of bundles which
	 *    simplifies the lookup for the addresses in the ipsecrequest
	 *    list, as we expect only one.
	 * 2) We do source and destination parts all together and do not
	 *    accept semi-defined information. This is just a decision,
	 *    there might be needs.
	 *
	 * --arno
	 */
	if (sp_out->req && sp_out->req->saidx.mode == IPSEC_MODE_TUNNEL) {
		/* For Tunnel mode, SA addresses are the endpoints */
		src = (struct sockaddr *) &sp_out->req->saidx.src;
		dst = (struct sockaddr *) &sp_out->req->saidx.dst;
	} else {
		/* Otherwise use requested addresses.
		 *
		 * We need to explicitly setup sa_src and sa_dst too,
		 * since the SA ports are different from IKE port. And
		 * src/dst ports will be overwritten when the matching
		 * phase1 is found. */
		src = sa_src = sp_src;
		dst = sa_dst = sp_dst;
	}
	if (sp_out->local && sp_out->remote) {
		/* hints available, let's use them */
		sa_src = src;
		sa_dst = dst;
		src = (struct sockaddr *) sp_out->local;
		dst = (struct sockaddr *) sp_out->remote;
	}

	/*
	 * If there is a phase 2 handler against the policy identifier in
	 * the acquire message, and if
	 *    1. its state is less than PHASE2ST_ESTABLISHED, then racoon
	 *       should ignore such a acquire message because the phase 2
	 *       is just negotiating.
	 *    2. its state is equal to PHASE2ST_ESTABLISHED, then racoon
	 *       has to prcesss such a acquire message because racoon may
	 *       lost the expire message.
	 */
	iph2 = getph2byid(src, dst, xpl->sadb_x_policy_id);
	if (iph2 != NULL) {
		if (iph2->status < PHASE2ST_ESTABLISHED) {
			plog(LLV_DEBUG, LOCATION, NULL,
				"ignore the acquire because ph2 found\n");
			return -1;
		}
		if (iph2->status == PHASE2ST_EXPIRED)
			iph2 = NULL;
		/*FALLTHROUGH*/
	}

	/* Check we are listening on source address. If not, ignore. */
	if (myaddr_getsport(src) == -1) {
		plog(LLV_DEBUG, LOCATION, NULL,
		     "Not listening on source address %s. Ignoring ACQUIRE.\n",
		     saddrwop2str(src));
		return 0;
	}

	/* get inbound policy */
    {

	memset(&spidx, 0, sizeof(spidx));
	spidx.dir = IPSEC_DIR_INBOUND;
	memcpy(&spidx.src, &sp_out->spidx.dst, sizeof(spidx.src));
	memcpy(&spidx.dst, &sp_out->spidx.src, sizeof(spidx.dst));
	spidx.prefs = sp_out->spidx.prefd;
	spidx.prefd = sp_out->spidx.prefs;
	spidx.ul_proto = sp_out->spidx.ul_proto;

#ifdef HAVE_SECCTX
	if (m_sec_ctx) {
		spidx.sec_ctx.ctx_doi = m_sec_ctx->sadb_x_ctx_doi;
		spidx.sec_ctx.ctx_alg = m_sec_ctx->sadb_x_ctx_alg;
		spidx.sec_ctx.ctx_strlen = m_sec_ctx->sadb_x_ctx_len;
		memcpy(spidx.sec_ctx.ctx_str,
		      ((char *)m_sec_ctx + sizeof(struct sadb_x_sec_ctx)),
		      spidx.sec_ctx.ctx_strlen);
	}
#endif /* HAVE_SECCTX */

	sp_in = getsp(&spidx);
	if (sp_in) {
		plog(LLV_DEBUG, LOCATION, NULL,
			"suitable inbound SP found: %s.\n",
			spidx2str(&sp_in->spidx));
	} else {
		plog(LLV_NOTIFY, LOCATION, NULL,
			"no in-bound policy found: %s\n",
			spidx2str(&spidx));
	}
    }

	/* allocate a phase 2 */
	iph2 = newph2();
	if (iph2 == NULL) {
		plog(LLV_ERROR, LOCATION, NULL,
			"failed to allocate phase2 entry.\n");
		return -1;
	}
	iph2->side = INITIATOR;
	iph2->spid = xpl->sadb_x_policy_id;
	iph2->satype = msg->sadb_msg_satype;
	iph2->seq = msg->sadb_msg_seq;
	iph2->status = PHASE2ST_STATUS2;

	/* set address used by IKE for the negotiation (might differ from
	 * SA address, i.e. might not be tunnel endpoints or addresses
	 * of transport mode SA) */
	iph2->dst = dupsaddr(dst);
	if (iph2->dst == NULL) {
		delph2(iph2);
		return -1;
	}
	iph2->src = dupsaddr(src);
	if (iph2->src == NULL) {
		delph2(iph2);
		return -1;
	}

	/* If sa_src and sa_dst have been set, this mean we have to
	 * set iph2->sa_src and iph2->sa_dst to provide the addresses
	 * of the SA because iph2->src and iph2->dst are only the ones
	 * used for the IKE exchanges. Those that need these addresses
	 * are for instance pk_sendupdate() or pk_sendgetspi() */
	if (sa_src) {
		iph2->sa_src = dupsaddr(sa_src);
		iph2->sa_dst = dupsaddr(sa_dst);
	}

	if (isakmp_get_sainfo(iph2, sp_out, sp_in) < 0) {
		delph2(iph2);
		return -1;
	}

#ifdef HAVE_SECCTX
	if (m_sec_ctx) {
		set_secctx_in_proposal(iph2, spidx);
	}
#endif /* HAVE_SECCTX */

	insph2(iph2);

	/* start isakmp initiation by using ident exchange */
	/* XXX should be looped if there are multiple phase 2 handler. */
	if (isakmp_post_acquire(iph2, NULL, TRUE) < 0) {
		plog(LLV_ERROR, LOCATION, NULL,
			"failed to begin ipsec sa negotication.\n");
		remph2(iph2);
		delph2(iph2);
		return -1;
	}

	return 0;
}

static int
pk_recvdelete(mhp)
	caddr_t *mhp;
{
	struct sadb_msg *msg;
	struct sadb_sa *sa;
	struct sockaddr *src, *dst;
	struct ph2handle *iph2 = NULL;
	u_int proto_id;

	/* ignore this message because of local test mode. */
	if (f_local)
		return 0;

	/* sanity check */
	if (mhp[0] == NULL
	 || mhp[SADB_EXT_SA] == NULL
	 || mhp[SADB_EXT_ADDRESS_SRC] == NULL
	 || mhp[SADB_EXT_ADDRESS_DST] == NULL) {
		plog(LLV_ERROR, LOCATION, NULL,
			"inappropriate sadb delete message passed.\n");
		return -1;
	}
	msg = (struct sadb_msg *)mhp[0];
	sa = (struct sadb_sa *)mhp[SADB_EXT_SA];
	pk_fixup_sa_addresses(mhp);
	src = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]);
	dst = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]);

	/* the message has to be processed or not ? */
	if (msg->sadb_msg_pid == getpid()) {
		plog(LLV_DEBUG, LOCATION, NULL,
			"%s message is not interesting "
			"because the message was originated by me.\n",
			s_pfkey_type(msg->sadb_msg_type));
		return -1;
	}

	proto_id = pfkey2ipsecdoi_proto(msg->sadb_msg_satype);
	if (proto_id == ~0) {
		plog(LLV_ERROR, LOCATION, NULL,
			"invalid proto_id %d\n", msg->sadb_msg_satype);
		return -1;
	}

	iph2 = getph2bysaidx(src, dst, proto_id, sa->sadb_sa_spi);
	if (iph2 == NULL) {
		/* ignore */
		plog(LLV_ERROR, LOCATION, NULL,
			"no iph2 found: %s\n",
			sadbsecas2str(src, dst, msg->sadb_msg_satype,
				sa->sadb_sa_spi, IPSEC_MODE_ANY));
		return 0;
	}

	plog(LLV_ERROR, LOCATION, NULL,
		"pfkey DELETE received: %s\n",
		sadbsecas2str(src, dst,
			msg->sadb_msg_satype, sa->sadb_sa_spi, IPSEC_MODE_ANY));

	/* send delete information */
	if (iph2->status == PHASE2ST_ESTABLISHED)
		isakmp_info_send_d2(iph2);

	remph2(iph2);
	delph2(iph2);

	return 0;
}

static int
pk_recvflush(mhp)
	caddr_t *mhp;
{
	/* ignore this message because of local test mode. */
	if (f_local)
		return 0;

	/* sanity check */
	if (mhp[0] == NULL) {
		plog(LLV_ERROR, LOCATION, NULL,
			"inappropriate sadb flush message passed.\n");
		return -1;
	}

	flushph2();

	return 0;
}

static int
getsadbpolicy(policy0, policylen0, type, iph2)
	caddr_t *policy0;
	int *policylen0, type;
	struct ph2handle *iph2;
{
	struct policyindex *spidx = (struct policyindex *)iph2->spidx_gen;
	struct sockaddr *src = NULL, *dst = NULL;
	struct sadb_x_policy *xpl;
	struct sadb_x_ipsecrequest *xisr;
	struct saproto *pr;
	struct saproto **pr_rlist;
	int rlist_len = 0;
	caddr_t policy, p;
	int policylen;
	int xisrlen;
	u_int satype, mode;
	int len = 0;
#ifdef HAVE_SECCTX
	int ctxlen = 0;
#endif /* HAVE_SECCTX */


	/* get policy buffer size */
	policylen = sizeof(struct sadb_x_policy);
	if (type != SADB_X_SPDDELETE) {
		if (iph2->sa_src && iph2->sa_dst) {
			src = iph2->sa_src; /* MIPv6: Use SA addresses, */
			dst = iph2->sa_dst; /* not IKE ones             */
		} else {
			src = iph2->src; /* Common case: SA addresses */
			dst = iph2->dst; /* and IKE ones are the same */
		}

		for (pr = iph2->approval->head; pr; pr = pr->next) {
			xisrlen = sizeof(*xisr);
			if (pr->encmode == IPSECDOI_ATTR_ENC_MODE_TUNNEL) {
				xisrlen += (sysdep_sa_len(src) +
					    sysdep_sa_len(dst));
			}

			policylen += PFKEY_ALIGN8(xisrlen);
		}
	}

#ifdef HAVE_SECCTX
	if (*spidx->sec_ctx.ctx_str) {
		ctxlen = sizeof(struct sadb_x_sec_ctx)
				+ PFKEY_ALIGN8(spidx->sec_ctx.ctx_strlen);
		policylen += ctxlen;
	}
#endif /* HAVE_SECCTX */

	/* make policy structure */
	policy = racoon_malloc(policylen);
	memset((void*)policy, 0xcd, policylen);
	if (!policy) {
		plog(LLV_ERROR, LOCATION, NULL,
			"buffer allocation failed.\n");
		return -1;
	}

	xpl = (struct sadb_x_policy *)policy;
	xpl->sadb_x_policy_len = PFKEY_UNIT64(policylen);
	xpl->sadb_x_policy_exttype = SADB_X_EXT_POLICY;
	xpl->sadb_x_policy_type = IPSEC_POLICY_IPSEC;
	xpl->sadb_x_policy_dir = spidx->dir;
	xpl->sadb_x_policy_id = 0;
#ifdef HAVE_PFKEY_POLICY_PRIORITY
	xpl->sadb_x_policy_priority = PRIORITY_DEFAULT;
#endif
	len++;

#ifdef HAVE_SECCTX
	if (*spidx->sec_ctx.ctx_str) {
		struct sadb_x_sec_ctx *p;

		p = (struct sadb_x_sec_ctx *)(xpl + len);
		memset(p, 0, ctxlen);
		p->sadb_x_sec_len = PFKEY_UNIT64(ctxlen);
		p->sadb_x_sec_exttype = SADB_X_EXT_SEC_CTX;
		p->sadb_x_ctx_len = spidx->sec_ctx.ctx_strlen;
		p->sadb_x_ctx_doi = spidx->sec_ctx.ctx_doi;
		p->sadb_x_ctx_alg = spidx->sec_ctx.ctx_alg;

		memcpy(p + 1,spidx->sec_ctx.ctx_str,spidx->sec_ctx.ctx_strlen);
		len += ctxlen;
	}
#endif /* HAVE_SECCTX */

	/* no need to append policy information any more if type is SPDDELETE */
	if (type == SADB_X_SPDDELETE)
		goto end;

	xisr = (struct sadb_x_ipsecrequest *)(xpl + len);

	/* The order of things is reversed for use in add policy messages */
	for (pr = iph2->approval->head; pr; pr = pr->next) rlist_len++;
	pr_rlist = racoon_malloc((rlist_len+1)*sizeof(struct saproto*));
	if (!pr_rlist) {
		plog(LLV_ERROR, LOCATION, NULL,
			"buffer allocation failed.\n");
		return -1;
	}
	pr_rlist[rlist_len--] = NULL;
	for (pr = iph2->approval->head; pr; pr = pr->next) pr_rlist[rlist_len--] = pr;
	rlist_len = 0;

	for (pr = pr_rlist[rlist_len++]; pr; pr = pr_rlist[rlist_len++]) {

		satype = doi2ipproto(pr->proto_id);
		if (satype == ~0) {
			plog(LLV_ERROR, LOCATION, NULL,
				"invalid proto_id %d\n", pr->proto_id);
			goto err;
		}
		mode = ipsecdoi2pfkey_mode(pr->encmode);
		if (mode == ~0) {
			plog(LLV_ERROR, LOCATION, NULL,
				"invalid encmode %d\n", pr->encmode);
			goto err;
		}

		/*
		 * the policy level cannot be unique because the policy
		 * is defined later than SA, so req_id cannot be bound to SA.
		 */
		xisr->sadb_x_ipsecrequest_proto = satype;
		xisr->sadb_x_ipsecrequest_mode = mode;
		if(iph2->proposal->head->reqid_in > 0){
			xisr->sadb_x_ipsecrequest_level = IPSEC_LEVEL_UNIQUE;
			xisr->sadb_x_ipsecrequest_reqid = iph2->proposal->head->reqid_in;
		}else{
			xisr->sadb_x_ipsecrequest_level = IPSEC_LEVEL_REQUIRE;
			xisr->sadb_x_ipsecrequest_reqid = 0;
		}
		p = (caddr_t)(xisr + 1);

		xisrlen = sizeof(*xisr);

		if (pr->encmode == IPSECDOI_ATTR_ENC_MODE_TUNNEL) {
			int src_len, dst_len;

			src_len = sysdep_sa_len(src);
			dst_len = sysdep_sa_len(dst);
			xisrlen += src_len + dst_len;

			memcpy(p, src, src_len);
			p += src_len;

			memcpy(p, dst, dst_len);
			p += dst_len;
		}

		xisr->sadb_x_ipsecrequest_len = PFKEY_ALIGN8(xisrlen);
		xisr = (struct sadb_x_ipsecrequest *)p;

	}
	racoon_free(pr_rlist);

end:
	*policy0 = policy;
	*policylen0 = policylen;

	return 0;

err:
	if (policy)
		racoon_free(policy);
	if (pr_rlist) racoon_free(pr_rlist);

	return -1;
}

int
pk_sendspdupdate2(iph2)
	struct ph2handle *iph2;
{
	struct policyindex *spidx = (struct policyindex *)iph2->spidx_gen;
	caddr_t policy = NULL;
	int policylen = 0;
	u_int64_t ltime, vtime;

	ltime = iph2->approval->lifetime;
	vtime = 0;

	if (getsadbpolicy(&policy, &policylen, SADB_X_SPDUPDATE, iph2)) {
		plog(LLV_ERROR, LOCATION, NULL,
			"getting sadb policy failed.\n");
		return -1;
	}

	if (pfkey_send_spdupdate2(
			lcconf->sock_pfkey,
			(struct sockaddr *)&spidx->src,
			spidx->prefs,
			(struct sockaddr *)&spidx->dst,
			spidx->prefd,
			spidx->ul_proto,
			ltime, vtime,
			policy, policylen, 0) < 0) {
		plog(LLV_ERROR, LOCATION, NULL,
			"libipsec failed send spdupdate2 (%s)\n",
			ipsec_strerror());
		goto end;
	}
	plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_spdupdate2\n");

end:
	if (policy)
		racoon_free(policy);

	return 0;
}

static int
pk_recvspdupdate(mhp)
	caddr_t *mhp;
{
	struct sadb_address *saddr, *daddr;
	struct sadb_x_policy *xpl;
	struct sadb_lifetime *lt;
	struct policyindex spidx;
	struct secpolicy *sp;
	struct sockaddr *local=NULL, *remote=NULL;
	u_int64_t created;
	int ret;

	/* sanity check */
	if (mhp[0] == NULL
	 || mhp[SADB_EXT_ADDRESS_SRC] == NULL
	 || mhp[SADB_EXT_ADDRESS_DST] == NULL
	 || mhp[SADB_X_EXT_POLICY] == NULL) {
		plog(LLV_ERROR, LOCATION, NULL,
			"inappropriate sadb spdupdate message passed.\n");
		return -1;
	}
	saddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_SRC];
	daddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_DST];
	xpl = (struct sadb_x_policy *)mhp[SADB_X_EXT_POLICY];
	lt = (struct sadb_lifetime*)mhp[SADB_EXT_LIFETIME_HARD];
	if(lt != NULL)
		created = lt->sadb_lifetime_addtime;
	else
		created = 0;

#ifdef HAVE_PFKEY_POLICY_PRIORITY
	KEY_SETSECSPIDX(xpl->sadb_x_policy_dir,
			saddr + 1,
			daddr + 1,
			saddr->sadb_address_prefixlen,
			daddr->sadb_address_prefixlen,
			saddr->sadb_address_proto,
			xpl->sadb_x_policy_priority,
			created,
			&spidx);
#else
	KEY_SETSECSPIDX(xpl->sadb_x_policy_dir,
			saddr + 1,
			daddr + 1,
			saddr->sadb_address_prefixlen,
			daddr->sadb_address_prefixlen,
			saddr->sadb_address_proto,
			created,
			&spidx);
#endif

#ifdef HAVE_SECCTX
	if (mhp[SADB_X_EXT_SEC_CTX] != NULL) {
		struct sadb_x_sec_ctx *ctx;

		ctx = (struct sadb_x_sec_ctx *)mhp[SADB_X_EXT_SEC_CTX];
		spidx.sec_ctx.ctx_alg = ctx->sadb_x_ctx_alg;
		spidx.sec_ctx.ctx_doi = ctx->sadb_x_ctx_doi;
		spidx.sec_ctx.ctx_strlen = ctx->sadb_x_ctx_len;
		memcpy(spidx.sec_ctx.ctx_str, ctx + 1, ctx->sadb_x_ctx_len);
	}
#endif /* HAVE_SECCTX */

	sp = getsp(&spidx);
	if (sp == NULL) {
		plog(LLV_DEBUG, LOCATION, NULL,
			"this policy did not exist for removal: \"%s\"\n",
			spidx2str(&spidx));
	} else {
		/* preserve hints before deleting the SP */
		local = sp->local;
		remote = sp->remote;
		sp->local = NULL;
		sp->remote = NULL;

		remsp(sp);
		delsp(sp);
	}

	/* Add new SP (with old hints) */
	ret = addnewsp(mhp, local, remote);

	if (local != NULL)
		racoon_free(local);
	if (remote != NULL)
		racoon_free(remote);

	if (ret < 0)
		return -1;

	return 0;
}

/*
 * this function has to be used by responder side.
 */
int
pk_sendspdadd2(iph2)
	struct ph2handle *iph2;
{
	struct policyindex *spidx = (struct policyindex *)iph2->spidx_gen;
	caddr_t policy = NULL;
	int policylen = 0;
	u_int64_t ltime, vtime;

	ltime = iph2->approval->lifetime;
	vtime = 0;

	if (getsadbpolicy(&policy, &policylen, SADB_X_SPDADD, iph2)) {
		plog(LLV_ERROR, LOCATION, NULL,
			"getting sadb policy failed.\n");
		return -1;
	}

	if (pfkey_send_spdadd2(
			lcconf->sock_pfkey,
			(struct sockaddr *)&spidx->src,
			spidx->prefs,
			(struct sockaddr *)&spidx->dst,
			spidx->prefd,
			spidx->ul_proto,
			ltime, vtime,
			policy, policylen, 0) < 0) {
		plog(LLV_ERROR, LOCATION, NULL,
			"libipsec failed send spdadd2 (%s)\n",
			ipsec_strerror());
		goto end;
	}
	plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_spdadd2\n");

end:
	if (policy)
		racoon_free(policy);

	return 0;
}

static int
pk_recvspdadd(mhp)
	caddr_t *mhp;
{
	struct sadb_address *saddr, *daddr;
	struct sadb_x_policy *xpl;
	struct sadb_lifetime *lt;
	struct policyindex spidx;
	struct secpolicy *sp;
	struct sockaddr *local = NULL, *remote = NULL;
	u_int64_t created;
	int ret;

	/* sanity check */
	if (mhp[0] == NULL
	 || mhp[SADB_EXT_ADDRESS_SRC] == NULL
	 || mhp[SADB_EXT_ADDRESS_DST] == NULL
	 || mhp[SADB_X_EXT_POLICY] == NULL) {
		plog(LLV_ERROR, LOCATION, NULL,
			"inappropriate sadb spdadd message passed.\n");
		return -1;
	}
	saddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_SRC];
	daddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_DST];
	xpl = (struct sadb_x_policy *)mhp[SADB_X_EXT_POLICY];
	lt = (struct sadb_lifetime*)mhp[SADB_EXT_LIFETIME_HARD];
	if(lt != NULL)
		created = lt->sadb_lifetime_addtime;
	else
		created = 0;

#ifdef HAVE_PFKEY_POLICY_PRIORITY
	KEY_SETSECSPIDX(xpl->sadb_x_policy_dir,
			saddr + 1,
			daddr + 1,
			saddr->sadb_address_prefixlen,
			daddr->sadb_address_prefixlen,
			saddr->sadb_address_proto,
			xpl->sadb_x_policy_priority,
			created,
			&spidx);
#else
	KEY_SETSECSPIDX(xpl->sadb_x_policy_dir,
			saddr + 1,
			daddr + 1,
			saddr->sadb_address_prefixlen,
			daddr->sadb_address_prefixlen,
			saddr->sadb_address_proto,
			created,
			&spidx);
#endif

#ifdef HAVE_SECCTX
	if (mhp[SADB_X_EXT_SEC_CTX] != NULL) {
		struct sadb_x_sec_ctx *ctx;

		ctx = (struct sadb_x_sec_ctx *)mhp[SADB_X_EXT_SEC_CTX];
		spidx.sec_ctx.ctx_alg = ctx->sadb_x_ctx_alg;
		spidx.sec_ctx.ctx_doi = ctx->sadb_x_ctx_doi;
		spidx.sec_ctx.ctx_strlen = ctx->sadb_x_ctx_len;
		memcpy(spidx.sec_ctx.ctx_str, ctx + 1, ctx->sadb_x_ctx_len);
	}
#endif /* HAVE_SECCTX */

	sp = getsp(&spidx);
	if (sp != NULL) {
		plog(LLV_ERROR, LOCATION, NULL,
			"such policy already exists. "
			"anyway replace it: %s\n",
			spidx2str(&spidx));

		/* preserve hints before deleting the SP */
		local = sp->local;
		remote = sp->remote;
		sp->local = NULL;
		sp->remote = NULL;

		remsp(sp);
		delsp(sp);
	}

	/* Add new SP (with old hints) */
	ret = addnewsp(mhp, local, remote);

	if (local != NULL)
		racoon_free(local);
	if (remote != NULL)
		racoon_free(remote);

	if (ret < 0)
		return -1;

	return 0;
}

/*
 * this function has to be used by responder side.
 */
int
pk_sendspddelete(iph2)
	struct ph2handle *iph2;
{
	struct policyindex *spidx = (struct policyindex *)iph2->spidx_gen;
	caddr_t policy = NULL;
	int policylen;

	if (getsadbpolicy(&policy, &policylen, SADB_X_SPDDELETE, iph2)) {
		plog(LLV_ERROR, LOCATION, NULL,
			"getting sadb policy failed.\n");
		return -1;
	}

	if (pfkey_send_spddelete(
			lcconf->sock_pfkey,
			(struct sockaddr *)&spidx->src,
			spidx->prefs,
			(struct sockaddr *)&spidx->dst,
			spidx->prefd,
			spidx->ul_proto,
			policy, policylen, 0) < 0) {
		plog(LLV_ERROR, LOCATION, NULL,
			"libipsec failed send spddelete (%s)\n",
			ipsec_strerror());
		goto end;
	}
	plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_spddelete\n");

end:
	if (policy)
		racoon_free(policy);

	return 0;
}

static int
pk_recvspddelete(mhp)
	caddr_t *mhp;
{
	struct sadb_address *saddr, *daddr;
	struct sadb_x_policy *xpl;
	struct sadb_lifetime *lt;
	struct policyindex spidx;
	struct secpolicy *sp;
	u_int64_t created;

	/* sanity check */
	if (mhp[0] == NULL
	 || mhp[SADB_EXT_ADDRESS_SRC] == NULL
	 || mhp[SADB_EXT_ADDRESS_DST] == NULL
	 || mhp[SADB_X_EXT_POLICY] == NULL) {
		plog(LLV_ERROR, LOCATION, NULL,
			"inappropriate sadb spddelete message passed.\n");
		return -1;
	}
	saddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_SRC];
	daddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_DST];
	xpl = (struct sadb_x_policy *)mhp[SADB_X_EXT_POLICY];
	lt = (struct sadb_lifetime*)mhp[SADB_EXT_LIFETIME_HARD];
	if(lt != NULL)
		created = lt->sadb_lifetime_addtime;
	else
		created = 0;

#ifdef HAVE_PFKEY_POLICY_PRIORITY
	KEY_SETSECSPIDX(xpl->sadb_x_policy_dir,
			saddr + 1,
			daddr + 1,
			saddr->sadb_address_prefixlen,
			daddr->sadb_address_prefixlen,
			saddr->sadb_address_proto,
			xpl->sadb_x_policy_priority,
			created,
			&spidx);
#else
	KEY_SETSECSPIDX(xpl->sadb_x_policy_dir,
			saddr + 1,
			daddr + 1,
			saddr->sadb_address_prefixlen,
			daddr->sadb_address_prefixlen,
			saddr->sadb_address_proto,
			created,
			&spidx);
#endif

#ifdef HAVE_SECCTX
	if (mhp[SADB_X_EXT_SEC_CTX] != NULL) {
		struct sadb_x_sec_ctx *ctx;

		ctx = (struct sadb_x_sec_ctx *)mhp[SADB_X_EXT_SEC_CTX];
		spidx.sec_ctx.ctx_alg = ctx->sadb_x_ctx_alg;
		spidx.sec_ctx.ctx_doi = ctx->sadb_x_ctx_doi;
		spidx.sec_ctx.ctx_strlen = ctx->sadb_x_ctx_len;
		memcpy(spidx.sec_ctx.ctx_str, ctx + 1, ctx->sadb_x_ctx_len);
	}
#endif /* HAVE_SECCTX */

	sp = getsp(&spidx);
	if (sp == NULL) {
		plog(LLV_ERROR, LOCATION, NULL,
			"no policy found: %s\n",
			spidx2str(&spidx));
		return -1;
	}

	remsp(sp);
	delsp(sp);

	return 0;
}

static int
pk_recvspdexpire(mhp)
	caddr_t *mhp;
{
	struct sadb_address *saddr, *daddr;
	struct sadb_x_policy *xpl;
	struct sadb_lifetime *lt;
	struct policyindex spidx;
	struct secpolicy *sp;
	u_int64_t created;

	/* sanity check */
	if (mhp[0] == NULL
	 || mhp[SADB_EXT_ADDRESS_SRC] == NULL
	 || mhp[SADB_EXT_ADDRESS_DST] == NULL
	 || mhp[SADB_X_EXT_POLICY] == NULL) {
		plog(LLV_ERROR, LOCATION, NULL,
			"inappropriate sadb spdexpire message passed.\n");
		return -1;
	}
	saddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_SRC];
	daddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_DST];
	xpl = (struct sadb_x_policy *)mhp[SADB_X_EXT_POLICY];
	lt = (struct sadb_lifetime*)mhp[SADB_EXT_LIFETIME_HARD];
	if(lt != NULL)
		created = lt->sadb_lifetime_addtime;
	else
		created = 0;

#ifdef HAVE_PFKEY_POLICY_PRIORITY
	KEY_SETSECSPIDX(xpl->sadb_x_policy_dir,
			saddr + 1,
			daddr + 1,
			saddr->sadb_address_prefixlen,
			daddr->sadb_address_prefixlen,
			saddr->sadb_address_proto,
			xpl->sadb_x_policy_priority,
			created,
			&spidx);
#else
	KEY_SETSECSPIDX(xpl->sadb_x_policy_dir,
			saddr + 1,
			daddr + 1,
			saddr->sadb_address_prefixlen,
			daddr->sadb_address_prefixlen,
			saddr->sadb_address_proto,
			created,
			&spidx);
#endif

#ifdef HAVE_SECCTX
	if (mhp[SADB_X_EXT_SEC_CTX] != NULL) {
		struct sadb_x_sec_ctx *ctx;

		ctx = (struct sadb_x_sec_ctx *)mhp[SADB_X_EXT_SEC_CTX];
		spidx.sec_ctx.ctx_alg = ctx->sadb_x_ctx_alg;
		spidx.sec_ctx.ctx_doi = ctx->sadb_x_ctx_doi;
		spidx.sec_ctx.ctx_strlen = ctx->sadb_x_ctx_len;
		memcpy(spidx.sec_ctx.ctx_str, ctx + 1, ctx->sadb_x_ctx_len);
	}
#endif /* HAVE_SECCTX */

	sp = getsp(&spidx);
	if (sp == NULL) {
		plog(LLV_ERROR, LOCATION, NULL,
			"no policy found: %s\n",
			spidx2str(&spidx));
		return -1;
	}

	remsp(sp);
	delsp(sp);

	return 0;
}

static int
pk_recvspdget(mhp)
	caddr_t *mhp;
{
	/* sanity check */
	if (mhp[0] == NULL) {
		plog(LLV_ERROR, LOCATION, NULL,
			"inappropriate sadb spdget message passed.\n");
		return -1;
	}

	return 0;
}

static int
pk_recvspddump(mhp)
	caddr_t *mhp;
{
	struct sadb_msg *msg;
	struct sadb_address *saddr, *daddr;
	struct sadb_x_policy *xpl;
	struct sadb_lifetime *lt;
	struct policyindex spidx;
	struct secpolicy *sp;
	struct sockaddr *local=NULL, *remote=NULL;
	u_int64_t created;
	int ret;

	/* sanity check */
	if (mhp[0] == NULL) {
		plog(LLV_ERROR, LOCATION, NULL,
			"inappropriate sadb spddump message passed.\n");
		return -1;
	}
	msg = (struct sadb_msg *)mhp[0];
	saddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_SRC];
	daddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_DST];
	xpl = (struct sadb_x_policy *)mhp[SADB_X_EXT_POLICY];
	lt = (struct sadb_lifetime*)mhp[SADB_EXT_LIFETIME_HARD];
	if(lt != NULL)
		created = lt->sadb_lifetime_addtime;
	else
		created = 0;

	if (saddr == NULL || daddr == NULL || xpl == NULL) {
		plog(LLV_ERROR, LOCATION, NULL,
			"inappropriate sadb spddump message passed.\n");
		return -1;
	}

#ifdef HAVE_PFKEY_POLICY_PRIORITY
	KEY_SETSECSPIDX(xpl->sadb_x_policy_dir,
			saddr + 1,
			daddr + 1,
			saddr->sadb_address_prefixlen,
			daddr->sadb_address_prefixlen,
			saddr->sadb_address_proto,
			xpl->sadb_x_policy_priority,
			created,
			&spidx);
#else
	KEY_SETSECSPIDX(xpl->sadb_x_policy_dir,
			saddr + 1,
			daddr + 1,
			saddr->sadb_address_prefixlen,
			daddr->sadb_address_prefixlen,
			saddr->sadb_address_proto,
			created,
			&spidx);
#endif

#ifdef HAVE_SECCTX
	if (mhp[SADB_X_EXT_SEC_CTX] != NULL) {
		struct sadb_x_sec_ctx *ctx;

		ctx = (struct sadb_x_sec_ctx *)mhp[SADB_X_EXT_SEC_CTX];
		spidx.sec_ctx.ctx_alg = ctx->sadb_x_ctx_alg;
		spidx.sec_ctx.ctx_doi = ctx->sadb_x_ctx_doi;
		spidx.sec_ctx.ctx_strlen = ctx->sadb_x_ctx_len;
		memcpy(spidx.sec_ctx.ctx_str, ctx + 1, ctx->sadb_x_ctx_len);
	}
#endif /* HAVE_SECCTX */

	sp = getsp(&spidx);
	if (sp != NULL) {
		plog(LLV_ERROR, LOCATION, NULL,
			"such policy already exists. "
			"anyway replace it: %s\n",
			spidx2str(&spidx));

		/* preserve hints before deleting the SP */
		local = sp->local;
		remote = sp->remote;
		sp->local = NULL;
		sp->remote = NULL;

		remsp(sp);
		delsp(sp);
	}

	/* Add new SP (with old hints) */
	ret = addnewsp(mhp, local, remote);

	if (local != NULL)
		racoon_free(local);
	if (remote != NULL)
		racoon_free(remote);

	if (ret < 0)
		return -1;

	return 0;
}

static int
pk_recvspdflush(mhp)
	caddr_t *mhp;
{
	/* sanity check */
	if (mhp[0] == NULL) {
		plog(LLV_ERROR, LOCATION, NULL,
			"inappropriate sadb spdflush message passed.\n");
		return -1;
	}

	flushsp();

	return 0;
}

#if defined(SADB_X_MIGRATE) && defined(SADB_X_EXT_KMADDRESS)

/* MIGRATE support (pk_recvmigrate() is the handler of MIGRATE message).
 *
 * pk_recvmigrate()
 *   1) some preprocessing and checks
 *   2) parsing of sadb_x_kmaddress extension
 *   3) SP lookup using selectors and content of policy extension from MIGRATE
 *   4) resolution of current local and remote IKE addresses
 *   5) Use of addresses to get Phase 1 handler if any
 *   6) Update of IKE addresses in Phase 1 (iph1->local and iph1->remote)
 *   7) Update of IKE addresses in Phase 2 (iph2->src and iph2->dst)
 *   8) Update of IKE addresses in SP (sp->local and sp->remote)
 *   9) Loop on sadb_x_ipsecrequests pairs from MIGRATE
 *      - update of associated ipsecrequests entries in sp->req (should be
 *        only one as racoon does not support bundles), i.e. update of
 *        tunnel endpoints when required.
 *      - If tunnel mode endpoints have been updated, lookup of associated
 *        Phase 2 handle to also update sa_src and sa_dst entries
 *
 * XXX Note that we do not support yet the update of SA addresses for transport
 *     mode, but only the update of SA addresses for tunnel mode (endpoints).
 *     Reasons are:
 *      - there is no initial need for MIPv6
 *      - racoon does not support bundles
 *      - this would imply more work to deal with sainfo update (if feasible).
 */

/* Generic argument structure for migration callbacks */
struct migrate_args {
	struct sockaddr *local;
	struct sockaddr *remote;
};

/*
 * Update local and remote addresses of given Phase 1. Schedule removal
 * if negotiation was going on and restart a one from updated address.
 *
 * -1 is returned on error. 0 if everything went right.
 */
static int
migrate_ph1_ike_addresses(iph1, arg)
        struct ph1handle *iph1;
        void *arg;
{
	struct migrate_args *ma = (struct migrate_args *) arg;
	struct remoteconf *rmconf;
	u_int16_t port;

	/* Already up-to-date? */
	if (cmpsaddr(iph1->local, ma->local) == CMPSADDR_MATCH &&
	    cmpsaddr(iph1->remote, ma->remote) == CMPSADDR_MATCH)
		return 0;

	if (iph1->status < PHASE1ST_ESTABLISHED) {
		/* Bad luck! We received a MIGRATE *while* negotiating
		 * Phase 1 (i.e. it was not established yet). If we act as
		 * initiator we need to restart the negotiation. As
		 * responder, our best bet is to update our addresses
		 * and wait for the initiator to do something */
		plog(LLV_WARNING, LOCATION, NULL, "MIGRATE received *during* "
		     "Phase 1 negotiation (%s).\n",
		     saddr2str_fromto("%s => %s", ma->local, ma->remote));

		/* If we are not acting as initiator, let's just leave and
		 * let the remote peer handle the restart */
		rmconf = getrmconf(ma->remote, 0);
		if (rmconf == NULL || !rmconf->passive) {
			iph1->status = PHASE1ST_EXPIRED;
			isakmp_ph1delete(iph1);

			/* This is unlikely, but let's just check if a Phase 1
			 * for the new addresses already exist */
			if (getph1byaddr(ma->local, ma->remote, 0)) {
				plog(LLV_WARNING, LOCATION, NULL, "No need "
				     "to start a new Phase 1 negotiation. One "
				     "already exists.\n");
				return 0;
			}

			plog(LLV_WARNING, LOCATION, NULL, "As initiator, "
			     "restarting it.\n");
			 /* Note that the insertion of the new Phase 1 will not
			  * interfere with the fact we are called from enumph1,
			  * because it is inserted as first element. --arno */
			isakmp_ph1begin_i(rmconf, ma->local, ma->remote);

			return 0;
		}
	}

	if (iph1->local != NULL) {
		plog(LLV_DEBUG, LOCATION, NULL, "Migrating Phase 1 local "
		     "address from %s\n",
		     saddr2str_fromto("%s to %s", iph1->local, ma->local));
		port = extract_port(iph1->local);
		racoon_free(iph1->local);
	} else
		port = 0;

	iph1->local = dupsaddr(ma->local);
	if (iph1->local == NULL) {
		plog(LLV_ERROR, LOCATION, NULL, "unable to allocate "
		     "Phase 1 local address.\n");
		return -1;
	}
	set_port(iph1->local, port);

	if (iph1->remote != NULL) {
		plog(LLV_DEBUG, LOCATION, NULL, "Migrating Phase 1 remote "
		     "address from %s\n",
		     saddr2str_fromto("%s to %s", iph1->remote, ma->remote));
		port = extract_port(iph1->remote);
		racoon_free(iph1->remote);
	} else
		port = 0;

	iph1->remote = dupsaddr(ma->remote);
	if (iph1->remote == NULL) {
		plog(LLV_ERROR, LOCATION, NULL, "unable to allocate "
		     "Phase 1 remote address.\n");
		return -1;
	}
	set_port(iph1->remote, port);

	return 0;
}

/* Update src and dst of all current Phase 2 handles.
 * with provided local and remote addresses.
 * Our intent is NOT to modify IPsec SA endpoints but IKE
 * addresses so we need to take care to separate those if
 * they are different. -1 is returned on error. 0 if everything
 * went right.
 *
 * Note: we do not maintain port information as it is not
 *       expected to be meaningful --arno
 */
static int
migrate_ph2_ike_addresses(iph2, arg)
	struct ph2handle *iph2;
	void *arg;
{
	struct migrate_args *ma = (struct migrate_args *) arg;
	struct ph1handle *iph1;

	/* If Phase 2 has an associated Phase 1, migrate addresses */
	if (iph2->ph1)
		migrate_ph1_ike_addresses(iph2->ph1, arg);

	/* Already up-to-date? */
	if (cmpsaddr(iph2->src, ma->local) == CMPSADDR_MATCH &&
	    cmpsaddr(iph2->dst, ma->remote) == CMPSADDR_MATCH)
		return 0;

	/* save src/dst as sa_src/sa_dst before rewriting */
	if (iph2->sa_src == NULL && iph2->sa_dst == NULL) {
		iph2->sa_src = iph2->src;
		iph2->sa_dst = iph2->dst;
		iph2->src = NULL;
		iph2->dst = NULL;
	}

	if (iph2->src != NULL)
		racoon_free(iph2->src);
	iph2->src = dupsaddr(ma->local);
	if (iph2->src == NULL) {
		plog(LLV_ERROR, LOCATION, NULL,
		     "unable to allocate Phase 2 src address.\n");
		return -1;
	}

	if (iph2->dst != NULL)
		racoon_free(iph2->dst);
	iph2->dst = dupsaddr(ma->remote);
	if (iph2->dst == NULL) {
		plog(LLV_ERROR, LOCATION, NULL,
		     "unable to allocate Phase 2 dst address.\n");
		return -1;
	}

	return 0;
}

/* Consider existing Phase 2 handles with given spid and update their source
 * and destination addresses for SA. As racoon does not support bundles, if
 * we modify multiple occurrences, this probably imply rekeying has happened.
 *
 * Both addresses passed to the function are expected not to be NULL and of
 * same family. -1 is returned on error. 0 if everything went right.
 *
 * Specific care is needed to support Phase 2 for which negotiation has
 * already started but are which not yet established.
 */
static int
migrate_ph2_sa_addresses(iph2, args)
	struct ph2handle *iph2;
	void *args;
{
	struct migrate_args *ma = (struct migrate_args *) args;

	if (iph2->sa_src != NULL) {
		racoon_free(iph2->sa_src);
		iph2->sa_src = NULL;
	}

	if (iph2->sa_dst != NULL) {
		racoon_free(iph2->sa_dst);
		iph2->sa_dst = NULL;
	}

	iph2->sa_src = dupsaddr(ma->local);
	if (iph2->sa_src == NULL) {
		plog(LLV_ERROR, LOCATION, NULL,
		     "unable to allocate Phase 2 sa_src address.\n");
		return -1;
	}

	iph2->sa_dst = dupsaddr(ma->remote);
	if (iph2->sa_dst == NULL) {
		plog(LLV_ERROR, LOCATION, NULL,
		     "unable to allocate Phase 2 sa_dst address.\n");
		return -1;
	}

	if (iph2->status < PHASE2ST_ESTABLISHED) {
		struct remoteconf *rmconf;
		/* We were negotiating for that SA when we received the MIGRATE.
		 * We cannot simply update the addresses and let the exchange
		 * go on. We have to restart the whole negotiation if we are
		 * the initiator. Otherwise (acting as responder), we just need
		 * to delete our ph2handle and wait for the initiator to start
		 * a new negotiation. */

		if (iph2->ph1 && iph2->ph1->rmconf)
			rmconf = iph2->ph1->rmconf;
		else
			rmconf = getrmconf(iph2->dst, 0);

		if (rmconf && !rmconf->passive) {
			struct ph1handle *iph1hint;

			plog(LLV_WARNING, LOCATION, iph2->dst, "MIGRATE received "
			     "*during* IPsec SA negotiation. As initiator, "
			     "restarting it.\n");

			/* Turn off expiration timer ...*/
			sched_cancel(&iph2->sce);
			iph2->status = PHASE2ST_EXPIRED;

			/* ... clean Phase 2 handle ... */
			iph1hint = iph2->ph1;
			initph2(iph2);
			iph2->status = PHASE2ST_STATUS2;

			/* and start a new negotiation */
			if (isakmp_post_acquire(iph2, iph1hint, FALSE) < 0) {
				plog(LLV_ERROR, LOCATION, iph2->dst, "failed "
				     "to begin IPsec SA renegotiation after "
				     "MIGRATE reception.\n");
				remph2(iph2);
				delph2(iph2);
				return -1;
			}
		} else {
			plog(LLV_WARNING, LOCATION, iph2->dst, "MIGRATE received "
			     "*during* IPsec SA negotiation. As responder, let's"
			     "wait for the initiator to act.\n");

			/* Simply schedule deletion */
			isakmp_ph2expire(iph2);
		}
	}

	return 0;
}

/* Update SP hints (local and remote addresses) for future IKE
 * negotiations of SA associated with that SP. -1 is returned
 * on error. 0 if everything went right.
 *
 * Note: we do not maintain port information as it is not
 *       expected to be meaningful --arno
 */
static int
migrate_sp_ike_addresses(sp, local, remote)
        struct secpolicy *sp;
        struct sockaddr *local, *remote;
{
	if (sp == NULL || local == NULL || remote == NULL)
		return -1;

	if (sp->local != NULL)
		racoon_free(sp->local);

	sp->local = dupsaddr(local);
	if (sp->local == NULL) {
		plog(LLV_ERROR, LOCATION, NULL, "unable to allocate "
		     "local hint for SP.\n");
		return -1;
	}

	if (sp->remote != NULL)
		racoon_free(sp->remote);

	sp->remote = dupsaddr(remote);
	if (sp->remote == NULL) {
		plog(LLV_ERROR, LOCATION, NULL, "unable to allocate "
		     "remote hint for SP.\n");
		return -1;
	}

	return 0;
}

/* Given current ipsecrequest (isr_cur) to be migrated in considered
   tree, the function first checks that it matches the expected one
   (xisr_old) provided in MIGRATE message and then updates the addresses
   if it is tunnel mode (with content of xisr_new). Various other checks
   are performed. For transport mode, structures are not modified, only
   the checks are done. -1 is returned on error. */
static int
migrate_ph2_one_isr(spid, isr_cur, xisr_old, xisr_new)
        u_int32_t spid;
        struct ipsecrequest *isr_cur;
	struct sadb_x_ipsecrequest *xisr_old, *xisr_new;
{
	struct secasindex *saidx = &isr_cur->saidx;
	struct sockaddr *osaddr, *odaddr, *nsaddr, *ndaddr;
	struct ph2selector ph2sel;
	struct migrate_args ma;

	/* First, check that mode and proto do match */
	if (xisr_old->sadb_x_ipsecrequest_proto != saidx->proto ||
	    xisr_old->sadb_x_ipsecrequest_mode != saidx->mode ||
	    xisr_new->sadb_x_ipsecrequest_proto != saidx->proto ||
	    xisr_new->sadb_x_ipsecrequest_mode != saidx->mode)
		return -1;

	/* Then, verify reqid if necessary */
	if (isr_cur->saidx.reqid &&
	    (xisr_old->sadb_x_ipsecrequest_reqid != IPSEC_LEVEL_UNIQUE ||
	     xisr_new->sadb_x_ipsecrequest_reqid != IPSEC_LEVEL_UNIQUE ||
	     isr_cur->saidx.reqid != xisr_old->sadb_x_ipsecrequest_reqid ||
	     isr_cur->saidx.reqid != xisr_new->sadb_x_ipsecrequest_reqid))
		return -1;

	/* If not tunnel mode, our work is over */
	if (saidx->mode != IPSEC_MODE_TUNNEL) {
		plog(LLV_DEBUG, LOCATION, NULL, "SADB_X_MIGRATE: "
		     "non tunnel mode isr, skipping SA address migration.\n");
		return 0;
	}

	/* Tunnel mode: let's check addresses do match and then update them. */
	osaddr = (struct sockaddr *)(xisr_old + 1);
	odaddr = (struct sockaddr *)(((u_int8_t *)osaddr) + sysdep_sa_len(osaddr));
	nsaddr = (struct sockaddr *)(xisr_new + 1);
	ndaddr = (struct sockaddr *)(((u_int8_t *)nsaddr) + sysdep_sa_len(nsaddr));

	/* Check family does match */
	if (osaddr->sa_family != odaddr->sa_family ||
	    nsaddr->sa_family != ndaddr->sa_family)
		return -1;

	/* Check family does match */
	if (saidx->src.ss_family != osaddr->sa_family)
		return -1;

	/* We log IPv4 to IPv6 and IPv6 to IPv4 switches */
	if (nsaddr->sa_family != osaddr->sa_family)
		plog(LLV_INFO, LOCATION, NULL, "SADB_X_MIGRATE: "
		     "changing address families (%d to %d) for endpoints.\n",
		     osaddr->sa_family, nsaddr->sa_family);

	if (cmpsaddr(osaddr, (struct sockaddr *) &saidx->src) != CMPSADDR_MATCH ||
	    cmpsaddr(odaddr, (struct sockaddr *) &saidx->dst) != CMPSADDR_MATCH) {
		plog(LLV_DEBUG, LOCATION, NULL, "SADB_X_MIGRATE: "
		     "mismatch of addresses in saidx and xisr.\n");
		return -1;
	}

	/* Excellent. Let's grab associated Phase 2 handle (if any)
	 * and update its sa_src and sa_dst entries.  Note that we
	 * make the assumption that racoon does not support bundles
	 * and make the lookup using spid: we blindly update
	 * sa_src and sa_dst for _all_ found Phase 2 handles */
	memset(&ph2sel, 0, sizeof(ph2sel));
	ph2sel.spid = spid;

	memset(&ma, 0, sizeof(ma));
	ma.local = nsaddr;
	ma.remote = ndaddr;

	if (enumph2(&ph2sel, migrate_ph2_sa_addresses, &ma) < 0)
		return -1;

	/* Now we can do the update of endpoints in secasindex */
	memcpy(&saidx->src, nsaddr, sysdep_sa_len(nsaddr));
	memcpy(&saidx->dst, ndaddr, sysdep_sa_len(ndaddr));

	return 0;
}

/* Process the raw (unparsed yet) list of sadb_x_ipsecrequests of MIGRATE
 * message. For each sadb_x_ipsecrequest pair (old followed by new),
 * the corresponding ipsecrequest entry in the SP is updated. Associated
 * existing Phase 2 handle is also updated (if any) */
static int
migrate_sp_isr_list(sp, xisr_list, xisr_list_len)
        struct secpolicy *sp;
	struct sadb_x_ipsecrequest *xisr_list;
	int xisr_list_len;
{
	struct sadb_x_ipsecrequest *xisr_new, *xisr_old = xisr_list;
	int xisr_old_len, xisr_new_len;
	struct ipsecrequest *isr_cur;

	isr_cur = sp->req; /* ipsecrequest list from from sp */

	while (xisr_list_len > 0 && isr_cur != NULL) {
		/* Get old xisr (length field is in bytes) */
		xisr_old_len = xisr_old->sadb_x_ipsecrequest_len;
		if (xisr_old_len < sizeof(*xisr_old) ||
		    xisr_old_len + sizeof(*xisr_new) > xisr_list_len) {
			plog(LLV_ERROR, LOCATION, NULL, "SADB_X_MIGRATE: "
			     "invalid ipsecrequest length. Exiting.\n");
			return -1;
		}

		/* Get new xisr with updated info */
		xisr_new = (struct sadb_x_ipsecrequest *)(((u_int8_t *)xisr_old) + xisr_old_len);
		xisr_new_len = xisr_new->sadb_x_ipsecrequest_len;
		if (xisr_new_len < sizeof(*xisr_new) ||
		    xisr_new_len + xisr_old_len > xisr_list_len) {
			plog(LLV_ERROR, LOCATION, NULL, "SADB_X_MIGRATE: "
			     "invalid ipsecrequest length. Exiting.\n");
			return -1;
		}

		/* Start by migrating current ipsecrequest from SP */
		if (migrate_ph2_one_isr(sp->id, isr_cur, xisr_old, xisr_new) == -1) {
			plog(LLV_ERROR, LOCATION, NULL, "SADB_X_MIGRATE: "
			     "Unable to match and migrate isr. Exiting.\n");
			return -1;
		}

		/* Update pointers for next round */
		xisr_list_len -= xisr_old_len + xisr_new_len;
		xisr_old = (struct sadb_x_ipsecrequest *)(((u_int8_t *)xisr_new) +
							  xisr_new_len);

		isr_cur = isr_cur->next; /* Get next ipsecrequest from SP */
	}

	/* Check we had the same amount of pairs in the MIGRATE
	   as the number of ipsecrequests in the SP */
	if ((xisr_list_len != 0) || isr_cur != NULL) {
		plog(LLV_ERROR, LOCATION, NULL, "SADB_X_MIGRATE: "
		     "number of ipsecrequest does not match the one in SP.\n");
		return -1;
	}

	return 0;
}

/* Parse sadb_x_kmaddress extension and make local and remote
 * parameters point to the new addresses (zero copy). -1 is
 * returned on error, meaning that addresses are not usable */
static int
parse_kmaddress(kmaddr, local, remote)
        struct sadb_x_kmaddress *kmaddr;
	struct sockaddr **local, **remote;
{
	int addrslen, local_len=0;
	struct ph1handle *iph1;

	if (kmaddr == NULL)
		return -1;

	/* Grab addresses in sadb_x_kmaddress extension */
	addrslen = PFKEY_EXTLEN(kmaddr) - sizeof(*kmaddr);
	if (addrslen < sizeof(struct sockaddr))
		return -1;

	*local = (struct sockaddr *)(kmaddr + 1);

	switch ((*local)->sa_family) {
	case AF_INET:
		local_len = sizeof(struct sockaddr_in);
		break;
#ifdef INET6
	case AF_INET6:
		local_len = sizeof(struct sockaddr_in6);
		break;
#endif
	default:
		return -1;
	}

	if (addrslen != PFKEY_ALIGN8(2*local_len))
		return -1;

	*remote = (struct sockaddr *)(((u_int8_t *)(*local)) + local_len);

	if ((*local)->sa_family != (*remote)->sa_family)
		return -1;

	return 0;
}

/* Handler of PF_KEY MIGRATE message. Helpers are above */
static int
pk_recvmigrate(mhp)
	caddr_t *mhp;
{
	struct sadb_address *saddr, *daddr;
	struct sockaddr *old_saddr, *new_saddr;
	struct sockaddr *old_daddr, *new_daddr;
	struct sockaddr *old_local, *old_remote;
	struct sockaddr *local, *remote;
	struct sadb_x_kmaddress *kmaddr;
	struct sadb_x_policy *xpl;
	struct sadb_x_ipsecrequest *xisr_list;
	struct sadb_lifetime *lt;
	struct policyindex spidx;
	struct secpolicy *sp;
	struct ipsecrequest *isr_cur;
	struct secasindex *oldsaidx;
	struct ph2handle *iph2;
	struct ph1handle *iph1;
	struct ph2selector ph2sel;
	struct ph1selector ph1sel;
	u_int32_t spid;
	u_int64_t created;
	int xisr_list_len;
	int ulproto;
	struct migrate_args ma;

	/* Some sanity checks */

	if (mhp[0] == NULL
	 || mhp[SADB_EXT_ADDRESS_SRC] == NULL
	 || mhp[SADB_EXT_ADDRESS_DST] == NULL
	 || mhp[SADB_X_EXT_KMADDRESS] == NULL
	 || mhp[SADB_X_EXT_POLICY] == NULL) {
		plog(LLV_ERROR, LOCATION, NULL,
			"SADB_X_MIGRATE: invalid MIGRATE message received.\n");
		return -1;
	}
	kmaddr = (struct sadb_x_kmaddress *)mhp[SADB_X_EXT_KMADDRESS];
	saddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_SRC];
	daddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_DST];
	xpl = (struct sadb_x_policy *)mhp[SADB_X_EXT_POLICY];
	lt = (struct sadb_lifetime*)mhp[SADB_EXT_LIFETIME_HARD];
	if (lt != NULL)
		created = lt->sadb_lifetime_addtime;
	else
		created = 0;

	if (xpl->sadb_x_policy_type != IPSEC_POLICY_IPSEC) {
		plog(LLV_WARNING, LOCATION, NULL,"SADB_X_MIGRATE: "
		     "found non IPsec policy in MIGRATE message. Exiting.\n");
		return -1;
	}

	if (PFKEY_EXTLEN(xpl) < sizeof(*xpl)) {
		plog(LLV_ERROR, LOCATION, NULL, "SADB_X_MIGRATE: "
		     "invalid size for sadb_x_policy. Exiting.\n");
		return -1;
	}

	/* Some logging to help debbugging */
	if (xpl->sadb_x_policy_dir == IPSEC_DIR_OUTBOUND)
		plog(LLV_DEBUG, LOCATION, NULL,
		     "SADB_X_MIGRATE: Outbound SA being migrated.\n");
	else
		plog(LLV_DEBUG, LOCATION, NULL,
		     "SADB_X_MIGRATE: Inbound SA being migrated.\n");

	/* validity check */
	xisr_list = (struct sadb_x_ipsecrequest *)(xpl + 1);
	xisr_list_len = PFKEY_EXTLEN(xpl) - sizeof(*xpl);
	if (xisr_list_len < sizeof(*xisr_list)) {
		plog(LLV_ERROR, LOCATION, NULL, "SADB_X_MIGRATE: "
		     "invalid sadb_x_policy message length. Exiting.\n");
		return -1;
	}

	if (parse_kmaddress(kmaddr, &local, &remote) == -1) {
		plog(LLV_ERROR, LOCATION, NULL, "SADB_X_MIGRATE: "
		     "invalid sadb_x_kmaddress extension. Exiting.\n");
		return -1;
	}

	/* 0 means ANY */
	if (saddr->sadb_address_proto == 0)
		ulproto = IPSEC_ULPROTO_ANY;
	else
		ulproto = saddr->sadb_address_proto;

#ifdef HAVE_PFKEY_POLICY_PRIORITY
	KEY_SETSECSPIDX(xpl->sadb_x_policy_dir,
			saddr + 1,
			daddr + 1,
			saddr->sadb_address_prefixlen,
			daddr->sadb_address_prefixlen,
			ulproto,
			xpl->sadb_x_policy_priority,
			created,
			&spidx);
#else
	KEY_SETSECSPIDX(xpl->sadb_x_policy_dir,
			saddr + 1,
			daddr + 1,
			saddr->sadb_address_prefixlen,
			daddr->sadb_address_prefixlen,
			ulproto,
			created,
			&spidx);
#endif

	/* Everything seems ok, let's get the SP.
	 *
	 * XXX We could also do the lookup using the spid from xpl.
	 *     I don't know which one is better.  --arno */
	sp = getsp(&spidx);
	if (sp == NULL) {
		plog(LLV_ERROR, LOCATION, NULL,
			"SADB_X_MIGRATE: Passed policy does not exist: %s\n",
			spidx2str(&spidx));
		return -1;
	}

	/* Get the best source and destination addresses used for IKE
	 * negotiation, to find and migrate existing Phase 1 */
	if (sp->local && sp->remote) {
		/* hints available, let's use them */
		old_local  = (struct sockaddr *)sp->local;
		old_remote = (struct sockaddr *)sp->remote;
	} else if (sp->req && sp->req->saidx.mode == IPSEC_MODE_TUNNEL) {
		/* Tunnel mode and no hint, use endpoints */
		old_local  = (struct sockaddr *)&sp->req->saidx.src;
		old_remote = (struct sockaddr *)&sp->req->saidx.dst;
	} else {
		/* default, use selectors as fallback */
		old_local  = (struct sockaddr *)&sp->spidx.src;
		old_remote = (struct sockaddr *)&sp->spidx.dst;
	}

	/* We migrate all Phase 1 that match our old local and remote
	 * addresses (no matter their state).
	 *
	 * XXX In fact, we should probably havea special treatment for
	 * Phase 1 that are being established when we receive a MIGRATE.
	 * This can happen if a movement occurs during the initial IKE
	 * negotiation. In that case, I wonder if should restart the
	 * negotiation from the new address or just update things like
	 * we do it now.
	 *
	 * XXX while looking at getph1byaddr(), the comment at the
	 * beginning of the function expects comparison to happen
	 * without ports considerations but it uses CMPSADDR() which
	 * relies either on cmpsaddrstrict() or cmpsaddrwop() based
	 * on NAT-T support being activated. That make me wonder if I
	 * should force ports to 0 (ANY) in local and remote values
	 * used below.
	 *
	 * -- arno */

	/* Apply callback data ...*/
	memset(&ma, 0, sizeof(ma));
	ma.local = local;
	ma.remote = remote;

	/* Fill phase1 match criteria ... */
	memset(&ph1sel, 0, sizeof(ph1sel));
	ph1sel.local = old_local;
	ph1sel.remote = old_remote;


	/* Have matching Phase 1 found and addresses updated. As this is a
	 * time consuming task on a busy responder, and MIGRATE messages
	 * are always sent for *both* inbound and outbound (and possibly
	 * forward), we only do that for outbound SP. */
	if (xpl->sadb_x_policy_dir == IPSEC_DIR_OUTBOUND &&
	    enumph1(&ph1sel, migrate_ph1_ike_addresses, &ma) < 0) {
		plog(LLV_ERROR, LOCATION, NULL, "SADB_X_MIGRATE: Unable "
		     "to migrate Phase 1 addresses.\n");
		return -1;
	}

	/* We can now update IKE addresses in Phase 2 handle. */
	memset(&ph2sel, 0, sizeof(ph2sel));
	ph2sel.spid = sp->id;
	if (enumph2(&ph2sel, migrate_ph2_ike_addresses, &ma) < 0) {
		plog(LLV_ERROR, LOCATION, NULL, "SADB_X_MIGRATE: Unable "
		     "to migrate Phase 2 IKE addresses.\n");
		return -1;
	}

	/* and _then_ in SP. */
	if (migrate_sp_ike_addresses(sp, local, remote) < 0) {
		plog(LLV_ERROR, LOCATION, NULL,
		     "SADB_X_MIGRATE: Unable to migrate SP IKE addresses.\n");
		return -1;
	}

	/* Loop on sadb_x_ipsecrequest list to possibly update sp->req
	 * entries and associated live Phase 2 handles (their sa_src
	 * and sa_dst) */
	if (migrate_sp_isr_list(sp, xisr_list, xisr_list_len) < 0) {
		plog(LLV_ERROR, LOCATION, NULL,
		     "SADB_X_MIGRATE: Unable to migrate isr list.\n");
		return -1;
	}

	return 0;
}
#endif

/*
 * send error against acquire message to kernel.
 */
int
pk_sendeacquire(iph2)
	struct ph2handle *iph2;
{
	struct sadb_msg *newmsg;
	int len;

	len = sizeof(struct sadb_msg);
	newmsg = racoon_calloc(1, len);
	if (newmsg == NULL) {
		plog(LLV_ERROR, LOCATION, NULL,
			"failed to get buffer to send acquire.\n");
		return -1;
	}

	memset(newmsg, 0, len);
	newmsg->sadb_msg_version = PF_KEY_V2;
	newmsg->sadb_msg_type = SADB_ACQUIRE;
	newmsg->sadb_msg_errno = ENOENT;	/* XXX */
	newmsg->sadb_msg_satype = iph2->satype;
	newmsg->sadb_msg_len = PFKEY_UNIT64(len);
	newmsg->sadb_msg_reserved = 0;
	newmsg->sadb_msg_seq = iph2->seq;
	newmsg->sadb_msg_pid = (u_int32_t)getpid();

	/* send message */
	len = pfkey_send(lcconf->sock_pfkey, newmsg, len);

	racoon_free(newmsg);

	return 0;
}

/*
 * check if the algorithm is supported or not.
 * OUT	 0: ok
 *	-1: ng
 */
int
pk_checkalg(class, calg, keylen)
	int class, calg, keylen;
{
	int sup, error;
	u_int alg;
	struct sadb_alg alg0;

	switch (algclass2doi(class)) {
	case IPSECDOI_PROTO_IPSEC_ESP:
		sup = SADB_EXT_SUPPORTED_ENCRYPT;
		break;
	case IPSECDOI_ATTR_AUTH:
		sup = SADB_EXT_SUPPORTED_AUTH;
		break;
	case IPSECDOI_PROTO_IPCOMP:
		plog(LLV_DEBUG, LOCATION, NULL,
			"no check of compression algorithm; "
			"not supported in sadb message.\n");
		return 0;
	default:
		plog(LLV_ERROR, LOCATION, NULL,
			"invalid algorithm class.\n");
		return -1;
	}
	alg = ipsecdoi2pfkey_alg(algclass2doi(class), algtype2doi(class, calg));
	if (alg == ~0)
		return -1;

	if (keylen == 0) {
		if (ipsec_get_keylen(sup, alg, &alg0)) {
			plog(LLV_ERROR, LOCATION, NULL,
				"%s.\n", ipsec_strerror());
			return -1;
		}
		keylen = alg0.sadb_alg_minbits;
	}

	error = ipsec_check_keylen(sup, alg, keylen);
	if (error)
		plog(LLV_ERROR, LOCATION, NULL,
			"%s.\n", ipsec_strerror());

	return error;
}

/*
 * differences with pfkey_recv() in libipsec/pfkey.c:
 * - never performs busy wait loop.
 * - returns NULL and set *lenp to negative on fatal failures
 * - returns NULL and set *lenp to non-negative on non-fatal failures
 * - returns non-NULL on success
 */
static struct sadb_msg *
pk_recv(so, lenp)
	int so;
	int *lenp;
{
	struct sadb_msg buf, *newmsg;
	int reallen;
	int retry = 0;

	*lenp = -1;
	do
	{
	    plog(LLV_DEBUG, LOCATION, NULL, "pk_recv: retry[%d] recv() \n", retry );
	    *lenp = recv(so, (caddr_t)&buf, sizeof(buf), MSG_PEEK | MSG_DONTWAIT);
	    retry++;
	}
	while (*lenp < 0 && errno == EAGAIN && retry < 3);

	if (*lenp < 0)
		return NULL;	/*fatal*/

	else if (*lenp < sizeof(buf))
		return NULL;

	reallen = PFKEY_UNUNIT64(buf.sadb_msg_len);
	if (reallen < sizeof(buf)) {
		*lenp = -1;
		errno = EIO;
		return NULL;    /*fatal*/
	}
	if ((newmsg = racoon_calloc(1, reallen)) == NULL)
		return NULL;

	*lenp = recv(so, (caddr_t)newmsg, reallen, MSG_PEEK);
	if (*lenp < 0) {
		racoon_free(newmsg);
		return NULL;	/*fatal*/
	} else if (*lenp != reallen) {
		racoon_free(newmsg);
		return NULL;
	}

	*lenp = recv(so, (caddr_t)newmsg, reallen, 0);
	if (*lenp < 0) {
		racoon_free(newmsg);
		return NULL;	/*fatal*/
	} else if (*lenp != reallen) {
		racoon_free(newmsg);
		return NULL;
	}

	return newmsg;
}

/* see handler.h */
u_int32_t
pk_getseq()
{
	return eay_random();
}

static int
addnewsp(mhp, local, remote)
	caddr_t *mhp;
	struct sockaddr *local, *remote;
{
	struct secpolicy *new = NULL;
	struct sadb_address *saddr, *daddr;
	struct sadb_x_policy *xpl;
	struct sadb_lifetime *lt;
	u_int64_t created;

	/* sanity check */
	if (mhp[SADB_EXT_ADDRESS_SRC] == NULL
	 || mhp[SADB_EXT_ADDRESS_DST] == NULL
	 || mhp[SADB_X_EXT_POLICY] == NULL) {
		plog(LLV_ERROR, LOCATION, NULL,
			"inappropriate sadb spd management message passed.\n");
		goto bad;
	}

	saddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_SRC];
	daddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_DST];
	xpl = (struct sadb_x_policy *)mhp[SADB_X_EXT_POLICY];
	lt = (struct sadb_lifetime*)mhp[SADB_EXT_LIFETIME_HARD];
	if(lt != NULL)
		created = lt->sadb_lifetime_addtime;
	else
		created = 0;
	lt = (struct sadb_lifetime*)mhp[SADB_EXT_LIFETIME_HARD];
	if(lt != NULL)
		created = lt->sadb_lifetime_addtime;
	else
		created = 0;

#ifdef __linux__
	/* bsd skips over per-socket policies because there will be no
	 * src and dst extensions in spddump messages. On Linux the only
	 * way to achieve the same is check for policy id.
	 */
	if (xpl->sadb_x_policy_id % 8 >= 3) return 0;
#endif

	new = newsp();
	if (new == NULL) {
		plog(LLV_ERROR, LOCATION, NULL,
			"failed to allocate buffer\n");
		goto bad;
	}

	new->spidx.dir = xpl->sadb_x_policy_dir;
	new->id = xpl->sadb_x_policy_id;
	new->policy = xpl->sadb_x_policy_type;
	new->req = NULL;

	/* check policy */
	switch (xpl->sadb_x_policy_type) {
	case IPSEC_POLICY_DISCARD:
	case IPSEC_POLICY_NONE:
	case IPSEC_POLICY_ENTRUST:
	case IPSEC_POLICY_BYPASS:
		break;

	case IPSEC_POLICY_IPSEC:
	    {
		int tlen;
		struct sadb_x_ipsecrequest *xisr;
		struct ipsecrequest **p_isr = &new->req;

		/* validity check */
		if (PFKEY_EXTLEN(xpl) < sizeof(*xpl)) {
			plog(LLV_ERROR, LOCATION, NULL,
				"invalid msg length.\n");
			goto bad;
		}

		tlen = PFKEY_EXTLEN(xpl) - sizeof(*xpl);
		xisr = (struct sadb_x_ipsecrequest *)(xpl + 1);

		while (tlen > 0) {

			/* length check */
			if (xisr->sadb_x_ipsecrequest_len < sizeof(*xisr)) {
				plog(LLV_ERROR, LOCATION, NULL,
					"invalid msg length.\n");
				goto bad;
			}

			/* allocate request buffer */
			*p_isr = newipsecreq();
			if (*p_isr == NULL) {
				plog(LLV_ERROR, LOCATION, NULL,
					"failed to get new ipsecreq.\n");
				goto bad;
			}

			/* set values */
			(*p_isr)->next = NULL;

			switch (xisr->sadb_x_ipsecrequest_proto) {
			case IPPROTO_ESP:
			case IPPROTO_AH:
			case IPPROTO_IPCOMP:
				break;
			default:
				plog(LLV_ERROR, LOCATION, NULL,
					"invalid proto type: %u\n",
					xisr->sadb_x_ipsecrequest_proto);
				goto bad;
			}
			(*p_isr)->saidx.proto = xisr->sadb_x_ipsecrequest_proto;

			switch (xisr->sadb_x_ipsecrequest_mode) {
			case IPSEC_MODE_TRANSPORT:
			case IPSEC_MODE_TUNNEL:
				break;
			case IPSEC_MODE_ANY:
			default:
				plog(LLV_ERROR, LOCATION, NULL,
					"invalid mode: %u\n",
					xisr->sadb_x_ipsecrequest_mode);
				goto bad;
			}
			(*p_isr)->saidx.mode = xisr->sadb_x_ipsecrequest_mode;

			switch (xisr->sadb_x_ipsecrequest_level) {
			case IPSEC_LEVEL_DEFAULT:
			case IPSEC_LEVEL_USE:
			case IPSEC_LEVEL_REQUIRE:
				break;
			case IPSEC_LEVEL_UNIQUE:
				(*p_isr)->saidx.reqid =
					xisr->sadb_x_ipsecrequest_reqid;
				break;

			default:
				plog(LLV_ERROR, LOCATION, NULL,
					"invalid level: %u\n",
					xisr->sadb_x_ipsecrequest_level);
				goto bad;
			}
			(*p_isr)->level = xisr->sadb_x_ipsecrequest_level;

			/* set IP addresses if there */
			if (xisr->sadb_x_ipsecrequest_len > sizeof(*xisr)) {
				struct sockaddr *paddr;

				paddr = (struct sockaddr *)(xisr + 1);
				bcopy(paddr, &(*p_isr)->saidx.src,
					sysdep_sa_len(paddr));

				paddr = (struct sockaddr *)((caddr_t)paddr
							+ sysdep_sa_len(paddr));
				bcopy(paddr, &(*p_isr)->saidx.dst,
					sysdep_sa_len(paddr));
			}

			(*p_isr)->sp = new;

			/* initialization for the next. */
			p_isr = &(*p_isr)->next;
			tlen -= xisr->sadb_x_ipsecrequest_len;

			/* validity check */
			if (tlen < 0) {
				plog(LLV_ERROR, LOCATION, NULL,
					"becoming tlen < 0\n");
			}

			xisr = (struct sadb_x_ipsecrequest *)((caddr_t)xisr
			                 + xisr->sadb_x_ipsecrequest_len);
		}
	    }
		break;
	default:
		plog(LLV_ERROR, LOCATION, NULL,
			"invalid policy type.\n");
		goto bad;
	}

#ifdef HAVE_PFKEY_POLICY_PRIORITY
	KEY_SETSECSPIDX(xpl->sadb_x_policy_dir,
			saddr + 1,
			daddr + 1,
			saddr->sadb_address_prefixlen,
			daddr->sadb_address_prefixlen,
			saddr->sadb_address_proto,
			xpl->sadb_x_policy_priority,
			created,
			&new->spidx);
#else
	KEY_SETSECSPIDX(xpl->sadb_x_policy_dir,
			saddr + 1,
			daddr + 1,
			saddr->sadb_address_prefixlen,
			daddr->sadb_address_prefixlen,
			saddr->sadb_address_proto,
			created,
			&new->spidx);
#endif

#ifdef HAVE_SECCTX
	if (mhp[SADB_X_EXT_SEC_CTX] != NULL) {
		struct sadb_x_sec_ctx *ctx;

		ctx = (struct sadb_x_sec_ctx *)mhp[SADB_X_EXT_SEC_CTX];
		new->spidx.sec_ctx.ctx_alg = ctx->sadb_x_ctx_alg;
		new->spidx.sec_ctx.ctx_doi = ctx->sadb_x_ctx_doi;
		new->spidx.sec_ctx.ctx_strlen = ctx->sadb_x_ctx_len;
		memcpy(new->spidx.sec_ctx.ctx_str,ctx + 1,ctx->sadb_x_ctx_len);
	}
#endif /* HAVE_SECCTX */

	/* Set local and remote hints for that SP, if available */
	if (local && remote) {
		new->local = dupsaddr(local);
		new->remote = dupsaddr(remote);
	}

	inssp(new);

	return 0;
bad:
	if (new != NULL) {
		if (new->req != NULL)
			racoon_free(new->req);
		racoon_free(new);
	}
	return -1;
}

/* proto/mode/src->dst spi */
const char *
sadbsecas2str(src, dst, proto, spi, mode)
	struct sockaddr *src, *dst;
	int proto;
	u_int32_t spi;
	int mode;
{
	static char buf[256];
	u_int doi_proto, doi_mode = 0;
	char *p;
	int blen, i;

	doi_proto = pfkey2ipsecdoi_proto(proto);
	if (doi_proto == ~0)
		return NULL;
	if (mode) {
		doi_mode = pfkey2ipsecdoi_mode(mode);
		if (doi_mode == ~0)
			return NULL;
	}

	blen = sizeof(buf) - 1;
	p = buf;

	i = snprintf(p, blen, "%s%s%s ",
		s_ipsecdoi_proto(doi_proto),
		mode ? "/" : "",
		mode ? s_ipsecdoi_encmode(doi_mode) : "");
	if (i < 0 || i >= blen)
		return NULL;
	p += i;
	blen -= i;

	i = snprintf(p, blen, "%s->", saddr2str(src));
	if (i < 0 || i >= blen)
		return NULL;
	p += i;
	blen -= i;

	i = snprintf(p, blen, "%s ", saddr2str(dst));
	if (i < 0 || i >= blen)
		return NULL;
	p += i;
	blen -= i;

	if (spi) {
		snprintf(p, blen, "spi=%lu(0x%lx)", (unsigned long)ntohl(spi),
		    (unsigned long)ntohl(spi));
	}

	return buf;
}
#ifdef __rtems__
#include "rtems-bsd-racoon-pfkey-data.h"
#endif /* __rtems__ */
