root/crypto/dist/ipsec-tools/src/racoon/pfkey.c
/*      $NetBSD: pfkey.c,v 1.63 2025/03/07 15:55:29 christos Exp $      */

/* $Id: pfkey.c,v 1.63 2025/03/07 15:55:29 christos 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(u_int);
static u_int ipsecdoi2pfkey_ealg(u_int);
static u_int ipsecdoi2pfkey_calg(u_int);
static u_int ipsecdoi2pfkey_alg(u_int, u_int);
static u_int keylen_aalg(u_int);
static u_int keylen_ealg(u_int, int);

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

static int (*pkrecvf[])(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(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
 */
/*ARGSUSED*/
static int
pfkey_handler(void *ctx __unused, 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(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;
        }

        for (;;) {
                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(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(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 ~0u;

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

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

/* IPSECDOI_ESP -> SADB_EALG */
static u_int
ipsecdoi2pfkey_ealg(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_AESGCM16
        case IPSECDOI_ESP_AESGCM16:
                return SADB_X_EALG_AESGCM16;
#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 ~0u;

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

/* IPCOMP -> SADB_CALG */
static u_int
ipsecdoi2pfkey_calg(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 ~0u;
        }
        /*NOTREACHED*/
}

/* IPSECDOI_PROTO -> SADB_SATYPE */
u_int
ipsecdoi2pfkey_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 ~0u;
        }
        /*NOTREACHED*/
}

static u_int
ipsecdoi2pfkey_alg(u_int algclass, u_int 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 ~0u;
        }
        /*NOTREACHED*/
}

/* SADB_SATYPE -> IPSECDOI_PROTO */
u_int
pfkey2ipsecdoi_proto(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 ~0u;
        }
        /*NOTREACHED*/
}

/* IPSECDOI_ATTR_ENC_MODE -> IPSEC_MODE */
u_int
ipsecdoi2pfkey_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 ~0u;
        }
        /*NOTREACHED*/
}

/* IPSECDOI_ATTR_ENC_MODE -> IPSEC_MODE */
u_int
pfkey2ipsecdoi_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 ~0u;
        }
        /*NOTREACHED*/
}

/* default key length for encryption algorithm */
static u_int
keylen_aalg(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 ~0u;
        }
        return res;
}

/* default key length for encryption algorithm */
static u_int
keylen_ealg(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 ~0u;
        }
        return res;
}

void
pk_fixup_sa_addresses(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(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)) == ~0u)
                        goto bad;
                if ((*e_keylen = keylen_ealg(t_id, *e_keylen)) == ~0u)
                        goto bad;
                *e_keylen >>= 3;

                if ((*a_type = ipsecdoi2pfkey_aalg(hashtype)) == ~0u)
                        goto bad;
                if ((*a_keylen = keylen_aalg(hashtype)) == ~0u)
                        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)) == ~0u)
                        goto bad;
                if ((*a_keylen = keylen_aalg(hashtype)) == ~0u)
                        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)) == ~0u)
                        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(struct ph2handle *iph2)
{
        struct sockaddr *src = NULL, *dst = NULL;
        u_int satype, mode;
        struct saprop *pp;
        struct saproto *pr;
        uint32_t minspi, maxspi;
        uint8_t natt_type = 0;
        uint16_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 == ~0u) {
                        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 == ~0u) {
                        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(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(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 == ~0u) {
                        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 == ~0u) {
                        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);
                        sa_args.l_natt_oa = 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(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 == ~0u) {
                        plog(LLV_ERROR, LOCATION, NULL,
                                "invalid proto_id %d\n", msg->sadb_msg_satype);
                        return -1;
                }
                encmode = pfkey2ipsecdoi_mode(sa_mode);
                if (encmode == ~0u) {
                        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(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 == ~0u) {
                        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 == ~0u) {
                        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);
                        sa_args.l_natt_oa = iph2->natoa_dst;
#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(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(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 == ~0u) {
                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(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 process such an acquire message because racoon may
         *       have 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(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 == ~0u) {
                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(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(caddr_t *policy0, int *policylen0, int 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 == ~0u) {
                        plog(LLV_ERROR, LOCATION, NULL,
                                "invalid proto_id %d\n", pr->proto_id);
                        goto err;
                }
                mode = ipsecdoi2pfkey_mode(pr->encmode);
                if (mode == ~0u) {
                        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(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(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(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(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(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(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(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(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(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) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "inappropriate sadb spddump 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;

        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(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;
        uint16_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)
        uint32_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_level != IPSEC_LEVEL_UNIQUE ||
             xisr_new->sadb_x_ipsecrequest_level != 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 *)(((uint8_t *)osaddr) + sysdep_sa_len(osaddr));
        nsaddr = (struct sockaddr *)(xisr_new + 1);
        ndaddr = (struct sockaddr *)(((uint8_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 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 *)(((uint8_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 *)(((uint8_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 *)(((uint8_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;
        uint32_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(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 = (uint32_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(int class, int calg, int 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 == ~0u)
                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(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 */
uint32_t
pk_getseq(void)
{
        return eay_random();
}

static int
addnewsp(caddr_t *mhp, struct sockaddr *local, struct sockaddr *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(struct sockaddr *src, struct sockaddr *dst, int proto,
    uint32_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 == ~0u)
                return NULL;
        if (mode) {
                doi_mode = pfkey2ipsecdoi_mode(mode);
                if (doi_mode == ~0u)
                        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;
}